[
  {
    "path": ".gitignore",
    "content": "*.filters\r\n\r\n/Test.VC.VC.opendb\r\n\r\n/Test.VC.db\r\n\r\n*.sdf\r\n\r\n*.vs\r\n\r\n*.pdb\r\n\r\n*.log\r\n\r\n*.tlog\r\n\r\n*.idb\r\n\r\nx64/*\r\n\r\n*.suo\r\n\r\n*.ipch\r\n\r\n# Prerequisites\r\n*.d\r\n\r\n# Compiled Object files\r\n*.slo\r\n*.lo\r\n*.o\r\n*.obj\r\n\r\n# Precompiled Headers\r\n*.gch\r\n*.pch\r\n\r\n# Compiled Dynamic libraries\r\n*.so\r\n*.dylib\r\n*.dll\r\n\r\n# Fortran module files\r\n*.mod\r\n*.smod\r\n\r\n# Compiled Static libraries\r\n*.lai\r\n*.la\r\n*.a\r\n*.lib\r\n\r\n# Executables\r\n*.exe\r\n*.out\r\n*.app\r\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2016 Li Tong\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "fpsgame\r\n====\r\n\r\nA simple FPS fight game based on 0 A.D..which is also an open-source game engine and a free software. You can get more information on this [link](https://play0ad.com/).\r\n"
  },
  {
    "path": "Test/ActorBash.cpp",
    "content": "#include \"Engine.h\"\n#include \"Game.h\"\n#include \"ObjectDummy.h\"\n#include \"BodyRigid.h\"\n#include \"BodyDummy.h\"\n#include \"Physics.h\"\n#include \"ShapeCapsule.h\"\n#include \"ActorBase.h\"\n#include \"Visualizer.h\"\n#include \"sys/SysControl.h\"\n\n\nusing namespace MathLib;\n\n#ifdef MEMORY_INFO\n#define new new(__FILE__, __LINE__) \n\n#endif // MEMORY_INFO\n\n/*\nTest high performance.\n*/\n#define ACTOR_BASE_IFPS             (1.0f / 120.0f)\n#define ACTOR_BASE_CLAMP            15.0f\n#define ACTOR_BASE_COLLISIONS       4\n\n\n/*\n*/\nCActorBase::CActorBase()\n{\n\tm_vUp = vec3(0.0f, 0.0f, 1.0f);\n\tm_pObject = new CObjectDummy();\n\tm_pDummy = new CBodyDummy();\n\tm_pShape = new CShapeCapsule(1.0f, 1.0f);\n\n\tm_nFlush = 0;\n\tm_vPosition = Vec3_zero;\n\tm_vVelocity = Vec3_zero;\n\tm_fPhiAngle = 0.0f;\t\t\t\t//倾斜角\t\t二维平面坐标系中，直线向Y周延伸的方向与X轴正向之间的夹角\n\tm_fThetaAngle = 0.0f;\t\t\t//方位角，正北方那条线与当前线条按照顺时针走过的角度\n\n\n\tfor (int i = 0; i < NUM_STATES; i++)\n\t{\n\t\tm_pStates[i] = 0;\n\t\tm_pTimes[i] = 0.0f;\n\t}\n\tm_pDummy->SetEnabled(1);\n\tm_pObject->SetBody(NULL);\n\tm_pObject->SetBody(m_pDummy);\n\tm_pShape->SetBody(NULL);\n\tm_pShape->SetBody(m_pDummy);\n\n\tm_pObject->SetWorldTransform(Get_Body_Transform());\n\tm_pShape->SetRestitution(0.0f);\n\tm_pShape->SetCollisionMask(2);\n\n\n\tSetEnabled(1);\n\tSetViewDirection(vec3(0.0f, 1.0f, 0.0f));\n\n\tSetCollision(1);\n\tSetCollisionRadius(0.3f);\n\tSetCollisionHeight(1.0f);\n\n\t\n\n\tSetMinVelocity(2.0f);\n\tSetMaxVelocity(4.0f);\n\n\tSetAcceleration(8.0f);\n\tSetDamping(8.0f);\n\tSetJumping(1.5f);\n\n\tSetFriction(2.0f);\n\tSetGround(0);\n\tSetCeiling(0);\n}\n\nCActorBase::~CActorBase()\n{\n\tm_pDummy->SetObject(NULL);\n\tdelete m_pObject;\n\tdelete m_pDummy;\n}\n\nvoid CActorBase::SetEnabled(int enable)\n{\n\tm_nEnable = enable;\n\tm_pDummy->SetEnabled(m_nEnable);\n}\n\n\nint CActorBase::IsEnabled() const\n{\n\treturn m_nEnable;\n}\nvoid CActorBase::Update(float ifps)\n{\n\tif (!m_nEnable)\n\t{\n\t\treturn;\n\t}\n\n\t// impulse\n\tvec3 impulse = vec3_zero;\n\n\t// ortho basis\n\tvec3 tangent, binormal;\n\tOrthoBasis(m_vUp, tangent, binormal);\n\n\t// current basis\n\tvec3 x = quat(m_vUp, -m_fPhiAngle) * binormal;\n\tvec3 y = Normalize(Cross(m_vUp, x));\n\tvec3 z = Normalize(Cross(x, y));\n\n\thandle states\n\t\tUpdate_States(1, ifps);\n\n\t// old velocity\n\tfloat x_velocity = Dot(x, m_vVelocity);\n\tfloat y_velocity = Dot(y, m_vVelocity);\n\tfloat z_velocity = Dot(z, m_vVelocity);\n\t// movement\n\tif (m_pStates[STATE_FORWARD]) impulse += x;\n\tif (m_pStates[STATE_BACKWARD]) impulse -= x;\n\tif (m_pStates[STATE_MOVE_LEFT]) impulse += y;\n\tif (m_pStates[STATE_MOVE_RIGHT]) impulse -= y;\n\timpulse.normalize();\n\t//velocity\n\tif (m_pStates[STATE_RUN])\n\t\timpulse *= m_fMaxVelocity;\n\telse\n\t\timpulse *= m_fMinVelocity;\n\t// jump\n\tif (m_pStates[STATE_JUMP] == STATE_BEGIN)\n\t{\n\t\timpulse += z * CMathCore::Sqrt(2.0f * 9.8f * m_fJumping) / (m_fAcceleration * ifps);\n\t}\n\n\t// rotate velocity\n\tif (GetGround())\n\t{\n\t\tm_vVelocity = x * x_velocity + y * y_velocity + z * z_velocity;\n\t}\n\n\t// time\n\tfloat time = ifps * g_Engine.pPhysics->GetScale();\n\n\t// target velocity\n\tfloat target_velocity = Length(vec2(Dot(x, impulse), Dot(y, impulse)));\n\n\t// penetration tolerance\n\tfloat penetration = g_Engine.pPhysics->GetPenetrationTolerance();\n\tfloat penetration_2 = penetration * 2.0f;\n\n\t// frozen linear velocity\n\tfloat frozen_velocity = g_Engine.pPhysics->GetFrozenLinearVelocity();\n\n\t// friction\n\tfloat friction = 0.0f;\n\tif (target_velocity < EPSILON)\n\t{\n\t\tfriction = m_fFriction;\n\t}\n\n\t//clear collision flags\n\tif (GetCollision())\n\t{\n\t\tm_nGround = 0;\n\t\tm_nCeiling = 0;\n\t}\n\t// movement\n\tdo\n\t{\n\t\t// adaptive time step\n\t\tfloat ifps = Min(time, ACTOR_BASE_IFPS);\n\t\ttime -= ifps;\n\n\t\t// save old velocity\n\t\tfloat old_velocity = Length(vec2(Dot(x, m_vVelocity), Dot(y, m_vVelocity)));\n\n\t\t// integrate velocity\n\t\tm_vVelocity += impulse * (m_fAcceleration * ifps);\n\t\tm_vVelocity += g_Engine.pPhysics->GetGravity() * ifps;\n\n\t\t// damping\n\t\tfloat current_velocity = Length(vec2(Dot(x, m_vVelocity), Dot(y, m_vVelocity)));\n\t\tif (target_velocity < EPSILON || current_velocity > target_velocity)\n\t\t{\n\t\t\tm_vVelocity = (x * Dot(x, m_vVelocity) + y * Dot(y, m_vVelocity)) * CMathCore::Exp(-m_fDamping * ifps) + z * Dot(z, m_vVelocity);\n\t\t}\n\n\t\t// clamp maximum velocity\n\t\tcurrent_velocity = Length(vec2(Dot(x, m_vVelocity), Dot(y, m_vVelocity)));\n\t\tif (current_velocity > old_velocity)\n\t\t{\n\t\t\tif (current_velocity > target_velocity)\n\t\t\t{\n\t\t\t\tm_vVelocity = (x * Dot(x, m_vVelocity) + y * Dot(y, m_vVelocity)) * target_velocity / current_velocity + z * Dot(z, m_vVelocity);\n\t\t\t}\n\t\t}\n\n\t\t// frozen velocity\n\t\tint is_frozen = 0;\n\t\tif (current_velocity < frozen_velocity)\n\t\t{\n\t\t\tm_vVelocity = z * Dot(z, m_vVelocity);\n\t\t\tis_frozen = 1;\n\t\t}\n\n\t\t// integrate position\n\t\t//m_vPosition += Vec3(m_vVelocity * ifps);\n\n\t\t// world collision\n\t\tif (GetCollision())\n\t\t{\n\t\t\t// get collision\n\t\t\tvec3 tangent, binormal;\n\t\t\tconst Vec3 *caps = m_pShape->GetCaps();\n\t\t\tfor (int i = 0; i < ACTOR_BASE_COLLISIONS; i++)\n\t\t\t{\n\t\t\t\tm_pDummy->SetTransform(Get_Body_Transform());\n\t\t\t\tm_pShape->GetCollision(m_vecContacts, 0.0f);\n\t\t\t\tif (m_vecContacts.Size() == 0) break;\n\t\t\t\tfloat inum_contacts = 1.0f / CMathCore::Itof(m_vecContacts.Size());\n\t\t\t\tfor (int j = 0; j < m_vecContacts.Size(); j++)\n\t\t\t\t{\n\t\t\t\t\tconst CShape::Contact &c = m_vecContacts[j];\n\n\t\t\t\t\tvec3 normalCollision = c.normal;\n\n\t\t\t\t\tif (is_frozen && c.depth < penetration_2)\n\t\t\t\t\t{\n\t\t\t\t\t\tm_vPosition += Vec3(z * (Max(c.depth - penetration, 0.0f) * inum_contacts * Dot(z, normalCollision)));\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tm_vPosition += Vec3(normalCollision * (Max(c.depth - penetration, 0.0f) * inum_contacts));\n\t\t\t\t\t\tis_frozen = 0;\n\t\t\t\t\t}\n\t\t\t\t\tfloat normal_velocity = Dot(normalCollision, m_vVelocity);\n\t\t\t\t\tif (normal_velocity < 0.0f)\n\t\t\t\t\t{\n\t\t\t\t\t\tm_vVelocity -= normalCollision * normal_velocity;\n\t\t\t\t\t}\n\t\t\t\t\tif (friction > EPSILON)\n\t\t\t\t\t{\n\t\t\t\t\t\tOrthoBasis(c.normal, tangent, binormal);\n\t\t\t\t\t\tfloat tangent_velocity = Dot(tangent, m_vVelocity);\n\t\t\t\t\t\tfloat binormal_velocity = Dot(binormal, m_vVelocity);\n\t\t\t\t\t\tif (CMathCore::Abs(tangent_velocity) > EPSILON || CMathCore::Abs(binormal_velocity) > EPSILON) {\n\t\t\t\t\t\t\tfloat friction_velocity = Clamp(Max(-normal_velocity, 0.0f) * friction * CMathCore::RSqrt(tangent_velocity * tangent_velocity + binormal_velocity * binormal_velocity), -1.0f, 1.0f);\n\t\t\t\t\t\t\tm_vVelocity -= tangent * tangent_velocity * friction_velocity;\n\t\t\t\t\t\t\tm_vVelocity -= binormal * binormal_velocity * friction_velocity;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (Dot(c.normal, m_vUp) > 0.5f && Dot(vec3(c.point - caps[0]), m_vUp) < 0.0f) m_nGround = 1;\n\t\t\t\t\tif (Dot(c.normal, m_vUp) < -0.5f && Dot(vec3(c.point - caps[1]), m_vUp) > 0.0f) m_nCeiling = 1;\n\t\t\t\t}\n\t\t\t}\n\n\n\t\t\tm_vPosition += Vec3(m_vVelocity * ifps);\n\t\t}\n\t\twhile (time > EPSILON);\n\n\t\t// current position\n\t\tm_pObject->SetWorldTransform(Get_Body_Transform());\n\t\tm_WorldBoundBox.Set(m_BoundBox, Translate(m_vPosition));\n\t\tm_WorldBoundSphere.Set(m_BoundSphere, Translate(m_vPosition));\n\t}\n}\n\n/*\n*/\nvoid CActorBase::Update_Bounds()\n{\n\tfloat radius = m_pShape->GetRadius();\n\tfloat hheight = m_pShape->GetHHeight();\n\tm_BoundBox.Set(vec3(-radius, -radius, 0.0f), vec3(radius, radius, (radius + hheight) * 2.0f));\n\tm_BoundSphere.Set(vec3(0.0f, 0.0f, radius + hheight), radius + hheight);\n\n\tm_WorldBoundBox.Set(m_BoundBox, Translate(m_vPosition));\n\tm_WorldBoundSphere.Set(m_BoundSphere, Translate(m_vPosition));\n}\n\n/*\n*/\nvoid CActorBase::SetIntersectionMask(int mask)\n{\n\tm_pShape->SetIntersectionMask(mask);\n}\n\nint CActorBase::GetIntersectionMask() const\n{\n\treturn m_pShape->GetIntersectionMask();\n}\n\n/*\n*/\nvoid CActorBase::SetCollision(int c)\n{\n\tm_nCollision = c;\n}\n\nint CActorBase::GetCollision() const\n{\n\treturn m_nCollision;\n}\nvoid CActorBase::SetCollisionMask(int mask)\n{\n\tm_pShape->SetCollisionMask(mask);\n}\n\nint CActorBase::GetCollisionMask() const\n{\n\treturn m_pShape->GetCollisionMask();\n}\nvoid CActorBase::SetCollisionRadius(float radius)\n{\n\tif (!Compare(m_pShape->GetRadius(), radius))\n\t{\n\t\tm_pDummy->SetPreserveTransform(Mat4(Translate(m_vUp * (radius - m_pShape->GetRadius()))) * m_pDummy->GetTransform());\n\t\tm_pShape->SetRadius(radius);\n\t}\n\tUpdate_Bounds();\n}\n\nfloat CActorBase::GetCollisionRadius() const\n{\n\treturn m_pShape->GetRadius();\n}\n\n/*\n*/\nvoid CActorBase::SetCollisionHeight(float height)\n{\n\tif (!Compare(m_pShape->GetHeight(), height))\n\t{\n\t\tm_pDummy->SetPreserveTransform(Mat4(Translate(m_vUp * (height - m_pShape->GetHeight()) * 0.5f)) * m_pDummy->GetTransform());\n\t\tm_pShape->SetHeight(height);\n\t}\n\tUpdate_Bounds();\n}\nfloat CActorBase::GetCollisionHeight() const\n{\n\treturn m_pShape->GetHeight();\n}\n\nvoid CActorBase::SetMinVelocity(float velocity)\n{\n\tm_fMinVelocity = Max(velocity, 0.0f);\n}\nfloat CActorBase::GetMinVelocity() const\n{\n\treturn m_fMinVelocity;\n}\n\n/*\n*/\nvoid CActorBase::SetMaxVelocity(float velocity)\n{\n\tm_fMaxVelocity = Max(velocity, 0.0f);\n}\n\nfloat CActorBase::GetMaxVelocity() const\n{\n\treturn m_fMaxVelocity;\n}\n\n/*\n*/\nvoid CActorBase::SetAcceleration(float accel)\n{\n\tm_fAcceleration = Max(accel, 0.0f);\n}\n\nfloat CActorBase::GetAcceleration() const\n{\n\treturn m_fAcceleration;\n}\n\n/*\n*/\nvoid CActorBase::SetDamping(float d)\n{\n\tm_fDamping = Max(d, 0.0f);\n}\n\nfloat CActorBase::GetDamping() const\n{\n\treturn m_fDamping;\n}\n\n/*\n*/\nvoid CActorBase::SetJumping(float j)\n{\n\tm_fJumping = Max(j, 0.0f);\n}\nfloat CActorBase::GetJumping() const\n{\n\treturn m_fJumping;\n}\n\n/*\n*/\nvoid CActorBase::SetViewDirection(const vec3 &d)\n{\n\tm_vDirection = Normalize(d);\n\n\t// ortho basis\n\tvec3 tangent, binormal;\n\tOrthoBasis(m_vUp, tangent, binormal);\n\n\t// decompose direction\n\tm_fPhiAngle = CMathCore::ATan2(Dot(m_vDirection, tangent), Dot(m_vDirection, binormal)) * RAD2DEG;\n\tm_fThetaAngle = CMathCore::ACos(Clamp(Dot(m_vDirection, m_vUp), -1.0f, 1.0f)) * RAD2DEG - 90.0f;\n\n\tm_pObject->SetWorldTransform(Get_Body_Transform());\n}\n\nconst vec3 &CActorBase::GetViewDirection() const\n{\n\treturn m_vDirection;\n}\n/******************************************************************************\\\n*\n* States\n*\n\\******************************************************************************/\n\n/*\n*/\nint CActorBase::GetState(int state) const\n{\n\tassert(state >= 0 && state < NUM_STATES && \"CPlayerActor::GetState(): bad state number\");\n\treturn m_pStates[state];\n}\n\nfloat CActorBase::GetStateTime(int state) const\n{\n\tassert(state >= 0 && state < NUM_STATES && \"CPlayerActor::GetStateTime(): bad state number\");\n\treturn m_pTimes[state];\n}\n\n/******************************************************************************\\\n*\n* Contacts\n*\n\\******************************************************************************/\n\n/*\n*/\nint CActorBase::GetNumContacts() const\n{\n\treturn m_vecContacts.Size();\n}\n\nconst CShape::Contact &CActorBase::GetContact(int num) const\n{\n\treturn m_vecContacts[num];\n}\n\n/*\n*/\nvoid CActorBase::SetGround(int g)\n{\n\tm_nGround = g;\n}\n\nint CActorBase::GetGround() const\n{\n\treturn m_nGround;\n}\n\n/*\n*/\nvoid CActorBase::SetCeiling(int c)\n{\n\tm_nCeiling = c;\n}\n\nint CActorBase::GetCeiling() const\n{\n\treturn m_nCeiling;\n}\n\n/*\n*/\nMat4 CActorBase::Get_Body_Transform() const\n{\n\tVec3 center = m_vPosition + Vec3(m_vUp * (m_pShape->GetHHeight() + m_pShape->GetRadius()));\n\treturn SetTo(center, center + Vec3(m_vDirection - m_vUp * Dot(m_vDirection, m_vUp)), m_vUp) * Mat4(RotateX(-90.0f) * RotateZ(90.0f));\n}\n\n/*\n*/\nint CActorBase::Update_State(int condition, int state, int begin, int end, float ifps)\n{\n\t// disabled to begin\n\tif (condition && m_pStates[state] == STATE_DISABLED && begin)\n\t{\n\t\tm_pStates[state] = STATE_BEGIN;\n\t\tm_pTimes[state] = 0.0f;\n\t\treturn STATE_BEGIN;\n\t}\n\n\t// enabled or begin to end\n\tif (condition == 0 && (m_pStates[state] == STATE_ENABLED || m_pStates[state] == STATE_BEGIN) && end)\n\t{\n\t\tm_pStates[state] = STATE_END;\n\t\treturn STATE_END;\n\t}\n\t// begin to enabled\n\tif ((condition && m_pStates[state] == STATE_BEGIN) || m_pStates[state] == STATE_ENABLED)\n\t{\n\t\tm_pStates[state] = STATE_ENABLED;\n\t\tm_pTimes[state] += ifps;\n\t\treturn STATE_ENABLED;\n\t}\n\n\t// end to disabled\n\tif (m_pStates[state] == STATE_END)\n\t{\n\t\tm_pStates[state] = STATE_DISABLED;\n\t\treturn STATE_DISABLED;\n\t}\n\n\treturn STATE_DISABLED;\n\n}\n\nvoid CActorBase::Update_States(int enabled, float ifps)\n{\n\t// handle states\n\tif (enabled)\n\t{\n\t\tif (g_pSysControl->GetState(CSysControl::STATE_FORWARD) && g_pSysControl->GetState(CSysControl::STATE_BACKWARD))\n\t\t{\n\t\t\tUpdate_State(0, STATE_FORWARD, 1, 1, ifps);\n\t\t\tUpdate_State(0, STATE_BACKWARD, 1, 1, ifps);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tUpdate_State(g_pSysControl->GetState(CSysControl::STATE_FORWARD), STATE_FORWARD, 1, 1, ifps);\n\t\t\tUpdate_State(g_pSysControl->GetState(CSysControl::STATE_BACKWARD), STATE_BACKWARD, 1, 1, ifps);\n\t\t}\n\t\tif (g_pSysControl->GetState(CSysControl::STATE_MOVE_LEFT) && g_pSysControl->GetState(CSysControl::STATE_MOVE_RIGHT))\n\t\t{\n\t\t\tUpdate_State(0, STATE_MOVE_LEFT, 1, 1, ifps);\n\t\t\tUpdate_State(0, STATE_MOVE_RIGHT, 1, 1, ifps);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tUpdate_State(g_pSysControl->GetState(CSysControl::STATE_MOVE_LEFT), STATE_MOVE_LEFT, 1, 1, ifps);\n\t\t\tUpdate_State(g_pSysControl->GetState(CSysControl::STATE_MOVE_RIGHT), STATE_MOVE_RIGHT, 1, 1, ifps);\n\t\t}\n\n\t\tUpdate_State(g_pSysControl->GetState(CSysControl::STATE_CROUCH), STATE_CROUCH, 1, 1, ifps);\n\t\tUpdate_State(g_pSysControl->GetState(CSysControl::STATE_JUMP), STATE_JUMP, m_nGround, 1, ifps);\n\t\tUpdate_State(g_pSysControl->GetState(CSysControl::STATE_RUN), STATE_RUN, 1, 1, ifps);\n\t}\n\t// disable states\n\telse\n\t{\n\t\tUpdate_State(0, STATE_FORWARD, 1, 1, ifps);\n\t\tUpdate_State(0, STATE_BACKWARD, 1, 1, ifps);\n\t\tUpdate_State(0, STATE_MOVE_LEFT, 1, 1, ifps);\n\t\tUpdate_State(0, STATE_MOVE_RIGHT, 1, 1, ifps);\n\t\tUpdate_State(0, STATE_CROUCH, 1, 1, ifps);\n\t\tUpdate_State(0, STATE_JUMP, m_nGround, m_nGround, ifps);\n\t\tUpdate_State(0, STATE_RUN, 1, 1, ifps);\n\t}\n}\n\n\n/******************************************************************************\\\n*\n* Bounds\n*\n\\******************************************************************************/\n\n/*\n*/\nconst CBoundBox &CActorBase::GetBoundBox() const\n{\n\treturn m_BoundBox;\n}\n\nconst CBoundSphere &CActorBase::GetBoundSphere() const\n{\n\treturn m_BoundSphere;\n}\n\nconst CWorldBoundBox & CActorBase::GetWorldBoundBox() const\n{\n\treturn m_WorldBoundBox;\n}\n\nconst CWorldBoundSphere & CActorBase::GetWorldBoundSphere() const\n{\n\treturn m_WorldBoundSphere;\n}\n\nvoid CActorBase::RenderVisualizer()\n{\n\tm_pShape->RenderVisualizer(vec4(1.0f, 0.0f, 0.0f, 1.0f));\n\tg_Engine.pVisualizer->RenderVector(m_pShape->GetCenter(), m_pShape->GetCenter() + m_vDirection, vec4(1.0f, 0.0f, 0.0f, 1.0f));\n}\n\nvoid CActorBase::SetFriction(float friction)\n{\n\tm_fFriction = friction;\n}\n\nfloat CActorBase::GetFriction() const\n{\n\treturn m_fFriction;\n}\n\nvoid CActorBase::SetPosition(const MathLib::vec3& pos)\n{\n\tm_vPosition = pos;\n}\n\nconst MathLib::vec3& CActorBase::GetPosition() const\n{\n\treturn m_vPosition;\n}\n"
  },
  {
    "path": "Test/ActorBash.h",
    "content": "#pragma once\r\n#ifndef __ACTOR_BASE_H__\r\n#define __ACTOR_BASE_H__\r\n\r\n#include \"Shape.h\"\r\n#include \"Bounds.h\"\r\n#include \"Player.h\"\r\n/*\r\n */\r\nclass CBRObject;\r\nclass CBodyDummy;\r\nclass CBodyRigid;\r\nclass CObjectDummy;\r\nclass CShapeCapsule;\r\n\r\n/*\r\n *ײײ뾶ײ߶ȵȣ\r\n */\r\nclass CActorBase\r\n{    \r\npublic:\r\n\tCActorBase();\r\n\tvirtual ~CActorBase();\r\n\r\n\tvoid SetEnabled(int enable);\r\n\tint IsEnabled() const;\r\n\r\n\tvoid Update(float ifps);\r\n\r\n\t// intersection mask\r\n\tvoid SetIntersectionMask(int mask);\r\n\tint GetIntersectionMask() const;\r\n\r\n\t    // collision\r\n    void SetCollision(int collision);\r\n    int GetCollision() const;\r\n        \r\n    // collision mask\r\n    void SetCollisionMask(int mask);\r\n    int GetCollisionMask() const;\r\n        \r\n    // collision radius\r\n    void SetCollisionRadius(float radius);\r\n    float GetCollisionRadius() const;\r\n        \r\n    // collision height\r\n    void SetCollisionHeight(float height);\r\n    float GetCollisionHeight() const;\r\n        \r\n    // maximum friction\tĦֵ\r\n    void SetFriction(float friction);\r\n    float GetFriction() const;\r\n        \r\n    // minimum velocity\tСٶ\r\n    void SetMinVelocity(float velocity);\r\n    float GetMinVelocity() const;\r\n        \r\n    // maximum velocity ٶ\r\n    void SetMaxVelocity(float velocity);\r\n    float GetMaxVelocity() const;\r\n\r\n\t// acceleration ٶ\r\n    void SetAcceleration(float acceleration);\r\n    float GetAcceleration() const;\r\n        \r\n    // damping\tб\r\n    void SetDamping(float damping);\r\n    float GetDamping() const;\r\n        \r\n    // jumping Ծ\r\n    void SetJumping(float jumping);\r\n    float GetJumping() const;\r\n  \r\n    // view direction ߳\r\n    void SetViewDirection(const MathLib::vec3 &direction);\r\n    const MathLib::vec3 &GetViewDirection() const;\r\n    //position λ\r\n    void SetPosition( const MathLib::vec3 & pos );\r\n    const MathLib::vec3& GetPosition() const;\r\n\t\r\n\r\n\r\n\r\n\tenum {            //״̬\r\n\t\tSTATE_FORWARD = 0,\r\n\t\tSTATE_BACKWARD,\r\n\t\tSTATE_MOVE_LEFT,\r\n\t\tSTATE_MOVE_RIGHT,\r\n\t\tSTATE_CROUCH,\t//׷\r\n\t\tSTATE_JUMP,\r\n\t\tSTATE_RUN,\r\n\t\tNUM_STATES,\r\n\t};\r\n\t    // state status\t\t//״̬Ƿã״̬ʼ\r\n    enum {\r\n        STATE_DISABLED = 0,\r\n        STATE_ENABLED,\r\n        STATE_BEGIN,\r\n        STATE_END,\r\n    };\r\n\r\n\t    // current state\r\n    int GetState(int state) const;\r\n    float GetStateTime(int state) const;\r\n        \r\n    // contacts\r\n    int GetNumContacts() const;\r\n    const CShape::Contact &GetContact(int num) const;\r\n        \r\n    // ground flag\t//ƶ͵λñ־\r\n    void SetGround(int ground);\r\n    int GetGround() const;\r\n        \r\n    // ceiling flag\t//ƶߵλñ־\r\n    void SetCeiling(int ceiling);\r\n    int GetCeiling() const;\r\n        \r\n    // bounds Լ\r\n    virtual const CBoundBox &GetBoundBox() const;\r\n    virtual const CBoundSphere &GetBoundSphere() const;\r\n    //ͬ\r\n    virtual const CWorldBoundBox &GetWorldBoundBox() const;\r\n    virtual const CWorldBoundSphere &GetWorldBoundSphere() const;\r\n\r\n    virtual void  RenderVisualizer();\r\nprivate:\r\n\t    // update bounds\t//±߽\r\n    void Update_Bounds();\r\n        \r\n    // player transformation\r\n    MathLib::Mat4 Get_Body_Transform() const;\r\n   \r\n    // update states\r\n    int Update_State(int condition,int state,int begin,int end,float ifps);\r\n    void Update_States(int enabled,float ifps);\r\n        \r\n    int m_nFrame;                   // frame number\r\n    \r\n    MathLib::vec3   m_vUp;          // up  άе\r\n    MathLib::vec3   m_vVelocity;    // velocity vector\tǰٶ\r\n\r\n    CObjectDummy *m_pObject;        // dummy object\r\n    CBodyDummy *m_pDummy;           // dummy body\r\n        \r\n    int m_nCollision;               // collision flag\r\n    CShapeCapsule *m_pShape;        // collision shape\r\n \r\n    float m_fFriction;              // fraction when frozen\r\n    float m_fMinVelocity;           // minimum velocity\r\n    float m_fMaxVelocity;           // maximum velocity\r\n    float m_fAcceleration;          // acceleration\r\n    float m_fDamping;               // damping\r\n    float m_fJumping;               // jumping\r\n        \r\n    int m_nFlush;                   // flush flag\r\n    MathLib::Vec3 m_vPosition;      // position\r\n    MathLib::vec3 m_vDirection;     // direction\r\n    float m_fPhiAngle;              // phi angle\r\n    float m_fThetaAngle;            // theta angle\r\n        \r\n    int m_pStates[NUM_STATES];      // state vector\r\n    float m_pTimes[NUM_STATES];     // time vector\r\n        \r\n    int m_nGround;                  // ground flag\r\n    int m_nCeiling;                 // ceiling flag\r\n        \r\n    CBoundBox m_BoundBox;           // bounding box\r\n    CBoundSphere m_BoundSphere;     // bounding sphere\r\n\r\n    CWorldBoundBox m_WorldBoundBox;           // bounding box\r\n    CWorldBoundSphere m_WorldBoundSphere;     // bounding sphere\r\n\r\n    CVector<CShape::Contact> m_vecContacts;\r\n    int m_nEnable;\r\n\r\n\r\n};\r\n#endif  __PLAYER_ACTOR_H__ "
  },
  {
    "path": "Test/CameraBash.cpp",
    "content": "#include \"CameraBase.h\"\n#include \"sys/SysControl.h\"\n#include \"HMDWrapper.h\"\n#include \"Engine.h\"\n#include \"Game.h\"\n\nusing namespace MathLib;\n\n\n#ifdef MEMORY_INFO\n#define new new(__FILE__, __LINE__) \n#endif // MEMORY_INFO\n\n#define CAMERA_BASE_CLAMP            89.9f\n\n\nCCameraBase::CCameraBase(): CPlayer(PLAYER_SPECTATOR) \n{\n\tm_nFlush = 0;\n    m_vPosition = Vec3_zero;\n    m_fPhiAngle = 0.0f;\n    m_fThetaAngle = 0.0f;\n    m_pLastPlayer = NULL;\n\tm_nEnabelMouse = 1;\n    SetRadius(1.0f);\n\n\tSetMinThetaAngle(-90.0f);\n    SetMaxThetaAngle(90.0f);\n    SetTurning(90.0f);\n\t\n    SetViewDirection(vec3(0.0f,1.0f,0.0f));\n\n    SetEnabled(0);\n    SetHMDEnabled(1);\n}\n\nCCameraBase::~CCameraBase()\n{\n    Leave();\n}\n\nvoid CCameraBase::SetEnabled(int nEnable)\n{\n\tif(nEnable)\n\t{\n\t\tm_pLastPlayer =  g_Engine.pGame->GetPlayer();\n        CPlayer::SetEnabled(nEnable);\n\t\tg_Engine.pGame->SetPlayer(this);\n\n\t}\n\telse\n\t{\n\t\tif (m_pLastPlayer)\n        {\n            g_Engine.pGame->SetPlayer(m_pLastPlayer);\n            CPlayer::SetEnabled(nEnable);\n        }\n\t}\n}\n\n\nint CCameraBase::IsEnabled() const\n{\n    return CPlayer::IsEnabled();\n}\nvoid CCameraBase::SetHMDEnabled(int nEnable)\n{\n    m_nHMDEnable = nEnable;\n}\n\nint CCameraBase::IsHMDEnabled() const\n{\n    return m_nHMDEnable;\n}\n\nvoid CCameraBase::SetRadius(float r)\n{\n    m_fRadius = r;\n}\n\nfloat CCameraBase::GetRadius() const\n{\n    return m_fRadius;\n}\n/******************************************************************************\\\n*\n* Parameters\n*\n\\******************************************************************************/\n\n/*\n */\nvoid CCameraBase::Update_Bounds() \n{\n    m_BoundSphere.Set(vec3_zero,m_fRadius);\n    m_BoundBox.Set(m_BoundSphere);\n    Update_World_Position();\n}\n\nvoid CCameraBase::SetMinThetaAngle(float angle)\n{\n    m_fMinThetaAngle = Clamp(angle,-CAMERA_BASE_CLAMP,CAMERA_BASE_CLAMP);\n}\n\nfloat CCameraBase::GetMinThetaAngle() const\n{\n    return m_fMinThetaAngle;\n}\n\n/*\n*/\nvoid CCameraBase::SetMaxThetaAngle(float angle)\n{\n\tm_fMaxThetaAngle = Clamp(angle, -CAMERA_BASE_CLAMP, CAMERA_BASE_CLAMP);\n}\nfloat CCameraBase::GetMaxThetaAngle() const\n{\n\treturn m_fMaxThetaAngle;\n}\nfloat CCameraBase::GetTurning() const\n{\n\treturn m_fTurning;\n}\nvoid CCameraBase::SetPhiAngle(float angle)\n{\n\tangle = angle - m_fPhiAngle;\n\tm_vDirection = quat(m_vUp, angle) * m_vDirection;\n\tm_fPhiAngle += angle;\n\n\tFlushTransform();\n}\n\n\nfloat CCameraBase::GetPhiAngle() const\n{\n\treturn m_fPhiAngle;\n}\n\nvoid CCameraBase::SetThetaAngle(float angle)\t\t\n{\n\tangle = Clamp(angle, m_fMinThetaAngle, m_fMaxThetaAngle) - m_fThetaAngle;\t\n\tm_vDirection = quat(Cross(m_vUp, m_vDirection), angle) * m_vDirection;\n\tm_fThetaAngle += angle;\n\n\n\tFlushTransform();\n}\n\n\nfloat CCameraBase::GetThetaAngle() const\n{\n\treturn m_fThetaAngle;\n}\n\n/*\n*\n*/\n\nvoid CCameraBase::SetViewDirection(const vec3 &d)  //设置视角方位，参考三维坐标系\n{ \n\tm_vDirection = Normalize(d);\n\n\t// ortho basis\n\tvec3 tangent, binormal;\n\tOrthoBasis(m_vUp, tangent, binormal);\n\n\t// decompose direction\n\tm_fPhiAngle = CMathCore::ATan2(Dot(m_vDirection, tangent), Dot(m_vDirection, binormal)) * RAD2DEG;\n\tm_fThetaAngle = CMathCore::ACos(Clamp(Dot(m_vDirection, m_vUp), -1.0f, 1.0f)) * RAD2DEG - 90.0f;\n\tm_fThetaAngle = Clamp(m_fThetaAngle, m_fMinThetaAngle, m_fMaxThetaAngle);\n\n\n\n\tFlushTransform();\n}\nconst vec3 &CCameraBase::GetViewDirection() const\n{\n\treturn m_vDirection;\n}\n\nvoid CCameraBase::UpdateControls(float ifps)\n{\n\tif (g_pHMD->GetUseHMD() && m_nHMDEnable)\n\t{\n\t\tUpdate_HMD(ifps);\n\t}\n\telse\n\t{\n\t\tUpdate_Controls(ifps);\n\t}\n}\n\n\n/******************************************************************************\\\n*\n* Flush\n*\n\\******************************************************************************/\n"
  },
  {
    "path": "Test/CameraBash.h",
    "content": "#ifndef __CAMERA_BASE_H__\n#define __CAMERA_BASE_H__\n#include \"Player.h\"\n\nclass CPlayer;\n\n/*\n*ͷǶȣӾ뾶СǶȣת߷򣬵ȡ\n*/\nclass CCameraBase : public CPlayer\n{\npublic:\n\n\tCCameraBase();\n\tvirtual ~CCameraBase();\n\n\tvoid SetEnabled(int nEnable);\n\tint IsEnabled() const;\n\n\tvoid SetHMDEnabled(int nEnable);\n\tint IsHMDEnabled() const;\n\n\tvoid SetRadius(float r);\n\tfloat GetRadius() const;\n\n\t// minimum theta angle\n\tvoid SetMinThetaAngle(float angle);\n\tfloat GetMinThetaAngle() const;\n\n\t// maximum theta angle\n\tvoid SetMaxThetaAngle(float angle);\n\tfloat GetMaxThetaAngle() const;\n\n\t// turning\n\tvoid SetTurning(float turning);\n\tfloat GetTurning() const;\n\t// phi angle\n\tvoid SetPhiAngle(float angle);\n\tfloat GetPhiAngle() const;\n\n\t// theta angle\n\tvoid SetThetaAngle(float angle);\n\tfloat GetThetaAngle() const;\n\n\t// view direction\n\tvoid SetViewDirection(const MathLib::vec3 &direction);\n\tconst MathLib::vec3 &GetViewDirection() const;\n\n\t// update\n\tvirtual void UpdateControls(float ifps);\n\n\t// flush\n\tvirtual void FlushTransform();\n\n\t// bounds\n\tvirtual const CBoundBox &GetBoundBox() const;\n\tvirtual const CBoundSphere &GetBoundSphere() const;\n\n\tvoid SetFlush(int nFlush) { m_nFlush = nFlush; }\n\tvoid SetMouseControls(int nEnable) { m_nEnabelMouse = nEnable; }\nprivate:\n\tvoid Update_Controls(float ifps);\n\tvoid Update_HMD(float ifps);\n\t// update bounds\n\tvoid Update_Bounds();\n\n\t// update transformation\n\tvirtual void Update_Transform();\n\n\tfloat m_fRadius;\n\tfloat m_fMinThetaAngle;             // minimum theta angle\n\tfloat m_fMaxThetaAngle;             // maximum theta angle\n\tfloat m_fAcceleration;              // acceleration\n\tfloat m_fTurning;                   // turning\n\n\tint m_nFlush;                       // flush flag\n\tMathLib::Vec3 m_vPosition;          // position\n\tMathLib::vec3 m_vDirection;         // direction\n\tfloat m_fPhiAngle;                  // phi angle\n\tfloat m_fThetaAngle;                // theta angle\n\n\tCBoundBox m_BoundBox;               // bounding box\n\tCBoundSphere m_BoundSphere;         // bounding sphere\n\n\tCPlayer *m_pLastPlayer;\n\n\tint m_nHMDEnable;\n\tint m_nEnabelMouse;\n}\n\n#endif"
  },
  {
    "path": "Test/Common.cpp",
    "content": "#include \"Common.h\"\n#include \"Engine.h\"\n#include \"Game.h\"\n#include \"World.h\"\n\nCCommon::CCommon(void)\n{\n}\n\n\nCCommon::~CCommon(void)\n{\n}\n\nvec3 CCommon::GetTransformDirection(const mat4& matTransform, const vec3& vUP)\n{\n\treturn MakeRotationFromYZ(matTransform.getColumn3(1), vUP) * vec3(0.0f, -1.0f, 0.0f);\n}\n\nint CCommon::GetIntersectionPosition(vec3& vRetPoint, const vec3& p0, const vec3& p1, int nMask /*= 2|4*/)\n{\n\tvec3 n;\n\tint s;\n\n\tg_Engine.pWorld->GetIntersection(p0, p1, nMask, vRetPoint, n, s);\n\n\treturn s >= 0;\n}\n\nint CCommon::GetIntersectionObject(CBRObject** vRetObject, const vec3& p0, const vec3& p1, int nMask /*= 2|4*/)\n{\n\tvec3 p, n;\n\tint s;\n\n\t*vRetObject = NULL;\n\t*vRetObject = g_Engine.pWorld->GetIntersection(p0, p1, nMask, p, n, s);\n\n\treturn s >= 0;\n}\n\nfloat CCommon::CalcAxisScale(const mat4& modelview, float fov, vec4 objectPosW, float sizeInPixels, float viewHeight)\n{\n\tfloat worldHeight;\n\t// World height on origin's z value\n\tvec4 objPosV;\n\tobjPosV = modelview * objectPosW;\n\tworldHeight = 2.0f * CMathCore::Abs(objPosV.z) * (float)CMathCore::Tan(fov / 2.0f * DEG2RAD);\n\n\treturn sizeInPixels * (worldHeight / viewHeight);\n}"
  },
  {
    "path": "Test/Common.h",
    "content": "#pragma once\n#include \"MathLib.h\"\nusing namespace MathLib;\nclass CBRObject;\n\nclass CCommon\n{\npublic:\n\tCCommon(void);\n\t~CCommon(void);\n\tstatic int\t GetIntersectionObject(CBRObject** vRetObject, const vec3& p0, const vec3& p1, int nMask = 2 | 4);\n\tstatic float  CalcAxisScale(float fov,vec4 objectPosW,float sizeInPixels,float viewHeight);\n\tstatic vec3   GetTransformDirection(const mat4& matTransform,const vec3& vUP = vec3(0.0f,0.0f,1.0f));\n\tstatic int\t GetIntersectionPosition(const mat4& modelview,vec3& vRetPoint,const vec3& p0,const vec3& p1,int nMask = 2|4);\n\n};"
  },
  {
    "path": "Test/FPSRole.cpp",
    "content": "#include \"FPSRole.h\"\n#include \"Engine.h\"\n#include \"Creature.h\"\n#include \"AnimationBlend.h\"\n#include \"WorldEffect.h\"\n#include \"ObjectMeshSkinned.h\"\n#include \"Game.h\"\n#include \"ObjectParticles.h\"\n\n\nCFPSRole::CFPSRole(void)\n{\n\tm_nFire = 0;\n\tm_fAniCoolingTime = 0;\n\tm_fAniNowCoolingTime = 0;\n\tm_fSudCoolingTime = 0;\t\n\tm_fEmitCoolingTime = 0;\n\n\tm_fEmitNowCoolingTime = 0;\n\tm_fSudNowCoolingTime = 0;\n\n\tm_strMuzzleBone = \"wuqi02\";\n\tm_pArmsMesh = NULL;\n\tm_pBulletParticle = NULL;\n\tm_strBulletName = \"data/effect/particle/zgfwq_sj_02.node\";\n\tm_strMuzzleEffect  = \"data/effect/particle/zgfwq_sj_01.node\";\n}\nCFPSRole::~CFPSRole(void)\n{\n\tg_Engine.pGame->RemoveNode(m_pBulletParticle);\n\tg_Engine.pGame->RemoveNode(m_pMuzzleEffect);\n}\n\nint CFPSRole::Init( int nRoleID,const char* strCharFile )\n{\n\tif(CRoleBase::Init(nRoleID,strCharFile))\n\t{\n\t\tm_pStand[0] = (CAnimationBlendRotate*)m_pCreature->GetAnimationBlend(\"stand_h\");\n\t\tm_pFire[0] = (CAnimationBlendRotate*)m_pCreature->GetAnimationBlend(\"fire_h\");\n\t\tm_pStand[1] = (CAnimationBlendRotate*)m_pCreature->GetAnimationBlend(\"stand_v\");\n\t\tm_pFire[1] = (CAnimationBlendRotate*)m_pCreature->GetAnimationBlend(\"fire_v\");\n\t\tm_pRun[0] = (CAnimationBlendDual*)m_pCreature->GetAnimationBlend(\"run_h\");\n\t\tm_pRun[1] = (CAnimationBlendDual*)m_pCreature->GetAnimationBlend(\"run_v\");\n\n\n\t\tm_fAniNowCoolingTime = 0.0f;\n\t\tm_fAniCoolingTime = 1.0f / m_pFire[0]->GetTriggerSpeed();\n\t\tm_fEmitNowCoolingTime = 0;\n\t\tm_fSudNowCoolingTime = 0;\n\n\t\t\n\t\tm_fSudCoolingTime = m_fAniCoolingTime  * 4.0f; //ֵ\n\t\tm_fEmitCoolingTime = m_fAniCoolingTime * 3.0f;//ֵ\n\n\n\t\tm_pBulletParticle = (CObjectParticles*)g_Engine.pGame->LoadNode(m_strBulletName);\n\t\tm_pBulletParticle->GetTracker(TRACKER_CUSTOM)->SetTrackValue(0,0.0f,vec4(1.0f,1.0f,1.0f,0.8f));\n\t\tm_pBulletParticle->SetName(CUtilStr::Format(\"%d\",m_nRoleID));\n\n\t\tm_pMuzzleEffect = (CWorldEffect*)g_Engine.pGame->LoadNode(m_strMuzzleEffect);\n\t\tm_pMuzzleEffect->Stop();\n\n\t\tm_pMuzzleEffect->SetLoop(0);\n\t\tm_pMuzzleEffect->SetLoopCount(1);\n\n\t}\n\treturn 1;//always return 1;\n}\n\nvoid CFPSRole::OnKeyFrame( _ActionCallback_KeyFrame* pKeyInfo )\n{\n\t//I will finish this function later.\n}\n\nvoid CFPSRole::OnActionComplete( _ActionCallback_Complete* pActInfo )\n{\n\treturn CRoleBase::OnActionComplete(pActInfo);\n}\n\n\nvoid CFPSRole::SetPaceAnimationF_B( int nF_B )\n{\n\tswitch(nF_B)\n\t{\n\tcase 0:\n\t\tm_pRun[0]->CloseBlend();\n\t\tbreak;\n\tcase 1:\t\n\t\tm_pRun[0]->PlayAnimationA();\n\t\tbreak;\n\tcase 2:\n\t\tm_pRun[0]->PlayAnimationB();\n\t\tbreak;\n\t}\n}\n\nvoid CFPSRole::SetPaceAnimationL_R( int nF_B )\n{\n\tswitch(nF_B)\n\t{\n\tcase 0:\n\t\tm_pRun[1]->CloseBlend();\n\t\tbreak;\n\tcase 1:\t\t\n\t\tm_pRun[1]->PlayAnimationA();\n\t\tbreak;\n\tcase 2:\t\t\n\t\tm_pRun[1]->PlayAnimationB();\n\t\tbreak;\n\t}\n}\n\nvoid CFPSRole::OnFire( float fCoolingTime )\n{\n\n}\n\nvoid CFPSRole::UpdateMuzzleTransform()\n{\n\tm_pFire[0]->LockFrame(2.3f);\n\tm_pFire[1]->LockFrame(2.3f);\n\tm_pStand[0]->LockFrame(2.3f);\n\tm_pStand[1]->LockFrame(2.3f);\n\n\tm_pArmsMesh->UpdateLink_Bone();\n\tm_pArmsMesh->SetTransform(m_pArmsMesh->GetTransform());\n\n\tm_matMuzzleTransform = m_pArmsMesh->GetWorldBoneTransform(m_nMuzzleBone);\n\n}\n\nvoid CFPSRole::SetMuzzleSpinL_R( float vValue )\n{\n\tm_pStand[1]->SetRotateSpin(vValue);\n\tm_pFire[1]->SetRotateSpin(vValue);\n}\n\n\nvoid CFPSRole::SetMuzzleSpinU_D( float vValue )\n{\n\tm_pStand[0]->SetRotateSpin(vValue);\n\tm_pFire[0]->SetRotateSpin(vValue);\n}\n\nvoid CFPSRole::Update( float ifps )\n{\n\tUpdateMuzzleTransform();\t\t\n\tm_pBulletParticle->SetWorldTransform(m_matMuzzleTransform);\n\tm_pMuzzleEffect->SetWorldTransform(m_matMuzzleTransform*Scale(0.4f,1.0f,0.5f));\n\n\tUpdateFire(ifps);\n\tCRoleBase::Update(ifps);\n}\nvoid CFPSRole::SetPaceAnimationL_R( int nF_B )\n{\n\tswitch(nF_B)\n\t{\n\tcase 0:\n\t\t{\n\t\t\tm_pRun[1]->CloseBlend();\n\t\t}break;\n\tcase 1:\n\t\t{\n\t\t\tm_pRun[1]->PlayAnimationA();\n\t\t}break;\n\tcase 2:\n\t\t{\n\t\t\tm_pRun[1]->PlayAnimationB();\n\t\t}break;\n\t}\n}\n\nvoid CFPSRole::UpdateFire( float ifps )\n{\n\tm_fAniNowCoolingTime -= ifps;\n\tm_fSudNowCoolingTime -= ifps;\n\tm_fEmitNowCoolingTime -= ifps;\n\n\tif(m_nFire)\n\t{\n\t\t//\n\t\tif(m_fAniNowCoolingTime < EPSILON)\n\t\t{\n\t\t\tm_fAniNowCoolingTime = m_fAniCoolingTime;\n\t\t\tm_pFire[0]->OnJumpFrame();\n\t\t\tm_pFire[1]->OnJumpFrame();\n\t\t}\n\n\t\t//\n\t\tif(m_fSudNowCoolingTime < EPSILON)\n\t\t{\n\t\t\tm_fSudNowCoolingTime = m_fSudCoolingTime;\n\t\t\tm_pMuzzleEffect->Play();\n\t\t}\n\n\t\t//ӵ\n\t\tif(m_fEmitNowCoolingTime < EPSILON)\n\t\t{\n\t\t\tm_fEmitNowCoolingTime = m_fEmitCoolingTime;\n\t\t\tm_pBulletParticle->FireBullet();\n\t\t\tOnFire(m_fEmitCoolingTime);\n\t\t}\n\t}\n}\n\nint CFPSRole::SetupArms( int nAssembly,int nBody )\n{\n\tint nRet = m_pCreature->SetupBody(nAssembly,nBody);\n\tif(!nRet)\n\t{\n\t\treturn 0;\n\t}\n\n\tCNode* pNode = GetAssemblyNode(nAssembly);\n\tif(pNode->GetType() == CNode::OBJECT_MESH_SKINNED)\n\t{\n\t\tm_pArmsMesh = (CObjectMeshSkinned*)pNode;\n\t\tm_nMuzzleBone = m_pArmsMesh->FindBone(m_strMuzzleBone);\n\t}\n\n\treturn 1;\n}\n"
  },
  {
    "path": "Test/FPSRole.h",
    "content": "#pragma once\n#include \"Rolebase.h\"\n#include \"UtilStr.h\"\n\nclass CAnimationBlendRotate;\nclass CAnimationBlendDual;\nclass CObjectParticles;\nclass CWorldEffect;\nclass CObjectMeshSkinned;\n\nclass CFPSRole :public CRoleBase\n{\npublic:\n\tCFPSRole(void);\n\tvirtual ~CFPSRole(void);\n\tvirtual int  Init(int nRoleID, const char* strCharFile);\n\tvirtual void\tUpdate(float ifps);\n\n\tvoid    SetMuzzleSpinU_D(float vValue);\n\tvoid    SetMuzzleSpinL_R(float vValue);\n\tvoid    SetPaceAnimationF_B(int nF_B);\n\tvoid    SetPaceAnimationL_R(int nF_B);\n\tvoid    OpenFire() { m_nFire = 1; }\n\tvoid    CloseFire() { m_nFire = 0; }\n\tint     SetupArms(int nAssembly, int nBody);\nprotected:\n\tvirtual void\t\tOnKeyFrame(_ActionCallback_KeyFrame* pKeyInfo);\n\tvirtual void\t\tOnActionComplete(_ActionCallback_Complete* pActInfo);\nprotected:\n\tCAnimationBlendRotate*\t\tm_pStand[2];\n\tCAnimationBlendRotate*\t\tm_pFire[2];\n\tCAnimationBlendDual*\t\tm_pRun[2];\n\n\tint\t\t\t\t\t\tm_nFire;//开枪\n\tfloat\t\t\t\t\tm_fAniCoolingTime;//后坐力动画冷却时间\n\tfloat\t\t\t\t\tm_fAniNowCoolingTime;//后坐力动画已冷却时间\n\tfloat\t\t\t\t\tm_fSudCoolingTime;//开枪声音冷却时间\n\tfloat\t\t\t\t\tm_fSudNowCoolingTime;//开枪声音已冷却时间\n\tfloat\t\t\t\t\tm_fEmitCoolingTime;//发射子弹冷却时间\n\tfloat\t\t\t\t\tm_fEmitNowCoolingTime;//发射子弹已冷却时间\n\n\tCUtilStr\t\t\t\t\tm_strMuzzleBone;\n\tint\t\t\t\t\t\tm_nMuzzleBone;\n\tCObjectMeshSkinned*\t\tm_pArmsMesh;\n\n\tvoid\t\t\t\t\t\tUpdateFire(float ifps);\n\tvirtual void\t\t\t\tOnFire(float fCoolingTime);\n\tvoid\t\t\t\t\t\tUpdateMuzzleTransform();\n\tCUtilStr\t\t\t\t\tm_strBulletName;\n\tCObjectParticles*\t\t\tm_pBulletParticle;\n\tCUtilStr\t\t\t\t\tm_strMuzzleEffect;\n\tCWorldEffect*\t\t\t\tm_pMuzzleEffect;\n\tmat4\t\t\t\t\t\tm_matMuzzleTransform;\n};\n\n"
  },
  {
    "path": "Test/FPSRoleLocal.cpp",
    "content": "#include \"FPSRoleLocal.h\"\n#include \"Creature.h\"\n#include \"Engine.h\"\n#include \"Input.h\"\n#include \"App.h\"\n#include \"ObjectMeshSkinned.h\"\n#include \"ActorBase.h\"\n#include \"Visualizer.h\"\n#include \"game.h\"\n#include \"RayControl.h\"\n#include \"Common.h\"\n#include \"ControlsApp.h\"\n\nCFPSRoleLocal::CFPSRoleLocal(void)\n{\n\tm_nCharMode = 0;\n\tm_nMuzzleBone = -1;\n\tm_nUpdateMove = 0;\n}\n\nCFPSRoleLocal::~CFPSRoleLocal(void)\n{\n\tdelete m_pActorBase;\n}\n\nint CFPSRoleLocal::Init( int nRoleID,const char* strCharFile )\n{\n\tCFPSRole::Init(nRoleID,strCharFile);\n\n\tm_pCreature->SetupBody(1,1);\n\tm_pCreature->SetHighShadow(1);\n\tSetupArms(2,0);\n\n\tm_pActorBase = new CActorBase();\n\tm_pActorBase->SetEnabled(1);\n\n\t//\n\t//m_vCameraOffset = vec3(0.0f,0.0f,1.8f);\n\tm_vCameraOffset = vec3(0.0f,0.0f,1.8f);\t\t\n\t//m_vGunOffset = vec3(0.0,0.0,-0.0f);\n\tm_vGunOffset = vec3(0.2,0.2,0.2f);\n\t//m_vRotateOffset = vec3(-0.076326f, 0.018540f, -1.725809f); // gua dian  weizhi);\n\tm_vRotateOffset = vec3(-0.076326f, 0.018540f, -1.725809f); // gua dian  weizhi);\n\n\treturn 1;\n}\n\nvoid CFPSRoleLocal::Update( float ifps )\n{\n\tif(m_pFire[0] && m_pFire[1])\n\t{\t\n\t\t//if(g_Engine.pApp->GetMouseButtonState(CApp::BUTTON_LEFT) || g_Engine.pApp->GetMouseButtonState(CApp::BUTTON_LDCLICK) )\n\t\tif(g_Engine.pInput->IsLBDownPress()||g_Engine.pInput->IsLBDBDown())\n\t\t{\n\t\t\tOpenFire();\t\t//굥\n\t\t}\n\t\telse\n\t\t{\t\t\t\n\t\t\tCloseFire();\n\t\t}\n\t}\n\t\n\tCFPSRole::Update(ifps);\n\tCRayControl::Instance().Update(m_matMuzzleTransform,vec3(0.0f,0.5f,-0.03f));\n}\n\nvoid CFPSRoleLocal::OnFire( float fCoolingTime )\n{\n\tCFPSRole::OnFire(fCoolingTime);\n}\n\nvoid CFPSRoleLocal::UpdateTransform( const MathLib::mat4 & matRotate )\n{\n\tmat4 m = Translate(m_pActorBase->GetPosition()+ m_vCameraOffset) * Translate(m_vGunOffset) * matRotate * Translate(m_vRotateOffset);\n\tm_pCreature->GetMainObjectMesh()->SetWorldTransform(m);\n}\n\nvoid CFPSRoleLocal::SetActorPosition( const vec3& vPosition )\n{\n\tm_pActorBase->SetPosition(vPosition);\n}\n\nvoid CFPSRoleLocal::SetActorDirection( const vec3& vDirection )\n{\n\tm_pActorBase->SetViewDirection(vDirection);\n}\n\nMathLib::vec3 CFPSRoleLocal::GetActorPosition()\n{\n\treturn m_pActorBase->GetPosition();\n}\n\nvoid CFPSRoleLocal::UpdateActor( float ifps )\n{\n\tm_pActorBase->Update(ifps);\n}"
  },
  {
    "path": "Test/FPSRoleLocal.h",
    "content": "#pragma once\n#include \"FPSRole.h\"\n\nclass CActorBase;\n\nclass CFPSRoleLocal : public CFPSRole\n{\npublic:\n\tCFPSRoleLocal(void);\n\tvirtual ~CFPSRoleLocal(void);\npublic:\n\tvirtual int  Init(int nRoleID, const char* strCharFile);\n\tvirtual void\tUpdate(float ifps);\n\tvoid\t\t\tUpdateTransform(const MathLib::mat4 & matRotate);\n\n\tMathLib::vec3 GetActorPosition();\n\tvoid\t\t\tSetActorPosition(const vec3& vPosition);\n\tvoid\t\t\tSetActorDirection(const vec3& vDirection);\n\tvoid\t\t\tUpdateActor(float ifps);\n\tMathLib::vec3 GetCameraOffset() { return m_vCameraOffset; }\nprotected:\n\tvirtual void OnFire(float fCoolingTime);\n\tint\t\t\t\tm_nCharMode;\n\tCActorBase*\t\tm_pActorBase;\n\tMathLib::vec3\t\tm_vCameraOffset;\n\tMathLib::vec3\t\tm_vGunOffset;\n\tMathLib::vec3\t\tm_vRotateOffset;\n};"
  },
  {
    "path": "Test/GameMain.cpp",
    "content": "#include \"GameMain.h\"\n#include \"GameProcess.h\"\n#include \"sys/SysControl.h\"\n#include \"Engine.h\"\n#include \"Game.h\"\n\nCGameMain::CGameMain(void)\n{\n\n}\n\nCGameMain::~CGameMain(void)\n{\n\n}\nint CGameMain::Init()\n{\n\tg_pSysControl->Init();\n\tm_pGameProcess = new CGameProcess();\n\treturn m_pGameProcess->Init();\n}\n\nint CGameMain::ShutDown()\n{\n\tg_pSysControl->Shutdown();\n\tm_pGameProcess->ShutDown();\n\tdelete m_pGameProcess;\n\treturn 1;\n}\n\nint CGameMain::Update()\n{\n\tfloat ifps = g_Engine.pGame->GetIFps();\n\tg_pSysControl->Update(ifps);\n\tm_pGameProcess->Update();\n\treturn 1;\n}\nint CGameMain::Render()\n{\n\tm_pGameProcess->Render();\n\treturn 1;\n}\n"
  },
  {
    "path": "Test/GameMain.h",
    "content": "#pragma once\nclass CGameProcess;\nclass CGameMain\n{\npublic:\n\tCGameMain(void);\n\tvirtual ~CGameMain(void); \npublic:\n\tint        Init();\n\tint        ShutDown();\n\tint        Update();\n\tint        Render();\t\n\tCGameProcess*\t\tm_pGameProcess;\n};"
  },
  {
    "path": "Test/GameProcess.cpp",
    "content": "#include \"GameProcess.h\"\n#include \"Object.h\"\n#include \"Engine.h\"\n#include \"World.h\"\n#include \"App.h\"\n#include \"ToolsCamera.h\"\n#include \"ControlsApp.h\"\n#include \"MathLib.h\"\n#include \"Game.h\"\n#include \"Editor.h\"\n#include \"Input.h\"\n#include \"BlueRayUtils.h\"\n#include \"World.h\"\n#include \"Action.h\"\n#include \"FPSRoleLocal.h\"\n#include \"StarControl.h\"\n#include \"RayControl.h\"\n#include \"CameraBase.h\"\n#include \"ActorBase.h\"\n#include \"sys/SysControl.h\"\n#include \"FileSystem.h\"\n#include \"RenderManager.h\"\n#include \"Texture.h\"\n\nusing namespace MathLib;\n\nCGameProcess::CGameProcess(void)\n{\n\n}\n\nCGameProcess::~CGameProcess(void)\n{\n\n}\nint CGameProcess::Init()\n{\n\tg_Engine.pFileSystem->CacheFilesFormExt(\"char\");\n\tg_Engine.pFileSystem->CacheFilesFormExt(\"node\");\n\tg_Engine.pFileSystem->CacheFilesFormExt(\"smesh\");\n\tg_Engine.pFileSystem->CacheFilesFormExt(\"sanim\");\n\n\tg_Engine.pWorld->LoadWorld(\"data/scene/terrain/test/test.world\");\n\t//g_Engine.pWorld->LoadWorld(\"data/scene/terrain/cj/cj.world\"); \n\tg_Engine.pControls->SetKeyPressFunc(KeyPress);\n\tg_Engine.pControls->SetKeyReleaseFunc(KeyRelease);\n\n\tm_pRole = new CFPSRoleLocal();\n\tm_pRole->Init(10001, \"data/role/hero/FpsRole/fps.char\");\t\t\n\n\tm_pRole->SetActorPosition(vec3(0, 0, 0));\t//设置角色初始位置。以门处作为原点，三维坐标系vec3是向量\n\tm_pSkillSystem = new CSkillSystem(this);\n\tm_pCameraBase = new CCameraBase();\n\tm_pCameraBase->SetEnabled(1);\n\tg_pSysControl->SetMouseGrab(1);\n\n\tm_pStarControl = new CStarControl();\n\tm_pRayControl = new CRayControl();\n\n\treturn 1;\n}\n\nint CGameProcess::ShutDown()\t\t\t//关闭游戏进程\n{\n\tdelete m_pRole;\n\tdelete m_pSkillSystem;\n\tdelete m_pCameraBase;\n\tdelete m_pStarControl;\n\tdelete m_pRayControl;\n\n\tDelAllListen();\n\treturn 0;\n}\n\nint CGameProcess::Update()\n{\n\tfloat ifps = g_Engine.pGame->GetIFps();\n\n\tif (g_Engine.pInput->IsKeyDown('1'))\n\t{\n\t\tCAction* pAction = m_pRole->OrceAction(\"attack02\");\n\t\tif (pAction)\n\t\t{\n\t\t\tpAction->SetupSkillThrow(vec3_zero, -1.0f, 2.0f);\n\t\t\tm_pRole->StopMove();\n\t\t}\n\t}\n\telse if (g_Engine.pInput->IsKeyDown('2'))\n\t{\n\t\tCAction* pAction = m_pRole->OrceAction(\"skill01\");\n\t\tif (pAction)\n\t\t{\n\t\t\tm_pRole->StopMove();\n\t\t}\n\t}\n\telse if (g_Engine.pInput->IsKeyDown('3'))\n\t{\n\t\tCAction* pAction = m_pRole->OrceAction(\"skill02\");\n\t\tif (pAction)\n\t\t{\n\t\t\tm_pRole->StopMove();\n\t\t\tCRoleBase* pTarget = NULL;\n\t\t\tfor (int i = 0; i < 20; i++)\n\t\t\t{\n\t\t\t\tfloat l = (m_vAIList[i]->GetPosition() - m_pRole->GetPosition()).length();\n\t\t\t\tif (l > 5.0f && l < 15.0f)\n\t\t\t\t{\n\t\t\t\t\tpTarget = m_vAIList[i];\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (pTarget)\n\t\t\t{\n\t\t\t\tCVector<int> vTarget;\n\t\t\t\tvTarget.Append(pTarget->GetRoleID());\n\t\t\t\tpAction->SetupSkillBulletTarget(vTarget);\n\t\t\t\tm_pRole->SetDirection((pTarget->GetPosition() - m_pRole->GetPosition()).normalize(), 1);\n\t\t\t}\n\t\t}\n\t}\n\telse if (g_Engine.pInput->IsKeyDown('4'))//多发子弹\n\t{\n\t\tCAction* pAction = m_pRole->OrceAction(\"skill02\");\n\t\tif (pAction)\n\t\t{\n\t\t\tm_pRole->StopMove();\n\t\t\tCVector<int> vTarget;\n\t\t\tfor (int i = 0; i < 20; i++)\n\t\t\t{\n\t\t\t\tfloat l = (m_vAIList[i]->GetPosition() - m_pRole->GetPosition()).length();\n\t\t\t\tif (l > 5.0f && l < 20.0f)\n\t\t\t\t{\n\t\t\t\t\tvTarget.Append(m_vAIList[i]->GetRoleID());\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!vTarget.Empty())\n\t\t\t{\n\t\t\t\tpAction->SetupSkillBulletTarget(vTarget);\n\t\t\t}\n\t\t}\n\t}\n\telse if (g_Engine.pInput->IsKeyDown('5'))\n\t{\n\t\tCAction* pAction = m_pRole->OrceAction(\"skill03\");\n\t\tif (pAction)\n\t\t{\n\t\t\tm_pRole->StopMove();\n\t\t\tCVector<vec3> vPos;\n\t\t\tpAction->SetupSkillTargetPoint(vPos);\n\t\t}\n\t}\n\telse if (g_Engine.pInput->IsKeyDown('6'))\n\t{\n\t\tCAction* pAction = m_pRole->OrceAction(\"skill06\");\n\t\tif (pAction)\n\t\t{\n\t\t\tm_pRole->StopMove();\n\t\t\tCVector<vec3> vPos;\n\t\t\tfor (int i = 0; i < 20; i++)\n\t\t\t{\n\t\t\t\tfloat l = (m_vAIList[i]->GetPosition() - m_pRole->GetPosition()).length();\n\t\t\t\tif (l > 5.0f && l < 20.0f)\n\t\t\t\t\tvPos.Append(m_vAIList[i]->GetPosition());\n\n\t\t\t}\n\t\t\tpAction->SetupSkillTargetPoint(vPos);\n\t\t}\n\t}\n\telse if (g_Engine.pInput->IsKeyDown('7'))\n\t{\n\t\tCAction* pAction = m_pRole->OrceAction(\"skill05\");\n\t\tif (pAction)\n\t\t{\n\t\t\tm_pRole->StopMove();\n\t\t\tCVector<int> vTarget;\n\t\t\tfor (int i = 0; i < 20; i++)\n\t\t\t{\n\t\t\t\tfloat l = (m_vAIList[i]->GetPosition() - m_pRole->GetPosition()).length();\n\t\t\t\tif (l > 5.0f && l < 20.0f)\n\t\t\t\t\tvTarget.Append(m_vAIList[i]->GetRoleID());\n\t\t\t}\n\t\t\tif (!vTarget.Empty())\n\t\t\t{\n\t\t\t\tpAction->SetupSkillBulletTarget(vTarget);\n\t\t\t}\n\t\t}\n\t}\n\telse if (g_Engine.pInput->IsKeyDown('8'))\n\t{\n\t\tCAction* pAction = m_pRole->OrceAction(\"skill07\");\n\t\tif (pAction)\n\t\t{\n\t\t\tm_pRole->StopMove();\n\t\t\tCVector<vec3> vPos;\n\t\t\tfor (int i = 0; i < 20; i++)\n\t\t\t{\n\t\t\t\tfloat l = (m_vAIList[i]->GetPosition() - m_pRole->GetPosition()).length();\n\t\t\t\tif (l > 5.0f && l < 20.0f)\n\t\t\t\t\tvPos.Append(m_vAIList[i]->GetPosition());\n\t\t\t}\n\t\t\tpAction->SetupSkillBulletPosition(vPos);\n\t\t}\n\t}\n\telse if (g_Engine.pInput->IsKeyDown('9'))\n\t{\n\t\tCAction* pAction = m_pRole->OrceAction(\"skill08\");\n\t\tif (pAction)\n\t\t{\n\t\t\tm_pRole->StopMove();\n\t\t\tpAction->SetupSkillBulletDirection(1);\n\t\t}\n\n\t}\n\telse if (g_Engine.pInput->IsKeyUp(CInput::KEY_ESC))\n\t{\n\t\tg_Engine.pApp->Exit();\n\t}\n\tif(g_Engine.pInput->IsLBDown())\n\t{\n\t\tvec3 p0,p1;\n\t\tBlueRay::GetPlayerMouseDirection(p0,p1);\n\t\tvec3 vRetPoint,vRetNormal;\n\t\tint nS = -1;\n\t\tg_Engine.pWorld->GetIntersection(p0,p1,CBRObject::MASK_SCENE,vRetPoint,vRetNormal,nS);\n\n\t\tif(-1 != nS)\n\t\t{\n\t\t\tm_pRole->MoveToPath(vRetPoint);\n\t\t}\n\t}\n\tfor(int i = 0;i < 20;i++)\n\t{\n\t\tif(!m_vAIList[i]->IsMoveing())\n\t\t{\n\t\t\tvec3 vPos = vec3(g_Engine.pGame->GetRandomFloat(-20.0f,20.0f),g_Engine.pGame->GetRandomFloat(-20.0f,20.0f),1.1f);\n\t\t\tm_vAIList[i]->MoveToPath(vPos);\n\t\t}\n\n\t\tm_vAIList[i]->Update(ifps);\n\t}\n\tif (g_Engine.pInput->IsLBDown())\t//shubiaozuojian\n\t{\n\t\tg_pSysControl->SetMouseGrab(1);\n\t\tm_pStarControl->Click();\n\t}\n\tif (g_Engine.pInput->IsKeyDown(CInput::KEY_ESC))\t\t\n\t{\n\t\tg_pSysControl->SetMouseGrab(0);\n\t}\n\tm_pCameraBase->SetMouseControls(g_pSysControl->GetMouseGrab());\n\tm_pCameraBase->Update(ifps);\n\n\tm_pRole->SetActorDirection(m_pCameraBase->GetViewDirection());\n\tm_pRole->UpdateActor(ifps);\n\tm_pCameraBase->SetPosition(m_pRole->GetActorPosition() + m_pRole->GetCameraOffset());\n\n\tvec3 x = m_pCameraBase->GetModelview().getRow3(0);\n\tvec3 y = m_pCameraBase->GetModelview().getRow3(1);\n\tvec3 z = m_pCameraBase->GetModelview().getRow3(2);\n\n\tmat4 r = mat4_identity;\n\n\tr.setColumn3(0, -x);\t//\n\tr.setColumn3(1, z);\t\t//\n\tr.setColumn3(2, y);\t\t//\n\n\tm_pRole->UpdateTransform(r);\n\n\tm_pRole->Update(ifps);\n\tm_pSkillSystem->Update(ifps);\n\n\tm_pStarControl->Update(m_pCameraBase->GetPosition(), m_pCameraBase->GetDirection());\n\n\treturn 1;\n}\n\nint CGameProcess::Render()\n{\n\t////test\n\t//m_pTestHero->Render();\n\t//g_Engine.pVisualizer->RenderLine3D(vec3(0.0f,0.0f,1.0f), vec3(10.0f,0.0f,1.0f), vec4(1.0f,0.0f,0.0f,1.0f));\n\t//g_Engine.pVisualizer->RenderLine3D(vec3(0.0f,0.0f,1.0f), vec3(0.0f,10.0f,1.0f), vec4(0.0f,1.0f,0.0f,1.0f));\n\t//g_Engine.pVisualizer->RenderLine3D(vec3(0.0f,0.0f,1.0f), vec3(0.0f,0.0f,11.0f), vec4(0.0f,0.0f,1.0f,1.0f));\n\t////~test\n\n\treturn 1;\n}\n\nint CGameProcess::KeyPress(unsigned int nKey)\n{\n\tif (nKey == 'w')\n\t{\n\t\t//g_Engine.pControls->SetState(CControls::STATE_RESTORE, 1);\n\t\tg_Engine.pControls->SetState(CControls::STATE_FORWARD, 1);\n\t}\n\telse if (nKey == 's')\n\t{\n\t\tg_Engine.pControls->SetState(CControls::STATE_RESTORE, 1);\n\t\tg_Engine.pControls->SetState(CControls::STATE_BACKWARD, 1);\n\t}\n\telse if (nKey == 'a')\n\t{\n\t\tg_Engine.pControls->SetState(CControls::STATE_RESTORE, 1);\n\t\tg_Engine.pControls->SetState(CControls::STATE_MOVE_LEFT, 1);\n\t}\n\telse if (nKey == 'd')\n\t{\n\t\tg_Engine.pControls->SetState(CControls::STATE_RESTORE, 1);\n\t\tg_Engine.pControls->SetState(CControls::STATE_MOVE_RIGHT, 1);\n\t}\n\telse if (nKey == 'q')\n\t{\n\t\tg_Engine.pControls->SetState(CControls::STATE_JUMP, 1);\n\t}\n\telse if (nKey == 'e')\n\t{\n\t\tg_Engine.pControls->SetState(CControls::STATE_CROUCH, 1);\n\t}\n\telse if (nKey == CApp::KEY_SHIFT)\n\t{\n\t\tg_Engine.pControls->SetState(CControls::STATE_RUN, 1);\n\t}\n\treturn 1;\n\n\n}\nint CGameProcess::KeyRelease(unsigned int nKey)\n{\n\tif (nKey == 'w')\n\t{\n\t\tg_Engine.pControls->SetState(CControls::STATE_RESTORE, 0);\n\t\tg_Engine.pControls->SetState(CControls::STATE_FORWARD, 0);\n\t}\n\telse if (nKey == 's')\n\t{\n\t\tg_Engine.pControls->SetState(CControls::STATE_RESTORE, 0);\n\t\tg_Engine.pControls->SetState(CControls::STATE_BACKWARD, 0);\n\t}\n\telse if (nKey == 'a')\n\t{\n\t\tg_Engine.pControls->SetState(CControls::STATE_RESTORE, 0);\n\t\tg_Engine.pControls->SetState(CControls::STATE_MOVE_LEFT, 0);\n\t}\n\telse if (nKey == 'd')\n\t{\n\t\tg_Engine.pControls->SetState(CControls::STATE_RESTORE, 0);\n\t\tg_Engine.pControls->SetState(CControls::STATE_MOVE_RIGHT, 0);\n\t}\n\telse if (nKey == 'q')\n\t{\n\t\tg_Engine.pControls->SetState(CControls::STATE_JUMP, 0);\n\t}\n\telse if (nKey == 'e')\n\t{\n\t\tg_Engine.pControls->SetState(CControls::STATE_CROUCH, 0);\n\t}\n\telse if (nKey == CApp::KEY_SHIFT)\n\t{\n\t\tg_Engine.pControls->SetState(CControls::STATE_RUN, 0);\n\t}\n\n\treturn 1;\n}\n\nCRoleBase* CGameProcess::GetRole(int nID)\n{\t\n\tfor(int i = 0;i < 20;i++)\n\t{\n\t\tif(m_vAIList[i]->GetRoleID() == nID)\n\t\t{\n\t\t\treturn m_vAIList[i];\n\t\t}\n\t}\n\n\treturn NULL;\n}\nCRoleBase* CGameProcess::GetRoleFormIndex(int nIndex)\n{\n\treturn m_vAIList[nIndex];\n\treturn NULL;\n}\n"
  },
  {
    "path": "Test/GameProcess.h",
    "content": "#pragma once\n#include \"MessageBase.h\"\n#include \"SkillSystem.h\"\n#include \"RoleBase.h\"\n#include \"Vector.h\"\n\n\nclass CFPSRoleLocal;\nclass CCameraBase;\nclass CActorBase;\nclass CStarControl;\nclass CRayControl;\nclass CGameProcess : public CMessageBase\n{\npublic:\n\tCGameProcess(void);\n\tvirtual ~CGameProcess(void);\n\tint        Init();\n\tint        ShutDown();\n\tint        Update();\n\tint        Render();\n\n\tCRoleBase*  GetRole(int nID);\n\tCRoleBase*  GetRoleFormIndex(int nIndex);\n\npublic:\n\tstatic int  KeyPress(unsigned int nKey);\n\tstatic int  KeyRelease(unsigned int nKey);\n\nprotected:\n\tCFPSRoleLocal*\tm_pRole;\n\tCSkillSystem*\t\tm_pSkillSystem;\n\tCCameraBase*\t\tm_pCameraBase;\n\tCStarControl*\t\tm_pStarControl;\n\tCRayControl*\t\tm_pRayControl;\nprivate:\n};"
  },
  {
    "path": "Test/HMDWrapper.cpp",
    "content": "#include \"HMDWrapper.h\"\n#include \"interface\\DeviceMgrInterface.h\"\n\nusing namespace MathLib;\n\nclass CLocalHMDWrapper : public CHMDWrapper\n{\npublic:\n\n\tCLocalHMDWrapper();\n\tvirtual ~CLocalHMDWrapper();\n\tvirtual void                Init();\n\tvirtual void                Update();\n\tvirtual void                Shutdown();\n\tvirtual void                SetUseHMD(int u);\n\tvirtual int                 GetUseHMD() const;\n\tvirtual const MathLib::vec3 &GetLocalDirection() const;\n\tvirtual const MathLib::vec3 &GetLocalUp() const;\nprivate:\n\n\tint     m_nUseHMD;\n\tint     m_nHaveHMD;\n\tvec3    m_vLocalDirection;\n\tvec3    m_vLocalUp;\n\n};\n\nCLocalHMDWrapper::CLocalHMDWrapper()\n{\n\tm_nUseHMD = 1;\n\tm_nHaveHMD = 0;\n\tm_vLocalDirection = forward_vector;\n\tm_vLocalUp = up_vector;\n}\n\nCLocalHMDWrapper::~CLocalHMDWrapper()\n{\n\n}\n\nvoid CLocalHMDWrapper::Init()\n{\n\tm_nHaveHMD = Interface::CDeviceInterface::GetInstance()->InitEnv();\n}\n\nvoid CLocalHMDWrapper::Update()\n{\n\tif (GetUseHMD())\n\t{\n\t\tInterface::SEulerAngles sData;\n\t\tInterface::CDeviceInterface::GetInstance()->GetHMDSensorRotation(sData);\n\t\tInterface::CDeviceInterface::GetInstance()->Run();\n\n\t\tvec3 dir = forward_vector;\n\t\tvec3 up = up_vector;\n\t\tquat rotate(-sData.fPitch * RAD2DEG, sData.fRoll * RAD2DEG, sData.fYaw * RAD2DEG);\n\n\t\tm_vLocalDirection = rotate * dir;\n\t\tm_vLocalUp = rotate * up;\n\t}\n}\n\nvoid CLocalHMDWrapper::Shutdown()\n{\n\tInterface::CDeviceInterface::GetInstance()->Destroy();\n}\n\nvoid CLocalHMDWrapper::SetUseHMD(int u)\n{\n\tm_nUseHMD = u;\n}\n\nint CLocalHMDWrapper::GetUseHMD() const\n{\n\treturn m_nUseHMD && m_nHaveHMD;\n}\n\nconst MathLib::vec3 & CLocalHMDWrapper::GetLocalDirection() const\n{\n\treturn m_vLocalDirection;\n}\n\nconst MathLib::vec3 & CLocalHMDWrapper::GetLocalUp() const\n{\n\treturn m_vLocalUp;\n}\n\nCLocalHMDWrapper LocalHMDWrapper;\nCHMDWrapper *g_pHMD = &LocalHMDWrapper;"
  },
  {
    "path": "Test/HMDWrapper.h",
    "content": "#ifndef __HMD_WRAPPER_H__\n#define __HMD_WRAPPER_H__\n\n#include \"MathLib.h\"\n\n\nclass CHMDWrapper\n{\n\npublic:\n\n\tCHMDWrapper() {}\n\tvirtual ~CHMDWrapper() {}\n\tvirtual void                Init() = 0;\n\tvirtual void                Update() = 0;\n\tvirtual void                Shutdown() = 0;\n\n\tvirtual void                SetUseHMD(int u) = 0;\n\tvirtual int                 GetUseHMD() const = 0;\n\n\tvirtual const MathLib::vec3 &GetLocalDirection() const = 0;\n\tvirtual const MathLib::vec3 &GetLocalUp() const = 0;\n\nprivate:\n\n};\n\nextern CHMDWrapper *g_pHMD;\n\n#endif"
  },
  {
    "path": "Test/MathLib.h",
    "content": "#pragma once\n//MathLib.h\n\n#ifndef _MATHLIB_\n#define _MATHLIB_\n\n//Make some change to check if new branch is created.\n\n#include <iostream>\n#include <cmath>\n#include <iomanip>\n\nusing namespace std;\n\n//------------------------------------------------------------\ndouble integral(double(*f)(double), double a = 0, double b = 1, double tol = 1e-5, long n = 50);\n//------------------------------------------------------------\ndouble limit(double(*f)(double), double a = 0, double b = 1, double tol = 1e-5, long n = 1);\n//------------------------------------------------------------\ndouble diff(double(*f)(double), double a = 0, double b = 1, double tol = 1e-5, long n = 1);\n//------------------------------------------------------------\n#define PLOT_WIDTH 60\ndouble plot(double x[], int n, char c = '*');\n//------------------------------------------------------------\ndouble fplot(double(*f)(double), double a = 0, double b = 1, int n = 25, char c = '*');\n//------------------------------------------------------------\nint subset(double *a, int i, int j);\n\nvoid subsort(double *a, int i, int j);\n//------------------------------------------------------------\nvoid shellsort(double *a, int n);\n//------------------------------------------------------------\nvoid cmpsort(double *a, int n);\n//------------------------------------------------------------\nvoid rollsort(double *a, int n);\n//------------------------------------------------------------\n\nint sgn(double x);\n\nint sign(double x);\n//------------------------------------------------------------\n\n#define FORMAT_STD 1\n#define FORMAT_EXP 0\n#define MATHLIB_PI 3.14159265\n\nclass complex\n{\nprotected:\n\tdouble real;\n\tdouble imag;\n\tdouble len;\n\tdouble arg;\npublic:\n\tcomplex(double r = 0, double i = 0, int format = FORMAT_STD);\n\n\tvoid set(double r = 0, double i = 0, int format = FORMAT_STD);\n\tvoid output(int format = FORMAT_STD);\n\tvoid input(int format = FORMAT_STD);\n\n\tfriend void set(complex &c, double r = 0, double i = 0, int format = FORMAT_STD);\n\tfriend void output(complex &c, int format = FORMAT_STD);\n\tfriend void input(complex &c, int format = FORMAT_STD);\n\tfriend ostream & operator <<(ostream &out, complex &c);\n\tfriend istream & operator <<(istream &in, complex &c);\n\n\tdouble getreal();\n\tdouble getimag();\n\tdouble getlen();\n\tdouble getarg();\n\n\toperator double() { return sgn(real)*len; }\n\tcomplex operator ~();\n\tcomplex operator !();\n\tcomplex operator -();\n\tcomplex operator +();\n\n\tfriend complex operator +(const complex &, const complex &);\n\tfriend complex operator -(const complex &, const complex &);\n\tfriend complex operator *(const complex &, const complex &);\n\tfriend complex operator /(const complex &, const complex &);\n\tfriend complex operator &(const complex &, const complex &);\n\tfriend complex operator |(const complex &, const complex &);\n\tfriend complex operator ^(const complex &, const double);\n\n\tfriend complex operator +=(complex &, const complex &);\n\tfriend complex operator -=(complex &, const complex &);\n\tfriend complex operator *=(complex &, const complex &);\n\tfriend complex operator /=(complex &, const complex &);\n\tfriend complex operator &=(complex &, const complex &);\n\tfriend complex operator |=(complex &, const complex &);\n\tfriend complex operator ^=(complex &, const double);\n\n\tfriend int operator ==(const complex &, const complex &);\n\tfriend int operator !=(const complex &, const complex &);\n\tfriend int operator <(const complex &, const complex &);\n\tfriend int operator <=(const complex &, const complex &);\n\tfriend int operator >(const complex &, const complex &);\n\tfriend int operator >=(const complex &, const complex &);\n\n\n\tfriend complex exp(const complex &);\n\tfriend complex log(const complex &);\n\tfriend complex sin(const complex &);\n\tfriend complex cos(const complex &);\n\tfriend double arg(const complex &);\n\tfriend double abs(const complex &);\n}\n#endif\t\t//_MATHLIB_"
  },
  {
    "path": "Test/MoveDummy.cpp",
    "content": "#include \"MoveDummy.h\"\n#include \"ObjectDummy.h\"\n#include \"BodyDummy.h\"\n#include \"ShapeCapsule.h\"\n\n#include \"Utils.h\"\n#include \"Engine.h\"\n#include \"Physics.h\"\n#include \"Game.h\"\n#include \"World.h\"\n\n#define MOVE_DUMMY_IFPS\t\t\t(1.0f/100.0f)\t\t\n#define MOVE_DUMMY_CLAMP           89.9f\n#define MOVE_DUMMY_COLLISIONS\t\t1\n\nusing namespace MathLib;\n\nCMoveDummy::CMoveDummy()\t\t//캯\n{\n\tm_pObject = new CObjectDummy();\t\t//һʵ\n\tm_pDummy = new CBodyDummy();\n\tm_pShape = new CShapeCapsule(0.5f, 1.0f);\n\n\tSetCollisionMask(1);\n\tClear();\n}\n\nCMoveDummy::~CMoveDummy()\n{\n\tm_pDummy->SetObject(NULL);\n\tSAFE_DELETE(m_pObject);\n\tSAFE_DELETE(m_pDummy);\n}\n\nvoid CMoveDummy::SetCollision(int c)\n{\n\tm_nCollision = c;\n}\n\nint CMoveDummy::GetCollision() const\n{\n\treturn m_nCollision;\n}\n\nvoid CMoveDummy::SetCollisionMask(int m)\n{\n\tm_pShape->SetCollisionMask(m);\n}\n\nint CMoveDummy::GetCollisionMask() const\n{\n\treturn m_nCollisionMask;\n}\n\nvoid CMoveDummy::SetGround(int g)\n{\n\tm_nGround = g;\n}\n\nint CMoveDummy::GetGround() const\n{\n\treturn m_nGround;\n}\n\nvoid CMoveDummy::SetCeiling(int c)\n{\n\tm_nCeiling = c;\n}\n\nint CMoveDummy::GetCeiling() const\n{\n\treturn m_nCeiling;\n}\n\nvoid CMoveDummy::SetPosition(const MathLib::vec3& p)\n{\n\tm_vPosition = p;\n}\n\nconst MathLib::vec3& CMoveDummy::GetPosition() const\n{\n\treturn m_vPosition;\n}\n\nvoid CMoveDummy::SetCollisionRadius(float radius)\n{\n\tif (!Compare(m_pShape->GetRadius(), radius))\n\t{\n\t\tm_pDummy->SetPreserveTransform(Mat4(Translate(m_vUp * (radius - m_pShape->GetRadius()))) * m_pDummy->GetTransform());\n\t\tm_pShape->SetRadius(radius);\n\t}\n\n\tUpdate_Bounds();\n}\n\n\nfloat CMoveDummy::GetCollisionRadius() const\n{\n\treturn m_pShape->GetRadius();\n}\n\nvoid CMoveDummy::SetCollisionHeight(float height)\n{\n\tif (!Compare(m_pShape->GetHeight(), height))\n\t{\n\t\tm_pDummy->SetPreserveTransform(Mat4(Translate(m_vUp * (height - m_pShape->GetHeight()) * 0.5f)) * m_pDummy->GetTransform());\n\t\tm_pShape->SetHeight(height);\n\t}\n\n\tUpdate_Bounds();\n}\nfloat CMoveDummy::GetCollisionHeight() const\n{\n\treturn m_pShape->GetHeight();\n}\n\nvoid CMoveDummy::SetUp(const MathLib::vec3& u)\n{\n\tm_vUp = u;\n}\n\nconst MathLib::vec3& CMoveDummy::GetUp() const\n{\n\treturn m_vUp;\n}\nvoid CMoveDummy::SetEnabled(int e)\n{\n\tm_nEnabled = e;\n}\n\nfloat CMoveDummy::GetCollisionHeight() const\n{\n\treturn m_pShape->GetHeight();\n}\nvoid CMoveDummy::SetUp(const MathLib::vec3& u)\n{\n\tm_vUp = u;\n}\n\nconst MathLib::vec3& CMoveDummy::GetUp() const\n{\n\treturn m_vUp;\n}\nvoid CMoveDummy::SetEnabled(int e)\n{\n\tm_nEnabled = e;\n}\n\nint CMoveDummy::GetEnabled() const\n{\n\treturn m_nEnabled;\n}\n\nvoid CMoveDummy::SetVelocity(const MathLib::vec3& v)\n{\n\tm_vVelocity = v;\n}\n\nconst MathLib::vec3& CMoveDummy::GetVelocity() const\n{\n\treturn m_vVelocity;\n}\n\nvoid CMoveDummy::SetMaxVelocity(float v)\n{\n\tm_fMaxVelocity = v;\n}\nfloat CMoveDummy::GetMaxVelocity() const\n{\n\treturn m_fMaxVelocity;\n}\n\nvoid CMoveDummy::SetAcceleration(float a)\n{\n\tm_fAcceleration = a;\n}\nfloat CMoveDummy::GetAcceleration() const\n{\n\treturn m_fAcceleration;\n}\nfloat CMoveDummy::GetMaxThrough() const\n{\n\treturn m_fMaxThrough;\n}\nvoid CMoveDummy::Clear()\n{\n\tSetVelocity(vec3_zero);\n\tSetMaxVelocity(2.5f);\n\tSetAcceleration(15.0f);\n\tSetCollision(1);\n\tSetCollisionMask(1);\n\tSetGround(0);\n\tSetCeiling(0);\n\tSetPosition(vec3_zero);\n\tSetUp(vec3(0.0f, 0.0f, 1.0f));\n\tSetEnabled(1);\n\tm_pDummy->SetEnabled(1);\n\tm_pObject->SetBody(NULL);\n\tm_pObject->SetBody(m_pDummy);\n\n\tm_pShape->SetBody(NULL);\n\tm_pShape->SetBody(m_pDummy);\n\tm_pShape->SetExclusionMask(2);\n\n\tm_pObject->SetWorldTransform(Get_Body_Transform());\n\n\tSetCollisionRadius(0.5f);\n\tSetCollisionHeight(1.0f);\n\n\n\tSetMaxThrough(0.4f);\n\n\tm_vecContacts.Clear();\n\n}\nint CMoveDummy::Update(float fIfps, const MathLib::vec3& vDirection)\n{\n\treturn Update(fIfps, vDirection, m_vPosition);\n}\nint CMoveDummy::Update(float fIfps, const MathLib::vec3& vDirection, const MathLib::vec3& pos)\n{\n\tSetPosition(pos);\n\n\tvec3 vOldPosition = m_vPosition;\n\n\tfloat fVVelocity = Dot(m_vVelocity, m_vUp);\n\tfloat fHVelocity = CMathCore::Sqrt(CMathCore::Abs(m_vVelocity.length2() - fVVelocity*fVVelocity));\n\n\tm_vVelocity = m_vUp * fVVelocity + vDirection * fHVelocity;\n\t// time\n\tfloat time = fIfps;// * g_Engine.pPhysics->GetScale();\n\n\t// penetration tolerance\n\tfloat penetration = g_Engine.pPhysics->GetPenetrationTolerance();\n\tfloat penetration_2 = penetration * 2.0f;\n\t// clear collision flags\n\tif (GetCollision())\n\t{\n\t\tm_nGround = 0;\n\t\tm_nCeiling = 0;\n\t}\n\n\t// frozen linear velocity\n\tfloat frozen_velocity = g_Engine.pPhysics->GetFrozenLinearVelocity();\n\n\n\t// movement\n\tdo\n\t{\n\t\t// adaptive time step\n\t\tfloat ifps = Min(time, MOVE_DUMMY_IFPS);\n\t\ttime -= ifps;\n\t\t// integrate velocity\n\t\tm_vVelocity += vDirection * (m_fAcceleration * ifps);\n\t\tm_vVelocity += g_Engine.pPhysics->GetGravity() * ifps;\n\n\t\tfVVelocity = Dot(m_vVelocity, m_vUp);\n\t\tfHVelocity = CMathCore::Sqrt(m_vVelocity.length2() - fVVelocity*fVVelocity);\n\n\t\tm_vVelocity = m_vUp * fVVelocity + vDirection * m_fMaxVelocity;\n\n\n\n\n\n\n\t}\n\twhile (time > EPSILON);\n\tif (GetCollision())\n\t{\n\t\tint nSurface = -1;\n\t}\n\n\tVec3 vStart = m_vPosition + vDirection * GetCollisionRadius() + Vec3(m_vUp * (m_pShape->GetHHeight() + m_pShape->GetRadius()));\n\tVec3 vEnd = vOldPosition + Vec3(m_vUp * (m_pShape->GetHHeight() + m_pShape->GetRadius()));\n\n\tCBRObject *p = g_Engine.pWorld->GetIntersection(vStart, vEnd, 2, nSurface);\n\n\tif (p != NULL)\n\t{\n\t\tm_vPosition = vOldPosition;\n\t}\n\t// current position\n\tm_pObject->SetWorldTransform(Get_Body_Transform());\n\n\tfor (int j = 0; j < m_vecContacts.Size(); j++)\n\t{\n\t\tconst CShape::Contact &c = m_vecContacts[j];\n\t\tif (Dot(vDirection, c.normal) < -0.9f)\n\t\t{\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\treturn 1;\n}\nvoid CMoveDummy::Update_Bounds()\n{\n\tfloat radius = m_pShape->GetRadius();\n\tfloat hheight = m_pShape->GetHHeight();\n\tm_BoundBox.Set(vec3(-radius, -radius, 0.0f), vec3(radius, radius, (radius + hheight) * 2.0f));\n\tm_BoundSphere.Set(vec3(0.0f, 0.0f, radius + hheight), radius + hheight);\n}\n\nMathLib::Mat4 CMoveDummy::Get_Body_Transform() const\n{\n\tVec3 center = m_vPosition + Vec3(m_vUp * (m_pShape->GetHHeight() + m_pShape->GetRadius()));\n\treturn Translate(center);\n}\n"
  },
  {
    "path": "Test/MoveDummy.h",
    "content": "#pragma once\n#ifndef __MOVE_DUMMY_H__\n#define __MOVE_DUMMY_H__\n\n#include \"MathLib.h\"\n#include \"Bounds.h\"\n#include \"Vector.h\"\n#include \"Shape.h\"\n\nclass   CObjectDummy;\nclass   CBodyDummy;\nclass   CShapeCapsule;\n\nclass CMoveDummy\n{\npublic:\n\n\tCMoveDummy();\n\t~CMoveDummy();\n\tvoid\tSetCollision(int c);\n\tint\t\tGetCollision() const;\n\n\tvoid\tSetCollisionMask(int m);\n\tint\t\tGetCollisionMask() const;\n\tvoid\tSetGround(int g);\n\tint\t\tGetGround() const;\n\tvoid\tSetCeiling(int c);\n\tint\t\tGetCeiling() const;\n\tvoid\tSetPosition(const MathLib::vec3& p);\n\tconst MathLib::vec3&    GetPosition() const;\n\n\tvoid\tSetCollisionRadius(float radius);\n\tfloat\tGetCollisionRadius() const;\n\n\tvoid\tSetCollisionHeight(float height);\n\tfloat\tGetCollisionHeight() const;\n\n\tvoid                    SetUp(const MathLib::vec3& u);\n\tconst MathLib::vec3&    GetUp() const;\n\n\tvoid\tSetEnabled(int e);\n\tint\t\tGetEnabled() const;\n\n\tvoid                    SetVelocity(const MathLib::vec3& v);\n\tconst MathLib::vec3&    GetVelocity() const;\n\n\tvoid\tSetMaxVelocity(float v);\n\tfloat\tGetMaxVelocity() const;\n\n\tvoid\tSetAcceleration(float a);\n\tfloat\tGetAcceleration() const;\n\n\tvoid\tSetMaxThrough(float d);\n\tfloat\tGetMaxThrough() const;\n\n\tvoid\tClear();\n\n\tint\t\tUpdate(float fIfps, const MathLib::vec3& vDirection);\n\tint\t\tUpdate(float fIfps, const MathLib::vec3& vDirection, const MathLib::vec3& pos);\n\n\nprivate:\n\tvoid\t\t\tUpdate_Bounds();\n\tMathLib::Mat4\tGet_Body_Transform() const;\n\nprivate:\n\n\tint                         m_nCollision;\n\tint                         m_nCollisionMask;\n\tint                         m_nGround;\n\tint                         m_nCeiling;\n\tCObjectDummy*               m_pObject;      // dummy object\n\tCBodyDummy*                 m_pDummy;       // dummy body\n\tCShapeCapsule*              m_pShape;       // collision shape\n\tMathLib::vec3               m_vPosition;\n\tMathLib::vec3               m_vUp;\n\tint                         m_nEnabled;\n\tMathLib::vec3               m_vVelocity;\n\tfloat                       m_fMaxVelocity;\n\tfloat                       m_fAcceleration;\n\tfloat                       m_fMaxThrough;\n\n\tCBoundBox                   m_BoundBox;\n\tCBoundSphere                m_BoundSphere;\n\tCVector<CShape::Contact>    m_vecContacts;\n};\n\n#endif"
  },
  {
    "path": "Test/RayControl.cpp",
    "content": "#include \"RayControl.h\"\n#include \"ObjectParticles.h\"\n#include \"Engine.h\"\n#include \"Game.h\"\n#include \"Object.h\"\n#include \"Player.h\"\n#include \"Common.h\"\n#include \"App.h\"\n\nCRayControl::CRayControl(void)\n{\n\tm_pStarNormal = NULL;\n\tInit();\n}\n\nCRayControl::~CRayControl(void)\n{\n\tg_Engine.pGame->RemoveNode(m_pStarNormal);\n}\n\nint CRayControl::Init()\n{\n\tm_pStarNormal = (CObjectParticles*)g_Engine.pGame->LoadNode(\"data/StarControl/ray_line.node\");\n\n\tif (!m_pStarNormal)\n\t\treturn 0;\n\n\tm_pStarNormal->GetTracker(TRACKER_CUSTOM)->SetTrackValue(0, 0, vec4(1.0f, 1.0f, 1.0f, 0.5f));\n\n\treturn 1;\n}\nvoid CRayControl::Update(const mat4& matTransform, const vec3 & vOffset)\n{\n\tm_pStarNormal->SetWorldTransform(matTransform*Translate(vOffset));\n}\n\nint CRayControl::isEnable()\n{\n\treturn m_pStarNormal->IsEnabled();\n}\n\nvoid CRayControl::SetEnable(int nEnable)\n{\n\tm_pStarNormal->SetEnabled(nEnable);\n}\n\nvoid CRayControl::SetColor(const vec4& vColor)\n{\n\tm_pStarNormal->SetObjectColor(vColor);\n}\n"
  },
  {
    "path": "Test/RayControl.h",
    "content": "#pragma once\n#include \"MathLib.h\"\n#include \"Singleton.h\"\n\n\nclass CObjectParticles;\nusing namespace MathLib;\n\nclass CRayControl : public CSingleton<CRayControl>\n{\npublic:\n\tCRayControl(void);\n\t~CRayControl(void);\npublic:\n\tint\t\tInit();\n\tvoid\t\tUpdate(const mat4& matTransform, const vec3 & vOffset = vec3_zero);\n\tint\t\tisEnable();\n\tvoid\t\tSetEnable(int nEnable);\n\tvoid\t\tSetColor(const vec4& vColor);\nprotected:\n\tCObjectParticles*\t  m_pStarNormal;\n};"
  },
  {
    "path": "Test/RoleBase.cpp",
    "content": "#include \"RoleBase.h\"\n#include \"Creature.h\"\n#include \"interface.h\"\n#include \"Action.h\"\n#include \"Utils.h\"\n#include \"NavigationFinder.h\"\n#include \"Engine.h\"\n#include \"World.h\"\n#include \"Object.h\"\n#include \"Avatar.h\"\n\nCRoleBase::CRoleBase(void)\n{\n\tm_pCreature = NULL;\n\tm_pActionComplete = NULL;\n\tm_nOrceDirection = 0;\n\tm_nMoveing = 0;\n\tm_fMoveSpeed = 6.0f;\n\tm_nMoveToType = 0;\n\tm_nUpdateMove = 1;\n}\n\nCRoleBase::~CRoleBase(void)\n{\n\tSAFE_DELETE(m_pActionComplete);\n\tSAFE_DELETE(m_pCreature);\n\tSAFE_DELETE(m_pPathFind);\n}\n\n//ʼɫ\nint CRoleBase::Init(int nRoleID, const char* strCharFile)\n{\n\tif (m_pCreature)return 1;\n\n\tm_nMoveing = 1;\n\tm_nRoleID = nRoleID;\n\tm_pCreature = new CCreature();\n\tm_pActionComplete = MakeInterface(this, &CRoleBase::OnActionMsg);\n\tm_pCreature->SetActionComplete(m_pActionComplete);\n\n\tm_pCreature->Load(strCharFile);\n\tm_pCreature->SetupBody(1, 0);\n\tm_pCreature->SetPosition(vec3_zero);\n\tm_pCreature->SetDirection(vec3(0.0f, -1.0f, 0.0f));\n\tm_pCreature->SetRoleID(nRoleID);\n\n\tm_AngleYaw.SetDirection(vec3(0.0f, -1.0f, 0.0f));\n\n\tm_pPathFind = new CNavigationFinder();\n\tm_pPathFind->SetMode(CNavigationFinder::MODE_PATHFIND_STRAIGHT);\n\n\treturn 1;\n}\n\nvoid CRoleBase::Update(float ifps)\n{\n\tif (NULL == m_pCreature)return;\n\n\tif (m_nUpdateMove)\n\t{\n\t\tm_AngleYaw.Update(ifps);\n\t\tUpdateMove(ifps);\n\t\tm_pCreature->SetDirection(m_AngleYaw.GetForwardDirection(), m_nOrceDirection);\n\t}\n\n\tm_pCreature->Update();\n}\n\n\nint CRoleBase::OnActionMsg(void* pVoid)\n{\n\t_ActionCallback* pInfo = (_ActionCallback*)pVoid;\n\n\tswitch (pInfo->nMsgID)\n\t{\n\tcase 0:\n\t{\n\t\tOnKeyFrame((_ActionCallback_KeyFrame*)pVoid);\n\t}break;\n\tcase 1:\n\t{\n\t\tOnActionComplete((_ActionCallback_Complete*)pVoid);\n\t}break;\n\t}\n\n\treturn 1;\n}\n//ؼ֡ص\nvoid CRoleBase::OnKeyFrame(_ActionCallback_KeyFrame* pKeyInfo)\n{\n\n}//ɻص\nvoid CRoleBase::OnActionComplete(_ActionCallback_Complete* pActInfo)\n{\n\tif (NULL == m_pCreature)return;\n\n\tif (m_nMoveing)\n\t{\n\t\tm_pCreature->PlayAction(\"run\");\n\t}\n\telse\n\t{\n\t\tm_pCreature->PlayAction(\"stand\");\n\t}\n}\nCAction* CRoleBase::PlayAction(const char* szName, int nLoop, float fCorrectingTime /*= 1.0f*/)\n{\n\tif (NULL == m_pCreature)return NULL;\n\n\tif (1 == m_pCreature->PlayAction(szName, nLoop, fCorrectingTime))\n\t{\n\t\treturn m_pCreature->GetNowAction();\n\t}\n\n\treturn NULL;\n}\nCAction* CRoleBase::OrceAction(const char* szName, int nLoop, float fCorrectingTime /*= 1.0f*/)\n{\n\tif (NULL == m_pCreature)return NULL;\n\n\tif (1 == m_pCreature->OrceAction(szName, nLoop, fCorrectingTime))\n\t{\n\t\treturn m_pCreature->GetNowAction();\n\t}\n\n\treturn NULL;\n}\nvoid CRoleBase::SetPosition(const vec3& vPosition, int nOrce /*= 0*/)\n{\n\tif (NULL == m_pCreature)return;\n\n\tm_pCreature->SetPosition(vPosition, nOrce);\n}\n\nvoid CRoleBase::SetDirection(const vec3& vDirection, int nOrce /*= 0*/)\n{\n\tif (NULL == m_pCreature)return;\n\tif (m_pCreature->GetNowAction()->GetLockMove() && !nOrce)return;\n\n\tm_nOrceDirection = nOrce;\n\tm_AngleYaw.SetDirection(vDirection);\n}\nvoid CRoleBase::SetDirectionTo(const vec3& vDirection, int nOrce /*= 0*/)\n{\n\tif (NULL == m_pCreature)return;\n\tif (m_pCreature->GetNowAction()->GetLockMove() && !nOrce)return;\n\n\tm_nOrceDirection = nOrce;\n\tm_AngleYaw.SetDestDirection(vDirection);\n}\n\nint CRoleBase::MoveTo(const vec3& vPosition)\n{\n\tif (NULL == m_pCreature)return -1;\n\tif (m_pCreature->GetNowAction()->GetLockMove())return -1;\n\n\tm_vStartPathPosition = m_vNowPathPosition = m_pCreature->GetPosition();\n\tm_nMoveing = 1;\n\tm_nMoveToType = 0;\n\tm_vDestPathPosition = vPosition;\n\tm_pCreature->PlayAction(\"run\", 1);\n\n\treturn 1;\n}\n\nint CRoleBase::MoveToPath(const vec3& vPosition)\n{\n\tif (NULL == m_pCreature)return -1;\n\tif (m_pCreature->GetNowAction()->GetLockMove())return -1;\n\n\tm_pPathFind->Create(m_pCreature->GetPosition(), vPosition, 0);\n\n\tif (m_pPathFind->GetReached() || m_pPathFind->GetNumPoints() >= 2)\n\t{\n\t\tm_nMoveing = 1;\n\t\tm_nMoveToType = 1;\n\t\tm_nNowPathPoint = 0;\n\t\tm_vNowPathPosition = m_pCreature->GetPosition();\n\t\tm_pCreature->PlayAction(\"run\", 1);\n\t\treturn 1;\n\t}\n\n\treturn 0;\n\n}\nvoid CRoleBase::StopMove()\n{\n\tm_nMoveing = 0;\n\tm_pCreature->PlayAction(\"stand\", 1);\n}\n\nvoid CRoleBase::StopMove(const vec3& vPosition)\n{\n\tm_nMoveing = 0;\n\tSetPosition(vPosition, 1);\n\tm_pCreature->PlayAction(\"stand\", 1);\n}\n\nvoid CRoleBase::UpdateMove(float ifps)\n{\n\tif (!m_nMoveing)return;\n\tvec3 vDir = vec3(0.0f, 1.0f, 0.0f);\n\tif (m_nMoveToType == 0)\n\t{\n\t\tvDir = (m_vDestPathPosition - m_vStartPathPosition).normalize();\n\t\tm_vNowPathPosition += vDir * m_fMoveSpeed * ifps;\n\t\tMathLib::vec3 d = (m_vDestPathPosition - m_vNowPathPosition);\n\t\td.z = 0.0f;\n\t\tint bTest = MathLib::Dot(d.normalize(), vec3(vDir.x, vDir.y, 0.0f)) < 0.0f;\n\t\tif (bTest || vDir.z >= 0.95f)\n\t\t{\n\t\t\tif (bTest && d.length() > 0.10f)\n\t\t\t{\n\t\t\t\tm_vNowPathPosition = m_vDestPathPosition;\n\t\t\t}\n\n\t\t\tStopMove();\n\t\t}\n\t}\n\n}"
  },
  {
    "path": "Test/RoleBase.h",
    "content": "#pragma once\n#include \"MathLib.h\"\n#include \"UtilStr.h\"\n\nclass CCreature;\nclass CAction;\nclass CInterfaceBase;\nclass CNavigationFinder;\n\nstruct _ActionCallback_KeyFrame;\nstruct _ActionCallback_Complete;\n\nclass CNode;\n\nclass CRoleBase\n{\npublic:\n\tCRoleBase(void);\n\tvirtual ~CRoleBase(void);\n\n\tvirtual int\tInit(int nRoleID, const char* strCharFile);\n\tvirtual void\tUpdate(float ifps);\n\n\tint\t\tGetRoleID() { return m_nRoleID; }\n\tvoid\t\tSetRoleID(int nID) { m_nRoleID = nID; }\n\n\tCAction*\tPlayAction(const char* szName, int nLoop = 0, float fCorrectingTime = 1.0f);\n\tCAction*\tOrceAction(const char* szName, int nLoop = 0, float fCorrectingTime = 1.0f);\n\n\tvoid\t\tSetPosition(const vec3& vPosition, int nOrce = 0);\n\tvoid\t\tSetDirection(const vec3& vDirection, int nOrce = 0);\n\tvoid\t\tSetDirectionTo(const vec3& vDirection, int nOrce = 0);\n\n\tvirtual int\tMoveTo(const vec3& vPosition);\n\tvirtual int\tMoveToPath(const vec3& vPosition);\n\tvirtual void\tStopMove();\n\tvirtual void\tStopMove(const vec3& vPosition);\n\n\tint\t\tIsMoveing() { return m_nMoveing; }\n\n\tvec3\t\tGetDirection();\n\tvec3\t\tGetPosition();\n\n\tfloat\tGetRoleRadius();\n\tfloat\tGetRoleHeight();\nprotected:\n\tvoid\t\tUpdateMove(float ifps);\n\tvec3\t\tTestRoleZ(vec3& vPosition);\n\tCNode*\tGetAssemblyNode(const CUtilStr& strAssembly);\n\tCNode*\tGetAssemblyNode(int nAssembly);\n\n\tCCreature*\tm_pCreature;\n\tint\t\t\tm_nRoleID;\n\tint\t\t\tm_nOrceDirection;\n\n\tCAngleYaw\tm_AngleYaw;\n\tCNavigationFinder* m_pPathFind;\n\tint\t\t\t\t m_nMoveing;\n\tint\t\t\t\t m_nMoveToType;\n\tfloat\t\t\t m_fMoveSpeed;\n\tint\t\t\t\t m_nNowPathPoint;\n\tvec3\t\t\t\t m_vNowPathPosition;\n\tvec3\t\t\t\t m_vStartPathPosition;\n\tvec3\t\t\t\t m_vDestPathPosition;\n\n\tint\t\t\t     m_nUpdateMove;\nprotected:\n\tCInterfaceBase*\tm_pActionComplete;\n\tint\t\t\t\tOnActionMsg(void* pVoid);\n\tvirtual void\t\tOnKeyFrame(_ActionCallback_KeyFrame* pKeyInfo);\n\tvirtual void\t\tOnActionComplete(_ActionCallback_Complete* pActInfo);\n};"
  },
  {
    "path": "Test/Test.vcxproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project DefaultTargets=\"Build\" ToolsVersion=\"15.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\r\n  <ItemGroup Label=\"ProjectConfigurations\">\r\n    <ProjectConfiguration Include=\"Debug|Win32\">\r\n      <Configuration>Debug</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Release|Win32\">\r\n      <Configuration>Release</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Debug|x64\">\r\n      <Configuration>Debug</Configuration>\r\n      <Platform>x64</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Release|x64\">\r\n      <Configuration>Release</Configuration>\r\n      <Platform>x64</Platform>\r\n    </ProjectConfiguration>\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClInclude Include=\"ActorBash.h\" />\r\n    <ClInclude Include=\"CameraBash.h\" />\r\n    <ClInclude Include=\"Common.h\" />\r\n    <ClInclude Include=\"FPSRole.h\" />\r\n    <ClInclude Include=\"FPSRoleLocal.h\" />\r\n    <ClInclude Include=\"GameMain.h\" />\r\n    <ClInclude Include=\"GameProcess.h\" />\r\n    <ClInclude Include=\"HMDWrapper.h\" />\r\n    <ClInclude Include=\"main.h\" />\r\n    <ClInclude Include=\"MathLib.h\" />\r\n    <ClInclude Include=\"MoveDummy.h\" />\r\n    <ClInclude Include=\"RayControl.h\" />\r\n    <ClInclude Include=\"RoleBase.h\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClCompile Include=\"ActorBash.cpp\" />\r\n    <ClCompile Include=\"CameraBash.cpp\" />\r\n    <ClCompile Include=\"Common.cpp\" />\r\n    <ClCompile Include=\"FPSRole.cpp\" />\r\n    <ClCompile Include=\"FPSRoleLocal.cpp\" />\r\n    <ClCompile Include=\"GameMain.cpp\" />\r\n    <ClCompile Include=\"GameProcess.cpp\" />\r\n    <ClCompile Include=\"HMDWrapper.cpp\" />\r\n    <ClCompile Include=\"main.cpp\" />\r\n    <ClCompile Include=\"MoveDummy.cpp\" />\r\n    <ClCompile Include=\"RayControl.cpp\" />\r\n    <ClCompile Include=\"RoleBase.cpp\" />\r\n  </ItemGroup>\r\n  <PropertyGroup Label=\"Globals\">\r\n    <VCProjectVersion>15.0</VCProjectVersion>\r\n    <ProjectGuid>{C944C3E9-7AD8-4117-B664-DBE932214E45}</ProjectGuid>\r\n    <RootNamespace>Test</RootNamespace>\r\n    <WindowsTargetPlatformVersion>10.0.15063.0</WindowsTargetPlatformVersion>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>Application</ConfigurationType>\r\n    <UseDebugLibraries>true</UseDebugLibraries>\r\n    <PlatformToolset>v141</PlatformToolset>\r\n    <CharacterSet>MultiByte</CharacterSet>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>Application</ConfigurationType>\r\n    <UseDebugLibraries>false</UseDebugLibraries>\r\n    <PlatformToolset>v141</PlatformToolset>\r\n    <WholeProgramOptimization>true</WholeProgramOptimization>\r\n    <CharacterSet>MultiByte</CharacterSet>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\" Label=\"Configuration\">\r\n    <ConfigurationType>Application</ConfigurationType>\r\n    <UseDebugLibraries>true</UseDebugLibraries>\r\n    <PlatformToolset>v141</PlatformToolset>\r\n    <CharacterSet>MultiByte</CharacterSet>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\" Label=\"Configuration\">\r\n    <ConfigurationType>Application</ConfigurationType>\r\n    <UseDebugLibraries>false</UseDebugLibraries>\r\n    <PlatformToolset>v141</PlatformToolset>\r\n    <WholeProgramOptimization>true</WholeProgramOptimization>\r\n    <CharacterSet>MultiByte</CharacterSet>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\r\n  <ImportGroup Label=\"ExtensionSettings\">\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"Shared\">\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <PropertyGroup Label=\"UserMacros\" />\r\n  <PropertyGroup />\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <ClCompile>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <Optimization>Disabled</Optimization>\r\n      <SDLCheck>true</SDLCheck>\r\n    </ClCompile>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\r\n    <ClCompile>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <Optimization>Disabled</Optimization>\r\n      <SDLCheck>true</SDLCheck>\r\n    </ClCompile>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <ClCompile>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <Optimization>MaxSpeed</Optimization>\r\n      <FunctionLevelLinking>true</FunctionLevelLinking>\r\n      <IntrinsicFunctions>true</IntrinsicFunctions>\r\n      <SDLCheck>true</SDLCheck>\r\n    </ClCompile>\r\n    <Link>\r\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\r\n      <OptimizeReferences>true</OptimizeReferences>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n    <ClCompile>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <Optimization>MaxSpeed</Optimization>\r\n      <FunctionLevelLinking>true</FunctionLevelLinking>\r\n      <IntrinsicFunctions>true</IntrinsicFunctions>\r\n      <SDLCheck>true</SDLCheck>\r\n    </ClCompile>\r\n    <Link>\r\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\r\n      <OptimizeReferences>true</OptimizeReferences>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\r\n  <ImportGroup Label=\"ExtensionTargets\">\r\n  </ImportGroup>\r\n</Project>"
  },
  {
    "path": "Test/main.cpp",
    "content": "#include \"Engine.h\"\n#include \"interface.h\"\n#include \"GameMain.h\"\n\n\n#include \"Console.h\"\n#include \"direct.h\"\n#include \"HMDWrapper.h\"\n\n#include \"EngineConsole.h\"\n#include \"FileSystem.h\"\n\nint main(int argc, char* argv[])\n{\n#ifdef USE_HMD\t\t\n\tg_pHMD->Init();\n\tint useHmd = g_pHMD->GetUseHMD();\n\tif (useHmd)\n\t{\n\t\t//ͷ3d\n\t\trender_stereo = 2;\n\n\t\t//Զ崰ڷֱ ͷֱһֱ\n\t\tvideo_mode = -1;\n\t\tvideo_width = 2160;\n\t\tvideo_height = 1200;\n\n\t\t// ȫʽ 0    1ȫ\n\t\tvideo_fullscreen = 0;\n\n\t\tvideo_resizable = 0;\n\t\tvideo_vsync = 0;\n\t}\n\telse\n\t{\n\t\t//ͷ\n\t\trender_stereo = 0;\n\n\t\t//Զ崰ڷֱ\n\t\tvideo_mode = -1;\n\t\tvideo_fullscreen = 0;\n\t\tvideo_width = 1280;\n\t\tvideo_height = 720;\n\t\tvideo_resizable = 0;\n\t\tvideo_vsync = 0;\n\t}\n#else\n\trender_stereo = 0;\n\n\tvideo_mode = -1;\t\n\tvideo_fullscreen = 0;\t\n\tvideo_width = 1280;\t\t\n\tvideo_height = 720;\t\t\n\tvideo_resizable = 0;\t\n\tvideo_vsync = 0;\t\t\n#endif\n\n\tchar pAppPath[260];\t\t\n\tgetcwd(pAppPath, 260);\n\tstrcat(pAppPath, \"\\\\\");\n\n\tCGameMain t_Game;\n\n\tCInterfaceBase *pInit = MakeInterface(&t_Game, &CGameMain::Init);\n\tCInterfaceBase *pUpdate = MakeInterface(&t_Game, &CGameMain::Update);\n\tCInterfaceBase *pShutdown = MakeInterface(&t_Game, &CGameMain::ShutDown);\n\tCInterfaceBase *pRender = MakeInterface(&t_Game, &CGameMain::Render);\n\n\tchar *pArg[3] = { NULL, \"-engine_config\", \"data/TestProject.cfg\" };\t\n\tg_Engine.pEngine = new CEngine(NULL, pAppPath, NULL, NULL, 3, pArg, NULL, \"1123581321\", pInit);\t\n\n\t//1. 2.· 3. 4. 5.  6.һָָ,涨Ǹ  7. 8. 9.һָ\n\t//CEngine(CApp *pApp, const char *pAppPath, void *pExternalWindow, const char *pHomePath, int argc, char **argv, const char *pProject, const char *pPassword, CInterfaceBase *pInitInterface);\n\n\tg_Engine.pEngine->SetUpdateInterface(pUpdate);\n\tg_Engine.pEngine->SetShutdownInterface(pShutdown);\t\n\tg_Engine.pEngine->SetRenderInterface(pRender);\t\n\n\twhile (g_Engine.pEngine->IsDone() == 0)\t//isDone\n\t{\n#ifdef USE_HMD\n\t\tg_pHMD->Update();\n#endif\n\t\tg_Engine.pEngine->DoUpdate();\n\t\tg_Engine.pEngine->DoRender();\n\t\tg_Engine.pEngine->DoSwap();\n\t}\n\n\tdelete g_Engine.pEngine;\n\tg_Engine.pEngine = NULL;\n\n#ifdef USE_HMD\n\tg_pHMD->Shutdown();\n#endif\n\t//return 1; \n\treturn 0;\t\n\n}"
  },
  {
    "path": "Test/main.h",
    "content": "#pragma once\n\n\n#include <cstdio>\n\nint main(void)\n{\n\treturn 0;\n}"
  },
  {
    "path": "fpsgame/CommonConvert.cpp",
    "content": "#include \"precompiled.h\"\n#include \"CommonConvert.h\"\n\n#include \"StdSkeletons.h\"\n\n#include <algorithm>\n\n\n#include <cassert>\n\nvoid require_(int line, bool value, const char* type, const char* message)\n{\n\tif (value) return;\n\tchar linestr[16];\n\tsprintf(linestr, \"%d\", line);\n\tthrow ColladaException(std::string(type) + \" (line \" + linestr + \"): \" + message);\n}\n\nconst FMVector3 FMVector3_XAxis(1.0f, 0.0f, 0.0f);\nstatic float identity[] = { 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 };\nFMMatrix44 FMMatrix44_Identity(identity);\n\n/** Error handler for libxml2 */\nvoid errorHandler(void* ctx, const char* msg, ...)\n{\n\tchar buffer[1024];\n\tva_list ap;\n\tva_start(ap, msg);\n\tvsnprintf(buffer, sizeof(buffer), msg, ap);\n\tbuffer[sizeof(buffer) - 1] = '\\0';\n\tva_end(ap);\n\n\t*((std::string*)ctx) += buffer;\n}\n\nFColladaErrorHandler::FColladaErrorHandler(std::string& xmlErrors_)\n\t: xmlErrors(xmlErrors_)\n{\n\t// Grab all the error output from libxml2, for useful error reporting\n\txmlSetGenericErrorFunc(&xmlErrors, &errorHandler);\n\n\tFUError::AddErrorCallback(FUError::DEBUG_LEVEL, this, &FColladaErrorHandler::OnError);\n\tFUError::AddErrorCallback(FUError::WARNING_LEVEL, this, &FColladaErrorHandler::OnError);\n\tFUError::AddErrorCallback(FUError::ERROR_LEVEL, this, &FColladaErrorHandler::OnError);\n}\n\n\nCommonConvert::~CommonConvert()\n{\n\t\n}\n\nFColladaErrorHandler::~FColladaErrorHandler()\n{\n\txmlSetGenericErrorFunc(NULL, NULL);\n\n\tFUError::RemoveErrorCallback(FUError::DEBUG_LEVEL, this, &FColladaErrorHandler::OnError);\n\tFUError::RemoveErrorCallback(FUError::WARNING_LEVEL, this, &FColladaErrorHandler::OnError);\n\tFUError::RemoveErrorCallback(FUError::ERROR_LEVEL, this, &FColladaErrorHandler::OnError);\n}\n\nvoid FColladaErrorHandler::OnError(FUError::Level errorLevel, uint32 errorCode, uint32 UNUSED(lineNumber))\n{\n\t// Ignore warnings about missing materials, since we ignore materials entirely anyway\n\tif (errorCode == FUError::WARNING_INVALID_POLYGON_MAT_SYMBOL)\n\t\treturn;\n\n\tconst char* errorString = FUError::GetErrorString((FUError::Code) errorCode);\n\tif (!errorString)\n\t\terrorString = \"Unknown error code\";\n\n\tif (errorLevel == FUError::DEBUG_LEVEL)\n\t\tLog(LOG_INFO, \"FCollada %d: %s\", errorCode, errorString);\n\telse if (errorLevel == FUError::WARNING_LEVEL)\n\t\tLog(LOG_WARNING, \"FCollada %d: %s\", errorCode, errorString);\n\telse\n\t\tthrow ColladaException(errorString);\n}\n\n\n//////////////////////////////////////////////////////////////////////////\n\nvoid FColladaDocument::LoadFromText(const char *text)\n{\n\tdocument.reset(FCollada::NewTopDocument());\n\n\tconst char* newText = NULL;\n\tsize_t newTextSize = 0;\n\tFixBrokenXML(text, &newText, &newTextSize);\n\n\t// Log(LOG_INFO, \"%s\", newText);\n\tbool status = FCollada::LoadDocumentFromMemory(\"unknown.dae\", document.get(), (void*)newText, newTextSize);\n\n\tif (newText != text)\n\t\txmlFree((void*)newText);\n\n\tREQUIRE_SUCCESS(status);\n}\n\nvoid FColladaDocument::ReadExtras(xmlNode* UNUSED(colladaNode))\n{\n\t// TODO: This was needed to recognise and load XSI models.\n\t// XSI support should be reintroduced some time, but this function\n\t// may not be necessary since FCollada might now provide access to the\n\t// 'extra' data via a proper API.\n\n\t/*\n\tif (! IsEquivalent(colladaNode->name, DAE_COLLADA_ELEMENT))\n\treturn;\n\n\textra.reset(new FCDExtra(document.get()));\n\n\txmlNodeList extraNodes;\n\tFUXmlParser::FindChildrenByType(colladaNode, DAE_EXTRA_ELEMENT, extraNodes);\n\tfor (xmlNodeList::iterator it = extraNodes.begin(); it != extraNodes.end(); ++it)\n\t{\n\txmlNode* extraNode = (*it);\n\textra->LoadFromXML(extraNode);\n\t}\n\t*/\n}\n\n//////////////////////////////////////////////////////////////////////////\n\nCommonConvert::CommonConvert(const char* text, std::string& xmlErrors)\n\t: m_Err(xmlErrors)\n{\n\tm_Doc.LoadFromText(text);\n\tFCDSceneNode* root = m_Doc.GetDocument()->GetVisualSceneRoot();\n\tREQUIRE(root != NULL, \"has root object\");\n\n\t// Find the instance to convert\n\tif (!FindSingleInstance(root, m_Instance, m_EntityTransform))\n\t\tthrow ColladaException(\"Couldn't find object to convert\");\n\n\tassert(m_Instance);\n\tLog(LOG_INFO, \"Converting '%s'\", m_Instance->GetEntity()->GetName().c_str());\n\n\tm_IsXSI = false;\n\tFCDAsset* asset = m_Doc.GetDocument()->GetAsset();\n\tif (asset && asset->GetContributorCount() >= 1)\n\t{\n\t\tstd::string tool(asset->GetContributor(0)->GetAuthoringTool());\n\t\tif (tool.find(\"XSI\") != tool.npos)\n\t\t\tm_IsXSI = true;\n\t}\n\n\tFMVector3 upAxis = m_Doc.GetDocument()->GetAsset()->GetUpAxis();\n\tm_YUp = (upAxis.y != 0); // assume either Y_UP or Z_UP (TODO: does anyone ever do X_UP?)\n}\n\n// CommonConvert::~CommonConvert()\n// {\n// }\n\n//////////////////////////////////////////////////////////////////////////\n\n// HACK: The originals don't get exported properly from FCollada (3.02, DLL), so define\n// them here instead of fixing it correctly.\nconst FMVector3 FMVector3_XAxis(1.0f, 0.0f, 0.0f);\nstatic float identity[] = { 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 };\nFMMatrix44 FMMatrix44_Identity(identity);\n\nstruct FoundInstance\n{\n\tFCDEntityInstance* instance;\n\tFMMatrix44 transform;\n};\n\nstatic bool IsVisible_XSI(FCDSceneNode* node, bool& visible)\n{\n\t// Look for <extra><technique profile=\"XSI\"><SI_Visibility><xsi_param sid=\"visibility\">\n\n\tFCDExtra* extra = node->GetExtra();\n\tif (!extra) return false;\n\n\tFCDEType* type = extra->GetDefaultType();\n\tif (!type) return false;\n\n\tFCDETechnique* technique = type->FindTechnique(\"XSI\");\n\tif (!technique) return false;\n\n\tFCDENode* visibility1 = technique->FindChildNode(\"SI_Visibility\");\n\tif (!visibility1) return false;\n\n\tFCDENode* visibility2 = visibility1->FindChildNode(\"xsi_param\");\n\tif (!visibility2) return false;\n\n\tif (IsEquivalent(visibility2->GetContent(), \"TRUE\"))\n\t\tvisible = true;\n\telse if (IsEquivalent(visibility2->GetContent(), \"FALSE\"))\n\t\tvisible = false;\n\treturn true;\n}\n\nstatic bool IsVisible(FCDSceneNode* node)\n{\n\tbool visible = false;\n\n\t// Try the XSI visibility property\n\tif (IsVisible_XSI(node, visible))\n\t\treturn visible;\n\n\t// Else fall back to the FCollada-specific setting\n\tvisible = (node->GetVisibility() != 0.0);\n\treturn visible;\n}\n\n/**\n* Recursively finds all entities under the current node. If onlyMarked is\n* set, only matches entities where the user-defined property was set to\n* \"export\" in the modelling program.\n*\n* @param node root of subtree to search\n* @param instances output - appends matching entities\n* @param transform transform matrix of current subtree\n* @param onlyMarked only match entities with \"export\" property\n*/\nstatic void FindInstances(FCDSceneNode* node, std::vector<FoundInstance>& instances, const FMMatrix44& transform, bool onlyMarked)\n{\n\tfor (size_t i = 0; i < node->GetChildrenCount(); ++i)\n\t{\n\t\tFCDSceneNode* child = node->GetChild(i);\n\t\tFindInstances(child, instances, transform * node->ToMatrix(), onlyMarked);\n\t}\n\n\tfor (size_t i = 0; i < node->GetInstanceCount(); ++i)\n\t{\n\t\tif (onlyMarked)\n\t\t{\n\t\t\tif (node->GetNote() != \"export\")\n\t\t\t\tcontinue;\n\t\t}\n\n\t\t// Only accept instances of appropriate types, and not e.g. lights\n\t\tFCDEntity::Type type = node->GetInstance(i)->GetEntityType();\n\t\tif (!(type == FCDEntity::GEOMETRY || type == FCDEntity::CONTROLLER))\n\t\t\tcontinue;\n\n\t\t// Ignore invisible objects, because presumably nobody wanted to export them\n\t\tif (!IsVisible(node))\n\t\t\tcontinue;\n\n\t\tFoundInstance f;\n\t\tf.transform = transform * node->ToMatrix();\n\t\tf.instance = node->GetInstance(i);\n\t\tinstances.push_back(f);\n\t\tLog(LOG_INFO, \"Found convertible object '%s'\", node->GetName().c_str());\n\t}\n}\n\n\nbool FindSingleInstance(FCDSceneNode* node, FCDEntityInstance*& instance, FMMatrix44& transform)\n{\n\tstd::vector<FoundInstance> instances;\n\n\tFindInstances(node, instances, FMMatrix44_Identity, true);\n\tif (instances.size() > 1)\n\t{\n\t\tLog(LOG_ERROR, \"Found too many export-marked objects\");\n\t\treturn false;\n\t}\n\tif (instances.empty())\n\t{\n\t\tFindInstances(node, instances, FMMatrix44_Identity, false);\n\t\tif (instances.size() > 1)\n\t\t{\n\t\t\tLog(LOG_ERROR, \"Found too many possible objects to convert - try adding the 'export' property to disambiguate one\");\n\t\t\treturn false;\n\t\t}\n\t\tif (instances.empty())\n\t\t{\n\t\t\tLog(LOG_ERROR, \"Didn't find any objects in the scene\");\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tassert(instances.size() == 1); // if we got this far\n\tinstance = instances[0].instance;\n\ttransform = instances[0].transform;\n\treturn true;\n}\n\n//////////////////////////////////////////////////////////////////////////\n\nstatic bool ReverseSortWeight(const FCDJointWeightPair& a, const FCDJointWeightPair& b)\n{\n\treturn (a.weight > b.weight);\n}\n\nvoid SkinReduceInfluences(FCDSkinController* skin, size_t maxInfluenceCount, float minimumWeight)\n{\n\t// Approximately equivalent to:\n\t//    skin->ReduceInfluences(maxInfluenceCount, minimumWeight);\n\t// except this version merges multiple weights for the same joint\n\n\tfor (size_t i = 0; i < skin->GetInfluenceCount(); ++i)\n\t{\n\t\tFCDSkinControllerVertex& influence = *skin->GetVertexInfluence(i);\n\n\t\tstd::vector<FCDJointWeightPair> newWeights;\n\t\tfor (size_t i = 0; i < influence.GetPairCount(); ++i)\n\t\t{\n\t\t\tFCDJointWeightPair* weight = influence.GetPair(i);\n\n\t\t\tfor (size_t j = 0; j < newWeights.size(); ++j)\n\t\t\t{\n\t\t\t\tFCDJointWeightPair& newWeight = newWeights[j];\n\t\t\t\tif (weight->jointIndex == newWeight.jointIndex)\n\t\t\t\t{\n\t\t\t\t\tnewWeight.weight += weight->weight;\n\t\t\t\t\tgoto MERGED_WEIGHTS;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tnewWeights.push_back(*weight);\n\t\tMERGED_WEIGHTS:;\n\t\t}\n\n\t\t// Put highest-weighted influences at the front of the list\n\t\tstd::sort(newWeights.begin(), newWeights.end(), ReverseSortWeight);\n\n\t\t// Limit the maximum number of influences\n\t\tif (newWeights.size() > maxInfluenceCount)\n\t\t\tnewWeights.resize(maxInfluenceCount);\n\n\t\t// Enforce the minimum weight per influence\n\t\t// (This is done here rather than in the earlier loop, because several\n\t\t// small weights for the same bone might add up to a value above the\n\t\t// threshold)\n\t\twhile (!newWeights.empty() && newWeights.back().weight < minimumWeight)\n\t\t\tnewWeights.pop_back();\n\n\t\t// Renormalise, so sum(weights)=1\n\t\tfloat totalWeight = 0;\n\t\tfor (std::vector<FCDJointWeightPair>::iterator itNW = newWeights.begin(); itNW != newWeights.end(); ++itNW)\n\t\t\ttotalWeight += itNW->weight;\n\t\tfor (std::vector<FCDJointWeightPair>::iterator itNW = newWeights.begin(); itNW != newWeights.end(); ++itNW)\n\t\t\titNW->weight /= totalWeight;\n\n\t\t// Copy new weights into the skin\n\t\tinfluence.SetPairCount(0);\n\t\tfor (std::vector<FCDJointWeightPair>::iterator itNW = newWeights.begin(); itNW != newWeights.end(); ++itNW)\n\t\t\tinfluence.AddPair(itNW->jointIndex, itNW->weight);\n\t}\n\n\tskin->SetDirtyFlag();\n}\n\n\nvoid FixSkeletonRoots(FCDControllerInstance& UNUSED(controllerInstance))\n{\n\t// TODO: Need to reintroduce XSI support at some point\n#if 0\n\t// HACK: The XSI exporter doesn't do a <skeleton> and FCollada doesn't\n\t// seem to know where else to look, so just guess that it's somewhere\n\t// under Scene_Root\n\tif (controllerInstance.GetSkeletonRoots().empty())\n\t{\n\t\t// HACK (evil): SetSkeletonRoot is declared but not defined, and there's\n\t\t// no other proper way to modify the skeleton-roots list, so cheat horribly\n\t\tFUUriList& uriList = const_cast<FUUriList&>(controllerInstance.GetSkeletonRoots());\n\t\turiList.push_back(FUUri(\"Scene_Root\"));\n\t\tcontrollerInstance.LinkImport();\n\t}\n#endif\n}\n\nconst Skeleton& FindSkeleton(const FCDControllerInstance& controllerInstance)\n{\n\t// I can't see any proper way to determine the real root of the skeleton,\n\t// so just choose an arbitrary bone and search upwards until we find a\n\t// recognised ancestor (or until we fall off the top of the tree)\n\n\tconst Skeleton* skeleton = NULL;\n\tconst FCDSceneNode* joint = controllerInstance.GetJoint(0);\n\twhile (joint && (skeleton = Skeleton::FindSkeleton(joint->GetName().c_str())) == NULL)\n\t{\n\t\tjoint = joint->GetParent();\n\t}\n\tREQUIRE(skeleton != NULL, \"recognised skeleton structure\");\n\treturn *skeleton;\n}\n\nvoid TransformBones(std::vector<BoneTransform>& bones, const FMMatrix44& scaleTransform, bool yUp)\n{\n\tfor (size_t i = 0; i < bones.size(); ++i)\n\t{\n\t\t// Apply the desired transformation to the bone coordinates\n\t\tFMVector3 trans(bones[i].translation, 0);\n\t\ttrans = scaleTransform.TransformCoordinate(trans);\n\t\tbones[i].translation[0] = trans.x;\n\t\tbones[i].translation[1] = trans.y;\n\t\tbones[i].translation[2] = trans.z;\n\n\t\t// DON'T apply the transformation to orientation, because I can't get\n\t\t// that kind of thing to work in practice (interacting nicely between\n\t\t// the models and animations), so this function assumes the transform\n\t\t// just does scaling, so there's no need to rotate anything. (But I think\n\t\t// this code would work for rotation, though not very efficiently.)\n\t\t/*\n\t\tFMMatrix44 m = FMQuaternion(bones[i].orientation[0], bones[i].orientation[1], bones[i].orientation[2], bones[i].orientation[3]).ToMatrix();\n\t\tm *= scaleTransform;\n\t\tHMatrix matrix;\n\t\tmemcpy(matrix, m.Transposed().m, sizeof(matrix));\n\t\tAffineParts parts;\n\t\tdecomp_affine(matrix, &parts);\n\n\t\tbones[i].orientation[0] = parts.q.x;\n\t\tbones[i].orientation[1] = parts.q.y;\n\t\tbones[i].orientation[2] = parts.q.z;\n\t\tbones[i].orientation[3] = parts.q.w;\n\t\t*/\n\n\t\tif (yUp)\n\t\t{\n\t\t\t// TODO: this is all just guesses which seem to work for data\n\t\t\t// exported from XSI, rather than having been properly thought\n\t\t\t// through\n\t\t\tbones[i].translation[2] = -bones[i].translation[2];\n\t\t\tbones[i].orientation[2] = -bones[i].orientation[2];\n\t\t\tbones[i].orientation[3] = -bones[i].orientation[3];\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Convert bone translations from xyz into xzy axes:\n\t\t\tstd::swap(bones[i].translation[1], bones[i].translation[2]);\n\n\t\t\t// To convert the quaternions: imagine you're using the axis/angle\n\t\t\t// representation, then swap the y,z basis vectors and change the\n\t\t\t// direction of rotation by negating the angle ( => negating sin(angle)\n\t\t\t// => negating x,y,z => changing (x,y,z,w) to (-x,-z,-y,w)\n\t\t\t// but then (-x,-z,-y,w) == (x,z,y,-w) so do that instead)\n\t\t\tstd::swap(bones[i].orientation[1], bones[i].orientation[2]);\n\t\t\tbones[i].orientation[3] = -bones[i].orientation[3];\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "fpsgame/CommonConvert.h",
    "content": "#pragma once\n#ifndef INCLUDED_COMMONCONVERT\n#define INCLUDED_COMMONCONVERT\n\n#include <exception>\n#include <string>\n#include <memory>\n#include <vector>\n\nclass Skeleton;\n\n\nstruct OutputCB\n{\n\tvirtual ~OutputCB()\n\t{\n\n\t}\n\tvirtual void operator() (const char* data, unsigned int length) = 0;\n};\n\nclass ColladaException : public std::exception\n{\nprivate:\n\tstd::string msg;\npublic:\n\tColladaException(const std::string& msg) :msg(msg)\n\t{\n\n\t}\n\t~ColladaException()\n\t\tthrow()\n\t{\n\n\t}\n};\n/**\n* Standard error handler - logs FCollada messages using Log(), and also\n* maintains a list of XML parser errors.\n*/\nclass FoclladaErrorHandler\n{\n\nprivate:\n\tvoid OnError(FUError::Level errorLevel, uint32 errorCode, uint32 lineNumber);\n\tstd::string& xmlErrors;\n\n\tvoid operator=(FColladaErrorHandler);\n\npublic:\n\tFoclladaErrorHandler(std::string& xmlErrors);\n\t~FoclladaErrorHandler();\n};\n\n/**\n* Standard document loader. Based on FCDocument::LoadFromText, but allows\n* access to \\<extra\\> nodes at the document level (i.e. directly in \\<COLLADA\\>).\n*/\nclass FColladaDocument\n{\npublic:\n\tvoid LoadFromText(const char* text);\n\n\t/** Returns the FCDocument that was loaded. */\n\tFCDocument* GetDocument() const { return document.get(); }\n\n\t/** Returns the \\<extra\\> data from the \\<COLLADA\\> element. */\n\tFCDExtra* GetExtra() const { return extra.get(); }\n\nprivate:\n\tvoid ReadExtras(xmlNode* colladaNode);\n\tstd::unique_ptr<FCDocument> document;\n\tstd::unique_ptr<FCDExtra> extra;\n};\n\n\n#endif"
  },
  {
    "path": "fpsgame/Decompose.cpp",
    "content": "/* Copyright (C) 2015 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"precompiled.h\"\n\n#ifdef _MSC_VER\n# pragma warning(disable: 4244 4305 4127 4701)\n#endif\n\n/**** Decompose.c ****/\n/* Ken Shoemake, 1993 */\n#include <math.h>\n#include \"Decompose.h\"\n\n/******* Matrix Preliminaries *******/\n\n/** Fill out 3x3 matrix to 4x4 **/\n#define mat_pad(A) (A[W][X]=A[X][W]=A[W][Y]=A[Y][W]=A[W][Z]=A[Z][W]=0,A[W][W]=1)\n\n/** Copy nxn matrix A to C using \"gets\" for assignment **/\n#define mat_copy(C,gets,A,n) {int i,j; for(i=0;i<n;i++) for(j=0;j<n;j++)\\\n    C[i][j] gets (A[i][j]);}\n\n/** Copy transpose of nxn matrix A to C using \"gets\" for assignment **/\n#define mat_tpose(AT,gets,A,n) {int i,j; for(i=0;i<n;i++) for(j=0;j<n;j++)\\\n    AT[i][j] gets (A[j][i]);}\n\n/** Assign nxn matrix C the element-wise combination of A and B using \"op\" **/\n#define mat_binop(C,gets,A,op,B,n) {int i,j; for(i=0;i<n;i++) for(j=0;j<n;j++)\\\n    C[i][j] gets (A[i][j]) op (B[i][j]);}\n\n/** Multiply the upper left 3x3 parts of A and B to get AB **/\nvoid mat_mult(HMatrix A, HMatrix B, HMatrix AB)\n{\n    int i, j;\n    for (i=0; i<3; i++) for (j=0; j<3; j++)\n\tAB[i][j] = A[i][0]*B[0][j] + A[i][1]*B[1][j] + A[i][2]*B[2][j];\n}\n\n/** Return dot product of length 3 vectors va and vb **/\nfloat vdot(float *va, float *vb)\n{\n    return (va[0]*vb[0] + va[1]*vb[1] + va[2]*vb[2]);\n}\n\n/** Set v to cross product of length 3 vectors va and vb **/\nvoid vcross(float *va, float *vb, float *v)\n{\n    v[0] = va[1]*vb[2] - va[2]*vb[1];\n    v[1] = va[2]*vb[0] - va[0]*vb[2];\n    v[2] = va[0]*vb[1] - va[1]*vb[0];\n}\n\n/** Set MadjT to transpose of inverse of M times determinant of M **/\nvoid adjoint_transpose(HMatrix M, HMatrix MadjT)\n{\n    vcross(M[1], M[2], MadjT[0]);\n    vcross(M[2], M[0], MadjT[1]);\n    vcross(M[0], M[1], MadjT[2]);\n}\n\n/******* Quaternion Preliminaries *******/\n\n/* Construct a (possibly non-unit) quaternion from real components. */\nQuat Qt_(float x, float y, float z, float w)\n{\n    Quat qq;\n    qq.x = x; qq.y = y; qq.z = z; qq.w = w;\n    return (qq);\n}\n\n/* Return conjugate of quaternion. */\nQuat Qt_Conj(Quat q)\n{\n    Quat qq;\n    qq.x = -q.x; qq.y = -q.y; qq.z = -q.z; qq.w = q.w;\n    return (qq);\n}\n\n/* Return quaternion product qL * qR.  Note: order is important!\n * To combine rotations, use the product Mul(qSecond, qFirst),\n * which gives the effect of rotating by qFirst then qSecond. */\nQuat Qt_Mul(Quat qL, Quat qR)\n{\n    Quat qq;\n    qq.w = qL.w*qR.w - qL.x*qR.x - qL.y*qR.y - qL.z*qR.z;\n    qq.x = qL.w*qR.x + qL.x*qR.w + qL.y*qR.z - qL.z*qR.y;\n    qq.y = qL.w*qR.y + qL.y*qR.w + qL.z*qR.x - qL.x*qR.z;\n    qq.z = qL.w*qR.z + qL.z*qR.w + qL.x*qR.y - qL.y*qR.x;\n    return (qq);\n}\n\n/* Return product of quaternion q by scalar w. */\nQuat Qt_Scale(Quat q, float w)\n{\n    Quat qq;\n    qq.w = q.w*w; qq.x = q.x*w; qq.y = q.y*w; qq.z = q.z*w;\n    return (qq);\n}\n\n/* Construct a unit quaternion from rotation matrix.  Assumes matrix is\n * used to multiply column vector on the left: vnew = mat vold.\t Works\n * correctly for right-handed coordinate system and right-handed rotations.\n * Translation and perspective components ignored. */\nQuat Qt_FromMatrix(HMatrix mat)\n{\n    /* This algorithm avoids near-zero divides by looking for a large component\n     * - first w, then x, y, or z.  When the trace is greater than zero,\n     * |w| is greater than 1/2, which is as small as a largest component can be.\n     * Otherwise, the largest diagonal entry corresponds to the largest of |x|,\n     * |y|, or |z|, one of which must be larger than |w|, and at least 1/2. */\n    Quat qu;\n    double tr, s;\n\n    tr = mat[X][X] + mat[Y][Y]+ mat[Z][Z];\n    if (tr >= 0.0) {\n\t    s = sqrt(tr + mat[W][W]);\n\t    qu.w = s*0.5;\n\t    s = 0.5 / s;\n\t    qu.x = (mat[Z][Y] - mat[Y][Z]) * s;\n\t    qu.y = (mat[X][Z] - mat[Z][X]) * s;\n\t    qu.z = (mat[Y][X] - mat[X][Y]) * s;\n\t} else {\n\t    int h = X;\n\t    if (mat[Y][Y] > mat[X][X]) h = Y;\n\t    if (mat[Z][Z] > mat[h][h]) h = Z;\n\t    switch (h) {\n#define caseMacro(i,j,k,I,J,K) \\\n\t    case I:\\\n\t\ts = sqrt( (mat[I][I] - (mat[J][J]+mat[K][K])) + mat[W][W] );\\\n\t\tqu.i = s*0.5;\\\n\t\ts = 0.5 / s;\\\n\t\tqu.j = (mat[I][J] + mat[J][I]) * s;\\\n\t\tqu.k = (mat[K][I] + mat[I][K]) * s;\\\n\t\tqu.w = (mat[K][J] - mat[J][K]) * s;\\\n\t\tbreak\n\t    caseMacro(x,y,z,X,Y,Z);\n\t    caseMacro(y,z,x,Y,Z,X);\n\t    caseMacro(z,x,y,Z,X,Y);\n\t    }\n\t}\n    if (mat[W][W] != 1.0) qu = Qt_Scale(qu, 1/sqrt(mat[W][W]));\n    return (qu);\n}\n/******* Decomp Auxiliaries *******/\n\nstatic HMatrix mat_id = {{1,0,0,0},{0,1,0,0},{0,0,1,0},{0,0,0,1}};\n\n/** Compute either the 1 or infinity norm of M, depending on tpose **/\nfloat mat_norm(HMatrix M, int tpose)\n{\n    int i;\n    float sum, max;\n    max = 0.0;\n    for (i=0; i<3; i++) {\n\tif (tpose) sum = fabs(M[0][i])+fabs(M[1][i])+fabs(M[2][i]);\n\telse\t   sum = fabs(M[i][0])+fabs(M[i][1])+fabs(M[i][2]);\n\tif (max<sum) max = sum;\n    }\n    return max;\n}\n\nfloat norm_inf(HMatrix M) {return mat_norm(M, 0);}\nfloat norm_one(HMatrix M) {return mat_norm(M, 1);}\n\n/** Return index of column of M containing maximum abs entry, or -1 if M=0 **/\nint find_max_col(HMatrix M)\n{\n    float abs, max;\n    int i, j, col;\n    max = 0.0; col = -1;\n    for (i=0; i<3; i++) for (j=0; j<3; j++) {\n\tabs = M[i][j]; if (abs<0.0) abs = -abs;\n\tif (abs>max) {max = abs; col = j;}\n    }\n    return col;\n}\n\n/** Setup u for Household reflection to zero all v components but first **/\nvoid make_reflector(float *v, float *u)\n{\n    float s = sqrt(vdot(v, v));\n    u[0] = v[0]; u[1] = v[1];\n    u[2] = v[2] + ((v[2]<0.0) ? -s : s);\n    s = sqrt(2.0/vdot(u, u));\n    u[0] = u[0]*s; u[1] = u[1]*s; u[2] = u[2]*s;\n}\n\n/** Apply Householder reflection represented by u to column vectors of M **/\nvoid reflect_cols(HMatrix M, float *u)\n{\n    int i, j;\n    for (i=0; i<3; i++) {\n\tfloat s = u[0]*M[0][i] + u[1]*M[1][i] + u[2]*M[2][i];\n\tfor (j=0; j<3; j++) M[j][i] -= u[j]*s;\n    }\n}\n/** Apply Householder reflection represented by u to row vectors of M **/\nvoid reflect_rows(HMatrix M, float *u)\n{\n    int i, j;\n    for (i=0; i<3; i++) {\n\tfloat s = vdot(u, M[i]);\n\tfor (j=0; j<3; j++) M[i][j] -= u[j]*s;\n    }\n}\n\n/** Find orthogonal factor Q of rank 1 (or less) M **/\nvoid do_rank1(HMatrix M, HMatrix Q)\n{\n    float v1[3], v2[3], s;\n    int col;\n    mat_copy(Q,=,mat_id,4);\n    /* If rank(M) is 1, we should find a non-zero column in M */\n    col = find_max_col(M);\n    if (col<0) return; /* Rank is 0 */\n    v1[0] = M[0][col]; v1[1] = M[1][col]; v1[2] = M[2][col];\n    make_reflector(v1, v1); reflect_cols(M, v1);\n    v2[0] = M[2][0]; v2[1] = M[2][1]; v2[2] = M[2][2];\n    make_reflector(v2, v2); reflect_rows(M, v2);\n    s = M[2][2];\n    if (s<0.0) Q[2][2] = -1.0;\n    reflect_cols(Q, v1); reflect_rows(Q, v2);\n}\n\n/** Find orthogonal factor Q of rank 2 (or less) M using adjoint transpose **/\nvoid do_rank2(HMatrix M, HMatrix MadjT, HMatrix Q)\n{\n    float v1[3], v2[3];\n    float w, x, y, z, c, s, d;\n    int col;\n    /* If rank(M) is 2, we should find a non-zero column in MadjT */\n    col = find_max_col(MadjT);\n    if (col<0) {do_rank1(M, Q); return;} /* Rank<2 */\n    v1[0] = MadjT[0][col]; v1[1] = MadjT[1][col]; v1[2] = MadjT[2][col];\n    make_reflector(v1, v1); reflect_cols(M, v1);\n    vcross(M[0], M[1], v2);\n    make_reflector(v2, v2); reflect_rows(M, v2);\n    w = M[0][0]; x = M[0][1]; y = M[1][0]; z = M[1][1];\n    if (w*z>x*y) {\n\tc = z+w; s = y-x; d = sqrt(c*c+s*s); c = c/d; s = s/d;\n\tQ[0][0] = Q[1][1] = c; Q[0][1] = -(Q[1][0] = s);\n    } else {\n\tc = z-w; s = y+x; d = sqrt(c*c+s*s); c = c/d; s = s/d;\n\tQ[0][0] = -(Q[1][1] = c); Q[0][1] = Q[1][0] = s;\n    }\n    Q[0][2] = Q[2][0] = Q[1][2] = Q[2][1] = 0.0; Q[2][2] = 1.0;\n    reflect_cols(Q, v1); reflect_rows(Q, v2);\n}\n\n\n/******* Polar Decomposition *******/\n\n/* Polar Decomposition of 3x3 matrix in 4x4,\n * M = QS.  See Nicholas Higham and Robert S. Schreiber,\n * Fast Polar Decomposition of An Arbitrary Matrix,\n * Technical Report 88-942, October 1988,\n * Department of Computer Science, Cornell University.\n */\nfloat polar_decomp(HMatrix M, HMatrix Q, HMatrix S)\n{\n#define TOL 1.0e-6\n    HMatrix Mk, MadjTk, Ek;\n    float det, M_one, M_inf, MadjT_one, MadjT_inf, E_one, gamma, g1, g2;\n    int i, j;\n    mat_tpose(Mk,=,M,3);\n    M_one = norm_one(Mk);  M_inf = norm_inf(Mk);\n    do {\n\tadjoint_transpose(Mk, MadjTk);\n\tdet = vdot(Mk[0], MadjTk[0]);\n\tif (det==0.0) {do_rank2(Mk, MadjTk, Mk); break;}\n\tMadjT_one = norm_one(MadjTk); MadjT_inf = norm_inf(MadjTk);\n\tgamma = sqrt(sqrt((MadjT_one*MadjT_inf)/(M_one*M_inf))/fabs(det));\n\tg1 = gamma*0.5;\n\tg2 = 0.5/(gamma*det);\n\tmat_copy(Ek,=,Mk,3);\n\tmat_binop(Mk,=,g1*Mk,+,g2*MadjTk,3);\n\tmat_copy(Ek,-=,Mk,3);\n\tE_one = norm_one(Ek);\n\tM_one = norm_one(Mk);  M_inf = norm_inf(Mk);\n    } while (E_one>(M_one*TOL));\n    mat_tpose(Q,=,Mk,3); mat_pad(Q);\n    mat_mult(Mk, M, S);\t mat_pad(S);\n    for (i=0; i<3; i++) for (j=i; j<3; j++)\n\tS[i][j] = S[j][i] = 0.5*(S[i][j]+S[j][i]);\n    return (det);\n}\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n/******* Spectral Decomposition *******/\n\n/* Compute the spectral decomposition of symmetric positive semi-definite S.\n * Returns rotation in U and scale factors in result, so that if K is a diagonal\n * matrix of the scale factors, then S = U K (U transpose). Uses Jacobi method.\n * See Gene H. Golub and Charles F. Van Loan. Matrix Computations. Hopkins 1983.\n */\nHVect spect_decomp(HMatrix S, HMatrix U)\n{\n    HVect kv;\n    double Diag[3],OffD[3]; /* OffD is off-diag (by omitted index) */\n    double g,h,fabsh,fabsOffDi,t,theta,c,s,tau,ta,OffDq,a,b;\n    static char nxt[] = {Y,Z,X};\n    int sweep, i, j;\n    mat_copy(U,=,mat_id,4);\n    Diag[X] = S[X][X]; Diag[Y] = S[Y][Y]; Diag[Z] = S[Z][Z];\n    OffD[X] = S[Y][Z]; OffD[Y] = S[Z][X]; OffD[Z] = S[X][Y];\n    for (sweep=20; sweep>0; sweep--) {\n\tfloat sm = fabs(OffD[X])+fabs(OffD[Y])+fabs(OffD[Z]);\n\tif (sm==0.0) break;\n\tfor (i=Z; i>=X; i--) {\n\t    int p = nxt[i]; int q = nxt[p];\n\t    fabsOffDi = fabs(OffD[i]);\n\t    g = 100.0*fabsOffDi;\n\t    if (fabsOffDi>0.0) {\n\t\th = Diag[q] - Diag[p];\n\t\tfabsh = fabs(h);\n\t\tif (fabsh+g==fabsh) {\n\t\t    t = OffD[i]/h;\n\t\t} else {\n\t\t    theta = 0.5*h/OffD[i];\n\t\t    t = 1.0/(fabs(theta)+sqrt(theta*theta+1.0));\n\t\t    if (theta<0.0) t = -t;\n\t\t}\n\t\tc = 1.0/sqrt(t*t+1.0); s = t*c;\n\t\ttau = s/(c+1.0);\n\t\tta = t*OffD[i]; OffD[i] = 0.0;\n\t\tDiag[p] -= ta; Diag[q] += ta;\n\t\tOffDq = OffD[q];\n\t\tOffD[q] -= s*(OffD[p] + tau*OffD[q]);\n\t\tOffD[p] += s*(OffDq   - tau*OffD[p]);\n\t\tfor (j=Z; j>=X; j--) {\n\t\t    a = U[j][p]; b = U[j][q];\n\t\t    U[j][p] -= s*(b + tau*a);\n\t\t    U[j][q] += s*(a - tau*b);\n\t\t}\n\t    }\n\t}\n    }\n    kv.x = Diag[X]; kv.y = Diag[Y]; kv.z = Diag[Z]; kv.w = 1.0;\n    return (kv);\n}\n\n/******* Spectral Axis Adjustment *******/\n\n/* Given a unit quaternion, q, and a scale vector, k, find a unit quaternion, p,\n * which permutes the axes and turns freely in the plane of duplicate scale\n * factors, such that q p has the largest possible w component, i.e. the\n * smallest possible angle. Permutes k's components to go with q p instead of q.\n * See Ken Shoemake and Tom Duff. Matrix Animation and Polar Decomposition.\n * Proceedings of Graphics Interface 1992. Details on p. 262-263.\n */\nQuat snuggle(Quat q, HVect *k)\n{\n#define SQRTHALF (0.7071067811865475244)\n#define sgn(n,v)    ((n)?-(v):(v))\n#define swap(a,i,j) {a[3]=a[i]; a[i]=a[j]; a[j]=a[3];}\n#define cycle(a,p)  if (p) {a[3]=a[0]; a[0]=a[1]; a[1]=a[2]; a[2]=a[3];}\\\n\t\t    else   {a[3]=a[2]; a[2]=a[1]; a[1]=a[0]; a[0]=a[3];}\n    Quat p;\n    float ka[4];\n    int i, turn = -1;\n    ka[X] = k->x; ka[Y] = k->y; ka[Z] = k->z;\n    if (ka[X]==ka[Y]) {if (ka[X]==ka[Z]) turn = W; else turn = Z;}\n    else {if (ka[X]==ka[Z]) turn = Y; else if (ka[Y]==ka[Z]) turn = X;}\n    if (turn>=0) {\n\tQuat qtoz, qp;\n\tunsigned neg[3], win;\n\tdouble mag[3], t;\n\tstatic Quat qxtoz = {0,SQRTHALF,0,SQRTHALF};\n\tstatic Quat qytoz = {SQRTHALF,0,0,SQRTHALF};\n\tstatic Quat qppmm = { 0.5, 0.5,-0.5,-0.5};\n\tstatic Quat qpppp = { 0.5, 0.5, 0.5, 0.5};\n\tstatic Quat qmpmm = {-0.5, 0.5,-0.5,-0.5};\n\tstatic Quat qpppm = { 0.5, 0.5, 0.5,-0.5};\n\tstatic Quat q0001 = { 0.0, 0.0, 0.0, 1.0};\n\tstatic Quat q1000 = { 1.0, 0.0, 0.0, 0.0};\n\tswitch (turn) {\n\tdefault: return (Qt_Conj(q));\n\tcase X: q = Qt_Mul(q, qtoz = qxtoz); swap(ka,X,Z) break;\n\tcase Y: q = Qt_Mul(q, qtoz = qytoz); swap(ka,Y,Z) break;\n\tcase Z: qtoz = q0001; break;\n\t}\n\tq = Qt_Conj(q);\n\tmag[0] = (double)q.z*q.z+(double)q.w*q.w-0.5;\n\tmag[1] = (double)q.x*q.z-(double)q.y*q.w;\n\tmag[2] = (double)q.y*q.z+(double)q.x*q.w;\n\tfor (i=0; i<3; i++) if ((neg[i] = (mag[i]<0.0)) != 0) mag[i] = -mag[i];\n\tif (mag[0]>mag[1]) {if (mag[0]>mag[2]) win = 0; else win = 2;}\n\telse\t\t   {if (mag[1]>mag[2]) win = 1; else win = 2;}\n\tswitch (win) {\n\tcase 0: if (neg[0]) p = q1000; else p = q0001; break;\n\tcase 1: if (neg[1]) p = qppmm; else p = qpppp; cycle(ka,0) break;\n\tcase 2: if (neg[2]) p = qmpmm; else p = qpppm; cycle(ka,1) break;\n\t}\n\tqp = Qt_Mul(q, p);\n\tt = sqrt(mag[win]+0.5);\n\tp = Qt_Mul(p, Qt_(0.0,0.0,-qp.z/t,qp.w/t));\n\tp = Qt_Mul(qtoz, Qt_Conj(p));\n    } else {\n\tfloat qa[4], pa[4];\n\tunsigned lo, hi, neg[4], par = 0;\n\tdouble all, big, two;\n\tqa[0] = q.x; qa[1] = q.y; qa[2] = q.z; qa[3] = q.w;\n\tfor (i=0; i<4; i++) {\n\t    pa[i] = 0.0;\n\t    if ((neg[i] = (qa[i]<0.0)) != 0) qa[i] = -qa[i];\n\t    par ^= neg[i];\n\t}\n\t/* Find two largest components, indices in hi and lo */\n\tif (qa[0]>qa[1]) lo = 0; else lo = 1;\n\tif (qa[2]>qa[3]) hi = 2; else hi = 3;\n\tif (qa[lo]>qa[hi]) {\n\t    if (qa[lo^1]>qa[hi]) {hi = lo; lo ^= 1;}\n\t    else {hi ^= lo; lo ^= hi; hi ^= lo;}\n\t} else {if (qa[hi^1]>qa[lo]) lo = hi^1;}\n\tall = (qa[0]+qa[1]+qa[2]+qa[3])*0.5;\n\ttwo = (qa[hi]+qa[lo])*SQRTHALF;\n\tbig = qa[hi];\n\tif (all>two) {\n\t    if (all>big) {/*all*/\n\t\t{int i; for (i=0; i<4; i++) pa[i] = sgn(neg[i], 0.5);}\n\t\tcycle(ka,par)\n\t    } else {/*big*/ pa[hi] = sgn(neg[hi],1.0);}\n\t} else {\n\t    if (two>big) {/*two*/\n\t\tpa[hi] = sgn(neg[hi],SQRTHALF); pa[lo] = sgn(neg[lo], SQRTHALF);\n\t\tif (lo>hi) {hi ^= lo; lo ^= hi; hi ^= lo;}\n\t\tif (hi==W) {hi = \"\\001\\002\\000\"[lo]; lo = 3-hi-lo;}\n\t\tswap(ka,hi,lo)\n\t    } else {/*big*/ pa[hi] = sgn(neg[hi],1.0);}\n\t}\n\tp.x = -pa[0]; p.y = -pa[1]; p.z = -pa[2]; p.w = pa[3];\n    }\n    k->x = ka[X]; k->y = ka[Y]; k->z = ka[Z];\n    return (p);\n}\n\n\n\n\n\n\n\n\n\n\n\n/******* Decompose Affine Matrix *******/\n\n/* Decompose 4x4 affine matrix A as TFRUK(U transpose), where t contains the\n * translation components, q contains the rotation R, u contains U, k contains\n * scale factors, and f contains the sign of the determinant.\n * Assumes A transforms column vectors in right-handed coordinates.\n * See Ken Shoemake and Tom Duff. Matrix Animation and Polar Decomposition.\n * Proceedings of Graphics Interface 1992.\n */\nvoid decomp_affine(HMatrix A, AffineParts *parts)\n{\n    HMatrix Q, S, U;\n    Quat p;\n    float det;\n    parts->t = Qt_(A[X][W], A[Y][W], A[Z][W], 0);\n    det = polar_decomp(A, Q, S);\n    if (det<0.0) {\n\tmat_copy(Q,=,-Q,3);\n\tparts->f = -1;\n    } else parts->f = 1;\n    parts->q = Qt_FromMatrix(Q);\n    parts->k = spect_decomp(S, U);\n    parts->u = Qt_FromMatrix(U);\n    p = snuggle(parts->u, &parts->k);\n    parts->u = Qt_Mul(parts->u, p);\n}\n\n/******* Invert Affine Decomposition *******/\n\n/* Compute inverse of affine decomposition.\n */\nvoid invert_affine(AffineParts *parts, AffineParts *inverse)\n{\n    Quat t, p;\n    inverse->f = parts->f;\n    inverse->q = Qt_Conj(parts->q);\n    inverse->u = Qt_Mul(parts->q, parts->u);\n    inverse->k.x = (parts->k.x==0.0) ? 0.0 : 1.0/parts->k.x;\n    inverse->k.y = (parts->k.y==0.0) ? 0.0 : 1.0/parts->k.y;\n    inverse->k.z = (parts->k.z==0.0) ? 0.0 : 1.0/parts->k.z;\n    inverse->k.w = parts->k.w;\n    t = Qt_(-parts->t.x, -parts->t.y, -parts->t.z, 0);\n    t = Qt_Mul(Qt_Conj(inverse->u), Qt_Mul(t, inverse->u));\n    t = Qt_(inverse->k.x*t.x, inverse->k.y*t.y, inverse->k.z*t.z, 0);\n    p = Qt_Mul(inverse->q, inverse->u);\n    t = Qt_Mul(p, Qt_Mul(t, Qt_Conj(p)));\n    inverse->t = (inverse->f>0.0) ? t : Qt_(-t.x, -t.y, -t.z, 0);\n}\n"
  },
  {
    "path": "fpsgame/Decompose.h",
    "content": "/* Copyright (C) 2009 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n/**** Decompose.h - Basic declarations ****/\n#ifndef INCLUDED_DECOMPOSE\n#define INCLUDED_DECOMPOSE\ntypedef struct {float x, y, z, w;} Quat; /* Quaternion */\nenum QuatPart {X, Y, Z, W};\ntypedef Quat HVect; /* Homogeneous 3D vector */\ntypedef float HMatrix[4][4]; /* Right-handed, for column vectors */\ntypedef struct {\n    HVect t;\t/* Translation components */\n    Quat  q;\t/* Essential rotation\t  */\n    Quat  u;\t/* Stretch rotation\t  */\n    HVect k;\t/* Stretch factors\t  */\n    float f;\t/* Sign of determinant\t  */\n} AffineParts;\nfloat polar_decomp(HMatrix M, HMatrix Q, HMatrix S);\nHVect spect_decomp(HMatrix S, HMatrix U);\nQuat snuggle(Quat q, HVect *k);\nvoid decomp_affine(HMatrix A, AffineParts *parts);\nvoid invert_affine(AffineParts *parts, AffineParts *inverse);\n#endif\n"
  },
  {
    "path": "fpsgame/Dll.cpp",
    "content": "/* Copyright (C) 2011 Wildfire Games.\n* This file is part of 0 A.D.\n*\n* 0 A.D. is free software: you can redistribute it and/or modify\n* it under the terms of the GNU General Public License as published by\n* the Free Software Foundation, either version 2 of the License, or\n* (at your option) any later version.\n*\n* 0 A.D. is distributed in the hope that it will be useful,\n* but WITHOUT ANY WARRANTY; without even the implied warranty of\n* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n* GNU General Public License for more details.\n*\n* You should have received a copy of the GNU General Public License\n* along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#include \"precompiled.h\"\n\n#include \"CommonConvert.h\"\n#include \"PMDConvert.h\"\n#include \"PSAConvert.h\"\n#include \"StdSkeletons.h\"\n\n#include <cstdarg>\n#include <cassert>\n\nvoid default_logger(void*, int severity, const char* message)\n{\n\tfprintf(stderr, \"[%d] %s\\n\", severity, message);\n}\n\nstatic LogFn g_Logger = &default_logger;\nstatic void* g_LoggerCBData = NULL;\n\nEXPORT void set_logger(LogFn logger, void* cb_data)\n{\n\tif (logger)\n\t{\n\t\tg_Logger = logger;\n\t\tg_LoggerCBData = cb_data;\n\t}\n\telse\n\t{\n\t\tg_Logger = &default_logger;\n\t\tg_LoggerCBData = NULL;\n\t}\n}\n\nEXPORT void poll_logger()\n{\n\tif (!g_Logger)\n\t{\n\t\tg_Logger = NULL;\n\t\tg_LoggerCBData;\n\t}\n}\n\nvoid Log(int severity, const char* msg, ...)\n{\n\tchar buffer[1024];\n\tva_list ap;\n\tva_start(ap, msg);\n\tvsnprintf(buffer, sizeof(buffer), msg, ap);\n\tbuffer[sizeof(buffer) - 1] = '\\0';\n\tva_end(ap);\n\n\tg_Logger(g_LoggerCBData, severity, buffer);\n}\n\nstruct BufferedOutputCallback : public OutputCB\n{\n\tstatic const unsigned int bufferSize = 4096;\n\tchar buffer[bufferSize];\n\tunsigned int bufferUsed;\n\n\tOutputFn fn;\n\tvoid* cb_data;\n\n\tBufferedOutputCallback(OutputFn fn, void* cb_data)\n\t\t: fn(fn), cb_data(cb_data), bufferUsed(0)\n\t{\n\t}\n\n\t~BufferedOutputCallback()\n\t{\n\t\t// flush the buffer if it's not empty\n\t\tif (bufferUsed > 0)\n\t\t\tfn(cb_data, buffer, bufferUsed);\n\t}\n\n\tvirtual void operator() (const char* data, unsigned int length)\n\t{\n\t\tif (bufferUsed + length > bufferSize)\n\t\t{\n\t\t\t// will overflow buffer, so flush the buffer first\n\t\t\tfn(cb_data, buffer, bufferUsed);\n\t\t\tbufferUsed = 0;\n\n\t\t\tif (length > bufferSize)\n\t\t\t{\n\t\t\t\t// new data won't fit in buffer, so send it out unbuffered\n\t\t\t\tfn(cb_data, data, length);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\t// append onto buffer\n\t\tmemcpy(buffer + bufferUsed, data, length);\n\t\tbufferUsed += length;\n\t\tassert(bufferUsed <= bufferSize);\n\t}\n};\n\nint convert_dae_to_whatever(const char* dae, OutputFn writer, void* cb_data, void(*conv)(const char*, OutputCB&, std::string&))\n{\n\tLog(LOG_INFO, \"Starting conversion\");\n\n\tFCollada::Initialize();\n\n\tstd::string xmlErrors;\n\tBufferedOutputCallback cb(writer, cb_data);\n\ttry\n\t{\n\t\tconv(dae, cb, xmlErrors);\n\t}\n\tcatch (const ColladaException& e)\n\t{\n\t\tif (!xmlErrors.empty())\n\t\t\tLog(LOG_ERROR, \"%s\", xmlErrors.c_str());\n\n\t\tLog(LOG_ERROR, \"%s\", e.what());\n\n\t\tFCollada::Release();\n\n\t\treturn -2;\n\t}\n\n\tFCollada::Release();\n\n\tif (!xmlErrors.empty())\n\t{\n\t\tLog(LOG_ERROR, \"%s\", xmlErrors.c_str());\n\n\t\treturn -1;\n\t}\n\n\treturn 0;\n}\n\nEXPORT int convert_dae_to_pmd(const char* dae, OutputFn pmd_writer, void* cb_data)\n{\n\treturn convert_dae_to_whatever(dae, pmd_writer, cb_data, ColladaToPMD);\n}\n\nEXPORT int convert_dae_to_psa(const char* dae, OutputFn psa_writer, void* cb_data)\n{\n\treturn convert_dae_to_whatever(dae, psa_writer, cb_data, ColladaToPSA);\n}\n\nEXPORT int set_skeleton_definitions(const char* xml, int length)\n{\n\tstd::string xmlErrors;\n\ttry\n\t{\n\t\tSkeleton::LoadSkeletonDataFromXml(xml, length, xmlErrors);\n\t}\n\tcatch (const ColladaException& e)\n\t{\n\t\tif (!xmlErrors.empty())\n\t\t\tLog(LOG_ERROR, \"%s\", xmlErrors.c_str());\n\n\t\tLog(LOG_ERROR, \"%s\", e.what());\n\n\t\treturn -1;\n\t}\n\n\treturn 0;\n}\n"
  },
  {
    "path": "fpsgame/Dll.h",
    "content": "#pragma once\n/* Copyright (C) 2015 Wildfire Games.\n* This file is part of 0 A.D.\n*\n* 0 A.D. is free software: you can redistribute it and/or modify\n* it under the terms of the GNU General Public License as published by\n* the Free Software Foundation, either version 2 of the License, or\n* (at your option) any later version.\n*\n* 0 A.D. is distributed in the hope that it will be useful,\n* but WITHOUT ANY WARRANTY; without even the implied warranty of\n* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n* GNU General Public License for more details.\n*\n* You should have received a copy of the GNU General Public License\n* along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#ifndef INCLUDED_COLLADA_DLL\n#define INCLUDED_COLLADA_DLL\n\n#ifdef _WIN32\n# ifdef COLLADA_DLL\n#  define EXPORT extern \"C\" __declspec(dllexport)\n# else\n#  define EXPORT extern \"C\" __declspec(dllimport)\n# endif\n#elif defined(__GNUC__)\n# define EXPORT extern \"C\" __attribute__ ((visibility (\"default\")))\n#else\n# define EXPORT extern \"C\"\n#endif\n\n#define LOG_INFO 0\n#define LOG_WARNING 1\n#define LOG_ERROR 2\n\ntypedef void(*LogFn) (void* cb_data, int severity, const char* text);\ntypedef void(*OutputFn) (void* cb_data, const char* data, unsigned int length);\n\n/* This version number should be bumped whenever incompatible changes\n* are made, to invalidate old caches. */\n#define COLLADA_CONVERTER_VERSION 3\n\nEXPORT void set_logger(LogFn logger, void* cb_data);\nEXPORT int set_skeleton_definitions(const char* xml, int length);\nEXPORT int convert_dae_to_pmd(const char* dae, OutputFn pmd_writer, void* cb_data);\nEXPORT int convert_dae_to_psa(const char* dae, OutputFn psa_writer, void* cb_data);\nEXPORT void poll_logger(void* cb_data);\n#endif /* INCLUDED_COLLADA_DLL */\n#pragma once\n"
  },
  {
    "path": "fpsgame/GeomReindex.cpp",
    "content": "/* Copyright (C) 2009 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"precompiled.h\"\n\n#include \"GeomReindex.h\"\n\n#include \"FCollada.h\"\n#include \"FCDocument/FCDEntity.h\"\n#include \"FCDocument/FCDGeometryMesh.h\"\n#include \"FCDocument/FCDGeometryPolygons.h\"\n#include \"FCDocument/FCDGeometryPolygonsInput.h\"\n#include \"FCDocument/FCDGeometrySource.h\"\n#include \"FCDocument/FCDSkinController.h\"\n\n#include <cassert>\n#include <vector>\n#include <map>\n#include <algorithm>\n\ntypedef std::pair<float, float> uv_pair_type;\n\nstruct VertexData\n{\n\tVertexData(const float* pos, const float* norm, const std::vector<uv_pair_type> &uvs, \n\t\t   const std::vector<FCDJointWeightPair>& weights)\n\t\t: x(pos[0]), y(pos[1]), z(pos[2]),\n\t\tnx(norm[0]), ny(norm[1]), nz(norm[2]),\n\t\tuvs(uvs),\n\t\tweights(weights)\n\t{\n\t}\n\n\tfloat x, y, z;\n\tfloat nx, ny, nz;\n\tstd::vector<uv_pair_type> uvs;\n\tstd::vector<FCDJointWeightPair> weights;\n};\n\nbool similar(float a, float b)\n{\n\treturn (fabsf(a - b) < 0.000001f);\n}\n\nbool operator==(const FCDJointWeightPair& a, const FCDJointWeightPair& b)\n{\n\treturn (a.jointIndex == b.jointIndex && similar(a.weight, b.weight));\n}\n\nbool operator<(const FCDJointWeightPair& a, const FCDJointWeightPair& b)\n{\n\t// Sort by decreasing weight, then by increasing joint ID\n\tif (a.weight > b.weight)\n\t\treturn true;\n\telse if (a.weight < b.weight)\n\t\treturn false;\n\telse if (a.jointIndex < b.jointIndex)\n\t\treturn true;\n\telse\n\t\treturn false;\n}\n\nbool operator==(const uv_pair_type& a, const uv_pair_type& b)\n{\n\treturn similar(a.first, b.first) && similar(a.second, b.second);\n}\n\nbool operator==(const VertexData& a, const VertexData& b)\n{\n\treturn (similar(a.x,  b.x)  && similar(a.y,  b.y)  && similar(a.z,  b.z)\n\t\t && similar(a.nx, b.nx) && similar(a.ny, b.ny) && similar(a.nz, b.nz)\n\t\t && (a.uvs == b.uvs)\n\t\t && (a.weights == b.weights));\n}\n\nbool operator<(const VertexData& a, const VertexData& b)\n{\n#define CMP(f) if (a.f < b.f) return true; if (a.f > b.f) return false\n\tCMP(x);  CMP(y);  CMP(z);\n\tCMP(nx); CMP(ny); CMP(nz);\n\tCMP(uvs);\n\tCMP(weights);\n#undef CMP\n\treturn false;\n}\n\ntemplate <typename T>\nstruct InserterWithoutDuplicates\n{\n\tInserterWithoutDuplicates(std::vector<T>& vec) : vec(vec)\n\t{\n\t}\n\n\tsize_t add(const T& val)\n\t{\n\t\ttypename std::map<T, size_t>::iterator it = btree.find(val);\n\t\tif (it != btree.end())\n\t\t\treturn it->second;\n\n\t\tsize_t idx = vec.size();\n\t\tvec.push_back(val);\n\t\tbtree.insert(std::make_pair(val, idx));\n\t\treturn idx;\n\t}\n\n\tstd::vector<T>& vec;\n\tstd::map<T, size_t> btree; // for faster lookups (so we can build a duplicate-free list in O(n log n) instead of O(n^2))\n\nprivate:\n\tInserterWithoutDuplicates& operator=(const InserterWithoutDuplicates&);\n};\n\nvoid CanonicaliseWeights(std::vector<FCDJointWeightPair>& weights)\n{\n\t// Convert weight-lists into a standard format, so simple vector equality\n\t// can be used to determine equivalence\n\tstd::sort(weights.begin(), weights.end());\n}\n\nvoid ReindexGeometry(FCDGeometryPolygons* polys, FCDSkinController* skin)\n{\n\t// Given geometry with:\n\t//   positions, normals, texcoords, bone blends\n\t// each with their own data array and index array, change it to\n\t// have a single optimised index array shared by all vertexes.\n\n\tFCDGeometryPolygonsInput* inputPosition = polys->FindInput(FUDaeGeometryInput::POSITION);\n\tFCDGeometryPolygonsInput* inputNormal   = polys->FindInput(FUDaeGeometryInput::NORMAL);\n\tFCDGeometryPolygonsInput* inputTexcoord = polys->FindInput(FUDaeGeometryInput::TEXCOORD);\n\n\tsize_t numVertices = polys->GetFaceVertexCount();\n\n\tassert(inputPosition->GetIndexCount() == numVertices);\n\tassert(inputNormal  ->GetIndexCount() == numVertices);\n\tassert(inputTexcoord->GetIndexCount() == numVertices);\n\n\tconst uint32* indicesPosition = inputPosition->GetIndices();\n\tconst uint32* indicesNormal   = inputNormal->GetIndices();\n\tconst uint32* indicesTexcoord = inputTexcoord->GetIndices();\n\n\tassert(indicesPosition);\n\tassert(indicesNormal);\n\tassert(indicesTexcoord); // TODO - should be optional, because textureless meshes aren't unreasonable\n\n\tFCDGeometrySourceList texcoordSources;\n\tpolys->GetParent()->FindSourcesByType(FUDaeGeometryInput::TEXCOORD, texcoordSources);\n\n\tFCDGeometrySource* sourcePosition = inputPosition->GetSource();\n\tFCDGeometrySource* sourceNormal   = inputNormal  ->GetSource();\n\n\tconst float* dataPosition = sourcePosition->GetData();\n\tconst float* dataNormal   = sourceNormal  ->GetData();\n\n\tif (skin)\n\t{\n#ifndef NDEBUG\n\t\tsize_t numVertexPositions = sourcePosition->GetDataCount() / sourcePosition->GetStride();\n\t\tassert(skin->GetInfluenceCount() == numVertexPositions);\n#endif\n\t}\n\n\tuint32 stridePosition = sourcePosition->GetStride();\n\tuint32 strideNormal   = sourceNormal  ->GetStride();\n\n\tstd::vector<uint32> indicesCombined;\n\tstd::vector<VertexData> vertexes;\n\tInserterWithoutDuplicates<VertexData> inserter(vertexes);\n\n\tfor (size_t i = 0; i < numVertices; ++i)\n\t{\n\t\tstd::vector<FCDJointWeightPair> weights;\n\t\tif (skin)\n\t\t{\n\t\t\tFCDSkinControllerVertex* influences = skin->GetVertexInfluence(indicesPosition[i]);\n\t\t\tassert(influences != NULL);\n\t\t\tfor (size_t j = 0; j < influences->GetPairCount(); ++j)\n\t\t\t{\n\t\t\t\tFCDJointWeightPair* pair = influences->GetPair(j);\n\t\t\t\tassert(pair != NULL);\n\t\t\t\tweights.push_back(*pair);\n\t\t\t}\n\t\t\tCanonicaliseWeights(weights);\n\t\t}\n\n\t\tstd::vector<uv_pair_type> uvs;\n\t\tfor (size_t set = 0; set < texcoordSources.size(); ++set)\n\t\t{\n\t\t\tconst float* dataTexcoord = texcoordSources[set]->GetData();\n\t\t\tuint32 strideTexcoord = texcoordSources[set]->GetStride();\n\t\t\t\n\t\t\tuv_pair_type p;\n\t\t\tp.first = dataTexcoord[indicesTexcoord[i]*strideTexcoord];\n\t\t\tp.second = dataTexcoord[indicesTexcoord[i]*strideTexcoord + 1];\n\t\t\tuvs.push_back(p);\n\t\t}\n\n\t\tVertexData vtx (\n\t\t\t&dataPosition[indicesPosition[i]*stridePosition],\n\t\t\t&dataNormal  [indicesNormal  [i]*strideNormal],\n\t\t\tuvs,\n\t\t\tweights\n\t\t);\n\t\tsize_t idx = inserter.add(vtx);\n\t\tindicesCombined.push_back((uint32)idx);\n\t}\n\n\t// TODO: rearrange indicesCombined (and rearrange vertexes to match) to use\n\t// the vertex cache efficiently\n\t// (<http://home.comcast.net/~tom_forsyth/papers/fast_vert_cache_opt.html> etc)\n\n\tFloatList newDataPosition;\n\tFloatList newDataNormal;\n\tFloatList newDataTexcoord;\n\tstd::vector<std::vector<FCDJointWeightPair> > newWeightedMatches;\n\n\tfor (size_t i = 0; i < vertexes.size(); ++i)\n\t{\n\t\tnewDataPosition.push_back(vertexes[i].x);\n\t\tnewDataPosition.push_back(vertexes[i].y);\n\t\tnewDataPosition.push_back(vertexes[i].z);\n\t\tnewDataNormal  .push_back(vertexes[i].nx);\n\t\tnewDataNormal  .push_back(vertexes[i].ny);\n\t\tnewDataNormal  .push_back(vertexes[i].nz);\n\t\tnewWeightedMatches.push_back(vertexes[i].weights);\n\t}\n\n\t// (Slightly wasteful to duplicate this array so many times, but FCollada\n\t// doesn't seem to support multiple inputs with the same source data)\n\tinputPosition->SetIndices(&indicesCombined.front(), indicesCombined.size());\n\tinputNormal  ->SetIndices(&indicesCombined.front(), indicesCombined.size());\n\tinputTexcoord->SetIndices(&indicesCombined.front(), indicesCombined.size());\n\n\tfor (size_t set = 0; set < texcoordSources.size(); ++set)\n\t{\n\t\tnewDataTexcoord.clear();\n\t\tfor (size_t i = 0; i < vertexes.size(); ++i)\n\t\t{\n\t\t\tnewDataTexcoord.push_back(vertexes[i].uvs[set].first);\n\t\t\tnewDataTexcoord.push_back(vertexes[i].uvs[set].second);\n\t\t}\n\t\ttexcoordSources[set]->SetData(newDataTexcoord, 2);\n\t}\n\n\tsourcePosition->SetData(newDataPosition, 3);\n\tsourceNormal  ->SetData(newDataNormal,   3);\n\n\tif (skin)\n\t{\n\t\tskin->SetInfluenceCount(newWeightedMatches.size());\n\t\tfor (size_t i = 0; i < newWeightedMatches.size(); ++i)\n\t\t{\n\t\t\tskin->GetVertexInfluence(i)->SetPairCount(0);\n\t\t\tfor (size_t j = 0; j < newWeightedMatches[i].size(); ++j)\n\t\t\t\tskin->GetVertexInfluence(i)->AddPair(newWeightedMatches[i][j].jointIndex, newWeightedMatches[i][j].weight);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "fpsgame/GeomReindex.h",
    "content": "/* Copyright (C) 2009 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef INCLUDED_GEOMREINDEX\n#define INCLUDED_GEOMREINDEX\n\nclass FCDGeometryPolygons;\nclass FCDSkinController;\n\nvoid ReindexGeometry(FCDGeometryPolygons* polys, FCDSkinController* skin = 0);\n\n#endif // INCLUDED_GEOMREINDEX\n"
  },
  {
    "path": "fpsgame/JSInterfaace_GameView.h",
    "content": "/* Copyright (C) 2013 Wildfire Games.\n* This file is part of 0 A.D.\n*\n* 0 A.D. is free software: you can redistribute it and/or modify\n* it under the terms of the GNU General Public License as published by\n* the Free Software Foundation, either version 2 of the License, or\n* (at your option) any later version.\n*\n* 0 A.D. is distributed in the hope that it will be useful,\n* but WITHOUT ANY WARRANTY; without even the implied warranty of\n* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n* GNU General Public License for more details.\n*\n* You should have received a copy of the GNU General Public License\n* along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n\n#ifndef INCLUDED_JSINTERFACE_GAMEVIEW\n#define INCLUDED_JSINTERFACE_GAMEVIEW\n\n#include \"ps/CStr.h\"\n#include \"scriptinterface/ScriptInterface.h\"\n\n#define DECLARE_BOOLEAN_SCRIPT_SETTING(NAME) \\\n\tbool Get##NAME##Enabled(ScriptInterface::CxPrivate* pCxPrivate); \\\n\tvoid Set##NAME##Enabled(ScriptInterface::CxPrivate* pCxPrivate, bool Enabled);\n\nnamespace JSI_GameView\n{\n\tvoid RegisterScriptFunctions(ScriptInterface& ScriptInterface);\n\n\tDECLARE_BOOLEAN_SCRIPT_SETTING(Culling);\n\tDECLARE_BOOLEAN_SCRIPT_SETTING(LockCullCamera);\n\tDECLARE_BOOLEAN_SCRIPT_SETTING(ConstrainCamera);\n}\n\n#undef DECLARE_BOOLEAN_SCRIPT_SETTING\n\n#endif\n\n"
  },
  {
    "path": "fpsgame/JSInterface_GameView.cpp",
    "content": "/* Copyright (C) 2013 Wildfire Games.\n* This file is part of 0 A.D.\n*\n* 0 A.D. is free software: you can redistribute it and/or modify\n* it under the terms of the GNU General Public License as published by\n* the Free Software Foundation, either version 2 of the License, or\n* (at your option) any later version.\n*\n* 0 A.D. is distributed in the hope that it will be useful,\n* but WITHOUT ANY WARRANTY; without even the implied warranty of\n* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n* GNU General Public License for more details.\n*\n* You should have received a copy of the GNU General Public License\n* along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#include \"precompiled.h\"\n\n#include \"JSInterface_GameView.h\"\n\n#include \"graphics/GameView.h\"\n#include \"ps/Game.h\"\n#include \"ps/CLogger.h\"\n#include \"ps/Profile.h\"\n#include \"scriptinterface/ScriptInterface.h\"\n\n#define IMPLEMENT_BOOLEAN_SCRIPT_SETTING(NAME) \\\nbool JSI_GameView::Get##NAME##Enabled(ScriptInterface::CxPrivate* UNUSED(pCxPrivate)) \\\n{ \\\n\tif (!g_Game || !g_Game->GetView()) \\\n\t\t{ \\\n\t\tLOGERROR(\"Trying to get a setting from GameView when it's not initialized!\"); \\\n\t\treturn false; \\\n\t\t} \\\n\treturn g_Game->GetView()->Get##NAME##Enabled(); \\\n} \\\n\\\nvoid JSI_GameView::Set##NAME##Enabled(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), bool Enabled) \\\n{ \\\n\tif (!g_Game || !g_Game->GetView()) \\\n\t\t{ \\\n\t\tLOGERROR(\"Trying to set a setting of GameView when it's not initialized!\"); \\\n\t\treturn; \\\n\t\t} \\\n\tg_Game->GetView()->Set##NAME##Enabled(Enabled); \\\n}\n\nIMPLEMENT_BOOLEAN_SCRIPT_SETTING(Culling);\nIMPLEMENT_BOOLEAN_SCRIPT_SETTING(LockCullCamera);\nIMPLEMENT_BOOLEAN_SCRIPT_SETTING(ConstrainCamera);\n\n#undef IMPLEMENT_BOOLEAN_SCRIPT_SETTING\n\n\n#define REGISTER_BOOLEAN_SCRIPT_SETTING(NAME) \\\nscriptInterface.RegisterFunction<bool, &JSI_GameView::Get##NAME##Enabled>(\"GameView_Get\" #NAME \"Enabled\"); \\\nscriptInterface.RegisterFunction<void, bool, &JSI_GameView::Set##NAME##Enabled>(\"GameView_Set\" #NAME \"Enabled\");\n\nvoid JSI_GameView::RegisterScriptFunctions(ScriptInterface& scriptInterface)\n{\n\tREGISTER_BOOLEAN_SCRIPT_SETTING(Culling);\n\tREGISTER_BOOLEAN_SCRIPT_SETTING(LockCullCamera);\n\tREGISTER_BOOLEAN_SCRIPT_SETTING(ConstrainCamera);\n}\n\n#undef REGISTER_BOOLEAN_SCRIPT_SETTING\n\n\n\n"
  },
  {
    "path": "fpsgame/Math.cpp",
    "content": "/* Copyright (C) 2009 Wildfire Games.\n* This file is part of 0 A.D.\n*\n* 0 A.D. is free software: you can redistribute it and/or modify\n* it under the terms of the GNU General Public License as published by\n* the Free Software Foundation, either version 2 of the License, or\n* (at your option) any later version.\n*\n* 0 A.D. is distributed in the hope that it will be useful,\n* but WITHOUT ANY WARRANTY; without even the implied warranty of\n* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n* GNU General Public License for more details.\n*\n* You should have received a copy of the GNU General Public License\n* along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#include \"precompiled.h\"\n\n#include \"Maths.h\"\n\n#include \"FCollada.h\"\n\n\nvoid DumpMatrix(const FMMatrix44& m)\n{\n\tLog(LOG_INFO, \"\\n[%f %f %f %f]\\n[%f %f %f %f]\\n[%f %f %f %f]\\n[%f %f %f %f]\",\n\t\tm.m[0][0], m.m[0][1], m.m[0][2], m.m[0][3],\n\t\tm.m[1][0], m.m[1][1], m.m[1][2], m.m[1][3],\n\t\tm.m[2][0], m.m[2][1], m.m[2][2], m.m[2][3],\n\t\tm.m[3][0], m.m[3][1], m.m[3][2], m.m[3][3]\n\t);\n}\n\nFMMatrix44 DecomposeToScaleMatrix(const FMMatrix44& m)\n{\n\tFMVector3 scale, rotation, translation;\n\tfloat inverted;\n\tm.Decompose(scale, rotation, translation, inverted);\n\treturn FMMatrix44::ScaleMatrix(scale);\n}\n\n/*\nFMMatrix44 operator+ (const FMMatrix44& a, const FMMatrix44& b)\n{\nFMMatrix44 r;\nfor (int x = 0; x < 4; ++x)\nfor (int y = 0; y < 4; ++y)\nr[x][y] = a[x][y] + b[x][y];\nreturn r;\n}\n\nFMMatrix44 operator/ (const FMMatrix44& a, const float b)\n{\nFMMatrix44 r;\nfor (int x = 0; x < 4; ++x)\nfor (int y = 0; y < 4; ++y)\nr[x][y] = a[x][y] / b;\nreturn r;\n}\n\nFMMatrix44 QuatToMatrix(float x, float y, float z, float w)\n{\nFMMatrix44 r;\n\nr[0][0] = 1.0f - (y*y*2 + z*z*2);\nr[1][0] = x*y*2 - w*z*2;\nr[2][0] = x*z*2 + w*y*2;\nr[3][0] = 0;\n\nr[0][1] = x*y*2 + w*z*2;\nr[1][1] = 1.0f - (x*x*2 + z*z*2);\nr[2][1] = y*z*2 - w*x*2;\nr[3][1] = 0;\n\nr[0][2] = x*z*2 - w*y*2;\nr[1][2] = y*z*2 + w*x*2;\nr[2][2] = 1.0f - (x*x*2 + y*y*2);\nr[3][2] = 0;\n\nr[0][3] = 0;\nr[1][3] = 0;\nr[2][3] = 0;\nr[3][3] = 1;\n\nreturn r;\n}\n*/\n"
  },
  {
    "path": "fpsgame/Math.h",
    "content": "#pragma once\r\n/* Copyright (C) 2009 Wildfire Games.\n* This file is part of 0 A.D.\n*\n* 0 A.D. is free software: you can redistribute it and/or modify\n* it under the terms of the GNU General Public License as published by\n* the Free Software Foundation, either version 2 of the License, or\n* (at your option) any later version.\n*\n* 0 A.D. is distributed in the hope that it will be useful,\n* but WITHOUT ANY WARRANTY; without even the implied warranty of\n* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n* GNU General Public License for more details.\n*\n* You should have received a copy of the GNU General Public License\n* along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#ifndef INCLUDED_MATHS\n#define INCLUDED_MATHS\n\nclass FMMatrix44;\n\nextern void DumpMatrix(const FMMatrix44& m);\nextern FMMatrix44 DecomposeToScaleMatrix(const FMMatrix44& m);\n\n// (None of these are used any more)\n// extern FMMatrix44 operator+ (const FMMatrix44& a, const FMMatrix44& b);\n// extern FMMatrix44 operator/ (const FMMatrix44& a, const float b);\n// extern FMMatrix44 QuatToMatrix(float x, float y, float z, float w);\n\n#endif // INCLUDED_MATHS"
  },
  {
    "path": "fpsgame/PMDConvert.cpp",
    "content": "/* Copyright (C) 2012 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"precompiled.h\"\n\n#include \"PMDConvert.h\"\n#include \"CommonConvert.h\"\n\n#include \"FCollada.h\"\n#include \"FCDocument/FCDAsset.h\"\n#include \"FCDocument/FCDocument.h\"\n#include \"FCDocument/FCDocumentTools.h\"\n#include \"FCDocument/FCDController.h\"\n#include \"FCDocument/FCDControllerInstance.h\"\n#include \"FCDocument/FCDGeometry.h\"\n#include \"FCDocument/FCDGeometryMesh.h\"\n#include \"FCDocument/FCDGeometryPolygons.h\"\n#include \"FCDocument/FCDGeometryPolygonsInput.h\"\n#include \"FCDocument/FCDGeometryPolygonsTools.h\"\n#include \"FCDocument/FCDGeometrySource.h\"\n#include \"FCDocument/FCDSceneNode.h\"\n#include \"FCDocument/FCDSkinController.h\"\n\n#include \"StdSkeletons.h\"\n#include \"Decompose.h\"\n#include \"Maths.h\"\n#include \"GeomReindex.h\"\n\n#include <cassert>\n#include <vector>\n#include <algorithm>\n\nconst size_t maxInfluences = 4;\nstruct VertexBlend\n{\n\tuint8 bones[maxInfluences];\n\tfloat weights[maxInfluences];\n};\nVertexBlend defaultInfluences = { { 0xFF, 0xFF, 0xFF, 0xFF }, { 0, 0, 0, 0 } };\n\nstruct PropPoint\n{\n\tstd::string name;\n\tfloat translation[3];\n\tfloat orientation[4];\n\tuint8 bone;\n};\n\n// Based on FMVector3::Normalize, but that function uses a static member\n// FMVector3::XAxis which causes irritating linker errors. Rather than trying\n// to make that XAxis work in a cross-platform way, just reimplement Normalize:\nstatic FMVector3 FMVector3_Normalize(const FMVector3& vec)\n{\n\tfloat l = vec.Length();\n\tif (l > 0.0f)\n\t\treturn FMVector3(vec.x/l, vec.y/l, vec.z/l);\n\telse\n\t\treturn FMVector3(1.0f, 0.0f, 0.0f);\n}\n\nstatic void AddStaticPropPoints(std::vector<PropPoint> &propPoints, const FMMatrix44& upAxisTransform, FCDSceneNode* node)\n{\n\tif (node->GetName().find(\"prop-\") == 0 || node->GetName().find(\"prop_\") == 0)\n\t{\n\t\t// Strip off the \"prop-\" from the name\n\t\tstd::string propPointName (node->GetName().substr(5));\n\n\t\tLog(LOG_INFO, \"Adding prop point %s\", propPointName.c_str());\n\n\t\t// CalculateWorldTransform applies transformations recursively for all parents of this node\n\t\t// upAxisTransform transforms this node to right-handed Z_UP coordinates\n\n\t\tFMMatrix44 transform = upAxisTransform * node->CalculateWorldTransform();\n\n\t\tHMatrix matrix;\n\t\tmemcpy(matrix, transform.Transposed().m, sizeof(matrix));\n\n\t\tAffineParts parts;\n\t\tdecomp_affine(matrix, &parts);\n\n\t\t// Add prop point in game coordinates\n\n\t\tPropPoint p = {\n\t\t\tpropPointName,\n\t\t\t\n\t\t\t// Flip translation across the x-axis by swapping y and z\n\t\t\t{ parts.t.x, parts.t.z, parts.t.y },\n\n\t\t\t// To convert the quaternions: imagine you're using the axis/angle\n\t\t\t// representation, then swap the y,z basis vectors and change the\n\t\t\t// direction of rotation by negating the angle ( => negating sin(angle)\n\t\t\t// => negating x,y,z => changing (x,y,z,w) to (-x,-z,-y,w)\n\t\t\t// but then (-x,-z,-y,w) == (x,z,y,-w) so do that instead)\n\t\t\t{ parts.q.x, parts.q.z, parts.q.y, -parts.q.w },\n\n\t\t\t0xff\n\t\t};\n\t\tpropPoints.push_back(p);\n\t}\n\n\t// Search children for prop points\n\tfor (size_t i = 0; i < node->GetChildrenCount(); ++i)\n\t\tAddStaticPropPoints(propPoints, upAxisTransform, node->GetChild(i));\n}\n\nclass PMDConvert\n{\npublic:\n\t/**\n\t * Converts a COLLADA XML document into the PMD mesh format.\n\t *\n\t * @param input XML document to parse\n\t * @param output callback for writing the PMD data; called lots of times\n\t *               with small strings\n\t * @param xmlErrors output - errors reported by the XML parser\n\t * @throws ColladaException on failure\n\t */\n\tstatic void ColladaToPMD(const char* input, OutputCB& output, std::string& xmlErrors)\n\t{\n\t\tCommonConvert converter(input, xmlErrors);\n\n\t\tif (converter.GetInstance().GetEntity()->GetType() == FCDEntity::GEOMETRY)\n\t\t{\n\t\t\tLog(LOG_INFO, \"Found static geometry\");\n\n\t\t\tFCDGeometryPolygons* polys = GetPolysFromGeometry((FCDGeometry*)converter.GetInstance().GetEntity());\n\n\t\t\t// Convert the geometry into a suitable form for the game\n\t\t\tReindexGeometry(polys);\n\n\t\t\tstd::vector<VertexBlend> boneWeights;\t// unused\n\t\t\tstd::vector<BoneTransform> boneTransforms;\t// unused\n\t\t\tstd::vector<PropPoint> propPoints;\n\n\t\t\t// Get the raw vertex data\n\n\t\t\tFCDGeometryPolygonsInput* inputPosition = polys->FindInput(FUDaeGeometryInput::POSITION);\n\t\t\tFCDGeometryPolygonsInput* inputNormal   = polys->FindInput(FUDaeGeometryInput::NORMAL);\n\n\t\t\tconst uint32* indicesCombined = inputPosition->GetIndices();\n\t\t\tsize_t indicesCombinedCount = inputPosition->GetIndexCount();\n\t\t\t// (ReindexGeometry guarantees position/normal/texcoord have the same indexes)\n\n\t\t\tFCDGeometrySource* sourcePosition = inputPosition->GetSource();\n\t\t\tFCDGeometrySource* sourceNormal   = inputNormal  ->GetSource();\n\t\t\t\n\t\t\tFCDGeometrySourceList texcoordSources;\n\t\t\tpolys->GetParent()->FindSourcesByType(FUDaeGeometryInput::TEXCOORD, texcoordSources);\n\n\t\t\tfloat* dataPosition = sourcePosition->GetData();\n\t\t\tfloat* dataNormal   = sourceNormal  ->GetData();\n\t\t\tsize_t vertexCount = sourcePosition->GetDataCount() / 3;\n\t\t\tassert(sourcePosition->GetDataCount() == vertexCount*3);\n\t\t\tassert(sourceNormal  ->GetDataCount() == vertexCount*3);\n\n\t\t\tstd::vector<float*> dataTexcoords;\n\t\t\tfor (size_t i = 0; i < texcoordSources.size(); ++i)\n\t\t\t{\n\t\t\t\tdataTexcoords.push_back(texcoordSources[i]->GetData());\n\t\t\t}\n\n\t\t\t// Transform mesh coordinate system to game coordinates\n\t\t\t// (doesn't modify prop points)\n\n\t\t\tTransformStaticModel(dataPosition, dataNormal, vertexCount, converter.GetEntityTransform(), converter.IsYUp());\n\n\t\t\t// Add static prop points\n\t\t\t//\twhich are empty child nodes of the main parent\n\n\t\t\t// Default prop points are already given in game coordinates\n\t\t\tAddDefaultPropPoints(propPoints);\n\t\t\t\n\t\t\t// Calculate transform to convert from COLLADA-defined up_axis to Z-up because\n\t\t\t//\tit's relatively straightforward to convert that to game coordinates\n\t\t\tFMMatrix44 upAxisTransform = FMMatrix44_Identity;\n\t\t\tif (converter.IsYUp())\n\t\t\t{\n\t\t\t\t// Prop points are rotated -90 degrees about the X-axis, reverse that rotation\n\t\t\t\t// (do this once now because it's easier than messing with quaternions later)\n\t\t\t\tupAxisTransform = FMMatrix44::XAxisRotationMatrix(1.57f);\n\t\t\t}\n\n\t\t\tAddStaticPropPoints(propPoints, upAxisTransform, converter.GetInstance().GetParent());\n\n\t\t\tWritePMD(output, indicesCombined, indicesCombinedCount, dataPosition, dataNormal, dataTexcoords, vertexCount, boneWeights, boneTransforms, propPoints);\n\t\t}\n\t\telse if (converter.GetInstance().GetType() == FCDEntityInstance::CONTROLLER)\n\t\t{\n\t\t\tLog(LOG_INFO, \"Found skinned geometry\");\n\n\t\t\tFCDControllerInstance& controllerInstance = static_cast<FCDControllerInstance&>(converter.GetInstance());\n\n\t\t\t// (NB: GetType is deprecated and should be replaced with HasType,\n\t\t\t// except that has irritating linker errors when using a DLL, so don't\n\t\t\t// bother)\n\t\t\t\n\t\t\tassert(converter.GetInstance().GetEntity()->GetType() == FCDEntity::CONTROLLER); // assume this is always true?\n\t\t\tFCDController* controller = static_cast<FCDController*>(converter.GetInstance().GetEntity());\n\n\t\t\tFCDSkinController* skin = controller->GetSkinController();\n\t\t\tREQUIRE(skin != NULL, \"is skin controller\");\n\n\t\t\tFixSkeletonRoots(controllerInstance);\n\n\t\t\t// Data for joints is stored in two places - avoid overflows by limiting\n\t\t\t// to the minimum of the two sizes, and warn if they're different (which\n\t\t\t// happens in practice for slightly-broken meshes)\n\t\t\tsize_t jointCount = std::min(skin->GetJointCount(), controllerInstance.GetJointCount());\n\t\t\tif (skin->GetJointCount() != controllerInstance.GetJointCount())\n\t\t\t{\n\t\t\t\tLog(LOG_WARNING, \"Mismatched bone counts (skin has %d, skeleton has %d)\", \n\t\t\t\t\tskin->GetJointCount(), controllerInstance.GetJointCount());\n\t\t\t\tfor (size_t i = 0; i < skin->GetJointCount(); ++i)\n\t\t\t\t\tLog(LOG_INFO, \"Skin joint %d: %s\", i, skin->GetJoint(i)->GetId().c_str());\n\t\t\t\tfor (size_t i = 0; i < controllerInstance.GetJointCount(); ++i)\n\t\t\t\t\tLog(LOG_INFO, \"Skeleton joint %d: %s\", i, controllerInstance.GetJoint(i)->GetName().c_str());\n\t\t\t}\n\n\t\t\t// Get the skinned mesh for this entity\n\t\t\tFCDGeometry* baseGeometry = controller->GetBaseGeometry();\n\t\t\tREQUIRE(baseGeometry != NULL, \"controller has base geometry\");\n\t\t\tFCDGeometryPolygons* polys = GetPolysFromGeometry(baseGeometry);\n\n\t\t\t// Make sure it doesn't use more bones per vertex than the game can handle\n\t\t\tSkinReduceInfluences(skin, maxInfluences, 0.001f);\n\n\t\t\t// Convert the geometry into a suitable form for the game\n\t\t\tReindexGeometry(polys, skin);\n\n\t\t\tconst Skeleton& skeleton = FindSkeleton(controllerInstance);\n\n\t\t\t// Convert the bone influences into VertexBlend structures for the PMD:\n\n\t\t\tbool hasComplainedAboutNonexistentJoints = false; // because we want to emit a warning only once\n\n\t\t\tstd::vector<VertexBlend> boneWeights; // one per vertex\n\n\t\t\tconst FCDSkinControllerVertex* vertexInfluences = skin->GetVertexInfluences();\n\t\t\tfor (size_t i = 0; i < skin->GetInfluenceCount(); ++i)\n\t\t\t{\n\t\t\t\tVertexBlend influences = defaultInfluences;\n\n\t\t\t\tassert(vertexInfluences[i].GetPairCount() <= maxInfluences);\n\t\t\t\t\t// guaranteed by ReduceInfluences; necessary for avoiding\n\t\t\t\t\t// out-of-bounds writes to the VertexBlend\n\n\t\t\t\tif (vertexInfluences[i].GetPairCount() == 0)\n\t\t\t\t{\n\t\t\t\t\t// Blender exports some models with vertices that have no influences,\n\t\t\t\t\t//\twhich I've not found details about in the COLLADA spec, however,\n\t\t\t\t\t//\tit seems to work OK to treat these vertices the same as if they\n\t\t\t\t\t//\twere only influenced by the bind-shape matrix (see comment below),\n\t\t\t\t\t//\tso we use the same special case here.\n\t\t\t\t\tinfluences.bones[0] = (uint8)jointCount;\n\t\t\t\t\tinfluences.weights[0] = 1.0f;\n\t\t\t\t}\n\n\t\t\t\tfor (size_t j = 0; j < vertexInfluences[i].GetPairCount(); ++j)\n\t\t\t\t{\n\t\t\t\t\tif (vertexInfluences[i].GetPair(j)->jointIndex == -1)\n\t\t\t\t\t{\n\t\t\t\t\t\t// This is a special case we must handle, according to the COLLADA spec:\n\t\t\t\t\t\t//\t\"An index of -1 into the array of joints refers to the bind shape\"\n\t\t\t\t\t\t//\n\t\t\t\t\t\t//\twhich basically means when skinning the vertex it's relative to the\n\t\t\t\t\t\t//\tbind-shape transform instead of an animated bone. Since our skinning\n\t\t\t\t\t\t//\tis in world space, we will have already applied the bind-shape transform,\n\t\t\t\t\t\t//\tso we don't have to worry about that, though we DO have to apply the\n\t\t\t\t\t\t//\tworld space transform of the model for the indicated vertex.\n\t\t\t\t\t\t//\n\t\t\t\t\t\t//\tTo indicate this special case, we use a bone ID set to the total number\n\t\t\t\t\t\t//\tof bones in the model, which will have a special \"bone matrix\" reserved\n\t\t\t\t\t\t//\tthat contains the world space transform of the model during skinning.\n\t\t\t\t\t\t//\t(see http://trac.wildfiregames.com/ticket/1012)\n\t\t\t\t\t\tinfluences.bones[j] = (uint8)jointCount;\n\t\t\t\t\t\tinfluences.weights[j] = vertexInfluences[i].GetPair(j)->weight;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\t// Check for less than 254 joints because we store them in a u8,\n\t\t\t\t\t\t//\t0xFF is a reserved value (no influence), and we reserve one slot\n\t\t\t\t\t\t//\tfor the above special case.\n\t\t\t\t\t\tuint32 jointIdx = vertexInfluences[i].GetPair(j)->jointIndex;\n\t\t\t\t\t\tREQUIRE(jointIdx < 0xFE, \"sensible number of joints (<254)\");\n\n\t\t\t\t\t\t// Find the joint on the skeleton, after checking it really exists\n\t\t\t\t\t\tFCDSceneNode* joint = NULL;\n\t\t\t\t\t\tif (jointIdx < controllerInstance.GetJointCount())\n\t\t\t\t\t\t\tjoint = controllerInstance.GetJoint(jointIdx);\n\n\t\t\t\t\t\t// Complain on error\n\t\t\t\t\t\tif (! joint)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (! hasComplainedAboutNonexistentJoints)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tLog(LOG_WARNING, \"Vertexes influenced by nonexistent joint\");\n\t\t\t\t\t\t\t\thasComplainedAboutNonexistentJoints = true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Store into the VertexBlend\n\t\t\t\t\t\tint boneId = skeleton.GetBoneID(joint->GetName().c_str());\n\t\t\t\t\t\tif (boneId < 0)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t// The relevant joint does exist, but it's not a recognised\n\t\t\t\t\t\t\t// bone in our chosen skeleton structure\n\t\t\t\t\t\t\tLog(LOG_ERROR, \"Vertex influenced by unrecognised bone '%s'\", joint->GetName().c_str());\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tinfluences.bones[j] = (uint8)boneId;\n\t\t\t\t\t\tinfluences.weights[j] = vertexInfluences[i].GetPair(j)->weight;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tboneWeights.push_back(influences);\n\t\t\t}\n\n\t\t\t// Convert the bind pose into BoneTransform structures for the PMD:\n\n\t\t\tBoneTransform boneDefault  = { { 0, 0, 0 }, { 0, 0, 0, 1 } }; // identity transform\n\t\t\tstd::vector<BoneTransform> boneTransforms (skeleton.GetBoneCount(), boneDefault);\n\n\t\t\tfor (size_t i = 0; i < jointCount; ++i)\n\t\t\t{\n\t\t\t\tFCDSceneNode* joint = controllerInstance.GetJoint(i);\n\n\t\t\t\tint boneId = skeleton.GetRealBoneID(joint->GetName().c_str());\n\t\t\t\tif (boneId < 0)\n\t\t\t\t{\n\t\t\t\t\t// unrecognised joint - it's probably just a prop point\n\t\t\t\t\t// or something, so ignore it\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tFMMatrix44 bindPose = skin->GetJoint(i)->GetBindPoseInverse().Inverted();\n\n\t\t\t\tHMatrix matrix;\n\t\t\t\tmemcpy(matrix, bindPose.Transposed().m, sizeof(matrix));\n\t\t\t\t\t// set matrix = bindPose^T, to match what decomp_affine wants\n\n\t\t\t\tAffineParts parts;\n\t\t\t\tdecomp_affine(matrix, &parts);\n\n\t\t\t\tBoneTransform b = {\n\t\t\t\t\t{ parts.t.x, parts.t.y, parts.t.z },\n\t\t\t\t\t{ parts.q.x, parts.q.y, parts.q.z, parts.q.w }\n\t\t\t\t};\n\n\t\t\t\tboneTransforms[boneId] = b;\n\t\t\t}\n\n\t\t\t// Construct the list of prop points.\n\t\t\t// Currently takes all objects that are directly attached to a\n\t\t\t// standard bone, and whose name begins with \"prop-\" or \"prop_\".\n\n\t\t\tstd::vector<PropPoint> propPoints;\n\t\t\tAddDefaultPropPoints(propPoints);\n\n\t\t\tfor (size_t i = 0; i < jointCount; ++i)\n\t\t\t{\n\t\t\t\tFCDSceneNode* joint = controllerInstance.GetJoint(i);\n\n\t\t\t\tint boneId = skeleton.GetBoneID(joint->GetName().c_str());\n\t\t\t\tif (boneId < 0)\n\t\t\t\t{\n\t\t\t\t\t// unrecognised joint name - ignore, same as before\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Check all the objects attached to this bone\n\t\t\t\tfor (size_t j = 0; j < joint->GetChildrenCount(); ++j)\n\t\t\t\t{\n\t\t\t\t\tFCDSceneNode* child = joint->GetChild(j);\n\t\t\t\t\tif (child->GetName().find(\"prop-\") != 0 && child->GetName().find(\"prop_\") != 0)\n\t\t\t\t\t{\n\t\t\t\t\t\t// doesn't begin with \"prop-\", so skip it\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\t// Strip off the \"prop-\" from the name\n\t\t\t\t\tstd::string propPointName (child->GetName().substr(5));\n\n\t\t\t\t\tLog(LOG_INFO, \"Adding prop point %s\", propPointName.c_str());\n\n\t\t\t\t\t// Get translation and orientation of local transform\n\n\t\t\t\t\tFMMatrix44 localTransform = child->ToMatrix();\n\n\t\t\t\t\tHMatrix matrix;\n\t\t\t\t\tmemcpy(matrix, localTransform.Transposed().m, sizeof(matrix));\n\n\t\t\t\t\tAffineParts parts;\n\t\t\t\t\tdecomp_affine(matrix, &parts);\n\n\t\t\t\t\t// Add prop point to list\n\n\t\t\t\t\tPropPoint p = {\n\t\t\t\t\t\tpropPointName,\n\t\t\t\t\t\t{ parts.t.x, parts.t.y, parts.t.z },\n\t\t\t\t\t\t{ parts.q.x, parts.q.y, parts.q.z, parts.q.w },\n\t\t\t\t\t\t(uint8)boneId\n\t\t\t\t\t};\n\t\t\t\t\tpropPoints.push_back(p);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Get the raw vertex data\n\n\t\t\tFCDGeometryPolygonsInput* inputPosition = polys->FindInput(FUDaeGeometryInput::POSITION);\n\t\t\tFCDGeometryPolygonsInput* inputNormal   = polys->FindInput(FUDaeGeometryInput::NORMAL);\n\n\t\t\tconst uint32* indicesCombined = inputPosition->GetIndices();\n\t\t\tsize_t indicesCombinedCount = inputPosition->GetIndexCount();\n\t\t\t// (ReindexGeometry guarantees position/normal/texcoord have the same indexes)\n\n\t\t\tFCDGeometrySource* sourcePosition = inputPosition->GetSource();\n\t\t\tFCDGeometrySource* sourceNormal   = inputNormal  ->GetSource();\n\n\t\t\tFCDGeometrySourceList texcoordSources;\n\t\t\tpolys->GetParent()->FindSourcesByType(FUDaeGeometryInput::TEXCOORD, texcoordSources);\n\n\t\t\tfloat* dataPosition = sourcePosition->GetData();\n\t\t\tfloat* dataNormal   = sourceNormal  ->GetData();\n\t\t\tsize_t vertexCount = sourcePosition->GetDataCount() / 3;\n\t\t\tassert(sourcePosition->GetDataCount() == vertexCount*3);\n\t\t\tassert(sourceNormal  ->GetDataCount() == vertexCount*3);\n\n\t\t\tstd::vector<float*> dataTexcoords;\n\t\t\tfor (size_t i = 0; i < texcoordSources.size(); ++i)\n\t\t\t{\n\t\t\t\tdataTexcoords.push_back(texcoordSources[i]->GetData());\n\t\t\t}\n\n\t\t\t// Transform model coordinate system to game coordinates\n\n\t\t\tTransformSkinnedModel(dataPosition, dataNormal, vertexCount, boneTransforms, propPoints,\n\t\t\t\tconverter.GetEntityTransform(), skin->GetBindShapeTransform(),\n\t\t\t\tconverter.IsYUp(), converter.IsXSI());\n\n\t\t\tWritePMD(output, indicesCombined, indicesCombinedCount, dataPosition, dataNormal, dataTexcoords, vertexCount, boneWeights, boneTransforms, propPoints);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tthrow ColladaException(\"Unrecognised object type\");\n\t\t}\n\n\t}\n\n\t/**\n\t * Adds the default \"root\" prop-point.\n\t */\n\tstatic void AddDefaultPropPoints(std::vector<PropPoint>& propPoints)\n\t{\n\t\tPropPoint root;\n\t\troot.name = \"root\";\n\t\troot.translation[0] = root.translation[1] = root.translation[2] = 0.0f;\n\t\troot.orientation[0] = root.orientation[1] = root.orientation[2] = 0.0f;\n\t\troot.orientation[3] = 1.0f;\n\t\troot.bone = 0xFF;\n\t\tpropPoints.push_back(root);\n\t}\n\n\t/**\n\t * Writes the model data in the PMD format.\n\t */\n\tstatic void WritePMD(OutputCB& output,\n\t\tconst uint32* indices, size_t indexCount,\n\t\tconst float* position, const float* normal,\n\t\tconst std::vector<float*>& texcoords,\n\t\tsize_t vertexCount,\n\t\tconst std::vector<VertexBlend>& boneWeights, const std::vector<BoneTransform>& boneTransforms,\n\t\tconst std::vector<PropPoint>& propPoints)\n\t{\n\t\tstatic const VertexBlend noBlend = { { 0xFF, 0xFF, 0xFF, 0xFF }, { 0, 0, 0, 0 } };\n\n\t\tsize_t faceCount = indexCount/3;\n\t\tsize_t boneCount = boneTransforms.size();\n\t\tif (boneCount)\n\t\t\tassert(boneWeights.size() == vertexCount);\n\n\t\tsize_t propPointsSize = 0; // can't calculate this statically, so loop over all the prop points\n\t\tfor (size_t i = 0; i < propPoints.size(); ++i)\n\t\t{\n\t\t\tpropPointsSize += 4 + propPoints[i].name.length();\n\t\t\tpropPointsSize += 3*4 + 4*4 + 1;\n\t\t}\n\n\t\toutput(\"PSMD\", 4);  // magic number\n\t\twrite(output, (uint32)4); // version number\n\t\twrite(output, (uint32)(\n\t\t\t// for UVs, we add one uint32 (i.e. 4 bytes) per model that gives the number of \n\t\t\t// texcoord sets in the model, plus 2 floats per new UV \n\t\t\t// pair per vertex (i.e. 8 bytes * number of pairs * vertex count) \n\t\t\t4 + 11*4*vertexCount + 4 + 8*texcoords.size()*vertexCount + // vertices\n\t\t\t4 + 6*faceCount + // faces\n\t\t\t4 + 7*4*boneCount + // bones\n\t\t\t4 + propPointsSize // props\n\t\t\t)); // data size\n\n\t\t// Vertex data\n\t\twrite<uint32>(output, (uint32)vertexCount);\n\t\twrite<uint32>(output, (uint32)texcoords.size()); // UV pairs per vertex\n\t\tfor (size_t i = 0; i < vertexCount; ++i)\n\t\t{\n\t\t\toutput((char*)&position[i*3], 12);\n\t\t\toutput((char*)&normal  [i*3], 12);\n\n\t\t\tfor (size_t s = 0; s < texcoords.size(); ++s)\n\t\t\t{\n\t\t\t\toutput((char*)&texcoords[s][i*2], 8);\n\t\t\t}\n\n\t\t\tif (boneCount)\n\t\t\t\twrite(output, boneWeights[i]);\n\t\t\telse\n\t\t\t\twrite(output, noBlend);\n\t\t}\n\n\t\t// Face data\n\t\twrite(output, (uint32)faceCount);\n\t\tfor (size_t i = 0; i < indexCount; ++i)\n\t\t{\n\t\t\twrite(output, (uint16)indices[i]);\n\t\t}\n\n\t\t// Bones data\n\t\twrite(output, (uint32)boneCount);\n\t\tfor (size_t i = 0; i < boneCount; ++i)\n\t\t{\n\t\t\toutput((char*)&boneTransforms[i], 7*4);\n\t\t}\n\n\t\t// Prop points data\n\t\twrite(output, (uint32)propPoints.size());\n\t\tfor (size_t i = 0; i < propPoints.size(); ++i)\n\t\t{\n\t\t\tuint32 nameLen = (uint32)propPoints[i].name.length();\n\t\t\twrite(output, nameLen);\n\t\t\toutput(propPoints[i].name.c_str(), nameLen);\n\t\t\twrite(output, propPoints[i].translation);\n\t\t\twrite(output, propPoints[i].orientation);\n\t\t\twrite(output, propPoints[i].bone);\n\t\t}\n\t}\n\n\tstatic FCDGeometryPolygons* GetPolysFromGeometry(FCDGeometry* geom)\n\t{\n\t\tREQUIRE(geom->IsMesh(), \"geometry is mesh\");\n\t\tFCDGeometryMesh* mesh = geom->GetMesh();\n\n \t\tif (! mesh->IsTriangles())\n \t\t\tFCDGeometryPolygonsTools::Triangulate(mesh);\n\t\t\n\t\tREQUIRE(mesh->IsTriangles(), \"mesh is made of triangles\");\n\t\tREQUIRE(mesh->GetPolygonsCount() == 1, \"mesh has single set of polygons\");\n\t\tFCDGeometryPolygons* polys = mesh->GetPolygons(0);\n\t\tREQUIRE(polys->FindInput(FUDaeGeometryInput::POSITION) != NULL, \"mesh has vertex positions\");\n\t\tREQUIRE(polys->FindInput(FUDaeGeometryInput::NORMAL) != NULL, \"mesh has vertex normals\");\n\t\tREQUIRE(polys->FindInput(FUDaeGeometryInput::TEXCOORD) != NULL, \"mesh has vertex tex coords\");\n\t\treturn polys;\n\t}\n\n\t/**\n\t * Applies world-space transform to vertex data and transforms Collada's right-handed\n\t *\tY-up / Z-up coordinates to the game's left-handed Y-up coordinate system\n\t *\n\t * TODO: Maybe we should use FCDocumentTools::StandardizeUpAxisAndLength in addition\n\t *\t\tto this, so we'd only have one up-axis case to worry about, but it doesn't seem to\n\t *\t\tcorrectly adjust the prop points in Y_UP models.\n\t */\n\tstatic void TransformStaticModel(float* position, float* normal, size_t vertexCount,\n\t\tconst FMMatrix44& transform, bool yUp)\n\t{\n\t\tfor (size_t i = 0; i < vertexCount; ++i)\n\t\t{\n\t\t\tFMVector3 pos (&position[i*3], 0);\n\t\t\tFMVector3 norm (&normal[i*3], 0);\n\n\t\t\t// Apply the scene-node transforms\n\t\t\tpos = transform.TransformCoordinate(pos);\n\t\t\tnorm = FMVector3_Normalize(transform.TransformVector(norm));\n\n\t\t\t// Convert from right-handed Y_UP or Z_UP to the game's coordinate system (left-handed Y-up)\n\n\t\t\tif (yUp)\n\t\t\t{\n\t\t\t\tpos.z = -pos.z;\n\t\t\t\tnorm.z = -norm.z;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tstd::swap(pos.y, pos.z);\n\t\t\t\tstd::swap(norm.y, norm.z);\n\t\t\t}\n\n\t\t\t// Copy back to array\n\n\t\t\tposition[i*3] = pos.x;\n\t\t\tposition[i*3+1] = pos.y;\n\t\t\tposition[i*3+2] = pos.z;\n\n\t\t\tnormal[i*3] = norm.x;\n\t\t\tnormal[i*3+1] = norm.y;\n\t\t\tnormal[i*3+2] = norm.z;\n\t\t}\n\t}\n\n\t/**\n\t * Applies world-space transform to vertex data and transforms Collada's right-handed\n\t *\tY-up / Z-up coordinates to the game's left-handed Y-up coordinate system\n\t *\n\t * TODO: Maybe we should use FCDocumentTools::StandardizeUpAxisAndLength in addition\n\t *\t\tto this, so we'd only have one up-axis case to worry about, but it doesn't seem to\n\t *\t\tcorrectly adjust the prop points in Y_UP models.\n\t */\n\tstatic void TransformSkinnedModel(float* position, float* normal, size_t vertexCount,\n\t\tstd::vector<BoneTransform>& bones, std::vector<PropPoint>& propPoints,\n\t\tconst FMMatrix44& transform, const FMMatrix44& bindTransform, bool yUp, bool isXSI)\n\t{\n\t\tFMMatrix44 scaledTransform; // for vertexes\n\t\tFMMatrix44 scaleMatrix; // for bones\n\n\t\t// HACK: see comment in PSAConvert::TransformVertices\n\t\tif (isXSI)\n\t\t{\n\t\t\tscaleMatrix = DecomposeToScaleMatrix(transform);\n\t\t\tscaledTransform = DecomposeToScaleMatrix(bindTransform) * transform;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tscaleMatrix = FMMatrix44_Identity;\n\t\t\tscaledTransform = bindTransform;\n\t\t}\n\n\t\t// Update the vertex positions and normals\n\t\tfor (size_t i = 0; i < vertexCount; ++i)\n\t\t{\n\t\t\tFMVector3 pos (&position[i*3], 0);\n\t\t\tFMVector3 norm (&normal[i*3], 0);\n\n\t\t\t// Apply the scene-node transforms\n\t\t\tpos = scaledTransform.TransformCoordinate(pos);\n\t\t\tnorm = FMVector3_Normalize(scaledTransform.TransformVector(norm));\n\n\t\t\t// Convert from right-handed Y_UP or Z_UP to the game's coordinate system (left-handed Y-up)\n\n\t\t\tif (yUp)\n\t\t\t{\n\t\t\t\tpos.z = -pos.z;\n\t\t\t\tnorm.z = -norm.z;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tstd::swap(pos.y, pos.z);\n\t\t\t\tstd::swap(norm.y, norm.z);\n\t\t\t}\n\n\t\t\t// and copy back into the original array\n\n\t\t\tposition[i*3] = pos.x;\n\t\t\tposition[i*3+1] = pos.y;\n\t\t\tposition[i*3+2] = pos.z;\n\n\t\t\tnormal[i*3] = norm.x;\n\t\t\tnormal[i*3+1] = norm.y;\n\t\t\tnormal[i*3+2] = norm.z;\n\t\t}\n\n\t\tTransformBones(bones, scaleMatrix, yUp);\n\n\t\t// And do the same for prop points\n\t\tfor (size_t i = 0; i < propPoints.size(); ++i)\n\t\t{\n\t\t\tif (yUp)\n\t\t\t{\n\t\t\t\tpropPoints[i].translation[0] = -propPoints[i].translation[0];\n\t\t\t\tpropPoints[i].orientation[0] = -propPoints[i].orientation[0];\n\t\t\t\tpropPoints[i].orientation[3] = -propPoints[i].orientation[3];\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// Flip translation across the x-axis by swapping y and z\n\t\t\t\tstd::swap(propPoints[i].translation[1], propPoints[i].translation[2]);\n\n\t\t\t\t// To convert the quaternions: imagine you're using the axis/angle\n\t\t\t\t// representation, then swap the y,z basis vectors and change the\n\t\t\t\t// direction of rotation by negating the angle ( => negating sin(angle)\n\t\t\t\t// => negating x,y,z => changing (x,y,z,w) to (-x,-z,-y,w)\n\t\t\t\t// but then (-x,-z,-y,w) == (x,z,y,-w) so do that instead)\n\t\t\t\tstd::swap(propPoints[i].orientation[1], propPoints[i].orientation[2]);\n\t\t\t\tpropPoints[i].orientation[3] = -propPoints[i].orientation[3];\n\t\t\t}\n\t\t}\n\n\t}\n};\n\n\n// The above stuff is just in a class since I don't like having to bother\n// with forward declarations of functions - but provide the plain function\n// interface here:\n\nvoid ColladaToPMD(const char* input, OutputCB& output, std::string& xmlErrors)\n{\n\tPMDConvert::ColladaToPMD(input, output, xmlErrors);\n}\n"
  },
  {
    "path": "fpsgame/PMDConvert.h",
    "content": "/* Copyright (C) 2009 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef INCLUDED_PMDCONVERT\n#define INCLUDED_PMDCONVERT\n\n#include <string>\n\nstruct OutputCB;\n\nvoid ColladaToPMD(const char* input, OutputCB& output, std::string& xmlErrors);\n\n#endif // INCLUDED_PMDCONVERT\n"
  },
  {
    "path": "fpsgame/PSAConvert.cpp",
    "content": "/* Copyright (C) 2013 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"precompiled.h\"\n\n#include \"PSAConvert.h\"\n#include \"CommonConvert.h\"\n\n//#include \"FCollada.h\"\n//#include \"FCDocument/FCDocument.h\"\n//#include \"FCDocument/FCDocumentTools.h\"\n//#include \"FCDocument/FCDAnimated.h\"\n//#include \"FCDocument/FCDAnimationCurve.h\"\n//#include \"FCDocument/FCDAnimationKey.h\"\n//#include \"FCDocument/FCDController.h\"\n//#include \"FCDocument/FCDControllerInstance.h\"\n//#include \"FCDocument/FCDExtra.h\"\n//#include \"FCDocument/FCDGeometry.h\"\n//#include \"FCDocument/FCDGeometryMesh.h\"\n//#include \"FCDocument/FCDGeometryPolygons.h\"\n//#include \"FCDocument/FCDGeometrySource.h\"\n//#include \"FCDocument/FCDSceneNode.h\"\n\n#include \"StdSkeletons.h\"\n#include \"Decompose.h\"\n//#include \"Maths.h\"\n#include \"GeomReindex.h\"\n\n#include <cassert>\n#include <vector>\n#include <limits>\n#include <iterator>\n#include <algorithm>\n\nclass PSAConvert\n{\npublic:\n\t/**\n\t * Converts a COLLADA XML document into the PSA animation format.\n\t *\n\t * @param input XML document to parse\n\t * @param output callback for writing the PSA data; called lots of times\n\t *               with small strings\n\t * @param xmlErrors output - errors reported by the XML parser\n\t * @throws ColladaException on failure\n\t */\n\tstatic void ColladaToPSA(const char* input, OutputCB& output, std::string& xmlErrors)\n\t{\n\t\tCommonConvert converter(input, xmlErrors);\n\n\t\tif (converter.GetInstance().GetType() == FCDEntityInstance::CONTROLLER)\n\t\t{\n\t\t\tFCDControllerInstance& controllerInstance = static_cast<FCDControllerInstance&>(converter.GetInstance());\n\n\t\t\tFixSkeletonRoots(controllerInstance);\n\n\t\t\tassert(converter.GetInstance().GetEntity()->GetType() == FCDEntity::CONTROLLER); // assume this is always true?\n\t\t\tFCDController* controller = static_cast<FCDController*>(converter.GetInstance().GetEntity());\n\n\t\t\tFCDSkinController* skin = controller->GetSkinController();\n\t\t\tREQUIRE(skin != NULL, \"is skin controller\");\n\n\t\t\tconst Skeleton& skeleton = FindSkeleton(controllerInstance);\n\n\t\t\tfloat frameLength = 1.f / 30.f; // currently we always want to create PMDs at fixed 30fps\n\n\t\t\t// Find the extents of the animation:\n\n\t\t\tfloat timeStart = 0, timeEnd = 0;\n\t\t\tGetAnimationRange(converter.GetDocument(), skeleton, controllerInstance, timeStart, timeEnd);\n\t\t\t// To catch broken animations / skeletons.xml:\n\t\t\tREQUIRE(timeEnd > timeStart, \"animation end frame must come after start frame\");\n\n\t\t\t// Count frames; don't include the last keyframe\n\t\t\tsize_t frameCount = (size_t)((timeEnd - timeStart) / frameLength - 0.5f);\n\t\t\tREQUIRE(frameCount > 0, \"animation must have frames\");\n\t\t\t// (TODO: sort out the timing/looping problems)\n\n\t\t\tsize_t boneCount = skeleton.GetBoneCount();\n\n\t\t\tstd::vector<BoneTransform> boneTransforms;\n\n\t\t\tfor (size_t frame = 0; frame < frameCount; ++frame)\n\t\t\t{\n\t\t\t\tfloat time = timeStart + frameLength * frame;\n\n\t\t\t\tBoneTransform boneDefault  = { { 0, 0, 0 }, { 0, 0, 0, 1 } };\n\t\t\t\tstd::vector<BoneTransform> frameBoneTransforms (boneCount, boneDefault);\n\n\t\t\t\t// Move the model into the new animated pose\n\t\t\t\t// (We can't tell exactly which nodes should be animated, so\n\t\t\t\t// just update the entire world recursively)\n\t\t\t\tEvaluateAnimations(converter.GetRoot(), time);\n\n\t\t\t\t// Convert the pose into the form require by the game\n\t\t\t\tfor (size_t i = 0; i < controllerInstance.GetJointCount(); ++i)\n\t\t\t\t{\n\t\t\t\t\tFCDSceneNode* joint = controllerInstance.GetJoint(i);\n\n\t\t\t\t\tint boneId = skeleton.GetRealBoneID(joint->GetName().c_str());\n\t\t\t\t\tif (boneId < 0)\n\t\t\t\t\t\tcontinue; // not a recognised bone - ignore it, same as before\n\n\t\t\t\t\tFMMatrix44 worldTransform = joint->CalculateWorldTransform();\n\n\t\t\t\t\tHMatrix matrix;\n\t\t\t\t\tmemcpy(matrix, worldTransform.Transposed().m, sizeof(matrix));\n\n\t\t\t\t\tAffineParts parts;\n\t\t\t\t\tdecomp_affine(matrix, &parts);\n\n\t\t\t\t\tBoneTransform b = {\n\t\t\t\t\t\t{ parts.t.x, parts.t.y, parts.t.z },\n\t\t\t\t\t\t{ parts.q.x, parts.q.y, parts.q.z, parts.q.w }\n\t\t\t\t\t};\n\n\t\t\t\t\tframeBoneTransforms[boneId] = b;\n\t\t\t\t}\n\n\t\t\t\t// Push frameBoneTransforms onto the back of boneTransforms\n\t\t\t\tcopy(frameBoneTransforms.begin(), frameBoneTransforms.end(),\n\t\t\t\t\tstd::inserter(boneTransforms, boneTransforms.end()));\n\t\t\t}\n\n\t\t\t// Convert into game's coordinate space\n\t\t\tTransformVertices(boneTransforms, skin->GetBindShapeTransform(), converter.IsYUp(), converter.IsXSI());\n\n\t\t\t// Write out the file\n\t\t\tWritePSA(output, frameCount, boneCount, boneTransforms);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tthrow ColladaException(\"Unrecognised object type\");\n\t\t}\n\t}\n\n\t/**\n\t * Writes the animation data in the PSA format.\n\t */\n\tstatic void WritePSA(OutputCB& output, size_t frameCount, size_t boneCount, const std::vector<BoneTransform>& boneTransforms)\n\t{\n\t\toutput(\"PSSA\", 4);  // magic number\n\t\twrite(output, (uint32)1); // version number\n\t\twrite(output, (uint32)(\n\t\t\t4 + 0 + // name\n\t\t\t4 + // frameLength\n\t\t\t4 + 4 + // numBones, numFrames\n\t\t\t7*4*boneCount*frameCount // boneStates\n\t\t\t)); // data size\n\n\t\t// Name\n\t\twrite(output, (uint32)0);\n\n\t\t// Frame length\n\t\twrite(output, 1000.f/30.f);\n\n\t\twrite(output, (uint32)boneCount);\n\t\twrite(output, (uint32)frameCount);\n\n\t\tfor (size_t i = 0; i < boneCount*frameCount; ++i)\n\t\t{\n\t\t\toutput((char*)&boneTransforms[i], 7*4);\n\t\t}\n\t}\n\n\tstatic void TransformVertices(std::vector<BoneTransform>& bones,\n\t\tconst FMMatrix44& transform, bool yUp, bool isXSI)\n\t{\n\t\t// HACK: we want to handle scaling in XSI because that makes it easy\n\t\t// for artists to adjust the models to the right size. But this way\n\t\t// doesn't work in Max, and I can't see how to make it do so, so this\n\t\t// is only applied to models from XSI.\n\t\tif (isXSI)\n\t\t{\n\t\t\tTransformBones(bones, DecomposeToScaleMatrix(transform), yUp);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tTransformBones(bones, FMMatrix44_Identity, yUp);\n\t\t}\n\t}\n\n\tstatic void GetAnimationRange(const FColladaDocument& doc, const Skeleton& skeleton,\n\t\tconst FCDControllerInstance& controllerInstance,\n\t\tfloat& timeStart, float& timeEnd)\n\t{\n\t\t// FCollada tools export <extra> info in the scene to specify the start\n\t\t// and end times.\n\t\t// If that isn't available, we have to search for the earliest and latest\n\t\t// keyframes on any of the bones.\n\t\tif (doc.GetDocument()->HasStartTime() && doc.GetDocument()->HasEndTime())\n\t\t{\n\t\t\ttimeStart = doc.GetDocument()->GetStartTime();\n\t\t\ttimeEnd = doc.GetDocument()->GetEndTime();\n\t\t\treturn;\n\t\t}\n\n\t\t// XSI exports relevant information in\n\t\t// <extra><technique profile=\"XSI\"><SI_Scene><xsi_param sid=\"start\">\n\t\t// (and 'end' and 'frameRate') so use those\n\t\tif (GetAnimationRange_XSI(doc, timeStart, timeEnd))\n\t\t\treturn;\n\n\t\ttimeStart = std::numeric_limits<float>::max();\n\t\ttimeEnd = -std::numeric_limits<float>::max();\n\t\tfor (size_t i = 0; i < controllerInstance.GetJointCount(); ++i)\n\t\t{\n\t\t\tconst FCDSceneNode* joint = controllerInstance.GetJoint(i);\n\t\t\tREQUIRE(joint != NULL, \"joint exists\");\n\n\t\t\tint boneId = skeleton.GetBoneID(joint->GetName().c_str());\n\t\t\tif (boneId < 0)\n\t\t\t{\n\t\t\t\t// unrecognised joint - it's probably just a prop point\n\t\t\t\t// or something, so ignore it\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Skip unanimated joints\n\t\t\tif (joint->GetTransformCount() == 0)\n\t\t\t\tcontinue;\n\n\t\t\tfor (size_t j = 0; j < joint->GetTransformCount(); ++j)\n\t\t\t{\n\t\t\t\tconst FCDTransform* transform = joint->GetTransform(j);\n\n\t\t\t\tif (! transform->IsAnimated())\n\t\t\t\t\tcontinue;\n\n\t\t\t\t// Iterate over all curves to find the earliest and latest keys\n\t\t\t\tconst FCDAnimated* anim = transform->GetAnimated();\n\t\t\t\tconst FCDAnimationCurveListList& curvesList = anim->GetCurves();\n\t\t\t\tfor (size_t j = 0; j < curvesList.size(); ++j)\n\t\t\t\t{\n\t\t\t\t\tconst FCDAnimationCurveTrackList& curves = curvesList[j];\n\t\t\t\t\tfor (size_t k = 0; k < curves.size(); ++k)\n\t\t\t\t\t{\n\t\t\t\t\t\tconst FCDAnimationCurve* curve = curves[k];\n\t\t\t\t\t\ttimeStart = std::min(timeStart, curve->GetKeys()[0]->input);\n\t\t\t\t\t\ttimeEnd = std::max(timeEnd, curve->GetKeys()[curve->GetKeyCount()-1]->input);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tstatic bool GetAnimationRange_XSI(const FColladaDocument& doc, float& timeStart, float& timeEnd)\n\t{\n\t\tFCDExtra* extra = doc.GetExtra();\n\t\tif (! extra) return false;\n\n\t\tFCDEType* type = extra->GetDefaultType();\n\t\tif (! type) return false;\n\n\t\tFCDETechnique* technique = type->FindTechnique(\"XSI\");\n\t\tif (! technique) return false;\n\n\t\tFCDENode* scene = technique->FindChildNode(\"SI_Scene\");\n\t\tif (! scene) return false;\n\n\t\tfloat start = FLT_MAX, end = -FLT_MAX, framerate = 0.f;\n\n\t\tFCDENodeList paramNodes;\n\t\tscene->FindChildrenNodes(\"xsi_param\", paramNodes);\n\t\tfor (FCDENodeList::iterator it = paramNodes.begin(); it != paramNodes.end(); ++it)\n\t\t{\n\t\t\tif ((*it)->ReadAttribute(\"sid\") == \"start\")\n\t\t\t\tstart = FUStringConversion::ToFloat((*it)->GetContent());\n\t\t\telse if ((*it)->ReadAttribute(\"sid\") == \"end\")\n\t\t\t\tend = FUStringConversion::ToFloat((*it)->GetContent());\n\t\t\telse if ((*it)->ReadAttribute(\"sid\") == \"frameRate\")\n\t\t\t\tframerate = FUStringConversion::ToFloat((*it)->GetContent());\n\t\t}\n\n\t\tif (framerate != 0.f && start != FLT_MAX && end != -FLT_MAX)\n\t\t{\n\t\t\ttimeStart = start / framerate;\n\t\t\ttimeEnd = end / framerate;\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tstatic void EvaluateAnimations(FCDSceneNode& node, float time)\n\t{\n\t\tfor (size_t i = 0; i < node.GetTransformCount(); ++i)\n\t\t{\n\t\t\tFCDTransform* transform = node.GetTransform(i);\n\t\t\tFCDAnimated* anim = transform->GetAnimated();\n\t\t\tif (anim)\n\t\t\t\tanim->Evaluate(time);\n\t\t}\n\n\t\tfor (size_t i = 0; i < node.GetChildrenCount(); ++i)\n\t\t\tEvaluateAnimations(*node.GetChild(i), time);\n\t}\n\n};\n\n\n// The above stuff is just in a class since I don't like having to bother\n// with forward declarations of functions - but provide the plain function\n// interface here:\n\nvoid ColladaToPSA(const char* input, OutputCB& output, std::string& xmlErrors)\n{\n\tPSAConvert::ColladaToPSA(input, output, xmlErrors);\n}\n"
  },
  {
    "path": "fpsgame/PSAConvert.h",
    "content": "/* Copyright (C) 2009 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef INCLUDED_PSACONVERT\n#define INCLUDED_PSACONVERT\n\n#include <string>\n\nstruct OutputCB;\n\nvoid ColladaToPSA(const char* input, OutputCB& output, std::string& xmlErrors);\n\n#endif // INCLUDED_PSACONVERT\n"
  },
  {
    "path": "fpsgame/Resource.h",
    "content": "//{{NO_DEPENDENCIES}}\n// Microsoft Visual C++ generated include file.\n// Used by fpsgame.rc\n//\n\n#define IDS_APP_TITLE\t\t\t103\n\n#define IDR_MAINFRAME\t\t\t128\n#define IDD_FPSGAME_DIALOG\t102\n#define IDD_ABOUTBOX\t\t\t103\n#define IDM_ABOUT\t\t\t\t104\n#define IDM_EXIT\t\t\t\t105\n#define IDI_FPSGAME\t\t\t107\n#define IDI_SMALL\t\t\t\t108\n#define IDC_FPSGAME\t\t\t109\n#define IDC_MYICON\t\t\t\t2\n#ifndef IDC_STATIC\n#define IDC_STATIC\t\t\t\t-1\n#endif\n\n#ifdef APSTUDIO_INVOKED\n#ifndef APSTUDIO_READONLY_SYMBOLS\n\n#define _APS_NO_MFC\t\t\t\t\t130\n#define _APS_NEXT_RESOURCE_VALUE\t129\n#define _APS_NEXT_COMMAND_VALUE\t\t32771\n#define _APS_NEXT_CONTROL_VALUE\t\t1000\n#define _APS_NEXT_SYMED_VALUE\t\t110\n#endif\n#endif\n"
  },
  {
    "path": "fpsgame/SkillSystem.cpp",
    "content": "#include \"SkillSystem.h\"\n#include \"BulletSystem.h\"\n#include \"interface.h\"\n#include \"GameProcess.h\"\n#include \"ObjectParticles.h\"\n\nCSkillSystem::CSkillSystem(CGameProcess*\tpGameProcess)\n{\n\tCBulletSystem::GetInstance()->SetCallback(MakeInterface(this, &CSkillSystem::OnSkillCallback));\n\tm_pGameProcess = pGameProcess;\n\tAddListen(BR_PARTICLE_HIT, MakeMessageFunc(this, &CSkillSystem::OnParticleHit));\n}\n\nCSkillSystem::~CSkillSystem(void)\n{\n\tCBulletSystem::ReleaseInstance();\n}\n\nint CSkillSystem::OnSkillCallback(void* pVoid)\n{\n\t_BulletCallback* pBase = (_BulletCallback*)pVoid;\n\tswitch (pBase->nMsgID)\n\t{\n\tcase _BulletCallback::GetTargetPosition:\n\t{\n\t\t_Bullet_GetTargetPosition* pBullet = (_Bullet_GetTargetPosition*)pBase;\n\t\tCRoleBase* pRole = m_pGameProcess->GetRole(pBullet->nTarget);//test\n\t\tif (pRole)\n\t\t{\n\t\t\tpBullet->nRetState = 1;\n\t\t\tpBullet->vRetPos = pRole->GetPosition() + vec3(0.0f, 0.0f, 1.2f);\n\t\t\tpBullet->vRetDir = pRole->GetDirection();\n\t\t}\n\t\telse\n\t\t{\n\t\t\tpBullet->nRetState = 0;\n\t\t}\n\t}break;\n\tcase _BulletCallback::HitTarget:\n\t{\n\t\t_Bullet_HitTarget* pBullet = (_Bullet_HitTarget*)pBase;\n\n\t}break;\n\tcase _BulletCallback::GetCollision:\n\t{\n\t\t_Bullet_GetCollision* pBullet = (_Bullet_GetCollision*)pBase;\n\t\t//test\n\t\tfor (int i = 0; i < 20; i++)\n\t\t{\n\t\t\tCRoleBase* pRole = m_pGameProcess->GetRoleFormIndex(i);\n\t\t\tif (pRole)\n\t\t\t{\n\t\t\t\tif ((pRole->GetPosition() - pBullet->vPos).length() < pBullet->fRadius + pRole->GetRoleRadius())\n\t\t\t\t{\n\t\t\t\t\tpBullet->nRetTarget = pRole->GetRoleID();\n\t\t\t\t\tpBullet->vRetHitPoint = pRole->GetPosition() + vec3(0.0f, 0.0f, 1.2f);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}break;\n\tcase _BulletCallback::Blast:\n\t{\n\t\t_Bullet_Blast* pBullet = (_Bullet_Blast*)pBase;\n\n\t}break;\n\t}\n\n\treturn 1;\n}\n\nvoid CSkillSystem::Update(float ifps)\n{\n\tCBulletSystem::GetInstance()->Update();\n}\n\nvoid CSkillSystem::Reset()\n{\n\tCBulletSystem::GetInstance()->Reset();\n}\nint CSkillSystem::OnParticleHit(void* v1, void* v2, void* v3)\n{\n\tCObjectParticles* pPar = (CObjectParticles*)v1;\n\tCBRObject* pHitObject = (CBRObject*)v2;\n\n\tint nContact = *(int*)v3;\n\tint nParID = atoi(pPar->GetName());\n\tint nObjID = atoi(pHitObject->GetName());\n\n\treturn 1;\n}"
  },
  {
    "path": "fpsgame/SkillSystem.h",
    "content": "#pragma once\n#include \"MessageBase.h\"\n\nclass CInterfaceBase;\nclass CGameProcess;\nclass CSkillSystem : public CMessageBase\t\t//ϵͳ̳Ϣ\n{\npublic:\n\tCSkillSystem(CGameProcess*\tpGameProcess);\n\tvirtual ~CSkillSystem(void);\n\npublic:\n\tvoid\t\tUpdate(float ifps);\n\tvoid\t\tReset();\nprotected:\n\tint\tOnParticleHit(void* v1, void* v2, void* v3);\n\tint\tOnSkillCallback(void* pVoid);\n\tCGameProcess*\t   m_pGameProcess;\n};"
  },
  {
    "path": "fpsgame/Star2DControl.cpp",
    "content": "#include \"Star2DControl.h\"\n#include \"Engine.h\"\n\nusing namespace MathLib;\n\nCStar2DControl::CStar2DControl(void)\n{\n\tm_pNormalTex = NULL;\n\tm_pClickTex = NULL;\n\tm_nEnabled = 1;\n\tm_nClickState = 0;\n\tm_fClickScale = 1.0f;\n\tm_vNormalColor = vec4_one;\n\tm_vClickColor = vec4_one;\n\tm_fNormalTexScale = 0.1f;\n\tm_fClickTexScale = 0.1f;\n\tInit();\n}\n\nCStar2DControl::~CStar2DControl(void)\n{\n\tg_Engine.pGui->ReleaseTexture(m_pNormalTex);\n\tg_Engine.pGui->ReleaseTexture(m_pClickTex);\n}\n\nint CStar2DControl::Init()\n{\n\treturn Load(NORMAL_NAME, CLICK_NAME);\n}\nvoid CStar2DControl::Update()\n{\n\tif (!IsEnable())\n\t{\n\t\treturn;\n\t}\n\tm_fClickScale += m_nClickState * g_Engine.pGame->GetIFps() * 5.5f;\n\tif (m_fClickScale <= 0.8f)\n\t{\n\t\tm_nClickState = 1;\n\t}\n\telse if (m_fClickScale >= 1.0f)\n\t{\n\t\tm_fClickScale = 1.0f;\n\t\tm_nClickState = 0;\n\t}\n\n\tif (m_pNormalTex && m_pNormalTex->GetTexture())\n\t{\n\t\tCTexture *tex = m_pNormalTex->GetTexture();\n\t\tfloat width = CMathCore::Itof(tex->GetWidth());\n\t\tfloat height = CMathCore::Itof(tex->GetHeight());\n\n\t\tif (m_fNormalTexScale != 0.0f)\n\t\t{\n\t\t\tfloat aspect = height / width;\n\t\t\twidth = g_Engine.pGui->GetWidth() * m_fNormalTexScale;\n\t\t\theight = width * aspect;\n\t\t}\n\n\t\tfloat CenterX = g_Engine.pGui->GetWidth() / 2.0f;\n\t\tfloat CenterY = g_Engine.pGui->GetHeight() / 2.0f;\n\t\tfloat x0 = CenterX - width / 2.0f;\n\t\tfloat y0 = CenterY - height / 2.0f;\n\t\tfloat x1 = CenterX + width / 2.0f;\n\t\tfloat y1 = CenterY + height / 2.0f;\n\t\tg_Engine.pGui->RenderTexture(tex, vec4(x0, y0, x1, y1), m_vNormalColor);\n\t}\n\tif (m_pClickTex && m_pClickTex->GetTexture())\n\t{\n\t\tCTexture *tex = m_pClickTex->GetTexture();\n\t\tfloat width = CMathCore::Itof(tex->GetWidth());\n\t\tfloat height = CMathCore::Itof(tex->GetHeight());\n\n\t\tif (m_fClickTexScale != 0.0f)\n\t\t{\n\t\t\tfloat aspect = height / width;\n\t\t\twidth = g_Engine.pGui->GetWidth() * m_fClickTexScale;\n\t\t\theight = width * aspect;\n\t\t}\n\n\t\twidth *= m_fClickScale;\n\t\theight *= m_fClickScale;\n\n\t\tfloat CenterX = g_Engine.pGui->GetWidth() / 2.0f;\n\t\tfloat CenterY = g_Engine.pGui->GetHeight() / 2.0f;\n\t\tfloat x0 = CenterX - width / 2.0f;\n\t\tfloat y0 = CenterY - height / 2.0f;\n\t\tfloat x1 = CenterX + width / 2.0f;\n\t\tfloat y1 = CenterY + height / 2.0f;\n\t\tg_Engine.pGui->RenderTexture(tex, vec4(x0, y0, x1, y1), m_vClickColor);\n\t}\n\n}\nvoid CStar2DControl::Click()\n{\n\tif(m_nClickState != -1)\n\t\tm_nClickState = -1;\n}\nint CStar2DControl::Load(const char* strNormal, const char* strClick)\n{\n\tif (m_strNormalName != strNormal)\n\t{\n\t\tm_strNormalName = strNormal;\n\t\tg_Engine.pGui->ReleaseTexture(m_pNormalTex);\n\t\tm_pNormalTex = g_Engine.pGui->CreateTexture(strNormal);\n\t}\n\n\tif (m_strClickName != strClick)\n\t{\n\t\tm_strClickName = strClick;\n\t\tg_Engine.pGui->ReleaseTexture(m_pClickTex);\n\t\tm_pClickTex = g_Engine.pGui->CreateTexture(strClick);\n\t}\n\n\treturn 0;\n}\nint CStar2DControl::IsEnable()\n{\n\treturn m_nEnabled;\n}\nvoid CStar2DControl::SetEnable(int nEnable)\n{\n\tm_nEnabled = nEnable;\n}\nvoid CStar2DControl::SetNormalColor(const vec4& vColor)\n{\n\tm_vNormalColor = vColor;\n}\n\nconst MathLib::vec4& CStar2DControl::GetNormalColor() const\n{\n\treturn m_vNormalColor;\n}\n\nvoid CStar2DControl::SetClickColor(const vec4& vColor)\n{\n\tm_vClickColor = vColor;\n}\nconst MathLib::vec4& CStar2DControl::GetClickColor() const\n{\n\treturn m_vClickColor;\n}\n\nvoid CStar2DControl::SetNormalScale(float fScale)\n{\n\tm_fNormalTexScale = fScale;\n}\n\nfloat CStar2DControl::GetNormalScale() const\n{\n\treturn m_fNormalTexScale;\n}\n\nvoid CStar2DControl::SetClickScale(float fScale)\n{\n\tm_fClickTexScale = fScale;\n}\n\nfloat CStar2DControl::GetClickScale() const\n{\n\treturn m_fClickTexScale;\n}\n"
  },
  {
    "path": "fpsgame/Star2DControl.h",
    "content": "#pragma once\n#include \"MathLib.h\"\n#include \"Singleton.h\"\n#include \"UtilStr.h\"\n\nclass CBRObject;\nclass CTextureImage;\n\nclass CStar2DControl : public CSingleton < CStar2DControl >\n{\n\npublic:\n\n\tCStar2DControl(void);\n\t~CStar2DControl(void);\n\npublic:\n\n\tint         Init();\n\tvoid        Update();\n\tvoid        Click();\n\tint         Load(const char* strNormal, const char* strClick);\n\tvoid        SetEnable(int nEnable);\n\tint         IsEnable();\n\n\tvoid        SetNormalColor(const MathLib::vec4& vColor);\n\tconst MathLib::vec4& GetNormalColor() const;\n\n\tvoid        SetClickColor(const MathLib::vec4& vColor);\n\tconst MathLib::vec4& GetClickColor() const;\n\n\n\t//Ļռ 01ֵ\n\tvoid        SetNormalScale(float fScale);\n\tfloat       GetNormalScale() const;\n\n\tvoid        SetClickScale(float fScale);\n\tfloat       GetClickScale() const;\n\nprotected:\n\n\tCUtilStr        m_strNormalName;\n\tCUtilStr        m_strClickName;\n\tCTextureImage   *m_pNormalTex;\n\tCTextureImage   *m_pClickTex;\n\tint             m_nEnabled;\n\tint             m_nClickState;\n\tfloat           m_fClickScale;\n\tMathLib::vec4   m_vNormalColor;\n\tMathLib::vec4   m_vClickColor;\n\tfloat           m_fNormalTexScale;\n\tfloat           m_fClickTexScale;\n};"
  },
  {
    "path": "fpsgame/StartControl.cpp",
    "content": "#include \"StarControl.h\"\n#include \"ObjectParticles.h\"\n#include \"Engine.h\"\n#include \"Game.h\"\n#include \"Object.h\"\n#include \"Player.h\"\n#include \"Common.h\"\n#include \"App.h\"\n\nCStarControl::CStarControl(void)\n{\n\tm_pStarNormal = NULL;\n\tm_pStarClick = NULL;\n\tm_fViewDistance = 0.5f;\n\tm_nClickState = 0;\n\tm_fClickScale = 1.0f;\n\tInit();\n}\n\nCStarControl::~CStarControl(void)\n{\n\tg_Engine.pGame->RemoveNode(m_pStarNormal);\n\tg_Engine.pGame->RemoveNode(m_pStarClick);\n}\n\nint CStarControl::Init()\n{\n\treturn Load(\"data/StarControl/Star_mesh.node\", \"data/StarControl/Star_click.node\");\n}\n\nvoid CStarControl::Update(const vec3& vPos, const vec3& vDir)\n{\n\tm_fClickScale += m_nClickState * g_Engine.pGame->GetIFps() * 5.5f;\n\tif (m_fClickScale <= 0.8f)\n\t{\n\t\tm_nClickState = 1;\n\t}\n\telse if (m_fClickScale >= 1.0f)\n\t{\n\t\tm_fClickScale = 1.0f;\n\t\tm_nClickState = 0;\n\t}\n\tfloat fViewDistance = m_fViewDistance;\n\tmat4 matStar = Translate(vPos + vDir * fViewDistance);\n\tCPlayer* pPlayer = g_Engine.pGame->GetPlayer();\n\tif (pPlayer)\n\t{\n\t\tvec3 x = pPlayer->GetModelview().getRow3(0);\n\t\tvec3 y = pPlayer->GetModelview().getRow3(1);\n\t\tvec3 z = pPlayer->GetModelview().getRow3(2);\n\n\t\tmatStar.setColumn3(0, x);\n\t\tmatStar.setColumn3(1, -z);\n\t\tmatStar.setColumn3(2, y);\n\t}\n\tvec4 pos = vec4(1.0f, 0.0f, 0.0f, 1.0f);\n\tfloat s = CCommon::CalcAxisScale(g_Engine.pGame->GetPlayer()->GetModelview(), g_Engine.pGame->GetPlayer()->GetFov(),\n\t\tvec4(matStar.getColumn3(3), 1.0f),\n\t\t200.0f,\n\t\tCMathCore::Itof(g_Engine.pApp->GetHeight()));\n\n\tm_pStarNormal->SetWorldTransform(matStar * Scale(s, 1.0f, s));\n\tm_pStarClick->SetWorldTransform(matStar * Scale(m_fClickScale, 1.0f, m_fClickScale) * Scale(s, 1.0f, s));\n}\nvoid CStarControl::Click()\n{\n\tm_nClickState = -1;\n}\nint CStarControl::Load(const char* strNormal, const char* strClick)\n{\n\tif (m_pStarClick && m_pStarNormal)\n\t{\n\t\tg_Engine.pGame->RemoveNode(m_pStarNormal);\n\t\tg_Engine.pGame->RemoveNode(m_pStarClick);\n\t}\n\n\tm_pStarNormal = (CBRObject*)g_Engine.pGame->LoadNode(strNormal);\n\tm_pStarClick = (CBRObject*)g_Engine.pGame->LoadNode(strClick);\n\n\tif (m_pStarClick && m_pStarNormal)\n\t{\n\t\treturn 1;\n\t}\n\n\treturn 0;\n}\n\nint CStarControl::isEnable()\n{\n\treturn m_pStarClick->IsEnabled();\n}\nvoid CStarControl::SetEnable(int nEnable)\n{\n\tm_pStarClick->SetEnabled(nEnable);\n\tm_pStarNormal->SetEnabled(nEnable);\n}\nvoid CStarControl::SetColor(const vec4& vColor)\n{\n\tm_pStarNormal->SetObjectColor(vColor);\n}"
  },
  {
    "path": "fpsgame/StartControl.h",
    "content": "#pragma once\n#include \"MathLib.h\"\n#include \"Singleton.h\"\n\nclass CBRObject;\nusing namespace MathLib;\n\nclass CStarControl : public CSingleton<CStarControl>\n{\npublic:\n\tCStarControl(void);\n\t~CStarControl(void);\npublic:\n\tint\t\tInit();\n\tvoid\t\tUpdate(const vec3& vPos, const vec3& vDir);\n\tvoid\t\tClick();\n\tint\t\tLoad(const char* strNormal, const char* strClick);\n\tint\t\tisEnable();\n\tvoid\t\tSetEnable(int nEnable);\n\tvoid\t\tSetViewDistance(float fViewDistance) { m_fViewDistance = fViewDistance; }\n\tvoid\t\tSetColor(const vec4& vColor);\t\t//ô4ά\nprotected:\n\tCBRObject*\t\t\tm_pStarNormal;\n\tCBRObject*\t\t\tm_pStarClick;\n\tfloat\t\t\t\tm_fViewDistance;\n\tint\t\t\t\t\tm_nClickState;\n\tfloat\t\t\t\tm_fClickScale;\n};\n\n"
  },
  {
    "path": "fpsgame/StdSkeletons.cpp",
    "content": "#include \"precompiled.h\"\n#include \"StdSkeletons.h\"\n\n#include \"libxml/parser.h\"\n#include \"libxml/xmlerror.h\"\n\n\n\n#include \"CommonConvert.h\"\n\n#include \"FUtils/FUXmlParser.h\"\n\n#include <map>\n\nnamespace\n{\n\tstruct SkeletonMap : public std::map<std::string, const Skeleton*>\n\t{\n\t\tSkeletonMap()\n\t\t{\n\n\t\t}\n\t\t~SkeletonMap()\n\t\t{\n\t\t\tfor (iterator it = begin(); it != end(); ++it)\n\t\t\t\tdelete it->second;\n\t\t}\n\t};\n\tSkeletonMap g_StandardSkeletons;\n\tSkeletonMap g_MappedSkeletons;\n\n\tstruct Bone\n\t{\n\t\tstd::string parent;\n\t\tstd::string name;\n\t\tint targetId;\n\t\tint realTargetId;\n\t};\n}\n\nconst Skeleton* Skeleton::FindSkeleton(const std::string& name)\n{\n\treturn g_MappedSkeletons[name];\n}\n\nSkeleton::Skeleton() : m(new Skeleton_impl) { }\nSkeleton::~Skeleton() { }\n\nstruct Skeleton_impl\n{\n\tstd::string title;\n\tstd::vector<Bone> bones;\n\tconst Skeleton* target;\n};\n\nint Skeleton::GetBoneID(const std::string& name) const\n{\n\tfor (size_t i = 0; i < m->bones.size(); ++i)\n\t\tif (m->bones[i].name == name)\n\t\t\treturn m->bones[i].targetId;\n\treturn -1;\n}\n\nint Skeleton::GetRealBoneID(const std::string& name) const\n{\n\tfor (size_t i = 0; i < m->bones.size(); ++i)\n\t\tif (m->bones[i].name == name)\n\t\t\treturn m->bones[i].realTargetId;\n\treturn -1;\n}\n\nint Skeleton::GetBoneCount() const\n{\n\treturn (int)m->target->m->bones.size();\n}\n\nnamespace\n{\n\tbool AlreadyUsedTargetBone(const std::vector<Bone>& bones, int targetId)\n\t{\n\t\tfor (size_t i = 0; i < bones.size(); ++i)\n\t\t\tif (bones[i].targetId == targetId)\n\t\t\t\treturn true;\n\t\treturn false;\n\t}\n\n\t// Recursive helper function used by LoadSkeletonData\n\tvoid LoadSkeletonBones(xmlNode* parent, std::vector<Bone>& bones, const Skeleton* targetSkeleton, const std::string& targetName)\n\t{\n\t\txmlNodeList boneNodes;\n\t\tFUXmlParser::FindChildrenByType(parent, \"bone\", boneNodes);\n\t\tfor (xmlNodeList::iterator boneNode = boneNodes.begin(); boneNode != boneNodes.end(); ++boneNode)\n\t\t{\n\t\t\tstd::string name(FUXmlParser::ReadNodeProperty(*boneNode, \"name\"));\n\n\t\t\tBone b;\n\t\t\tb.name = name;\n\n\t\t\tstd::string newTargetName = targetName;\n\n\t\t\tif (targetSkeleton)\n\t\t\t{\n\t\t\t\txmlNode* targetNode = FUXmlParser::FindChildByType(*boneNode, \"target\");\n\t\t\t\tif (targetNode)\n\t\t\t\t\tnewTargetName = FUXmlParser::ReadNodeContentFull(targetNode);\n\t\t\t\t// else fall back to the parent node's target\n\n\t\t\t\tb.targetId = targetSkeleton->GetBoneID(newTargetName);\n\t\t\t\tREQUIRE(b.targetId != -1, \"skeleton bone target matches some standard_skeleton bone name\");\n\n\t\t\t\tif (AlreadyUsedTargetBone(bones, b.targetId))\n\t\t\t\t\tb.realTargetId = -1;\n\t\t\t\telse\n\t\t\t\t\tb.realTargetId = b.targetId;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// No target - this is a standard skeleton\n\n\t\t\t\tb.targetId = (int)bones.size();\n\t\t\t\tb.realTargetId = b.targetId;\n\t\t\t}\n\n\t\t\tbones.push_back(b);\n\n\t\t\tLoadSkeletonBones(*boneNode, bones, targetSkeleton, newTargetName);\n\t\t}\n\t}\n\n\tvoid LoadSkeletonData(xmlNode* root)\n\t{\n\t\txmlNodeList skeletonNodes;\n\t\tFUXmlParser::FindChildrenByType(root, \"standard_skeleton\", skeletonNodes);\n\t\tFUXmlParser::FindChildrenByType(root, \"skeleton\", skeletonNodes);\n\t\tfor (xmlNodeList::iterator skeletonNode = skeletonNodes.begin();\n\t\t\tskeletonNode != skeletonNodes.end(); ++skeletonNode)\n\t\t{\n\t\t\tstd::unique_ptr<Skeleton> skeleton(new Skeleton());\n\n\t\t\tstd::string title(FUXmlParser::ReadNodeProperty(*skeletonNode, \"title\"));\n\n\t\t\tskeleton->m->title = title;\n\n\t\t\tif (IsEquivalent((*skeletonNode)->name, \"standard_skeleton\"))\n\t\t\t{\n\t\t\t\tskeleton->m->target = NULL;\n\n\t\t\t\tLoadSkeletonBones(*skeletonNode, skeleton->m->bones, NULL, \"\");\n\n\t\t\t\tstd::string id(FUXmlParser::ReadNodeProperty(*skeletonNode, \"id\"));\n\t\t\t\tREQUIRE(!id.empty(), \"standard_skeleton has id\");\n\n\t\t\t\tg_StandardSkeletons[id] = skeleton.release();\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// Non-standard skeletons need to choose a standard skeleton\n\t\t\t\t// as their target to be mapped onto\n\n\t\t\t\tstd::string target(FUXmlParser::ReadNodeProperty(*skeletonNode, \"target\"));\n\t\t\t\tconst Skeleton* targetSkeleton = g_StandardSkeletons[target];\n\t\t\t\tREQUIRE(targetSkeleton != NULL, \"skeleton target matches some standard_skeleton id\");\n\n\t\t\t\tskeleton->m->target = targetSkeleton;\n\n\t\t\t\tLoadSkeletonBones(*skeletonNode, skeleton->m->bones, targetSkeleton, \"\");\n\n\t\t\t\t// Currently the only supported identifier is a precise name match,\n\t\t\t\t// so just look for that\n\t\t\t\txmlNode* identifier = FUXmlParser::FindChildByType(*skeletonNode, \"identifier\");\n\t\t\t\tREQUIRE(identifier != NULL, \"skeleton has <identifier>\");\n\t\t\t\txmlNode* identRoot = FUXmlParser::FindChildByType(identifier, \"root\");\n\t\t\t\tREQUIRE(identRoot != NULL, \"skeleton identifier has <root>\");\n\t\t\t\tstd::string identRootName(FUXmlParser::ReadNodeContentFull(identRoot));\n\n\t\t\t\tg_MappedSkeletons[identRootName] = skeleton.release();\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid errorHandler(void* ctx, const char* msg, ...);\n\nvoid Skeleton::LoadSkeletonDataFromXml(const char* xmlData, size_t xmlLength, std::string& xmlErrors)\n{\n\txmlDoc* doc = NULL;\n\ttry\n\t{\n\t\txmlSetGenericErrorFunc(&xmlErrors, &errorHandler);\n\t\tdoc = xmlParseMemory(xmlData, (int)xmlLength);\n\t\tif (doc)\n\t\t{\n\t\t\txmlNode* root = xmlDocGetRootElement(doc);\n\t\t\tLoadSkeletonData(root);\n\t\t\txmlFreeDoc(doc);\n\t\t\tdoc = NULL;\n\t\t}\n\t\txmlSetGenericErrorFunc(NULL, NULL);\n\t}\n\tcatch (const ColladaException&)\n\t{\n\t\tif (doc)\n\t\t\txmlFreeDoc(doc);\n\t\txmlSetGenericErrorFunc(NULL, NULL);\n\t\tthrow;\n\t}\n\n\tif (!xmlErrors.empty())\n\t\tthrow ColladaException(\"XML parsing failed\");\n}"
  },
  {
    "path": "fpsgame/StdSkeletons.h",
    "content": "#pragma once\n#ifndef INCLUDED_STDSKELETONS\n#define INCLUDED_STDSKELETONS\n\n#include <string>\n#include <memory>\n\nstruct Skeleton_impl;\n\nclass Sketeton\n{\npublic:\n\t/** Default constructor - don't use; use FindSkeleton instead. */\n\tSkeleton();\n\t~Skeleton();\n\n\t/**\n\t* Returns the number of bones in the standard-skeleton which this current\n\t* skeleton is mapped onto.\n\t*/\n\tint GetBoneCount() const;\n\t\n\t/**\n\t* Tries to find a skeleton that matches the given root bone name.\n\t* Returns NULL if there is no match.\n\t*/\n\tstatic const Skeleton* FindSkeleton(const std::string& rootBoneName);\n\n\n\tint GetBoneID(const std::string& name) const;\n\tint GetRealBoneID(const std::string& name) const;\n\n\n\n\tstatic void LoadSkeletonDataFromXml(const char* xmlData, size_t xmlLength, std::string& xmlErrors);\n\n\tstd::unique_ptr<Skeleton_impl> m;\nprivate:\n\tSkeleton(Skeleton&);\n};\n\n#endif\t// INCLUDED_STDSKELETONS"
  },
  {
    "path": "fpsgame/XMLFix.cpp",
    "content": "/* Copyright (C) 2009 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"precompiled.h\"\n\n#include \"XMLFix.h\"\n\n#include \"CommonConvert.h\"\n\n#include \"FUtils/FUXmlParser.h\"\n\n/*\n\nThings that are fixed here:\n\n----\n\n3ds Max \"file://\" image URLs\n\nIdentifier: /COLLADA/asset/contributor/authoring_tool = \"FBX COLLADA exporter\"\n\nProblem: /COLLADA/library_images/image/init_from = \"file://\" which crashes some versions of FCollada\n\nFix: Delete the whole library_images subtree, since we never use it anyway.\nThen delete library_effects and library_materials too, to avoid broken references.\n\n----\n\n3ds Max broken material references\n\nIdentifier: /COLLADA/asset/contributor/authoring_tool = \"FBX COLLADA exporter\"\n\nProblem: /COLLADA/library_visual_scenes/.../instance_material/@target sometimes\nrefers to non-existent material IDs.\n\nFix: Delete the whole bind_material subtree, since we never use it anyway.\n\n----\n\n*/\n\nstatic xmlNode* findChildElement(xmlNode* node, const char* name)\n{\n\tfor (xmlNode* child = node->children; child; child = child->next)\n\t{\n\t\tif (child->type == XML_ELEMENT_NODE && strcmp((const char*)child->name, name) == 0)\n\t\t\treturn child;\n\t}\n\treturn NULL;\n}\n\nstatic bool applyFBXFixesNode(xmlNode* node)\n{\n\tbool changed = false;\n\tfor (xmlNode* child = node->children; child; child = child->next)\n\t{\n\t\tif (child->type == XML_ELEMENT_NODE)\n\t\t{\n\t\t\tif (strcmp((const char*)child->name, \"node\") == 0)\n\t\t\t{\n\t\t\t\tif (applyFBXFixesNode(child))\n\t\t\t\t\tchanged = true;\n\t\t\t}\n\t\t\telse if (strcmp((const char*)child->name, \"instance_geometry\") == 0)\n\t\t\t{\n\t\t\t\txmlNode* bind_material = findChildElement(child, \"bind_material\");\n\t\t\t\tif (! bind_material) continue;\n\t\t\t\tLog(LOG_INFO, \"Found a bind_material to delete\");\n\t\t\t\txmlUnlinkNode(bind_material);\n\t\t\t\txmlFreeNode(bind_material);\n\n\t\t\t\tchanged = true;\n\t\t\t}\n\t\t}\n\t}\n\treturn changed;\n}\n\nstatic bool applyFBXFixes(xmlNode* root)\n{\n\tLog(LOG_INFO, \"Applying fixes for 3ds Max exporter\");\n\n\tbool changed = false;\n\n\txmlNode* library_images = findChildElement(root, \"library_images\");\n\tif (library_images)\n\t{\n\t\tLog(LOG_INFO, \"Found library_images to delete\");\n\t\txmlUnlinkNode(library_images);\n\t\txmlFreeNode(library_images);\n\t\tchanged = true;\n\t}\n\n\txmlNode* library_materials = findChildElement(root, \"library_materials\");\n\tif (library_materials)\n\t{\n\t\tLog(LOG_INFO, \"Found library_materials to delete\");\n\t\txmlUnlinkNode(library_materials);\n\t\txmlFreeNode(library_materials);\n\t\tchanged = true;\n\t}\n\n\txmlNode* library_effects = findChildElement(root, \"library_effects\");\n\tif (library_effects)\n\t{\n\t\tLog(LOG_INFO, \"Found library_effects to delete\");\n\t\txmlUnlinkNode(library_effects);\n\t\txmlFreeNode(library_effects);\n\t\tchanged = true;\n\t}\n\n\txmlNode* library_visual_scenes = findChildElement(root, \"library_visual_scenes\");\n\tif (library_visual_scenes) // (Assume there's only one of these)\n\t{\n\t\txmlNode* visual_scene = findChildElement(library_visual_scenes, \"visual_scene\");\n\t\tif (visual_scene) // (Assume there's only one of these)\n\t\t{\n\t\t\tfor (xmlNode* child = visual_scene->children; child; child = child->next)\n\t\t\t{\n\t\t\t\tif (child->type == XML_ELEMENT_NODE && strcmp((const char*)child->name, \"node\") == 0)\n\t\t\t\t\tif (applyFBXFixesNode(child))\n\t\t\t\t\t\tchanged = true;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn changed;\n}\n\nstatic bool processDocument(xmlNode* root)\n{\n\txmlNode* asset = findChildElement(root, \"asset\");\n\tif (! asset) return false;\n\txmlNode* contributor = findChildElement(asset, \"contributor\");\n\tif (! contributor) return false;\n\txmlNode* authoring_tool = findChildElement(contributor, \"authoring_tool\");\n\tif (! authoring_tool) return false;\n\n\txmlNode* authoring_tool_text = authoring_tool->children;\n\tif (! authoring_tool_text) return false;\n\tif (authoring_tool_text->type != XML_TEXT_NODE) return false;\n\txmlChar* toolname = authoring_tool_text->content;\n\tLog(LOG_INFO, \"Authoring tool: %s\", toolname);\n\tif (strcmp((const char*)toolname, \"FBX COLLADA exporter\") == 0)\n\t\treturn applyFBXFixes(root);\n\telse\n\t\treturn false;\n}\n\nvoid FixBrokenXML(const char* text, const char** out, size_t* outSize)\n{\n\tLog(LOG_INFO, \"Running FixBrokenXML\");\n\n\tsize_t textSize = strlen(text);\n\txmlDocPtr doc = xmlParseMemory(text, (int)textSize);\n\n\txmlNode* root = xmlDocGetRootElement(doc);\n\tif (root && processDocument(root))\n\t{\n\t\t// Reserialising the document, then parsing it again inside FCollada, is a bit ugly;\n\t\t// but it's the only way I can see to make it work through FCollada's public API\n\t\txmlChar* mem = NULL;\n\t\tint size = -1;\n\t\txmlDocDumpFormatMemory(doc, &mem, &size, 0);\n\t\t*out = (const char*)mem;\n\t\t*outSize = size;\n\t}\n\telse\n\t{\n\t\t*out = text;\n\t\t*outSize = textSize;\n\t}\n\n\txmlFreeDoc(doc);\n}\n"
  },
  {
    "path": "fpsgame/XMLFix.h",
    "content": "/* Copyright (C) 2009 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef XMLFIX_INCLUDED\n#define XMLFIX_INCLUDED\n\n/**\n * Fixes some errors in COLLADA XML files that would otherwise prevent\n * FCollada from loading them successfully.\n * 'out' is either a new XML document, which must be freed with xmlFree;\n * otherwise it is equal to 'text' if no changes were made.\n */\nvoid FixBrokenXML(const char* text, const char** out, size_t* outSize);\n\n#endif // XMLFIX_INCLUDED\n"
  },
  {
    "path": "fpsgame/fpsgame.cpp",
    "content": "#include \"stdafx.h\"\n#include \"fpsgame.h\"\n#include <windows.h>\n\n#define MAX_LOADSTRING 100\n \n"
  },
  {
    "path": "fpsgame/fpsgame.h",
    "content": "#pragma once\n\n#include \"stdafx.h\""
  },
  {
    "path": "fpsgame/fpsgame.vcxproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project DefaultTargets=\"Build\" ToolsVersion=\"15.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\r\n  <ItemGroup Label=\"ProjectConfigurations\">\r\n    <ProjectConfiguration Include=\"Debug|Win32\">\r\n      <Configuration>Debug</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Release|Win32\">\r\n      <Configuration>Release</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Debug|x64\">\r\n      <Configuration>Debug</Configuration>\r\n      <Platform>x64</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Release|x64\">\r\n      <Configuration>Release</Configuration>\r\n      <Platform>x64</Platform>\r\n    </ProjectConfiguration>\r\n  </ItemGroup>\r\n  <PropertyGroup Label=\"Globals\">\r\n    <VCProjectVersion>15.0</VCProjectVersion>\r\n    <ProjectGuid>{057B5724-2848-4AB5-91E2-D9F26C9E9BB6}</ProjectGuid>\r\n    <Keyword>Win32Proj</Keyword>\r\n    <RootNamespace>fpsgame</RootNamespace>\r\n    <WindowsTargetPlatformVersion>10.0.15063.0</WindowsTargetPlatformVersion>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>Application</ConfigurationType>\r\n    <UseDebugLibraries>true</UseDebugLibraries>\r\n    <PlatformToolset>v141</PlatformToolset>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>Application</ConfigurationType>\r\n    <UseDebugLibraries>false</UseDebugLibraries>\r\n    <PlatformToolset>v141</PlatformToolset>\r\n    <WholeProgramOptimization>true</WholeProgramOptimization>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\" Label=\"Configuration\">\r\n    <ConfigurationType>Application</ConfigurationType>\r\n    <UseDebugLibraries>true</UseDebugLibraries>\r\n    <PlatformToolset>v141</PlatformToolset>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\" Label=\"Configuration\">\r\n    <ConfigurationType>Application</ConfigurationType>\r\n    <UseDebugLibraries>false</UseDebugLibraries>\r\n    <PlatformToolset>v141</PlatformToolset>\r\n    <WholeProgramOptimization>true</WholeProgramOptimization>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\r\n  <ImportGroup Label=\"ExtensionSettings\">\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"Shared\">\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <PropertyGroup Label=\"UserMacros\" />\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <LinkIncremental>true</LinkIncremental>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\r\n    <LinkIncremental>true</LinkIncremental>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <LinkIncremental>false</LinkIncremental>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n    <LinkIncremental>false</LinkIncremental>\r\n  </PropertyGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <ClCompile>\r\n      <PrecompiledHeader>Use</PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <Optimization>Disabled</Optimization>\r\n      <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Windows</SubSystem>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\r\n    <ClCompile>\r\n      <PrecompiledHeader>NotUsing</PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <Optimization>Disabled</Optimization>\r\n      <PreprocessorDefinitions>_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Windows</SubSystem>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <ClCompile>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <PrecompiledHeader>Use</PrecompiledHeader>\r\n      <Optimization>MaxSpeed</Optimization>\r\n      <FunctionLevelLinking>true</FunctionLevelLinking>\r\n      <IntrinsicFunctions>true</IntrinsicFunctions>\r\n      <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Windows</SubSystem>\r\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\r\n      <OptimizeReferences>true</OptimizeReferences>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n    <ClCompile>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <PrecompiledHeader>Use</PrecompiledHeader>\r\n      <Optimization>MaxSpeed</Optimization>\r\n      <FunctionLevelLinking>true</FunctionLevelLinking>\r\n      <IntrinsicFunctions>true</IntrinsicFunctions>\r\n      <PreprocessorDefinitions>NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Windows</SubSystem>\r\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\r\n      <OptimizeReferences>true</OptimizeReferences>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemGroup>\r\n    <ClInclude Include=\"Camera.h\" />\r\n    <ClInclude Include=\"CommonConvert.h\" />\r\n    <ClInclude Include=\"Decompose.h\" />\r\n    <ClInclude Include=\"Dll.h\" />\r\n    <ClInclude Include=\"fpsgame.h\" />\r\n    <ClInclude Include=\"GeomReindex.h\" />\r\n    <ClInclude Include=\"graphics\\Camera.h\" />\r\n    <ClInclude Include=\"graphics\\CinemaManager.h\" />\r\n    <ClInclude Include=\"graphics\\CinimaPath.h\" />\r\n    <ClInclude Include=\"graphics\\ColladaManager.h\" />\r\n    <ClInclude Include=\"graphics\\Color.h\" />\r\n    <ClInclude Include=\"graphics\\Decal.h\" />\r\n    <ClInclude Include=\"graphics\\Entity.h\" />\r\n    <ClInclude Include=\"graphics\\Font.h\" />\r\n    <ClInclude Include=\"graphics\\FontManager.h\" />\r\n    <ClInclude Include=\"graphics\\FontMetrics.h\" />\r\n    <ClInclude Include=\"graphics\\Frustum.h\" />\r\n    <ClInclude Include=\"graphics\\GameView.h\" />\r\n    <ClInclude Include=\"graphics\\HeightMipmap.h\" />\r\n    <ClInclude Include=\"graphics\\HFTracer.h\" />\r\n    <ClInclude Include=\"graphics\\LightEnv.h\" />\r\n    <ClInclude Include=\"graphics\\LOSTexture.h\" />\r\n    <ClInclude Include=\"graphics\\MapGenerator.h\" />\r\n    <ClInclude Include=\"graphics\\MapIO.h\" />\r\n    <ClInclude Include=\"graphics\\MapReader.h\" />\r\n    <ClInclude Include=\"graphics\\MapWriter.h\" />\r\n    <ClInclude Include=\"graphics\\Material.h\" />\r\n    <ClInclude Include=\"graphics\\MaterialManager.h\" />\r\n    <ClInclude Include=\"graphics\\MeshManager.h\" />\r\n    <ClInclude Include=\"graphics\\MiniPatch.h\" />\r\n    <ClInclude Include=\"graphics\\Model.h\" />\r\n    <ClInclude Include=\"graphics\\ModelAbstract.h\" />\r\n    <ClInclude Include=\"graphics\\ModelDef.h\" />\r\n    <ClInclude Include=\"graphics\\ObjectBase.h\" />\r\n    <ClInclude Include=\"graphics\\ObjectEntry.h\" />\r\n    <ClInclude Include=\"graphics\\ObjectManager.h\" />\r\n    <ClInclude Include=\"graphics\\Overlay.h\" />\r\n    <ClInclude Include=\"graphics\\ParticleEmitter.h\" />\r\n    <ClInclude Include=\"graphics\\ParticleEmitterType.h\" />\r\n    <ClInclude Include=\"graphics\\ParticleManager.h\" />\r\n    <ClInclude Include=\"graphics\\Patch.h\" />\r\n    <ClInclude Include=\"graphics\\RenderableObject.h\" />\r\n    <ClInclude Include=\"graphics\\SColor.h\" />\r\n    <ClInclude Include=\"graphics\\ShaderDefines.h\" />\r\n    <ClInclude Include=\"graphics\\ShaderManager.h\" />\r\n    <ClInclude Include=\"graphics\\ShaderProgram.h\" />\r\n    <ClInclude Include=\"graphics\\ShaderProgramPtr.h\" />\r\n    <ClInclude Include=\"graphics\\ShaderTechnique.h\" />\r\n    <ClInclude Include=\"graphics\\SkeletonAnim.h\" />\r\n    <ClInclude Include=\"graphics\\SkeletonAnimDef.h\" />\r\n    <ClInclude Include=\"graphics\\SkeletonAnimManager.h\" />\r\n    <ClInclude Include=\"graphics\\Terrain.h\" />\r\n    <ClInclude Include=\"graphics\\TerrainProperties.h\" />\r\n    <ClInclude Include=\"graphics\\TerrainTextureEntry.h\" />\r\n    <ClInclude Include=\"graphics\\TerrainTextureManager.h\" />\r\n    <ClInclude Include=\"graphics\\TerritoryBoundary.h\" />\r\n    <ClInclude Include=\"graphics\\TerritoryTexture.h\" />\r\n    <ClInclude Include=\"graphics\\TextRenderer.h\" />\r\n    <ClInclude Include=\"graphics\\Texture.h\" />\r\n    <ClInclude Include=\"graphics\\TextureConverter.h\" />\r\n    <ClInclude Include=\"graphics\\TextureManager.h\" />\r\n    <ClInclude Include=\"graphics\\Unit.h\" />\r\n    <ClInclude Include=\"graphics\\UnitAnimation.h\" />\r\n    <ClInclude Include=\"graphics\\UnitManager.h\" />\r\n    <ClInclude Include=\"gui\\adts\\bit_buf.h\" />\r\n    <ClInclude Include=\"gui\\adts\\cache_adt.h\" />\r\n    <ClInclude Include=\"gui\\adts\\dyn_hash_tbl.h\" />\r\n    <ClInclude Include=\"gui\\adts\\ring_buf.h\" />\r\n    <ClInclude Include=\"gui\\alignment.h\" />\r\n    <ClInclude Include=\"gui\\allocators\\aligned_allocator.h\" />\r\n    <ClInclude Include=\"gui\\allocators\\allocator_adapters.h\" />\r\n    <ClInclude Include=\"gui\\allocators\\allocator_checker.h\" />\r\n    <ClInclude Include=\"gui\\allocators\\allocator_policies.h\" />\r\n    <ClInclude Include=\"gui\\allocators\\arena.h\" />\r\n    <ClInclude Include=\"gui\\allocators\\dynarray.h\" />\r\n    <ClInclude Include=\"gui\\allocators\\freelist.h\" />\r\n    <ClInclude Include=\"gui\\allocators\\headerless.h\" />\r\n    <ClInclude Include=\"gui\\allocators\\overrun_protector.h\" />\r\n    <ClInclude Include=\"gui\\allocators\\page_aligned.h\" />\r\n    <ClInclude Include=\"gui\\allocators\\pool.h\" />\r\n    <ClInclude Include=\"gui\\allocators\\shared_ptr.h\" />\r\n    <ClInclude Include=\"gui\\allocators\\tests\\test_allocators.h\" />\r\n    <ClInclude Include=\"gui\\allocators\\tests\\test_headerless.h\" />\r\n    <ClInclude Include=\"gui\\allocators\\unique_range.h\" />\r\n    <ClInclude Include=\"gui\\app_hooks.h\" />\r\n    <ClInclude Include=\"gui\\base32.h\" />\r\n    <ClInclude Include=\"gui\\bits.h\" />\r\n    <ClInclude Include=\"gui\\byte_order.h\" />\r\n    <ClInclude Include=\"gui\\code_annotation.h\" />\r\n    <ClInclude Include=\"gui\\code_generation.h\" />\r\n    <ClInclude Include=\"gui\\config.h\" />\r\n    <ClInclude Include=\"gui\\config2.h\" />\r\n    <ClInclude Include=\"gui\\debug.h\" />\r\n    <ClInclude Include=\"gui\\debug_stl.h\" />\r\n    <ClInclude Include=\"gui\\external_libraries\\curl.h\" />\r\n    <ClInclude Include=\"gui\\external_libraries\\dbghelp.h\" />\r\n    <ClInclude Include=\"gui\\external_libraries\\dbghelp_funcs.h\" />\r\n    <ClInclude Include=\"gui\\external_libraries\\enet.h\" />\r\n    <ClInclude Include=\"gui\\external_libraries\\glext_funcs.h\" />\r\n    <ClInclude Include=\"gui\\external_libraries\\icu.h\" />\r\n    <ClInclude Include=\"gui\\external_libraries\\libsdl.h\" />\r\n    <ClInclude Include=\"gui\\external_libraries\\libsdl_fwd.h\" />\r\n    <ClInclude Include=\"gui\\external_libraries\\openal.h\" />\r\n    <ClInclude Include=\"gui\\external_libraries\\opengl.h\" />\r\n    <ClInclude Include=\"gui\\external_libraries\\openmp.h\" />\r\n    <ClInclude Include=\"gui\\external_libraries\\png.h\" />\r\n    <ClInclude Include=\"gui\\external_libraries\\powrprof.h\" />\r\n    <ClInclude Include=\"gui\\external_libraries\\suppress_boost_warnings.h\" />\r\n    <ClInclude Include=\"gui\\external_libraries\\tinygettext.h\" />\r\n    <ClInclude Include=\"gui\\external_libraries\\vorbis.h\" />\r\n    <ClInclude Include=\"gui\\external_libraries\\wxwidgets.h\" />\r\n    <ClInclude Include=\"gui\\external_libraries\\zlib.h\" />\r\n    <ClInclude Include=\"gui\\file\\archive\\archive.h\" />\r\n    <ClInclude Include=\"gui\\file\\archive\\archive_zip.h\" />\r\n    <ClInclude Include=\"gui\\file\\archive\\codec.h\" />\r\n    <ClInclude Include=\"gui\\file\\archive\\codec_zlib.h\" />\r\n    <ClInclude Include=\"gui\\file\\archive\\stream.h\" />\r\n    <ClInclude Include=\"gui\\file\\common\\file_loader.h\" />\r\n    <ClInclude Include=\"gui\\file\\common\\file_stats.h\" />\r\n    <ClInclude Include=\"gui\\file\\common\\real_directory.h\" />\r\n    <ClInclude Include=\"gui\\file\\common\\trace.h\" />\r\n    <ClInclude Include=\"gui\\file\\disabled_tests\\test_file_cache.h\" />\r\n    <ClInclude Include=\"gui\\file\\disabled_tests\\test_path.h\" />\r\n    <ClInclude Include=\"gui\\file\\file.h\" />\r\n    <ClInclude Include=\"gui\\file\\file_system.h\" />\r\n    <ClInclude Include=\"gui\\file\\io\\io.h\" />\r\n    <ClInclude Include=\"gui\\file\\io\\write_buffer.h\" />\r\n    <ClInclude Include=\"gui\\file\\vfs\\file_cache.h\" />\r\n    <ClInclude Include=\"gui\\file\\vfs\\tests\\test_vfs_tree.h\" />\r\n    <ClInclude Include=\"gui\\file\\vfs\\vfs.h\" />\r\n    <ClInclude Include=\"gui\\file\\vfs\\vfs_lookup.h\" />\r\n    <ClInclude Include=\"gui\\file\\vfs\\vfs_path.h\" />\r\n    <ClInclude Include=\"gui\\file\\vfs\\vfs_populate.h\" />\r\n    <ClInclude Include=\"gui\\file\\vfs\\vfs_tree.h\" />\r\n    <ClInclude Include=\"gui\\file\\vfs\\vfs_util.h\" />\r\n    <ClInclude Include=\"gui\\fnv_hash.h\" />\r\n    <ClInclude Include=\"gui\\frequency_filter.h\" />\r\n    <ClInclude Include=\"gui\\input.h\" />\r\n    <ClInclude Include=\"gui\\lib.h\" />\r\n    <ClInclude Include=\"gui\\lib_api.h\" />\r\n    <ClInclude Include=\"gui\\module_init.h\" />\r\n    <ClInclude Include=\"gui\\ogl.h\" />\r\n    <ClInclude Include=\"gui\\os_path.h\" />\r\n    <ClInclude Include=\"gui\\path.h\" />\r\n    <ClInclude Include=\"gui\\pointer_typedefs.h\" />\r\n    <ClInclude Include=\"gui\\precompiled.h\" />\r\n    <ClInclude Include=\"gui\\rand.h\" />\r\n    <ClInclude Include=\"gui\\regex.h\" />\r\n    <ClInclude Include=\"gui\\secure_crt.h\" />\r\n    <ClInclude Include=\"gui\\self_test.h\" />\r\n    <ClInclude Include=\"gui\\status.h\" />\r\n    <ClInclude Include=\"gui\\svn_revision.h\" />\r\n    <ClInclude Include=\"gui\\timer.h\" />\r\n    <ClInclude Include=\"gui\\types.h\" />\r\n    <ClInclude Include=\"gui\\utf8.h\" />\r\n    <ClInclude Include=\"JSInterfaace_GameView.h\" />\r\n    <ClInclude Include=\"Math.h\" />\r\n    <ClInclude Include=\"PMDConvert.h\" />\r\n    <ClInclude Include=\"precompiled.h\" />\r\n    <ClInclude Include=\"PSAConvert.h\" />\r\n    <ClInclude Include=\"Resource.h\" />\r\n    <ClInclude Include=\"stdafx.h\" />\r\n    <ClInclude Include=\"StdSkeletons.h\" />\r\n    <ClInclude Include=\"targetver.h\" />\r\n    <ClInclude Include=\"test_Color.h\" />\r\n    <ClInclude Include=\"XMLFix.h\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClCompile Include=\"Camera.cpp\" />\r\n    <ClCompile Include=\"CommonConvert.cpp\" />\r\n    <ClCompile Include=\"Decompose.cpp\" />\r\n    <ClCompile Include=\"Dll.cpp\" />\r\n    <ClCompile Include=\"fpsgame.cpp\" />\r\n    <ClCompile Include=\"GeomReindex.cpp\" />\r\n    <ClCompile Include=\"graphics\\Camera.cpp\" />\r\n    <ClCompile Include=\"graphics\\CinemaManager.cpp\" />\r\n    <ClCompile Include=\"graphics\\CinimaPath.cpp\" />\r\n    <ClCompile Include=\"graphics\\ColladaManager.cpp\" />\r\n    <ClCompile Include=\"graphics\\Color.cpp\" />\r\n    <ClCompile Include=\"graphics\\Decal.cpp\" />\r\n    <ClCompile Include=\"graphics\\Font.cpp\" />\r\n    <ClCompile Include=\"graphics\\FontManager.cpp\" />\r\n    <ClCompile Include=\"graphics\\FontMetrics.cpp\" />\r\n    <ClCompile Include=\"graphics\\Frustum.cpp\" />\r\n    <ClCompile Include=\"graphics\\GameView.cpp\" />\r\n    <ClCompile Include=\"graphics\\HeightMipmap.cpp\" />\r\n    <ClCompile Include=\"graphics\\HFTracer.cpp\" />\r\n    <ClCompile Include=\"graphics\\LightEnv.cpp\" />\r\n    <ClCompile Include=\"graphics\\LOSTexture.cpp\" />\r\n    <ClCompile Include=\"graphics\\MapGenerator.cpp\" />\r\n    <ClCompile Include=\"graphics\\MapReader.cpp\" />\r\n    <ClCompile Include=\"graphics\\MapWriter.cpp\" />\r\n    <ClCompile Include=\"graphics\\Material.cpp\" />\r\n    <ClCompile Include=\"graphics\\MaterialManager.cpp\" />\r\n    <ClCompile Include=\"graphics\\MeshManager.cpp\" />\r\n    <ClCompile Include=\"graphics\\MiniPatch.cpp\" />\r\n    <ClCompile Include=\"graphics\\Model.cpp\" />\r\n    <ClCompile Include=\"graphics\\ModelAbstract.cpp\" />\r\n    <ClCompile Include=\"graphics\\ModelDef.cpp\" />\r\n    <ClCompile Include=\"graphics\\ObjectBase.cpp\" />\r\n    <ClCompile Include=\"graphics\\ObjectEntry.cpp\" />\r\n    <ClCompile Include=\"graphics\\ObjectManager.cpp\" />\r\n    <ClCompile Include=\"graphics\\Overlay.cpp\" />\r\n    <ClCompile Include=\"graphics\\ParticleEmitter.cpp\" />\r\n    <ClCompile Include=\"graphics\\ParticleEmitterType.cpp\" />\r\n    <ClCompile Include=\"graphics\\ParticleManager.cpp\" />\r\n    <ClCompile Include=\"graphics\\Patch.cpp\" />\r\n    <ClCompile Include=\"graphics\\ShaderDefines.cpp\" />\r\n    <ClCompile Include=\"graphics\\ShaderManager.cpp\" />\r\n    <ClCompile Include=\"graphics\\ShaderProgram.cpp\" />\r\n    <ClCompile Include=\"graphics\\ShaderProgramFFP.cpp\" />\r\n    <ClCompile Include=\"graphics\\ShaderTechnique.cpp\" />\r\n    <ClCompile Include=\"graphics\\SkeletonAnimDef.cpp\" />\r\n    <ClCompile Include=\"graphics\\SkeletonAnimManager.cpp\" />\r\n    <ClCompile Include=\"graphics\\Terrain.cpp\" />\r\n    <ClCompile Include=\"graphics\\TerrainProperties.cpp\" />\r\n    <ClCompile Include=\"graphics\\TerrainTextureEntry.cpp\" />\r\n    <ClCompile Include=\"graphics\\TerrainTextureManager.cpp\" />\r\n    <ClCompile Include=\"graphics\\TerritoryBoundary.cpp\" />\r\n    <ClCompile Include=\"graphics\\TerritoryTexture.cpp\" />\r\n    <ClCompile Include=\"graphics\\TextRenderer.cpp\" />\r\n    <ClCompile Include=\"graphics\\TextureConverter.cpp\" />\r\n    <ClCompile Include=\"graphics\\TextureManager.cpp\" />\r\n    <ClCompile Include=\"graphics\\Unit.cpp\" />\r\n    <ClCompile Include=\"graphics\\UnitAnimation.cpp\" />\r\n    <ClCompile Include=\"graphics\\UnitManager.cpp\" />\r\n    <ClCompile Include=\"gui\\allocators\\arena.cpp\" />\r\n    <ClCompile Include=\"gui\\allocators\\dynarray.cpp\" />\r\n    <ClCompile Include=\"gui\\allocators\\freelist.cpp\" />\r\n    <ClCompile Include=\"gui\\allocators\\headerless.cpp\" />\r\n    <ClCompile Include=\"gui\\allocators\\page_aligned.cpp\" />\r\n    <ClCompile Include=\"gui\\allocators\\pool.cpp\" />\r\n    <ClCompile Include=\"gui\\allocators\\shared_ptr.cpp\" />\r\n    <ClCompile Include=\"gui\\allocators\\unique_range.cpp\" />\r\n    <ClCompile Include=\"gui\\app_hooks.cpp\" />\r\n    <ClCompile Include=\"gui\\base32.cpp\" />\r\n    <ClCompile Include=\"gui\\bits.cpp\" />\r\n    <ClCompile Include=\"gui\\byte_order.cpp\" />\r\n    <ClCompile Include=\"gui\\debug.cpp\" />\r\n    <ClCompile Include=\"gui\\debug_stl.cpp\" />\r\n    <ClCompile Include=\"gui\\external_libraries\\dbghelp.cpp\" />\r\n    <ClCompile Include=\"gui\\file\\archive\\archive.cpp\" />\r\n    <ClCompile Include=\"gui\\file\\archive\\archive_zip.cpp\" />\r\n    <ClCompile Include=\"gui\\file\\archive\\codec.cpp\" />\r\n    <ClCompile Include=\"gui\\file\\archive\\codec_zlib.cpp\" />\r\n    <ClCompile Include=\"gui\\file\\archive\\stream.cpp\" />\r\n    <ClCompile Include=\"gui\\file\\common\\file_loader.cpp\" />\r\n    <ClCompile Include=\"gui\\file\\common\\file_stats.cpp\" />\r\n    <ClCompile Include=\"gui\\file\\common\\real_directory.cpp\" />\r\n    <ClCompile Include=\"gui\\file\\common\\trace.cpp\" />\r\n    <ClCompile Include=\"gui\\file\\file.cpp\" />\r\n    <ClCompile Include=\"gui\\file\\file_system.cpp\" />\r\n    <ClCompile Include=\"gui\\file\\io\\io.cpp\" />\r\n    <ClCompile Include=\"gui\\file\\io\\write_buffer.cpp\" />\r\n    <ClCompile Include=\"gui\\file\\vfs\\file_cache.cpp\" />\r\n    <ClCompile Include=\"gui\\file\\vfs\\vfs.cpp\" />\r\n    <ClCompile Include=\"gui\\file\\vfs\\vfs_lookup.cpp\" />\r\n    <ClCompile Include=\"gui\\file\\vfs\\vfs_path.cpp\" />\r\n    <ClCompile Include=\"gui\\file\\vfs\\vfs_populate.cpp\" />\r\n    <ClCompile Include=\"gui\\file\\vfs\\vfs_tree.cpp\" />\r\n    <ClCompile Include=\"gui\\file\\vfs\\vfs_util.cpp\" />\r\n    <ClCompile Include=\"gui\\fnv_hash.cpp\" />\r\n    <ClCompile Include=\"gui\\frequency_filter.cpp\" />\r\n    <ClCompile Include=\"gui\\input.cpp\" />\r\n    <ClCompile Include=\"gui\\lib.cpp\" />\r\n    <ClCompile Include=\"gui\\module_init.cpp\" />\r\n    <ClCompile Include=\"gui\\ogl.cpp\" />\r\n    <ClCompile Include=\"gui\\path.cpp\" />\r\n    <ClCompile Include=\"gui\\rand.cpp\" />\r\n    <ClCompile Include=\"gui\\regex.cpp\" />\r\n    <ClCompile Include=\"gui\\secure_crt.cpp\" />\r\n    <ClCompile Include=\"gui\\status.cpp\" />\r\n    <ClCompile Include=\"gui\\svn_revision.cpp\" />\r\n    <ClCompile Include=\"gui\\timer.cpp\" />\r\n    <ClCompile Include=\"gui\\utf8.cpp\" />\r\n    <ClCompile Include=\"gui\\wsecure_crt.cpp\" />\r\n    <ClCompile Include=\"JSInterface_GameView.cpp\" />\r\n    <ClCompile Include=\"Math.cpp\" />\r\n    <ClCompile Include=\"PMDConvert.cpp\" />\r\n    <ClCompile Include=\"precompiled.cpp\" />\r\n    <ClCompile Include=\"PSAConvert.cpp\" />\r\n    <ClCompile Include=\"StdSkeletons.cpp\" />\r\n    <ClCompile Include=\"XMLFix.cpp\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ResourceCompile Include=\"fpsgame.rc\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <Image Include=\"fpsgame.ico\" />\r\n    <Image Include=\"small.ico\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <None Include=\"tests.py\" />\r\n  </ItemGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\r\n  <ImportGroup Label=\"ExtensionTargets\">\r\n  </ImportGroup>\r\n</Project>"
  },
  {
    "path": "fpsgame/fpsgame.vcxproj.user",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project ToolsVersion=\"15.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\r\n  <PropertyGroup />\r\n</Project>"
  },
  {
    "path": "fpsgame/graphics/Camera.cpp",
    "content": "/* Copyright (C) 2010 Wildfire Games.\n* This file is part of 0 A.D.\n*\n* 0 A.D. is free software: you can redistribute it and/or modify\n* it under the terms of the GNU General Public License as published by\n* the Free Software Foundation, either version 2 of the License, or\n* (at your option) any later version.\n*\n* 0 A.D. is distributed in the hope that it will be useful,\n* but WITHOUT ANY WARRANTY; without even the implied warranty of\n* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n* GNU General Public License for more details.\n*\n* You should have received a copy of the GNU General Public License\n* along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n/*\n* CCamera holds a view and a projection matrix. It also has a frustum\n* which can be used to cull objects for rendering.\n*/\n\n#include \"precompiled.h\"\n\n#include \"Camera.h\"\n\n#include \"graphics/HFTracer.h\"\n#include \"graphics/Terrain.h\"\n#include \"lib/ogl.h\"\n#include \"maths/MathUtil.h\"\n#include \"maths/Vector4D.h\"\n#include \"ps/Game.h\"\n#include \"ps/World.h\"\n#include \"renderer/Renderer.h\"\n#include \"renderer/WaterManager.h\"\n\nCCamera::CCamera()\n{\n\t// set viewport to something anything should handle, but should be initialised\n\t// to window size before use\n\tm_ViewPort.m_X = 0;\n\tm_ViewPort.m_Y = 0;\n\tm_ViewPort.m_Width = 800;\n\tm_ViewPort.m_Height = 600;\n}\n\nCCamera::~CCamera()\n{\n}\n\nvoid CCamera::SetProjection(float nearp, float farp, float fov)\n{\n\tm_NearPlane = nearp;\n\tm_FarPlane = farp;\n\tm_FOV = fov;\n\n\tfloat aspect = (float)m_ViewPort.m_Width / (float)m_ViewPort.m_Height;\n\tfloat f = 1.0f / tanf(m_FOV / 2);\n\n\tm_ProjMat.SetZero();\n\tm_ProjMat._11 = f / aspect;\n\tm_ProjMat._22 = f;\n\tm_ProjMat._33 = -(m_FarPlane + m_NearPlane) / (m_NearPlane - m_FarPlane);\n\tm_ProjMat._34 = 2 * m_FarPlane*m_NearPlane / (m_NearPlane - m_FarPlane);\n\tm_ProjMat._43 = 1.0f;\n}\n\nvoid CCamera::SetProjectionTile(int tiles, int tile_x, int tile_y)\n{\n\n\tfloat aspect = (float)m_ViewPort.m_Width / (float)m_ViewPort.m_Height;\n\tfloat f = 1.0f / tanf(m_FOV / 2);\n\n\tm_ProjMat._11 = tiles*f / aspect;\n\tm_ProjMat._22 = tiles*f;\n\tm_ProjMat._13 = -(1 - tiles + 2 * tile_x);\n\tm_ProjMat._23 = -(1 - tiles + 2 * tile_y);\n}\n\n//Updates the frustum planes. Should be called\n//everytime the view or projection matrices are\n//altered.\nvoid CCamera::UpdateFrustum(const CBoundingBoxAligned& scissor)\n{\n\tCMatrix3D MatFinal;\n\tCMatrix3D MatView;\n\n\tm_Orientation.GetInverse(MatView);\n\n\tMatFinal = m_ProjMat * MatView;\n\n\tm_ViewFrustum.SetNumPlanes(6);\n\n\t// get the RIGHT plane\n\tm_ViewFrustum.m_aPlanes[0].m_Norm.X = scissor[1].X*MatFinal._41 - MatFinal._11;\n\tm_ViewFrustum.m_aPlanes[0].m_Norm.Y = scissor[1].X*MatFinal._42 - MatFinal._12;\n\tm_ViewFrustum.m_aPlanes[0].m_Norm.Z = scissor[1].X*MatFinal._43 - MatFinal._13;\n\tm_ViewFrustum.m_aPlanes[0].m_Dist = scissor[1].X*MatFinal._44 - MatFinal._14;\n\n\t// get the LEFT plane\n\tm_ViewFrustum.m_aPlanes[1].m_Norm.X = -scissor[0].X*MatFinal._41 + MatFinal._11;\n\tm_ViewFrustum.m_aPlanes[1].m_Norm.Y = -scissor[0].X*MatFinal._42 + MatFinal._12;\n\tm_ViewFrustum.m_aPlanes[1].m_Norm.Z = -scissor[0].X*MatFinal._43 + MatFinal._13;\n\tm_ViewFrustum.m_aPlanes[1].m_Dist = -scissor[0].X*MatFinal._44 + MatFinal._14;\n\n\t// get the BOTTOM plane\n\tm_ViewFrustum.m_aPlanes[2].m_Norm.X = -scissor[0].Y*MatFinal._41 + MatFinal._21;\n\tm_ViewFrustum.m_aPlanes[2].m_Norm.Y = -scissor[0].Y*MatFinal._42 + MatFinal._22;\n\tm_ViewFrustum.m_aPlanes[2].m_Norm.Z = -scissor[0].Y*MatFinal._43 + MatFinal._23;\n\tm_ViewFrustum.m_aPlanes[2].m_Dist = -scissor[0].Y*MatFinal._44 + MatFinal._24;\n\n\t// get the TOP plane\n\tm_ViewFrustum.m_aPlanes[3].m_Norm.X = scissor[1].Y*MatFinal._41 - MatFinal._21;\n\tm_ViewFrustum.m_aPlanes[3].m_Norm.Y = scissor[1].Y*MatFinal._42 - MatFinal._22;\n\tm_ViewFrustum.m_aPlanes[3].m_Norm.Z = scissor[1].Y*MatFinal._43 - MatFinal._23;\n\tm_ViewFrustum.m_aPlanes[3].m_Dist = scissor[1].Y*MatFinal._44 - MatFinal._24;\n\n\t// get the FAR plane\n\tm_ViewFrustum.m_aPlanes[4].m_Norm.X = scissor[1].Z*MatFinal._41 - MatFinal._31;\n\tm_ViewFrustum.m_aPlanes[4].m_Norm.Y = scissor[1].Z*MatFinal._42 - MatFinal._32;\n\tm_ViewFrustum.m_aPlanes[4].m_Norm.Z = scissor[1].Z*MatFinal._43 - MatFinal._33;\n\tm_ViewFrustum.m_aPlanes[4].m_Dist = scissor[1].Z*MatFinal._44 - MatFinal._34;\n\n\t// get the NEAR plane\n\tm_ViewFrustum.m_aPlanes[5].m_Norm.X = -scissor[0].Z*MatFinal._41 + MatFinal._31;\n\tm_ViewFrustum.m_aPlanes[5].m_Norm.Y = -scissor[0].Z*MatFinal._42 + MatFinal._32;\n\tm_ViewFrustum.m_aPlanes[5].m_Norm.Z = -scissor[0].Z*MatFinal._43 + MatFinal._33;\n\tm_ViewFrustum.m_aPlanes[5].m_Dist = -scissor[0].Z*MatFinal._44 + MatFinal._34;\n\n\tfor (size_t i = 0; i < 6; ++i)\n\t\tm_ViewFrustum.m_aPlanes[i].Normalize();\n}\n\nvoid CCamera::ClipFrustum(const CPlane& clipPlane)\n{\n\tCPlane normClipPlane = clipPlane;\n\tnormClipPlane.Normalize();\n\tm_ViewFrustum.AddPlane(normClipPlane);\n}\n\nvoid CCamera::SetViewPort(const SViewPort& viewport)\n{\n\tm_ViewPort.m_X = viewport.m_X;\n\tm_ViewPort.m_Y = viewport.m_Y;\n\tm_ViewPort.m_Width = viewport.m_Width;\n\tm_ViewPort.m_Height = viewport.m_Height;\n}\n\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// GetCameraPlanePoints: return four points in camera space at given distance from camera\nvoid CCamera::GetCameraPlanePoints(float dist, CVector3D pts[4]) const\n{\n\tfloat aspect = float(m_ViewPort.m_Width) / float(m_ViewPort.m_Height);\n\tfloat x = dist*aspect*tanf(m_FOV*0.5f);\n\tfloat y = dist*tanf(m_FOV*0.5f);\n\n\tpts[0].X = -x;\n\tpts[0].Y = -y;\n\tpts[0].Z = dist;\n\tpts[1].X = x;\n\tpts[1].Y = -y;\n\tpts[1].Z = dist;\n\tpts[2].X = x;\n\tpts[2].Y = y;\n\tpts[2].Z = dist;\n\tpts[3].X = -x;\n\tpts[3].Y = y;\n\tpts[3].Z = dist;\n}\n\nvoid CCamera::BuildCameraRay(int px, int py, CVector3D& origin, CVector3D& dir) const\n{\n\tCVector3D cPts[4];\n\tGetCameraPlanePoints(m_FarPlane, cPts);\n\n\t// transform to world space\n\tCVector3D wPts[4];\n\tfor (int i = 0; i < 4; i++)\n\t\twPts[i] = m_Orientation.Transform(cPts[i]);\n\n\t// get world space position of mouse point\n\tfloat dx = (float)px / (float)g_Renderer.GetWidth();\n\tfloat dz = 1 - (float)py / (float)g_Renderer.GetHeight();\n\n\tCVector3D vdx = wPts[1] - wPts[0];\n\tCVector3D vdz = wPts[3] - wPts[0];\n\tCVector3D pt = wPts[0] + (vdx * dx) + (vdz * dz);\n\n\t// copy origin\n\torigin = m_Orientation.GetTranslation();\n\t// build direction\n\tdir = pt - origin;\n\tdir.Normalize();\n}\n\nvoid CCamera::GetScreenCoordinates(const CVector3D& world, float& x, float& y) const\n{\n\tCMatrix3D transform = m_ProjMat * m_Orientation.GetInverse();\n\n\tCVector4D screenspace = transform.Transform(CVector4D(world.X, world.Y, world.Z, 1.0f));\n\n\tx = screenspace.X / screenspace.W;\n\ty = screenspace.Y / screenspace.W;\n\tx = (x + 1) * 0.5f * g_Renderer.GetWidth();\n\ty = (1 - y) * 0.5f * g_Renderer.GetHeight();\n}\n\nCVector3D CCamera::GetWorldCoordinates(int px, int py, bool aboveWater) const\n{\n\tCHFTracer tracer(g_Game->GetWorld()->GetTerrain());\n\tint x, z;\n\tCVector3D origin, dir, delta, terrainPoint, waterPoint;\n\n\tBuildCameraRay(px, py, origin, dir);\n\n\n\tbool gotTerrain = tracer.RayIntersect(origin, dir, x, z, terrainPoint);\n\n\tif (!aboveWater)\n\t{\n\t\tif (gotTerrain)\n\t\t\treturn terrainPoint;\n\n\t\t// Off the edge of the world?\n\t\t// Work out where it /would/ hit, if the map were extended out to infinity with average height.\n\t\treturn GetWorldCoordinates(px, py, 50.0f);\n\t}\n\n\tCPlane plane;\n\tplane.Set(CVector3D(0.f, 1.f, 0.f),\t\t\t\t\t\t\t\t\t\t// upwards normal\n\t\tCVector3D(0.f, g_Renderer.GetWaterManager()->m_WaterHeight, 0.f));\t// passes through water plane\n\n\tbool gotWater = plane.FindRayIntersection(origin, dir, &waterPoint);\n\n\t// Clamp the water intersection to within the map's bounds, so that\n\t// we'll always return a valid position on the map\n\tssize_t mapSize = g_Game->GetWorld()->GetTerrain()->GetVerticesPerSide();\n\tif (gotWater)\n\t{\n\t\twaterPoint.X = clamp(waterPoint.X, 0.f, (float)((mapSize - 1)*TERRAIN_TILE_SIZE));\n\t\twaterPoint.Z = clamp(waterPoint.Z, 0.f, (float)((mapSize - 1)*TERRAIN_TILE_SIZE));\n\t}\n\n\tif (gotTerrain)\n\t{\n\t\tif (gotWater)\n\t\t{\n\t\t\t// Intersecting both heightmap and water plane; choose the closest of those\n\t\t\tif ((origin - terrainPoint).LengthSquared() < (origin - waterPoint).LengthSquared())\n\t\t\t\treturn terrainPoint;\n\t\t\telse\n\t\t\t\treturn waterPoint;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Intersecting heightmap but parallel to water plane\n\t\t\treturn terrainPoint;\n\t\t}\n\t}\n\telse\n\t{\n\t\tif (gotWater)\n\t\t{\n\t\t\t// Only intersecting water plane\n\t\t\treturn waterPoint;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Not intersecting terrain or water; just return 0,0,0.\n\t\t\treturn CVector3D(0.f, 0.f, 0.f);\n\t\t}\n\t}\n\n}\n\nCVector3D CCamera::GetWorldCoordinates(int px, int py, float h) const\n{\n\tCPlane plane;\n\tplane.Set(CVector3D(0.f, 1.f, 0.f), CVector3D(0.f, h, 0.f)); // upwards normal, passes through h\n\n\tCVector3D origin, dir, delta, currentTarget;\n\n\tBuildCameraRay(px, py, origin, dir);\n\n\tif (plane.FindRayIntersection(origin, dir, &currentTarget))\n\t\treturn currentTarget;\n\n\t// No intersection with the infinite plane - nothing sensible can be returned,\n\t// so just choose an arbitrary point on the plane\n\treturn CVector3D(0.f, h, 0.f);\n}\n\nCVector3D CCamera::GetFocus() const\n{\n\t// Basically the same as GetWorldCoordinates\n\n\tCHFTracer tracer(g_Game->GetWorld()->GetTerrain());\n\tint x, z;\n\n\tCVector3D origin, dir, delta, terrainPoint, waterPoint;\n\n\torigin = m_Orientation.GetTranslation();\n\tdir = m_Orientation.GetIn();\n\n\tbool gotTerrain = tracer.RayIntersect(origin, dir, x, z, terrainPoint);\n\n\tCPlane plane;\n\tplane.Set(CVector3D(0.f, 1.f, 0.f),\t\t\t\t\t\t\t\t\t\t// upwards normal\n\t\tCVector3D(0.f, g_Renderer.GetWaterManager()->m_WaterHeight, 0.f));\t// passes through water plane\n\n\tbool gotWater = plane.FindRayIntersection(origin, dir, &waterPoint);\n\n\t// Clamp the water intersection to within the map's bounds, so that\n\t// we'll always return a valid position on the map\n\tssize_t mapSize = g_Game->GetWorld()->GetTerrain()->GetVerticesPerSide();\n\tif (gotWater)\n\t{\n\t\twaterPoint.X = clamp(waterPoint.X, 0.f, (float)((mapSize - 1)*TERRAIN_TILE_SIZE));\n\t\twaterPoint.Z = clamp(waterPoint.Z, 0.f, (float)((mapSize - 1)*TERRAIN_TILE_SIZE));\n\t}\n\n\tif (gotTerrain)\n\t{\n\t\tif (gotWater)\n\t\t{\n\t\t\t// Intersecting both heightmap and water plane; choose the closest of those\n\t\t\tif ((origin - terrainPoint).LengthSquared() < (origin - waterPoint).LengthSquared())\n\t\t\t\treturn terrainPoint;\n\t\t\telse\n\t\t\t\treturn waterPoint;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Intersecting heightmap but parallel to water plane\n\t\t\treturn terrainPoint;\n\t\t}\n\t}\n\telse\n\t{\n\t\tif (gotWater)\n\t\t{\n\t\t\t// Only intersecting water plane\n\t\t\treturn waterPoint;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Not intersecting terrain or water; just return 0,0,0.\n\t\t\treturn CVector3D(0.f, 0.f, 0.f);\n\t\t}\n\t}\n}\n\nvoid CCamera::LookAt(const CVector3D& camera, const CVector3D& target, const CVector3D& up)\n{\n\tCVector3D delta = target - camera;\n\tLookAlong(camera, delta, up);\n}\n\nvoid CCamera::LookAlong(CVector3D camera, CVector3D orientation, CVector3D up)\n{\n\torientation.Normalize();\n\tup.Normalize();\n\tCVector3D s = orientation.Cross(up);\n\n\tm_Orientation._11 = -s.X;\tm_Orientation._12 = up.X;\tm_Orientation._13 = orientation.X;\tm_Orientation._14 = camera.X;\n\tm_Orientation._21 = -s.Y;\tm_Orientation._22 = up.Y;\tm_Orientation._23 = orientation.Y;\tm_Orientation._24 = camera.Y;\n\tm_Orientation._31 = -s.Z;\tm_Orientation._32 = up.Z;\tm_Orientation._33 = orientation.Z;\tm_Orientation._34 = camera.Z;\n\tm_Orientation._41 = 0.0f;\tm_Orientation._42 = 0.0f;\tm_Orientation._43 = 0.0f;\t\t\tm_Orientation._44 = 1.0f;\n}\n\n\n///////////////////////////////////////////////////////////////////////////////////\n// Render the camera's frustum\nvoid CCamera::Render(int intermediates) const\n{\n#if CONFIG2_GLES\n\t#warning TODO : implement camera frustum for GLES\n#else\n\tCVector3D nearPoints[4];\n\tCVector3D farPoints[4];\n\n\tGetCameraPlanePoints(m_NearPlane, nearPoints);\n\tGetCameraPlanePoints(m_FarPlane, farPoints);\n\tfor (int i = 0; i < 4; i++)\n\t{\n\t\tnearPoints[i] = m_Orientation.Transform(nearPoints[i]);\n\t\tfarPoints[i] = m_Orientation.Transform(farPoints[i]);\n\t}\n\n\t// near plane\n\tglBegin(GL_POLYGON);\n\tglVertex3fv(&nearPoints[0].X);\n\tglVertex3fv(&nearPoints[1].X);\n\tglVertex3fv(&nearPoints[2].X);\n\tglVertex3fv(&nearPoints[3].X);\n\tglEnd();\n\n\t// far plane\n\tglBegin(GL_POLYGON);\n\tglVertex3fv(&farPoints[0].X);\n\tglVertex3fv(&farPoints[1].X);\n\tglVertex3fv(&farPoints[2].X);\n\tglVertex3fv(&farPoints[3].X);\n\tglEnd();\n\n\t// connection lines\n\tglBegin(GL_QUAD_STRIP);\n\tglVertex3fv(&nearPoints[0].X);\n\tglVertex3fv(&farPoints[0].X);\n\tglVertex3fv(&nearPoints[1].X);\n\tglVertex3fv(&farPoints[1].X);\n\tglVertex3fv(&nearPoints[2].X);\n\tglVertex3fv(&farPoints[2].X);\n\tglVertex3fv(&nearPoints[3].X);\n\tglVertex3fv(&farPoints[3].X);\n\tglVertex3fv(&nearPoints[0].X);\n\tglVertex3fv(&farPoints[0].X);\n\tglEnd();\n\n\t// intermediate planes\n\tCVector3D intermediatePoints[4];\n\tfor (int i = 0; i < intermediates; ++i)\n\t{\n\t\tfloat t = (i + 1.0) / (intermediates + 1.0);\n\n\t\tfor (int j = 0; j < 4; ++j)\n\t\t\tintermediatePoints[j] = nearPoints[j] * t + farPoints[j] * (1.0 - t);\n\n\t\tglBegin(GL_POLYGON);\n\t\tglVertex3fv(&intermediatePoints[0].X);\n\t\tglVertex3fv(&intermediatePoints[1].X);\n\t\tglVertex3fv(&intermediatePoints[2].X);\n\t\tglVertex3fv(&intermediatePoints[3].X);\n\t\tglEnd();\n\t}\n#endif\n}\n"
  },
  {
    "path": "fpsgame/graphics/Camera.h",
    "content": "/* Copyright (C) 2013 Wildfire Games.\n* This file is part of 0 A.D.\n*\n* 0 A.D. is free software: you can redistribute it and/or modify\n* it under the terms of the GNU General Public License as published by\n* the Free Software Foundation, either version 2 of the License, or\n* (at your option) any later version.\n*\n* 0 A.D. is distributed in the hope that it will be useful,\n* but WITHOUT ANY WARRANTY; without even the implied warranty of\n* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n* GNU General Public License for more details.\n*\n* You should have received a copy of the GNU General Public License\n* along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n/*\n* CCamera holds a view and a projection matrix. It also has a frustum\n* which can be used to cull objects for rendering.\n*/\n\n#ifndef INCLUDED_CAMERA\n#define INCLUDED_CAMERA\n\n#include \"Frustum.h\"\n#include \"maths/BoundingBoxAligned.h\"\n#include \"maths/Matrix3D.h\"\n\n// view port\nstruct SViewPort\n{\n\tint m_X;\n\tint m_Y;\n\tint m_Width;\n\tint m_Height;\n};\n\nclass CCamera\n{\npublic:\n\tCCamera();\n\t~CCamera();\n\n\t// Methods for projection\n\tvoid SetProjection(float nearp, float farp, float fov);\n\tvoid SetProjection(const CMatrix3D& matrix) { m_ProjMat = matrix; }\n\tvoid SetProjectionTile(int tiles, int tile_x, int tile_y);\n\tCMatrix3D& GetProjection() { return m_ProjMat; }\n\tconst CMatrix3D& GetProjection() const { return m_ProjMat; }\n\n\tCMatrix3D& GetOrientation() { return m_Orientation; }\n\tconst CMatrix3D& GetOrientation() const { return m_Orientation; }\n\n\tCMatrix3D GetViewProjection() const { return m_ProjMat * m_Orientation.GetInverse(); }\n\n\t// Updates the frustum planes. Should be called\n\t// everytime the view or projection matrices are\n\t// altered.\n\tvoid UpdateFrustum(const CBoundingBoxAligned& scissor = CBoundingBoxAligned(CVector3D(-1.0f, -1.0f, -1.0f), CVector3D(1.0f, 1.0f, 1.0f)));\n\tvoid ClipFrustum(const CPlane& clipPlane);\n\tconst CFrustum& GetFrustum() const { return m_ViewFrustum; }\n\n\tvoid SetViewPort(const SViewPort& viewport);\n\tconst SViewPort& GetViewPort() const { return m_ViewPort; }\n\n\t// getters\n\tfloat GetNearPlane() const { return m_NearPlane; }\n\tfloat GetFarPlane() const { return m_FarPlane; }\n\tfloat GetFOV() const { return m_FOV; }\n\n\t// return four points in camera space at given distance from camera\n\tvoid GetCameraPlanePoints(float dist, CVector3D pts[4]) const;\n\n\t// Build a ray passing through the screen coordinate (px, py) and the camera\n\t/////////////////////////////////////////////////////////////////////////////////////////\n\t// BuildCameraRay: calculate origin and ray direction of a ray through\n\t// the pixel (px,py) on the screen\n\tvoid BuildCameraRay(int px, int py, CVector3D& origin, CVector3D& dir) const;\n\n\t// General helpers that seem to fit here\n\n\t// Get the screen-space coordinates corresponding to a given world-space position \n\tvoid GetScreenCoordinates(const CVector3D& world, float& x, float& y) const;\n\n\t// Get the point on the terrain corresponding to pixel (px,py) (or the mouse coordinates)\n\t// The aboveWater parameter determines whether we want to stop at the water plane or also get underwater points\n\tCVector3D GetWorldCoordinates(int px, int py, bool aboveWater = false) const;\n\t// Get the point on the plane at height h corresponding to pixel (px,py)\n\tCVector3D GetWorldCoordinates(int px, int py, float h) const;\n\t// Get the point on the terrain (or water plane) the camera is pointing towards\n\tCVector3D GetFocus() const;\n\n\t// Build an orientation matrix from camera position, camera focus point, and up-vector\n\tvoid LookAt(const CVector3D& camera, const CVector3D& orientation, const CVector3D& up);\n\n\t// Build an orientation matrix from camera position, camera orientation, and up-vector\n\tvoid LookAlong(CVector3D camera, CVector3D focus, CVector3D up);\n\n\t/**\n\t* Render: Renders the camera's frustum in world space.\n\t* The caller should set the color using glColorXy before calling Render.\n\t*\n\t* @param intermediates determines how many intermediate distance planes should\n\t* be hinted at between the near and far planes\n\t*/\n\tvoid Render(int intermediates = 0) const;\n\npublic:\n\t// This is the orientation matrix. The inverse of this\n\t// is the view matrix\n\tCMatrix3D\t\tm_Orientation;\n\n\t// Should not be tweaked externally if possible\n\tCMatrix3D\t\tm_ProjMat;\n\nprivate:\n\tfloat\t\t\tm_NearPlane;\n\tfloat\t\t\tm_FarPlane;\n\tfloat\t\t\tm_FOV;\n\tSViewPort\t\tm_ViewPort;\n\n\tCFrustum\t\tm_ViewFrustum;\n};\n\n#endif\n"
  },
  {
    "path": "fpsgame/graphics/CinemaManager.cpp",
    "content": "/* Copyright (C) 2016 Wildfire Games.\n* This file is part of 0 A.D.\n*\n* 0 A.D. is free software: you can redistribute it and/or modify\n* it under the terms of the GNU General Public License as published by\n* the Free Software Foundation, either version 2 of the License, or\n* (at your option) any later version.\n*\n* 0 A.D. is distributed in the hope that it will be useful,\n* but WITHOUT ANY WARRANTY; without even the implied warranty of\n* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n* GNU General Public License for more details.\n*\n* You should have received a copy of the GNU General Public License\n* along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#include \"precompiled.h\"\n\n#include <sstream>\n#include <string>\n\n#include \"graphics/Camera.h\"\n#include \"graphics/CinemaManager.h\"\n#include \"graphics/GameView.h\"\n#include \"gui/CGUI.h\"\n#include \"gui/GUIManager.h\"\n#include \"gui/IGUIObject.h\"\n#include \"lib/ogl.h\"\n#include \"maths/MathUtil.h\"\n#include \"maths/Quaternion.h\"\n#include \"maths/Vector3D.h\"\n#include \"maths/Vector4D.h\"\n#include \"ps/CLogger.h\"\n#include \"ps/CStr.h\"\n#include \"ps/Game.h\"\n#include \"ps/GameSetup/Config.h\"\n#include \"ps/Hotkey.h\"\n#include \"simulation2/components/ICmpOverlayRenderer.h\"\n#include \"simulation2/components/ICmpRangeManager.h\"\n#include \"simulation2/components/ICmpSelectable.h\"\n#include \"simulation2/components/ICmpTerritoryManager.h\"\n#include \"simulation2/MessageTypes.h\"\n#include \"simulation2/system/ComponentManager.h\"\n#include \"simulation2/Simulation2.h\"\n#include \"renderer/Renderer.h\"\n\n\nCCinemaManager::CCinemaManager()\n\t: m_DrawPaths(false)\n{\n}\n\nvoid CCinemaManager::AddPath(const CStrW& name, const CCinemaPath& path)\n{\n\tif (m_CinematicSimulationData.m_Paths.find(name) != m_CinematicSimulationData.m_Paths.end())\n\t{\n\t\tLOGWARNING(\"Path with name '%s' already exists\", name.ToUTF8());\n\t\treturn;\n\t}\n\tm_CinematicSimulationData.m_Paths[name] = path;\n}\n\nvoid CCinemaManager::AddPathToQueue(const CStrW& name)\n{\n\tif (!HasPath(name))\n\t{\n\t\tLOGWARNING(\"Path with name '%s' doesn't exist\", name.ToUTF8());\n\t\treturn;\n\t}\n\tm_CinematicSimulationData.m_PathQueue.push_back(m_CinematicSimulationData.m_Paths[name]);\n}\n\nvoid CCinemaManager::ClearQueue()\n{\n\tm_CinematicSimulationData.m_PathQueue.clear();\n}\n\nvoid CCinemaManager::SetAllPaths(const std::map<CStrW, CCinemaPath>& paths)\n{\n\tm_CinematicSimulationData.m_Paths = paths;\n}\n\nbool CCinemaManager::HasPath(const CStrW& name) const\n{\n\treturn m_CinematicSimulationData.m_Paths.find(name) != m_CinematicSimulationData.m_Paths.end();\n}\n\nvoid CCinemaManager::SetEnabled(bool enabled)\n{\n\t// TODO: maybe assert?\n\tif (m_CinematicSimulationData.m_PathQueue.empty() && enabled)\n\t{\n\t\tenabled = false;\n\t\tm_CinematicSimulationData.m_Paused = true;\n\t}\n\n\tif (m_CinematicSimulationData.m_Enabled == enabled)\n\t\treturn;\n\n\t// TODO: Enabling/Disabling does not work if the session GUI page is not the top page.\n\t// This can happen in various situations, for example when the player wins/looses the game\n\t// while the cinematic is running (a message box is the top page in this case). \n\t// It might be better to disable the whole GUI during the cinematic instead of a specific \n\t// GUI object.\n\n\t// sn - session gui object\n\tIGUIObject *sn = g_GUI->FindObjectByName(\"sn\");\n\tCmpPtr<ICmpRangeManager> cmpRangeManager(g_Game->GetSimulation2()->GetSimContext().GetSystemEntity());\n\tCmpPtr<ICmpTerritoryManager> cmpTerritoryManager(g_Game->GetSimulation2()->GetSimContext().GetSystemEntity());\n\n\t// GUI visibility\n\tif (sn)\n\t{\n\t\tif (enabled)\n\t\t\tsn->SetSetting(\"hidden\", L\"true\");\n\t\telse\n\t\t\tsn->SetSetting(\"hidden\", L\"false\");\n\t}\n\n\t// Overlay visibility\n\tg_Renderer.SetOptionBool(CRenderer::Option::OPT_SILHOUETTES, !enabled);\n\tif (cmpRangeManager)\n\t{\n\t\tif (enabled)\n\t\t\tm_CinematicSimulationData.m_MapRevealed = cmpRangeManager->GetLosRevealAll(-1);\n\t\t// TODO: improve m_MapRevealed state and without fade in\n\t\tcmpRangeManager->SetLosRevealAll(-1, enabled);\n\t}\n\tif (cmpTerritoryManager)\n\t\tcmpTerritoryManager->SetVisibility(!enabled);\n\tICmpSelectable::SetOverrideVisibility(!enabled);\n\tICmpOverlayRenderer::SetOverrideVisibility(!enabled);\n\n\tm_CinematicSimulationData.m_Enabled = enabled;\n}\n\nvoid CCinemaManager::Play()\n{\n\tm_CinematicSimulationData.m_Paused = false;\n}\n\nvoid CCinemaManager::Stop()\n{\n\tm_CinematicSimulationData.m_PathQueue.clear();\n}\n\nvoid CCinemaManager::Update(const float deltaRealTime)\n{\n\tif (g_Game->m_Paused != m_CinematicSimulationData.m_Paused)\n\t{\n\t\tm_CinematicSimulationData.m_Paused = g_Game->m_Paused;\n\n\t\t// sn - session gui object\n\t\tIGUIObject *sn = g_GUI->FindObjectByName(\"sn\");\n\n\t\t// GUI visibility\n\t\tif (sn)\n\t\t{\n\t\t\tif (m_CinematicSimulationData.m_Paused)\n\t\t\t\tsn->SetSetting(\"hidden\", L\"false\");\n\t\t\telse\n\t\t\t\tsn->SetSetting(\"hidden\", L\"true\");\n\t\t}\n\t}\n\n\tif (m_CinematicSimulationData.m_PathQueue.empty() || !m_CinematicSimulationData.m_Enabled || m_CinematicSimulationData.m_Paused)\n\t\treturn;\n\n\tif (HotkeyIsPressed(\"leave\"))\n\t{\n\t\t// TODO: implement skip\n\t}\n\n\tm_CinematicSimulationData.m_PathQueue.front().Play(deltaRealTime);\n}\n\nvoid CCinemaManager::Render()\n{\n\tif (GetEnabled())\n\t{\n\t\tDrawBars();\n\t\treturn;\n\t}\n\n\tif (!m_DrawPaths)\n\t\treturn;\n\n\t// draw all paths\n\tfor (const std::pair<CStrW, CCinemaPath>& p : m_CinematicSimulationData.m_Paths)\n\t\tp.second.Draw();\n}\n\nvoid CCinemaManager::DrawBars() const\n{\n\tint height = (float)g_xres / 2.39f;\n\tint shift = (g_yres - height) / 2;\n\tif (shift <= 0)\n\t\treturn;\n\n#if CONFIG2_GLES\n\t#warning TODO : implement bars for GLES\n#else\n\t// Set up transform for GL bars\n\tglMatrixMode(GL_PROJECTION);\n\tglPushMatrix();\n\tglLoadIdentity();\n\tglMatrixMode(GL_MODELVIEW);\n\tglPushMatrix();\n\tglLoadIdentity();\n\tCMatrix3D transform;\n\ttransform.SetOrtho(0.f, (float)g_xres, 0.f, (float)g_yres, -1.f, 1000.f);\n\tglLoadMatrixf(&transform._11);\n\n\tglColor4f(0.0f, 0.0f, 0.0f, 1.0f);\n\n\tglEnable(GL_BLEND);\n\tglDisable(GL_DEPTH_TEST);\n\n\tglBegin(GL_QUADS);\n\tglVertex2i(0, 0);\n\tglVertex2i(g_xres, 0);\n\tglVertex2i(g_xres, shift);\n\tglVertex2i(0, shift);\n\tglEnd();\n\n\tglBegin(GL_QUADS);\n\tglVertex2i(0, g_yres - shift);\n\tglVertex2i(g_xres, g_yres - shift);\n\tglVertex2i(g_xres, g_yres);\n\tglVertex2i(0, g_yres);\n\tglEnd();\n\n\tglDisable(GL_BLEND);\n\tglEnable(GL_DEPTH_TEST);\n\n\t// Restore transform\n\tglMatrixMode(GL_PROJECTION);\n\tglPopMatrix();\n\tglMatrixMode(GL_MODELVIEW);\n\tglPopMatrix();\n#endif\n}\n\nInReaction cinema_manager_handler(const SDL_Event_* ev)\n{\n\t// put any events that must be processed even if inactive here\n\tif (!g_Game || !g_Game->IsGameStarted())\n\t\treturn IN_PASS;\n\n\tCCinemaManager* pCinemaManager = g_Game->GetView()->GetCinema();\n\n\treturn pCinemaManager->HandleEvent(ev);\n}\n\nInReaction CCinemaManager::HandleEvent(const SDL_Event_* ev)\n{\n\tswitch (ev->ev.type)\n\t{\n\tcase SDL_MOUSEBUTTONDOWN:\n\tcase SDL_MOUSEBUTTONUP:\n\t\tif (GetEnabled() && !m_CinematicSimulationData.m_Paused)\n\t\t\treturn IN_HANDLED;\n\tdefault:\n\t\treturn IN_PASS;\n\t}\n}\n\nbool CCinemaManager::GetEnabled() const\n{\n\treturn m_CinematicSimulationData.m_Enabled;\n}\n\nbool CCinemaManager::IsPlaying() const\n{\n\treturn !m_CinematicSimulationData.m_Paused;\n}\n\nconst std::map<CStrW, CCinemaPath>& CCinemaManager::GetAllPaths()\n{\n\treturn m_CinematicSimulationData.m_Paths;\n}\n\nCinematicSimulationData* CCinemaManager::GetCinematicSimulationData()\n{\n\treturn &m_CinematicSimulationData;\n}\n\nbool CCinemaManager::GetPathsDrawing() const\n{\n\treturn m_DrawPaths;\n}\n\nvoid CCinemaManager::SetPathsDrawing(const bool drawPath)\n{\n\tm_DrawPaths = drawPath;\n}\n"
  },
  {
    "path": "fpsgame/graphics/CinemaManager.h",
    "content": "/* Copyright (C) 2015 Wildfire Games.\n* This file is part of 0 A.D.\n*\n* 0 A.D. is free software: you can redistribute it and/or modify\n* it under the terms of the GNU General Public License as published by\n* the Free Software Foundation, either version 2 of the License, or\n* (at your option) any later version.\n*\n* 0 A.D. is distributed in the hope that it will be useful,\n* but WITHOUT ANY WARRANTY; without even the implied warranty of\n* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n* GNU General Public License for more details.\n*\n* You should have received a copy of the GNU General Public License\n* along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n\n#ifndef INCLUDED_CINEMAMANAGER\n#define INCLUDED_CINEMAMANAGER\n\n#include <list>\n#include <map>\n\n#include \"lib/input.h\" // InReaction - can't forward-declare enum\n\n#include \"graphics/CinemaPath.h\"\n#include \"ps/CStr.h\"\n\n/*\ndesc: contains various functions used for cinematic camera paths\nSee also: CinemaHandler.cpp, CinemaPath.cpp\n*/\n\n// Cinematic structure for data accessable from the simulation\nstruct CinematicSimulationData\n{\n\tbool m_Enabled;\n\tbool m_Paused;\n\tstd::map<CStrW, CCinemaPath> m_Paths;\n\tstd::list<CCinemaPath> m_PathQueue;\n\n\t// States before playing\n\tbool m_MapRevealed;\n\n\tfixed m_ElapsedTime, m_TotalTime, m_CurrentPathElapsedTime;\n\n\tCinematicSimulationData()\n\t\t: m_Enabled(false),\n\t\tm_Paused(true),\n\t\tm_MapRevealed(false),\n\t\tm_ElapsedTime(fixed::Zero()),\n\t\tm_TotalTime(fixed::Zero()),\n\t\tm_CurrentPathElapsedTime(fixed::Zero())\n\t{}\n};\n\n/**\n* Class for in game playing of cinematics. Should only be instantiated in CGameView.\n*/\n\nclass CCinemaManager\n{\npublic:\n\tCCinemaManager();\n\t~CCinemaManager() {}\n\n\t/**\n\t* Adds the path to the path list\n\t* @param name path name\n\t* @param CCinemaPath path data\n\t*/\n\tvoid AddPath(const CStrW& name, const CCinemaPath& path);\n\n\t/**\n\t* Adds the path to the playlist\n\t* @param name path name\n\t*/\n\tvoid AddPathToQueue(const CStrW& name);\n\n\t/**\n\t* Clears the playlist\n\t*/\n\tvoid ClearQueue();\n\n\t/**\n\t* Checks the path name in the path list\n\t* @param name path name\n\t* @return true if path with that name exists, else false\n\t*/\n\tbool HasPath(const CStrW& name) const;\n\n\tconst std::map<CStrW, CCinemaPath>& GetAllPaths();\n\tvoid SetAllPaths(const std::map<CStrW, CCinemaPath>& tracks);\n\n\t/**\n\t* Starts play paths\n\t*/\n\tvoid Play();\n\tvoid Stop();\n\tbool IsPlaying() const;\n\n\t/**\n\t* Renders black bars and paths (if enabled)\n\t*/\n\tvoid Render();\n\tvoid DrawBars() const;\n\n\t/**\n\t* Get current enable state of the cinema manager\n\t*/\n\tbool GetEnabled() const;\n\n\t/**\n\t* Sets enable state of the cinema manager (shows/hide gui, show/hide rings, etc)\n\t* @enable new state\n\t*/\n\tvoid SetEnabled(bool enabled);\n\n\t/**\n\t* Updates CCinemManager and current path\n\t* @param deltaRealTime Elapsed real time since the last frame.\n\t*/\n\tvoid Update(const float deltaRealTime);\n\n\tInReaction HandleEvent(const SDL_Event_* ev);\n\n\tCinematicSimulationData* GetCinematicSimulationData();\n\n\tbool GetPathsDrawing() const;\n\tvoid SetPathsDrawing(const bool drawPath);\n\nprivate:\n\tbool m_DrawPaths;\n\n\t// Cinematic data is accessed from the simulation\n\tCinematicSimulationData m_CinematicSimulationData;\n};\n\nextern InReaction cinema_manager_handler(const SDL_Event_* ev);\n\n#endif\n"
  },
  {
    "path": "fpsgame/graphics/CinimaPath.cpp",
    "content": "/* Copyright (C) 2016 Wildfire Games.\n* This file is part of 0 A.D.\n*\n* 0 A.D. is free software: you can redistribute it and/or modify\n* it under the terms of the GNU General Public License as published by\n* the Free Software Foundation, either version 2 of the License, or\n* (at your option) any later version.\n*\n* 0 A.D. is distributed in the hope that it will be useful,\n* but WITHOUT ANY WARRANTY; without even the implied warranty of\n* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n* GNU General Public License for more details.\n*\n* You should have received a copy of the GNU General Public License\n* along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#include \"precompiled.h\"\n\n#include \"CinemaPath.h\"\n\n#include <sstream>\n#include <string>\n\n#include \"Camera.h\"\n#include \"CinemaManager.h\"\n#include \"GameView.h\"\n#include \"gui/CGUI.h\"\n#include \"gui/GUIManager.h\"\n#include \"gui/IGUIObject.h\"\n#include \"lib/ogl.h\"\n#include \"maths/MathUtil.h\"\n#include \"maths/Quaternion.h\"\n#include \"maths/Vector3D.h\"\n#include \"maths/Vector4D.h\"\n#include \"ps/CLogger.h\"\n#include \"ps/CStr.h\"\n#include \"ps/Game.h\"\n#include \"renderer/Renderer.h\"\n\n\nCCinemaPath::CCinemaPath(const CCinemaData& data, const TNSpline& spline, const TNSpline& targetSpline)\n\t: CCinemaData(data), TNSpline(spline), m_TargetSpline(targetSpline), m_TimeElapsed(0.f)\n{\n\tm_TimeElapsed = 0;\n\n\t// Calculate curves by nodes\n\tBuildSpline();\n\tm_TargetSpline.BuildSpline();\n\n\tif (m_Orientation == L\"target\")\n\t{\n\t\tm_LookAtTarget = true;\n\t\tENSURE(!m_TargetSpline.GetAllNodes().empty());\n\t}\n\n\t// Set distortion mode and style\n\tif (data.m_Mode == L\"ease_in\")\n\t\tDistModePtr = &CCinemaPath::EaseIn;\n\telse if (data.m_Mode == L\"ease_out\")\n\t\tDistModePtr = &CCinemaPath::EaseOut;\n\telse if (data.m_Mode == L\"ease_inout\")\n\t\tDistModePtr = &CCinemaPath::EaseInOut;\n\telse if (data.m_Mode == L\"ease_outin\")\n\t\tDistModePtr = &CCinemaPath::EaseOutIn;\n\telse\n\t{\n\t\tLOGWARNING(\"Cinematic mode not found for '%s'\", data.m_Mode.ToUTF8().c_str());\n\t\tDistModePtr = &CCinemaPath::EaseInOut;\n\t}\n\n\tif (data.m_Style == L\"default\")\n\t\tDistStylePtr = &CCinemaPath::EaseDefault;\n\telse if (data.m_Style == L\"growth\")\n\t\tDistStylePtr = &CCinemaPath::EaseGrowth;\n\telse if (data.m_Style == L\"expo\")\n\t\tDistStylePtr = &CCinemaPath::EaseExpo;\n\telse if (data.m_Style == L\"circle\")\n\t\tDistStylePtr = &CCinemaPath::EaseCircle;\n\telse if (data.m_Style == L\"sine\")\n\t\tDistStylePtr = &CCinemaPath::EaseSine;\n\telse\n\t{\n\t\tLOGWARNING(\"Cinematic style not found for '%s'\", data.m_Style.ToUTF8().c_str());\n\t\tDistStylePtr = &CCinemaPath::EaseDefault;\n\t}\n}\n\nvoid CCinemaPath::Draw() const\n{\n\tDrawSpline(*this, CVector4D(0.2f, 0.2f, 1.f, 0.5f), 100, true);\n\tDrawNodes(*this, CVector4D(0.5f, 1.0f, 0.f, 0.5f));\n\n\tif (!m_LookAtTarget)\n\t\treturn;\n\n\tDrawSpline(m_TargetSpline, CVector4D(1.0f, 0.2f, 0.2f, 0.5f), 100, true);\n\tDrawNodes(m_TargetSpline, CVector4D(1.0f, 0.5f, 0.f, 0.5f));\n}\n\nvoid CCinemaPath::DrawSpline(const RNSpline& spline, const CVector4D& RGBA, int smoothness, bool lines) const\n{\n\tif (spline.NodeCount < 2 || DistModePtr == NULL)\n\t\treturn;\n\tif (spline.NodeCount == 2 && lines)\n\t\tsmoothness = 2;\n\n\tfloat start = spline.MaxDistance.ToFloat() / smoothness;\n\tfloat time = 0;\n\n#if CONFIG2_GLES\n\t#warning TODO : do something about CCinemaPath on GLES\n#else\n\n\tglColor4f(RGBA.X, RGBA.Y, RGBA.Z, RGBA.W);\n\tif (lines)\n\t{\n\t\tglLineWidth(1.8f);\n\t\tglEnable(GL_LINE_SMOOTH);\n\t\tglBegin(GL_LINE_STRIP);\n\n\t\tfor (int i = 0; i <= smoothness; ++i)\n\t\t{\n\t\t\t// Find distorted time\n\t\t\ttime = start*i / spline.MaxDistance.ToFloat();\n\t\t\tCVector3D tmp = spline.GetPosition(time);\n\t\t\tglVertex3f(tmp.X, tmp.Y, tmp.Z);\n\t\t}\n\t\tglEnd();\n\t\tglDisable(GL_LINE_SMOOTH);\n\t\tglLineWidth(1.0f);\n\t}\n\telse\n\t{\n\t\tsmoothness /= 2;\n\t\tstart = spline.MaxDistance.ToFloat() / smoothness;\n\t\tglEnable(GL_POINT_SMOOTH);\n\t\tglPointSize(3.0f);\n\t\tglBegin(GL_POINTS);\n\n\t\tfor (int i = 0; i <= smoothness; ++i)\n\t\t{\n\t\t\t// Find distorted time\n\t\t\ttime = (this->*DistModePtr)(start*i / spline.MaxDistance.ToFloat());\n\t\t\tCVector3D tmp = spline.GetPosition(time);\n\t\t\tglVertex3f(tmp.X, tmp.Y, tmp.Z);\n\t\t}\n\t\tglColor3f(1.0f, 1.0f, 0.0f); // yellow\n\n\t\tfor (size_t i = 0; i < spline.GetAllNodes().size(); ++i)\n\t\t\tglVertex3f(\n\t\t\t\tspline.GetAllNodes()[i].Position.X.ToFloat(),\n\t\t\t\tspline.GetAllNodes()[i].Position.Y.ToFloat(),\n\t\t\t\tspline.GetAllNodes()[i].Position.Z.ToFloat()\n\t\t\t);\n\n\t\tglEnd();\n\t\tglPointSize(1.0f);\n\t\tglDisable(GL_POINT_SMOOTH);\n\t}\n\n#endif\n}\n\nvoid CCinemaPath::DrawNodes(const RNSpline& spline, const CVector4D& RGBA) const\n{\n#if CONFIG2_GLES\n\t#warning TODO : do something about CCinemaPath on GLES\n#else\n\tglEnable(GL_POINT_SMOOTH);\n\tglPointSize(5.0f);\n\n\tglColor4f(RGBA.X, RGBA.Y, RGBA.Z, RGBA.W);\n\tglBegin(GL_POINTS);\n\tconst std::vector<SplineData>& nodes = spline.GetAllNodes();\n\tfor (size_t i = 0; i < nodes.size(); ++i)\n\t\tglVertex3f(nodes[i].Position.X.ToFloat(), nodes[i].Position.Y.ToFloat(), nodes[i].Position.Z.ToFloat());\n\tglEnd();\n\n\tglPointSize(1.0f);\n\tglDisable(GL_POINT_SMOOTH);\n#endif\n}\n\nCVector3D CCinemaPath::GetNodePosition(const int index) const\n{\n\treturn Node[index].Position;\n}\n\nfixed CCinemaPath::GetNodeDuration(const int index) const\n{\n\treturn Node[index].Distance;\n}\n\nfixed CCinemaPath::GetDuration() const\n{\n\treturn MaxDistance;\n}\n\nfloat CCinemaPath::GetNodeFraction() const\n{\n\treturn (m_TimeElapsed - m_PreviousNodeTime) / Node[m_CurrentNode].Distance.ToFloat();\n}\n\nfloat CCinemaPath::GetElapsedTime() const\n{\n\treturn m_TimeElapsed;\n}\n\nCStrW CCinemaPath::GetName() const\n{\n\treturn m_Name;\n}\n\nvoid CCinemaPath::SetTimescale(fixed scale)\n{\n\tm_Timescale = scale;\n}\n\nvoid CCinemaPath::MoveToPointAt(float t, float nodet, const CVector3D& startRotation)\n{\n\tCCamera *camera = g_Game->GetView()->GetCamera();\n\tt = (this->*DistModePtr)(t);\n\n\tCVector3D pos = GetPosition(t);\n\n\tif (m_LookAtTarget)\n\t{\n\t\tif (m_TimeElapsed <= m_TargetSpline.MaxDistance.ToFloat())\n\t\t\tcamera->LookAt(pos, m_TargetSpline.GetPosition(m_TimeElapsed / m_TargetSpline.MaxDistance.ToFloat()), CVector3D(0, 1, 0));\n\t\telse\n\t\t\tcamera->LookAt(pos, m_TargetSpline.GetAllNodes().back().Position, CVector3D(0, 1, 0));\n\t}\n\telse\n\t{\n\t\tCVector3D nodeRotation = Node[m_CurrentNode + 1].Rotation;\n\t\tCQuaternion start, end;\n\t\tstart.FromEulerAngles(DEGTORAD(startRotation.X), DEGTORAD(startRotation.Y), DEGTORAD(startRotation.Z));\n\t\tend.FromEulerAngles(DEGTORAD(nodeRotation.X), DEGTORAD(nodeRotation.Y), DEGTORAD(nodeRotation.Z));\n\t\tstart.Slerp(start, end, nodet);\n\n\t\tcamera->m_Orientation.SetIdentity();\n\t\tcamera->m_Orientation.Rotate(start);\n\t\tcamera->m_Orientation.Translate(pos);\n\t}\n\tcamera->UpdateFrustum();\n}\n\n// Distortion mode functions\nfloat CCinemaPath::EaseIn(float t) const\n{\n\treturn (this->*DistStylePtr)(t);\n}\n\nfloat CCinemaPath::EaseOut(float t) const\n{\n\treturn 1.0f - EaseIn(1.0f - t);\n}\n\nfloat CCinemaPath::EaseInOut(float t) const\n{\n\tif (t < m_Switch)\n\t\treturn EaseIn(1.0f / m_Switch * t) * m_Switch;\n\treturn EaseOut(1.0f / m_Switch * (t - m_Switch)) * m_Switch + m_Switch;\n}\n\nfloat CCinemaPath::EaseOutIn(float t) const\n{\n\tif (t < m_Switch)\n\t\treturn EaseOut(1.0f / m_Switch * t) * m_Switch;\n\treturn EaseIn(1.0f / m_Switch * (t - m_Switch)) * m_Switch + m_Switch;\n}\n\n// Distortion style functions\nfloat CCinemaPath::EaseDefault(float t) const\n{\n\treturn t;\n}\n\nfloat CCinemaPath::EaseGrowth(float t) const\n{\n\treturn pow(t, m_Growth);\n}\n\nfloat CCinemaPath::EaseExpo(float t) const\n{\n\tif (t == 0)\n\t\treturn t;\n\treturn powf(m_Growth, 10 * (t - 1.0f));\n}\n\nfloat CCinemaPath::EaseCircle(float t) const\n{\n\tt = -(sqrt(1.0f - t*t) - 1.0f);\n\tif (m_GrowthCount > 1.0f)\n\t{\n\t\t--m_GrowthCount;\n\t\treturn (this->*DistStylePtr)(t);\n\t}\n\treturn t;\n}\n\nfloat CCinemaPath::EaseSine(float t) const\n{\n\tt = 1.0f - cos(t * (float)M_PI / 2);\n\tif (m_GrowthCount > 1.0f)\n\t{\n\t\t--m_GrowthCount;\n\t\treturn (this->*DistStylePtr)(t);\n\t}\n\treturn t;\n}\n\nconst CCinemaData* CCinemaPath::GetData() const\n{\n\treturn CCinemaData::GetData();\n}\n\nbool CCinemaPath::Validate()\n{\n\tif (m_TimeElapsed > GetDuration().ToFloat() || m_TimeElapsed < 0.0f)\n\t\treturn false;\n\n\t// Find current node and past \"node time\"\n\tfloat previousTime = 0.0f, cumulation = 0.0f;\n\n\t// Ignore the last node, since it is a blank (node time values are shifted down one from interface)\n\tfor (size_t i = 0; i < Node.size() - 1; ++i)\n\t{\n\t\tcumulation += Node[i].Distance.ToFloat();\n\t\tif (m_TimeElapsed <= cumulation)\n\t\t{\n\t\t\tm_PreviousNodeTime = previousTime;\n\t\t\tm_PreviousRotation = Node[i].Rotation;\n\t\t\tm_CurrentNode = i; // We're moving toward this next node, so use its rotation\n\t\t\treturn true;\n\t\t}\n\t\tpreviousTime += Node[i].Distance.ToFloat();\n\t}\n\tdebug_warn(\"validation of cinema path is wrong\\n\");\n\treturn false;\n}\n\nbool CCinemaPath::Play(const float deltaRealTime)\n{\n\tm_TimeElapsed += m_Timescale.ToFloat() * deltaRealTime;\n\tif (!Validate())\n\t\treturn false;\n\n\tMoveToPointAt(m_TimeElapsed / GetDuration().ToFloat(), GetNodeFraction(), m_PreviousRotation);\n\treturn true;\n}\n\nbool CCinemaPath::Empty() const\n{\n\treturn Node.empty();\n}\n\nvoid CCinemaPath::Reset()\n{\n\tm_TimeElapsed = 0.0f;\n}\n\nfixed CCinemaPath::GetTimescale() const\n{\n\treturn m_Timescale;\n}\n\nconst TNSpline& CCinemaPath::GetTargetSpline() const\n{\n\treturn m_TargetSpline;\n}\n"
  },
  {
    "path": "fpsgame/graphics/CinimaPath.h",
    "content": "#pragma once\r\n/* Copyright (C) 2016 Wildfire Games.\n* This file is part of 0 A.D.\n*\n* 0 A.D. is free software: you can redistribute it and/or modify\n* it under the terms of the GNU General Public License as published by\n* the Free Software Foundation, either version 2 of the License, or\n* (at your option) any later version.\n*\n* 0 A.D. is distributed in the hope that it will be useful,\n* but WITHOUT ANY WARRANTY; without even the implied warranty of\n* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n* GNU General Public License for more details.\n*\n* You should have received a copy of the GNU General Public License\n* along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#ifndef INCLUDED_CINEMAPATH\n#define INCLUDED_CINEMAPATH\n\n#include \"maths/NUSpline.h\"\n#include \"ps/CStr.h\"\n\nclass CVector3D;\nclass CVector4D;\nclass CCamera;\n\n// For loading data\nclass CCinemaData\n{\npublic:\n\tCCinemaData() : m_LookAtTarget(false), m_GrowthCount(0), m_Growth(1), m_Switch(1), m_Timescale(fixed::FromInt(1)) {}\n\tvirtual ~CCinemaData() {}\n\n\tconst CCinemaData* GetData() const { return this; }\n\n\tCStrW m_Name;\n\tCStrW m_Orientation;\n\tCStrW m_Mode;\n\tCStrW m_Style;\n\n\tbool m_LookAtTarget;\n\n\tfixed m_Timescale; // a negative timescale results in backwards play\n\n\t\t\t\t\t   // Distortion variables\n\tmutable float m_GrowthCount;\n\tfloat m_Growth;\n\tfloat m_Switch;\n};\n\n\n// Once the data is part of the path, it shouldn't be changeable, so use private inheritance.\n// This class encompasses the spline and the information which determines how the path will operate\n// and also provides the functionality for doing so\n\nclass CCinemaPath : private CCinemaData, public TNSpline\n{\npublic:\n\tCCinemaPath() : m_TimeElapsed(0), m_PreviousNodeTime(0) {}\n\tCCinemaPath(const CCinemaData& data, const TNSpline& spline, const TNSpline& targetSpline);\n\n\t// Sets camera position to calculated point on spline\n\tvoid MoveToPointAt(float t, float nodet, const CVector3D&);\n\n\t// Distortion mode functions-change how ratio is passed to distortion style functions\n\tfloat EaseIn(float t) const;\n\tfloat EaseOut(float t) const;\n\tfloat EaseInOut(float t) const;\n\tfloat EaseOutIn(float t) const;\n\n\t// Distortion style functions\n\tfloat EaseDefault(float t) const;\n\tfloat EaseGrowth(float t) const;\n\tfloat EaseExpo(float t) const;\n\tfloat EaseCircle(float t) const;\n\tfloat EaseSine(float t) const;\n\n\tfloat (CCinemaPath::*DistStylePtr)(float ratio) const;\n\tfloat (CCinemaPath::*DistModePtr)(float ratio) const;\n\n\tconst CCinemaData* GetData() const;\n\npublic:\n\n\tvoid Draw() const;\n\tvoid DrawSpline(const RNSpline& spline, const CVector4D& RGBA, int smoothness, bool lines) const;\n\tvoid DrawNodes(const RNSpline& spline, const CVector4D& RGBA) const;\n\n\tCVector3D GetNodePosition(const int index) const;\n\tfixed GetNodeDuration(const int index) const;\n\tfixed GetDuration() const;\n\n\tfloat GetNodeFraction() const;\n\tfloat GetElapsedTime() const;\n\n\tCStrW GetName() const;\n\n\tvoid SetTimescale(fixed scale);\n\n\tfloat m_TimeElapsed;\n\tfloat m_PreviousNodeTime; // How much time has passed before the current node\n\n\tsize_t m_CurrentNode;\n\tCVector3D m_PreviousRotation;\n\npublic:\n\n\t/**\n\t* Returns false if finished.\n\t* @param deltaRealTime Elapsed real time since the last frame.\n\t*/\n\tbool Play(const float deltaRealTime);\n\n\t/**\n\t* Validate the path\n\t* @return true if the path is valid\n\t*/\n\tbool Validate();\n\n\t/**\n\t* Returns true if path doesn't contain nodes\n\t*/\n\tbool Empty() const;\n\n\t/**\n\t* Resets the path state\n\t*/\n\tvoid Reset();\n\n\tfixed GetTimescale() const;\n\n\tconst TNSpline& GetTargetSpline() const;\n\nprivate:\n\n\tTNSpline m_TargetSpline;\n};\n\n#endif // INCLUDED_CINEMAPATH\n"
  },
  {
    "path": "fpsgame/graphics/ColladaManager.cpp",
    "content": "/* Copyright (C) 2015 Wildfire Games.\n* This file is part of 0 A.D.\n*\n* 0 A.D. is free software: you can redistribute it and/or modify\n* it under the terms of the GNU General Public License as published by\n* the Free Software Foundation, either version 2 of the License, or\n* (at your option) any later version.\n*\n* 0 A.D. is distributed in the hope that it will be useful,\n* but WITHOUT ANY WARRANTY; without even the implied warranty of\n* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n* GNU General Public License for more details.\n*\n* You should have received a copy of the GNU General Public License\n* along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#include \"precompiled.h\"\n\n#include \"ColladaManager.h\"\n\n#include <boost/algorithm/string.hpp>\n\n#include \"graphics/ModelDef.h\"\n#include \"lib/fnv_hash.h\"\n#include \"maths/MD5.h\"\n#include \"ps/CacheLoader.h\"\n#include \"ps/CLogger.h\"\n#include \"ps/CStr.h\"\n#include \"ps/DllLoader.h\"\n#include \"ps/Filesystem.h\"\n\nnamespace Collada\n{\n#include \"collada/DLL.h\"\n}\n\nnamespace\n{\n\tvoid ColladaLog(void* cb_data, int severity, const char* text)\n\t{\n\t\tVfsPath* path = static_cast<VfsPath*>(cb_data);\n\n\t\tif (severity == LOG_INFO)\n\t\t\tLOGMESSAGE(\"%s: %s\", path->string8(), text);\n\t\telse if (severity == LOG_WARNING)\n\t\t\tLOGWARNING(\"%s: %s\", path->string8(), text);\n\t\telse\n\t\t\tLOGERROR(\"%s: %s\", path->string8(), text);\n\t}\n\n\tvoid ColladaOutput(void* cb_data, const char* data, unsigned int length)\n\t{\n\t\tWriteBuffer* writeBuffer = static_cast<WriteBuffer*>(cb_data);\n\t\twriteBuffer->Append(data, (size_t)length);\n\t}\n}\n\nclass CColladaManagerImpl\n{\n\tDllLoader dll;\n\n\tvoid(*set_logger)(Collada::LogFn logger, void* cb_data);\n\tint(*set_skeleton_definitions)(const char* xml, int length);\n\tint(*convert_dae_to_pmd)(const char* dae, Collada::OutputFn pmd_writer, void* cb_data);\n\tint(*convert_dae_to_psa)(const char* dae, Collada::OutputFn psa_writer, void* cb_data);\n\npublic:\n\tCColladaManagerImpl(const PIVFS& vfs)\n\t\t: dll(\"Collada\"), m_VFS(vfs), m_skeletonHashInvalidated(true)\n\t{\n\t\t// Support hotloading\n\t\tRegisterFileReloadFunc(ReloadChangedFileCB, this);\n\t}\n\n\t~CColladaManagerImpl()\n\t{\n\t\tif (dll.IsLoaded())\n\t\t\tset_logger(NULL, NULL); // unregister the log handler\n\t\tUnregisterFileReloadFunc(ReloadChangedFileCB, this);\n\t}\n\n\tStatus ReloadChangedFile(const VfsPath& path)\n\t{\n\t\t// Ignore files that aren't in the right path\n\t\tif (!boost::algorithm::starts_with(path.string(), L\"art/skeletons/\"))\n\t\t\treturn INFO::OK;\n\n\t\tif (path.Extension() != L\".xml\")\n\t\t\treturn INFO::OK;\n\n\t\tm_skeletonHashInvalidated = true;\n\n\t\t// If the file doesn't exist (e.g. it was deleted), don't bother reloading\n\t\t// or 'unloading' since that isn't possible\n\t\tif (!VfsFileExists(path))\n\t\t\treturn INFO::OK;\n\n\t\tif (!dll.IsLoaded() && !TryLoadDLL())\n\t\t\treturn ERR::FAIL;\n\n\t\tLOGMESSAGE(\"Hotloading skeleton definitions from '%s'\", path.string8());\n\t\t// Set the filename for the logger to report\n\t\tset_logger(ColladaLog, const_cast<void*>(static_cast<const void*>(&path)));\n\n\t\tCVFSFile skeletonFile;\n\t\tif (skeletonFile.Load(m_VFS, path) != PSRETURN_OK)\n\t\t{\n\t\t\tLOGERROR(\"Failed to read skeleton defintions from '%s'\", path.string8());\n\t\t\treturn ERR::FAIL;\n\t\t}\n\n\t\tint ok = set_skeleton_definitions((const char*)skeletonFile.GetBuffer(), (int)skeletonFile.GetBufferSize());\n\t\tif (ok < 0)\n\t\t{\n\t\t\tLOGERROR(\"Failed to load skeleton definitions from '%s'\", path.string8());\n\t\t\treturn ERR::FAIL;\n\t\t}\n\n\t\treturn INFO::OK;\n\t}\n\n\tstatic Status ReloadChangedFileCB(void* param, const VfsPath& path)\n\t{\n\t\treturn static_cast<CColladaManagerImpl*>(param)->ReloadChangedFile(path);\n\t}\n\n\tbool Convert(const VfsPath& daeFilename, const VfsPath& pmdFilename, CColladaManager::FileType type)\n\t{\n\t\t// To avoid always loading the DLL when it's usually not going to be\n\t\t// used (and to do the same on Linux where delay-loading won't help),\n\t\t// and to avoid compile-time dependencies (because it's a minor pain\n\t\t// to get all the right libraries to build the COLLADA DLL), we load\n\t\t// it dynamically when it is required, instead of using the exported\n\t\t// functions and binding at link-time.\n\t\tif (!dll.IsLoaded())\n\t\t{\n\t\t\tif (!TryLoadDLL())\n\t\t\t\treturn false;\n\n\t\t\tif (!LoadSkeletonDefinitions())\n\t\t\t{\n\t\t\t\tdll.Unload(); // Error should have been logged already\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\t// Set the filename for the logger to report\n\t\tset_logger(ColladaLog, const_cast<void*>(static_cast<const void*>(&daeFilename)));\n\n\t\t// We need to null-terminate the buffer, so do it (possibly inefficiently)\n\t\t// by converting to a CStr\n\t\tCStr daeData;\n\t\t{\n\t\t\tCVFSFile daeFile;\n\t\t\tif (daeFile.Load(m_VFS, daeFilename) != PSRETURN_OK)\n\t\t\t\treturn false;\n\t\t\tdaeData = daeFile.GetAsString();\n\t\t}\n\n\t\t// Do the conversion into a memory buffer\n\t\t// We need to check the result, as archive builder needs to know if the source dae\n\t\t//\twas sucessfully converted to .pmd/psa\n\t\tint result = -1;\n\t\tWriteBuffer writeBuffer;\n\t\tswitch (type)\n\t\t{\n\t\tcase CColladaManager::PMD:\n\t\t\tresult = convert_dae_to_pmd(daeData.c_str(), ColladaOutput, &writeBuffer);\n\t\t\tbreak;\n\t\tcase CColladaManager::PSA:\n\t\t\tresult = convert_dae_to_psa(daeData.c_str(), ColladaOutput, &writeBuffer);\n\t\t\tbreak;\n\t\t}\n\n\t\t// don't create zero-length files (as happens in test_invalid_dae when\n\t\t// we deliberately pass invalid XML data) because the VFS caching\n\t\t// logic warns when asked to load such.\n\t\tif (writeBuffer.Size())\n\t\t{\n\t\t\tStatus ret = m_VFS->CreateFile(pmdFilename, writeBuffer.Data(), writeBuffer.Size());\n\t\t\tENSURE(ret == INFO::OK);\n\t\t}\n\n\t\treturn (result == 0);\n\t}\n\n\tbool TryLoadDLL()\n\t{\n\t\tif (!dll.LoadDLL())\n\t\t{\n\t\t\tLOGERROR(\"Failed to load COLLADA conversion DLL\");\n\t\t\treturn false;\n\t\t}\n\n\t\ttry\n\t\t{\n\t\t\tdll.LoadSymbol(\"set_logger\", set_logger);\n\t\t\tdll.LoadSymbol(\"set_skeleton_definitions\", set_skeleton_definitions);\n\t\t\tdll.LoadSymbol(\"convert_dae_to_pmd\", convert_dae_to_pmd);\n\t\t\tdll.LoadSymbol(\"convert_dae_to_psa\", convert_dae_to_psa);\n\t\t}\n\t\tcatch (PSERROR_DllLoader&)\n\t\t{\n\t\t\tLOGERROR(\"Failed to load symbols from COLLADA conversion DLL\");\n\t\t\tdll.Unload();\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\n\tbool LoadSkeletonDefinitions()\n\t{\n\t\tVfsPaths pathnames;\n\t\tif (vfs::GetPathnames(m_VFS, L\"art/skeletons/\", L\"*.xml\", pathnames) < 0)\n\t\t{\n\t\t\tLOGERROR(\"No skeleton definition files present\");\n\t\t\treturn false;\n\t\t}\n\n\t\tbool loaded = false;\n\t\tfor (const VfsPath& path : pathnames)\n\t\t{\n\t\t\tLOGMESSAGE(\"Loading skeleton definitions from '%s'\", path.string8());\n\t\t\t// Set the filename for the logger to report\n\t\t\tset_logger(ColladaLog, const_cast<void*>(static_cast<const void*>(&path)));\n\n\t\t\tCVFSFile skeletonFile;\n\t\t\tif (skeletonFile.Load(m_VFS, path) != PSRETURN_OK)\n\t\t\t{\n\t\t\t\tLOGERROR(\"Failed to read skeleton defintions from '%s'\", path.string8());\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tint ok = set_skeleton_definitions((const char*)skeletonFile.GetBuffer(), (int)skeletonFile.GetBufferSize());\n\t\t\tif (ok < 0)\n\t\t\t{\n\t\t\t\tLOGERROR(\"Failed to load skeleton definitions from '%s'\", path.string8());\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tloaded = true;\n\t\t}\n\n\t\tif (!loaded)\n\t\t\tLOGERROR(\"Failed to load any skeleton definitions\");\n\n\t\treturn loaded;\n\t}\n\n\t/**\n\t* Creates MD5 hash key from skeletons.xml info and COLLADA converter version,\n\t* used to invalidate cached .pmd/psas\n\t*\n\t* @param[out] hash resulting MD5 hash\n\t* @param[out] version version passed to CCacheLoader, used if code change should force\n\t*\t\t  cache invalidation\n\t*/\n\tvoid PrepareCacheKey(MD5& hash, u32& version)\n\t{\n\t\t// Add converter version to the hash\n\t\tversion = COLLADA_CONVERTER_VERSION;\n\n\t\t// Cache the skeleton files hash data\n\t\tif (m_skeletonHashInvalidated)\n\t\t{\n\t\t\tVfsPaths paths;\n\t\t\tif (vfs::GetPathnames(m_VFS, L\"art/skeletons/\", L\"*.xml\", paths) != INFO::OK)\n\t\t\t{\n\t\t\t\tLOGWARNING(\"Failed to load skeleton definitions\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Sort the paths to not invalidate the cache if mods are mounted in different order\n\t\t\t// (No need to stable_sort as the VFS gurantees that we have no duplicates)\n\t\t\tstd::sort(paths.begin(), paths.end());\n\n\t\t\t// We need two u64s per file\n\t\t\tm_skeletonHashes.clear();\n\t\t\tm_skeletonHashes.reserve(paths.size() * 2);\n\n\t\t\tCFileInfo fileInfo;\n\t\t\tfor (const VfsPath& path : paths)\n\t\t\t{\n\t\t\t\t// This will cause an assertion failure if *it doesn't exist,\n\t\t\t\t//\tbecause fileinfo is not a NULL pointer, which is annoying but that\n\t\t\t\t//\tshould never happen, unless there really is a problem\n\t\t\t\tif (m_VFS->GetFileInfo(path, &fileInfo) != INFO::OK)\n\t\t\t\t{\n\t\t\t\t\tLOGERROR(\"Failed to stat '%s' for DAE caching\", path.string8());\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tm_skeletonHashes.push_back((u64)fileInfo.MTime() & ~1); //skip lowest bit, since zip and FAT don't preserve it\n\t\t\t\t\tm_skeletonHashes.push_back((u64)fileInfo.Size());\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Check if we were able to load any skeleton files\n\t\t\tif (m_skeletonHashes.empty())\n\t\t\t\tLOGERROR(\"Failed to stat any skeleton definitions for DAE caching\");\n\t\t\t// We can continue, something else will break if we try loading a skeletal model\n\n\t\t\tm_skeletonHashInvalidated = false;\n\t\t}\n\n\t\tfor (const u64& h : m_skeletonHashes)\n\t\t\thash.Update((const u8*)&h, sizeof(h));\n\t}\n\nprivate:\n\tPIVFS m_VFS;\n\tbool m_skeletonHashInvalidated;\n\tstd::vector<u64> m_skeletonHashes;\n};\n\nCColladaManager::CColladaManager(const PIVFS& vfs)\n\t: m(new CColladaManagerImpl(vfs)), m_VFS(vfs)\n{\n}\n\nCColladaManager::~CColladaManager()\n{\n\tdelete m;\n}\n\nVfsPath CColladaManager::GetLoadablePath(const VfsPath& pathnameNoExtension, FileType type)\n{\n\tstd::wstring extn;\n\tswitch (type)\n\t{\n\tcase PMD: extn = L\".pmd\"; break;\n\tcase PSA: extn = L\".psa\"; break;\n\t\t// no other alternatives\n\t}\n\n\t/*\n\n\tAlgorithm:\n\t* Calculate hash of skeletons.xml and converter version.\n\t* Use CCacheLoader to check for archived or loose cached .pmd/psa.\n\t* If cached version exists:\n\t* Return pathname of cached .pmd/psa.\n\t* Else, if source .dae for this model exists:\n\t* Convert it to cached .pmd/psa.\n\t* If converter succeeded:\n\t* Return pathname of cached .pmd/psa.\n\t* Else, fail (return empty path).\n\t* Else, if uncached .pmd/psa exists:\n\t* Return pathname of uncached .pmd/psa.\n\t* Else, fail (return empty path).\n\n\tSince we use CCacheLoader which automatically hashes file size and mtime,\n\tand handles archived files and loose cache, when preparing the cache key\n\twe add converter version number (so updates of the converter cause\n\tregeneration of the .pmd/psa) and the global skeletons.xml file size and\n\tmtime, as modelers frequently change the contents of skeletons.xml and get\n\tperplexed if the in-game models haven't updated as expected (we don't know\n\twhich models were affected by the skeletons.xml change, if any, so we just\n\tregenerate all of them)\n\n\tTODO (maybe): The .dae -> .pmd/psa conversion may fail (e.g. if the .dae is\n\tinvalid or unsupported), but it may take a long time to start the conversion\n\tthen realise it's not going to work. That will delay the loading of the game\n\tevery time, which is annoying, so maybe it should cache the error message\n\tuntil the .dae is updated and fixed. (Alternatively, avoid having that many\n\tbroken .daes in the game.)\n\n\t*/\n\n\t// Now we're looking for cached files\n\tCCacheLoader cacheLoader(m_VFS, extn);\n\tMD5 hash;\n\tu32 version;\n\tm->PrepareCacheKey(hash, version);\n\n\tVfsPath cachePath;\n\tVfsPath sourcePath = pathnameNoExtension.ChangeExtension(L\".dae\");\n\tStatus ret = cacheLoader.TryLoadingCached(sourcePath, hash, version, cachePath);\n\n\tif (ret == INFO::OK)\n\t\t// Found a valid cached version\n\t\treturn cachePath;\n\n\t// No valid cached version, check if we have a source .dae\n\tif (ret != INFO::SKIPPED)\n\t{\n\t\t// No valid cached version was found, and no source .dae exists\n\t\tENSURE(ret < 0);\n\n\t\t// Check if source (uncached) .pmd/psa exists\n\t\tsourcePath = pathnameNoExtension.ChangeExtension(extn);\n\t\tif (m_VFS->GetFileInfo(sourcePath, NULL) != INFO::OK)\n\t\t{\n\t\t\t// Broken reference, the caller will need to handle this\n\t\t\treturn L\"\";\n\t\t}\n\t\telse\n\t\t{\n\t\t\treturn sourcePath;\n\t\t}\n\t}\n\n\t// No valid cached version was found - but source .dae exists\n\t// We'll try converting it\n\n\t// We have a source .dae and invalid cached version, so regenerate cached version\n\tif (!m->Convert(sourcePath, cachePath, type))\n\t{\n\t\t// The COLLADA converter failed for some reason, this will need to be handled\n\t\t//\tby the caller\n\t\treturn L\"\";\n\t}\n\n\treturn cachePath;\n}\n\nbool CColladaManager::GenerateCachedFile(const VfsPath& sourcePath, FileType type, VfsPath& archiveCachePath)\n{\n\tstd::wstring extn;\n\tswitch (type)\n\t{\n\tcase PMD: extn = L\".pmd\"; break;\n\tcase PSA: extn = L\".psa\"; break;\n\t\t// no other alternatives\n\t}\n\n\tCCacheLoader cacheLoader(m_VFS, extn);\n\n\tarchiveCachePath = cacheLoader.ArchiveCachePath(sourcePath);\n\n\treturn m->Convert(sourcePath, VfsPath(\"cache\") / archiveCachePath, type);\n}\n"
  },
  {
    "path": "fpsgame/graphics/ColladaManager.h",
    "content": "/* Copyright (C) 2013 Wildfire Games.\n* This file is part of 0 A.D.\n*\n* 0 A.D. is free software: you can redistribute it and/or modify\n* it under the terms of the GNU General Public License as published by\n* the Free Software Foundation, either version 2 of the License, or\n* (at your option) any later version.\n*\n* 0 A.D. is distributed in the hope that it will be useful,\n* but WITHOUT ANY WARRANTY; without even the implied warranty of\n* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n* GNU General Public License for more details.\n*\n* You should have received a copy of the GNU General Public License\n* along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#ifndef INCLUDED_COLLADAMANAGER\n#define INCLUDED_COLLADAMANAGER\n\n#include \"lib/file/vfs/vfs.h\"\n\nclass CStr8;\nclass CColladaManagerImpl;\nclass MD5;\n\nclass CColladaManager\n{\n\tNONCOPYABLE(CColladaManager);\n\npublic:\n\tenum FileType { PMD, PSA };\n\n\tCColladaManager(const PIVFS& vfs);\n\t~CColladaManager();\n\n\t/**\n\t* Returns the VFS path to a PMD/PSA file for the given source file.\n\t* Performs a (cached) conversion from COLLADA if necessary.\n\t*\n\t* @param pathnameNoExtension path and name, minus extension, of file to load.\n\t*\t\t  One of either \"sourceName.pmd\" or \"sourceName.dae\" should exist.\n\t* @param type FileType, .pmd or .psa\n\t*\n\t* @return full VFS path (including extension) of file to load; or empty\n\t* string if there was a problem and it could not be loaded. Doesn't knowingly\n\t* return an invalid path.\n\t*/\n\tVfsPath GetLoadablePath(const VfsPath& pathnameNoExtension, FileType type);\n\n\t/**\n\t* Converts DAE to archive cached .pmd/psa and outputs the resulting path\n\t* (used by archive builder)\n\t*\n\t* @param[in] sourcePath path of the .dae to load\n\t* @param[in] type FileType, .pmd or .psa\n\t* @param[out] archiveCachePath output path of the cached file\n\t*\n\t* @return true if COLLADA converter completed successfully; or false if it failed\n\t*/\n\tbool GenerateCachedFile(const VfsPath& sourcePath, FileType type, VfsPath& archiveCachePath);\n\nprivate:\n\tCColladaManagerImpl* m;\n\tPIVFS m_VFS;\n};\n\n#endif // INCLUDED_COLLADAMANAGER\n"
  },
  {
    "path": "fpsgame/graphics/Color.cpp",
    "content": "#ifndef INCLUDED_COLOR\n#define INCLUDED_COLOR\n\n// #include \"SColor.h\"\n#include \"maths/Vector3D.h\"\n#include \"maths/Vector4D.h\"\n\n// simple defines for 3 and 4 component floating point colors - just map to\n// corresponding vector types\ntypedef CVector3D RGBColor;\ntypedef CVector4D RGBAColor;\n\n// exposed as function pointer because it is set at init-time to\n// one of several implementations depending on CPU caps.\nextern SColor4ub(*ConvertRGBColorTo4ub)(const RGBColor& src);\n\n// call once ia32_Init has run; detects CPU caps and activates the best\n// possible codepath.\nextern void ColorActivateFastImpl();\n\n#endif\n"
  },
  {
    "path": "fpsgame/graphics/Color.h",
    "content": "#ifndef INCLUDED_COLOR\n#define INCLUDED_COLOR\n\n=\n=#include \"SColor.h\"\n#include \"maths/Vector3D.h\"\n#include \"maths/Vector4D.h\"\n\n// simple defines for 3 and 4 component floating point colors - just map to\n// corresponding vector types\ntypedef CVector3D RGBColor;\ntypedef CVector4D RGBAColor;\n\n// exposed as function pointer because it is set at init-time to\n// one of several implementations depending on CPU caps.\nextern SColor4ub(*ConvertRGBColorTo4ub)(const RGBColor& src);\n\n// call once ia32_Init has run; detects CPU caps and activates the best\n// possible codepath.\nextern void ColorActivateFastImpl();\n\n#endif\n"
  },
  {
    "path": "fpsgame/graphics/Decal.cpp",
    "content": "/* Copyright (C) 2013 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"precompiled.h\"\n\n#include \"Decal.h\"\n\n#include \"graphics/Terrain.h\"\n#include \"maths/MathUtil.h\"\n\nCModelAbstract* CModelDecal::Clone() const\n{\n\tCModelDecal* clone = new CModelDecal(m_Terrain, m_Decal);\n\treturn clone;\n}\n\nvoid CModelDecal::CalcVertexExtents(ssize_t& i0, ssize_t& j0, ssize_t& i1, ssize_t& j1)\n{\n\tCVector3D corner0(m_Decal.m_OffsetX + m_Decal.m_SizeX/2, 0, m_Decal.m_OffsetZ + m_Decal.m_SizeZ/2);\n\tCVector3D corner1(m_Decal.m_OffsetX + m_Decal.m_SizeX/2, 0, m_Decal.m_OffsetZ - m_Decal.m_SizeZ/2);\n\tCVector3D corner2(m_Decal.m_OffsetX - m_Decal.m_SizeX/2, 0, m_Decal.m_OffsetZ - m_Decal.m_SizeZ/2);\n\tCVector3D corner3(m_Decal.m_OffsetX - m_Decal.m_SizeX/2, 0, m_Decal.m_OffsetZ + m_Decal.m_SizeZ/2);\n\n\tcorner0 = GetTransform().Transform(corner0);\n\tcorner1 = GetTransform().Transform(corner1);\n\tcorner2 = GetTransform().Transform(corner2);\n\tcorner3 = GetTransform().Transform(corner3);\n\n\ti0 = floor(std::min(std::min(corner0.X, corner1.X), std::min(corner2.X, corner3.X)) / TERRAIN_TILE_SIZE);\n\tj0 = floor(std::min(std::min(corner0.Z, corner1.Z), std::min(corner2.Z, corner3.Z)) / TERRAIN_TILE_SIZE);\n\ti1 = ceil(std::max(std::max(corner0.X, corner1.X), std::max(corner2.X, corner3.X)) / TERRAIN_TILE_SIZE);\n\tj1 = ceil(std::max(std::max(corner0.Z, corner1.Z), std::max(corner2.Z, corner3.Z)) / TERRAIN_TILE_SIZE);\n\n\ti0 = clamp(i0, (ssize_t)0, m_Terrain->GetVerticesPerSide()-1);\n\tj0 = clamp(j0, (ssize_t)0, m_Terrain->GetVerticesPerSide()-1);\n\ti1 = clamp(i1, (ssize_t)0, m_Terrain->GetVerticesPerSide()-1);\n\tj1 = clamp(j1, (ssize_t)0, m_Terrain->GetVerticesPerSide()-1);\n}\n\nvoid CModelDecal::CalcBounds()\n{\n\tssize_t i0, j0, i1, j1;\n\tCalcVertexExtents(i0, j0, i1, j1);\n\tm_WorldBounds = m_Terrain->GetVertexesBound(i0, j0, i1, j1);\n}\n\nvoid CModelDecal::SetTerrainDirty(ssize_t i0, ssize_t j0, ssize_t i1, ssize_t j1)\n{\n\t// Check if there's no intersection between the dirty range and this decal\n\tssize_t bi0, bj0, bi1, bj1;\n\tCalcVertexExtents(bi0, bj0, bi1, bj1);\n\tif (bi1 < i0 || bi0 > i1 || bj1 < j0 || bj0 > j1)\n\t\treturn;\n\n\tSetDirty(RENDERDATA_UPDATE_VERTICES);\n}\n\nvoid CModelDecal::InvalidatePosition()\n{\n\tm_PositionValid = false;\n}\n\nvoid CModelDecal::ValidatePosition()\n{\n\tif (m_PositionValid)\n\t{\n\t\tENSURE(!m_Parent || m_Parent->m_PositionValid);\n\t\treturn;\n\t}\n\n\tif (m_Parent && !m_Parent->m_PositionValid)\n\t{\n\t\t// Make sure we don't base our calculations on\n\t\t// a parent animation state that is out of date.\n\t\tm_Parent->ValidatePosition();\n\n\t\t// Parent will recursively call our validation.\n\t\tENSURE(m_PositionValid);\n\t\treturn;\n\t}\n\n\tm_PositionValid = true;\n}\n\nvoid CModelDecal::SetTransform(const CMatrix3D& transform)\n{\n\t// Since decals are assumed to be horizontal and projected downwards\n\t// onto the terrain, use just the Y-axis rotation and the translation\n\tCMatrix3D newTransform;\n\tnewTransform.SetYRotation(transform.GetYRotation() + m_Decal.m_Angle);\n\tnewTransform.Translate(transform.GetTranslation());\n\n\tCRenderableObject::SetTransform(newTransform);\n\tInvalidatePosition();\n}\n\nvoid CModelDecal::RemoveShadows()\n{\n\tm_Decal.m_Material.AddShaderDefine(str_DISABLE_RECEIVE_SHADOWS, str_1);\n\tm_Decal.m_Material.RecomputeCombinedShaderDefines();\n}\n"
  },
  {
    "path": "fpsgame/graphics/Decal.h",
    "content": "/* Copyright (C) 2013 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef INCLUDED_DECAL\n#define INCLUDED_DECAL\n\n#include \"graphics/Material.h\"\n#include \"graphics/ModelAbstract.h\"\n#include \"graphics/Texture.h\"\n\nclass CTerrain;\n\n/**\n * Terrain decal definition.\n * Decals are rectangular textures that are projected vertically downwards\n * onto the terrain.\n */\nstruct SDecal\n{\n\tSDecal(const CMaterial& material, float sizeX, float sizeZ, float angle,\n\t\t\tfloat offsetX, float offsetZ, bool floating)\n\t\t: m_Material(material), m_SizeX(sizeX), m_SizeZ(sizeZ), m_Angle(angle),\n\t\t  m_OffsetX(offsetX), m_OffsetZ(offsetZ), m_Floating(floating)\n\t{\n\t}\n\n\tCMaterial m_Material;\n\tfloat m_SizeX;\n\tfloat m_SizeZ;\n\tfloat m_Angle;\n\tfloat m_OffsetX;\n\tfloat m_OffsetZ;\n\tbool m_Floating;\n};\n\nclass CModelDecal : public CModelAbstract\n{\npublic:\n\tCModelDecal(CTerrain* terrain, const SDecal& decal)\n\t\t: m_Terrain(terrain), m_Decal(decal)\n\t{\n\t\tENSURE(terrain != NULL);\n\t}\n\n\t/// Dynamic cast\n\tvirtual CModelDecal* ToCModelDecal()\n\t{\n\t\treturn this;\n\t}\n\n\tvirtual CModelAbstract* Clone() const;\n\n\tvirtual void SetDirtyRec(int dirtyflags)\n\t{\n\t\tSetDirty(dirtyflags);\n\t}\n\n\tvirtual void SetTerrainDirty(ssize_t i0, ssize_t j0, ssize_t i1, ssize_t j1);\n\n\tvirtual void CalcBounds();\n\tvirtual void ValidatePosition();\n\tvirtual void InvalidatePosition();\n\tvirtual void SetTransform(const CMatrix3D& transform);\n\n\t// remove shadow receiving\n\tvoid RemoveShadows();\n\n\t/**\n\t * Compute the terrain vertex indexes that bound the decal's\n\t * projection onto the terrain.\n\t * The returned indexes are clamped to the terrain size.\n\t */\n\tvoid CalcVertexExtents(ssize_t& i0, ssize_t& j0, ssize_t& i1, ssize_t& j1);\n\n\tCTerrain* m_Terrain;\n\tSDecal m_Decal;\n};\n\n#endif // INCLUDED_DECAL\n"
  },
  {
    "path": "fpsgame/graphics/Entity.h",
    "content": "/* Copyright (C) 2015 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef INCLUDED_RMS_ENTITY\n#define INCLUDED_RMS_ENTITY\n\n#include \"maths/FixedVector3D.h\"\n\n// Struct for parsing random map data\nstruct Entity\n{\n\tstd::wstring templateName;\n\tu16 entityID;\n\tu16 playerID;\n\tCFixedVector3D position;\n\tCFixedVector3D rotation;\n};\n\n#endif\n"
  },
  {
    "path": "fpsgame/graphics/Font.cpp",
    "content": "/* Copyright (C) 2013 Wildfire Games.\n* This file is part of 0 A.D.\n*\n* 0 A.D. is free software: you can redistribute it and/or modify\n* it under the terms of the GNU General Public License as published by\n* the Free Software Foundation, either version 2 of the License, or\n* (at your option) any later version.\n*\n* 0 A.D. is distributed in the hope that it will be useful,\n* but WITHOUT ANY WARRANTY; without even the implied warranty of\n* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n* GNU General Public License for more details.\n*\n* You should have received a copy of the GNU General Public License\n* along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#include \"precompiled.h\"\n#include \"Font.h\"\n\n#include \"graphics/FontManager.h\"\n#include \"ps/Filesystem.h\"\n#include \"ps/CLogger.h\"\n#include \"renderer/Renderer.h\"\n\n#include <map>\n#include <string>\n\nCFont::GlyphMap::GlyphMap()\n{\n\tmemset(m_Data, 0, sizeof(m_Data));\n}\n\nCFont::GlyphMap::~GlyphMap()\n{\n\tfor (size_t i = 0; i < ARRAY_SIZE(m_Data); i++)\n\t\tdelete[] m_Data[i];\n}\n\nvoid CFont::GlyphMap::set(u16 i, const GlyphData& val)\n{\n\tif (!m_Data[i >> 8])\n\t\tm_Data[i >> 8] = new GlyphData[256]();\n\tm_Data[i >> 8][i & 0xff] = val;\n\tm_Data[i >> 8][i & 0xff].defined = 1;\n}\n\nint CFont::GetCharacterWidth(wchar_t c) const\n{\n\tconst GlyphData* g = m_Glyphs.get(c);\n\n\tif (!g)\n\t\tg = m_Glyphs.get(0xFFFD); // Use the missing glyph symbol\n\n\tif (!g)\n\t\treturn 0;\n\n\treturn g->xadvance;\n}\n\nvoid CFont::CalculateStringSize(const wchar_t* string, int& width, int& height) const\n{\n\twidth = 0;\n\theight = m_Height;\n\n\tfor (const wchar_t* c = string; *c != '\\0'; c++)\n\t{\n\t\tconst GlyphData* g = m_Glyphs.get(*c);\n\n\t\tif (!g)\n\t\t\tg = m_Glyphs.get(0xFFFD); // Use the missing glyph symbol\n\n\t\tif (g)\n\t\t\twidth += g->xadvance; // Add the character's advance distance\n\t}\n}\n"
  },
  {
    "path": "fpsgame/graphics/Font.h",
    "content": "/* Copyright (C) 2013 Wildfire Games.\n* This file is part of 0 A.D.\n*\n* 0 A.D. is free software: you can redistribute it and/or modify\n* it under the terms of the GNU General Public License as published by\n* the Free Software Foundation, either version 2 of the License, or\n* (at your option) any later version.\n*\n* 0 A.D. is distributed in the hope that it will be useful,\n* but WITHOUT ANY WARRANTY; without even the implied warranty of\n* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n* GNU General Public License for more details.\n*\n* You should have received a copy of the GNU General Public License\n* along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#ifndef INCLUDED_FONT\n#define INCLUDED_FONT\n\n#include \"graphics/Texture.h\"\n#include \"lib/res/handle.h\"\n\n#include <map>\n\nclass CStrW;\nstruct UnifontGlyphData;\n\n/**\n* Storage for a bitmap font. Loaded by CFontManager.\n*/\nclass CFont\n{\n\tfriend class CFontManager;\n\tCFont() {}\npublic:\n\tstruct GlyphData\n\t{\n\t\tfloat u0, v0, u1, v1;\n\t\ti16 x0, y0, x1, y1;\n\t\ti16 xadvance;\n\t\tu8 defined;\n\t};\n\n\t/**\n\t* Relatively efficient lookup of GlyphData from 16-bit Unicode codepoint.\n\t* This is stored as a sparse 2D array, exploiting the knowledge that a font\n\t* typically only supports a small number of 256-codepoint blocks, so most\n\t* elements of m_Data will be NULL.\n\t*/\n\tclass GlyphMap\n\t{\n\t\tNONCOPYABLE(GlyphMap);\n\tpublic:\n\t\tGlyphMap();\n\t\t~GlyphMap();\n\t\tvoid set(u16 i, const GlyphData& val);\n\n\t\tconst GlyphData* get(u16 i) const\n\t\t{\n\t\t\tif (!m_Data[i >> 8])\n\t\t\t\treturn NULL;\n\t\t\tif (!m_Data[i >> 8][i & 0xff].defined)\n\t\t\t\treturn NULL;\n\t\t\treturn &m_Data[i >> 8][i & 0xff];\n\t\t}\n\n\tprivate:\n\t\tGlyphData* m_Data[256];\n\t};\n\n\tbool HasRGB() const { return m_HasRGB; }\n\tint GetLineSpacing() const { return m_LineSpacing; }\n\tint GetHeight() const { return m_Height; }\n\tint GetCharacterWidth(wchar_t c) const;\n\tvoid CalculateStringSize(const wchar_t* string, int& w, int& h) const;\n\tvoid GetGlyphBounds(float& x0, float& y0, float& x1, float& y1) const\n\t{\n\t\tx0 = m_BoundsX0;\n\t\ty0 = m_BoundsY0;\n\t\tx1 = m_BoundsX1;\n\t\ty1 = m_BoundsY1;\n\t}\n\tconst GlyphMap& GetGlyphs() const { return m_Glyphs; }\n\tCTexturePtr GetTexture() const { return m_Texture; }\n\nprivate:\n\tCTexturePtr m_Texture;\n\n\tbool m_HasRGB; // true if RGBA, false if ALPHA\n\n\tGlyphMap m_Glyphs;\n\n\tint m_LineSpacing;\n\tint m_Height; // height of a capital letter, roughly\n\n\t\t\t\t  // Bounding box of all glyphs\n\tfloat m_BoundsX0;\n\tfloat m_BoundsY0;\n\tfloat m_BoundsX1;\n\tfloat m_BoundsY1;\n};\n\n#endif // INCLUDED_FONT\n"
  },
  {
    "path": "fpsgame/graphics/FontManager.cpp",
    "content": "/* Copyright (C) 2013 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"precompiled.h\"\n\n#include \"FontManager.h\"\n\n#include \"graphics/Font.h\"\n#include \"graphics/TextureManager.h\"\n#include \"ps/CLogger.h\"\n#include \"ps/CStr.h\"\n#include \"ps/Filesystem.h\"\n#include \"renderer/Renderer.h\"\n\n#include <cfloat>\n\nshared_ptr<CFont> CFontManager::LoadFont(CStrIntern fontName)\n{\n\tFontsMap::iterator it = m_Fonts.find(fontName);\n\tif (it != m_Fonts.end())\n\t\treturn it->second;\n\n\tshared_ptr<CFont> font(new CFont());\n\n\tif (!ReadFont(font.get(), fontName))\n\t{\n\t\t// Fall back to default font (unless this is the default font)\n\t\tif (fontName == str_sans_10)\n\t\t\tfont.reset();\n\t\telse\n\t\t\tfont = LoadFont(str_sans_10);\n\t}\n\n\tm_Fonts[fontName] = font;\n\treturn font;\n}\n\nbool CFontManager::ReadFont(CFont* font, CStrIntern fontName)\n{\n\tconst VfsPath path(L\"fonts/\");\n\n\t// Read font definition file into a stringstream\n\tshared_ptr<u8> buf;\n\tsize_t size;\n\tconst VfsPath fntName(fontName.string() + \".fnt\");\n\tif (g_VFS->LoadFile(path / fntName, buf, size) < 0)\n\t{\n\t\tLOGERROR(\"Failed to open font file %s\", (path / fntName).string8());\n\t\treturn false;\n\t}\n\tstd::istringstream FNTStream(std::string((const char*)buf.get(), size));\n\n\tint Version;\n\tFNTStream >> Version;\n\tif (Version != 101) // Make sure this is from a recent version of the font builder\n\t{\n\t\tLOGERROR(\"Font %s has invalid version\", fontName.c_str());\n\t\treturn 0;\n\t}\n\n\tint TextureWidth, TextureHeight;\n\tFNTStream >> TextureWidth >> TextureHeight;\n\n\tstd::string Format;\n\tFNTStream >> Format;\n\tif (Format == \"rgba\")\n\t\tfont->m_HasRGB = true;\n\telse if (Format == \"a\")\n\t\tfont->m_HasRGB = false;\n\telse\n\t\tdebug_warn(L\"Invalid .fnt format string\");\n\n\tint NumGlyphs;\n\tFNTStream >> NumGlyphs;\n\n\tFNTStream >> font->m_LineSpacing;\n\tFNTStream >> font->m_Height;\n\n\tfont->m_BoundsX0 = FLT_MAX;\n\tfont->m_BoundsY0 = FLT_MAX;\n\tfont->m_BoundsX1 = -FLT_MAX;\n\tfont->m_BoundsY1 = -FLT_MAX;\n\n\tfor (int i = 0; i < NumGlyphs; ++i)\n\t{\n\t\tint          Codepoint, TextureX, TextureY, Width, Height, OffsetX, OffsetY, Advance;\n\t\tFNTStream >> Codepoint>>TextureX>>TextureY>>Width>>Height>>OffsetX>>OffsetY>>Advance;\n\n\t\tif (Codepoint < 0 || Codepoint > 0xFFFF)\n\t\t{\n\t\t\tLOGWARNING(\"Font %s has invalid codepoint 0x%x\", fontName.c_str(), Codepoint);\n\t\t\tcontinue;\n\t\t}\n\n\t\tfloat u = (float)TextureX / (float)TextureWidth;\n\t\tfloat v = (float)TextureY / (float)TextureHeight;\n\t\tfloat w = (float)Width  / (float)TextureWidth;\n\t\tfloat h = (float)Height / (float)TextureHeight;\n\n\t\tCFont::GlyphData g = { u, -v, u+w, -v+h, (i16)OffsetX, (i16)-OffsetY, (i16)(OffsetX+Width), (i16)(-OffsetY+Height), (i16)Advance };\n\t\tfont->m_Glyphs.set((u16)Codepoint, g);\n\n\t\tfont->m_BoundsX0 = std::min(font->m_BoundsX0, (float)g.x0);\n\t\tfont->m_BoundsY0 = std::min(font->m_BoundsY0, (float)g.y0);\n\t\tfont->m_BoundsX1 = std::max(font->m_BoundsX1, (float)g.x1);\n\t\tfont->m_BoundsY1 = std::max(font->m_BoundsY1, (float)g.y1);\n\t}\n\n\tENSURE(font->m_Height); // Ensure the height has been found (which should always happen if the font includes an 'I')\n\n\t// Load glyph texture\n\tconst VfsPath imgName(fontName.string() + \".png\");\n\tCTextureProperties textureProps(path / imgName);\n\ttextureProps.SetFilter(GL_NEAREST);\n\tif (!font->m_HasRGB)\n\t\ttextureProps.SetFormatOverride(GL_ALPHA);\n\tfont->m_Texture = g_Renderer.GetTextureManager().CreateTexture(textureProps);\n\n\treturn true;\n}\n"
  },
  {
    "path": "fpsgame/graphics/FontManager.h",
    "content": "/* Copyright (C) 2013 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef INCLUDED_FONTMANAGER\n#define INCLUDED_FONTMANAGER\n\n#include <boost/unordered_map.hpp>\n\nclass CFont;\nclass CStrIntern;\n\n/**\n * Font manager: loads and caches bitmap fonts.\n */\nclass CFontManager\n{\npublic:\n\tshared_ptr<CFont> LoadFont(CStrIntern fontName);\n\nprivate:\n\tbool ReadFont(CFont* font, CStrIntern fontName);\n\n\ttypedef boost::unordered_map<CStrIntern, shared_ptr<CFont> > FontsMap;\n\tFontsMap m_Fonts;\n};\n\n#endif // INCLUDED_FONTMANAGER\n"
  },
  {
    "path": "fpsgame/graphics/FontMetrics.cpp",
    "content": "/* Copyright (C) 2013 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"precompiled.h\"\n#include \"FontMetrics.h\"\n\n#include \"graphics/Font.h\"\n#include \"graphics/FontManager.h\"\n#include \"ps/Filesystem.h\"\n#include \"ps/CLogger.h\"\n#include \"renderer/Renderer.h\"\n\nCFontMetrics::CFontMetrics(CStrIntern font)\n{\n\tm_Font = g_Renderer.GetFontManager().LoadFont(font);\n}\n\nint CFontMetrics::GetLineSpacing() const\n{\n\t// Return some arbitrary default if the font failed to load, so that the\n\t// user of CFontMetrics doesn't have to care about failures\n\tif (!m_Font)\n\t\treturn 12;\n\treturn m_Font->GetLineSpacing();\n}\n\nint CFontMetrics::GetHeight() const\n{\n\tif (!m_Font)\n\t\treturn 6;\n\treturn m_Font->GetHeight();\n}\n\nint CFontMetrics::GetCharacterWidth(wchar_t c) const\n{\n\tif (!m_Font)\n\t\treturn 6;\n\treturn m_Font->GetCharacterWidth(c);\n}\n\nvoid CFontMetrics::CalculateStringSize(const wchar_t* string, int& w, int& h) const\n{\n\tif (!m_Font)\n\t\tw = h = 0;\n\telse\n\t\tm_Font->CalculateStringSize(string, w, h);\n}\n"
  },
  {
    "path": "fpsgame/graphics/FontMetrics.h",
    "content": "/* Copyright (C) 2013 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef INCLUDED_FONTMETRICS\n#define INCLUDED_FONTMETRICS\n\nclass CFont;\nclass CStrIntern;\n\n/**\n * Helper class for measuring sizes of text.\n * This will load the font when necessary, and will return plausible values\n * if loading fails (since misrendering is better than crashing).\n */\nclass CFontMetrics\n{\npublic:\n\tCFontMetrics(CStrIntern font);\n\n\tint GetLineSpacing() const;\n\tint GetHeight() const;\n\tint GetCharacterWidth(wchar_t c) const;\n\tvoid CalculateStringSize(const wchar_t* string, int& w, int& h) const;\n\nprivate:\n\tshared_ptr<CFont> m_Font;\n};\n\n#endif // INCLUDED_FONTMETRICS\n"
  },
  {
    "path": "fpsgame/graphics/Frustum.cpp",
    "content": "/* Copyright (C) 2009 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n/*\n * CFrustum is a collection of planes which define a viewing space.\n */\n\n/*\nUsually associated with the camera, there are 6 planes which define the\nview pyramid. But we allow more planes per frustum which may be used for\nportal rendering, where a portal may have 3 or more edges.\n*/\n\n#include \"precompiled.h\"\n\n#include \"Frustum.h\"\n#include \"maths/BoundingBoxAligned.h\"\n#include \"maths/MathUtil.h\"\n#include \"maths/Matrix3D.h\"\n\nCFrustum::CFrustum ()\n{\n\tm_NumPlanes = 0;\n}\n\nCFrustum::~CFrustum ()\n{\n}\n\nvoid CFrustum::SetNumPlanes (size_t num)\n{\n\tm_NumPlanes = num;\n\n\t//clip it\n\tif (m_NumPlanes >= MAX_NUM_FRUSTUM_PLANES)\n\t{\n\t\tdebug_warn(L\"CFrustum::SetNumPlanes: Too many planes\");\n\t\tm_NumPlanes = MAX_NUM_FRUSTUM_PLANES-1;\n\t}\n}\n\nvoid CFrustum::AddPlane(const CPlane& plane)\n{\n\tif (m_NumPlanes >= MAX_NUM_FRUSTUM_PLANES)\n\t{\n\t\tdebug_warn(L\"CFrustum::AddPlane: Too many planes\");\n\t\treturn;\n\t}\n\n\tm_aPlanes[m_NumPlanes++] = plane;\n}\n\nvoid CFrustum::Transform(CMatrix3D& m)\n{\n\tfor (size_t i = 0; i < m_NumPlanes; ++i)\n\t{\n\t\tCVector3D n = m.Rotate(m_aPlanes[i].m_Norm);\n\t\tCVector3D p = m.Transform(m_aPlanes[i].m_Norm * -m_aPlanes[i].m_Dist);\n\t\tm_aPlanes[i].Set(n, p);\n\t\tm_aPlanes[i].Normalize();\n\t}\n}\n\nbool CFrustum::IsPointVisible(const CVector3D& point) const\n{\n\tfor (size_t i=0; i<m_NumPlanes; ++i)\n\t{\n\t\tif (m_aPlanes[i].IsPointOnBackSide(point))\n\t\t\treturn false;\n\t}\n\n\treturn true;\n}\n\nbool CFrustum::DoesSegmentIntersect(const CVector3D& startRef, const CVector3D& endRef) const\n{\n\tCVector3D start = startRef;\n\tCVector3D end = endRef;\n\n\tif(IsPointVisible(start) || IsPointVisible(end))\n\t\treturn true;\n\n\tCVector3D intersect;\n\tfor (size_t i = 0; i<m_NumPlanes; ++i)\n\t{\n\t\tif (m_aPlanes[i].FindLineSegIntersection(start, end, &intersect))\n\t\t{\n\t\t\tif (IsPointVisible(intersect))\n\t\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\nbool CFrustum::IsSphereVisible(const CVector3D& center, float radius) const\n{\n\tfor (size_t i = 0; i < m_NumPlanes; ++i)\n\t{\n\t\t// If none of the sphere is in front of the plane, then\n\t\t// it is outside the frustum\n\t\tif (-m_aPlanes[i].DistanceToPlane(center) > radius)\n\t\t\treturn false;\n\t}\n\n\treturn true;\n}\n\nbool CFrustum::IsBoxVisible(const CVector3D& position, const CBoundingBoxAligned& bounds) const\n{\n\t//basically for every plane we calculate the furthest point\n\t//in the box to that plane. If that point is beyond the plane\n\t//then the box is not visible\n\tCVector3D FarPoint;\n\tCVector3D Min = position+bounds[0];\n\tCVector3D Max = position+bounds[1];\n\n\tfor (size_t i=0; i<m_NumPlanes; ++i)\n\t{\n\t\tFarPoint.X = m_aPlanes[i].m_Norm.X > 0.0f ? Max.X : Min.X;\n\t\tFarPoint.Y = m_aPlanes[i].m_Norm.Y > 0.0f ? Max.Y : Min.Y;\n\t\tFarPoint.Z = m_aPlanes[i].m_Norm.Z > 0.0f ? Max.Z : Min.Z;\n\t\t\n\t\tif (m_aPlanes[i].IsPointOnBackSide(FarPoint))\n\t\t\treturn false;\n\t}\n\n\treturn true;\n}\n\nbool CFrustum::IsBoxVisible(const CBoundingBoxAligned& bounds) const\n{\n\t//Same as the previous one, but with the position = (0, 0, 0)\n\tCVector3D FarPoint;\n\n\tfor (size_t i=0; i<m_NumPlanes; ++i)\n\t{\n\t\tFarPoint.X = m_aPlanes[i].m_Norm.X > 0.0f ? bounds[1].X : bounds[0].X;\n\t\tFarPoint.Y = m_aPlanes[i].m_Norm.Y > 0.0f ? bounds[1].Y : bounds[0].Y;\n\t\tFarPoint.Z = m_aPlanes[i].m_Norm.Z > 0.0f ? bounds[1].Z : bounds[0].Z;\n\t\t\n\t\tif (m_aPlanes[i].IsPointOnBackSide(FarPoint))\n\t\t\treturn false;\n\t}\n\n\treturn true;\n}\n"
  },
  {
    "path": "fpsgame/graphics/Frustum.h",
    "content": "/* Copyright (C) 2009 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n/*\n * CFrustum is a collection of planes which define a viewing space.\n */\n\n/*\nUsually associated with the camera, there are 6 planes which define the\nview pyramid. But we allow more planes per frustum which may be used for\nportal rendering, where a portal may have 3 or more edges.\n*/\n\n#ifndef INCLUDED_FRUSTUM\n#define INCLUDED_FRUSTUM\n\n#include \"maths/Plane.h\"\n\n//10 planes should be enough\n#define MAX_NUM_FRUSTUM_PLANES\t\t(10)\n\nclass CBoundingBoxAligned;\nclass CMatrix3D;\n\nclass CFrustum\n{\npublic:\n\tCFrustum ();\n\t~CFrustum ();\n\n\t//Set the number of planes to use for\n\t//calculations. This is clipped to\n\t//[0,MAX_NUM_FRUSTUM_PLANES]\n\tvoid SetNumPlanes (size_t num);\n\n\tsize_t GetNumPlanes() const { return m_NumPlanes; }\n\n\tvoid AddPlane (const CPlane& plane);\n\n\tvoid Transform(CMatrix3D& m);\n\n\t//The following methods return true if the shape is\n\t//partially or completely in front of the frustum planes\n\tbool IsPointVisible(const CVector3D& point) const;\n\tbool DoesSegmentIntersect(const CVector3D& start, const CVector3D& end) const;\n\tbool IsSphereVisible(const CVector3D& center, float radius) const;\n\tbool IsBoxVisible(const CVector3D& position, const CBoundingBoxAligned& bounds) const;\n\tbool IsBoxVisible(const CBoundingBoxAligned& bounds) const;\n\n\tCPlane& operator[](size_t idx) { return m_aPlanes[idx]; }\n\tconst CPlane& operator[](size_t idx) const { return m_aPlanes[idx]; }\n\npublic:\n\t//make the planes public for ease of use\n\tCPlane m_aPlanes[MAX_NUM_FRUSTUM_PLANES];\n\nprivate:\n\tsize_t m_NumPlanes;\n};\n\n#endif\n"
  },
  {
    "path": "fpsgame/graphics/GameView.cpp",
    "content": "/* Copyright (C) 2015 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"precompiled.h\"\n\n#include \"GameView.h\"\n\n#include \"graphics/Camera.h\"\n#include \"graphics/CinemaManager.h\"\n#include \"graphics/ColladaManager.h\"\n#include \"graphics/HFTracer.h\"\n#include \"graphics/LOSTexture.h\"\n#include \"graphics/LightEnv.h\"\n#include \"graphics/Model.h\"\n#include \"graphics/ObjectManager.h\"\n#include \"graphics/Patch.h\"\n#include \"graphics/SkeletonAnimManager.h\"\n#include \"graphics/Terrain.h\"\n#include \"graphics/TerrainTextureManager.h\"\n#include \"graphics/TerritoryTexture.h\"\n#include \"graphics/Unit.h\"\n#include \"graphics/UnitManager.h\"\n#include \"graphics/scripting/JSInterface_GameView.h\"\n#include \"lib/input.h\"\n#include \"lib/timer.h\"\n#include \"lobby/IXmppClient.h\"\n#include \"maths/BoundingBoxAligned.h\"\n#include \"maths/MathUtil.h\"\n#include \"maths/Matrix3D.h\"\n#include \"maths/Quaternion.h\"\n#include \"ps/ConfigDB.h\"\n#include \"ps/Filesystem.h\"\n#include \"ps/Game.h\"\n#include \"ps/Globals.h\"\n#include \"ps/Hotkey.h\"\n#include \"ps/Joystick.h\"\n#include \"ps/Loader.h\"\n#include \"ps/LoaderThunks.h\"\n#include \"ps/Profile.h\"\n#include \"ps/Pyrogenesis.h\"\n#include \"ps/TouchInput.h\"\n#include \"ps/World.h\"\n#include \"renderer/Renderer.h\"\n#include \"renderer/WaterManager.h\"\n#include \"simulation2/Simulation2.h\"\n#include \"simulation2/components/ICmpPosition.h\"\n#include \"simulation2/components/ICmpRangeManager.h\"\n\nextern int g_xres, g_yres;\n\n// Maximum distance outside the edge of the map that the camera's\n// focus point can be moved\nstatic const float CAMERA_EDGE_MARGIN = 2.0f*TERRAIN_TILE_SIZE;\n\n/**\n * A value with exponential decay towards the target value.\n */\nclass CSmoothedValue\n{\npublic:\n\tCSmoothedValue(float value, float smoothness, float minDelta)\n\t\t: m_Target(value), m_Current(value), m_Smoothness(smoothness), m_MinDelta(minDelta)\n\t{\n\t}\n\n\tfloat GetSmoothedValue()\n\t{\n\t\treturn m_Current;\n\t}\n\n\tvoid SetValueSmoothly(float value)\n\t{\n\t\tm_Target = value;\n\t}\n\n\tvoid AddSmoothly(float value)\n\t{\n\t\tm_Target += value;\n\t}\n\n\tvoid Add(float value)\n\t{\n\t\tm_Target += value;\n\t\tm_Current += value;\n\t}\n\n\tfloat GetValue()\n\t{\n\t\treturn m_Target;\n\t}\n\n\tvoid SetValue(float value)\n\t{\n\t\tm_Target = value;\n\t\tm_Current = value;\n\t}\n\n\tfloat Update(float time)\n\t{\n\t\tif (fabs(m_Target - m_Current) < m_MinDelta)\n\t\t\treturn 0.0f;\n\n\t\tdouble p = pow((double)m_Smoothness, 10.0 * (double)time);\n\t\t// (add the factor of 10 so that smoothnesses don't have to be tiny numbers)\n\n\t\tdouble delta = (m_Target - m_Current) * (1.0 - p);\n\t\tm_Current += delta;\n\t\treturn (float)delta;\n\t}\n\n\tvoid ClampSmoothly(float min, float max)\n\t{\n\t\tm_Target = Clamp(m_Target, (double)min, (double)max);\n\t}\n\n\t// Wrap so 'target' is in the range [min, max]\n\tvoid Wrap(float min, float max)\n\t{\n\t\tdouble t = fmod(m_Target - min, (double)(max - min));\n\t\tif (t < 0)\n\t\t\tt += max - min;\n\t\tt += min;\n\n\t\tm_Current += t - m_Target;\n\t\tm_Target = t;\n\t}\n\nprivate:\n\tdouble m_Target; // the value which m_Current is tending towards\n\tdouble m_Current;\n\t// (We use double because the extra precision is worthwhile here)\n\n\tfloat m_MinDelta; // cutoff where we stop moving (to avoid ugly shimmering effects)\npublic:\n\tfloat m_Smoothness;\n};\n\nclass CGameViewImpl\n{\n\tNONCOPYABLE(CGameViewImpl);\npublic:\n\tCGameViewImpl(CGame* game)\n\t\t: Game(game),\n\t\tColladaManager(g_VFS), MeshManager(ColladaManager), SkeletonAnimManager(ColladaManager),\n\t\tObjectManager(MeshManager, SkeletonAnimManager, *game->GetSimulation2()),\n\t\tLOSTexture(*game->GetSimulation2()),\n\t\tTerritoryTexture(*game->GetSimulation2()),\n\t\tViewCamera(),\n\t\tCullCamera(),\n\t\tLockCullCamera(false),\n\t\tConstrainCamera(true),\n\t\tCulling(true),\n\t\tFollowEntity(INVALID_ENTITY),\n\t\tFollowFirstPerson(false),\n\n\t\t// Dummy values (these will be filled in by the config file)\n\t\tViewScrollSpeed(0),\n\t\tViewScrollSpeedModifier(1),\n\t\tViewRotateXSpeed(0),\n\t\tViewRotateXMin(0),\n\t\tViewRotateXMax(0),\n\t\tViewRotateXDefault(0),\n\t\tViewRotateYSpeed(0),\n\t\tViewRotateYSpeedWheel(0),\n\t\tViewRotateYDefault(0),\n\t\tViewRotateSpeedModifier(1),\n\t\tViewDragSpeed(0),\n\t\tViewZoomSpeed(0),\n\t\tViewZoomSpeedWheel(0),\n\t\tViewZoomMin(0),\n\t\tViewZoomMax(0),\n\t\tViewZoomDefault(0),\n\t\tViewZoomSpeedModifier(1),\n\t\tViewFOV(DEGTORAD(45.f)),\n\t\tViewNear(2.f),\n\t\tViewFar(4096.f),\n\t\tJoystickPanX(-1),\n\t\tJoystickPanY(-1),\n\t\tJoystickRotateX(-1),\n\t\tJoystickRotateY(-1),\n\t\tJoystickZoomIn(-1),\n\t\tJoystickZoomOut(-1),\n\t\tHeightSmoothness(0.5f),\n\t\tHeightMin(16.f),\n\n\t\tPosX(0, 0, 0.01f),\n\t\tPosY(0, 0, 0.01f),\n\t\tPosZ(0, 0, 0.01f),\n\t\tZoom(0, 0, 0.1f),\n\t\tRotateX(0, 0, 0.001f),\n\t\tRotateY(0, 0, 0.001f)\n\t{\n\t}\n\n\tCGame* Game;\n\tCColladaManager ColladaManager;\n\tCMeshManager MeshManager;\n\tCSkeletonAnimManager SkeletonAnimManager;\n\tCObjectManager ObjectManager;\n\tCLOSTexture LOSTexture;\n\tCTerritoryTexture TerritoryTexture;\n\n\t/**\n\t * this camera controls the eye position when rendering\n\t */\n\tCCamera ViewCamera;\n\n\t/**\n\t * this camera controls the frustum that is used for culling\n\t * and shadow calculations\n\t *\n\t * Note that all code that works with camera movements should only change\n\t * m_ViewCamera. The render functions automatically sync the cull camera to\n\t * the view camera depending on the value of m_LockCullCamera.\n\t */\n\tCCamera CullCamera;\n\n\t/**\n\t * When @c true, the cull camera is locked in place.\n\t * When @c false, the cull camera follows the view camera.\n\t *\n\t * Exposed to JS as gameView.lockCullCamera\n\t */\n\tbool LockCullCamera;\n\n\t/**\n\t * When @c true, culling is enabled so that only models that have a chance of\n\t * being visible are sent to the renderer.\n\t * Otherwise, the entire world is sent to the renderer.\n\t *\n\t * Exposed to JS as gameView.culling\n\t */\n\tbool Culling;\n\n\t/**\n\t * Whether the camera movement should be constrained by min/max limits\n\t * and terrain avoidance.\n\t */\n\tbool ConstrainCamera;\n\n\t/**\n\t * Cache global lighting environment. This is used  to check whether the\n\t * environment has changed during the last frame, so that vertex data can be updated etc.\n\t */\n\tCLightEnv CachedLightEnv;\n\n\tCCinemaManager CinemaManager;\n\n\t/**\n\t * Entity for the camera to follow, or INVALID_ENTITY if none.\n\t */\n\tentity_id_t FollowEntity;\n\n\t/**\n\t * Whether to follow FollowEntity in first-person mode.\n\t */\n\tbool FollowFirstPerson;\n\n\t////////////////////////////////////////\n\t// Settings\n\tfloat ViewScrollSpeed;\n\tfloat ViewScrollSpeedModifier;\n\tfloat ViewRotateXSpeed;\n\tfloat ViewRotateXMin;\n\tfloat ViewRotateXMax;\n\tfloat ViewRotateXDefault;\n\tfloat ViewRotateYSpeed;\n\tfloat ViewRotateYSpeedWheel;\n\tfloat ViewRotateYDefault;\n\tfloat ViewRotateSpeedModifier;\n\tfloat ViewDragSpeed;\n\tfloat ViewZoomSpeed;\n\tfloat ViewZoomSpeedWheel;\n\tfloat ViewZoomMin;\n\tfloat ViewZoomMax;\n\tfloat ViewZoomDefault;\n\tfloat ViewZoomSpeedModifier;\n\tfloat ViewFOV;\n\tfloat ViewNear;\n\tfloat ViewFar;\n\tint JoystickPanX;\n\tint JoystickPanY;\n\tint JoystickRotateX;\n\tint JoystickRotateY;\n\tint JoystickZoomIn;\n\tint JoystickZoomOut;\n\tfloat HeightSmoothness;\n\tfloat HeightMin;\n\n\t////////////////////////////////////////\n\t// Camera Controls State\n\tCSmoothedValue PosX;\n\tCSmoothedValue PosY;\n\tCSmoothedValue PosZ;\n\tCSmoothedValue Zoom;\n\tCSmoothedValue RotateX; // inclination around x axis (relative to camera)\n\tCSmoothedValue RotateY; // rotation around y (vertical) axis\n};\n\n#define IMPLEMENT_BOOLEAN_SETTING(NAME) \\\nbool CGameView::Get##NAME##Enabled() \\\n{ \\\n\treturn m->NAME; \\\n} \\\n\\\nvoid CGameView::Set##NAME##Enabled(bool Enabled) \\\n{ \\\n\tm->NAME = Enabled; \\\n}\n\nIMPLEMENT_BOOLEAN_SETTING(Culling);\nIMPLEMENT_BOOLEAN_SETTING(LockCullCamera);\nIMPLEMENT_BOOLEAN_SETTING(ConstrainCamera);\n\n#undef IMPLEMENT_BOOLEAN_SETTING\n\nstatic void SetupCameraMatrixSmooth(CGameViewImpl* m, CMatrix3D* orientation)\n{\n\torientation->SetIdentity();\n\torientation->RotateX(m->RotateX.GetSmoothedValue());\n\torientation->RotateY(m->RotateY.GetSmoothedValue());\n\torientation->Translate(m->PosX.GetSmoothedValue(), m->PosY.GetSmoothedValue(), m->PosZ.GetSmoothedValue());\n}\n\nstatic void SetupCameraMatrixSmoothRot(CGameViewImpl* m, CMatrix3D* orientation)\n{\n\torientation->SetIdentity();\n\torientation->RotateX(m->RotateX.GetSmoothedValue());\n\torientation->RotateY(m->RotateY.GetSmoothedValue());\n\torientation->Translate(m->PosX.GetValue(), m->PosY.GetValue(), m->PosZ.GetValue());\n}\n\nstatic void SetupCameraMatrixNonSmooth(CGameViewImpl* m, CMatrix3D* orientation)\n{\n\torientation->SetIdentity();\n\torientation->RotateX(m->RotateX.GetValue());\n\torientation->RotateY(m->RotateY.GetValue());\n\torientation->Translate(m->PosX.GetValue(), m->PosY.GetValue(), m->PosZ.GetValue());\n}\n\nCGameView::CGameView(CGame *pGame):\n\tm(new CGameViewImpl(pGame))\n{\n\tSViewPort vp;\n\tvp.m_X=0;\n\tvp.m_Y=0;\n\tvp.m_Width=g_xres;\n\tvp.m_Height=g_yres;\n\tm->ViewCamera.SetViewPort(vp);\n\n\tm->ViewCamera.SetProjection(m->ViewNear, m->ViewFar, m->ViewFOV);\n\tSetupCameraMatrixSmooth(m, &m->ViewCamera.m_Orientation);\n\tm->ViewCamera.UpdateFrustum();\n\n\tm->CullCamera = m->ViewCamera;\n\tg_Renderer.SetSceneCamera(m->ViewCamera, m->CullCamera);\n}\n\nCGameView::~CGameView()\n{\n\tUnloadResources();\n\n\tdelete m;\n}\n\nvoid CGameView::SetViewport(const SViewPort& vp)\n{\n\tm->ViewCamera.SetViewPort(vp);\n\tm->ViewCamera.SetProjection(m->ViewNear, m->ViewFar, m->ViewFOV);\n}\n\nCObjectManager& CGameView::GetObjectManager() const\n{\n\treturn m->ObjectManager;\n}\n\nCCamera* CGameView::GetCamera()\n{\n\treturn &m->ViewCamera;\n}\n\nCCinemaManager* CGameView::GetCinema()\n{\n\treturn &m->CinemaManager;\n};\n\nCLOSTexture& CGameView::GetLOSTexture()\n{\n\treturn m->LOSTexture;\n}\n\nCTerritoryTexture& CGameView::GetTerritoryTexture()\n{\n\treturn m->TerritoryTexture;\n}\n\nint CGameView::Initialize()\n{\n\tCFG_GET_VAL(\"view.scroll.speed\", m->ViewScrollSpeed);\n\tCFG_GET_VAL(\"view.scroll.speed.modifier\", m->ViewScrollSpeedModifier);\n\tCFG_GET_VAL(\"view.rotate.x.speed\", m->ViewRotateXSpeed);\n\tCFG_GET_VAL(\"view.rotate.x.min\", m->ViewRotateXMin);\n\tCFG_GET_VAL(\"view.rotate.x.max\", m->ViewRotateXMax);\n\tCFG_GET_VAL(\"view.rotate.x.default\", m->ViewRotateXDefault);\n\tCFG_GET_VAL(\"view.rotate.y.speed\", m->ViewRotateYSpeed);\n\tCFG_GET_VAL(\"view.rotate.y.speed.wheel\", m->ViewRotateYSpeedWheel);\n\tCFG_GET_VAL(\"view.rotate.y.default\", m->ViewRotateYDefault);\n\tCFG_GET_VAL(\"view.rotate.speed.modifier\", m->ViewRotateSpeedModifier);\n\tCFG_GET_VAL(\"view.drag.speed\", m->ViewDragSpeed);\n\tCFG_GET_VAL(\"view.zoom.speed\", m->ViewZoomSpeed);\n\tCFG_GET_VAL(\"view.zoom.speed.wheel\", m->ViewZoomSpeedWheel);\n\tCFG_GET_VAL(\"view.zoom.min\", m->ViewZoomMin);\n\tCFG_GET_VAL(\"view.zoom.max\", m->ViewZoomMax);\n\tCFG_GET_VAL(\"view.zoom.default\", m->ViewZoomDefault);\n\tCFG_GET_VAL(\"view.zoom.speed.modifier\", m->ViewZoomSpeedModifier);\n\n\tCFG_GET_VAL(\"joystick.camera.pan.x\", m->JoystickPanX);\n\tCFG_GET_VAL(\"joystick.camera.pan.y\", m->JoystickPanY);\n\tCFG_GET_VAL(\"joystick.camera.rotate.x\", m->JoystickRotateX);\n\tCFG_GET_VAL(\"joystick.camera.rotate.y\", m->JoystickRotateY);\n\tCFG_GET_VAL(\"joystick.camera.zoom.in\", m->JoystickZoomIn);\n\tCFG_GET_VAL(\"joystick.camera.zoom.out\", m->JoystickZoomOut);\n\n\tCFG_GET_VAL(\"view.height.smoothness\", m->HeightSmoothness);\n\tCFG_GET_VAL(\"view.height.min\", m->HeightMin);\n\n\tCFG_GET_VAL(\"view.pos.smoothness\", m->PosX.m_Smoothness);\n\tCFG_GET_VAL(\"view.pos.smoothness\", m->PosY.m_Smoothness);\n\tCFG_GET_VAL(\"view.pos.smoothness\", m->PosZ.m_Smoothness);\n\tCFG_GET_VAL(\"view.zoom.smoothness\", m->Zoom.m_Smoothness);\n\tCFG_GET_VAL(\"view.rotate.x.smoothness\", m->RotateX.m_Smoothness);\n\tCFG_GET_VAL(\"view.rotate.y.smoothness\", m->RotateY.m_Smoothness);\n\n\tCFG_GET_VAL(\"view.near\", m->ViewNear);\n\tCFG_GET_VAL(\"view.far\", m->ViewFar);\n\tCFG_GET_VAL(\"view.fov\", m->ViewFOV);\n\n\t// Convert to radians\n\tm->RotateX.SetValue(DEGTORAD(m->ViewRotateXDefault));\n\tm->RotateY.SetValue(DEGTORAD(m->ViewRotateYDefault));\n\tm->ViewFOV = DEGTORAD(m->ViewFOV);\n\n\treturn 0;\n}\n\n\n\nvoid CGameView::RegisterInit()\n{\n\t// CGameView init\n\tRegMemFun(this, &CGameView::Initialize, L\"CGameView init\", 1);\n\n\t// previously done by CGameView::InitResources\n\tRegMemFun(g_TexMan.GetSingletonPtr(), &CTerrainTextureManager::LoadTerrainTextures, L\"LoadTerrainTextures\", 60);\n\tRegMemFun(g_Renderer.GetSingletonPtr(), &CRenderer::LoadAlphaMaps, L\"LoadAlphaMaps\", 5);\n}\n\n\nvoid CGameView::BeginFrame()\n{\n\tif (m->LockCullCamera == false)\n\t{\n\t\t// Set up cull camera\n\t\tm->CullCamera = m->ViewCamera;\n\t}\n\tg_Renderer.SetSceneCamera(m->ViewCamera, m->CullCamera);\n\n\tCheckLightEnv();\n\n\tm->Game->CachePlayerColors();\n}\n\nvoid CGameView::Render()\n{\n\tg_Renderer.RenderScene(*this);\n}\n\n///////////////////////////////////////////////////////////\n// This callback is part of the Scene interface\n// Submit all objects visible in the given frustum\nvoid CGameView::EnumerateObjects(const CFrustum& frustum, SceneCollector* c)\n{\n\t{\n\tPROFILE3(\"submit terrain\");\n\n\tCTerrain* pTerrain = m->Game->GetWorld()->GetTerrain();\n\tfloat waterHeight = g_Renderer.GetWaterManager()->m_WaterHeight + 0.001f;\n\tconst ssize_t patchesPerSide = pTerrain->GetPatchesPerSide();\n\n\t// find out which patches will be drawn\n\tfor (ssize_t j=0; j<patchesPerSide; ++j)\n\t{\n\t\tfor (ssize_t i=0; i<patchesPerSide; ++i)\n\t\t{\n\t\t\tCPatch* patch=pTerrain->GetPatch(i,j);\t// can't fail\n\n\t\t\t// If the patch is underwater, calculate a bounding box that also contains the water plane\n\t\t\tCBoundingBoxAligned bounds = patch->GetWorldBounds();\n\t\t\tif(bounds[1].Y < waterHeight)\n\t\t\t\tbounds[1].Y = waterHeight;\n\n\t\t\tif (!m->Culling || frustum.IsBoxVisible(bounds))\n\t\t\t\tc->Submit(patch);\n\t\t}\n\t}\n\t}\n\n\tm->Game->GetSimulation2()->RenderSubmit(*c, frustum, m->Culling);\n}\n\n\nvoid CGameView::CheckLightEnv()\n{\n\tif (m->CachedLightEnv == g_LightEnv)\n\t\treturn;\n\n\tif (m->CachedLightEnv.GetLightingModel() != g_LightEnv.GetLightingModel())\n\t\tg_Renderer.MakeShadersDirty();\n\n\tm->CachedLightEnv = g_LightEnv;\n\tCTerrain* pTerrain = m->Game->GetWorld()->GetTerrain();\n\n\tif (!pTerrain)\n\t\treturn;\n\n\tPROFILE(\"update light env\");\n\tpTerrain->MakeDirty(RENDERDATA_UPDATE_COLOR);\n\n\tconst std::vector<CUnit*>& units = m->Game->GetWorld()->GetUnitManager().GetUnits();\n\tfor (size_t i = 0; i < units.size(); ++i)\n\t\tunits[i]->GetModel().SetDirtyRec(RENDERDATA_UPDATE_COLOR);\n}\n\n\nvoid CGameView::UnloadResources()\n{\n\tg_TexMan.UnloadTerrainTextures();\n\tg_Renderer.UnloadAlphaMaps();\n\tg_Renderer.GetWaterManager()->UnloadWaterTextures();\n}\n\nstatic void FocusHeight(CGameViewImpl* m, bool smooth)\n{\n\t/*\n\t\tThe camera pivot height is moved towards ground level.\n\t\tTo prevent excessive zoom when looking over a cliff,\n\t\tthe target ground level is the maximum of the ground level at the camera's near and pivot points.\n\t\tThe ground levels are filtered to achieve smooth camera movement.\n\t\tThe filter radius is proportional to the zoom level.\n\t\tThe camera height is clamped to prevent map penetration.\n\t*/\n\n\tif (!m->ConstrainCamera)\n\t\treturn;\n\n\tCCamera targetCam = m->ViewCamera;\n\tSetupCameraMatrixSmoothRot(m, &targetCam.m_Orientation);\n\n\tconst CVector3D position = targetCam.m_Orientation.GetTranslation();\n\tconst CVector3D forwards = targetCam.m_Orientation.GetIn();\n\n\t// horizontal view radius\n\tconst float radius = sqrtf(forwards.X * forwards.X + forwards.Z * forwards.Z) * m->Zoom.GetSmoothedValue();\n\tconst float near_radius = radius * m->HeightSmoothness;\n\tconst float pivot_radius = radius * m->HeightSmoothness;\n\n\tconst CVector3D nearPoint = position + forwards * m->ViewNear;\n\tconst CVector3D pivotPoint = position + forwards * m->Zoom.GetSmoothedValue();\n\n\tconst float ground = m->Game->GetWorld()->GetTerrain()->GetExactGroundLevel(nearPoint.X, nearPoint.Z);\n\n\t// filter ground levels for smooth camera movement\n\tconst float filtered_near_ground = m->Game->GetWorld()->GetTerrain()->GetFilteredGroundLevel(nearPoint.X, nearPoint.Z, near_radius);\n\tconst float filtered_pivot_ground = m->Game->GetWorld()->GetTerrain()->GetFilteredGroundLevel(pivotPoint.X, pivotPoint.Z, pivot_radius);\n\n\t// filtered maximum visible ground level in view\n\tconst float filtered_ground = std::max(filtered_near_ground, filtered_pivot_ground);\n\n\t// target camera height above pivot point\n\tconst float pivot_height = -forwards.Y * (m->Zoom.GetSmoothedValue() - m->ViewNear);\n\t// minimum camera height above filtered ground level\n\tconst float min_height = (m->HeightMin + ground - filtered_ground);\n\n\tconst float target_height = std::max(pivot_height, min_height);\n\tconst float height = (nearPoint.Y - filtered_ground);\n\tconst float diff = target_height - height;\n\tif (fabsf(diff) < 0.0001f)\n\t\treturn;\n\n\tif (smooth)\n\t{\n\t\tm->PosY.AddSmoothly(diff);\n\t}\n\telse\n\t{\n\t\tm->PosY.Add(diff);\n\t}\n}\n\nCVector3D CGameView::GetSmoothPivot(CCamera& camera) const\n{\n\treturn camera.m_Orientation.GetTranslation() + camera.m_Orientation.GetIn() * m->Zoom.GetSmoothedValue();\n}\n\nvoid CGameView::Update(const float deltaRealTime)\n{\n\t// If camera movement is being handled by the touch-input system,\n\t// then we should stop to avoid conflicting with it\n\tif (g_TouchInput.IsEnabled())\n\t\treturn;\n\n\tif (!g_app_has_focus)\n\t\treturn;\n\n\tif (m->CinemaManager.GetEnabled())\n\t{\n\t\tm->CinemaManager.Update(deltaRealTime);\n\t\treturn;\n\t}\n\n\t// Calculate mouse movement\n\tstatic int mouse_last_x = 0;\n\tstatic int mouse_last_y = 0;\n\tint mouse_dx = g_mouse_x - mouse_last_x;\n\tint mouse_dy = g_mouse_y - mouse_last_y;\n\tmouse_last_x = g_mouse_x;\n\tmouse_last_y = g_mouse_y;\n\n\tif (HotkeyIsPressed(\"camera.rotate.cw\"))\n\t\tm->RotateY.AddSmoothly(m->ViewRotateYSpeed * deltaRealTime);\n\tif (HotkeyIsPressed(\"camera.rotate.ccw\"))\n\t\tm->RotateY.AddSmoothly(-m->ViewRotateYSpeed * deltaRealTime);\n\tif (HotkeyIsPressed(\"camera.rotate.up\"))\n\t\tm->RotateX.AddSmoothly(-m->ViewRotateXSpeed * deltaRealTime);\n\tif (HotkeyIsPressed(\"camera.rotate.down\"))\n\t\tm->RotateX.AddSmoothly(m->ViewRotateXSpeed * deltaRealTime);\n\n\tfloat moveRightward = 0.f;\n\tfloat moveForward = 0.f;\n\n\tif (HotkeyIsPressed(\"camera.pan\"))\n\t{\n\t\tmoveRightward += m->ViewDragSpeed * mouse_dx;\n\t\tmoveForward += m->ViewDragSpeed * -mouse_dy;\n\t}\n\n\tif (g_mouse_active)\n\t{\n\t\tif (g_mouse_x >= g_xres - 2 && g_mouse_x < g_xres)\n\t\t\tmoveRightward += m->ViewScrollSpeed * deltaRealTime;\n\t\telse if (g_mouse_x <= 3 && g_mouse_x >= 0)\n\t\t\tmoveRightward -= m->ViewScrollSpeed * deltaRealTime;\n\n\t\tif (g_mouse_y >= g_yres - 2 && g_mouse_y < g_yres)\n\t\t\tmoveForward -= m->ViewScrollSpeed * deltaRealTime;\n\t\telse if (g_mouse_y <= 3 && g_mouse_y >= 0)\n\t\t\tmoveForward += m->ViewScrollSpeed * deltaRealTime;\n\t}\n\n\tif (HotkeyIsPressed(\"camera.right\"))\n\t\tmoveRightward += m->ViewScrollSpeed * deltaRealTime;\n\tif (HotkeyIsPressed(\"camera.left\"))\n\t\tmoveRightward -= m->ViewScrollSpeed * deltaRealTime;\n\tif (HotkeyIsPressed(\"camera.up\"))\n\t\tmoveForward += m->ViewScrollSpeed * deltaRealTime;\n\tif (HotkeyIsPressed(\"camera.down\"))\n\t\tmoveForward -= m->ViewScrollSpeed * deltaRealTime;\n\n\tif (g_Joystick.IsEnabled())\n\t{\n\t\t// This could all be improved with extra speed and sensitivity settings\n\t\t// (maybe use pow to allow finer control?), and inversion settings\n\n\t\tmoveRightward += g_Joystick.GetAxisValue(m->JoystickPanX) * m->ViewScrollSpeed * deltaRealTime;\n\t\tmoveForward -= g_Joystick.GetAxisValue(m->JoystickPanY) * m->ViewScrollSpeed * deltaRealTime;\n\n\t\tm->RotateX.AddSmoothly(g_Joystick.GetAxisValue(m->JoystickRotateX) * m->ViewRotateXSpeed * deltaRealTime);\n\t\tm->RotateY.AddSmoothly(-g_Joystick.GetAxisValue(m->JoystickRotateY) * m->ViewRotateYSpeed * deltaRealTime);\n\n\t\t// Use a +1 bias for zoom because I want this to work with trigger buttons that default to -1\n\t\tm->Zoom.AddSmoothly((g_Joystick.GetAxisValue(m->JoystickZoomIn) + 1.0f) / 2.0f * m->ViewZoomSpeed * deltaRealTime);\n\t\tm->Zoom.AddSmoothly(-(g_Joystick.GetAxisValue(m->JoystickZoomOut) + 1.0f) / 2.0f * m->ViewZoomSpeed * deltaRealTime);\n\t}\n\n\tif (moveRightward || moveForward)\n\t{\n\t\t// Break out of following mode when the user starts scrolling\n\t\tm->FollowEntity = INVALID_ENTITY;\n\n\t\tfloat s = sin(m->RotateY.GetSmoothedValue());\n\t\tfloat c = cos(m->RotateY.GetSmoothedValue());\n\t\tm->PosX.AddSmoothly(c * moveRightward);\n\t\tm->PosZ.AddSmoothly(-s * moveRightward);\n\t\tm->PosX.AddSmoothly(s * moveForward);\n\t\tm->PosZ.AddSmoothly(c * moveForward);\n\t}\n\n\tif (m->FollowEntity)\n\t{\n\t\tCmpPtr<ICmpPosition> cmpPosition(*(m->Game->GetSimulation2()), m->FollowEntity);\n\t\tCmpPtr<ICmpRangeManager> cmpRangeManager(*(m->Game->GetSimulation2()), SYSTEM_ENTITY);\n\t\tif (cmpPosition && cmpPosition->IsInWorld() &&\n\t\t    cmpRangeManager && cmpRangeManager->GetLosVisibility(m->FollowEntity, m->Game->GetViewedPlayerID()) == ICmpRangeManager::VIS_VISIBLE)\n\t\t{\n\t\t\t// Get the most recent interpolated position\n\t\t\tfloat frameOffset = m->Game->GetSimulation2()->GetLastFrameOffset();\n\t\t\tCMatrix3D transform = cmpPosition->GetInterpolatedTransform(frameOffset);\n\t\t\tCVector3D pos = transform.GetTranslation();\n\n\t\t\tif (m->FollowFirstPerson)\n\t\t\t{\n\t\t\t\tfloat x, z, angle;\n\t\t\t\tcmpPosition->GetInterpolatedPosition2D(frameOffset, x, z, angle);\n\t\t\t\tfloat height = 4.f;\n\t\t\t\tm->ViewCamera.m_Orientation.SetIdentity();\n\t\t\t\tm->ViewCamera.m_Orientation.RotateX((float)M_PI/24.f);\n\t\t\t\tm->ViewCamera.m_Orientation.RotateY(angle);\n\t\t\t\tm->ViewCamera.m_Orientation.Translate(pos.X, pos.Y + height, pos.Z);\n\n\t\t\t\tm->ViewCamera.UpdateFrustum();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// Move the camera to match the unit\n\t\t\t\tCCamera targetCam = m->ViewCamera;\n\t\t\t\tSetupCameraMatrixSmoothRot(m, &targetCam.m_Orientation);\n\n\t\t\t\tCVector3D pivot = GetSmoothPivot(targetCam);\n\t\t\t\tCVector3D delta = pos - pivot;\n\t\t\t\tm->PosX.AddSmoothly(delta.X);\n\t\t\t\tm->PosY.AddSmoothly(delta.Y);\n\t\t\t\tm->PosZ.AddSmoothly(delta.Z);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// The unit disappeared (died or garrisoned etc), so stop following it\n\t\t\tm->FollowEntity = INVALID_ENTITY;\n\t\t}\n\t}\n\n\tif (HotkeyIsPressed(\"camera.zoom.in\"))\n\t\tm->Zoom.AddSmoothly(-m->ViewZoomSpeed * deltaRealTime);\n\tif (HotkeyIsPressed(\"camera.zoom.out\"))\n\t\tm->Zoom.AddSmoothly(m->ViewZoomSpeed * deltaRealTime);\n\n\tif (m->ConstrainCamera)\n\t\tm->Zoom.ClampSmoothly(m->ViewZoomMin, m->ViewZoomMax);\n\n\tfloat zoomDelta = -m->Zoom.Update(deltaRealTime);\n\tif (zoomDelta)\n\t{\n\t\tCVector3D forwards = m->ViewCamera.m_Orientation.GetIn();\n\t\tm->PosX.AddSmoothly(forwards.X * zoomDelta);\n\t\tm->PosY.AddSmoothly(forwards.Y * zoomDelta);\n\t\tm->PosZ.AddSmoothly(forwards.Z * zoomDelta);\n\t}\n\n\tif (m->ConstrainCamera)\n\t\tm->RotateX.ClampSmoothly(DEGTORAD(m->ViewRotateXMin), DEGTORAD(m->ViewRotateXMax));\n\n\tFocusHeight(m, true);\n\n\t// Ensure the ViewCamera focus is inside the map with the chosen margins\n\t// if not so - apply margins to the camera\n\tif (m->ConstrainCamera)\n\t{\n\t\tCCamera targetCam = m->ViewCamera;\n\t\tSetupCameraMatrixSmoothRot(m, &targetCam.m_Orientation);\n\n\t\tCTerrain* pTerrain = m->Game->GetWorld()->GetTerrain();\n\n\t\tCVector3D pivot = GetSmoothPivot(targetCam);\n\t\tCVector3D delta = targetCam.m_Orientation.GetTranslation() - pivot;\n\n\t\tCVector3D desiredPivot = pivot;\n\n\t\tCmpPtr<ICmpRangeManager> cmpRangeManager(*(m->Game->GetSimulation2()), SYSTEM_ENTITY);\n\t\tif (cmpRangeManager && cmpRangeManager->GetLosCircular())\n\t\t{\n\t\t\t// Clamp to a circular region around the center of the map\n\t\t\tfloat r = pTerrain->GetMaxX() / 2;\n\t\t\tCVector3D center(r, desiredPivot.Y, r);\n\t\t\tfloat dist = (desiredPivot - center).Length();\n\t\t\tif (dist > r - CAMERA_EDGE_MARGIN)\n\t\t\t\tdesiredPivot = center + (desiredPivot - center).Normalized() * (r - CAMERA_EDGE_MARGIN);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Clamp to the square edges of the map\n\t\t\tdesiredPivot.X = Clamp(desiredPivot.X, pTerrain->GetMinX() + CAMERA_EDGE_MARGIN, pTerrain->GetMaxX() - CAMERA_EDGE_MARGIN);\n\t\t\tdesiredPivot.Z = Clamp(desiredPivot.Z, pTerrain->GetMinZ() + CAMERA_EDGE_MARGIN, pTerrain->GetMaxZ() - CAMERA_EDGE_MARGIN);\n\t\t}\n\n\t\t// Update the position so that pivot is within the margin\n\t\tm->PosX.SetValueSmoothly(desiredPivot.X + delta.X);\n\t\tm->PosZ.SetValueSmoothly(desiredPivot.Z + delta.Z);\n\t}\n\n\tm->PosX.Update(deltaRealTime);\n\tm->PosY.Update(deltaRealTime);\n\tm->PosZ.Update(deltaRealTime);\n\n\t// Handle rotation around the Y (vertical) axis\n\t{\n\t\tCCamera targetCam = m->ViewCamera;\n\t\tSetupCameraMatrixSmooth(m, &targetCam.m_Orientation);\n\n\t\tfloat rotateYDelta = m->RotateY.Update(deltaRealTime);\n\t\tif (rotateYDelta)\n\t\t{\n\t\t\t// We've updated RotateY, and need to adjust Pos so that it's still\n\t\t\t// facing towards the original focus point (the terrain in the center\n\t\t\t// of the screen).\n\n\t\t\tCVector3D upwards(0.0f, 1.0f, 0.0f);\n\n\t\t\tCVector3D pivot = GetSmoothPivot(targetCam);\n\t\t\tCVector3D delta = targetCam.m_Orientation.GetTranslation() - pivot;\n\n\t\t\tCQuaternion q;\n\t\t\tq.FromAxisAngle(upwards, rotateYDelta);\n\t\t\tCVector3D d = q.Rotate(delta) - delta;\n\n\t\t\tm->PosX.Add(d.X);\n\t\t\tm->PosY.Add(d.Y);\n\t\t\tm->PosZ.Add(d.Z);\n\t\t}\n\t}\n\n\t// Handle rotation around the X (sideways, relative to camera) axis\n\t{\n\t\tCCamera targetCam = m->ViewCamera;\n\t\tSetupCameraMatrixSmooth(m, &targetCam.m_Orientation);\n\n\t\tfloat rotateXDelta = m->RotateX.Update(deltaRealTime);\n\t\tif (rotateXDelta)\n\t\t{\n\t\t\tCVector3D rightwards = targetCam.m_Orientation.GetLeft() * -1.0f;\n\n\t\t\tCVector3D pivot = GetSmoothPivot(targetCam);\n\t\t\tCVector3D delta = targetCam.m_Orientation.GetTranslation() - pivot;\n\n\t\t\tCQuaternion q;\n\t\t\tq.FromAxisAngle(rightwards, rotateXDelta);\n\t\t\tCVector3D d = q.Rotate(delta) - delta;\n\n\t\t\tm->PosX.Add(d.X);\n\t\t\tm->PosY.Add(d.Y);\n\t\t\tm->PosZ.Add(d.Z);\n\t\t}\n\t}\n\n\t/* This is disabled since it doesn't seem necessary:\n\n\t// Ensure the camera's near point is never inside the terrain\n\tif (m->ConstrainCamera)\n\t{\n\t\tCMatrix3D target;\n\t\ttarget.SetIdentity();\n\t\ttarget.RotateX(m->RotateX.GetValue());\n\t\ttarget.RotateY(m->RotateY.GetValue());\n\t\ttarget.Translate(m->PosX.GetValue(), m->PosY.GetValue(), m->PosZ.GetValue());\n\n\t\tCVector3D nearPoint = target.GetTranslation() + target.GetIn() * defaultNear;\n\t\tfloat ground = m->Game->GetWorld()->GetTerrain()->GetExactGroundLevel(nearPoint.X, nearPoint.Z);\n\t\tfloat limit = ground + 16.f;\n\t\tif (nearPoint.Y < limit)\n\t\t\tm->PosY.AddSmoothly(limit - nearPoint.Y);\n\t}\n\t*/\n\n\tm->RotateY.Wrap(-(float)M_PI, (float)M_PI);\n\n\t// Update the camera matrix\n\tm->ViewCamera.SetProjection(m->ViewNear, m->ViewFar, m->ViewFOV);\n\tSetupCameraMatrixSmooth(m, &m->ViewCamera.m_Orientation);\n\tm->ViewCamera.UpdateFrustum();\n}\n\nfloat CGameView::GetCameraX()\n{\n\tCCamera targetCam = m->ViewCamera;\n\tCVector3D pivot = GetSmoothPivot(targetCam);\n\treturn pivot.X;\n}\n\nfloat CGameView::GetCameraZ()\n{\n\tCCamera targetCam = m->ViewCamera;\n\tCVector3D pivot = GetSmoothPivot(targetCam);\n\treturn pivot.Z;\n}\n\nfloat CGameView::GetCameraPosX()\n{\n\treturn m->PosX.GetValue();\n}\n\nfloat CGameView::GetCameraPosY()\n{\n\treturn m->PosY.GetValue();\n}\n\nfloat CGameView::GetCameraPosZ()\n{\n\treturn m->PosZ.GetValue();\n}\n\nfloat CGameView::GetCameraRotX()\n{\n\treturn m->RotateX.GetValue();\n}\n\nfloat CGameView::GetCameraRotY()\n{\n\treturn m->RotateY.GetValue();\n}\n\nfloat CGameView::GetCameraZoom()\n{\n\treturn m->Zoom.GetValue();\n}\n\nvoid CGameView::SetCamera(CVector3D Pos, float RotX, float RotY, float zoom)\n{\n\tm->PosX.SetValue(Pos.X);\n\tm->PosY.SetValue(Pos.Y);\n\tm->PosZ.SetValue(Pos.Z);\n\tm->RotateX.SetValue(RotX);\n\tm->RotateY.SetValue(RotY);\n\tm->Zoom.SetValue(zoom);\n\n\tFocusHeight(m, false);\n\n\tSetupCameraMatrixNonSmooth(m, &m->ViewCamera.m_Orientation);\n\tm->ViewCamera.UpdateFrustum();\n\n\t// Break out of following mode so the camera really moves to the target\n\tm->FollowEntity = INVALID_ENTITY;\n}\n\nvoid CGameView::MoveCameraTarget(const CVector3D& target)\n{\n\t// Maintain the same orientation and level of zoom, if we can\n\t// (do this by working out the point the camera is looking at, saving\n\t//  the difference between that position and the camera point, and restoring\n\t//  that difference to our new target)\n\n\tCCamera targetCam = m->ViewCamera;\n\tSetupCameraMatrixNonSmooth(m, &targetCam.m_Orientation);\n\n\tCVector3D pivot = GetSmoothPivot(targetCam);\n\tCVector3D delta = target - pivot;\n\n\tm->PosX.SetValueSmoothly(delta.X + m->PosX.GetValue());\n\tm->PosZ.SetValueSmoothly(delta.Z + m->PosZ.GetValue());\n\n\tFocusHeight(m, false);\n\n\t// Break out of following mode so the camera really moves to the target\n\tm->FollowEntity = INVALID_ENTITY;\n}\n\nvoid CGameView::ResetCameraTarget(const CVector3D& target)\n{\n\tCMatrix3D orientation;\n\torientation.SetIdentity();\n\torientation.RotateX(DEGTORAD(m->ViewRotateXDefault));\n\torientation.RotateY(DEGTORAD(m->ViewRotateYDefault));\n\n\tCVector3D delta = orientation.GetIn() * m->ViewZoomDefault;\n\tm->PosX.SetValue(target.X - delta.X);\n\tm->PosY.SetValue(target.Y - delta.Y);\n\tm->PosZ.SetValue(target.Z - delta.Z);\n\tm->RotateX.SetValue(DEGTORAD(m->ViewRotateXDefault));\n\tm->RotateY.SetValue(DEGTORAD(m->ViewRotateYDefault));\n\tm->Zoom.SetValue(m->ViewZoomDefault);\n\n\tFocusHeight(m, false);\n\n\tSetupCameraMatrixSmooth(m, &m->ViewCamera.m_Orientation);\n\tm->ViewCamera.UpdateFrustum();\n\n\t// Break out of following mode so the camera really moves to the target\n\tm->FollowEntity = INVALID_ENTITY;\n}\n\nvoid CGameView::ResetCameraAngleZoom()\n{\n\tCCamera targetCam = m->ViewCamera;\n\tSetupCameraMatrixNonSmooth(m, &targetCam.m_Orientation);\n\n\t// Compute the zoom adjustment to get us back to the default\n\tCVector3D forwards = targetCam.m_Orientation.GetIn();\n\n\tCVector3D pivot = GetSmoothPivot(targetCam);\n\tCVector3D delta = pivot - targetCam.m_Orientation.GetTranslation();\n\tfloat dist = delta.Dot(forwards);\n\tm->Zoom.AddSmoothly(m->ViewZoomDefault - dist);\n\n\t// Reset orientations to default\n\tm->RotateX.SetValueSmoothly(DEGTORAD(m->ViewRotateXDefault));\n\tm->RotateY.SetValueSmoothly(DEGTORAD(m->ViewRotateYDefault));\n}\n\nvoid CGameView::CameraFollow(entity_id_t entity, bool firstPerson)\n{\n\tm->FollowEntity = entity;\n\tm->FollowFirstPerson = firstPerson;\n}\n\nentity_id_t CGameView::GetFollowedEntity()\n{\n\treturn m->FollowEntity;\n}\n\nfloat CGameView::GetNear() const\n{\n\treturn m->ViewNear;\n}\n\nfloat CGameView::GetFar() const\n{\n\treturn m->ViewFar;\n}\n\nfloat CGameView::GetFOV() const\n{\n\treturn m->ViewFOV;\n}\n\nvoid CGameView::SetCameraProjection()\n{\n\tm->ViewCamera.SetProjection(m->ViewNear, m->ViewFar, m->ViewFOV);\n}\n\nInReaction game_view_handler(const SDL_Event_* ev)\n{\n\t// put any events that must be processed even if inactive here\n\tif(!g_app_has_focus || !g_Game || !g_Game->IsGameStarted())\n\t\treturn IN_PASS;\n\n\tCGameView *pView=g_Game->GetView();\n\n\treturn pView->HandleEvent(ev);\n}\n\nInReaction CGameView::HandleEvent(const SDL_Event_* ev)\n{\n\tswitch(ev->ev.type)\n\t{\n\n\tcase SDL_HOTKEYDOWN:\n\t\tstd::string hotkey = static_cast<const char*>(ev->ev.user.data1);\n\n\t\tif (hotkey == \"wireframe\")\n\t\t{\n\t\t\tif (g_XmppClient && g_rankedGame == true)\n\t\t\t\tbreak;\n\t\t\telse if (g_Renderer.GetModelRenderMode() == SOLID)\n\t\t\t{\n\t\t\t\tg_Renderer.SetTerrainRenderMode(EDGED_FACES);\n\t\t\t\tg_Renderer.SetModelRenderMode(EDGED_FACES);\n\t\t\t}\n\t\t\telse if (g_Renderer.GetModelRenderMode() == EDGED_FACES)\n\t\t\t{\n\t\t\t\tg_Renderer.SetTerrainRenderMode(WIREFRAME);\n\t\t\t\tg_Renderer.SetModelRenderMode(WIREFRAME);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tg_Renderer.SetTerrainRenderMode(SOLID);\n\t\t\t\tg_Renderer.SetModelRenderMode(SOLID);\n\t\t\t}\n\t\t\treturn IN_HANDLED;\n\t\t}\n\t\t// Mouse wheel must be treated using events instead of polling,\n\t\t// because SDL auto-generates a sequence of mousedown/mouseup events\n\t\t// and we never get to see the \"down\" state inside Update().\n\t\telse if (hotkey == \"camera.zoom.wheel.in\")\n\t\t{\n\t\t\tm->Zoom.AddSmoothly(-m->ViewZoomSpeedWheel);\n\t\t\treturn IN_HANDLED;\n\t\t}\n\t\telse if (hotkey == \"camera.zoom.wheel.out\")\n\t\t{\n\t\t\tm->Zoom.AddSmoothly(m->ViewZoomSpeedWheel);\n\t\t\treturn IN_HANDLED;\n\t\t}\n\t\telse if (hotkey == \"camera.rotate.wheel.cw\")\n\t\t{\n\t\t\tm->RotateY.AddSmoothly(m->ViewRotateYSpeedWheel);\n\t\t\treturn IN_HANDLED;\n\t\t}\n\t\telse if (hotkey == \"camera.rotate.wheel.ccw\")\n\t\t{\n\t\t\tm->RotateY.AddSmoothly(-m->ViewRotateYSpeedWheel);\n\t\t\treturn IN_HANDLED;\n\t\t}\n\t\telse if (hotkey == \"camera.scroll.speed.increase\")\n\t\t{\n\t\t\tm->ViewScrollSpeed *= m->ViewScrollSpeedModifier;\n\t\t\treturn IN_HANDLED;\n\t\t}\n\t\telse if (hotkey == \"camera.scroll.speed.decrease\")\n\t\t{\n\t\t\tm->ViewScrollSpeed /= m->ViewScrollSpeedModifier;\n\t\t\treturn IN_HANDLED;\n\t\t}\n\t\telse if (hotkey == \"camera.rotate.speed.increase\")\n\t\t{\n\t\t\tm->ViewRotateXSpeed *= m->ViewRotateSpeedModifier;\n\t\t\tm->ViewRotateYSpeed *= m->ViewRotateSpeedModifier;\n\t\t\treturn IN_HANDLED;\n\t\t}\n\t\telse if (hotkey == \"camera.rotate.speed.decrease\")\n\t\t{\n\t\t\tm->ViewRotateXSpeed /= m->ViewRotateSpeedModifier;\n\t\t\tm->ViewRotateYSpeed /= m->ViewRotateSpeedModifier;\n\t\t\treturn IN_HANDLED;\n\t\t}\n\t\telse if (hotkey == \"camera.zoom.speed.increase\")\n\t\t{\n\t\t\tm->ViewZoomSpeed *= m->ViewZoomSpeedModifier;\n\t\t\treturn IN_HANDLED;\n\t\t}\n\t\telse if (hotkey == \"camera.zoom.speed.decrease\")\n\t\t{\n\t\t\tm->ViewZoomSpeed /= m->ViewZoomSpeedModifier;\n\t\t\treturn IN_HANDLED;\n\t\t}\n\t\telse if (hotkey == \"camera.reset\")\n\t\t{\n\t\t\tResetCameraAngleZoom();\n\t\t\treturn IN_HANDLED;\n\t\t}\n\t}\n\n\treturn IN_PASS;\n}\n"
  },
  {
    "path": "fpsgame/graphics/GameView.h",
    "content": "/* Copyright (C) 2010 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef INCLUDED_GAMEVIEW\n#define INCLUDED_GAMEVIEW\n\n#include \"renderer/Scene.h\"\n#include \"simulation2/system/Entity.h\"\n\n#include \"lib/input.h\" // InReaction - can't forward-declare enum\n\nclass CGame;\nclass CObjectManager;\nclass CCamera;\nclass CCinemaManager;\nclass CVector3D;\nstruct SViewPort;\n\nclass JSObject;\n\nclass CGameViewImpl;\n\nclass CGameView : private Scene\n{\n\tNONCOPYABLE(CGameView);\nprivate:\n\tCGameViewImpl* m;\n\n\t// Check whether lighting environment has changed and update vertex data if necessary\n\tvoid CheckLightEnv();\n\npublic:\n\t//BEGIN: Implementation of Scene\n\tvirtual void EnumerateObjects(const CFrustum& frustum, SceneCollector* c);\n\tvirtual CLOSTexture& GetLOSTexture();\n\tvirtual CTerritoryTexture& GetTerritoryTexture();\n\t//END: Implementation of Scene\n\nprivate:\n\t// InitResources(): Load all graphics resources (textures, actor objects and\n\t// alpha maps) required by the game\n\t//void InitResources();\n\n\t// UnloadResources(): Unload all graphics resources loaded by InitResources\n\tvoid UnloadResources();\n\npublic:\n\tCGameView(CGame *pGame);\n\t~CGameView();\n\n\tvoid SetViewport(const SViewPort& vp);\n\n\tvoid RegisterInit();\n\tint Initialize();\n\n\tCObjectManager& GetObjectManager() const;\n\n\t/**\n\t * Updates all the view information (i.e. rotate camera, scroll, whatever). This will *not* change any \n\t * World information - only the *presentation*.\n\t * \n\t * @param deltaRealTime Elapsed real time since the last frame.\n\t */\n\tvoid Update(const float deltaRealTime);\n\n\tvoid BeginFrame();\n\tvoid Render();\n\n\tInReaction HandleEvent(const SDL_Event_* ev);\n\n\tfloat GetCameraX();\n\tfloat GetCameraZ();\n\tfloat GetCameraPosX();\n\tfloat GetCameraPosY();\n\tfloat GetCameraPosZ();\n\tfloat GetCameraRotX();\n\tfloat GetCameraRotY();\n\tfloat GetCameraZoom();\n\tvoid SetCamera(CVector3D Pos, float RotX, float RotY, float Zoom);\n\tvoid MoveCameraTarget(const CVector3D& target);\n\tvoid ResetCameraTarget(const CVector3D& target);\n\tvoid ResetCameraAngleZoom();\n\tvoid CameraFollow(entity_id_t entity, bool firstPerson);\n\tentity_id_t GetFollowedEntity();\n\n\tCVector3D GetSmoothPivot(CCamera &camera) const;\n\n\tfloat GetNear() const;\n\tfloat GetFar() const;\n\tfloat GetFOV() const;\n\t\n\t#define DECLARE_BOOLEAN_SETTING(NAME) \\\n\tbool Get##NAME##Enabled(); \\\n\tvoid Set##NAME##Enabled(bool Enabled);\n\n\tDECLARE_BOOLEAN_SETTING(Culling);\n\tDECLARE_BOOLEAN_SETTING(LockCullCamera);\n\tDECLARE_BOOLEAN_SETTING(ConstrainCamera);\n\n\t#undef DECLARE_BOOLEAN_SETTING\n\n\t// Set projection of current camera using near, far, and FOV values\n\tvoid SetCameraProjection();\n\n\tCCamera *GetCamera();\n\tCCinemaManager* GetCinema();\n\n\tJSObject* GetScript();\n};\nextern InReaction game_view_handler(const SDL_Event_* ev);\n#endif\n"
  },
  {
    "path": "fpsgame/graphics/HFTracer.cpp",
    "content": "/* Copyright (C) 2014 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n/*\n * Determine intersection of rays with a heightfield.\n */\n\n#include \"precompiled.h\"\n\n#include \"HFTracer.h\"\n\n#include \"graphics/Patch.h\"\n#include \"graphics/Terrain.h\"\n#include \"maths/BoundingBoxAligned.h\"\n#include \"maths/MathUtil.h\"\n#include \"maths/Vector3D.h\"\n\n#include <cfloat>\n\n// To cope well with points that are slightly off the edge of the map,\n// we act as if there's an N-tile margin around the edges of the heightfield.\n// (N shouldn't be too huge else it'll hurt performance a little when\n// RayIntersect loops through it all.)\n// CTerrain::CalcPosition implements clamp-to-edge behaviour so the tracer\n// will have that behaviour.\nstatic const int MARGIN_SIZE = 64;\n\n///////////////////////////////////////////////////////////////////////////////\n// CHFTracer constructor\nCHFTracer::CHFTracer(CTerrain *pTerrain):\n\tm_pTerrain(pTerrain),\n\tm_Heightfield(m_pTerrain->GetHeightMap()),\n\tm_MapSize(m_pTerrain->GetVerticesPerSide()),\n\tm_CellSize((float)TERRAIN_TILE_SIZE),\n\tm_HeightScale(HEIGHT_SCALE)\n{\n}\n\n\n///////////////////////////////////////////////////////////////////////////////\n// RayTriIntersect: intersect a ray with triangle defined by vertices\n// v0,v1,v2; return true if ray hits triangle at distance less than dist,\n// or false otherwise\nstatic bool RayTriIntersect(const CVector3D& v0, const CVector3D& v1, const CVector3D& v2,\n\t\t\t\t\t\t\t\tconst CVector3D& origin, const CVector3D& dir, float& dist)\n{\n\tconst float EPSILON=0.00001f;\n\n\t// calculate edge vectors\n\tCVector3D edge0=v1-v0;\n\tCVector3D edge1=v2-v0;\n\n    // begin calculating determinant - also used to calculate U parameter\n    CVector3D pvec=dir.Cross(edge1);\n\n    // if determinant is near zero, ray lies in plane of triangle\n    float det = edge0.Dot(pvec);\n    if (fabs(det)<EPSILON)\n        return false;\n\n    float inv_det = 1.0f/det;\n\n    // calculate vector from vert0 to ray origin\n    CVector3D tvec=origin-v0;\n\n    // calculate U parameter, test bounds\n    float u=tvec.Dot(pvec)*inv_det;\n    if (u<-0.01f || u>1.01f)\n        return false;\n\n    // prepare to test V parameter\n    CVector3D qvec=tvec.Cross(edge0);\n\n    // calculate V parameter and test bounds\n    float v=dir.Dot(qvec)*inv_det;\n    if (v<0.0f || u+v>1.0f)\n        return false;\n\n    // calculate distance to intersection point from ray origin\n    float d=edge1.Dot(qvec)*inv_det;\n    if (d>=0 && d<dist) {\n        dist=d;\n        return true;\n    }\n\n    return false;\n}\n\n///////////////////////////////////////////////////////////////////////////////\n// CellIntersect: test if ray intersects either of the triangles in the given\n// cell - return hit result, and distance to hit, if hit occurred\nbool CHFTracer::CellIntersect(int cx, int cz, const CVector3D& origin, const CVector3D& dir, float& dist) const\n{\n\tbool res=false;\n\n\t// get vertices for this cell\n\tCVector3D vpos[4];\n\tm_pTerrain->CalcPosition(cx,cz,vpos[0]);\n\tm_pTerrain->CalcPosition(cx+1,cz,vpos[1]);\n\tm_pTerrain->CalcPosition(cx+1,cz+1,vpos[2]);\n\tm_pTerrain->CalcPosition(cx,cz+1,vpos[3]);\n\n\tdist=1.0e30f;\n\tif (RayTriIntersect(vpos[0],vpos[1],vpos[2],origin,dir,dist)) {\n\t\tres=true;\n\t}\n\n\tif (RayTriIntersect(vpos[0],vpos[2],vpos[3],origin,dir,dist)) {\n\t\tres=true;\n\t}\n\n\treturn res;\n}\n\n///////////////////////////////////////////////////////////////////////////////\n// RayIntersect: intersect ray with this heightfield; return true if\n// intersection occurs (and fill in grid coordinates of intersection), or false\n// otherwise\nbool CHFTracer::RayIntersect(const CVector3D& origin, const CVector3D& dir, int& x, int& z, CVector3D& ipt) const\n{\n\t// If the map is empty (which should never happen),\n\t// return early before we crash when reading zero-sized heightmaps\n\tif (!m_MapSize)\n\t{\n\t\tdebug_warn(L\"CHFTracer::RayIntersect called with zero-size map\");\n\t\treturn false;\n\t}\n\n\t// intersect first against bounding box\n\tCBoundingBoxAligned bound;\n\tbound[0] = CVector3D(-MARGIN_SIZE * m_CellSize, 0, -MARGIN_SIZE * m_CellSize);\n\tbound[1] = CVector3D((m_MapSize + MARGIN_SIZE) * m_CellSize, 65535 * m_HeightScale, (m_MapSize + MARGIN_SIZE) * m_CellSize);\n\n\tfloat tmin,tmax;\n\tif (!bound.RayIntersect(origin,dir,tmin,tmax)) {\n\t\t// ray missed world bounds; no intersection\n\t\treturn false;\n\t}\n\n\t// project origin onto grid, if necessary, to get starting point for traversal\n\tCVector3D traversalPt;\n\tif (tmin>0) {\n\t\ttraversalPt=origin+dir*tmin;\n\t} else {\n\t\ttraversalPt=origin;\n\t}\n\n\t// setup traversal variables\n\tint sx=dir.X<0 ? -1 : 1;\n\tint sz=dir.Z<0 ? -1 : 1;\n\n\tfloat invCellSize=1.0f/float(m_CellSize);\n\n\tfloat fcx=traversalPt.X*invCellSize;\n\tint cx=(int)floor(fcx);\n\n\tfloat fcz=traversalPt.Z*invCellSize;\n\tint cz=(int)floor(fcz);\n\n\tfloat invdx = 1.0e20f;\n\tfloat invdz = 1.0e20f;\n\n\tif (fabs(dir.X) > 1.0e-20)\n\t\tinvdx = float(1.0/fabs(dir.X));\n\tif (fabs(dir.Z) > 1.0e-20)\n\t\tinvdz = float(1.0/fabs(dir.Z));\n\n\tdo {\n\t\t// test current cell\n\t\tif (cx >= -MARGIN_SIZE && cx < int(m_MapSize + MARGIN_SIZE - 1) && cz >= -MARGIN_SIZE && cz < int(m_MapSize + MARGIN_SIZE - 1))\n\t\t{\n\t\t\tfloat dist;\n\n\t\t\tif (CellIntersect(cx,cz,origin,dir,dist)) {\n\t\t\t\tx=cx;\n\t\t\t\tz=cz;\n\t\t\t\tipt=origin+dir*dist;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Degenerate case: y close to zero\n\t\t\t// catch travelling off the map\n\t\t\tif ((cx < -MARGIN_SIZE) && (sx < 0))\n\t\t\t\treturn false;\n\t\t\tif ((cx >= (int)(m_MapSize + MARGIN_SIZE - 1)) && (sx > 0))\n\t\t\t\treturn false;\n\t\t\tif ((cz < -MARGIN_SIZE) && (sz < 0))\n\t\t\t\treturn false;\n\t\t\tif ((cz >= (int)(m_MapSize + MARGIN_SIZE - 1)) && (sz > 0))\n\t\t\t\treturn false;\n\t\t}\n\n\t\t// get coords of current cell\n\t\tfcx=traversalPt.X*invCellSize;\n\t\tfcz=traversalPt.Z*invCellSize;\n\n\t\t// get distance to next cell in x,z\n\t\tfloat dx=(sx==-1) ? fcx-float(cx) : 1-(fcx-float(cx));\n\t\tdx*=invdx;\n\t\tfloat dz=(sz==-1) ? fcz-float(cz) : 1-(fcz-float(cz));\n\t\tdz*=invdz;\n\n\t\t// advance ..\n\t\tfloat dist;\n\t\tif (dx<dz) {\n\t\t\tcx+=sx;\n\t\t\tdist=dx;\n\t\t} else {\n\t\t\tcz+=sz;\n\t\t\tdist=dz;\n\t\t}\n\n\t\ttraversalPt+=dir*dist;\n\t} while (traversalPt.Y>=0);\n\n\t// fell off end of heightmap with no intersection; return a miss\n\treturn false;\n}\n\nstatic bool TestTile(u16* heightmap, int stride, int i, int j, const CVector3D& pos, const CVector3D& dir, CVector3D& isct)\n{\n\tu16 y00 = heightmap[i + j*stride];\n\tu16 y10 = heightmap[i+1 + j*stride];\n\tu16 y01 = heightmap[i + (j+1)*stride];\n\tu16 y11 = heightmap[i+1 + (j+1)*stride];\n\n\tCVector3D p00(    i * TERRAIN_TILE_SIZE, y00 * HEIGHT_SCALE,     j * TERRAIN_TILE_SIZE);\n\tCVector3D p10((i+1) * TERRAIN_TILE_SIZE, y10 * HEIGHT_SCALE,     j * TERRAIN_TILE_SIZE);\n\tCVector3D p01(    i * TERRAIN_TILE_SIZE, y01 * HEIGHT_SCALE, (j+1) * TERRAIN_TILE_SIZE);\n\tCVector3D p11((i+1) * TERRAIN_TILE_SIZE, y11 * HEIGHT_SCALE, (j+1) * TERRAIN_TILE_SIZE);\n\n\tint mid1 = y00+y11;\n\tint mid2 = y01+y10;\n\tint triDir = (mid1 < mid2);\n\n\tfloat dist = FLT_MAX;\n\n\tif (triDir)\n\t{\n\t\tif (RayTriIntersect(p00, p10, p01, pos, dir, dist) || // lower-left triangle\n\t\t    RayTriIntersect(p11, p01, p10, pos, dir, dist))   // upper-right triangle\n\t\t{\n\t\t\tisct = pos + dir * dist;\n\t\t\treturn true;\n\t\t}\n\t}\n\telse\n\t{\n\t\tif (RayTriIntersect(p00, p11, p01, pos, dir, dist) || // upper-left triangle\n\t\t    RayTriIntersect(p00, p10, p11, pos, dir, dist))   // lower-right triangle\n\t\t{\n\t\t\tisct = pos + dir * dist;\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\nbool CHFTracer::PatchRayIntersect(CPatch* patch, const CVector3D& origin, const CVector3D& dir, CVector3D* out)\n{\n\t// (TODO: This largely duplicates RayIntersect - some refactoring might be\n\t// nice in the future.)\n\n\t// General approach:\n\t// Given the ray defined by origin + dir * t, we increase t until it\n\t// enters the patch's bounding box. The x,z coordinates identify which\n\t// tile it is currently above/below. Do an intersection test vs the tile's\n\t// two triangles. If it doesn't hit, do a 2D line rasterisation to find\n\t// the next tiles the ray will pass through, and test each of them.\n\n\t// Start by jumping to the point where the ray enters the bounding box\n\tCBoundingBoxAligned bound = patch->GetWorldBounds();\n\tfloat tmin, tmax;\n\tif (!bound.RayIntersect(origin, dir, tmin, tmax))\n\t{\n\t\t// Ray missed patch; no intersection\n\t\treturn false;\n\t}\n\n\tint heightmapStride = patch->m_Parent->GetVerticesPerSide();\n\n\t// Get heightmap, offset to start at this patch\n\tu16* heightmap = patch->m_Parent->GetHeightMap() +\n\t\t\tpatch->m_X * PATCH_SIZE +\n\t\t\tpatch->m_Z * PATCH_SIZE * heightmapStride;\n\n\t// Get patch-space position of ray origin and bbox entry point\n\tCVector3D patchPos(\n\t\t\tpatch->m_X * PATCH_SIZE * TERRAIN_TILE_SIZE,\n\t\t\t0.0f,\n\t\t\tpatch->m_Z * PATCH_SIZE * TERRAIN_TILE_SIZE);\n\tCVector3D originPatch = origin - patchPos;\n\tCVector3D entryPatch = originPatch + dir * tmin;\n\n\t// We want to do a simple 2D line rasterisation (with the 3D ray projected\n\t// down onto the Y plane). That will tell us which cells are intersected\n\t// in 2D dimensions, then we can do a more precise 3D intersection test.\n\t//\n\t// WLOG, assume the ray has direction dir.x > 0, dir.z > 0, and starts in\n\t// cell (i,j). The next cell intersecting the line must be either (i+1,j)\n\t// or (i,j+1). To tell which, just check whether the point (i+1,j+1) is\n\t// above or below the ray. Advance into that cell and repeat.\n\t//\n\t// (If the ray passes precisely through (i+1,j+1), we can pick either.\n\t// If the ray is parallel to Y, only the first cell matters, then we can\n\t// carry on rasterising in any direction (a bit of a waste of time but\n\t// should be extremely rare, and it's safe and simple).)\n\n\t// Work out which tile we're starting in\n\tint i = clamp((int)(entryPatch.X / TERRAIN_TILE_SIZE), 0, (int)PATCH_SIZE-1);\n\tint j = clamp((int)(entryPatch.Z / TERRAIN_TILE_SIZE), 0, (int)PATCH_SIZE-1);\n\n\t// Work out which direction the ray is going in\n\tint di = (dir.X >= 0 ? 1 : 0);\n\tint dj = (dir.Z >= 0 ? 1 : 0);\n\n\tdo\n\t{\n\t\tCVector3D isct;\n\t\tif (TestTile(heightmap, heightmapStride, i, j, originPatch, dir, isct))\n\t\t{\n\t\t\tif (out)\n\t\t\t\t*out = isct + patchPos;\n\t\t\treturn true;\n\t\t}\n\n\t\t// Get the vertex between the two possible next cells\n\t\tfloat nx = (i + di) * (int)TERRAIN_TILE_SIZE;\n\t\tfloat nz = (j + dj) * (int)TERRAIN_TILE_SIZE;\n\n\t\t// Test which side of the ray the vertex is on, and advance into the\n\t\t// appropriate cell, using a test that works for all 4 combinations\n\t\t// of di,dj\n\t\tfloat dot = dir.Z * (nx - originPatch.X) - dir.X * (nz - originPatch.Z);\n\t\tif ((di == dj) == (dot > 0.0f))\n\t\t\tj += dj*2-1;\n\t\telse\n\t\t\ti += di*2-1;\n\t}\n\twhile (i >= 0 && j >= 0 && i < PATCH_SIZE && j < PATCH_SIZE);\n\n\t// Ran off the edge of the patch, so no intersection\n\treturn false;\n}\n"
  },
  {
    "path": "fpsgame/graphics/HFTracer.h",
    "content": "/* Copyright (C) 2014 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n/*\n * Determine intersection of rays with a heightfield.\n */\n\n#ifndef INCLUDED_HFTRACER\n#define INCLUDED_HFTRACER\n\nclass CPatch;\nclass CVector3D;\nclass CTerrain;\n\n///////////////////////////////////////////////////////////////////////////////\n// CHFTracer: a class for determining ray intersections with a heightfield\nclass CHFTracer\n{\npublic:\n\t// constructor; setup data\n\tCHFTracer(CTerrain *pTerrain);\n\n\t// intersect ray with this heightfield; return true if intersection \n\t// occurs (and fill in grid coordinates and point of intersection), or false otherwise\n\tbool RayIntersect(const CVector3D& origin, const CVector3D& dir, int& x, int& z, CVector3D& ipt) const;\n\n\t/**\n\t * Intersects ray with a single patch.\n\t * The ray is a half-infinite line starting at @p origin with direction @p dir\n\t * (not required to be a unit vector).. The patch is treated as a collection\n\t * of two-sided triangles, corresponding to the terrain tiles.\n\t *\n\t * If there is an intersection, returns true; and if @p out is not NULL, it\n\t * is set to the intersection point. This is guaranteed to be the earliest\n\t * tile intersected (starting at @p origin), but not necessarily the earlier\n\t * triangle inside that tile.\n\t *\n\t * This partly duplicates RayIntersect, but it only operates on a single\n\t * patch, and it's more precise (it uses the same tile triangulation as the\n\t * renderer), and tries to be more numerically robust.\n\t */\n\tstatic bool PatchRayIntersect(CPatch* patch, const CVector3D& origin, const CVector3D& dir, CVector3D* out);\n\nprivate:\n\t// test if ray intersects either of the triangles in the given \n\tbool CellIntersect(int cx, int cz, const CVector3D& origin, const CVector3D& dir, float& dist) const;\n\t\n\t// The terrain we're operating on\n\tCTerrain *m_pTerrain;\n\t// the heightfield were tracing\n\tconst u16* m_Heightfield;\n\t// size of the heightfield\n\tsize_t m_MapSize;\n\t// cell size - size of each cell in x and z\n\tfloat m_CellSize;\n\t// vertical scale - size of each cell in y\n\tfloat m_HeightScale;\n};\n\n#endif\n"
  },
  {
    "path": "fpsgame/graphics/HeightMipmap.cpp",
    "content": "/* Copyright (C) 2012 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"precompiled.h\"\n\n#include \"HeightMipmap.h\"\n\n#include \"lib/bits.h\"\n#include \"lib/timer.h\"\n#include \"lib/allocators/shared_ptr.h\"\n#include \"lib/tex/tex.h\"\n#include \"maths/MathUtil.h\"\n#include \"ps/Filesystem.h\"\n\n#include <cmath>\n\nCHeightMipmap::CHeightMipmap()\n{\n}\n\nCHeightMipmap::~CHeightMipmap()\n{\n\tReleaseData();\n}\n\nvoid CHeightMipmap::ReleaseData()\n{\n\tfor (size_t i = 0; i < m_Mipmap.size(); ++i)\n\t{\n\t\tdelete[] m_Mipmap[i].m_Heightmap;\n\t\tm_Mipmap[i].m_MapSize = 0;\n\t}\n\tm_Mipmap.clear();\n}\n\nvoid CHeightMipmap::Update(const u16* ptr)\n{\n\tENSURE(ptr != 0);\n\n\tUpdate(ptr, 0, 0, m_MapSize, m_MapSize);\n}\n\nvoid CHeightMipmap::Update(const u16* ptr, size_t left, size_t bottom, size_t right, size_t top)\n{\n\tENSURE(ptr != 0);\n\n\tsize_t mapSize = m_MapSize;\n\n\tfor (size_t i = 0; i < m_Mipmap.size(); ++i)\n\t{\n\t\t// update window\n\t\tleft = clamp<size_t>((size_t)floorf((float)left / mapSize * m_Mipmap[i].m_MapSize), 0, m_Mipmap[i].m_MapSize - 1);\n\t\tbottom = clamp<size_t>((size_t)floorf((float)bottom / mapSize * m_Mipmap[i].m_MapSize), 0, m_Mipmap[i].m_MapSize - 1);\n\n\t\tright = clamp<size_t>((size_t)ceilf((float)right / mapSize * m_Mipmap[i].m_MapSize), 0, m_Mipmap[i].m_MapSize);\n\t\ttop = clamp<size_t>((size_t)ceilf((float)top / mapSize * m_Mipmap[i].m_MapSize), 0, m_Mipmap[i].m_MapSize);\n\n\t\t// TODO: should verify that the bounds calculations are actually correct\n\n\t\t// update mipmap\n\t\tBilinearUpdate(m_Mipmap[i], mapSize, ptr, left, bottom, right, top);\n\n\t\tmapSize = m_Mipmap[i].m_MapSize;\n\t\tptr = m_Mipmap[i].m_Heightmap;\n\t}\n}\n\nvoid CHeightMipmap::Initialize(size_t mapSize, const u16* ptr)\n{\n\tENSURE(ptr != 0);\n\tENSURE(mapSize > 0);\n\n\tReleaseData();\n\n\tm_MapSize = mapSize;\n\tsize_t mipmapSize = round_down_to_pow2(mapSize);\n\n\twhile (mipmapSize > 1)\n\t{\n\t\tm_Mipmap.push_back(SMipmap(mipmapSize, new u16[mipmapSize*mipmapSize]));\n\t\tmipmapSize >>= 1;\n\t};\n\n\tUpdate(ptr);\n}\n\nfloat CHeightMipmap::GetTrilinearGroundLevel(float x, float z, float radius) const\n{\n\tfloat y;\n\tif (radius <= 0.0f) // avoid logf of non-positive value\n\t\ty = 0.0f;\n\telse\n\t\ty = clamp<float>(logf(radius * m_Mipmap[0].m_MapSize) / logf(2), 0, m_Mipmap.size());\n\n\tconst size_t iy = (size_t)clamp<ssize_t>((ssize_t)floorf(y), 0, m_Mipmap.size() - 2);\n\n\tconst float fy = y - iy;\n\n\tconst float h0 = BilinearFilter(m_Mipmap[iy], x, z);\n\tconst float h1 = BilinearFilter(m_Mipmap[iy + 1], x, z);\n\n\treturn (1 - fy) * h0 + fy * h1;\n}\n\nfloat CHeightMipmap::BilinearFilter(const SMipmap &mipmap, float x, float z) const\n{\n\tx *= mipmap.m_MapSize;\n\tz *= mipmap.m_MapSize;\n\n\tconst size_t xi = (size_t)clamp<ssize_t>((ssize_t)floor(x), 0, mipmap.m_MapSize - 2);\n\tconst size_t zi = (size_t)clamp<ssize_t>((ssize_t)floor(z), 0, mipmap.m_MapSize - 2);\n\n\tconst float xf = clamp<float>(x-xi, 0.0f, 1.0f);\n\tconst float zf = clamp<float>(z-zi, 0.0f, 1.0f);\n\n\tconst float h00 = mipmap.m_Heightmap[zi*mipmap.m_MapSize + xi];\n\tconst float h01 = mipmap.m_Heightmap[(zi+1)*mipmap.m_MapSize + xi];\n\tconst float h10 = mipmap.m_Heightmap[zi*mipmap.m_MapSize + (xi+1)];\n\tconst float h11 = mipmap.m_Heightmap[(zi+1)*mipmap.m_MapSize + (xi+1)];\n\n\treturn\n\t\t(1.f - xf) * (1.f - zf) * h00 +\n\t\t\t   xf  * (1.f - zf) * h10 +\n\t\t(1.f - xf) *        zf  * h01 +\n\t\t\t   xf  *        zf  * h11;\n}\n\nvoid CHeightMipmap::HalfResizeUpdate(SMipmap &out_mipmap, size_t mapSize, const u16* ptr, size_t left, size_t bottom, size_t right, size_t top)\n{\n\t// specialized, faster version of BilinearUpdate for powers of 2\n\n\tENSURE(out_mipmap.m_MapSize != 0);\n\n\tif (out_mipmap.m_MapSize * 2 != mapSize)\n\t\tdebug_warn(L\"wrong size\");\n\n\t// valid update window\n\tENSURE(left < out_mipmap.m_MapSize);\n\tENSURE(bottom < out_mipmap.m_MapSize);\n\tENSURE(right > left && right <= out_mipmap.m_MapSize);\n\tENSURE(top > bottom && top <= out_mipmap.m_MapSize);\n\n\tfor (size_t dstZ = bottom; dstZ < top; ++dstZ)\n\t{\n\t\tfor (size_t dstX = left; dstX < right; ++dstX)\n\t\t{\n\t\t\tsize_t srcX = dstX << 1;\n\t\t\tsize_t srcZ = dstZ << 1;\n\n\t\t\tu16 h00 = ptr[srcX + 0 + srcZ * mapSize];\n\t\t\tu16 h10 = ptr[srcX + 1 + srcZ * mapSize];\n\t\t\tu16 h01 = ptr[srcX + 0 + (srcZ + 1) * mapSize];\n\t\t\tu16 h11 = ptr[srcX + 1 + (srcZ + 1) * mapSize];\n\n\t\t\tout_mipmap.m_Heightmap[dstX + dstZ * out_mipmap.m_MapSize] = (h00 + h10 + h01 + h11) / 4;\n\t\t}\n\t}\n}\n\nvoid CHeightMipmap::BilinearUpdate(SMipmap &out_mipmap, size_t mapSize, const u16* ptr, size_t left, size_t bottom, size_t right, size_t top)\n{\n\tENSURE(out_mipmap.m_MapSize != 0);\n\n\t// filter should have full coverage\n\tENSURE(out_mipmap.m_MapSize <= mapSize && out_mipmap.m_MapSize * 2 >= mapSize);\n\n\t// valid update window\n\tENSURE(left < out_mipmap.m_MapSize);\n\tENSURE(bottom < out_mipmap.m_MapSize);\n\tENSURE(right > left && right <= out_mipmap.m_MapSize);\n\tENSURE(top > bottom && top <= out_mipmap.m_MapSize);\n\n\tif (out_mipmap.m_MapSize * 2 == mapSize)\n\t{\n\t\t// optimized for powers of 2\n\t\tHalfResizeUpdate(out_mipmap, mapSize, ptr, left, bottom, right, top);\n\t}\n\telse\n\t{\n\t\tfor (size_t dstZ = bottom; dstZ < top; ++dstZ)\n\t\t{\n\t\t\tfor (size_t dstX = left; dstX < right; ++dstX)\n\t\t\t{\n\t\t\t\tconst float x = ((float)dstX / (float)out_mipmap.m_MapSize) * mapSize;\n\t\t\t\tconst float z = ((float)dstZ / (float)out_mipmap.m_MapSize) * mapSize;\n\n\t\t\t\tconst size_t srcX = clamp<size_t>((size_t)x, 0, mapSize - 2);\n\t\t\t\tconst size_t srcZ = clamp<size_t>((size_t)z, 0, mapSize - 2);\n\n\t\t\t\tconst float fx = clamp<float>(x - srcX, 0.0f, 1.0f);\n\t\t\t\tconst float fz = clamp<float>(z - srcZ, 0.0f, 1.0f);\n\n\t\t\t\tconst float h00 = ptr[srcX + 0 + srcZ * mapSize];\n\t\t\t\tconst float h10 = ptr[srcX + 1 + srcZ * mapSize];\n\t\t\t\tconst float h01 = ptr[srcX + 0 + (srcZ + 1) * mapSize];\n\t\t\t\tconst float h11 = ptr[srcX + 1 + (srcZ + 1) * mapSize];\n\n\t\t\t\tout_mipmap.m_Heightmap[dstX + dstZ * out_mipmap.m_MapSize] = (u16)\n\t\t\t\t\t((1.f - fx) * (1.f - fz) * h00 +\n\t\t\t\t\t\t\tfx  * (1.f - fz) * h10 +\n\t\t\t\t\t (1.f - fx) *        fz  * h01 +\n\t\t\t\t\t\t\tfx  *        fz  * h11);\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid CHeightMipmap::DumpToDisk(const VfsPath& filename) const\n{\n\tconst size_t w = m_MapSize;\n\tconst size_t h = m_MapSize * 2;\n\tconst size_t bpp = 8;\n\tint flags = TEX_GREY|TEX_TOP_DOWN;\n\n\tconst size_t img_size = w * h * bpp/8;\n\tconst size_t hdr_size = tex_hdr_size(filename);\n\tshared_ptr<u8> buf;\n\tAllocateAligned(buf, hdr_size+img_size, maxSectorSize);\n\tvoid* img = buf.get() + hdr_size;\n\tTex t;\n\tWARN_IF_ERR(t.wrap(w, h, bpp, flags, buf, hdr_size));\n\n\tmemset(img, 0x00, img_size);\n\tsize_t yoff = 0;\n\tfor (size_t i = 0; i < m_Mipmap.size(); ++i)\n\t{\n\t\tsize_t size = m_Mipmap[i].m_MapSize;\n\t\tu16* heightmap = m_Mipmap[i].m_Heightmap;\n\t\tENSURE(size+yoff <= h);\n\t\tfor (size_t y = 0; y < size; ++y)\n\t\t{\n\t\t\tfor (size_t x = 0; x < size; ++x)\n\t\t\t{\n\t\t\t\tu16 val = heightmap[x + y*size];\n\t\t\t\t((u8*)img)[x + (y+yoff)*w] = val >> 8;\n\t\t\t}\n\t\t}\n\t\tyoff += size;\n\t}\n\n\tDynArray da;\n\tWARN_IF_ERR(t.encode(filename.Extension(), &da));\n\tg_VFS->CreateFile(filename, DummySharedPtr(da.base), da.pos);\n\t(void)da_free(&da);\n}\n"
  },
  {
    "path": "fpsgame/graphics/HeightMipmap.h",
    "content": "/* Copyright (C) 2012 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n\n/*\n * Describes ground using heightmap mipmaps\n * Used for camera movement\n */\n\n#ifndef INCLUDED_HEIGHTMIPMAP\n#define INCLUDED_HEIGHTMIPMAP\n\n#include \"lib/file/vfs/vfs_path.h\"\n\nstruct SMipmap\n{\n\tSMipmap() : m_MapSize(0), m_Heightmap(0) { }\n\tSMipmap(size_t MapSize, u16* Heightmap) : m_MapSize(MapSize), m_Heightmap(Heightmap) { }\n\n\tsize_t m_MapSize;\n\tu16* m_Heightmap;\n};\n\nclass CHeightMipmap\n{\n\tNONCOPYABLE(CHeightMipmap);\npublic:\n\n\tCHeightMipmap();\n\t~CHeightMipmap();\n\n\tvoid Initialize(size_t mapSize, const u16* ptr);\n\tvoid ReleaseData();\n\n\t// update the heightmap mipmaps\n\tvoid Update(const u16* ptr);\n\n\t// update a section of the heightmap mipmaps\n\t// (coordinates are heightmap cells, inclusive of lower bounds,\n\t// exclusive of upper bounds)\n\tvoid Update(const u16* ptr, size_t left, size_t bottom, size_t right, size_t top);\n\n\tfloat GetTrilinearGroundLevel(float x, float z, float radius) const;\n\n\tvoid DumpToDisk(const VfsPath& path) const;\n\nprivate:\n\n\t// get bilinear filtered height from mipmap\n\tfloat BilinearFilter(const SMipmap &mipmap, float x, float z) const;\n\n\t// update rectangle of the output mipmap by bilinear interpolating an input mipmap of exactly twice its size\n\tvoid HalfResizeUpdate(SMipmap &out_mipmap, size_t mapSize, const u16* ptr, size_t left, size_t bottom, size_t right, size_t top);\n\n\t// update rectangle of the output mipmap by bilinear interpolating the input mipmap\n\tvoid BilinearUpdate(SMipmap &out_mipmap, size_t mapSize, const u16* ptr, size_t left, size_t bottom, size_t right, size_t top);\n\n\t// size of this map in each direction\n\tsize_t m_MapSize;\n\n\t// mipmap list\n\tstd::vector<SMipmap> m_Mipmap;\n};\n\n#endif\n"
  },
  {
    "path": "fpsgame/graphics/LOSTexture.cpp",
    "content": "/* Copyright (C) 2014 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"precompiled.h\"\n\n#include \"LOSTexture.h\"\n\n#include \"graphics/ShaderManager.h\"\n#include \"graphics/Terrain.h\"\n#include \"lib/bits.h\"\n#include \"lib/config2.h\"\n#include \"ps/CLogger.h\"\n#include \"ps/Game.h\"\n#include \"ps/Profile.h\"\n#include \"renderer/Renderer.h\"\n#include \"renderer/TimeManager.h\"\n#include \"simulation2/Simulation2.h\"\n#include \"simulation2/components/ICmpRangeManager.h\"\n#include \"simulation2/components/ICmpTerrain.h\"\n\n/*\n\nThe LOS bitmap is computed with one value per map vertex, based on\nCCmpRangeManager's visibility information.\n\nThe bitmap is then blurred using an NxN filter (in particular a\n7-tap Binomial filter as an efficient integral approximation of a Gaussian).\nTo implement the blur efficiently without using extra memory for a second copy\nof the bitmap, we generate the bitmap with (N-1)/2 pixels of padding on each side,\nthen the blur shifts the image back into the corner.\n\nThe blurred bitmap is then uploaded into a GL texture for use by the renderer.\n\n*/\n\n\n// Blur with a NxN filter, where N = g_BlurSize must be an odd number.\nstatic const size_t g_BlurSize = 7;\n\n// Alignment (in bytes) of the pixel data passed into glTexSubImage2D.\n// This must be a multiple of GL_UNPACK_ALIGNMENT, which ought to be 1 (since\n// that's what we set it to) but in some weird cases appears to have a different\n// value. (See Trac #2594). Multiples of 4 are possibly good for performance anyway.\nstatic const size_t g_SubTextureAlignment = 4;\n\nCLOSTexture::CLOSTexture(CSimulation2& simulation)\n\t: m_Simulation(simulation), m_Dirty(true), m_ShaderInitialized(false),\n\tm_Texture(0), m_TextureSmooth1(0), m_TextureSmooth2(0),  m_smoothFbo(0),\n\tm_MapSize(0), m_TextureSize(0), whichTex(true)\n{\n\tif (CRenderer::IsInitialised() && g_Renderer.m_Options.m_SmoothLOS)\n\t\tCreateShader();\n}\n\nCLOSTexture::~CLOSTexture()\n{\n\tif (m_Texture)\n\t\tDeleteTexture();\n}\n\n// Create the LOS texture engine. Should be ran only once.\nbool CLOSTexture::CreateShader()\n{\n\tm_smoothShader = g_Renderer.GetShaderManager().LoadEffect(str_los_interp);\n\tCShaderProgramPtr shader = m_smoothShader->GetShader();\n\n\tm_ShaderInitialized = m_smoothShader && shader;\n\t\n\tif (!m_ShaderInitialized)\n\t{\n\t\tLOGERROR(\"Failed to load SmoothLOS shader, disabling.\");\n\t\tg_Renderer.m_Options.m_SmoothLOS = false;\n\t\treturn false;\n\t}\n\t\t\n\tpglGenFramebuffersEXT(1, &m_smoothFbo);\n\treturn true;\n}\n\nvoid CLOSTexture::DeleteTexture()\n{\n\tglDeleteTextures(1, &m_Texture);\n\n\tif (m_TextureSmooth1)\n\t\tglDeleteTextures(1, &m_TextureSmooth1);\n\n\tif (m_TextureSmooth2)\n\t\tglDeleteTextures(1, &m_TextureSmooth2);\n\n\tm_Texture = 0;\n\tm_TextureSmooth1 = 0;\n\tm_TextureSmooth2 = 0;\n}\n\nvoid CLOSTexture::MakeDirty()\n{\n\tm_Dirty = true;\n}\n\nvoid CLOSTexture::BindTexture(int unit)\n{\n\tif (m_Dirty)\n\t{\n\t\tRecomputeTexture(unit);\n\t\tm_Dirty = false;\n\t}\n\n\tg_Renderer.BindTexture(unit, m_Texture);\n}\n\nGLuint CLOSTexture::GetTextureSmooth()\n{\n\tif (CRenderer::IsInitialised() && !g_Renderer.m_Options.m_SmoothLOS)\n\t\treturn GetTexture();\n\telse\n\t\treturn whichTex ? m_TextureSmooth1 : m_TextureSmooth2;\n}\n\nvoid CLOSTexture::InterpolateLOS()\n{\n\tif (CRenderer::IsInitialised() && !g_Renderer.m_Options.m_SmoothLOS)\n\t\treturn;\n\n\tif (!m_ShaderInitialized)\n\t{\n\t\tif (!CreateShader())\n\t\t\treturn;\n\n\t\t// RecomputeTexture(0) will not cause the ConstructTexture to run.\n\t\t// Force the textures to be created.\n\t\tDeleteTexture();\n\t\tConstructTexture(0);\n\t\tm_Dirty = true;\n\t}\n\n\tif (m_Dirty)\n\t{\n\t\tRecomputeTexture(0);\n\t\tm_Dirty = false;\n\t}\n\n\tGLint originalFBO;\n\tglGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &originalFBO);\n\n\tpglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_smoothFbo);\n\tpglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, \n\t\t\t\t   whichTex ? m_TextureSmooth2 : m_TextureSmooth1, 0);\n\t\n\tGLenum status = pglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);\n\tif (status != GL_FRAMEBUFFER_COMPLETE_EXT)\n\t{\n\t\tLOGWARNING(\"LOS framebuffer object incomplete: 0x%04X\", status);\n\t}\n\n\tm_smoothShader->BeginPass();\n\tCShaderProgramPtr shader = m_smoothShader->GetShader();\n\t\n\tglDisable(GL_BLEND);\n\t\n\tshader->Bind();\n\t\n\tshader->BindTexture(str_losTex1, m_Texture);\n\tshader->BindTexture(str_losTex2, whichTex ? m_TextureSmooth1 : m_TextureSmooth2);\n\t\n\tshader->Uniform(str_delta, (float)g_Renderer.GetTimeManager().GetFrameDelta() * 4.0f, 0.0f, 0.0f, 0.0f);\n\t\n\tconst SViewPort oldVp = g_Renderer.GetViewport();\n\tconst SViewPort vp = { 0, 0, m_TextureSize, m_TextureSize };\n\tg_Renderer.SetViewport(vp);\n\t\n\tfloat quadVerts[] = {\n\t\t1.0f, 1.0f,\n\t\t-1.0f, 1.0f,\n\t\t-1.0f, -1.0f,\n\n\t\t-1.0f, -1.0f,\n\t\t1.0f, -1.0f,\n\t\t1.0f, 1.0f\n\t};\n\tfloat quadTex[] = {\n\t\t1.0f, 1.0f,\n\t\t0.0f, 1.0f,\n\t\t0.0f, 0.0f,\n\n\t\t0.0f, 0.0f,\n\t\t1.0f, 0.0f,\n\t\t1.0f, 1.0f\n\t};\n\tshader->TexCoordPointer(GL_TEXTURE0, 2, GL_FLOAT, 0, quadTex);\n\tshader->VertexPointer(2, GL_FLOAT, 0, quadVerts);\n\tshader->AssertPointersBound();\n\tglDrawArrays(GL_TRIANGLES, 0, 6);\n\t\n\tg_Renderer.SetViewport(oldVp);\n\n\tshader->Unbind();\n\tm_smoothShader->EndPass();\n\t\n\tpglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, 0, 0);\n\t\n\tpglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, originalFBO);\n\t\n\twhichTex = !whichTex;\n}\n\n\nGLuint CLOSTexture::GetTexture()\n{\n\tif (m_Dirty)\n\t{\n\t\tRecomputeTexture(0);\n\t\tm_Dirty = false;\n\t}\n\n\treturn m_Texture;\n}\n\nconst CMatrix3D& CLOSTexture::GetTextureMatrix()\n{\n\tENSURE(!m_Dirty);\n\treturn m_TextureMatrix;\n}\n\nconst CMatrix3D* CLOSTexture::GetMinimapTextureMatrix()\n{\n\tENSURE(!m_Dirty);\n\treturn &m_MinimapTextureMatrix;\n}\n\nvoid CLOSTexture::ConstructTexture(int unit)\n{\n\tCmpPtr<ICmpTerrain> cmpTerrain(m_Simulation, SYSTEM_ENTITY);\n\tif (!cmpTerrain)\n\t\treturn;\n\n\tm_MapSize = cmpTerrain->GetVerticesPerSide();\n\n\tm_TextureSize = (GLsizei)round_up_to_pow2(round_up((size_t)m_MapSize + g_BlurSize - 1, g_SubTextureAlignment));\n\n\tglGenTextures(1, &m_Texture);\n\n\t// Initialise texture with SoD color, for the areas we don't\n\t// overwrite with glTexSubImage2D later\n\tu8* texData = new u8[m_TextureSize * m_TextureSize * 4];\n\tmemset(texData, 0x00, m_TextureSize * m_TextureSize * 4);\n\t\n\tif (CRenderer::IsInitialised() && g_Renderer.m_Options.m_SmoothLOS)\n\t{\n\t\tglGenTextures(1, &m_TextureSmooth1);\n\t\tglGenTextures(1, &m_TextureSmooth2);\n\t\t\n\t\tg_Renderer.BindTexture(unit, m_TextureSmooth1);\n\t\tglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_TextureSize, m_TextureSize, 0, GL_ALPHA, GL_UNSIGNED_BYTE, texData);\n\t\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);\n\t\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);\n\t\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);\n\t\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);\n\n\t\tg_Renderer.BindTexture(unit, m_TextureSmooth2);\n\t\tglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_TextureSize, m_TextureSize, 0, GL_ALPHA, GL_UNSIGNED_BYTE, texData);\n\t\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);\n\t\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);\n\t\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);\n\t\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);\n\t}\n\t\n\tg_Renderer.BindTexture(unit, m_Texture);\n\tglTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, m_TextureSize, m_TextureSize, 0, GL_ALPHA, GL_UNSIGNED_BYTE, texData);\n\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);\n\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);\n\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);\n\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);\n\t\n\tdelete[] texData;\t\n\t\n\t{\n\t\t// Texture matrix: We want to map\n\t\t//   world pos (0, y, 0)  (i.e. first vertex)\n\t\t//     onto texcoord (0.5/texsize, 0.5/texsize)  (i.e. middle of first texel);\n\t\t//   world pos ((mapsize-1)*cellsize, y, (mapsize-1)*cellsize)  (i.e. last vertex)\n\t\t//     onto texcoord ((mapsize-0.5) / texsize, (mapsize-0.5) / texsize)  (i.e. middle of last texel)\n\n\t\tfloat s = (m_MapSize-1) / (float)(m_TextureSize * (m_MapSize-1) * TERRAIN_TILE_SIZE);\n\t\tfloat t = 0.5f / m_TextureSize;\n\t\tm_TextureMatrix.SetZero();\n\t\tm_TextureMatrix._11 = s;\n\t\tm_TextureMatrix._23 = s;\n\t\tm_TextureMatrix._14 = t;\n\t\tm_TextureMatrix._24 = t;\n\t\tm_TextureMatrix._44 = 1;\n\t}\n\n\t{\n\t\t// Minimap matrix: We want to map UV (0,0)-(1,1) onto (0,0)-(mapsize/texsize, mapsize/texsize)\n\n\t\tfloat s = m_MapSize / (float)m_TextureSize;\n\t\tm_MinimapTextureMatrix.SetZero();\n\t\tm_MinimapTextureMatrix._11 = s;\n\t\tm_MinimapTextureMatrix._22 = s;\n\t\tm_MinimapTextureMatrix._44 = 1;\n\t}\n}\n\nvoid CLOSTexture::RecomputeTexture(int unit)\n{\n\t// If the map was resized, delete and regenerate the texture\n\tif (m_Texture)\n\t{\n\t\tCmpPtr<ICmpTerrain> cmpTerrain(m_Simulation, SYSTEM_ENTITY);\n\t\tif (cmpTerrain && m_MapSize != (ssize_t)cmpTerrain->GetVerticesPerSide())\n\t\t\tDeleteTexture();\n\t}\n\n\tbool recreated = false;\n\tif (!m_Texture)\n\t{\n\t\tConstructTexture(unit);\n\t\trecreated = true;\n\t}\n\n\tPROFILE(\"recompute LOS texture\");\n\n\tstd::vector<u8> losData;\n\tsize_t pitch;\n\tlosData.resize(GetBitmapSize(m_MapSize, m_MapSize, &pitch));\n\n\tCmpPtr<ICmpRangeManager> cmpRangeManager(m_Simulation, SYSTEM_ENTITY);\n\tif (!cmpRangeManager)\n\t\treturn;\n\n\tICmpRangeManager::CLosQuerier los(cmpRangeManager->GetLosQuerier(g_Game->GetSimulation2()->GetSimContext().GetCurrentDisplayedPlayer()));\n\n\tGenerateBitmap(los, &losData[0], m_MapSize, m_MapSize, pitch);\n\n\tif (CRenderer::IsInitialised() && g_Renderer.m_Options.m_SmoothLOS && recreated)\n\t{\n\t\tg_Renderer.BindTexture(unit, m_TextureSmooth1);\t\t\n\t\tglTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, pitch, m_MapSize, GL_ALPHA, GL_UNSIGNED_BYTE, &losData[0]);\n\t\tg_Renderer.BindTexture(unit, m_TextureSmooth2);\t\t\n\t\tglTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, pitch, m_MapSize, GL_ALPHA, GL_UNSIGNED_BYTE, &losData[0]);\n\t}\n\n\tg_Renderer.BindTexture(unit, m_Texture);\n\tglTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, pitch, m_MapSize, GL_ALPHA, GL_UNSIGNED_BYTE, &losData[0]);\n}\n\nsize_t CLOSTexture::GetBitmapSize(size_t w, size_t h, size_t* pitch)\n{\n\t*pitch = round_up(w + g_BlurSize - 1, g_SubTextureAlignment);\n\treturn *pitch * (h + g_BlurSize - 1);\n}\n\nvoid CLOSTexture::GenerateBitmap(ICmpRangeManager::CLosQuerier los, u8* losData, size_t w, size_t h, size_t pitch)\n{\n\tu8 *dataPtr = losData;\n\n\t// Initialise the top padding\n\tfor (size_t j = 0; j < g_BlurSize/2; ++j)\n\t\tfor (size_t i = 0; i < pitch; ++i)\n\t\t\t*dataPtr++ = 0;\n\n\tfor (size_t j = 0; j < h; ++j)\n\t{\n\t\t// Initialise the left padding\n\t\tfor (size_t i = 0; i < g_BlurSize/2; ++i)\n\t\t\t*dataPtr++ = 0;\n\n\t\t// Fill in the visibility data\n\t\tfor (size_t i = 0; i < w; ++i)\n\t\t{\n\t\t\tif (los.IsVisible_UncheckedRange(i, j))\n\t\t\t\t*dataPtr++ = 255;\n\t\t\telse if (los.IsExplored_UncheckedRange(i, j))\n\t\t\t\t*dataPtr++ = 127;\n\t\t\telse\n\t\t\t\t*dataPtr++ = 0;\n\t\t}\n\n\t\t// Initialise the right padding\n\t\tfor (size_t i = 0; i < pitch - w - g_BlurSize/2; ++i)\n\t\t\t*dataPtr++ = 0;\n\t}\n\n\t// Initialise the bottom padding\n\tfor (size_t j = 0; j < g_BlurSize/2; ++j)\n\t\tfor (size_t i = 0; i < pitch; ++i)\n\t\t\t*dataPtr++ = 0;\n\n\t// Horizontal blur:\n\n\tfor (size_t j = g_BlurSize/2; j < h + g_BlurSize/2; ++j)\n\t{\n\t\tfor (size_t i = 0; i < w; ++i)\n\t\t{\n\t\t\tu8* d = &losData[i+j*pitch];\n\t\t\t*d = (\n\t\t\t\t1*d[0] +\n\t\t\t\t6*d[1] +\n\t\t\t\t15*d[2] +\n\t\t\t\t20*d[3] +\n\t\t\t\t15*d[4] +\n\t\t\t\t6*d[5] +\n\t\t\t\t1*d[6]\n\t\t\t) / 64;\n\t\t}\n\t}\n\n\t// Vertical blur:\n\n\tfor (size_t j = 0; j < h; ++j)\n\t{\n\t\tfor (size_t i = 0; i < w; ++i)\n\t\t{\n\t\t\tu8* d = &losData[i+j*pitch];\n\t\t\t*d = (\n\t\t\t\t1*d[0*pitch] +\n\t\t\t\t6*d[1*pitch] +\n\t\t\t\t15*d[2*pitch] +\n\t\t\t\t20*d[3*pitch] +\n\t\t\t\t15*d[4*pitch] +\n\t\t\t\t6*d[5*pitch] +\n\t\t\t\t1*d[6*pitch]\n\t\t\t) / 64;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "fpsgame/graphics/LOSTexture.h",
    "content": "/* Copyright (C) 2014 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"lib/ogl.h\"\n\n#include \"maths/Matrix3D.h\"\n#include \"simulation2/components/ICmpRangeManager.h\"\n\n#include \"graphics/ShaderManager.h\"\n\n\nclass CSimulation2;\n\n/**\n * Maintains the LOS (fog-of-war / shroud-of-darkness) texture, used for\n * rendering and for the minimap.\n */\nclass CLOSTexture\n{\n\tNONCOPYABLE(CLOSTexture);\n\tfriend class TestLOSTexture;\n\npublic:\n\tCLOSTexture(CSimulation2& simulation);\n\t~CLOSTexture();\n\n\t/**\n\t * Marks the LOS texture as needing recomputation. Call this after each\n\t * simulation update, to ensure responsive updates.\n\t */\n\tvoid MakeDirty();\n\n\t/**\n\t * Recomputes the LOS texture if necessary, and binds it to the requested\n\t * texture unit.\n\t * Also switches the current active texture unit, and enables texturing on it.\n\t * The texture is in 8-bit ALPHA format.\n\t */\n\tvoid BindTexture(int unit);\n\n\t/**\n\t * Recomputes the LOS texture if necessary, and returns the texture handle.\n\t * Also potentially switches the current active texture unit, and enables texturing on it.\n\t * The texture is in 8-bit ALPHA format.\n\t */\n\tGLuint GetTexture();\n\n\tvoid InterpolateLOS();\n\tGLuint GetTextureSmooth();\n\n\t/**\n\t * Returns a matrix to map (x,y,z) world coordinates onto (u,v) LOS texture\n\t * coordinates, in the form expected by glLoadMatrixf.\n\t * This must only be called after BindTexture.\n\t */\n\tconst CMatrix3D& GetTextureMatrix();\n\n\t/**\n\t * Returns a matrix to map (0,0)-(1,1) texture coordinates onto LOS texture\n\t * coordinates, in the form expected by glLoadMatrixf.\n\t * This must only be called after BindTexture.\n\t */\n\tconst CMatrix3D* GetMinimapTextureMatrix();\n\nprivate:\n\tvoid DeleteTexture();\n\tbool CreateShader();\n\tvoid ConstructTexture(int unit);\n\tvoid RecomputeTexture(int unit);\n\n\tsize_t GetBitmapSize(size_t w, size_t h, size_t* pitch);\n\tvoid GenerateBitmap(ICmpRangeManager::CLosQuerier los, u8* losData, size_t w, size_t h, size_t pitch);\n\n\tCSimulation2& m_Simulation;\n\n\tbool m_Dirty;\n\n\tbool m_ShaderInitialized;\n\n\tGLuint m_Texture;\n\tGLuint m_TextureSmooth1, m_TextureSmooth2;\n\t\n\tbool whichTex;\n\t\n\tGLuint m_smoothFbo;\n\tCShaderTechniquePtr m_smoothShader;\t\n\n\tssize_t m_MapSize; // vertexes per side\n\tGLsizei m_TextureSize; // texels per side\n\n\tCMatrix3D m_TextureMatrix;\n\tCMatrix3D m_MinimapTextureMatrix;\n};\n"
  },
  {
    "path": "fpsgame/graphics/LightEnv.cpp",
    "content": "/* Copyright (C) 2012 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n/*\n * CLightEnv implementation\n */\n\n#include \"precompiled.h\"\n\n#include \"maths/MathUtil.h\"\n\n#include \"graphics/LightEnv.h\"\n\n\nCLightEnv::CLightEnv()\n\t: m_Elevation(DEGTORAD(45)),\n\tm_Rotation(DEGTORAD(315)),\n\tm_LightingModel(\"standard\"),\n\tm_SunColor(1.5, 1.5, 1.5),\n\tm_TerrainAmbientColor(0x50/255.f, 0x60/255.f, 0x85/255.f),\n\tm_UnitsAmbientColor(0x80/255.f, 0x80/255.f, 0x80/255.f),\n\tm_FogColor(0xCC/255.f, 0xCC/255.f, 0xE5/255.f),\n\tm_FogFactor(0.000f),\n\tm_FogMax(0.5f),\n\tm_Brightness(0.0f), m_Contrast(1.0f), m_Saturation(0.99f), m_Bloom(0.1999f)\n{\n\tCalculateSunDirection();\n}\n\nvoid CLightEnv::SetElevation(float f)\n{\n\tm_Elevation = f;\n\tCalculateSunDirection();\n}\n\nvoid CLightEnv::SetRotation(float f)\n{\n\tm_Rotation = f;\n\tCalculateSunDirection();\n}\n\nvoid CLightEnv::CalculateSunDirection()\n{\n\tm_SunDir.Y = -sinf(m_Elevation);\n\tfloat scale = 1 + m_SunDir.Y;\n\tm_SunDir.X = scale * sinf(m_Rotation);\n\tm_SunDir.Z = scale * cosf(m_Rotation);\n\tm_SunDir.Normalize();\n}\n"
  },
  {
    "path": "fpsgame/graphics/LightEnv.h",
    "content": "/* Copyright (C) 2012 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n/*\n * CLightEnv, a class describing the current lights\n */\n\n#ifndef INCLUDED_LIGHTENV\n#define INCLUDED_LIGHTENV\n\n#include \"graphics/Color.h\"\n#include \"maths/MathUtil.h\"\n#include \"maths/Vector3D.h\"\n\nclass CMapWriter;\nclass CMapReader;\n\n/**\n * Class CLightEnv: description of a lighting environment - contains all the\n * necessary parameters for representation of the lighting within a scenario\n */\nclass CLightEnv\n{\nfriend class CMapWriter;\nfriend class CMapReader;\nfriend class CXMLReader;\nprivate:\n\t/**\n\t * Height of sun above the horizon, in radians.\n\t * For example, an elevation of M_PI/2 means the sun is straight up.\n\t */\n\tfloat m_Elevation;\n\n\t/**\n\t * Direction of sun on the compass, in radians.\n\t * For example, a rotation of zero means the sun is in the direction (0,0,-1)\n\t * and a rotation of M_PI/2 means the sun is in the direction (1,0,0) (not taking\n\t * elevation into account).\n\t */\n\tfloat m_Rotation;\n\n\t/**\n\t * Vector corresponding to m_Elevation and m_Rotation.\n\t * Updated by CalculateSunDirection.\n\t */\n\tCVector3D m_SunDir;\n\n\t/**\n\t * A string that shaders use to determine what lighting model to implement.\n\t * Current recognised values are \"old\" and \"standard\".\n\t */\n\tstd::string m_LightingModel;\n\npublic:\n\tRGBColor m_SunColor;\n\tRGBColor m_TerrainAmbientColor;\n\tRGBColor m_UnitsAmbientColor;\n\tRGBColor m_FogColor;\n\n\tfloat m_FogFactor;\n\tfloat m_FogMax;\n\t\n\tfloat m_Brightness, m_Contrast, m_Saturation, m_Bloom;\n\npublic:\n\tCLightEnv();\n\n\tfloat GetElevation() const { return m_Elevation; }\n\tfloat GetRotation() const { return m_Rotation; }\n\tconst CVector3D& GetSunDir() const { return m_SunDir; }\n\tconst std::string& GetLightingModel() const { return m_LightingModel; }\n\n\tvoid SetElevation(float f);\n\tvoid SetRotation(float f);\n\n\tvoid SetLightingModel(const std::string& model) { m_LightingModel = model; }\n\n\t/**\n\t * Calculate brightness of a point of a unit with the given normal vector,\n\t * for rendering with CPU lighting.\n\t * The resulting color contains both ambient and diffuse light.\n\t * To cope with sun overbrightness, the color is scaled by 0.5.\n\t *\n\t * @param normal normal vector (must have length 1)\n\t */\n\tRGBColor EvaluateUnitScaled(const CVector3D& normal) const\n\t{\n\t\tfloat dot = -normal.Dot(m_SunDir);\n\n\t\tRGBColor color = m_UnitsAmbientColor;\n\t\tif (dot > 0)\n\t\t\tcolor += m_SunColor * dot;\n\n\t\treturn color * 0.5f;\n\t}\n\n\t/**\n\t * Compute the diffuse sun lighting color on terrain, for rendering with CPU lighting.\n\t * To cope with sun overbrightness, the color is scaled by 0.5.\n\t *\n\t * @param normal normal vector (must have length 1)\n\t */\n\tSColor4ub EvaluateTerrainDiffuseScaled(const CVector3D& normal) const\n\t{\n\t\tfloat dot = -normal.Dot(m_SunDir);\n\t\treturn ConvertRGBColorTo4ub(m_SunColor * dot * 0.5f);\n\t}\n\n\t/**\n\t * Compute the diffuse sun lighting factor on terrain, for rendering with shader lighting.\n\t *\n\t * @param normal normal vector (must have length 1)\n\t */\n\tSColor4ub EvaluateTerrainDiffuseFactor(const CVector3D& normal) const\n\t{\n\t\tfloat dot = -normal.Dot(m_SunDir);\n\t\tint c = clamp((int)(dot * 255), 0, 255);\n\t\treturn SColor4ub(c, c, c, 255);\n\t}\n\n\t// Comparison operators\n\tbool operator==(const CLightEnv& o) const\n\t{\n\t\treturn m_Elevation == o.m_Elevation &&\n\t\t\tm_Rotation == o.m_Rotation &&\n\t\t\tm_LightingModel == o.m_LightingModel &&\n\t\t\tm_SunColor == o.m_SunColor &&\n\t\t\tm_TerrainAmbientColor == o.m_TerrainAmbientColor &&\n\t\t\tm_UnitsAmbientColor == o.m_UnitsAmbientColor &&\n\t\t\tm_FogColor == o.m_FogColor &&\n\t\t\tm_FogFactor == o.m_FogFactor &&\n\t\t\tm_FogMax == o.m_FogMax &&\n\t\t\tm_Brightness == o.m_Brightness &&\n\t\t\tm_Contrast == o.m_Contrast &&\n\t\t\tm_Saturation == o.m_Saturation &&\n\t\t\tm_Bloom == o.m_Bloom;\n\t}\n\n\tbool operator!=(const CLightEnv& o) const\n\t{\n\t\treturn !(*this == o);\n\t}\n\nprivate:\n\tvoid CalculateSunDirection();\n};\n\n#endif // INCLUDED_LIGHTENV\n"
  },
  {
    "path": "fpsgame/graphics/MapGenerator.cpp",
    "content": "/* Copyright (C) 2015 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"precompiled.h\"\n\n#include \"MapGenerator.h\"\n\n#include \"lib/timer.h\"\n#include \"ps/CLogger.h\"\n#include \"ps/Profile.h\"\n\n\n// TODO: what's a good default? perhaps based on map size\n#define RMS_RUNTIME_SIZE 96 * 1024 * 1024\n\n\nCMapGeneratorWorker::CMapGeneratorWorker()\n{\n\t// If something happens before we initialize, that's a failure\n\tm_Progress = -1;\n}\n\nCMapGeneratorWorker::~CMapGeneratorWorker()\n{\n\t// Wait for thread to end\n\tpthread_join(m_WorkerThread, NULL);\n}\n\nvoid CMapGeneratorWorker::Initialize(const VfsPath& scriptFile, const std::string& settings)\n{\n\tCScopeLock lock(m_WorkerMutex);\n\n\t// Set progress to positive value\n\tm_Progress = 1;\n\tm_ScriptPath = scriptFile;\n\tm_Settings = settings;\n\n\t// Launch the worker thread\n\tint ret = pthread_create(&m_WorkerThread, NULL, &RunThread, this);\n\tENSURE(ret == 0);\n}\n\nvoid* CMapGeneratorWorker::RunThread(void *data)\n{\n\tdebug_SetThreadName(\"MapGenerator\");\n\tg_Profiler2.RegisterCurrentThread(\"MapGenerator\");\n\n\tCMapGeneratorWorker* self = static_cast<CMapGeneratorWorker*>(data);\n\n\tself->m_ScriptInterface = new ScriptInterface(\"RMS\", \"MapGenerator\", ScriptInterface::CreateRuntime(g_ScriptRuntime, RMS_RUNTIME_SIZE));\n\n\t// Run map generation scripts\n\tif (!self->Run() || self->m_Progress > 0)\n\t{\n\t\t// Don't leave progress in an unknown state, if generator failed, set it to -1\n\t\tCScopeLock lock(self->m_WorkerMutex);\n\t\tself->m_Progress = -1;\n\t}\n\n\t// At this point the random map scripts are done running, so the thread has no further purpose\n\t//\tand can die. The data will be stored in m_MapData already if successful, or m_Progress\n\t//\twill contain an error value on failure.\n\n\treturn NULL;\n}\n\nbool CMapGeneratorWorker::Run()\n{\n\t// We must destroy the ScriptInterface in the same thread because the JSAPI requires that!\n\t// Also we must not be in a request when calling the ScriptInterface destructor, so the autoFree object\n\t// must be instantiated before the request (destructors are called in reverse order of instantiation)\n\tstruct AutoFree {\n\t\tAutoFree(ScriptInterface* p) : m_p(p) {}\n\t\t~AutoFree() { SAFE_DELETE(m_p); }\n\t\tScriptInterface* m_p;\n\t} autoFree(m_ScriptInterface);\n\t\n\tJSContext* cx = m_ScriptInterface->GetContext();\n\tJSAutoRequest rq(cx);\n\t\n\tm_ScriptInterface->SetCallbackData(static_cast<void*> (this));\n\n\t// Replace RNG with a seeded deterministic function\n\tm_ScriptInterface->ReplaceNondeterministicRNG(m_MapGenRNG);\n\tm_ScriptInterface->LoadGlobalScripts();\n\n\t// Functions for RMS\n\tm_ScriptInterface->RegisterFunction<bool, std::wstring, CMapGeneratorWorker::LoadLibrary>(\"LoadLibrary\");\n\tm_ScriptInterface->RegisterFunction<void, JS::HandleValue, CMapGeneratorWorker::ExportMap>(\"ExportMap\");\n\tm_ScriptInterface->RegisterFunction<void, int, CMapGeneratorWorker::SetProgress>(\"SetProgress\");\n\tm_ScriptInterface->RegisterFunction<void, CMapGeneratorWorker::MaybeGC>(\"MaybeGC\");\n\tm_ScriptInterface->RegisterFunction<std::vector<std::string>, CMapGeneratorWorker::GetCivData>(\"GetCivData\");\n\tm_ScriptInterface->RegisterFunction<CParamNode, std::string, CMapGeneratorWorker::GetTemplate>(\"GetTemplate\");\n\tm_ScriptInterface->RegisterFunction<bool, std::string, CMapGeneratorWorker::TemplateExists>(\"TemplateExists\");\n\tm_ScriptInterface->RegisterFunction<std::vector<std::string>, std::string, bool, CMapGeneratorWorker::FindTemplates>(\"FindTemplates\");\n\tm_ScriptInterface->RegisterFunction<std::vector<std::string>, std::string, bool, CMapGeneratorWorker::FindActorTemplates>(\"FindActorTemplates\");\n\n\t// Parse settings\n\tJS::RootedValue settingsVal(cx);\n\tif (!m_ScriptInterface->ParseJSON(m_Settings, &settingsVal) && settingsVal.isUndefined())\n\t{\n\t\tLOGERROR(\"CMapGeneratorWorker::Run: Failed to parse settings\");\n\t\treturn false;\n\t}\n\t\n\t// Init RNG seed\n\tu32 seed;\n\tif (!m_ScriptInterface->GetProperty(settingsVal, \"Seed\", seed))\n\t{\t// No seed specified\n\t\tLOGWARNING(\"CMapGeneratorWorker::Run: No seed value specified - using 0\");\n\t\tseed = 0;\n\t}\n\n\tm_MapGenRNG.seed(seed);\n\n\t// Copy settings to global variable\n\tJS::RootedValue global(cx, m_ScriptInterface->GetGlobalObject());\n\tif (!m_ScriptInterface->SetProperty(global, \"g_MapSettings\", settingsVal))\n\t{\n\t\tLOGERROR(\"CMapGeneratorWorker::Run: Failed to define g_MapSettings\");\n\t\treturn false;\n\t}\n\n\t// Load RMS\n\tLOGMESSAGE(\"Loading RMS '%s'\", m_ScriptPath.string8());\n\tif (!m_ScriptInterface->LoadGlobalScriptFile(m_ScriptPath))\n\t{\n\t\tLOGERROR(\"CMapGeneratorWorker::Run: Failed to load RMS '%s'\", m_ScriptPath.string8());\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\nint CMapGeneratorWorker::GetProgress()\n{\n\tCScopeLock lock(m_WorkerMutex);\n\treturn m_Progress;\n}\n\nshared_ptr<ScriptInterface::StructuredClone> CMapGeneratorWorker::GetResults()\n{\n\tCScopeLock lock(m_WorkerMutex);\n\treturn m_MapData;\n}\n\nbool CMapGeneratorWorker::LoadLibrary(ScriptInterface::CxPrivate* pCxPrivate, const std::wstring& name)\n{\n\tCMapGeneratorWorker* self = static_cast<CMapGeneratorWorker*>(pCxPrivate->pCBData);\n\treturn self->LoadScripts(name);\n}\n\nvoid CMapGeneratorWorker::ExportMap(ScriptInterface::CxPrivate* pCxPrivate, JS::HandleValue data)\n{\n\tCMapGeneratorWorker* self = static_cast<CMapGeneratorWorker*>(pCxPrivate->pCBData);\n\n\t// Copy results\n\tCScopeLock lock(self->m_WorkerMutex);\n\tself->m_MapData = self->m_ScriptInterface->WriteStructuredClone(data);\n\tself->m_Progress = 0;\n}\n\nvoid CMapGeneratorWorker::SetProgress(ScriptInterface::CxPrivate* pCxPrivate, int progress)\n{\n\tCMapGeneratorWorker* self = static_cast<CMapGeneratorWorker*>(pCxPrivate->pCBData);\n\n\t// Copy data\n\tCScopeLock lock(self->m_WorkerMutex);\n\tself->m_Progress = progress;\n}\n\nvoid CMapGeneratorWorker::MaybeGC(ScriptInterface::CxPrivate* pCxPrivate)\n{\n\tCMapGeneratorWorker* self = static_cast<CMapGeneratorWorker*>(pCxPrivate->pCBData);\n\tself->m_ScriptInterface->MaybeGC();\n}\n\nstd::vector<std::string> CMapGeneratorWorker::GetCivData(ScriptInterface::CxPrivate* UNUSED(pCxPrivate))\n{\n\tVfsPath path(L\"simulation/data/civs/\");\n\tVfsPaths pathnames;\n\n\tstd::vector<std::string> data;\n\n\t// Load all JSON files in civs directory\n\tStatus ret = vfs::GetPathnames(g_VFS, path, L\"*.json\", pathnames);\n\tif (ret == INFO::OK)\n\t{\n\t\tfor (const VfsPath& p : pathnames)\n\t\t{\n\t\t\t// Load JSON file\n\t\t\tCVFSFile file;\n\t\t\tPSRETURN ret = file.Load(g_VFS, p);\n\t\t\tif (ret != PSRETURN_OK)\n\t\t\t\tLOGERROR(\"CMapGeneratorWorker::GetCivData: Failed to load file '%s': %s\", p.string8(), GetErrorString(ret));\n\t\t\telse\n\t\t\t\tdata.push_back(file.DecodeUTF8()); // assume it's UTF-8\n\t\t}\n\t}\n\telse\n\t{\n\t\t// Some error reading directory\n\t\twchar_t error[200];\n\t\tLOGERROR(\"CMapGeneratorWorker::GetCivData: Error reading directory '%s': %s\", path.string8(), utf8_from_wstring(StatusDescription(ret, error, ARRAY_SIZE(error))));\n\t}\n\n\treturn data;\n\n}\n\nCParamNode CMapGeneratorWorker::GetTemplate(ScriptInterface::CxPrivate* pCxPrivate, const std::string& templateName)\n{\n\tCMapGeneratorWorker* self = static_cast<CMapGeneratorWorker*>(pCxPrivate->pCBData);\n\tconst CParamNode& templateRoot = self->m_TemplateLoader.GetTemplateFileData(templateName).GetChild(\"Entity\");\n\tif (!templateRoot.IsOk())\n\t\tLOGERROR(\"Invalid template found for '%s'\", templateName.c_str());\n\t\n\treturn templateRoot;\n}\n\nbool CMapGeneratorWorker::TemplateExists(ScriptInterface::CxPrivate* pCxPrivate, const std::string& templateName)\n{\n\tCMapGeneratorWorker* self = static_cast<CMapGeneratorWorker*>(pCxPrivate->pCBData);\n\treturn self->m_TemplateLoader.TemplateExists(templateName);\n}\n\nstd::vector<std::string> CMapGeneratorWorker::FindTemplates(ScriptInterface::CxPrivate* pCxPrivate, const std::string& path, bool includeSubdirectories)\n{\n\tCMapGeneratorWorker* self = static_cast<CMapGeneratorWorker*>(pCxPrivate->pCBData);\n\treturn self->m_TemplateLoader.FindTemplates(path, includeSubdirectories, SIMULATION_TEMPLATES);\n}\n\nstd::vector<std::string> CMapGeneratorWorker::FindActorTemplates(ScriptInterface::CxPrivate* pCxPrivate, const std::string& path, bool includeSubdirectories)\n{\n\tCMapGeneratorWorker* self = static_cast<CMapGeneratorWorker*>(pCxPrivate->pCBData);\n\treturn self->m_TemplateLoader.FindTemplates(path, includeSubdirectories, ACTOR_TEMPLATES);\n}\n\nbool CMapGeneratorWorker::LoadScripts(const std::wstring& libraryName)\n{\n\t// Ignore libraries that are already loaded\n\tif (m_LoadedLibraries.find(libraryName) != m_LoadedLibraries.end())\n\t\treturn true;\n\n\t// Mark this as loaded, to prevent it recursively loading itself\n\tm_LoadedLibraries.insert(libraryName);\n\n\tVfsPath path = L\"maps/random/\" + libraryName + L\"/\";\n\tVfsPaths pathnames;\n\n\t// Load all scripts in mapgen directory\n\tStatus ret = vfs::GetPathnames(g_VFS, path, L\"*.js\", pathnames);\n\tif (ret == INFO::OK)\n\t{\n\t\tfor (const VfsPath& p : pathnames)\n\t\t{\n\t\t\tLOGMESSAGE(\"Loading map generator script '%s'\", p.string8());\n\n\t\t\tif (!m_ScriptInterface->LoadGlobalScriptFile(p))\n\t\t\t{\n\t\t\t\tLOGERROR(\"CMapGeneratorWorker::LoadScripts: Failed to load script '%s'\", p.string8());\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\t// Some error reading directory\n\t\twchar_t error[200];\n\t\tLOGERROR(\"CMapGeneratorWorker::LoadScripts: Error reading scripts in directory '%s': %s\", path.string8(), utf8_from_wstring(StatusDescription(ret, error, ARRAY_SIZE(error))));\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n//////////////////////////////////////////////////////////////////////////////////\n//////////////////////////////////////////////////////////////////////////////////\n\nCMapGenerator::CMapGenerator() : m_Worker(new CMapGeneratorWorker())\n{\n}\n\nCMapGenerator::~CMapGenerator()\n{\n\tdelete m_Worker;\n}\n\nvoid CMapGenerator::GenerateMap(const VfsPath& scriptFile, const std::string& settings)\n{\n\tm_Worker->Initialize(scriptFile, settings);\n}\n\nint CMapGenerator::GetProgress()\n{\n\treturn m_Worker->GetProgress();\n}\n\nshared_ptr<ScriptInterface::StructuredClone> CMapGenerator::GetResults()\n{\n\treturn m_Worker->GetResults();\n}\n"
  },
  {
    "path": "fpsgame/graphics/MapGenerator.h",
    "content": "/* Copyright (C) 2015 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef INCLUDED_MAPGENERATOR\n#define INCLUDED_MAPGENERATOR\n\n#include \"ps/FileIo.h\"\n#include \"ps/ThreadUtil.h\"\n#include \"ps/TemplateLoader.h\"\n#include \"scriptinterface/ScriptInterface.h\"\n\n#include <boost/random/linear_congruential.hpp>\n\n#include <set>\n\nclass CMapGeneratorWorker;\n\n/**\n * Random map generator interface. Initialized by CMapReader and then checked\n * periodically during loading, until it's finished (progress value is 0).\n *\n * The actual work is performed by CMapGeneratorWorker in a separate thread.\n */\nclass CMapGenerator\n{\n\tNONCOPYABLE(CMapGenerator);\n\npublic:\n\tCMapGenerator();\n\t~CMapGenerator();\n\n\t/**\n\t * Start the map generator thread\n\t *\n\t * @param scriptFile The VFS path for the script, e.g. \"maps/random/latium.js\"\n\t * @param settings JSON string containing settings for the map generator\n\t */\n\tvoid GenerateMap(const VfsPath& scriptFile, const std::string& settings);\n\n\t/**\n\t * Get status of the map generator thread\n\t *\n\t * @return Progress percentage 1-100 if active, 0 when finished, or -1 on error \n\t */\n\tint GetProgress();\n\n\t/**\n\t * Get random map data, according to this format:\n\t * http://trac.wildfiregames.com/wiki/Random_Map_Generator_Internals#Dataformat\n\t *\n\t * @return StructuredClone containing map data\n\t */\n\tshared_ptr<ScriptInterface::StructuredClone> GetResults();\n\nprivate:\n\tCMapGeneratorWorker* m_Worker;\n\n};\n\n/**\n * Random map generator worker thread.\n * (This is run in a thread so that the GUI remains responsive while loading)\n *\n * Thread-safety:\n * - Initialize and constructor/destructor must be called from the main thread.\n * - ScriptInterface created and destroyed by thread\n * - StructuredClone used to return JS map data - jsvals can't be used across threads/runtimes\n */\nclass CMapGeneratorWorker\n{\npublic:\n\tCMapGeneratorWorker();\n\t~CMapGeneratorWorker();\n\n\t/**\n\t * Start the map generator thread\n\t *\n\t * @param scriptFile The VFS path for the script, e.g. \"maps/random/latium.js\"\n\t * @param settings JSON string containing settings for the map generator\n\t */\n\tvoid Initialize(const VfsPath& scriptFile, const std::string& settings);\n\n\t/**\n\t * Get status of the map generator thread\n\t *\n\t * @return Progress percentage 1-100 if active, 0 when finished, or -1 on error \n\t */\n\tint GetProgress();\n\n\t/**\n\t * Get random map data, according to this format:\n\t * http://trac.wildfiregames.com/wiki/Random_Map_Generator_Internals#Dataformat\n\t *\n\t * @return StructuredClone containing map data\n\t */\n\tshared_ptr<ScriptInterface::StructuredClone> GetResults();\n\t\nprivate:\n// Mapgen\n\n\t/**\n\t * Load all scripts of the given library\n\t * \n\t * @param libraryName String specifying name of the library (subfolder of ../maps/random/)\n\t * @return true if all scripts ran successfully, false if there's an error\n\t */\n\tbool LoadScripts(const std::wstring& libraryName);\n\t\n\t// callbacks for script functions\n\tstatic bool LoadLibrary(ScriptInterface::CxPrivate* pCxPrivate, const std::wstring& name);\n\tstatic void ExportMap(ScriptInterface::CxPrivate* pCxPrivate, JS::HandleValue data);\n\tstatic void SetProgress(ScriptInterface::CxPrivate* pCxPrivate, int progress);\n\tstatic void MaybeGC(ScriptInterface::CxPrivate* pCxPrivate);\n\tstatic std::vector<std::string> GetCivData(ScriptInterface::CxPrivate* pCxPrivate);\n\tstatic CParamNode GetTemplate(ScriptInterface::CxPrivate* pCxPrivate, const std::string& templateName);\n\tstatic bool TemplateExists(ScriptInterface::CxPrivate* pCxPrivate, const std::string& templateName);\n\tstatic std::vector<std::string> FindTemplates(ScriptInterface::CxPrivate* pCxPrivate, const std::string& path, bool includeSubdirectories);\n\tstatic std::vector<std::string> FindActorTemplates(ScriptInterface::CxPrivate* pCxPrivate, const std::string& path, bool includeSubdirectories);\n\n\tstd::set<std::wstring> m_LoadedLibraries;\n\tshared_ptr<ScriptInterface::StructuredClone> m_MapData;\n\tboost::rand48 m_MapGenRNG;\n\tint m_Progress;\n\tScriptInterface* m_ScriptInterface;\n\tVfsPath m_ScriptPath;\n\tstd::string m_Settings;\n\tCTemplateLoader m_TemplateLoader;\n\n// Thread\n\tstatic void* RunThread(void* data);\n\tbool Run();\n\n\tpthread_t m_WorkerThread;\n\tCMutex m_WorkerMutex;\n};\n\n\n#endif\t//INCLUDED_MAPGENERATOR\n"
  },
  {
    "path": "fpsgame/graphics/MapIO.h",
    "content": "/* Copyright (C) 2009 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef INCLUDED_MAPIO\n#define INCLUDED_MAPIO\n\nclass CMapIO \n{\npublic:\n\t// current file version given to saved maps \n\tenum { FILE_VERSION = 6 };\n\t// supported file read version - file with version less than this will be reject\n\tenum { FILE_READ_VERSION = 6 };\n\n#pragma pack(push, 1)\n\t// description of a tile for I/O purposes\n\tstruct STileDesc {\n\t\t// index into the texture array of first texture on tile\n\t\tu16 m_Tex1Index;\n\t\t// index into the texture array of second texture; (0xFFFF) if none\n\t\tu16 m_Tex2Index;\n\t\t// priority\n\t\tu32 m_Priority;\n\t};\n#pragma pack(pop)\n};\n\n#endif\n"
  },
  {
    "path": "fpsgame/graphics/MapReader.cpp",
    "content": "/* Copyright (C) 2016 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"precompiled.h\"\n\n#include \"MapReader.h\"\n\n#include \"graphics/Camera.h\"\n#include \"graphics/CinemaManager.h\"\n#include \"graphics/Entity.h\"\n#include \"graphics/GameView.h\"\n#include \"graphics/MapGenerator.h\"\n#include \"graphics/Patch.h\"\n#include \"graphics/Terrain.h\"\n#include \"graphics/TerrainTextureEntry.h\"\n#include \"graphics/TerrainTextureManager.h\"\n#include \"lib/timer.h\"\n#include \"lib/external_libraries/libsdl.h\"\n#include \"maths/MathUtil.h\"\n#include \"ps/CLogger.h\"\n#include \"ps/Loader.h\"\n#include \"ps/LoaderThunks.h\"\n#include \"ps/World.h\"\n#include \"ps/XML/Xeromyces.h\"\n#include \"renderer/PostprocManager.h\"\n#include \"renderer/SkyManager.h\"\n#include \"renderer/WaterManager.h\"\n#include \"simulation2/Simulation2.h\"\n#include \"simulation2/components/ICmpObstruction.h\"\n#include \"simulation2/components/ICmpOwnership.h\"\n#include \"simulation2/components/ICmpPlayer.h\"\n#include \"simulation2/components/ICmpPlayerManager.h\"\n#include \"simulation2/components/ICmpPosition.h\"\n#include \"simulation2/components/ICmpTerrain.h\"\n#include \"simulation2/components/ICmpVisual.h\"\n#include \"simulation2/components/ICmpWaterManager.h\"\n\n#include <boost/algorithm/string/predicate.hpp>\n\n\nCMapReader::CMapReader()\n\t: xml_reader(0), m_PatchesPerSide(0), m_MapGen(0)\n{\n\tcur_terrain_tex = 0;\t// important - resets generator state\n\n\t// Maps that don't override the default probably want the old lighting model\n\t//m_LightEnv.SetLightingModel(\"old\");\n\t//pPostproc->SetPostEffect(L\"default\");\n\n}\n\n// LoadMap: try to load the map from given file; reinitialise the scene to new data if successful\nvoid CMapReader::LoadMap(const VfsPath& pathname, JSRuntime* rt,  JS::HandleValue settings, CTerrain *pTerrain_,\n\t\t\t\t\t\t WaterManager* pWaterMan_, SkyManager* pSkyMan_,\n\t\t\t\t\t\t CLightEnv *pLightEnv_, CGameView *pGameView_, CCinemaManager* pCinema_, CTriggerManager* pTrigMan_, CPostprocManager* pPostproc_,\n\t\t\t\t\t\t CSimulation2 *pSimulation2_, const CSimContext* pSimContext_, int playerID_, bool skipEntities)\n{\n\t// latch parameters (held until DelayedLoadFinished)\n\tpTerrain = pTerrain_;\n\tpLightEnv = pLightEnv_;\n\tpGameView = pGameView_;\n\tpWaterMan = pWaterMan_;\n\tpSkyMan = pSkyMan_;\n\tpCinema = pCinema_;\n\tpTrigMan = pTrigMan_;\n\tpPostproc = pPostproc_;\n\tpSimulation2 = pSimulation2_;\n\tpSimContext = pSimContext_;\n\tm_PlayerID = playerID_;\n\tm_SkipEntities = skipEntities;\n\tm_StartingCameraTarget = INVALID_ENTITY;\n\tm_ScriptSettings.init(rt, settings);\n\n\tfilename_xml = pathname.ChangeExtension(L\".xml\");\n\n\t// In some cases (particularly tests) we don't want to bother storing a large\n\t// mostly-empty .pmp file, so we let the XML file specify basic terrain instead.\n\t// If there's an .xml file and no .pmp, then we're probably in this XML-only mode\n\tonly_xml = false;\n\tif (!VfsFileExists(pathname) && VfsFileExists(filename_xml))\n\t{\n\t\tonly_xml = true;\n\t}\n\n\tfile_format_version = CMapIO::FILE_VERSION; // default if there's no .pmp\n\n\tif (!only_xml)\n\t{\n\t\t// [25ms]\n\t\tunpacker.Read(pathname, \"PSMP\");\n\t\tfile_format_version = unpacker.GetVersion();\n\t}\n\n\t// check oldest supported version\n\tif (file_format_version < FILE_READ_VERSION)\n\t\tthrow PSERROR_File_InvalidVersion();\n\n\t// delete all existing entities\n\tif (pSimulation2)\n\t\tpSimulation2->ResetState();\n\n\t// reset post effects\n\tif (pPostproc)\n\t\tpPostproc->SetPostEffect(L\"default\");\n\n\t// load map or script settings script\n\tif (settings.isUndefined())\n\t\tRegMemFun(this, &CMapReader::LoadScriptSettings, L\"CMapReader::LoadScriptSettings\", 50);\n\telse\n\t\tRegMemFun(this, &CMapReader::LoadRMSettings, L\"CMapReader::LoadRMSettings\", 50);\n\n\t// load player settings script (must be done before reading map)\n\tRegMemFun(this, &CMapReader::LoadPlayerSettings, L\"CMapReader::LoadPlayerSettings\", 50);\n\n\t// unpack the data\n\tif (!only_xml)\n\t\tRegMemFun(this, &CMapReader::UnpackMap, L\"CMapReader::UnpackMap\", 1200);\n\n\t// read the corresponding XML file\n\tRegMemFun(this, &CMapReader::ReadXML, L\"CMapReader::ReadXML\", 50);\n\n\t// apply terrain data to the world\n\tRegMemFun(this, &CMapReader::ApplyTerrainData, L\"CMapReader::ApplyTerrainData\", 5);\n\n\t// read entities\n\tRegMemFun(this, &CMapReader::ReadXMLEntities, L\"CMapReader::ReadXMLEntities\", 5800);\n\n\t// apply misc data to the world\n\tRegMemFun(this, &CMapReader::ApplyData, L\"CMapReader::ApplyData\", 5);\n\n\t// load map settings script (must be done after reading map)\n\tRegMemFun(this, &CMapReader::LoadMapSettings, L\"CMapReader::LoadMapSettings\", 5);\n\n\tRegMemFun(this, &CMapReader::DelayLoadFinished, L\"CMapReader::DelayLoadFinished\", 5);\n}\n\n// LoadRandomMap: try to load the map data; reinitialise the scene to new data if successful\nvoid CMapReader::LoadRandomMap(const CStrW& scriptFile, JSRuntime* rt, JS::HandleValue settings, CTerrain *pTerrain_,\n\t\t\t\t\t\t WaterManager* pWaterMan_, SkyManager* pSkyMan_,\n\t\t\t\t\t\t CLightEnv *pLightEnv_, CGameView *pGameView_, CCinemaManager* pCinema_, CTriggerManager* pTrigMan_, CPostprocManager* pPostproc_,\n\t\t\t\t\t\t CSimulation2 *pSimulation2_, int playerID_)\n{\n\t// latch parameters (held until DelayedLoadFinished)\n\tm_ScriptFile = scriptFile;\n\tpSimulation2 = pSimulation2_;\n\tpSimContext = pSimulation2 ? &pSimulation2->GetSimContext() : NULL;\n\tm_ScriptSettings.init(rt, settings);\n\tpTerrain = pTerrain_;\n\tpLightEnv = pLightEnv_;\n\tpGameView = pGameView_;\n\tpWaterMan = pWaterMan_;\n\tpSkyMan = pSkyMan_;\n\tpCinema = pCinema_;\n\tpTrigMan = pTrigMan_;\n\tpPostproc = pPostproc_;\n\tm_PlayerID = playerID_;\n\tm_SkipEntities = false;\n\tm_StartingCameraTarget = INVALID_ENTITY;\n\n\t// delete all existing entities\n\tif (pSimulation2)\n\t\tpSimulation2->ResetState();\n\n\tonly_xml = false;\n\n\t// copy random map settings (before entity creation)\n\tRegMemFun(this, &CMapReader::LoadRMSettings, L\"CMapReader::LoadRMSettings\", 50);\n\n\t// load player settings script (must be done before reading map)\n\tRegMemFun(this, &CMapReader::LoadPlayerSettings, L\"CMapReader::LoadPlayerSettings\", 50);\n\n\t// load map generator with random map script\n\tRegMemFun(this, &CMapReader::GenerateMap, L\"CMapReader::GenerateMap\", 5000);\n\n\t// parse RMS results into terrain structure\n\tRegMemFun(this, &CMapReader::ParseTerrain, L\"CMapReader::ParseTerrain\", 500);\n\n\t// parse RMS results into environment settings\n\tRegMemFun(this, &CMapReader::ParseEnvironment, L\"CMapReader::ParseEnvironment\", 5);\n\n\t// parse RMS results into camera settings\n\tRegMemFun(this, &CMapReader::ParseCamera, L\"CMapReader::ParseCamera\", 5);\n\n\t// apply terrain data to the world\n\tRegMemFun(this, &CMapReader::ApplyTerrainData, L\"CMapReader::ApplyTerrainData\", 5);\n\n\t// parse RMS results into entities\n\tRegMemFun(this, &CMapReader::ParseEntities, L\"CMapReader::ParseEntities\", 1000);\n\n\t// apply misc data to the world\n\tRegMemFun(this, &CMapReader::ApplyData, L\"CMapReader::ApplyData\", 5);\n\n\t// load map settings script (must be done after reading map)\n\tRegMemFun(this, &CMapReader::LoadMapSettings, L\"CMapReader::LoadMapSettings\", 5);\n\n\tRegMemFun(this, &CMapReader::DelayLoadFinished, L\"CMapReader::DelayLoadFinished\", 5);\n}\n\n// UnpackMap: unpack the given data from the raw data stream into local variables\nint CMapReader::UnpackMap()\n{\n\t// now unpack everything into local data\n\tint ret = UnpackTerrain();\n\tif (ret != 0)\t// failed or timed out\n\t{\n\t\treturn ret;\n\t}\n\n\treturn 0;\n}\n\n// UnpackTerrain: unpack the terrain from the end of the input data stream\n//\t\t- data: map size, heightmap, list of textures used by map, texture tile assignments\nint CMapReader::UnpackTerrain()\n{\n\t// yield after this time is reached. balances increased progress bar\n\t// smoothness vs. slowing down loading.\n\tconst double end_time = timer_Time() + 200e-3;\n\n\t// first call to generator (this is skipped after first call,\n\t// i.e. when the loop below was interrupted)\n\tif (cur_terrain_tex == 0)\n\t{\n\t\tm_PatchesPerSide = (ssize_t)unpacker.UnpackSize();\n\n\t\t// unpack heightmap [600us]\n\t\tsize_t verticesPerSide = m_PatchesPerSide*PATCH_SIZE+1;\n\t\tm_Heightmap.resize(SQR(verticesPerSide));\n\t\tunpacker.UnpackRaw(&m_Heightmap[0], SQR(verticesPerSide)*sizeof(u16));\n\n\t\t// unpack # textures\n\t\tnum_terrain_tex = unpacker.UnpackSize();\n\t\tm_TerrainTextures.reserve(num_terrain_tex);\n\t}\n\n\t// unpack texture names; find handle for each texture.\n\t// interruptible.\n\twhile (cur_terrain_tex < num_terrain_tex)\n\t{\n\t\tCStr texturename;\n\t\tunpacker.UnpackString(texturename);\n\n\t\tENSURE(CTerrainTextureManager::IsInitialised()); // we need this for the terrain properties (even when graphics are disabled)\n\t\tCTerrainTextureEntry* texentry = g_TexMan.FindTexture(texturename);\n\t\tm_TerrainTextures.push_back(texentry);\n\n\t\tcur_terrain_tex++;\n\t\tLDR_CHECK_TIMEOUT(cur_terrain_tex, num_terrain_tex);\n\t}\n\n\t// unpack tile data [3ms]\n\tssize_t tilesPerSide = m_PatchesPerSide*PATCH_SIZE;\n\tm_Tiles.resize(size_t(SQR(tilesPerSide)));\n\tunpacker.UnpackRaw(&m_Tiles[0], sizeof(STileDesc)*m_Tiles.size());\n\n\t// reset generator state.\n\tcur_terrain_tex = 0;\n\n\treturn 0;\n}\n\nint CMapReader::ApplyTerrainData()\n{\n\tif (m_PatchesPerSide == 0)\n\t{\n\t\t// we'll probably crash when trying to use this map later\n\t\tthrow PSERROR_Game_World_MapLoadFailed(\"Error loading map: no terrain data.\\nCheck application log for details.\");\n\t}\n\n\tif (!only_xml)\n\t{\n\t\t// initialise the terrain\n\t\tpTerrain->Initialize(m_PatchesPerSide, &m_Heightmap[0]);\n\n\t\t// setup the textures on the minipatches\n\t\tSTileDesc* tileptr = &m_Tiles[0];\n\t\tfor (ssize_t j=0; j<m_PatchesPerSide; j++) {\n\t\t\tfor (ssize_t i=0; i<m_PatchesPerSide; i++) {\n\t\t\t\tfor (ssize_t m=0; m<PATCH_SIZE; m++) {\n\t\t\t\t\tfor (ssize_t k=0; k<PATCH_SIZE; k++) {\n\t\t\t\t\t\tCMiniPatch& mp = pTerrain->GetPatch(i,j)->m_MiniPatches[m][k];\t// can't fail\n\n\t\t\t\t\t\tmp.Tex = m_TerrainTextures[tileptr->m_Tex1Index];\n\t\t\t\t\t\tmp.Priority = tileptr->m_Priority;\n\n\t\t\t\t\t\ttileptr++;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tCmpPtr<ICmpTerrain> cmpTerrain(*pSimContext, SYSTEM_ENTITY);\n\tif (cmpTerrain)\n\t\tcmpTerrain->ReloadTerrain();\n\n\treturn 0;\n}\n\n// ApplyData: take all the input data, and rebuild the scene from it\nint CMapReader::ApplyData()\n{\n\t// copy over the lighting parameters\n\tif (pLightEnv)\n\t\t*pLightEnv = m_LightEnv;\n\n\tCmpPtr<ICmpPlayerManager> cmpPlayerManager(*pSimContext, SYSTEM_ENTITY);\n\n\tif (pGameView && cmpPlayerManager)\n\t{\n\t\t// Default to global camera (with constraints)\n\t\tpGameView->ResetCameraTarget(pGameView->GetCamera()->GetFocus());\n\n\t\t// TODO: Starting rotation?\n\t\tCmpPtr<ICmpPlayer> cmpPlayer(*pSimContext, cmpPlayerManager->GetPlayerByID(m_PlayerID));\n\t\tif (cmpPlayer && cmpPlayer->HasStartingCamera())\n\t\t{\n\t\t\t// Use player starting camera\n\t\t\tCFixedVector3D pos = cmpPlayer->GetStartingCameraPos();\n\t\t\tpGameView->ResetCameraTarget(CVector3D(pos.X.ToFloat(), pos.Y.ToFloat(), pos.Z.ToFloat()));\n\t\t}\n\t\telse if (m_StartingCameraTarget != INVALID_ENTITY)\n\t\t{\n\t\t\t// Point camera at entity\n\t\t\tCmpPtr<ICmpPosition> cmpPosition(*pSimContext, m_StartingCameraTarget);\n\t\t\tif (cmpPosition)\n\t\t\t{\n\t\t\t\tCFixedVector3D pos = cmpPosition->GetPosition();\n\t\t\t\tpGameView->ResetCameraTarget(CVector3D(pos.X.ToFloat(), pos.Y.ToFloat(), pos.Z.ToFloat()));\n\t\t\t}\n\t\t}\n\t}\n\n\treturn 0;\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n\nPSRETURN CMapSummaryReader::LoadMap(const VfsPath& pathname)\n{\n\tVfsPath filename_xml = pathname.ChangeExtension(L\".xml\");\n\n\tCXeromyces xmb_file;\n\tif (xmb_file.Load(g_VFS, filename_xml, \"scenario\") != PSRETURN_OK)\n\t\treturn PSRETURN_File_ReadFailed;\n\n\t// Define all the relevant elements used in the XML file\n\t#define EL(x) int el_##x = xmb_file.GetElementID(#x)\n\t#define AT(x) int at_##x = xmb_file.GetAttributeID(#x)\n\tEL(scenario);\n\tEL(scriptsettings);\n\t#undef AT\n\t#undef EL\n\n\tXMBElement root = xmb_file.GetRoot();\n\tENSURE(root.GetNodeName() == el_scenario);\n\n\tXERO_ITER_EL(root, child)\n\t{\n\t\tint child_name = child.GetNodeName();\n\t\tif (child_name == el_scriptsettings)\n\t\t{\n\t\t\tm_ScriptSettings = child.GetText();\n\t\t}\n\t}\n\n\treturn PSRETURN_OK;\n}\n\nvoid CMapSummaryReader::GetMapSettings(ScriptInterface& scriptInterface, JS::MutableHandleValue ret)\n{\n\tJSContext* cx = scriptInterface.GetContext();\n\tJSAutoRequest rq(cx);\n\n\tscriptInterface.Eval(\"({})\", ret);\n\tif (m_ScriptSettings.empty())\n\t\treturn;\n\n\tJS::RootedValue scriptSettingsVal(cx);\n\tscriptInterface.ParseJSON(m_ScriptSettings, &scriptSettingsVal);\n\tscriptInterface.SetProperty(ret, \"settings\", scriptSettingsVal, false);\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n\n// Holds various state data while reading maps, so that loading can be\n// interrupted (e.g. to update the progress display) then later resumed.\nclass CXMLReader\n{\n\tNONCOPYABLE(CXMLReader);\npublic:\n\tCXMLReader(const VfsPath& xml_filename, CMapReader& mapReader)\n\t\t: m_MapReader(mapReader), nodes(NULL, 0, NULL)\n\t{\n\t\tInit(xml_filename);\n\t}\n\n\tCStr ReadScriptSettings();\n\n\t// read everything except for entities\n\tvoid ReadXML();\n\n\t// return semantics: see Loader.cpp!LoadFunc.\n\tint ProgressiveReadEntities();\n\nprivate:\n\tCXeromyces xmb_file;\n\n\tCMapReader& m_MapReader;\n\n\tint el_entity;\n\tint el_tracks;\n\tint el_template, el_player;\n\tint el_position, el_orientation, el_obstruction;\n\tint el_actor;\n\tint at_x, at_y, at_z;\n\tint at_group, at_group2;\n\tint at_angle;\n\tint at_uid;\n\tint at_seed;\n\n\tXMBElementList nodes; // children of root\n\n\t// loop counters\n\tsize_t node_idx;\n\tsize_t entity_idx;\n\n\t// # entities+nonentities processed and total (for progress calc)\n\tint completed_jobs, total_jobs;\n\n\t// maximum used entity ID, so we can safely allocate new ones\n\tentity_id_t max_uid;\n\n\tvoid Init(const VfsPath& xml_filename);\n\n\tvoid ReadTerrain(XMBElement parent);\n\tvoid ReadEnvironment(XMBElement parent);\n\tvoid ReadCamera(XMBElement parent);\n\tvoid ReadPaths(XMBElement parent);\n\tvoid ReadTriggers(XMBElement parent);\n\tint ReadEntities(XMBElement parent, double end_time);\n};\n\n\nvoid CXMLReader::Init(const VfsPath& xml_filename)\n{\n\t// must only assign once, so do it here\n\tnode_idx = entity_idx = 0;\n\n\tif (xmb_file.Load(g_VFS, xml_filename, \"scenario\") != PSRETURN_OK)\n\t\tthrow PSERROR_File_ReadFailed();\n\n\t// define the elements and attributes that are frequently used in the XML file,\n\t// so we don't need to do lots of string construction and comparison when\n\t// reading the data.\n\t// (Needs to be synchronised with the list in CXMLReader - ugh)\n#define EL(x) el_##x = xmb_file.GetElementID(#x)\n#define AT(x) at_##x = xmb_file.GetAttributeID(#x)\n\tEL(entity);\n\tEL(tracks);\n\tEL(template);\n\tEL(player);\n\tEL(position);\n\tEL(orientation);\n\tEL(obstruction);\n\tEL(actor);\n\tAT(x); AT(y); AT(z);\n\tAT(group); AT(group2);\n\tAT(angle);\n\tAT(uid);\n\tAT(seed);\n#undef AT\n#undef EL\n\n\tXMBElement root = xmb_file.GetRoot();\n\tENSURE(xmb_file.GetElementString(root.GetNodeName()) == \"Scenario\");\n\tnodes = root.GetChildNodes();\n\n\t// find out total number of entities+nonentities\n\t// (used when calculating progress)\n\tcompleted_jobs = 0;\n\ttotal_jobs = 0;\n\tfor (XMBElement node : nodes)\n\t\ttotal_jobs += node.GetChildNodes().size();\n\n\t// Find the maximum entity ID, so we can safely allocate new IDs without conflicts\n\n\tmax_uid = SYSTEM_ENTITY;\n\n\tXMBElement ents = nodes.GetFirstNamedItem(xmb_file.GetElementID(\"Entities\"));\n\tXERO_ITER_EL(ents, ent)\n\t{\n\t\tCStr uid = ent.GetAttributes().GetNamedItem(at_uid);\n\t\tmax_uid = std::max(max_uid, (entity_id_t)uid.ToUInt());\n\t}\n}\n\n\nCStr CXMLReader::ReadScriptSettings()\n{\n\tXMBElement root = xmb_file.GetRoot();\n\tENSURE(xmb_file.GetElementString(root.GetNodeName()) == \"Scenario\");\n\tnodes = root.GetChildNodes();\n\n\tXMBElement settings = nodes.GetFirstNamedItem(xmb_file.GetElementID(\"ScriptSettings\"));\n\n\treturn settings.GetText();\n}\n\n\nvoid CXMLReader::ReadTerrain(XMBElement parent)\n{\n#define AT(x) int at_##x = xmb_file.GetAttributeID(#x)\n\tAT(patches);\n\tAT(texture);\n\tAT(priority);\n\tAT(height);\n#undef AT\n\n\tssize_t patches = 9;\n\tCStr texture = \"grass1_spring\";\n\tint priority = 0;\n\tu16 height = 16384;\n\n\tXERO_ITER_ATTR(parent, attr)\n\t{\n\t\tif (attr.Name == at_patches)\n\t\t\tpatches = attr.Value.ToInt();\n\t\telse if (attr.Name == at_texture)\n\t\t\ttexture = attr.Value;\n\t\telse if (attr.Name == at_priority)\n\t\t\tpriority = attr.Value.ToInt();\n\t\telse if (attr.Name == at_height)\n\t\t\theight = (u16)attr.Value.ToInt();\n\t}\n\n\tm_MapReader.m_PatchesPerSide = patches;\n\n\t// Load the texture\n\tENSURE(CTerrainTextureManager::IsInitialised()); // we need this for the terrain properties (even when graphics are disabled)\n\tCTerrainTextureEntry* texentry = g_TexMan.FindTexture(texture);\n\n\tm_MapReader.pTerrain->Initialize(patches, NULL);\n\n\t// Fill the heightmap\n\tu16* heightmap = m_MapReader.pTerrain->GetHeightMap();\n\tssize_t verticesPerSide = m_MapReader.pTerrain->GetVerticesPerSide();\n\tfor (ssize_t i = 0; i < SQR(verticesPerSide); ++i)\n\t\theightmap[i] = height;\n\n\t// Fill the texture map\n\tfor (ssize_t pz = 0; pz < patches; ++pz)\n\t{\n\t\tfor (ssize_t px = 0; px < patches; ++px)\n\t\t{\n\t\t\tCPatch* patch = m_MapReader.pTerrain->GetPatch(px, pz);\t// can't fail\n\n\t\t\tfor (ssize_t z = 0; z < PATCH_SIZE; ++z)\n\t\t\t{\n\t\t\t\tfor (ssize_t x = 0; x < PATCH_SIZE; ++x)\n\t\t\t\t{\n\t\t\t\t\tpatch->m_MiniPatches[z][x].Tex = texentry;\n\t\t\t\t\tpatch->m_MiniPatches[z][x].Priority = priority;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid CXMLReader::ReadEnvironment(XMBElement parent)\n{\n#define EL(x) int el_##x = xmb_file.GetElementID(#x)\n#define AT(x) int at_##x = xmb_file.GetAttributeID(#x)\n\tEL(lightingmodel);\n\tEL(posteffect);\n\tEL(skyset);\n\tEL(suncolor);\n\tEL(sunelevation);\n\tEL(sunrotation);\n\tEL(terrainambientcolor);\n\tEL(unitsambientcolor);\n\tEL(water);\n\tEL(waterbody);\n\tEL(type);\n\tEL(color);\n\tEL(tint);\n\tEL(height);\n\tEL(shininess);\t// for compatibility\n\tEL(waviness);\n\tEL(murkiness);\n\tEL(windangle);\n\tEL(reflectiontint);\t// for compatibility\n\tEL(reflectiontintstrength);\t// for compatibility\n\tEL(fog);\n\tEL(fogcolor);\n\tEL(fogfactor);\n\tEL(fogthickness);\n\tEL(postproc);\n\tEL(brightness);\n\tEL(contrast);\n\tEL(saturation);\n\tEL(bloom);\n\tAT(r); AT(g); AT(b);\n#undef AT\n#undef EL\n\n\tXERO_ITER_EL(parent, element)\n\t{\n\t\tint element_name = element.GetNodeName();\n\n\t\tXMBAttributeList attrs = element.GetAttributes();\n\n\t\tif (element_name == el_lightingmodel)\n\t\t{\n\t\t\t// NOP - obsolete.\n\t\t}\n\t\telse if (element_name == el_skyset)\n\t\t{\n\t\t\tif (m_MapReader.pSkyMan)\n\t\t\t\tm_MapReader.pSkyMan->SetSkySet(element.GetText().FromUTF8());\n\t\t}\n\t\telse if (element_name == el_suncolor)\n\t\t{\n\t\t\tm_MapReader.m_LightEnv.m_SunColor = RGBColor(\n\t\t\t\tattrs.GetNamedItem(at_r).ToFloat(),\n\t\t\t\tattrs.GetNamedItem(at_g).ToFloat(),\n\t\t\t\tattrs.GetNamedItem(at_b).ToFloat());\n\t\t}\n\t\telse if (element_name == el_sunelevation)\n\t\t{\n\t\t\tm_MapReader.m_LightEnv.m_Elevation = attrs.GetNamedItem(at_angle).ToFloat();\n\t\t}\n\t\telse if (element_name == el_sunrotation)\n\t\t{\n\t\t\tm_MapReader.m_LightEnv.m_Rotation = attrs.GetNamedItem(at_angle).ToFloat();\n\t\t}\n\t\telse if (element_name == el_terrainambientcolor)\n\t\t{\n\t\t\tm_MapReader.m_LightEnv.m_TerrainAmbientColor = RGBColor(\n\t\t\t\tattrs.GetNamedItem(at_r).ToFloat(),\n\t\t\t\tattrs.GetNamedItem(at_g).ToFloat(),\n\t\t\t\tattrs.GetNamedItem(at_b).ToFloat());\n\t\t}\n\t\telse if (element_name == el_unitsambientcolor)\n\t\t{\n\t\t\tm_MapReader.m_LightEnv.m_UnitsAmbientColor = RGBColor(\n\t\t\t\tattrs.GetNamedItem(at_r).ToFloat(),\n\t\t\t\tattrs.GetNamedItem(at_g).ToFloat(),\n\t\t\t\tattrs.GetNamedItem(at_b).ToFloat());\n\t\t}\n\t\telse if (element_name == el_fog)\n\t\t{\n\t\t\tXERO_ITER_EL(element, fog)\n\t\t\t{\n\t\t\t\tint element_name = fog.GetNodeName();\n\t\t\t\tif (element_name == el_fogcolor)\n\t\t\t\t{\n\t\t\t\t\tXMBAttributeList attrs = fog.GetAttributes();\n\t\t\t\t\tm_MapReader.m_LightEnv.m_FogColor = RGBColor(\n\t\t\t\t\t\tattrs.GetNamedItem(at_r).ToFloat(),\n\t\t\t\t\t\tattrs.GetNamedItem(at_g).ToFloat(),\n\t\t\t\t\t\tattrs.GetNamedItem(at_b).ToFloat());\n\t\t\t\t}\n\t\t\t\telse if (element_name == el_fogfactor)\n\t\t\t\t{\n\t\t\t\t\tm_MapReader.m_LightEnv.m_FogFactor = fog.GetText().ToFloat();\n\t\t\t\t}\n\t\t\t\telse if (element_name == el_fogthickness)\n\t\t\t\t{\n\t\t\t\t\tm_MapReader.m_LightEnv.m_FogMax = fog.GetText().ToFloat();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (element_name == el_postproc)\n\t\t{\n\t\t\tXERO_ITER_EL(element, postproc)\n\t\t\t{\n\t\t\t\tint element_name = postproc.GetNodeName();\n\t\t\t\tif (element_name == el_brightness)\n\t\t\t\t{\n\t\t\t\t\tm_MapReader.m_LightEnv.m_Brightness = postproc.GetText().ToFloat();\n\t\t\t\t}\n\t\t\t\telse if (element_name == el_contrast)\n\t\t\t\t{\n\t\t\t\t\tm_MapReader.m_LightEnv.m_Contrast = postproc.GetText().ToFloat();\n\t\t\t\t}\n\t\t\t\telse if (element_name == el_saturation)\n\t\t\t\t{\n\t\t\t\t\tm_MapReader.m_LightEnv.m_Saturation = postproc.GetText().ToFloat();\n\t\t\t\t}\n\t\t\t\telse if (element_name == el_bloom)\n\t\t\t\t{\n\t\t\t\t\tm_MapReader.m_LightEnv.m_Bloom = postproc.GetText().ToFloat();\n\t\t\t\t}\n\t\t\t\telse if (element_name == el_posteffect)\n\t\t\t\t{\n\t\t\t\t\tif (m_MapReader.pPostproc)\n\t\t\t\t\t\tm_MapReader.pPostproc->SetPostEffect(postproc.GetText().FromUTF8());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (element_name == el_water)\n\t\t{\n\t\t\tXERO_ITER_EL(element, waterbody)\n\t\t\t{\n\t\t\t\tENSURE(waterbody.GetNodeName() == el_waterbody);\n\t\t\t\tXERO_ITER_EL(waterbody, waterelement)\n\t\t\t\t{\n\t\t\t\t\tint element_name = waterelement.GetNodeName();\n\t\t\t\t\tif (element_name == el_height)\n\t\t\t\t\t{\n\t\t\t\t\t\tCmpPtr<ICmpWaterManager> cmpWaterManager(*m_MapReader.pSimContext, SYSTEM_ENTITY);\n\t\t\t\t\t\tENSURE(cmpWaterManager);\n\t\t\t\t\t\tcmpWaterManager->SetWaterLevel(entity_pos_t::FromString(waterelement.GetText()));\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// The rest are purely graphical effects, and should be ignored if\n\t\t\t\t\t// graphics are disabled\n\t\t\t\t\tif (!m_MapReader.pWaterMan)\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\tif (element_name == el_type)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (waterelement.GetText() == \"default\")\n\t\t\t\t\t\t\tm_MapReader.pWaterMan->m_WaterType = L\"ocean\";\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tm_MapReader.pWaterMan->m_WaterType =  waterelement.GetText().FromUTF8();\n\t\t\t\t\t}\n\t\t\t\t\telse if (element_name == el_shininess || element_name == el_reflectiontint || element_name == el_reflectiontintstrength)\n\t\t\t\t\t{\n\t\t\t\t\t\t// deprecated.\n\t\t\t\t\t}\n#define READ_COLOR(el, out) \\\n\t\t\t\t\telse if (element_name == el) \\\n\t\t\t\t\t{ \\\n\t\t\t\t\t\tXMBAttributeList attrs = waterelement.GetAttributes(); \\\n\t\t\t\t\t\tout = CColor( \\\n\t\t\t\t\t\t\tattrs.GetNamedItem(at_r).ToFloat(), \\\n\t\t\t\t\t\t\tattrs.GetNamedItem(at_g).ToFloat(), \\\n\t\t\t\t\t\t\tattrs.GetNamedItem(at_b).ToFloat(), \\\n\t\t\t\t\t\t\t1.f); \\\n\t\t\t\t\t}\n\n#define READ_FLOAT(el, out) \\\n\t\t\t\t\telse if (element_name == el) \\\n\t\t\t\t\t{ \\\n\t\t\t\t\t\tout = waterelement.GetText().ToFloat(); \\\n\t\t\t\t\t} \\\n\n\t\t\t\t\tREAD_COLOR(el_color, m_MapReader.pWaterMan->m_WaterColor)\n\t\t\t\t\tREAD_COLOR(el_tint, m_MapReader.pWaterMan->m_WaterTint)\n\t\t\t\t\tREAD_FLOAT(el_waviness, m_MapReader.pWaterMan->m_Waviness)\n\t\t\t\t\tREAD_FLOAT(el_murkiness, m_MapReader.pWaterMan->m_Murkiness)\n\t\t\t\t\tREAD_FLOAT(el_windangle, m_MapReader.pWaterMan->m_WindAngle)\n\n#undef READ_FLOAT\n#undef READ_COLOR\n\n\t\t\t\t\telse\n\t\t\t\t\t\tdebug_warn(L\"Invalid map XML data\");\n\t\t\t\t}\n\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tdebug_warn(L\"Invalid map XML data\");\n\t}\n\n\tm_MapReader.m_LightEnv.CalculateSunDirection();\n}\n\nvoid CXMLReader::ReadCamera(XMBElement parent)\n{\n\t// defaults if we don't find player starting camera\n#define EL(x) int el_##x = xmb_file.GetElementID(#x)\n#define AT(x) int at_##x = xmb_file.GetAttributeID(#x)\n\tEL(declination);\n\tEL(rotation);\n\tEL(position);\n\tAT(angle);\n\tAT(x); AT(y); AT(z);\n#undef AT\n#undef EL\n\n\tfloat declination = DEGTORAD(30.f), rotation = DEGTORAD(-45.f);\n\tCVector3D translation = CVector3D(100, 150, -100);\n\n\tXERO_ITER_EL(parent, element)\n\t{\n\t\tint element_name = element.GetNodeName();\n\n\t\tXMBAttributeList attrs = element.GetAttributes();\n\t\tif (element_name == el_declination)\n\t\t{\n\t\t\tdeclination = attrs.GetNamedItem(at_angle).ToFloat();\n\t\t}\n\t\telse if (element_name == el_rotation)\n\t\t{\n\t\t\trotation = attrs.GetNamedItem(at_angle).ToFloat();\n\t\t}\n\t\telse if (element_name == el_position)\n\t\t{\n\t\t\ttranslation = CVector3D(\n\t\t\t\tattrs.GetNamedItem(at_x).ToFloat(),\n\t\t\t\tattrs.GetNamedItem(at_y).ToFloat(),\n\t\t\t\tattrs.GetNamedItem(at_z).ToFloat());\n\t\t}\n\t\telse\n\t\t\tdebug_warn(L\"Invalid map XML data\");\n\t}\n\n\tif (m_MapReader.pGameView)\n\t{\n\t\tm_MapReader.pGameView->GetCamera()->m_Orientation.SetXRotation(declination);\n\t\tm_MapReader.pGameView->GetCamera()->m_Orientation.RotateY(rotation);\n\t\tm_MapReader.pGameView->GetCamera()->m_Orientation.Translate(translation);\n\t\tm_MapReader.pGameView->GetCamera()->UpdateFrustum();\n\t}\n}\n\nvoid CXMLReader::ReadPaths(XMBElement parent)\n{\n\t#define EL(x) int el_##x = xmb_file.GetElementID(#x)\n\t#define AT(x) int at_##x = xmb_file.GetAttributeID(#x)\n\n\tEL(path);\n\tEL(rotation);\n\tEL(node);\n\tEL(position);\n\tEL(target);\n\tAT(name);\n\tAT(timescale);\n\tAT(orientation);\n\tAT(mode);\n\tAT(style);\n\tAT(x);\n\tAT(y);\n\tAT(z);\n\tAT(deltatime);\n\n#undef EL\n#undef AT\n\n\tXERO_ITER_EL(parent, element)\n\t{\n\t\tint elementName = element.GetNodeName();\n\n\t\tif (elementName == el_path)\n\t\t{\n\t\t\tCCinemaData pathData;\n\t\t\tXMBAttributeList attrs = element.GetAttributes();\n\t\t\tCStrW pathName(attrs.GetNamedItem(at_name).FromUTF8());\n\t\t\tpathData.m_Timescale = fixed::FromString(attrs.GetNamedItem(at_timescale));\n\t\t\tTNSpline pathSpline, targetSpline;\n\t\t\tfixed lastTargetTime = fixed::Zero();\n\n\t\t\tpathData.m_Name = pathName;\n\t\t\tpathData.m_Orientation = attrs.GetNamedItem(at_orientation).FromUTF8();\n\t\t\tpathData.m_Mode = attrs.GetNamedItem(at_mode).FromUTF8();\n\t\t\tpathData.m_Style = attrs.GetNamedItem(at_style).FromUTF8();\n\n\t\t\tXERO_ITER_EL(element, pathChild)\n\t\t\t{\n\t\t\t\telementName = pathChild.GetNodeName();\n\t\t\t\tattrs = pathChild.GetAttributes();\n\n\t\t\t\t// Load node data used for spline\n\t\t\t\tif (elementName == el_node)\n\t\t\t\t{\n\t\t\t\t\tbool positionDeclared = false;\n\t\t\t\t\tSplineData data;\n\t\t\t\t\tdata.Distance = fixed::FromString(attrs.GetNamedItem(at_deltatime));\n\t\t\t\t\tlastTargetTime += fixed::FromString(attrs.GetNamedItem(at_deltatime));\n\t\t\t\t\tXERO_ITER_EL(pathChild, nodeChild)\n\t\t\t\t\t{\n\t\t\t\t\t\telementName = nodeChild.GetNodeName();\n\t\t\t\t\t\tattrs = nodeChild.GetAttributes();\n\n\t\t\t\t\t\tif (elementName == el_position)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tdata.Position.X = fixed::FromString(attrs.GetNamedItem(at_x));\n\t\t\t\t\t\t\tdata.Position.Y = fixed::FromString(attrs.GetNamedItem(at_y));\n\t\t\t\t\t\t\tdata.Position.Z = fixed::FromString(attrs.GetNamedItem(at_z));\n\t\t\t\t\t\t\tpositionDeclared = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (elementName == el_rotation)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tdata.Rotation.X = fixed::FromString(attrs.GetNamedItem(at_x));\n\t\t\t\t\t\t\tdata.Rotation.Y = fixed::FromString(attrs.GetNamedItem(at_y));\n\t\t\t\t\t\t\tdata.Rotation.Z = fixed::FromString(attrs.GetNamedItem(at_z));\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (elementName == el_target)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tCFixedVector3D targetPosition;\n\t\t\t\t\t\t\ttargetPosition.X = fixed::FromString(attrs.GetNamedItem(at_x));\n\t\t\t\t\t\t\ttargetPosition.Y = fixed::FromString(attrs.GetNamedItem(at_y));\n\t\t\t\t\t\t\ttargetPosition.Z = fixed::FromString(attrs.GetNamedItem(at_z));\n\n\t\t\t\t\t\t\ttargetSpline.AddNode(targetPosition, CFixedVector3D(), lastTargetTime);\n\t\t\t\t\t\t\tlastTargetTime = fixed::Zero();\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tLOGWARNING(\"Invalid cinematic element for node child\");\n\t\t\t\t\t}\n\n\t\t\t\t\t// Skip the node if no position\n\t\t\t\t\tif (positionDeclared)\n\t\t\t\t\t\tpathSpline.AddNode(data.Position, data.Rotation, data.Distance);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tLOGWARNING(\"Invalid cinematic element for path child\");\n\t\t\t}\n\n\t\t\t// Construct cinema path with data gathered\n\t\t\tCCinemaPath path(pathData, pathSpline, targetSpline);\n\t\t\tif (path.Empty())\n\t\t\t{\n\t\t\t\tLOGWARNING(\"Path with name '%s' is empty\", pathName.ToUTF8());\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (!m_MapReader.pCinema->HasPath(pathName))\n\t\t\t\tm_MapReader.pCinema->AddPath(pathName, path);\n\t\t\telse\n\t\t\t\tLOGWARNING(\"Path with name '%s' already exists\", pathName.ToUTF8());\n\t\t}\n\t\telse\n\t\t\tLOGWARNING(\"Invalid path child with name '%s'\", element.GetText());\n\t}\n}\n\nvoid CXMLReader::ReadTriggers(XMBElement UNUSED(parent))\n{\n}\n\nint CXMLReader::ReadEntities(XMBElement parent, double end_time)\n{\n\tXMBElementList entities = parent.GetChildNodes();\n\n\tENSURE(m_MapReader.pSimulation2);\n\tCSimulation2& sim = *m_MapReader.pSimulation2;\n\tCmpPtr<ICmpPlayerManager> cmpPlayerManager(sim, SYSTEM_ENTITY);\n\n\twhile (entity_idx < entities.size())\n\t{\n\t\t// all new state at this scope and below doesn't need to be\n\t\t// wrapped, since we only yield after a complete iteration.\n\n\t\tXMBElement entity = entities[entity_idx++];\n\t\tENSURE(entity.GetNodeName() == el_entity);\n\n\t\tXMBAttributeList attrs = entity.GetAttributes();\n\t\tCStr uid = attrs.GetNamedItem(at_uid);\n\t\tENSURE(!uid.empty());\n\t\tint EntityUid = uid.ToInt();\n\n\t\tCStrW TemplateName;\n\t\tint PlayerID = 0;\n\t\tCFixedVector3D Position;\n\t\tCFixedVector3D Orientation;\n\t\tlong Seed = -1;\n\n\t\t// Obstruction control groups.\n\t\tentity_id_t ControlGroup = INVALID_ENTITY;\n\t\tentity_id_t ControlGroup2 = INVALID_ENTITY;\n\n\t\tXERO_ITER_EL(entity, setting)\n\t\t{\n\t\t\tint element_name = setting.GetNodeName();\n\n\t\t\t// <template>\n\t\t\tif (element_name == el_template)\n\t\t\t{\n\t\t\t\tTemplateName = setting.GetText().FromUTF8();\n\t\t\t}\n\t\t\t// <player>\n\t\t\telse if (element_name == el_player)\n\t\t\t{\n\t\t\t\tPlayerID = setting.GetText().ToInt();\n\t\t\t}\n\t\t\t// <position>\n\t\t\telse if (element_name == el_position)\n\t\t\t{\n\t\t\t\tXMBAttributeList attrs = setting.GetAttributes();\n\t\t\t\tPosition = CFixedVector3D(\n\t\t\t\t\tfixed::FromString(attrs.GetNamedItem(at_x)),\n\t\t\t\t\tfixed::FromString(attrs.GetNamedItem(at_y)),\n\t\t\t\t\tfixed::FromString(attrs.GetNamedItem(at_z)));\n\t\t\t}\n\t\t\t// <orientation>\n\t\t\telse if (element_name == el_orientation)\n\t\t\t{\n\t\t\t\tXMBAttributeList attrs = setting.GetAttributes();\n\t\t\t\tOrientation = CFixedVector3D(\n\t\t\t\t\tfixed::FromString(attrs.GetNamedItem(at_x)),\n\t\t\t\t\tfixed::FromString(attrs.GetNamedItem(at_y)),\n\t\t\t\t\tfixed::FromString(attrs.GetNamedItem(at_z)));\n\t\t\t\t// TODO: what happens if some attributes are missing?\n\t\t\t}\n\t\t\t// <obstruction>\n\t\t\telse if (element_name == el_obstruction)\n\t\t\t{\n\t\t\t\tXMBAttributeList attrs = setting.GetAttributes();\n\t\t\t\tControlGroup = attrs.GetNamedItem(at_group).ToInt();\n\t\t\t\tControlGroup2 = attrs.GetNamedItem(at_group2).ToInt();\n\t\t\t}\n\t\t\t// <actor>\n\t\t\telse if (element_name == el_actor)\n\t\t\t{\n\t\t\t\tXMBAttributeList attrs = setting.GetAttributes();\n\t\t\t\tCStr seedStr = attrs.GetNamedItem(at_seed);\n\t\t\t\tif (!seedStr.empty())\n\t\t\t\t{\n\t\t\t\t\tSeed = seedStr.ToLong();\n\t\t\t\t\tENSURE(Seed >= 0);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t\tdebug_warn(L\"Invalid map XML data\");\n\t\t}\n\n\t\tentity_id_t ent = sim.AddEntity(TemplateName, EntityUid);\n\t\tentity_id_t player = cmpPlayerManager->GetPlayerByID(PlayerID);\n\t\tif (ent == INVALID_ENTITY || player == INVALID_ENTITY)\n\t\t{\t// Don't add entities with invalid player IDs\n\t\t\tLOGERROR(\"Failed to load entity template '%s'\", utf8_from_wstring(TemplateName));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tCmpPtr<ICmpPosition> cmpPosition(sim, ent);\n\t\t\tif (cmpPosition)\n\t\t\t{\n\t\t\t\tcmpPosition->JumpTo(Position.X, Position.Z);\n\t\t\t\tcmpPosition->SetYRotation(Orientation.Y);\n\t\t\t\t// TODO: other parts of the position\n\t\t\t}\n\n\t\t\tCmpPtr<ICmpOwnership> cmpOwnership(sim, ent);\n\t\t\tif (cmpOwnership)\n\t\t\t\tcmpOwnership->SetOwner(PlayerID);\n\n\t\t\tCmpPtr<ICmpObstruction> cmpObstruction(sim, ent);\n\t\t\tif (cmpObstruction)\n\t\t\t{\n\t\t\t\tif (ControlGroup != INVALID_ENTITY)\n\t\t\t\t\tcmpObstruction->SetControlGroup(ControlGroup);\n\t\t\t\tif (ControlGroup2 != INVALID_ENTITY)\n\t\t\t\t\tcmpObstruction->SetControlGroup2(ControlGroup2);\n\n\t\t\t\tcmpObstruction->ResolveFoundationCollisions();\n\t\t\t}\n\n\t\t\tCmpPtr<ICmpVisual> cmpVisual(sim, ent);\n\t\t\tif (cmpVisual)\n\t\t\t{\n\t\t\t\tif (Seed != -1)\n\t\t\t\t\tcmpVisual->SetActorSeed((u32)Seed);\n\t\t\t\t// TODO: variation/selection strings\n\t\t\t}\n\n\t\t\tif (PlayerID == m_MapReader.m_PlayerID && (boost::algorithm::ends_with(TemplateName, L\"civil_centre\") || m_MapReader.m_StartingCameraTarget == INVALID_ENTITY))\n\t\t\t{\n\t\t\t\t// Focus on civil centre or first entity owned by player\n\t\t\t\tm_MapReader.m_StartingCameraTarget = ent;\n\t\t\t}\n\t\t}\n\n\t\tcompleted_jobs++;\n\t\tLDR_CHECK_TIMEOUT(completed_jobs, total_jobs);\n\t}\n\n\treturn 0;\n}\n\nvoid CXMLReader::ReadXML()\n{\n\tfor (XMBElement node : nodes)\n\t{\n\t\tCStr name = xmb_file.GetElementString(node.GetNodeName());\n\t\tif (name == \"Terrain\")\n\t\t{\n\t\t\tReadTerrain(node);\n\t\t}\n\t\telse if (name == \"Environment\")\n\t\t{\n\t\t\tReadEnvironment(node);\n\t\t}\n\t\telse if (name == \"Camera\")\n\t\t{\n\t\t\tReadCamera(node);\n\t\t}\n\t\telse if (name == \"ScriptSettings\")\n\t\t{\n\t\t\t// Already loaded - this is to prevent an assertion\n\t\t}\n\t\telse if (name == \"Entities\")\n\t\t{\n\t\t\t// Handled by ProgressiveReadEntities instead\n\t\t}\n\t\telse if (name == \"Paths\")\n\t\t{\n\t\t\tReadPaths(node);\n\t\t}\n\t\telse if (name == \"Triggers\")\n\t\t{\n\t\t\tReadTriggers(node);\n\t\t}\n\t\telse if (name == \"Script\")\n\t\t{\n\t\t\tif (m_MapReader.pSimulation2)\n\t\t\t\tm_MapReader.pSimulation2->SetStartupScript(node.GetText());\n\t\t}\n\t\telse\n\t\t{\n\t\t\tdebug_printf(\"Invalid XML element in map file: %s\\n\", name.c_str());\n\t\t\tdebug_warn(L\"Invalid map XML data\");\n\t\t}\n\t}\n}\n\nint CXMLReader::ProgressiveReadEntities()\n{\n\t// yield after this time is reached. balances increased progress bar\n\t// smoothness vs. slowing down loading.\n\tconst double end_time = timer_Time() + 200e-3;\n\n\tint ret;\n\n\twhile (node_idx < nodes.size())\n\t{\n\t\tXMBElement node = nodes[node_idx];\n\t\tCStr name = xmb_file.GetElementString(node.GetNodeName());\n\t\tif (name == \"Entities\")\n\t\t{\n\t\t\tif (!m_MapReader.m_SkipEntities)\n\t\t\t{\n\t\t\t\tret = ReadEntities(node, end_time);\n\t\t\t\tif (ret != 0)\t// error or timed out\n\t\t\t\t\treturn ret;\n\t\t\t}\n\t\t}\n\n\t\tnode_idx++;\n\t}\n\n\treturn 0;\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n\n// load script settings from map\nint CMapReader::LoadScriptSettings()\n{\n\tif (!xml_reader)\n\t\txml_reader = new CXMLReader(filename_xml, *this);\n\n\t// parse the script settings\n\tif (pSimulation2)\n\t\tpSimulation2->SetMapSettings(xml_reader->ReadScriptSettings());\n\n\treturn 0;\n}\n\n// load player settings script\nint CMapReader::LoadPlayerSettings()\n{\n\tif (pSimulation2)\n\t\tpSimulation2->LoadPlayerSettings(true);\n\treturn 0;\n}\n\n// load map settings script\nint CMapReader::LoadMapSettings()\n{\n\tif (pSimulation2)\n\t\tpSimulation2->LoadMapSettings();\n\treturn 0;\n}\n\nint CMapReader::ReadXML()\n{\n\tif (!xml_reader)\n\t\txml_reader = new CXMLReader(filename_xml, *this);\n\n\txml_reader->ReadXML();\n\n\treturn 0;\n}\n\n// progressive\nint CMapReader::ReadXMLEntities()\n{\n\tif (!xml_reader)\n\t\txml_reader = new CXMLReader(filename_xml, *this);\n\n\tint ret = xml_reader->ProgressiveReadEntities();\n\t// finished or failed\n\tif (ret <= 0)\n\t{\n\t\tSAFE_DELETE(xml_reader);\n\t}\n\n\treturn ret;\n}\n\nint CMapReader::DelayLoadFinished()\n{\n\t// we were dynamically allocated by CWorld::Initialize\n\tdelete this;\n\n\treturn 0;\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n\nint CMapReader::LoadRMSettings()\n{\n\t// copy random map settings over to sim\n\tENSURE(pSimulation2);\n\tpSimulation2->SetMapSettings(m_ScriptSettings);\n\n\treturn 0;\n}\n\nint CMapReader::GenerateMap()\n{\n\tJSContext* cx = pSimulation2->GetScriptInterface().GetContext();\n\tJSAutoRequest rq(cx);\n\n\tif (!m_MapGen)\n\t{\n\t\t// Initialize map generator\n\t\tm_MapGen = new CMapGenerator();\n\n\t\tVfsPath scriptPath;\n\n\t\tif (m_ScriptFile.length())\n\t\t\tscriptPath = L\"maps/random/\"+m_ScriptFile;\n\n\t\t// Stringify settings to pass across threads\n\t\tstd::string scriptSettings = pSimulation2->GetScriptInterface().StringifyJSON(&m_ScriptSettings);\n\n\t\t// Try to generate map\n\t\tm_MapGen->GenerateMap(scriptPath, scriptSettings);\n\t}\n\n\t// Check status\n\tint progress = m_MapGen->GetProgress();\n\tif (progress < 0)\n\t{\n\t\t// RMS failed - return to main menu\n\t\tthrow PSERROR_Game_World_MapLoadFailed(\"Error generating random map.\\nCheck application log for details.\");\n\t}\n\telse if (progress == 0)\n\t{\n\t\t// Finished, get results as StructuredClone object, which must be read to obtain the JS val\n\t\tshared_ptr<ScriptInterface::StructuredClone> results = m_MapGen->GetResults();\n\n\t\t// Parse data into simulation context\n\t\tJS::RootedValue data(cx);\n\t\tpSimulation2->GetScriptInterface().ReadStructuredClone(results, &data);\n\n\t\tif (data.isUndefined())\n\t\t{\n\t\t\t// RMS failed - return to main menu\n\t\t\tthrow PSERROR_Game_World_MapLoadFailed(\"Error generating random map.\\nCheck application log for details.\");\n\t\t}\n\t\telse\n\t\t{\n\t\t\tm_MapData.init(cx, data);\n\t\t}\n\t}\n\telse\n\t{\n\t\t// Still working\n\n\t\t// Sleep for a while, slowing down the rendering thread\n\t\t// to allow more CPU for the map generator thread\n\t\tSDL_Delay(100);\n\t}\n\n\t// return progress\n\treturn progress;\n};\n\n\nint CMapReader::ParseTerrain()\n{\n\tTIMER(L\"ParseTerrain\");\n\tJSContext* cx = pSimulation2->GetScriptInterface().GetContext();\n\tJSAutoRequest rq(cx);\n\n\t// parse terrain from map data\n\t//\tan error here should stop the loading process\n#define GET_TERRAIN_PROPERTY(val, prop, out)\\\n\tif (!pSimulation2->GetScriptInterface().GetProperty(val, #prop, out))\\\n\t\t{\tLOGERROR(\"CMapReader::ParseTerrain() failed to get '%s' property\", #prop);\\\n\t\t\tthrow PSERROR_Game_World_MapLoadFailed(\"Error parsing terrain data.\\nCheck application log for details\"); }\n\n\tu32 size;\n\tGET_TERRAIN_PROPERTY(m_MapData, size, size)\n\n\tm_PatchesPerSide = size / PATCH_SIZE;\n\n\t// flat heightmap of u16 data\n\tGET_TERRAIN_PROPERTY(m_MapData, height, m_Heightmap)\n\n\t// load textures\n\tstd::vector<std::string> textureNames;\n\tGET_TERRAIN_PROPERTY(m_MapData, textureNames, textureNames)\n\tnum_terrain_tex = textureNames.size();\n\n\twhile (cur_terrain_tex < num_terrain_tex)\n\t{\n\t\tENSURE(CTerrainTextureManager::IsInitialised()); // we need this for the terrain properties (even when graphics are disabled)\n\t\tCTerrainTextureEntry* texentry = g_TexMan.FindTexture(textureNames[cur_terrain_tex]);\n\t\tm_TerrainTextures.push_back(texentry);\n\n\t\tcur_terrain_tex++;\n\t}\n\n\t// build tile data\n\tm_Tiles.resize(SQR(size));\n\n\tJS::RootedValue tileData(cx);\n\tGET_TERRAIN_PROPERTY(m_MapData, tileData, &tileData)\n\n\t// parse tile data object into flat arrays\n\tstd::vector<u16> tileIndex;\n\tstd::vector<u16> tilePriority;\n\tGET_TERRAIN_PROPERTY(tileData, index, tileIndex);\n\tGET_TERRAIN_PROPERTY(tileData, priority, tilePriority);\n\n\tENSURE(SQR(size) == tileIndex.size() && SQR(size) == tilePriority.size());\n\n\t// reorder by patches and store\n\tfor (size_t x = 0; x < size; ++x)\n\t{\n\t\tsize_t patchX = x / PATCH_SIZE;\n\t\tsize_t offX = x % PATCH_SIZE;\n\t\tfor (size_t y = 0; y < size; ++y)\n\t\t{\n\t\t\tsize_t patchY = y / PATCH_SIZE;\n\t\t\tsize_t offY = y % PATCH_SIZE;\n\n\t\t\tSTileDesc tile;\n\t\t\ttile.m_Tex1Index = tileIndex[y*size + x];\n\t\t\ttile.m_Tex2Index = 0xFFFF;\n\t\t\ttile.m_Priority = tilePriority[y*size + x];\n\n\t\t\tm_Tiles[(patchY * m_PatchesPerSide + patchX) * SQR(PATCH_SIZE) + (offY * PATCH_SIZE + offX)] = tile;\n\t\t}\n\t}\n\n\t// reset generator state\n\tcur_terrain_tex = 0;\n\n#undef GET_TERRAIN_PROPERTY\n\n\treturn 0;\n}\n\nint CMapReader::ParseEntities()\n{\n\tTIMER(L\"ParseEntities\");\n\tJSContext* cx = pSimulation2->GetScriptInterface().GetContext();\n\tJSAutoRequest rq(cx);\n\n\t// parse entities from map data\n\tstd::vector<Entity> entities;\n\n\tif (!pSimulation2->GetScriptInterface().GetProperty(m_MapData, \"entities\", entities))\n\t\tLOGWARNING(\"CMapReader::ParseEntities() failed to get 'entities' property\");\n\n\tCSimulation2& sim = *pSimulation2;\n\tCmpPtr<ICmpPlayerManager> cmpPlayerManager(sim, SYSTEM_ENTITY);\n\n\tsize_t entity_idx = 0;\n\tsize_t num_entities = entities.size();\n\n\tEntity currEnt;\n\n\twhile (entity_idx < num_entities)\n\t{\n\t\t// Get current entity struct\n\t\tcurrEnt = entities[entity_idx];\n\n\t\tentity_id_t ent = pSimulation2->AddEntity(currEnt.templateName, currEnt.entityID);\n\t\tentity_id_t player = cmpPlayerManager->GetPlayerByID(currEnt.playerID);\n\t\tif (ent == INVALID_ENTITY || player == INVALID_ENTITY)\n\t\t{\t// Don't add entities with invalid player IDs\n\t\t\tLOGERROR(\"Failed to load entity template '%s'\", utf8_from_wstring(currEnt.templateName));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tCmpPtr<ICmpPosition> cmpPosition(sim, ent);\n\t\t\tif (cmpPosition)\n\t\t\t{\n\t\t\t\tcmpPosition->JumpTo(currEnt.position.X, currEnt.position.Z);\n\t\t\t\tcmpPosition->SetYRotation(currEnt.rotation.Y);\n\t\t\t\t// TODO: other parts of the position\n\t\t\t}\n\n\t\t\tCmpPtr<ICmpOwnership> cmpOwnership(sim, ent);\n\t\t\tif (cmpOwnership)\n\t\t\t\tcmpOwnership->SetOwner(currEnt.playerID);\n\n\t\t\t// Detect and fix collisions between foundation-blocking entities.\n\t\t\t// This presently serves to copy wall tower control groups to wall\n\t\t\t// segments, allowing players to expand RMS-generated walls.\n\t\t\tCmpPtr<ICmpObstruction> cmpObstruction(sim, ent);\n\t\t\tif (cmpObstruction)\n\t\t\t\tcmpObstruction->ResolveFoundationCollisions();\n\n\t\t\tif (currEnt.playerID == m_PlayerID && (boost::algorithm::ends_with(currEnt.templateName, L\"civil_centre\") || m_StartingCameraTarget == INVALID_ENTITY))\n\t\t\t{\n\t\t\t\t// Focus on civil centre or first entity owned by player\n\t\t\t\tm_StartingCameraTarget = currEnt.entityID;\n\t\t\t}\n\t\t}\n\n\t\tentity_idx++;\n\t}\n\n\treturn 0;\n}\n\nint CMapReader::ParseEnvironment()\n{\n\t// parse environment settings from map data\n\tJSContext* cx = pSimulation2->GetScriptInterface().GetContext();\n\tJSAutoRequest rq(cx);\n\n#define GET_ENVIRONMENT_PROPERTY(val, prop, out)\\\n\tif (!pSimulation2->GetScriptInterface().GetProperty(val, #prop, out))\\\n\t\tLOGWARNING(\"CMapReader::ParseEnvironment() failed to get '%s' property\", #prop);\n\n\tJS::RootedValue envObj(cx);\n\tGET_ENVIRONMENT_PROPERTY(m_MapData, Environment, &envObj)\n\n\tif (envObj.isUndefined())\n\t{\n\t\tLOGWARNING(\"CMapReader::ParseEnvironment(): Environment settings not found\");\n\t\treturn 0;\n\t}\n\n\t//m_LightEnv.SetLightingModel(\"standard\");\n\tif (pPostproc)\n\t\tpPostproc->SetPostEffect(L\"default\");\n\n\tstd::wstring skySet;\n\tGET_ENVIRONMENT_PROPERTY(envObj, SkySet, skySet)\n\tif (pSkyMan)\n\t\tpSkyMan->SetSkySet(skySet);\n\n\tCColor sunColor;\n\tGET_ENVIRONMENT_PROPERTY(envObj, SunColor, sunColor)\n\tm_LightEnv.m_SunColor = RGBColor(sunColor.r, sunColor.g, sunColor.b);\n\n\tGET_ENVIRONMENT_PROPERTY(envObj, SunElevation, m_LightEnv.m_Elevation)\n\tGET_ENVIRONMENT_PROPERTY(envObj, SunRotation, m_LightEnv.m_Rotation)\n\n\tCColor terrainAmbientColor;\n\tGET_ENVIRONMENT_PROPERTY(envObj, TerrainAmbientColor, terrainAmbientColor)\n\tm_LightEnv.m_TerrainAmbientColor = RGBColor(terrainAmbientColor.r, terrainAmbientColor.g, terrainAmbientColor.b);\n\n\tCColor unitsAmbientColor;\n\tGET_ENVIRONMENT_PROPERTY(envObj, UnitsAmbientColor, unitsAmbientColor)\n\tm_LightEnv.m_UnitsAmbientColor = RGBColor(unitsAmbientColor.r, unitsAmbientColor.g, unitsAmbientColor.b);\n\n\t// Water properties\n\tJS::RootedValue waterObj(cx);\n\tGET_ENVIRONMENT_PROPERTY(envObj, Water, &waterObj)\n\n\tJS::RootedValue waterBodyObj(cx);\n\tGET_ENVIRONMENT_PROPERTY(waterObj, WaterBody, &waterBodyObj)\n\n\t// Water level - necessary\n\tfloat waterHeight;\n\tGET_ENVIRONMENT_PROPERTY(waterBodyObj, Height, waterHeight)\n\n\tCmpPtr<ICmpWaterManager> cmpWaterManager(*pSimulation2, SYSTEM_ENTITY);\n\tENSURE(cmpWaterManager);\n\tcmpWaterManager->SetWaterLevel(entity_pos_t::FromFloat(waterHeight));\n\n\t// If we have graphics, get rest of settings\n\tif (pWaterMan)\n\t{\n\t\tGET_ENVIRONMENT_PROPERTY(waterBodyObj, Type, pWaterMan->m_WaterType)\n\t\tif (pWaterMan->m_WaterType == L\"default\")\n\t\t\tpWaterMan->m_WaterType = L\"ocean\";\n\t\tGET_ENVIRONMENT_PROPERTY(waterBodyObj, Color, pWaterMan->m_WaterColor)\n\t\tGET_ENVIRONMENT_PROPERTY(waterBodyObj, Tint, pWaterMan->m_WaterTint)\n\t\tGET_ENVIRONMENT_PROPERTY(waterBodyObj, Waviness, pWaterMan->m_Waviness)\n\t\tGET_ENVIRONMENT_PROPERTY(waterBodyObj, Murkiness, pWaterMan->m_Murkiness)\n\t\tGET_ENVIRONMENT_PROPERTY(waterBodyObj, WindAngle, pWaterMan->m_WindAngle)\n\t}\n\n\tJS::RootedValue fogObject(cx);\n\tGET_ENVIRONMENT_PROPERTY(envObj, Fog, &fogObject);\n\n\tGET_ENVIRONMENT_PROPERTY(fogObject, FogFactor, m_LightEnv.m_FogFactor);\n\tGET_ENVIRONMENT_PROPERTY(fogObject, FogThickness, m_LightEnv.m_FogMax);\n\n\tCColor fogColor;\n\tGET_ENVIRONMENT_PROPERTY(fogObject, FogColor, fogColor);\n\tm_LightEnv.m_FogColor = RGBColor(fogColor.r, fogColor.g, fogColor.b);\n\n\tJS::RootedValue postprocObject(cx);\n\tGET_ENVIRONMENT_PROPERTY(envObj, Postproc, &postprocObject);\n\n\tstd::wstring postProcEffect;\n\tGET_ENVIRONMENT_PROPERTY(postprocObject, PostprocEffect, postProcEffect);\n\n\tif (pPostproc)\n\t\tpPostproc->SetPostEffect(postProcEffect);\n\n\tGET_ENVIRONMENT_PROPERTY(postprocObject, Brightness, m_LightEnv.m_Brightness);\n\tGET_ENVIRONMENT_PROPERTY(postprocObject, Contrast, m_LightEnv.m_Contrast);\n\tGET_ENVIRONMENT_PROPERTY(postprocObject, Saturation, m_LightEnv.m_Saturation);\n\tGET_ENVIRONMENT_PROPERTY(postprocObject, Bloom, m_LightEnv.m_Bloom);\n\n\tm_LightEnv.CalculateSunDirection();\n\n#undef GET_ENVIRONMENT_PROPERTY\n\n\treturn 0;\n}\n\nint CMapReader::ParseCamera()\n{\n\tJSContext* cx = pSimulation2->GetScriptInterface().GetContext();\n\tJSAutoRequest rq(cx);\n\t// parse camera settings from map data\n\t// defaults if we don't find player starting camera\n\tfloat declination = DEGTORAD(30.f), rotation = DEGTORAD(-45.f);\n\tCVector3D translation = CVector3D(100, 150, -100);\n\n#define GET_CAMERA_PROPERTY(val, prop, out)\\\n\tif (!pSimulation2->GetScriptInterface().GetProperty(val, #prop, out))\\\n\t\tLOGWARNING(\"CMapReader::ParseCamera() failed to get '%s' property\", #prop);\n\n\tJS::RootedValue cameraObj(cx);\n\tGET_CAMERA_PROPERTY(m_MapData, Camera, &cameraObj)\n\n\tif (!cameraObj.isUndefined())\n\t{\t// If camera property exists, read values\n\t\tCFixedVector3D pos;\n\t\tGET_CAMERA_PROPERTY(cameraObj, Position, pos)\n\t\ttranslation = pos;\n\n\t\tGET_CAMERA_PROPERTY(cameraObj, Rotation, rotation)\n\t\tGET_CAMERA_PROPERTY(cameraObj, Declination, declination)\n\t}\n#undef GET_CAMERA_PROPERTY\n\n\tif (pGameView)\n\t{\n\t\tpGameView->GetCamera()->m_Orientation.SetXRotation(declination);\n\t\tpGameView->GetCamera()->m_Orientation.RotateY(rotation);\n\t\tpGameView->GetCamera()->m_Orientation.Translate(translation);\n\t\tpGameView->GetCamera()->UpdateFrustum();\n\t}\n\n\treturn 0;\n}\n\nCMapReader::~CMapReader()\n{\n\t// Cleaup objects\n\tdelete xml_reader;\n\tdelete m_MapGen;\n}\n"
  },
  {
    "path": "fpsgame/graphics/MapReader.h",
    "content": "/* Copyright (C) 2016 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef INCLUDED_MAPREADER\n#define INCLUDED_MAPREADER\n\n#include \"MapIO.h\"\n#include \"lib/res/handle.h\"\n#include \"ps/CStr.h\"\n#include \"LightEnv.h\"\n#include \"ps/FileIo.h\"\n#include \"scriptinterface/ScriptInterface.h\"\n#include \"simulation2/system/Entity.h\"\n\nclass CObjectEntry;\nclass CTerrain;\nclass WaterManager;\nclass SkyManager;\nclass CLightEnv;\nclass CCinemaManager;\nclass CPostprocManager;\nclass CTriggerManager;\nclass CSimulation2;\nclass CSimContext;\nclass CTerrainTextureEntry;\nclass ScriptInterface;\nclass CGameView;\nclass CXMLReader;\nclass CMapGenerator;\n\nclass CMapReader : public CMapIO\n{\n\tfriend class CXMLReader;\n\npublic:\n\t// constructor\n\tCMapReader();\n\t~CMapReader();\n\n\t// LoadMap: try to load the map from given file; reinitialise the scene to new data if successful\n\tvoid LoadMap(const VfsPath& pathname, JSRuntime* rt, JS::HandleValue settings, CTerrain*, WaterManager*, SkyManager*, CLightEnv*, CGameView*,\n\t\tCCinemaManager*, CTriggerManager*, CPostprocManager* pPostproc, CSimulation2*, const CSimContext*, \n\t        int playerID, bool skipEntities);\n\n\tvoid LoadRandomMap(const CStrW& scriptFile, JSRuntime* rt, JS::HandleValue settings, CTerrain*, WaterManager*, SkyManager*, CLightEnv*, CGameView*, CCinemaManager*, CTriggerManager*, CPostprocManager* pPostproc_, CSimulation2*, int playerID);\n\nprivate:\n\t// Load script settings for use by scripts\n\tint LoadScriptSettings();\n\n\t// load player settings only\n\tint LoadPlayerSettings();\n\n\t// load map settings only\n\tint LoadMapSettings();\n\n\t// UnpackTerrain: unpack the terrain from the input stream\n\tint UnpackTerrain();\n\t// UnpackCinema: unpack the cinematic tracks from the input stream\n\tint UnpackCinema();\n\n\t// UnpackMap: unpack the given data from the raw data stream into local variables\n\tint UnpackMap();\n\n\t// ApplyData: take all the input data, and rebuild the scene from it\n\tint ApplyData();\n\tint ApplyTerrainData();\n\n\t// read some misc data from the XML file\n\tint ReadXML();\n\n\t// read entity data from the XML file\n\tint ReadXMLEntities();\n\n\t// clean up everything used during delayed load\n\tint DelayLoadFinished();\n\t\n\t// Copy random map settings over to sim\n\tint LoadRMSettings();\n\n\t// Generate random map\n\tint GenerateMap();\n\n\t// Parse script data into terrain\n\tint ParseTerrain();\n\n\t// Parse script data into entities\n\tint ParseEntities();\n\n\t// Parse script data into environment\n\tint ParseEnvironment();\n\n\t// Parse script data into camera\n\tint ParseCamera();\n\n\n\t// size of map \n\tssize_t m_PatchesPerSide;\n\t// heightmap for map\n\tstd::vector<u16> m_Heightmap;\n\t// list of terrain textures used by map\n\tstd::vector<CTerrainTextureEntry*> m_TerrainTextures;\n\t// tile descriptions for each tile\n\tstd::vector<STileDesc> m_Tiles;\n\t// lightenv stored in file\n\tCLightEnv m_LightEnv;\n\t// startup script\n\tCStrW m_Script;\n\n\t// random map data\n\tCStrW m_ScriptFile;\n\tJS::PersistentRootedValue m_ScriptSettings;\n\tJS::PersistentRootedValue m_MapData;\n\n\tCMapGenerator* m_MapGen;\n\n\t// state latched by LoadMap and held until DelayedLoadFinished\n\tCFileUnpacker unpacker;\n\tCTerrain* pTerrain;\n\tWaterManager* pWaterMan;\n\tSkyManager* pSkyMan;\n\tCPostprocManager* pPostproc;\n\tCLightEnv* pLightEnv;\n\tCGameView* pGameView;\n\tCCinemaManager* pCinema;\n\tCTriggerManager* pTrigMan;\n\tCSimulation2* pSimulation2;\n\tconst CSimContext* pSimContext;\n\tint m_PlayerID;\n\tbool m_SkipEntities;\n\tVfsPath filename_xml;\n\tbool only_xml;\n\tu32 file_format_version;\n\tentity_id_t m_StartingCameraTarget;\n\tCVector3D m_StartingCamera;\n\n\t// UnpackTerrain generator state\n\tsize_t cur_terrain_tex;\n\tsize_t num_terrain_tex;\n\n\tCXMLReader* xml_reader;\n};\n\n/**\n * A restricted map reader that returns various summary information\n * for use by scripts (particularly the GUI).\n */\nclass CMapSummaryReader\n{\npublic:\n\t/**\n\t * Try to load a map file.\n\t * @param pathname Path to .pmp or .xml file\n\t */\n\tPSRETURN LoadMap(const VfsPath& pathname);\n\n\t/**\n\t * Returns a value of the form:\n\t * @code\n\t * {\n\t *   \"settings\": { ... contents of the map's <ScriptSettings> ... }\n\t * }\n\t * @endcode\n\t */\n\tvoid GetMapSettings(ScriptInterface& scriptInterface, JS::MutableHandleValue);\n\nprivate:\n\tCStr m_ScriptSettings;\n};\n\n#endif\n"
  },
  {
    "path": "fpsgame/graphics/MapWriter.cpp",
    "content": "/* Copyright (C) 2016 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"precompiled.h\"\n\n#include \"Camera.h\"\n#include \"CinemaManager.h\"\n#include \"GameView.h\"\n#include \"LightEnv.h\"\n#include \"MapReader.h\"\n#include \"MapWriter.h\"\n#include \"Patch.h\"\n#include \"Terrain.h\"\n#include \"TerrainTextureEntry.h\"\n#include \"TerrainTextureManager.h\"\n\n#include \"maths/MathUtil.h\"\n#include \"maths/NUSpline.h\"\n#include \"ps/CLogger.h\"\n#include \"ps/Loader.h\"\n#include \"ps/Filesystem.h\"\n#include \"ps/XML/XMLWriter.h\"\n#include \"renderer/PostprocManager.h\"\n#include \"renderer/SkyManager.h\"\n#include \"renderer/WaterManager.h\"\n#include \"simulation2/Simulation2.h\"\n#include \"simulation2/components/ICmpObstruction.h\"\n#include \"simulation2/components/ICmpOwnership.h\"\n#include \"simulation2/components/ICmpPosition.h\"\n#include \"simulation2/components/ICmpTemplateManager.h\"\n#include \"simulation2/components/ICmpVisual.h\"\n#include \"simulation2/components/ICmpWaterManager.h\"\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n// CMapWriter constructor: nothing to do at the minute\nCMapWriter::CMapWriter()\n{\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n// SaveMap: try to save the current map to the given file\nvoid CMapWriter::SaveMap(const VfsPath& pathname, CTerrain* pTerrain,\n\t\t\t\t\t\t WaterManager* pWaterMan, SkyManager* pSkyMan,\n\t\t\t\t\t\t CLightEnv* pLightEnv, CCamera* pCamera, CCinemaManager* pCinema,\n\t\t\t\t\t\t CPostprocManager* pPostproc,\n\t\t\t\t\t\t CSimulation2* pSimulation2)\n{\n\tCFilePacker packer(FILE_VERSION, \"PSMP\");\n\n\t// build necessary data\n\tPackMap(packer, pTerrain);\n\n\ttry\n\t{\n\t\t// write it out\n\t\tpacker.Write(pathname);\n\t}\n\tcatch (PSERROR_File_WriteFailed&)\n\t{\n\t\tLOGERROR(\"Failed to write map '%s'\", pathname.string8());\n\t\treturn;\n\t}\n\n\tVfsPath pathnameXML = pathname.ChangeExtension(L\".xml\");\n\tWriteXML(pathnameXML, pWaterMan, pSkyMan, pLightEnv, pCamera, pCinema, pPostproc, pSimulation2);\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n// GetHandleIndex: return the index of the given handle in the given list; or 0xFFFF if\n// handle isn't in list\nstatic u16 GetEntryIndex(const CTerrainTextureEntry* entry, const std::vector<CTerrainTextureEntry*>& entries)\n{\n\tconst size_t limit = std::min(entries.size(), size_t(0xFFFEu));\t// paranoia\n\tfor (size_t i=0;i<limit;i++) {\n\t\tif (entries[i]==entry) {\n\t\t\treturn (u16)i;\n\t\t}\n\t}\n\n\treturn 0xFFFF;\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n// EnumTerrainTextures: build lists of textures used by map, and tile descriptions for\n// each tile on the terrain\nvoid CMapWriter::EnumTerrainTextures(CTerrain *pTerrain,\n\t\t\t\t\t\t\t\t\t std::vector<CStr>& textures,\n\t\t\t\t\t\t\t\t\t std::vector<STileDesc>& tiles)\n{\n\t// the list of all handles in use\n\tstd::vector<CTerrainTextureEntry*> entries;\n\n\t// resize tile array to required size\n\ttiles.resize(SQR(pTerrain->GetVerticesPerSide()-1));\n\tSTileDesc* tileptr=&tiles[0];\n\n\t// now iterate through all the tiles\n\tconst ssize_t patchesPerSide=pTerrain->GetPatchesPerSide();\n\tfor (ssize_t j=0;j<patchesPerSide;j++) {\n\t\tfor (ssize_t i=0;i<patchesPerSide;i++) {\n\t\t\tfor (ssize_t m=0;m<PATCH_SIZE;m++) {\n\t\t\t\tfor (ssize_t k=0;k<PATCH_SIZE;k++) {\n\t\t\t\t\tCMiniPatch& mp=pTerrain->GetPatch(i,j)->m_MiniPatches[m][k];\t// can't fail\n\t\t\t\t\tu16 index=u16(GetEntryIndex(mp.GetTextureEntry(),entries));\n\t\t\t\t\tif (index==0xFFFF) {\n\t\t\t\t\t\tindex=(u16)entries.size();\n\t\t\t\t\t\tentries.push_back(mp.GetTextureEntry());\n\t\t\t\t\t}\n\n\t\t\t\t\ttileptr->m_Tex1Index=index;\n\t\t\t\t\ttileptr->m_Tex2Index=0xFFFF;\n\t\t\t\t\ttileptr->m_Priority=mp.GetPriority();\n\t\t\t\t\ttileptr++;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// now find the texture names for each handle\n\tfor (size_t i=0;i<entries.size();i++) {\n\t\tCStr texturename;\n\t\tCTerrainTextureEntry* texentry=entries[i];\n\t\tif (!texentry) {\n\t\t\t// uh-oh, this shouldn't happen; set texturename to empty string\n\t\t\ttexturename=\"\";\n\t\t} else {\n\t\t\ttexturename=texentry->GetTag();\n\t\t}\n\t\ttextures.push_back(texturename);\n\t}\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n// PackMap: pack the current world into a raw data stream\nvoid CMapWriter::PackMap(CFilePacker& packer, CTerrain* pTerrain)\n{\n\t// now pack everything up\n\tPackTerrain(packer, pTerrain);\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n// PackTerrain: pack the terrain onto the end of the output data stream\n//\t\t- data: map size, heightmap, list of textures used by map, texture tile assignments\nvoid CMapWriter::PackTerrain(CFilePacker& packer, CTerrain* pTerrain)\n{\n\t// pack map size\n\tconst ssize_t mapsize = pTerrain->GetPatchesPerSide();\n\tpacker.PackSize(mapsize);\n\n\t// pack heightmap\n\tpacker.PackRaw(pTerrain->GetHeightMap(),sizeof(u16)*SQR(pTerrain->GetVerticesPerSide()));\n\n\t// the list of textures used by map\n\tstd::vector<CStr> terrainTextures;\n\t// descriptions of each tile\n\tstd::vector<STileDesc> tiles;\n\n\t// build lists by scanning through the terrain\n\tEnumTerrainTextures(pTerrain, terrainTextures, tiles);\n\n\t// pack texture names\n\tconst size_t numTextures = terrainTextures.size();\n\tpacker.PackSize(numTextures);\n\tfor (size_t i=0;i<numTextures;i++)\n\t\tpacker.PackString(terrainTextures[i]);\n\n\t// pack tile data\n\tpacker.PackRaw(&tiles[0],sizeof(STileDesc)*tiles.size());\n}\n\nvoid CMapWriter::WriteXML(const VfsPath& filename,\n\t\t\t\t\t\t  WaterManager* pWaterMan, SkyManager* pSkyMan,\n\t\t\t\t\t\t  CLightEnv* pLightEnv, CCamera* pCamera, CCinemaManager* pCinema,\n\t\t\t\t\t\t  CPostprocManager* pPostproc,\n\t\t\t\t\t\t  CSimulation2* pSimulation2)\n{\n\tXML_Start();\n\n\t{\n\t\tXML_Element(\"Scenario\");\n\t\tXML_Attribute(\"version\", (int)FILE_VERSION);\n\n\t\tENSURE(pSimulation2);\n\t\tCSimulation2& sim = *pSimulation2;\n\n\t\tif (!sim.GetStartupScript().empty())\n\t\t{\n\t\t\tXML_Element(\"Script\");\n\t\t\tXML_CDATA(sim.GetStartupScript().c_str());\n\t\t}\n\n\t\t{\n\t\t\tXML_Element(\"Environment\");\n\n\t\t\tXML_Setting(\"SkySet\", pSkyMan->GetSkySet());\n\t\t\t{\n\t\t\t\tXML_Element(\"SunColor\");\n\t\t\t\tXML_Attribute(\"r\", pLightEnv->m_SunColor.X); // yes, it's X/Y/Z...\n\t\t\t\tXML_Attribute(\"g\", pLightEnv->m_SunColor.Y);\n\t\t\t\tXML_Attribute(\"b\", pLightEnv->m_SunColor.Z);\n\t\t\t}\n\t\t\t{\n\t\t\t\tXML_Element(\"SunElevation\");\n\t\t\t\tXML_Attribute(\"angle\", pLightEnv->m_Elevation);\n\t\t\t}\n\t\t\t{\n\t\t\t\tXML_Element(\"SunRotation\");\n\t\t\t\tXML_Attribute(\"angle\", pLightEnv->m_Rotation);\n\t\t\t}\n\t\t\t{\n\t\t\t\tXML_Element(\"TerrainAmbientColor\");\n\t\t\t\tXML_Attribute(\"r\", pLightEnv->m_TerrainAmbientColor.X);\n\t\t\t\tXML_Attribute(\"g\", pLightEnv->m_TerrainAmbientColor.Y);\n\t\t\t\tXML_Attribute(\"b\", pLightEnv->m_TerrainAmbientColor.Z);\n\t\t\t}\n\t\t\t{\n\t\t\t\tXML_Element(\"UnitsAmbientColor\");\n\t\t\t\tXML_Attribute(\"r\", pLightEnv->m_UnitsAmbientColor.X);\n\t\t\t\tXML_Attribute(\"g\", pLightEnv->m_UnitsAmbientColor.Y);\n\t\t\t\tXML_Attribute(\"b\", pLightEnv->m_UnitsAmbientColor.Z);\n\t\t\t}\n\t\t\t{\n\t\t\t\tXML_Element(\"Fog\");\n\t\t\t\tXML_Setting(\"FogFactor\", pLightEnv->m_FogFactor);\n\t\t\t\tXML_Setting(\"FogThickness\", pLightEnv->m_FogMax);\n\t\t\t\t{\n\t\t\t\t\tXML_Element(\"FogColor\");\n\t\t\t\t\tXML_Attribute(\"r\", pLightEnv->m_FogColor.X);\n\t\t\t\t\tXML_Attribute(\"g\", pLightEnv->m_FogColor.Y);\n\t\t\t\t\tXML_Attribute(\"b\", pLightEnv->m_FogColor.Z);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t{\n\t\t\t\tXML_Element(\"Water\");\n\t\t\t\t{\n\t\t\t\t\tXML_Element(\"WaterBody\");\n\t\t\t\t\tCmpPtr<ICmpWaterManager> cmpWaterManager(sim, SYSTEM_ENTITY);\n\t\t\t\t\tENSURE(cmpWaterManager);\n\t\t\t\t\tXML_Setting(\"Type\", pWaterMan->m_WaterType);\n\t\t\t\t\t{\n\t\t\t\t\t\tXML_Element(\"Color\");\n\t\t\t\t\t\tXML_Attribute(\"r\", pWaterMan->m_WaterColor.r);\n\t\t\t\t\t\tXML_Attribute(\"g\", pWaterMan->m_WaterColor.g);\n\t\t\t\t\t\tXML_Attribute(\"b\", pWaterMan->m_WaterColor.b);\n\t\t\t\t\t}\n\t\t\t\t\t{\n\t\t\t\t\t\tXML_Element(\"Tint\");\n\t\t\t\t\t\tXML_Attribute(\"r\", pWaterMan->m_WaterTint.r);\n\t\t\t\t\t\tXML_Attribute(\"g\", pWaterMan->m_WaterTint.g);\n\t\t\t\t\t\tXML_Attribute(\"b\", pWaterMan->m_WaterTint.b);\n\t\t\t\t\t}\n\t\t\t\t\tXML_Setting(\"Height\", cmpWaterManager->GetExactWaterLevel(0, 0));\n\t\t\t\t\tXML_Setting(\"Waviness\", pWaterMan->m_Waviness);\n\t\t\t\t\tXML_Setting(\"Murkiness\", pWaterMan->m_Murkiness);\n\t\t\t\t\tXML_Setting(\"WindAngle\", pWaterMan->m_WindAngle);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t{\n\t\t\t\tXML_Element(\"Postproc\");\n\t\t\t\t{\n\t\t\t\t\tXML_Setting(\"Brightness\", pLightEnv->m_Brightness);\n\t\t\t\t\tXML_Setting(\"Contrast\", pLightEnv->m_Contrast);\n\t\t\t\t\tXML_Setting(\"Saturation\", pLightEnv->m_Saturation);\n\t\t\t\t\tXML_Setting(\"Bloom\", pLightEnv->m_Bloom);\n\t\t\t\t\tXML_Setting(\"PostEffect\", pPostproc->GetPostEffect());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t{\n\t\t\tXML_Element(\"Camera\");\n\n\t\t\t{\n\t\t\t\tXML_Element(\"Position\");\n\t\t\t\tCVector3D pos = pCamera->m_Orientation.GetTranslation();\n\t\t\t\tXML_Attribute(\"x\", pos.X);\n\t\t\t\tXML_Attribute(\"y\", pos.Y);\n\t\t\t\tXML_Attribute(\"z\", pos.Z);\n\t\t\t}\n\n\t\t\tCVector3D in = pCamera->m_Orientation.GetIn();\n\t\t\t// Convert to spherical coordinates\n\t\t\tfloat rotation = atan2(in.X, in.Z);\n\t\t\tfloat declination = atan2(sqrt(in.X*in.X + in.Z*in.Z), in.Y) - (float)M_PI/2;\n\n\t\t\t{\n\t\t\t\tXML_Element(\"Rotation\");\n\t\t\t\tXML_Attribute(\"angle\", rotation);\n\t\t\t}\n\t\t\t{\n\t\t\t\tXML_Element(\"Declination\");\n\t\t\t\tXML_Attribute(\"angle\", declination);\n\t\t\t}\n\t\t}\n\n\t\t{\n\t\t\tstd::string settings = sim.GetMapSettingsString();\n\t\t\tif (!settings.empty())\n\t\t\t{\n\t\t\t\tXML_Element(\"ScriptSettings\");\n\t\t\t\tXML_CDATA((\"\\n\" + settings + \"\\n\").c_str());\n\t\t\t}\n\t\t}\n\n\t\t{\n\t\t\tXML_Element(\"Entities\");\n\n\t\t\tCmpPtr<ICmpTemplateManager> cmpTemplateManager(sim, SYSTEM_ENTITY);\n\t\t\tENSURE(cmpTemplateManager);\n\n\t\t\t// This will probably need to be changed in the future, but for now we'll\n\t\t\t// just save all entities that have a position\n\t\t\tCSimulation2::InterfaceList ents = sim.GetEntitiesWithInterface(IID_Position);\n\t\t\tfor (CSimulation2::InterfaceList::const_iterator it = ents.begin(); it != ents.end(); ++it)\n\t\t\t{\n\t\t\t\tentity_id_t ent = it->first;\n\n\t\t\t\t// Don't save local entities (placement previews etc)\n\t\t\t\tif (ENTITY_IS_LOCAL(ent))\n\t\t\t\t\tcontinue;\n\n\t\t\t\tXML_Element(\"Entity\");\n\t\t\t\tXML_Attribute(\"uid\", ent);\n\n\t\t\t\tXML_Setting(\"Template\", cmpTemplateManager->GetCurrentTemplateName(ent));\n\n\t\t\t\tCmpPtr<ICmpOwnership> cmpOwnership(sim, ent);\n\t\t\t\tif (cmpOwnership)\n\t\t\t\t\tXML_Setting(\"Player\", (int)cmpOwnership->GetOwner());\n\n\t\t\t\tCmpPtr<ICmpPosition> cmpPosition(sim, ent);\n\t\t\t\tif (cmpPosition)\n\t\t\t\t{\n\t\t\t\t\tCFixedVector3D pos;\n\t\t\t\t\tif (cmpPosition->IsInWorld())\n\t\t\t\t\t\tpos = cmpPosition->GetPosition();\n\n\t\t\t\t\tCFixedVector3D rot = cmpPosition->GetRotation();\n\t\t\t\t\t{\n\t\t\t\t\t\tXML_Element(\"Position\");\n\t\t\t\t\t\tXML_Attribute(\"x\", pos.X);\n\t\t\t\t\t\tXML_Attribute(\"z\", pos.Z);\n\t\t\t\t\t\t// TODO: height offset etc\n\t\t\t\t\t}\n\t\t\t\t\t{\n\t\t\t\t\t\tXML_Element(\"Orientation\");\n\t\t\t\t\t\tXML_Attribute(\"y\", rot.Y);\n\t\t\t\t\t\t// TODO: X, Z maybe\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tCmpPtr<ICmpObstruction> cmpObstruction(sim, ent);\n\t\t\t\tif (cmpObstruction)\n\t\t\t\t{\n\t\t\t\t\t// TODO: Currently only necessary because Atlas\n\t\t\t\t\t// does not set up control groups for its walls.\n\t\t\t\t\tcmpObstruction->ResolveFoundationCollisions();\n\n\t\t\t\t\tentity_id_t group = cmpObstruction->GetControlGroup();\n\t\t\t\t\tentity_id_t group2 = cmpObstruction->GetControlGroup2();\n\n\t\t\t\t\t// Don't waste space writing the default control groups.\n\t\t\t\t\tif (group != ent || group2 != INVALID_ENTITY)\n\t\t\t\t\t{\n\t\t\t\t\t\tXML_Element(\"Obstruction\");\n\t\t\t\t\t\tif (group != ent)\n\t\t\t\t\t\t\tXML_Attribute(\"group\", group);\n\t\t\t\t\t\tif (group2 != INVALID_ENTITY)\n\t\t\t\t\t\t\tXML_Attribute(\"group2\", group2);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tCmpPtr<ICmpVisual> cmpVisual(sim, ent);\n\t\t\t\tif (cmpVisual)\n\t\t\t\t{\n\t\t\t\t\tu32 seed = cmpVisual->GetActorSeed();\n\t\t\t\t\tif (seed != (u32)ent)\n\t\t\t\t\t{\n\t\t\t\t\t\tXML_Element(\"Actor\");\n\t\t\t\t\t\tXML_Attribute(\"seed\", seed);\n\t\t\t\t\t}\n\t\t\t\t\t// TODO: variation/selection strings\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst std::map<CStrW, CCinemaPath>& paths = pCinema->GetAllPaths();\n\t\tstd::map<CStrW, CCinemaPath>::const_iterator it = paths.begin();\n\n\t\t{\n\t\t\tXML_Element(\"Paths\");\n\n\t\t\tfor ( ; it != paths.end(); ++it )\n\t\t\t{\n\t\t\t\tfixed timescale = it->second.GetTimescale();\n\t\t\t\tconst std::vector<SplineData>& nodes = it->second.GetAllNodes();\n\t\t\t\tconst std::vector<SplineData>& target_nodes = it->second.GetTargetSpline().GetAllNodes();\n\t\t\t\tconst CCinemaData* data = it->second.GetData();\n\t\t\t\t\n\t\t\t\tXML_Element(\"Path\");\n\t\t\t\tXML_Attribute(\"name\", data->m_Name);\n\t\t\t\tXML_Attribute(\"timescale\", timescale);\n\t\t\t\tXML_Attribute(\"orientation\", data->m_Orientation);\n\t\t\t\tXML_Attribute(\"mode\", data->m_Mode);\n\t\t\t\tXML_Attribute(\"style\", data->m_Style);\n\n\t\t\t\tfixed last_target = fixed::Zero();\n\t\t\t\tfor (size_t i = 0, j = 0; i < nodes.size(); ++i)\n\t\t\t\t{\n\t\t\t\t\tXML_Element(\"Node\");\n\t\t\t\t\tfixed distance = i > 0 ? nodes[i - 1].Distance : fixed::Zero();\n\t\t\t\t\tlast_target += distance;\n\n\t\t\t\t\tXML_Attribute(\"deltatime\", distance);\n\n\t\t\t\t\t{\n\t\t\t\t\t\tXML_Element(\"Position\");\n\t\t\t\t\t\tXML_Attribute(\"x\", nodes[i].Position.X);\n\t\t\t\t\t\tXML_Attribute(\"y\", nodes[i].Position.Y);\n\t\t\t\t\t\tXML_Attribute(\"z\", nodes[i].Position.Z);\n\t\t\t\t\t}\n\n\t\t\t\t\t{\n\t\t\t\t\t\tXML_Element(\"Rotation\");\n\t\t\t\t\t\tXML_Attribute(\"x\", nodes[i].Rotation.X);\n\t\t\t\t\t\tXML_Attribute(\"y\", nodes[i].Rotation.Y);\n\t\t\t\t\t\tXML_Attribute(\"z\", nodes[i].Rotation.Z);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (j >= target_nodes.size())\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\tfixed target_distance = j > 0 ? target_nodes[j - 1].Distance : fixed::Zero();\n\n\t\t\t\t\tif (target_distance > last_target)\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\t{\n\t\t\t\t\t\tXML_Element(\"Target\");\n\t\t\t\t\t\tXML_Attribute(\"x\", target_nodes[j].Position.X);\n\t\t\t\t\t\tXML_Attribute(\"y\", target_nodes[j].Position.Y);\n\t\t\t\t\t\tXML_Attribute(\"z\", target_nodes[j].Position.Z);\n\t\t\t\t\t}\n\n\t\t\t\t\tlast_target = fixed::Zero();\n\t\t\t\t\t++j;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tif (!XML_StoreVFS(g_VFS, filename))\n\t\tLOGERROR(\"Failed to write map '%s'\", filename.string8());\n}\n"
  },
  {
    "path": "fpsgame/graphics/MapWriter.h",
    "content": "/* Copyright (C) 2014 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef INCLUDED_MAPWRITER\n#define INCLUDED_MAPWRITER\n\n#include <vector>\n#include <list>\n#include \"MapIO.h\"\n#include \"ps/CStr.h\"\n#include \"ps/FileIo.h\"\n\n\nclass CLightEnv;\nclass CTerrain;\nclass CCamera;\nclass CCinemaManager;\nclass CPostprocManager;\nclass CTriggerManager;\nclass WaterManager;\nclass SkyManager;\nclass CSimulation2;\nstruct MapTrigger;\nstruct MapTriggerGroup;\nclass XMLWriter_File;\n\nclass CMapWriter : public CMapIO\n{\npublic:\n\t// constructor\n\tCMapWriter();\n\t// SaveMap: try to save the current map to the given file\n\tvoid SaveMap(const VfsPath& pathname, CTerrain* pTerr,\n\t\t\t\t\t\t\t\t\tWaterManager* pWaterMan, SkyManager* pSkyMan, \n\t\t\t\t\t\t\t\t\tCLightEnv* pLightEnv, CCamera* pCamera, \n\t\t\t\t\t\t\t\t\tCCinemaManager* pCinema, CPostprocManager* pPostproc,\n\t\t\t\t\t\t\t\t\tCSimulation2* pSimulation2);\n\nprivate:\n\t// PackMap: pack the current world into a raw data stream\n\tvoid PackMap(CFilePacker& packer, CTerrain* pTerrain);\n\t// PackTerrain: pack the terrain onto the end of the data stream\n\tvoid PackTerrain(CFilePacker& packer, CTerrain* pTerrain);\n\n\t// EnumTerrainTextures: build lists of textures used by map, and indices into this list \n\t// for each tile on the terrain\n\tvoid EnumTerrainTextures(CTerrain* pTerrain, std::vector<CStr>& textures,\n\t\tstd::vector<STileDesc>& tileIndices);\n\n\t// WriteXML: output some other data (entities, etc) in XML format\n\tvoid WriteXML(const VfsPath& pathname, WaterManager* pWaterMan,\n\t\t\t\t\t\t\t\tSkyManager* pSkyMan, CLightEnv* pLightEnv, CCamera* pCamera, \n\t\t\t\t\t\t\t\tCCinemaManager* pCinema, CPostprocManager* pPostproc,\n\t\t\t\t\t\t\t\tCSimulation2* pSimulation2);\n};\n\n#endif\n"
  },
  {
    "path": "fpsgame/graphics/Material.cpp",
    "content": "/* Copyright (C) 2012 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"precompiled.h\"\n\n#include \"Material.h\"\n\nstatic CColor BrokenColor(0.3f, 0.3f, 0.3f, 1.0f);\n\nCMaterial::CMaterial() :\n\tm_AlphaBlending(false)\n{\n}\n\nvoid CMaterial::SetShaderEffect(const CStr& effect)\n{\n\tm_ShaderEffect = CStrIntern(effect);\n}\n\nvoid CMaterial::AddShaderDefine(CStrIntern key, CStrIntern value)\n{\n\tm_ShaderDefines.Add(key, value);\n\tm_CombinedShaderDefines.clear();\n}\n\nvoid CMaterial::AddConditionalDefine(const char* defname, const char* defvalue, int type, std::vector<float> &args)\n{\n\tm_ConditionalDefines.Add(defname, defvalue, type, args);\n\tm_CombinedShaderDefines.clear();\n}\n\nvoid CMaterial::AddStaticUniform(const char* key, const CVector4D& value)\n{\n\tm_StaticUniforms.Add(key, value);\n}\n\nvoid CMaterial::AddSampler(const TextureSampler& texture)\n{\n\tm_Samplers.push_back(texture);\n\tif (texture.Name == str_baseTex)\n\t\tm_DiffuseTexture = texture.Sampler;\n}\n\nvoid CMaterial::AddRenderQuery(const char* key)\n{\n\tm_RenderQueries.Add(key);\n}\n\nvoid CMaterial::AddRequiredSampler(const CStr& samplerName)\n{\n\tCStrIntern string(samplerName);\n\tm_RequiredSamplers.push_back(string);\n}\n\n\n// Set up m_CombinedShaderDefines so that index i contains m_ShaderDefines, plus\n// the extra defines from m_ConditionalDefines[j] for all j where bit j is set in i.\n// This lets GetShaderDefines() cheaply return the defines for any combination of conditions.\n//\n// (This might scale badly if we had a large number of conditional defines per material,\n// but currently we don't expect to have many.)\nvoid CMaterial::RecomputeCombinedShaderDefines()\n{\n\tm_CombinedShaderDefines.clear();\n\tint size = m_ConditionalDefines.GetSize();\n\n\t// Loop over all 2^n combinations of flags\n\tfor (int i = 0; i < (1 << size); i++)\n\t{\n\t\tCShaderDefines defs = m_ShaderDefines;\n\t\tfor (int j = 0; j < size; j++)\n\t\t{\n\t\t\tif (i & (1 << j))\n\t\t\t{\n\t\t\t\tconst CShaderConditionalDefines::CondDefine& def = m_ConditionalDefines.GetItem(j);\n\t\t\t\tdefs.Add(def.m_DefName, def.m_DefValue);\n\t\t\t}\n\t\t}\n\t\tm_CombinedShaderDefines.push_back(defs);\n\t}\n}\n"
  },
  {
    "path": "fpsgame/graphics/Material.h",
    "content": "/* Copyright (C) 2012 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef INCLUDED_MATERIAL\n#define INCLUDED_MATERIAL\n\n#include \"graphics/ShaderDefines.h\"\n#include \"graphics/Texture.h\"\n#include \"ps/CStr.h\"\n#include \"ps/CStrIntern.h\"\n#include \"ps/Shapes.h\"\n#include \"simulation2/helpers/Player.h\"\n\nclass CMaterial\n{\npublic:\n\t\n\tstruct TextureSampler\n\t{\n\t\tTextureSampler(const CStr &n, CTexturePtr t) : Name(n), Sampler(t) {}\n\t\tTextureSampler(const CStrIntern &n, CTexturePtr t) : Name(n), Sampler(t) {}\n\t\t\n\t\tCStrIntern Name;\n\t\tCTexturePtr Sampler;\n\t};\n\t\n\ttypedef std::vector<TextureSampler> SamplersVector;\n\t\n\tCMaterial();\n\n\t// Whether this material's shaders use alpha blending, in which case\n\t// models using this material need to be rendered in a special order\n\t// relative to the alpha-blended water plane\n\tvoid SetUsesAlphaBlending(bool flag) { m_AlphaBlending = flag; }\n \tbool UsesAlphaBlending() { return m_AlphaBlending; }\n\n\tconst CTexturePtr& GetDiffuseTexture() const { return m_DiffuseTexture; }\n\n\tvoid SetShaderEffect(const CStr& effect);\n\tCStrIntern GetShaderEffect() const { return m_ShaderEffect; }\n\n\t// Must call RecomputeCombinedShaderDefines after this, before rendering with this material\n\tvoid AddShaderDefine(CStrIntern key, CStrIntern value);\n\n\t// conditionFlags is a bitmask representing which indexes of the\n\t// GetConditionalDefines() list are currently matching.\n\t// Use 0 if you don't care about conditional defines.\n\tconst CShaderDefines& GetShaderDefines(uint32_t conditionFlags) const { return m_CombinedShaderDefines.at(conditionFlags); }\n\t\n\t// Must call RecomputeCombinedShaderDefines after this, before rendering with this material\n\tvoid AddConditionalDefine(const char* defname, const char* defvalue, int type, std::vector<float> &args);\n\n\tconst CShaderConditionalDefines& GetConditionalDefines() const { return m_ConditionalDefines; }\n\n\tvoid AddStaticUniform(const char* key, const CVector4D& value);\n\tconst CShaderUniforms& GetStaticUniforms() const { return m_StaticUniforms; }\n\n\tvoid AddSampler(const TextureSampler& texture);\n\tconst SamplersVector& GetSamplers() const { return m_Samplers; }\n\t\n\tvoid AddRenderQuery(const char* key);\n\tconst CShaderRenderQueries& GetRenderQueries() const { return m_RenderQueries; }\n\n\tvoid AddRequiredSampler(const CStr& samplerName);\n\tconst std::vector<CStrIntern>& GetRequiredSampler() const { return m_RequiredSamplers; }\n\n\t// Must be called after all AddShaderDefine and AddConditionalDefine\n\tvoid RecomputeCombinedShaderDefines();\n\nprivate:\n\t\n\t// This pointer is kept to make it easier for the fixed pipeline to \n\t// access the only texture it's interested in.\n\tCTexturePtr m_DiffuseTexture;\n\t\n\tSamplersVector m_Samplers;\n\tstd::vector<CStrIntern> m_RequiredSamplers;\n\t\n\tCStrIntern m_ShaderEffect;\n\tCShaderDefines m_ShaderDefines;\n\tCShaderConditionalDefines m_ConditionalDefines;\n\tstd::vector<CShaderDefines> m_CombinedShaderDefines;\n\tCShaderUniforms m_StaticUniforms;\n\tCShaderRenderQueries m_RenderQueries;\n\n\tbool m_AlphaBlending;\n};\n\n#endif\n"
  },
  {
    "path": "fpsgame/graphics/MaterialManager.cpp",
    "content": "/* Copyright (C) 2015 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"precompiled.h\"\n\n#include \"MaterialManager.h\"\n\n#include \"lib/ogl.h\"\n#include \"maths/MathUtil.h\"\n#include \"maths/Vector4D.h\"\n#include \"ps/CLogger.h\"\n#include \"ps/ConfigDB.h\"\n#include \"ps/Filesystem.h\"\n#include \"ps/PreprocessorWrapper.h\"\n#include \"ps/XML/Xeromyces.h\"\n#include \"renderer/Renderer.h\"\n\n#include <sstream>\n\nCMaterialManager::CMaterialManager()\n{\n\tqualityLevel = 5.0;\n\tCFG_GET_VAL(\"materialmgr.quality\", qualityLevel);\n\tqualityLevel = clamp(qualityLevel, 0.0f, 10.0f);\n\n\tif (VfsDirectoryExists(L\"art/materials/\") && !CXeromyces::AddValidator(g_VFS, \"material\", \"art/materials/material.rng\"))\n\t\tLOGERROR(\"CMaterialManager: failed to load grammar file 'art/materials/material.rng'\");\n}\n\nCMaterial CMaterialManager::LoadMaterial(const VfsPath& pathname)\n{\n\tif (pathname.empty())\n\t\treturn CMaterial();\n\n\tstd::map<VfsPath, CMaterial>::iterator iter = m_Materials.find(pathname);\n\tif (iter != m_Materials.end())\n\t\treturn iter->second;\n\n\tCXeromyces xeroFile;\n\tif (xeroFile.Load(g_VFS, pathname, \"material\") != PSRETURN_OK)\n\t\treturn CMaterial();\n\n\t#define EL(x) int el_##x = xeroFile.GetElementID(#x)\n\t#define AT(x) int at_##x = xeroFile.GetAttributeID(#x)\n\tEL(alpha_blending);\n\tEL(alternative);\n\tEL(define);\n\tEL(shader);\n\tEL(uniform);\n\tEL(renderquery);\n\tEL(required_texture);\n\tEL(conditional_define);\n\tAT(effect);\n\tAT(if);\n\tAT(define);\n\tAT(quality);\n\tAT(material);\n\tAT(name);\n\tAT(value);\n\tAT(type);\n\tAT(min);\n\tAT(max);\n\tAT(conf);\n\t#undef AT\n\t#undef EL\n\n\tCMaterial material;\n\n\tXMBElement root = xeroFile.GetRoot();\n\t\n\tCPreprocessorWrapper preprocessor;\n\tpreprocessor.AddDefine(\"CFG_FORCE_ALPHATEST\", g_Renderer.m_Options.m_ForceAlphaTest ? \"1\" : \"0\");\n\t\n\tCVector4D vec(qualityLevel,0,0,0);\n\tmaterial.AddStaticUniform(\"qualityLevel\", vec);\n\n\tXERO_ITER_EL(root, node)\n\t{\n\t\tint token = node.GetNodeName();\n\t\tXMBAttributeList attrs = node.GetAttributes();\n\t\tif (token == el_alternative)\n\t\t{\n\t\t\tCStr cond = attrs.GetNamedItem(at_if);\n\t\t\tif (cond.empty() || !preprocessor.TestConditional(cond))\n\t\t\t{\n\t\t\t\tcond = attrs.GetNamedItem(at_quality);\n\t\t\t\tif (cond.empty())\n\t\t\t\t\tcontinue;\n\t\t\t\telse\n\t\t\t\t{\t\n\t\t\t\t\tif (cond.ToFloat() <= qualityLevel)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t\t\t\n\t\t\tmaterial = LoadMaterial(VfsPath(\"art/materials\") / attrs.GetNamedItem(at_material).FromUTF8());\n\t\t\tbreak;\n\t\t}\n\t\telse if (token == el_alpha_blending)\n\t\t{\n\t\t\tmaterial.SetUsesAlphaBlending(true);\n\t\t}\n\t\telse if (token == el_shader)\n\t\t{\n\t\t\tmaterial.SetShaderEffect(attrs.GetNamedItem(at_effect));\n\t\t}\n\t\telse if (token == el_define)\n\t\t{\n\t\t\tmaterial.AddShaderDefine(CStrIntern(attrs.GetNamedItem(at_name)), CStrIntern(attrs.GetNamedItem(at_value)));\n\t\t}\n\t\telse if (token == el_conditional_define)\n\t\t{\n\t\t\tstd::vector<float> args;\n\t\t\t\n\t\t\tCStr type = attrs.GetNamedItem(at_type).c_str();\n\t\t\tint typeID = -1;\n\t\t\t\n\t\t\tif (type == CStr(\"draw_range\"))\n\t\t\t{\n\t\t\t\ttypeID = DCOND_DISTANCE;\n\t\t\t\t\n\t\t\t\tfloat valmin = -1.0f; \n\t\t\t\tfloat valmax = -1.0f;\n\t\t\t\t\n\t\t\t\tCStr conf = attrs.GetNamedItem(at_conf);\n\t\t\t\tif (!conf.empty())\n\t\t\t\t{\n\t\t\t\t\tCFG_GET_VAL(\"materialmgr.\" + conf + \".min\", valmin);\n\t\t\t\t\tCFG_GET_VAL(\"materialmgr.\" + conf + \".max\", valmax);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tCStr dmin = attrs.GetNamedItem(at_min);\n\t\t\t\t\tif (!dmin.empty())\n\t\t\t\t\t\tvalmin = attrs.GetNamedItem(at_min).ToFloat();\n\t\t\t\t\t\n\t\t\t\t\tCStr dmax = attrs.GetNamedItem(at_max);\n\t\t\t\t\tif (!dmax.empty())\n\t\t\t\t\t\tvalmax = attrs.GetNamedItem(at_max).ToFloat();\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\targs.push_back(valmin);\n\t\t\t\targs.push_back(valmax);\n\t\t\t\t\n\t\t\t\tif (valmin >= 0.0f)\n\t\t\t\t{\n\t\t\t\t\tstd::stringstream sstr;\n\t\t\t\t\tsstr << valmin;\n\t\t\t\t\tmaterial.AddShaderDefine(CStrIntern(conf + \"_MIN\"), CStrIntern(sstr.str()));\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (valmax >= 0.0f)\n\t\t\t\t{\t\n\t\t\t\t\tstd::stringstream sstr;\n\t\t\t\t\tsstr << valmax;\n\t\t\t\t\tmaterial.AddShaderDefine(CStrIntern(conf + \"_MAX\"), CStrIntern(sstr.str()));\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tmaterial.AddConditionalDefine(attrs.GetNamedItem(at_name).c_str(), \n\t\t\t\t\t\t      attrs.GetNamedItem(at_value).c_str(), \n\t\t\t\t\t\t      typeID, args);\n\t\t}\t\t\n\t\telse if (token == el_uniform)\n\t\t{\n\t\t\tstd::stringstream str(attrs.GetNamedItem(at_value));\n\t\t\tCVector4D vec;\n\t\t\tstr >> vec.X >> vec.Y >> vec.Z >> vec.W;\n\t\t\tmaterial.AddStaticUniform(attrs.GetNamedItem(at_name).c_str(), vec);\n\t\t}\n\t\telse if (token == el_renderquery)\n\t\t{\n\t\t\tmaterial.AddRenderQuery(attrs.GetNamedItem(at_name).c_str());\n\t\t}\n\t\telse if (token == el_required_texture)\n\t\t{\n\t\t\tmaterial.AddRequiredSampler(attrs.GetNamedItem(at_name));\n\t\t\tif (!attrs.GetNamedItem(at_define).empty())\n\t\t\t\tmaterial.AddShaderDefine(CStrIntern(attrs.GetNamedItem(at_define)), str_1);\n\t\t}\n\t}\n\n\tmaterial.RecomputeCombinedShaderDefines();\n\n\tm_Materials[pathname] = material;\n\treturn material;\n}\n"
  },
  {
    "path": "fpsgame/graphics/MaterialManager.h",
    "content": "/* Copyright (C) 2012 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef INCLUDED_MATERIALMANAGER\n#define INCLUDED_MATERIALMANAGER\n\n#include <map>\n#include \"Material.h\"\n#include \"lib/file/vfs/vfs_path.h\"\n\nclass CMaterialManager\n{\npublic:\n\tCMaterialManager();\n\tCMaterial LoadMaterial(const VfsPath& pathname);\n\nprivate:\n\tstd::map<VfsPath, CMaterial> m_Materials;\n\tfloat qualityLevel;\n};\n\n#endif // INCLUDED_MATERIALMANAGER\n"
  },
  {
    "path": "fpsgame/graphics/MeshManager.cpp",
    "content": "/* Copyright (C) 2012 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"precompiled.h\"\n\n#include \"MeshManager.h\"\n\n#include \"graphics/ColladaManager.h\"\n#include \"graphics/ModelDef.h\"\n#include \"ps/CLogger.h\"\n#include \"ps/FileIo.h\" // to get access to its CError\n#include \"ps/Profile.h\"\n\n// TODO: should this cache models while they're not actively in the game?\n// (Currently they'll probably be deleted when the reference count drops to 0,\n// even if it's quite possible that they'll get reloaded very soon.)\n\nCMeshManager::CMeshManager(CColladaManager& colladaManager)\n: m_ColladaManager(colladaManager)\n{\n}\n\nCMeshManager::~CMeshManager()\n{\n}\n\nCModelDefPtr CMeshManager::GetMesh(const VfsPath& pathname)\n{\n\tconst VfsPath name = pathname.ChangeExtension(L\"\");\n\n\t// Find the mesh if it's already been loaded and cached\n\tmesh_map::iterator iter = m_MeshMap.find(name);\n\tif (iter != m_MeshMap.end() && !iter->second.expired())\n\t\treturn CModelDefPtr(iter->second);\n\n\tPROFILE(\"load mesh\");\n\n\tVfsPath pmdFilename = m_ColladaManager.GetLoadablePath(name, CColladaManager::PMD);\n\n\tif (pmdFilename.empty())\n\t{\n\t\tLOGERROR(\"Could not load mesh '%s'\", pathname.string8());\n\t\treturn CModelDefPtr();\n\t}\n\n\ttry\n\t{\n\t\tCModelDefPtr model (CModelDef::Load(pmdFilename, name));\n\t\tm_MeshMap[name] = model;\n\t\treturn model;\n\t}\n\tcatch (PSERROR_File&)\n\t{\n\t\tLOGERROR(\"Could not load mesh '%s'\", pmdFilename.string8());\n\t\treturn CModelDefPtr();\n\t}\n}\n"
  },
  {
    "path": "fpsgame/graphics/MeshManager.h",
    "content": "/* Copyright (C) 2015 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef INCLUDED_MESHMANAGER\n#define INCLUDED_MESHMANAGER\n\n#include \"lib/file/vfs/vfs_path.h\"\n\n#include <boost/unordered_map.hpp>\n#include <memory>\n\nclass CModelDef;\ntypedef std::shared_ptr<CModelDef> CModelDefPtr;\n\nclass CColladaManager;\n\nclass CMeshManager\n{\n\tNONCOPYABLE(CMeshManager);\npublic:\n\tCMeshManager(CColladaManager& colladaManager);\n\t~CMeshManager();\n\n\tCModelDefPtr GetMesh(const VfsPath& pathname);\n\nprivate:\n\ttypedef boost::unordered_map<VfsPath, std::weak_ptr<CModelDef> > mesh_map;\n\tmesh_map m_MeshMap;\n\tCColladaManager& m_ColladaManager;\n};\n\n#endif\n"
  },
  {
    "path": "fpsgame/graphics/MiniPatch.cpp",
    "content": "/* Copyright (C) 2010 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n/*\n * Definition of a single terrain tile\n */\n\n#include \"precompiled.h\"\n\n#include \"MiniPatch.h\"\n\n///////////////////////////////////////////////////////////////////////////////\n// Constructor\nCMiniPatch::CMiniPatch() :\n\tTex(NULL), Priority(0)\n{\n}\n"
  },
  {
    "path": "fpsgame/graphics/MiniPatch.h",
    "content": "/* Copyright (C) 2010 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n/*\n * Definition of a single terrain tile\n */\n\n#ifndef INCLUDED_MINIPATCH\n#define INCLUDED_MINIPATCH\n\n#include \"lib/res/handle.h\"\n\n#include \"graphics/TerrainTextureEntry.h\"\n\n///////////////////////////////////////////////////////////////////////////////\n// CMiniPatch: definition of a single terrain tile\nclass CMiniPatch\n{\npublic:\n\t// constructor\n\tCMiniPatch();\n\n\t// texture applied to tile\n\tCTerrainTextureEntry* Tex;\n\t// 'priority' of the texture - determines drawing order of terrain textures\n\tint Priority;\n\n\tCTerrainTextureEntry* GetTextureEntry() { return Tex; }\n\tint GetPriority() { return Priority; }\n};\n\n\n#endif\n"
  },
  {
    "path": "fpsgame/graphics/Model.cpp",
    "content": "/* Copyright (C) 2016 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n/*\n * Mesh object with texture and skinning information\n */\n\n#include \"precompiled.h\"\n\n#include \"Model.h\"\n\n#include \"Decal.h\"\n#include \"ModelDef.h\"\n#include \"maths/Quaternion.h\"\n#include \"maths/BoundingBoxAligned.h\"\n#include \"SkeletonAnim.h\"\n#include \"SkeletonAnimDef.h\"\n#include \"SkeletonAnimManager.h\"\n#include \"MeshManager.h\"\n#include \"ObjectEntry.h\"\n#include \"lib/res/graphics/ogl_tex.h\"\n#include \"lib/res/h_mgr.h\"\n#include \"lib/sysdep/rtl.h\"\n#include \"ps/Profile.h\"\n#include \"ps/CLogger.h\"\n#include \"renderer/Renderer.h\"\n#include \"simulation2/Simulation2.h\"\n#include \"simulation2/components/ICmpTerrain.h\"\n\n\n/////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Constructor\nCModel::CModel(CSkeletonAnimManager& skeletonAnimManager, CSimulation2& simulation)\n\t: m_Flags(0), m_Anim(NULL), m_AnimTime(0), m_Simulation(simulation),\n\tm_BoneMatrices(NULL), m_AmmoPropPoint(NULL), m_AmmoLoadedProp(0),\n\tm_SkeletonAnimManager(skeletonAnimManager)\n{\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Destructor\nCModel::~CModel()\n{\n\tReleaseData();\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// ReleaseData: delete anything allocated by the model\nvoid CModel::ReleaseData()\n{\n\trtl_FreeAligned(m_BoneMatrices);\n\n\tfor (size_t i = 0; i < m_Props.size(); ++i)\n\t\tdelete m_Props[i].m_Model;\n\tm_Props.clear();\n\n\tm_pModelDef = CModelDefPtr();\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// InitModel: setup model from given geometry\nbool CModel::InitModel(const CModelDefPtr& modeldef)\n{\n\t// clean up any existing data first\n\tReleaseData();\n\n\tm_pModelDef = modeldef;\n\t\n\tsize_t numBones = modeldef->GetNumBones();\n\tif (numBones != 0)\n\t{\n\t\tsize_t numBlends = modeldef->GetNumBlends();\n\n\t\t// allocate matrices for bone transformations\n\t\t// (one extra matrix is used for the special case of bind-shape relative weighting)\n\t\tm_BoneMatrices = (CMatrix3D*)rtl_AllocateAligned(sizeof(CMatrix3D) * (numBones + 1 + numBlends), 16);\n\t\tfor (size_t i = 0; i < numBones + 1 + numBlends; ++i)\n\t\t{\n\t\t\tm_BoneMatrices[i].SetIdentity();\n\t\t}\n\t}\n\n\tm_PositionValid = true;\n\n\treturn true;\n}\n\n\n/////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// CalcBound: calculate the world space bounds of this model\nvoid CModel::CalcBounds()\n{\n\t// Need to calculate the object bounds first, if that hasn't already been done\n\tif (! (m_Anim && m_Anim->m_AnimDef))\n\t{\n\t\tif (m_ObjectBounds.IsEmpty())\n\t\t\tCalcStaticObjectBounds();\n\t}\n\telse\n\t{\n\t\tif (m_Anim->m_ObjectBounds.IsEmpty())\n\t\t\tCalcAnimatedObjectBounds(m_Anim->m_AnimDef, m_Anim->m_ObjectBounds);\n\t\tENSURE(! m_Anim->m_ObjectBounds.IsEmpty()); // (if this happens, it'll be recalculating the bounds every time)\n\t\tm_ObjectBounds = m_Anim->m_ObjectBounds;\n\t}\n\n\t// Ensure the transform is set correctly before we use it\n\tValidatePosition();\n\n\t// Now transform the object-space bounds to world-space bounds\n\tm_ObjectBounds.Transform(GetTransform(), m_WorldBounds);\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// CalcObjectBounds: calculate object space bounds of this model, based solely on vertex positions\nvoid CModel::CalcStaticObjectBounds()\n{\n\tm_ObjectBounds.SetEmpty();\n\n\tsize_t numverts=m_pModelDef->GetNumVertices();\n\tSModelVertex* verts=m_pModelDef->GetVertices();\n\n\tfor (size_t i=0;i<numverts;i++) {\n\t\tm_ObjectBounds+=verts[i].m_Coords;\n\t}\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// CalcAnimatedObjectBound: calculate bounds encompassing all vertex positions for given animation \nvoid CModel::CalcAnimatedObjectBounds(CSkeletonAnimDef* anim, CBoundingBoxAligned& result)\n{\n\tresult.SetEmpty();\n\n\t// Set the current animation on which to perform calculations (if it's necessary)\n\tif (anim != m_Anim->m_AnimDef)\n\t{\n\t\tCSkeletonAnim dummyanim;\n\t\tdummyanim.m_AnimDef=anim;\n\t\tif (!SetAnimation(&dummyanim)) return;\n\t}\n\n\tsize_t numverts=m_pModelDef->GetNumVertices();\n\tSModelVertex* verts=m_pModelDef->GetVertices();\n\n\t// Remove any transformations, so that we calculate the bounding box\n\t// at the origin. The box is later re-transformed onto the object, without\n\t// having to recalculate the size of the box.\n\tCMatrix3D transform, oldtransform = GetTransform();\n\tCModelAbstract* oldparent = m_Parent;\n\t\n\tm_Parent = 0;\n\ttransform.SetIdentity();\n\tCRenderableObject::SetTransform(transform);\n\n\t// Following seems to stomp over the current animation time - which, unsurprisingly,\n\t// introduces artefacts in the currently playing animation. Save it here and restore it\n\t// at the end.\n\tfloat AnimTime = m_AnimTime;\n\n\t// iterate through every frame of the animation\n\tfor (size_t j=0;j<anim->GetNumFrames();j++) {\n\t\tm_PositionValid = false;\n\t\tValidatePosition();\n\n\t\t// extend bounds by vertex positions at the frame\n\t\tfor (size_t i=0;i<numverts;i++)\n\t\t{\n\t\t\tresult += CModelDef::SkinPoint(verts[i], GetAnimatedBoneMatrices());\n\t\t}\n\t\t// advance to next frame\n\t\tm_AnimTime += anim->GetFrameTime();\n\t}\n\n\tm_PositionValid = false;\n\tm_Parent = oldparent;\n\tSetTransform(oldtransform);\n\tm_AnimTime = AnimTime;\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////////////////\nconst CBoundingBoxAligned CModel::GetWorldBoundsRec()\n{\n\tCBoundingBoxAligned bounds = GetWorldBounds();\n\tfor (size_t i = 0; i < m_Props.size(); ++i)\n\t\tbounds += m_Props[i].m_Model->GetWorldBoundsRec();\n\treturn bounds;\n}\n\nconst CBoundingBoxAligned CModel::GetObjectSelectionBoundsRec()\n{\n\tCBoundingBoxAligned objBounds = GetObjectBounds();\t\t// updates the (children-not-included) object-space bounds if necessary\n\n\t// now extend these bounds to include the props' selection bounds (if any)\n\tfor (size_t i = 0; i < m_Props.size(); ++i)\n\t{\n\t\tconst Prop& prop = m_Props[i];\n\t\tif (prop.m_Hidden || !prop.m_Selectable)\n\t\t\tcontinue; // prop is hidden from rendering, so it also shouldn't be used for selection\n\n\t\tCBoundingBoxAligned propSelectionBounds = prop.m_Model->GetObjectSelectionBoundsRec();\n\t\tif (propSelectionBounds.IsEmpty())\n\t\t\tcontinue;\t// submodel does not wish to participate in selection box, exclude it\n\n\t\t// We have the prop's bounds in its own object-space; now we need to transform them so they can be properly added \n\t\t// to the bounds in our object-space. For that, we need the transform of the prop attachment point.\n\t\t// \n\t\t// We have the prop point information; however, it's not trivial to compute its exact location in our object-space\n\t\t// since it may or may not be attached to a bone (see SPropPoint), which in turn may or may not be in the middle of\n\t\t// an animation. The bone matrices might be of interest, but they're really only meant to be used for the animation \n\t\t// system and are quite opaque to use from the outside (see @ref ValidatePosition).\n\t\t// \n\t\t// However, a nice side effect of ValidatePosition is that it also computes the absolute world-space transform of \n\t\t// our props and sets it on their respective models. In particular, @ref ValidatePosition will compute the prop's\n\t\t// world-space transform as either\n\t\t// \n\t\t// T' = T x\tB x O\n\t\t// or \n\t\t// T' = T x O\n\t\t// \n\t\t// where T' is the prop's world-space transform, T is our world-space transform, O is the prop's local\n\t\t// offset/rotation matrix, and B is an optional transformation matrix of the bone the prop is attached to \n\t\t// (taking into account animation and everything).\n\t\t// \n\t\t// From this, it is clear that either O or B x O is the object-space transformation matrix of the prop. So,\n\t\t// all we need to do is apply our own inverse world-transform T^(-1) to T' to get our desired result. Luckily,\n\t\t// this is precomputed upon setting the transform matrix (see @ref SetTransform), so it is free to fetch.\n\t\t\n\t\tCMatrix3D propObjectTransform = prop.m_Model->GetTransform(); // T'\n\t\tpropObjectTransform.Concatenate(GetInvTransform()); // T^(-1) x T'\n\n\t\t// Transform the prop's bounds into our object coordinate space\n\t\tCBoundingBoxAligned transformedPropSelectionBounds;\n\t\tpropSelectionBounds.Transform(propObjectTransform, transformedPropSelectionBounds);\n\n\t\tobjBounds += transformedPropSelectionBounds;\n\t}\n\n\treturn objBounds;\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// BuildAnimation: load raw animation frame animation from given file, and build a \n// animation specific to this model\nCSkeletonAnim* CModel::BuildAnimation(const VfsPath& pathname, const CStr& name, int frequency, float speed, float actionpos, float actionpos2, float soundpos)\n{\n\tCSkeletonAnimDef* def = m_SkeletonAnimManager.GetAnimation(pathname);\n\tif (!def)\n\t\treturn NULL;\n\n\tCSkeletonAnim* anim = new CSkeletonAnim();\n\tanim->m_Name = name;\n\tanim->m_Frequency = frequency;\n\tanim->m_AnimDef = def;\n\tanim->m_Speed = speed;\n\n\tif (actionpos == -1.f)\n\t\tanim->m_ActionPos = -1.f;\n\telse\n\t\tanim->m_ActionPos = actionpos * anim->m_AnimDef->GetDuration();\n\n\tif (actionpos2 == -1.f)\n\t\tanim->m_ActionPos2 = -1.f;\n\telse\n\t\tanim->m_ActionPos2 = actionpos2 * anim->m_AnimDef->GetDuration();\n\n\tif (soundpos == -1.f)\n\t\tanim->m_SoundPos = -1.f;\n\telse\n\t\tanim->m_SoundPos = soundpos * anim->m_AnimDef->GetDuration();\n\n\tanim->m_ObjectBounds.SetEmpty();\n\tInvalidateBounds();\n\n\treturn anim;\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Update: update this model to the given time, in msec\nvoid CModel::UpdateTo(float time)\n{\n\t// update animation time, but don't calculate bone matrices - do that (lazily) when\n\t// something requests them; that saves some calculation work for offscreen models,\n\t// and also assures the world space, inverted bone matrices (required for normal\n\t// skinning) are up to date with respect to m_Transform\n\tm_AnimTime = time;\n\n\t// mark vertices as dirty\n\tSetDirty(RENDERDATA_UPDATE_VERTICES);\n\n\t// mark matrices as dirty\n\tInvalidatePosition();\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// InvalidatePosition\nvoid CModel::InvalidatePosition()\n{\n\tm_PositionValid = false;\n\n\tfor (size_t i = 0; i < m_Props.size(); ++i)\n\t\tm_Props[i].m_Model->InvalidatePosition();\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// ValidatePosition: ensure that current transform and bone matrices are both uptodate\nvoid CModel::ValidatePosition()\n{\n\tif (m_PositionValid)\n\t{\n\t\tENSURE(!m_Parent || m_Parent->m_PositionValid);\n\t\treturn;\n\t}\n\t\n\tif (m_Parent && !m_Parent->m_PositionValid)\n\t{\n\t\t// Make sure we don't base our calculations on\n\t\t// a parent animation state that is out of date.\n\t\tm_Parent->ValidatePosition();\n\t\t\n\t\t// Parent will recursively call our validation.\n\t\tENSURE(m_PositionValid);\n\t\treturn;\n\t}\n\n\tif (m_Anim && m_BoneMatrices)\n\t{\n//\t\tPROFILE( \"generating bone matrices\" );\n\t\n\t\tENSURE(m_pModelDef->GetNumBones() == m_Anim->m_AnimDef->GetNumKeys());\n\t\n\t\tm_Anim->m_AnimDef->BuildBoneMatrices(m_AnimTime, m_BoneMatrices, !(m_Flags & MODELFLAG_NOLOOPANIMATION));\n\t}\n\telse if (m_BoneMatrices)\n\t{\n\t\t// Bones but no animation - probably a buggy actor forgot to set up the animation,\n\t\t// so just render it in its bind pose\n\n\t\tfor (size_t i = 0; i < m_pModelDef->GetNumBones(); i++)\n\t\t{\n\t\t\tm_BoneMatrices[i].SetIdentity();\n\t\t\tm_BoneMatrices[i].Rotate(m_pModelDef->GetBones()[i].m_Rotation);\n\t\t\tm_BoneMatrices[i].Translate(m_pModelDef->GetBones()[i].m_Translation);\n\t\t}\n\t}\n\n\t// For CPU skinning, we precompute as much as possible so that the only\n\t// per-vertex work is a single matrix*vec multiplication.\n\t// For GPU skinning, we try to minimise CPU work by doing most computation\n\t// in the vertex shader instead.\n\t// Using g_Renderer.m_Options to detect CPU vs GPU is a bit hacky,\n\t// and this doesn't allow the setting to change at runtime, but there isn't\n\t// an obvious cleaner way to determine what data needs to be computed,\n\t// and GPU skinning is a rarely-used experimental feature anyway.\n\tbool worldSpaceBoneMatrices = !g_Renderer.m_Options.m_GPUSkinning;\n\tbool computeBlendMatrices = !g_Renderer.m_Options.m_GPUSkinning;\n\n\tif (m_BoneMatrices && worldSpaceBoneMatrices)\n\t{\n\t\t// add world-space transformation to m_BoneMatrices\n\t\tconst CMatrix3D transform = GetTransform();\n\t\tfor (size_t i = 0; i < m_pModelDef->GetNumBones(); i++)\n\t\t\tm_BoneMatrices[i].Concatenate(transform);\n\t}\n\n\t// our own position is now valid; now we can safely update our props' positions without fearing \n\t// that doing so will cause a revalidation of this model (see recursion above).\n\tm_PositionValid = true;\n\t\n\t// re-position and validate all props\n\tfor (size_t j = 0; j < m_Props.size(); ++j)\n\t{\n\t\tconst Prop& prop=m_Props[j];\n\n\t\tCMatrix3D proptransform = prop.m_Point->m_Transform;\n\n\t\tif (prop.m_Point->m_BoneIndex != 0xff)\n\t\t{\n\t\t\tCMatrix3D boneMatrix = m_BoneMatrices[prop.m_Point->m_BoneIndex];\n\t\t\tif (!worldSpaceBoneMatrices)\n\t\t\t\tboneMatrix.Concatenate(GetTransform());\n\t\t\tproptransform.Concatenate(boneMatrix);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// not relative to any bone; just apply world-space transformation (i.e. relative to object-space origin)\n\t\t\tproptransform.Concatenate(m_Transform);\n\t\t}\n\n\t\t// Adjust prop height to terrain level when needed\n\t\tif (prop.m_MaxHeight != 0.f || prop.m_MinHeight != 0.f) \n\t\t{\n\t\t\tCVector3D propTranslation = proptransform.GetTranslation();\n\t\t\tCVector3D objTranslation = m_Transform.GetTranslation();\n\n\t\t\tCmpPtr<ICmpTerrain> cmpTerrain(m_Simulation, SYSTEM_ENTITY);\n\t\t\tif (cmpTerrain)\n\t\t\t{\n\t\t\t\tfloat objTerrain = cmpTerrain->GetExactGroundLevel(objTranslation.X, objTranslation.Z);\n\t\t\t\tfloat propTerrain = cmpTerrain->GetExactGroundLevel(propTranslation.X, propTranslation.Z);\n\t\t\t\tfloat translateHeight = std::min(prop.m_MaxHeight,\n\t\t\t\t                                 std::max(prop.m_MinHeight, propTerrain - objTerrain));\n\t\t\t\tCMatrix3D translate = CMatrix3D();\n\t\t\t\ttranslate.SetTranslation(0.f, translateHeight, 0.f);\n\t\t\t\tproptransform.Concatenate(translate);\n\t\t\t}\n\t\t}\n\n\t\tprop.m_Model->SetTransform(proptransform);\n\t\tprop.m_Model->ValidatePosition();\n\t}\n\n\tif (m_BoneMatrices)\n\t{\n\t\tfor (size_t i = 0; i < m_pModelDef->GetNumBones(); i++)\n\t\t{\n\t\t\tm_BoneMatrices[i] = m_BoneMatrices[i] * m_pModelDef->GetInverseBindBoneMatrices()[i];\n\t\t}\n\n\t\t// Note: there is a special case of joint influence, in which the vertex\n\t\t//\tis influenced by the bind-shape transform instead of a particular bone,\n\t\t//\twhich we indicate with the blending bone ID set to the total number\n\t\t//\tof bones. But since we're skinning in world space, we use the model's\n\t\t//\tworld space transform and store that matrix in this special index.\n\t\t//\t(see http://trac.wildfiregames.com/ticket/1012)\n\t\tm_BoneMatrices[m_pModelDef->GetNumBones()] = m_Transform;\n\n\t\tif (computeBlendMatrices)\n\t\t\tm_pModelDef->BlendBoneMatrices(m_BoneMatrices);\n\t}\n}\n\n\n/////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// SetAnimation: set the given animation as the current animation on this model;\n// return false on error, else true\nbool CModel::SetAnimation(CSkeletonAnim* anim, bool once)\n{\n\tm_Anim = NULL; // in case something fails\n\n\tif (anim)\n\t{\n\t\tm_Flags &= ~MODELFLAG_NOLOOPANIMATION;\n\n\t\tif (once)\n\t\t\tm_Flags |= MODELFLAG_NOLOOPANIMATION;\n\n\t\tif (!m_BoneMatrices && anim->m_AnimDef)\n\t\t{\n\t\t\t// not boned, can't animate\n\t\t\treturn false;\n\t\t}\n\n\t\tif (m_BoneMatrices && !anim->m_AnimDef)\n\t\t{\n\t\t\t// boned, but animation isn't valid\n\t\t\t// (e.g. the default (static) idle animation on an animated unit)\n\t\t\treturn false;\n\t\t}\n\n\t\tif (anim->m_AnimDef && anim->m_AnimDef->GetNumKeys() != m_pModelDef->GetNumBones())\n\t\t{\n\t\t\t// mismatch between model's skeleton and animation's skeleton\n\t\t\tLOGERROR(\"Mismatch between model's skeleton and animation's skeleton (%lu model bones != %lu animation keys)\",\n\t\t\t\t\t(unsigned long)m_pModelDef->GetNumBones(), (unsigned long)anim->m_AnimDef->GetNumKeys());\n\t\t\treturn false;\n\t\t}\n\n\t\t// reset the cached bounds when the animation is changed\n\t\tm_ObjectBounds.SetEmpty();\n\t\tInvalidateBounds();\n\n\t\t// start anim from beginning \n\t\tm_AnimTime = 0;\n\t} \n\n\tm_Anim = anim;\n\n\treturn true;\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// CopyAnimation\nvoid CModel::CopyAnimationFrom(CModel* source)\n{\n\tm_Anim = source->m_Anim;\n\tm_AnimTime = source->m_AnimTime;\n\n\tm_Flags &= ~MODELFLAG_CASTSHADOWS;\n\tif (source->m_Flags & MODELFLAG_CASTSHADOWS)\n\t\tm_Flags |= MODELFLAG_CASTSHADOWS;\n\n\tm_ObjectBounds.SetEmpty();\n\tInvalidateBounds();\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// AddProp: add a prop to the model on the given point\nvoid CModel::AddProp(const SPropPoint* point, CModelAbstract* model, CObjectEntry* objectentry, float minHeight, float maxHeight, bool selectable)\n{\n\t// position model according to prop point position\n\n\t// this next call will invalidate the bounds of \"model\", which will in turn also invalidate the selection box\n\tmodel->SetTransform(point->m_Transform);\n\tmodel->m_Parent = this;\n\n\tProp prop;\n\tprop.m_Point = point;\n\tprop.m_Model = model;\n\tprop.m_ObjectEntry = objectentry;\n\tprop.m_MinHeight = minHeight;\n\tprop.m_MaxHeight = maxHeight;\n\tprop.m_Selectable = selectable;\n\tm_Props.push_back(prop);\n}\n\nvoid CModel::AddAmmoProp(const SPropPoint* point, CModelAbstract* model, CObjectEntry* objectentry)\n{\n\tAddProp(point, model, objectentry);\n\tm_AmmoPropPoint = point;\n\tm_AmmoLoadedProp = m_Props.size() - 1;\n\tm_Props[m_AmmoLoadedProp].m_Hidden = true;\n\n\t// we only need to invalidate the selection box here if it is based on props and their visibilities\n\tif (!m_CustomSelectionShape)\n\t\tm_SelectionBoxValid = false;\n}\n\nvoid CModel::ShowAmmoProp()\n{\n\tif (m_AmmoPropPoint == NULL)\n\t\treturn;\n\n\t// Show the ammo prop, hide all others on the same prop point\n\tfor (size_t i = 0; i < m_Props.size(); ++i)\n\t\tif (m_Props[i].m_Point == m_AmmoPropPoint)\n\t\t\tm_Props[i].m_Hidden = (i != m_AmmoLoadedProp);\n\t\n\t//  we only need to invalidate the selection box here if it is based on props and their visibilities\n\tif (!m_CustomSelectionShape)\n\t\tm_SelectionBoxValid = false;\n}\n\nvoid CModel::HideAmmoProp()\n{\n\tif (m_AmmoPropPoint == NULL)\n\t\treturn;\n\n\t// Hide the ammo prop, show all others on the same prop point\n\tfor (size_t i = 0; i < m_Props.size(); ++i)\n\t\tif (m_Props[i].m_Point == m_AmmoPropPoint)\n\t\t\tm_Props[i].m_Hidden = (i == m_AmmoLoadedProp);\n\n\t//  we only need to invalidate here if the selection box is based on props and their visibilities\n\tif (!m_CustomSelectionShape)\n\t\tm_SelectionBoxValid = false;\n}\n\nCModelAbstract* CModel::FindFirstAmmoProp()\n{\n\tif (m_AmmoPropPoint)\n\t\treturn m_Props[m_AmmoLoadedProp].m_Model;\n\n\tfor (size_t i = 0; i < m_Props.size(); ++i)\n\t{\n\t\tCModel* propModel = m_Props[i].m_Model->ToCModel();\n\t\tif (propModel)\n\t\t{\n\t\t\tCModelAbstract* model = propModel->FindFirstAmmoProp();\n\t\t\tif (model)\n\t\t\t\treturn model;\n\t\t}\n\t}\n\n\treturn NULL;\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Clone: return a clone of this model\nCModelAbstract* CModel::Clone() const\n{\n\tCModel* clone = new CModel(m_SkeletonAnimManager, m_Simulation);\n\tclone->m_ObjectBounds = m_ObjectBounds;\n\tclone->InitModel(m_pModelDef);\n\tclone->SetMaterial(m_Material);\n\tclone->SetAnimation(m_Anim);\n\tclone->SetFlags(m_Flags);\n\n\tfor (size_t i = 0; i < m_Props.size(); i++)\n\t{\n\t\t// eek!  TODO, RC - need to investigate shallow clone here\n\t\tif (m_AmmoPropPoint && i == m_AmmoLoadedProp)\n\t\t\tclone->AddAmmoProp(m_Props[i].m_Point, m_Props[i].m_Model->Clone(), m_Props[i].m_ObjectEntry);\n\t\telse\n\t\t\tclone->AddProp(m_Props[i].m_Point, m_Props[i].m_Model->Clone(), m_Props[i].m_ObjectEntry, m_Props[i].m_MinHeight, m_Props[i].m_MaxHeight, m_Props[i].m_Selectable);\n\t}\n\n\treturn clone;\n}\n\n\n/////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// SetTransform: set the transform on this object, and reorientate props accordingly\nvoid CModel::SetTransform(const CMatrix3D& transform)\n{\n\t// call base class to set transform on this object\n\tCRenderableObject::SetTransform(transform);\n\tInvalidatePosition();\n}\n\n//////////////////////////////////////////////////////////////////////////\n\nvoid CModel::AddFlagsRec(int flags)\n{\n\tm_Flags |= flags;\n\n\tif (flags & MODELFLAG_IGNORE_LOS)\n\t{\n\t\tm_Material.AddShaderDefine(str_IGNORE_LOS, str_1);\n\t\tm_Material.RecomputeCombinedShaderDefines();\n\t}\n\n\tfor (size_t i = 0; i < m_Props.size(); ++i)\n\t\tif (m_Props[i].m_Model->ToCModel())\n\t\t\tm_Props[i].m_Model->ToCModel()->AddFlagsRec(flags);\n}\n\nvoid CModel::RemoveShadowsRec()\n{\n\tm_Flags &= ~MODELFLAG_CASTSHADOWS;\n\n\tm_Material.AddShaderDefine(str_DISABLE_RECEIVE_SHADOWS, str_1);\n\tm_Material.RecomputeCombinedShaderDefines();\n\n\tfor (size_t i = 0; i < m_Props.size(); ++i)\n\t{\n\t\tif (m_Props[i].m_Model->ToCModel())\n\t\t\tm_Props[i].m_Model->ToCModel()->RemoveShadowsRec();\n\t\telse if (m_Props[i].m_Model->ToCModelDecal())\n\t\t\tm_Props[i].m_Model->ToCModelDecal()->RemoveShadows();\n\t}\n}\n\nvoid CModel::SetMaterial(const CMaterial &material)\n{\n\tm_Material = material;\n}\n\nvoid CModel::SetPlayerID(player_id_t id)\n{\n\tCModelAbstract::SetPlayerID(id);\n\n\tfor (std::vector<Prop>::iterator it = m_Props.begin(); it != m_Props.end(); ++it)\n\t\tit->m_Model->SetPlayerID(id);\n}\n\nvoid CModel::SetShadingColor(const CColor& color)\n{\n\tCModelAbstract::SetShadingColor(color);\n\n\tfor (std::vector<Prop>::iterator it = m_Props.begin(); it != m_Props.end(); ++it)\n\t\tit->m_Model->SetShadingColor(color);\n}\n"
  },
  {
    "path": "fpsgame/graphics/Model.h",
    "content": "/* Copyright (C) 2016 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n/*\n * Mesh object with texture and skinning information\n */\n\n#ifndef INCLUDED_MODEL\n#define INCLUDED_MODEL\n\n#include <vector>\n\n#include \"graphics/Texture.h\"\n#include \"graphics/Material.h\"\n#include \"graphics/MeshManager.h\"\n#include \"graphics/ModelAbstract.h\"\n\nstruct SPropPoint;\nclass CObjectEntry;\nclass CSkeletonAnim;\nclass CSkeletonAnimDef;\nclass CSkeletonAnimManager;\nclass CSimulation2;\n\n#define MODELFLAG_CASTSHADOWS\t\t(1<<0)\n#define MODELFLAG_NOLOOPANIMATION\t(1<<1)\n#define MODELFLAG_SILHOUETTE_DISPLAY\t(1<<2)\n#define MODELFLAG_SILHOUETTE_OCCLUDER\t(1<<3)\n#define MODELFLAG_IGNORE_LOS\t\t(1<<4)\n\n///////////////////////////////////////////////////////////////////////////////\n// CModel: basically, a mesh object - holds the texturing and skinning \n// information for a model in game\nclass CModel : public CModelAbstract\n{\n\tNONCOPYABLE(CModel);\n\npublic:\n\tstruct Prop\n\t{\n\t\tProp() : m_MinHeight(0.f), m_MaxHeight(0.f), m_Point(0), m_Model(0), m_ObjectEntry(0), m_Hidden(false), m_Selectable(true) {}\n\n\t\tfloat m_MinHeight;\n\t\tfloat m_MaxHeight;\n\n\t\t/**\n\t\t * Location of the prop point within its parent model, relative to either a bone in the parent model or to the \n\t\t * parent model's origin. See the documentation for @ref SPropPoint for more details.\n\t\t * @see SPropPoint\n\t\t */\n\t\tconst SPropPoint* m_Point;\n\n\t\t/**\n\t\t * Pointer to the model associated with this prop. Note that the transform matrix held by this model is the full object-to-world\n\t\t * space transform, taking into account all parent model positioning (see @ref CModel::ValidatePosition for positioning logic).\n\t\t * @see CModel::ValidatePosition\n\t\t */\n\t\tCModelAbstract* m_Model;\n\t\tCObjectEntry* m_ObjectEntry;\n\n\t\tbool m_Hidden; ///< Should this prop be temporarily removed from rendering?\n\t\tbool m_Selectable; /// < should this prop count in the selection size?\n\t};\n\npublic:\n\t// constructor\n\tCModel(CSkeletonAnimManager& skeletonAnimManager, CSimulation2& simulation);\n\t// destructor\n\t~CModel();\n\n\n\t/// Dynamic cast\n\tvirtual CModel* ToCModel()\n\t{\n\t\treturn this;\n\t}\n\n\t// setup model from given geometry\n\tbool InitModel(const CModelDefPtr& modeldef);\n\t// update this model's state; 'time' is the absolute time since the start of the animation, in MS\n\tvoid UpdateTo(float time);\n\n\t// get the model's geometry data\n\tconst CModelDefPtr& GetModelDef() { return m_pModelDef; }\n\n\t// set the model's material\n\tvoid SetMaterial(const CMaterial &material);\n\t// set the model's player ID, recursively through props\n\tvoid SetPlayerID(player_id_t id);\n\t// set the models mod color\n\tvirtual void SetShadingColor(const CColor& color);\n\t// get the model's material\n\tCMaterial& GetMaterial() { return m_Material; }\n\n\t// set the given animation as the current animation on this model\n\tbool SetAnimation(CSkeletonAnim* anim, bool once = false);\n\n\t// get the currently playing animation, if any\n\tCSkeletonAnim* GetAnimation() const { return m_Anim; }\n\n\t// set the animation state to be the same as from another; both models should\n\t// be compatible types (same type of skeleton)\n\tvoid CopyAnimationFrom(CModel* source);\n\n\t// set object flags\n\tvoid SetFlags(int flags) { m_Flags=flags; }\n\t// get object flags\n\tint GetFlags() const { return m_Flags; }\n\t// add object flags, recursively through props\n\tvoid AddFlagsRec(int flags);\n\t// remove shadow casting and receiving, recursively through props\n\t// TODO: replace with more generic shader define + flags setting\n\tvoid RemoveShadowsRec();\n\n\t// recurse down tree setting dirty bits\n\tvirtual void SetDirtyRec(int dirtyflags) {\n\t\tSetDirty(dirtyflags);\n\t\tfor (size_t i=0;i<m_Props.size();i++) {\n\t\t\tm_Props[i].m_Model->SetDirtyRec(dirtyflags);\n\t\t}\n\t}\n\n\tvirtual void SetTerrainDirty(ssize_t i0, ssize_t j0, ssize_t i1, ssize_t j1)\n\t{\n\t\tfor (size_t i = 0; i < m_Props.size(); ++i)\n\t\t\tm_Props[i].m_Model->SetTerrainDirty(i0, j0, i1, j1);\n\t}\n\n\tvirtual void SetEntityVariable(const std::string& name, float value)\n\t{\n\t\tfor (size_t i = 0; i < m_Props.size(); ++i)\n\t\t\tm_Props[i].m_Model->SetEntityVariable(name, value);\n\t}\n\n\t// --- WORLD/OBJECT SPACE BOUNDS -----------------------------------------------------------------\n\n\t/// Overridden to calculate both the world-space and object-space bounds of this model, and stores the result in\n\t/// m_Bounds and m_ObjectBounds, respectively.\n\tvirtual void CalcBounds();\n\n\t/// Returns the object-space bounds for this model, excluding its children.\n\tconst CBoundingBoxAligned& GetObjectBounds()\n\t{\n\t\tRecalculateBoundsIfNecessary();\t\t\t\t// recalculates both object-space and world-space bounds if necessary\n\t\treturn m_ObjectBounds;\n\t}\n\n\tvirtual const CBoundingBoxAligned GetWorldBoundsRec();\t\t// reimplemented here\n\n\t/// Auxiliary method; calculates object space bounds of this model, based solely on vertex positions, and stores \n\t/// the result in m_ObjectBounds. Called by CalcBounds (instead of CalcAnimatedObjectBounds) if it has been determined \n\t/// that the object-space bounds are static.\n\tvoid CalcStaticObjectBounds();\n\t\n\t/// Auxiliary method; calculate object-space bounds encompassing all vertex positions for given animation, and stores \n\t/// the result in m_ObjectBounds. Called by CalcBounds (instead of CalcStaticBounds) if it has been determined that the \n\t/// object-space bounds need to take animations into account.\n\tvoid CalcAnimatedObjectBounds(CSkeletonAnimDef* anim,CBoundingBoxAligned& result);\n\n\t// --- SELECTION BOX/BOUNDS ----------------------------------------------------------------------\n\n\t/// Reimplemented here since proper models should participate in selection boxes.\n\tvirtual const CBoundingBoxAligned GetObjectSelectionBoundsRec();\n\n\t/**\n\t * Set transform of this object.\n\t *\n\t * @note In order to ensure that all child props are updated properly,\n\t * you must call ValidatePosition().\n\t */\n\tvirtual void SetTransform(const CMatrix3D& transform);\n\n\t/**\n\t * Return whether this is a skinned/skeletal model. If it is, Get*BoneMatrices()\n\t * will return valid non-NULL arrays.\n\t */\n\tbool IsSkinned() { return (m_BoneMatrices != NULL); }\n\n\t// return the models bone matrices; 16-byte aligned for SSE reads\n\tconst CMatrix3D* GetAnimatedBoneMatrices() { \n\t\tENSURE(m_PositionValid);\n\t\treturn m_BoneMatrices;\n\t}\n\n\t/**\n\t * Load raw animation frame animation from given file, and build an\n\t * animation specific to this model.\n\t * @param pathname animation file to load\n\t * @param name animation name (e.g. \"idle\")\n\t * @param frequency influences the random choices \n\t * @param speed animation speed as a factor of the default animation speed\n\t * @param actionpos offset of 'action' event, in range [0, 1]\n\t * @param actionpos2 offset of 'action2' event, in range [0, 1]\n\t * @param sound offset of 'sound' event, in range [0, 1]\n\t * @return new animation, or NULL on error\n\t */\n\tCSkeletonAnim* BuildAnimation(const VfsPath& pathname, const CStr& name, int frequency, float speed, float actionpos, float actionpos2, float soundpos);\n\n\t/**\n\t * Add a prop to the model on the given point.\n\t */\n\tvoid AddProp(const SPropPoint* point, CModelAbstract* model, CObjectEntry* objectentry, float minHeight = 0.f, float maxHeight = 0.f, bool selectable = true);\n\n\t/**\n\t * Add a prop to the model on the given point, and treat it as the ammo prop.\n\t * The prop will be hidden by default.\n\t */\n\tvoid AddAmmoProp(const SPropPoint* point, CModelAbstract* model, CObjectEntry* objectentry);\n\n\t/**\n\t * Show the ammo prop (if any), and hide any other props on that prop point.\n\t */\n\tvoid ShowAmmoProp();\n\n\t/**\n\t * Hide the ammo prop (if any), and show any other props on that prop point.\n\t */\n\tvoid HideAmmoProp();\n\n\t/**\n\t * Find the first prop used for ammo, by this model or its own props.\n\t */\n\tCModelAbstract* FindFirstAmmoProp();\n\n\t// return prop list\n\tstd::vector<Prop>& GetProps() { return m_Props; }\n\tconst std::vector<Prop>& GetProps() const { return m_Props; }\n\n\t// return a clone of this model\n\tvirtual CModelAbstract* Clone() const;\n\n\t/**\n\t * Ensure that both the transformation and the bone\n\t * matrices are correct for this model and all its props.\n\t */\n\tvirtual void ValidatePosition();\n\n\t/**\n\t * Mark this model's position and bone matrices,\n\t * and all props' positions as invalid.\n\t */\n\tvirtual void InvalidatePosition();\n\nprivate:\n\t// delete anything allocated by the model\n\tvoid ReleaseData();\n\t\n\t// Needed for terrain aligned props\n\tCSimulation2& m_Simulation;\n\n\t// object flags\n\tint m_Flags;\n\t// model's material\n\tCMaterial m_Material;\n\t// pointer to the model's raw 3d data\n\tCModelDefPtr m_pModelDef;\n\t// object space bounds of model - accounts for bounds of all possible animations\n\t// that can play on a model. Not always up-to-date - currently CalcBounds()\n\t// updates it when necessary.\n\tCBoundingBoxAligned m_ObjectBounds;\n\t// animation currently playing on this model, if any\n\tCSkeletonAnim* m_Anim;\n\t// time (in MS) into the current animation\n\tfloat m_AnimTime;\n\t\n\t/**\n\t * Current state of all bones on this model; null if associated modeldef isn't skeletal.\n\t * Props may attach to these bones by means of the SPropPoint::m_BoneIndex field; in this case their\n\t * transformation matrix held is relative to the bone transformation (see @ref SPropPoint and \n\t * @ref CModel::ValidatePosition).\n\t * \n\t * @see SPropPoint\n\t */\n\tCMatrix3D* m_BoneMatrices;\n\t// list of current props on model\n\tstd::vector<Prop> m_Props;\n\n\t/**\n\t * The prop point to which the ammo prop is attached, or NULL if none\n\t */\n\tconst SPropPoint* m_AmmoPropPoint;\n\n\t/**\n\t * If m_AmmoPropPoint is not NULL, then the index in m_Props of the ammo prop\n\t */\n\tsize_t m_AmmoLoadedProp;\n\n\t// manager object which can load animations for us\n\tCSkeletonAnimManager& m_SkeletonAnimManager;\n};\n\n#endif\n"
  },
  {
    "path": "fpsgame/graphics/ModelAbstract.cpp",
    "content": "/* Copyright (C) 2011 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"precompiled.h\"\n\n#include \"ModelAbstract.h\"\n\n#include \"ps/CLogger.h\"\n\nconst CBoundingBoxOriented& CModelAbstract::GetSelectionBox()\n{\n\tif (!m_SelectionBoxValid)\n\t{\n\t\tCalcSelectionBox();\n\t\tm_SelectionBoxValid = true;\n\t}\n\treturn m_SelectionBox;\n}\n\nvoid CModelAbstract::CalcSelectionBox()\n{\n\tif (m_CustomSelectionShape)\n\t{\n\t\t// custom shape\n\t\tswitch(m_CustomSelectionShape->m_Type)\n\t\t{\n\t\tcase CustomSelectionShape::BOX:\n\t\t\t{\n\t\t\t\t// create object-space bounds according to the information in the descriptor, and transform them to world-space.\n\t\t\t\t// the box is centered on the X and Z axes, but extends from 0 to its height on the Y axis.\n\t\t\t\tconst float width = m_CustomSelectionShape->m_Size0;\n\t\t\t\tconst float depth = m_CustomSelectionShape->m_Size1;\n\t\t\t\tconst float height = m_CustomSelectionShape->m_Height;\n\n\t\t\t\tCBoundingBoxAligned bounds;\n\t\t\t\tbounds += CVector3D(-width/2.f, 0,     -depth/2.f);\n\t\t\t\tbounds += CVector3D( width/2.f, height, depth/2.f);\n\n\t\t\t\tbounds.Transform(GetTransform(), m_SelectionBox);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase CustomSelectionShape::CYLINDER:\n\t\t\t{\n\t\t\t\t// TODO: unimplemented\n\t\t\t\tm_SelectionBox.SetEmpty();\n\t\t\t\tLOGWARNING(\"[ModelAbstract] TODO: Cylinder selection boxes are not yet implemented. Use BOX or BOUNDS selection shapes instead.\");\n\t\t\t}\n\t\t\tbreak;\n\t\tdefault:\n\t\t\t{\n\t\t\t\tm_SelectionBox.SetEmpty();\n\t\t\t\t//LOGWARNING(\"[ModelAbstract] Unrecognized selection shape type: %ld\", m_CustomSelectionShape->m_Type);\n\t\t\t\tdebug_warn(\"[ModelAbstract] Unrecognized selection shape type\");\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n\telse\n\t{\n\t\t// standard method\n\n\t\t// Get the object-space bounds that should be used to construct this model (and its children)'s selection box\n\t\tCBoundingBoxAligned objBounds = GetObjectSelectionBoundsRec();\n\t\tif (objBounds.IsEmpty())\n\t\t{\n\t\t\tm_SelectionBox.SetEmpty(); // model does not wish to participate in selection\n\t\t\treturn;\n\t\t}\n\n\t\t// Prevent the bounding box from extending through the terrain; clip the lower plane at Y=0 in object space.\n\t\tif (objBounds[1].Y > 0.f) // should always be the case, unless the models are defined really weirdly\n\t\t\tobjBounds[0].Y = std::max(0.f, objBounds[0].Y);\n\n\t\t// transform object-space axis-aligned bounds to world-space arbitrary-aligned box\n\t\tobjBounds.Transform(GetTransform(), m_SelectionBox);\n\t}\n\t\n}\n"
  },
  {
    "path": "fpsgame/graphics/ModelAbstract.h",
    "content": "/* Copyright (C) 2011 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef INCLUDED_MODELABSTRACT\n#define INCLUDED_MODELABSTRACT\n\n#include \"maths/BoundingBoxOriented.h\"\n#include \"graphics/RenderableObject.h\"\n#include \"ps/Shapes.h\"\n#include \"simulation2/helpers/Player.h\"\n\nclass CModel;\nclass CModelDecal;\nclass CModelParticleEmitter;\n\n/**\n * Abstract base class for graphical objects that are used by units,\n * or as props attached to other CModelAbstract objects.\n * This includes meshes, terrain decals, and sprites.\n * These objects exist in a tree hierarchy.\n */\nclass CModelAbstract : public CRenderableObject\n{\n\tNONCOPYABLE(CModelAbstract);\n\npublic:\n\n\t/**\n\t * Describes a custom selection shape to be used for a model's selection box instead of the default \n\t * recursive bounding boxes.\n\t */\n\tstruct CustomSelectionShape\n\t{\n\t\tenum EType {\n\t\t\t/// The selection shape is determined by an oriented box of custom, user-specified size.\n\t\t\tBOX,\n\t\t\t/// The selection shape is determined by a cylinder of custom, user-specified size.\n\t\t\tCYLINDER\n\t\t};\n\n\t\tEType m_Type; ///< Type of shape.\n\t\tfloat m_Size0; ///< Box width if @ref BOX, or radius if @ref CYLINDER\n\t\tfloat m_Size1; ///< Box depth if @ref BOX, or radius if @ref CYLINDER\n\t\tfloat m_Height; ///< Box height if @ref BOX, cylinder height if @ref CYLINDER\n\t};\n\npublic:\n\t\n\tCModelAbstract()\n\t\t: m_Parent(NULL), m_PositionValid(false), m_ShadingColor(1, 1, 1, 1), m_PlayerID(INVALID_PLAYER), \n\t\t  m_SelectionBoxValid(false), m_CustomSelectionShape(NULL)\n\t{ }\n\n\t~CModelAbstract()\n\t{\n\t\tdelete m_CustomSelectionShape; // allocated and set externally by CCmpVisualActor, but our responsibility to clean up\n\t}\n\n\tvirtual CModelAbstract* Clone() const = 0;\n\n\t/// Dynamic cast\n\tvirtual CModel* ToCModel() { return NULL; }\n\n\t/// Dynamic cast\n\tvirtual CModelDecal* ToCModelDecal() { return NULL; }\n\n\t/// Dynamic cast\n\tvirtual CModelParticleEmitter* ToCModelParticleEmitter() { return NULL; }\n\n\t// (This dynamic casting is a bit ugly, but we won't have many subclasses\n\t// and this seems the easiest way to integrate with other code that wants\n\t// type-specific processing)\n\n\t/// Calls SetDirty on this model and all child objects.\n\tvirtual void SetDirtyRec(int dirtyflags) = 0;\n\n\t/// Returns world space bounds of this object and all child objects.\n\tvirtual const CBoundingBoxAligned GetWorldBoundsRec() { return GetWorldBounds(); } // default implementation\n\n\t/**\n\t * Returns the world-space selection box of this model. Used primarily for hittesting against against a selection ray. The \n\t * returned selection box may be empty to indicate that it does not wish to participate in the selection process.\n\t */\n\tvirtual const CBoundingBoxOriented& GetSelectionBox();\n\n\tvirtual void InvalidateBounds()\n\t{\n\t\tm_BoundsValid = false;\n\t\t// a call to this method usually means that the model's transform has changed, i.e. it has moved or rotated, so we'll also\n\t\t// want to update the selection box accordingly regardless of the shape it is built from.\n\t\tm_SelectionBoxValid = false;\n\t}\n\n\t/// Sets a custom selection shape as described by a @p descriptor. Argument may be NULL \n\t/// if you wish to keep the default behaviour of using the recursively-calculated bounding boxes.\n\tvoid SetCustomSelectionShape(CustomSelectionShape* descriptor)\n\t{\n\t\tif (m_CustomSelectionShape != descriptor)\n\t\t{\n\t\t\tm_CustomSelectionShape = descriptor;\n\t\t\tm_SelectionBoxValid = false; // update the selection box when it is next requested\n\t\t}\n\t}\n\n\t/**\n\t * Returns the (object-space) bounds that should be used to construct a selection box for this model and its children.\n\t * May return an empty bound to indicate that this model and its children should not be selectable themselves, or should\n\t * not be included in its parent model's selection box. This method is used for constructing the default selection boxes,\n\t * as opposed to any boxes of custom shape specified by @ref m_CustomSelectionShape.\n\t * \n\t * If you wish your model type to be included in selection boxes, override this method and have it return the object-space\n\t * bounds of itself, augmented recursively (via this method) with the object-space selection bounds of its children.\n\t */\n\tvirtual const CBoundingBoxAligned GetObjectSelectionBoundsRec() { return CBoundingBoxAligned::EMPTY; }\n\n\t/**\n\t * Called when terrain has changed in the given inclusive bounds.\n\t * Might call SetDirty if the change affects this model.\n\t */\n\tvirtual void SetTerrainDirty(ssize_t i0, ssize_t j0, ssize_t i1, ssize_t j1) = 0;\n\n\t/**\n\t * Called when the entity tries to set some variable to affect the display of this model\n\t * and/or its child objects.\n\t */\n\tvirtual void SetEntityVariable(const std::string& UNUSED(name), float UNUSED(value)) { }\n\n\t/**\n\t * Ensure that both the transformation and the bone matrices are correct for this model and all its props.\n\t */\n\tvirtual void ValidatePosition() = 0;\n\n\t/**\n\t * Mark this model's position and bone matrices, and all props' positions as invalid.\n\t */\n\tvirtual void InvalidatePosition() = 0;\n\n\tvirtual void SetPlayerID(player_id_t id) { m_PlayerID = id; }\n\n\t// get the model's player ID; initial default is INVALID_PLAYER\n\tvirtual player_id_t GetPlayerID() const { return m_PlayerID; }\n\n\tvirtual void SetShadingColor(const CColor& color) { m_ShadingColor = color; }\n\tvirtual CColor GetShadingColor() const { return m_ShadingColor; }\n\nprotected:\n\tvoid CalcSelectionBox();\n\npublic:\n\t/// If non-null, points to the model that we are attached to.\n\tCModelAbstract* m_Parent;\n\n\t/// True if both transform and and bone matrices are valid.\n\tbool m_PositionValid;\n\n\tplayer_id_t m_PlayerID;\n\n\t/// Modulating color\n\tCColor m_ShadingColor;\n\nprotected:\n\n\t/// Selection box for this model.\n\tCBoundingBoxOriented m_SelectionBox;\n\n\t/// Is the current selection box valid?\n\tbool m_SelectionBoxValid;\n\n\t/// Pointer to a descriptor for a custom-defined selection box shape. If no custom selection box is required, this is NULL \n\t/// and the standard recursive-bounding-box-based selection box is used. Otherwise, a custom selection box described by this \n\t/// field will be used.\n\t/// @see SetCustomSelectionShape\n\tCustomSelectionShape* m_CustomSelectionShape;\n\n};\n\n#endif // INCLUDED_MODELABSTRACT\n"
  },
  {
    "path": "fpsgame/graphics/ModelDef.cpp",
    "content": "/* Copyright (C) 2015 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n/*\n * Defines a raw 3d model.\n */\n\n#include \"precompiled.h\"\n\n#include \"ModelDef.h\"\n#include \"graphics/SkeletonAnimDef.h\"\n#include \"ps/FileIo.h\"\n#include \"maths/Vector4D.h\"\n\n#if HAVE_SSE\n# include <xmmintrin.h>\n#endif\n\nCVector3D CModelDef::SkinPoint(const SModelVertex& vtx,\n\t\t\t\t\t\t\t   const CMatrix3D newPoseMatrices[])\n{\n\tCVector3D result (0, 0, 0);\n\n\tfor (int i = 0; i < SVertexBlend::SIZE && vtx.m_Blend.m_Bone[i] != 0xff; ++i)\n\t{\n\t\tresult += newPoseMatrices[vtx.m_Blend.m_Bone[i]].Transform(vtx.m_Coords) * vtx.m_Blend.m_Weight[i];\n\t}\n\n\treturn result;\n}\n\nCVector3D CModelDef::SkinNormal(const SModelVertex& vtx,\n\t\t\t\t\t\t\t\tconst CMatrix3D newPoseMatrices[])\n{\n\t// To be correct, the normal vectors apparently need to be multiplied by the\n\t// inverse of the transpose. Unfortunately inverses are slow.\n\t// If a matrix is orthogonal, M * M^T = I and so the inverse of the transpose\n\t// is the original matrix. But that's not entirely relevant here, because\n\t// the bone matrices include translation components and so they're not\n\t// orthogonal.\n\t// But that's okay because we have\n\t//   M = T * R\n\t// and want to find\n\t//   n' = (M^T^-1) * n\n\t//      = (T * R)^T^-1 * n\n\t//      = (R^T * T^T)^-1 * n\n\t//      = (T^T^-1 * R^T^-1) * n\n\t// R is indeed orthogonal so R^T^-1 = R. T isn't orthogonal at all.\n\t// But n is only a 3-vector, and from the forms of T and R (which have\n\t// lots of zeroes) I can convince myself that replacing T with T^T^-1 has no\n\t// effect on anything but the fourth component of M^T^-1 - and the fourth\n\t// component is discarded since it has no effect on n', and so we can happily\n\t// use n' = M*n.\n\t//\n\t// (This isn't very good as a proof, but it's better than assuming M is\n\t// orthogonal when it's clearly not.)\n\n\tCVector3D result (0, 0, 0);\n\n\tfor (int i = 0; i < SVertexBlend::SIZE && vtx.m_Blend.m_Bone[i] != 0xff; ++i)\n\t{\n\t\tresult += newPoseMatrices[vtx.m_Blend.m_Bone[i]].Rotate(vtx.m_Norm) * vtx.m_Blend.m_Weight[i];\n\t}\n\t\n\t// If there was more than one influence, the result is probably not going\n\t// to be of unit length (since it's a weighted sum of several independent\n\t// unit vectors), so we need to normalise it.\n\t// (It's fairly common to only have one influence, so it seems sensible to\n\t// optimise that case a bit.)\n\tif (vtx.m_Blend.m_Bone[1] != 0xff) // if more than one influence\n\t\tresult.Normalize();\n\n\treturn result;\n}\n\nvoid CModelDef::SkinPointsAndNormals(\n\t\tsize_t numVertices,\n\t\tconst VertexArrayIterator<CVector3D>& Position,\n\t\tconst VertexArrayIterator<CVector3D>& Normal,\n\t\tconst SModelVertex* vertices,\n\t\tconst size_t* blendIndices,\n\t\tconst CMatrix3D newPoseMatrices[])\n{\n\t// To avoid some performance overhead, get the raw vertex array pointers\n\tchar* PositionData = Position.GetData();\n\tsize_t PositionStride = Position.GetStride();\n\tchar* NormalData = Normal.GetData();\n\tsize_t NormalStride = Normal.GetStride();\n\n\tfor (size_t j = 0; j < numVertices; ++j)\n\t{\n\t\tconst SModelVertex& vtx = vertices[j];\n\n\t\tCVector3D pos = newPoseMatrices[blendIndices[j]].Transform(vtx.m_Coords);\n\t\tCVector3D norm = newPoseMatrices[blendIndices[j]].Rotate(vtx.m_Norm);\n\n\t\t// If there was more than one influence, the result is probably not going\n\t\t// to be of unit length (since it's a weighted sum of several independent\n\t\t// unit vectors), so we need to normalise it.\n\t\t// (It's fairly common to only have one influence, so it seems sensible to\n\t\t// optimise that case a bit.)\n\t\tif (vtx.m_Blend.m_Bone[1] != 0xff) // if more than one influence\n\t\t\tnorm.Normalize();\n\n\t\tmemcpy(PositionData + PositionStride*j, &pos.X, 3*sizeof(float));\n\t\tmemcpy(NormalData + NormalStride*j, &norm.X, 3*sizeof(float));\n\t}\n}\n\n#if HAVE_SSE\nvoid CModelDef::SkinPointsAndNormals_SSE(\n\t\tsize_t numVertices,\n\t\tconst VertexArrayIterator<CVector3D>& Position,\n\t\tconst VertexArrayIterator<CVector3D>& Normal,\n\t\tconst SModelVertex* vertices,\n\t\tconst size_t* blendIndices,\n\t\tconst CMatrix3D newPoseMatrices[])\n{\n\t// To avoid some performance overhead, get the raw vertex array pointers\n\tchar* PositionData = Position.GetData();\n\tsize_t PositionStride = Position.GetStride();\n\tchar* NormalData = Normal.GetData();\n\tsize_t NormalStride = Normal.GetStride();\n\n\t// Must be aligned correctly for SSE\n\tASSERT((intptr_t)newPoseMatrices % 16 == 0);\n\tASSERT((intptr_t)PositionData % 16 == 0);\n\tASSERT((intptr_t)PositionStride % 16 == 0);\n\tASSERT((intptr_t)NormalData % 16 == 0);\n\tASSERT((intptr_t)NormalStride % 16 == 0);\n\n\t__m128 col0, col1, col2, col3, vec0, vec1, vec2;\n\n\tfor (size_t j = 0; j < numVertices; ++j)\n\t{\n\t\tconst SModelVertex& vtx = vertices[j];\n\t\tconst CMatrix3D& mtx = newPoseMatrices[blendIndices[j]];\n\n\t\t// Loads matrix to xmm registers.\n\t\tcol0 = _mm_load_ps(mtx._data);\n\t\tcol1 = _mm_load_ps(mtx._data + 4);\n\t\tcol2 = _mm_load_ps(mtx._data + 8);\n\t\tcol3 = _mm_load_ps(mtx._data + 12);\n\t\t\n\t\t// Loads and computes vertex coordinates.\n\t\tvec0 = _mm_load1_ps(&vtx.m_Coords.X);\t// v0 = [x, x, x, x]\n\t\tvec0 = _mm_mul_ps(col0, vec0);\t\t\t// v0 = [_11*x, _21*x, _31*x, _41*x]\n\t\tvec1 = _mm_load1_ps(&vtx.m_Coords.Y);\t// v1 = [y, y, y, y]\n\t\tvec1 = _mm_mul_ps(col1, vec1);\t\t\t// v1 = [_12*y, _22*y, _32*y, _42*y]\n\t\tvec0 = _mm_add_ps(vec0, vec1);\t\t\t// v0 = [_11*x + _12*y, ...]\n\t\tvec1 = _mm_load1_ps(&vtx.m_Coords.Z);\t// v1 = [z, z, z, z]\n\t\tvec1 = _mm_mul_ps(col2, vec1);\t\t\t// v1 = [_13*z, _23*z, _33*z, _43*z]\n\t\tvec1 = _mm_add_ps(vec1, col3);\t\t\t// v1 = [_13*z + _14, ...]\n\t\tvec0 = _mm_add_ps(vec0, vec1);\t\t\t// v0 = [_11*x + _12*y + _13*z + _14, ...]\n\t\t_mm_store_ps((float*)(PositionData + PositionStride*j), vec0);\n\n\t\t// Loads and computes normal vectors.\n\t\tvec0 = _mm_load1_ps(&vtx.m_Norm.X);\t\t// v0 = [x, x, x, x]\n\t\tvec0 = _mm_mul_ps(col0, vec0);\t\t\t// v0 = [_11*x, _21*x, _31*x, _41*x]\n\t\tvec1 = _mm_load1_ps(&vtx.m_Norm.Y);\t\t// v1 = [y, y, y, y]\n\t\tvec1 = _mm_mul_ps(col1, vec1);\t\t\t// v1 = [_12*y, _22*y, _32*y, _42*y]\n\t\tvec0 = _mm_add_ps(vec0, vec1);\t\t\t// v0 = [_11*x + _12*y, ...]\n\t\tvec1 = _mm_load1_ps(&vtx.m_Norm.Z);\t\t// v1 = [z, z, z, z]\n\t\tvec1 = _mm_mul_ps(col2, vec1);\t\t\t// v1 = [_13*z, _23*z, _33*z, _43*z]\n\t\tvec0 = _mm_add_ps(vec0, vec1);\t\t\t// v0 = [_11*x + _12*y + _13*z, ...]\n\n\t\t// If there was more than one influence, the result is probably not going\n\t\t// to be of unit length (since it's a weighted sum of several independent\n\t\t// unit vectors), so we need to normalise it.\n\t\t// (It's fairly common to only have one influence, so it seems sensible to\n\t\t// optimise that case a bit.)\n\t\tif (vtx.m_Blend.m_Bone[1] != 0xff) // if more than one influence\n\t\t{\n\t\t\t// Normalization.\n\t\t\t// vec1 = [x*x, y*y, z*z, ?*?]\n\t\t\tvec1 = _mm_mul_ps(vec0, vec0);\n\t\t\t// vec2 = [y*y, z*z, x*x, ?*?]\n\t\t\tvec2 = _mm_shuffle_ps(vec1, vec1, _MM_SHUFFLE(3, 0, 2, 1));\n\t\t\tvec1 = _mm_add_ps(vec1, vec2);\n\t\t\t// vec2 = [z*z, x*x, y*y, ?*?]\n\t\t\tvec2 = _mm_shuffle_ps(vec2, vec2, _MM_SHUFFLE(3, 0, 2, 1));\n\t\t\tvec1 = _mm_add_ps(vec1, vec2);\n\t\t\t// rsqrt(a) = 1 / sqrt(a)\n\t\t\tvec1 = _mm_rsqrt_ps(vec1);\n\t\t\tvec0 = _mm_mul_ps(vec0, vec1);\n\t\t}\n\t\t_mm_store_ps((float*)(NormalData + NormalStride*j), vec0);\n\t}\n}\n#endif\n\nvoid CModelDef::BlendBoneMatrices(\n\t\tCMatrix3D boneMatrices[])\n{\n\tfor (size_t i = 0; i < m_NumBlends; ++i)\n\t{\n\t\tconst SVertexBlend& blend = m_pBlends[i];\n\t\tCMatrix3D& boneMatrix = boneMatrices[m_NumBones + 1 + i];\n\t\t\n\t\t// Note: there is a special case of joint influence, in which the vertex\n\t\t//\tis influenced by the bind-shape matrix instead of a particular bone,\n\t\t//\twhich we indicate by setting the bone ID to the total number of bones.\n\t\t//\tIt should be blended with the world space transform and we have already\n\t\t//\tset up this matrix in boneMatrices.\n\t\t//\t(see http://trac.wildfiregames.com/ticket/1012)\n\n\t\tboneMatrix.Blend(boneMatrices[blend.m_Bone[0]], blend.m_Weight[0]);\n\t\tfor (size_t j = 1; j < SVertexBlend::SIZE && blend.m_Bone[j] != 0xFF; ++j)\n\t\t\tboneMatrix.AddBlend(boneMatrices[blend.m_Bone[j]], blend.m_Weight[j]);\n\t}\n}\n\n// CModelDef Constructor\nCModelDef::CModelDef() :\n\tm_NumVertices(0), m_NumUVsPerVertex(0), m_pVertices(0), m_NumFaces(0), m_pFaces(0),\n\tm_NumBones(0), m_Bones(0), m_InverseBindBoneMatrices(NULL),\n\tm_NumBlends(0), m_pBlends(0), m_pBlendIndices(0),\n\tm_Name(L\"[not loaded]\")\n{\n}\n\n// CModelDef Destructor\nCModelDef::~CModelDef()\n{\n\tfor(RenderDataMap::iterator it = m_RenderData.begin(); it != m_RenderData.end(); ++it)\n\t\tdelete it->second;\n\tdelete[] m_pVertices;\n\tdelete[] m_pFaces;\n\tdelete[] m_Bones;\n\tdelete[] m_InverseBindBoneMatrices;\n\tdelete[] m_pBlends;\n\tdelete[] m_pBlendIndices;\n}\n\n// FindPropPoint: find and return pointer to prop point matching given name; \n// return null if no match (case insensitive search)\nconst SPropPoint* CModelDef::FindPropPoint(const char* name) const\n{\n\tfor (size_t i = 0; i < m_PropPoints.size(); ++i)\n\t\tif (m_PropPoints[i].m_Name == name)\n\t\t\treturn &m_PropPoints[i];\n\n\treturn 0;\n}\n\n// Load: read and return a new CModelDef initialised with data from given file\nCModelDef* CModelDef::Load(const VfsPath& filename, const VfsPath& name)\n{\n\tCFileUnpacker unpacker;\n\n\t// read everything in from file\n\tunpacker.Read(filename,\"PSMD\");\n\t\t\t\n\t// check version\n\tif (unpacker.GetVersion()<FILE_READ_VERSION) {\n\t\tthrow PSERROR_File_InvalidVersion();\n\t}\n\n\tstd::unique_ptr<CModelDef> mdef (new CModelDef());\n\tmdef->m_Name = name;\n\n\t// now unpack everything \n\tmdef->m_NumVertices = unpacker.UnpackSize();\n\t\n\t// versions prior to 4 only support 1 UV set, 4 and later store it here\n\tif (unpacker.GetVersion() <= 3) \n\t{\n\t\tmdef->m_NumUVsPerVertex = 1;\n\t}\n\telse\n\t{\n\t\tmdef->m_NumUVsPerVertex = unpacker.UnpackSize();\n\t}\n\n\tmdef->m_pVertices=new SModelVertex[mdef->m_NumVertices];\n\t\n\tfor (size_t i = 0; i < mdef->m_NumVertices; ++i)\n\t{\n\t\tunpacker.UnpackRaw(&mdef->m_pVertices[i].m_Coords, 12);\n\t\tunpacker.UnpackRaw(&mdef->m_pVertices[i].m_Norm, 12);\n\t\t\n\t\tfor (size_t s = 0; s < mdef->m_NumUVsPerVertex; ++s)\n\t\t{\n\t\t\tfloat uv[2];\n\t\t\tunpacker.UnpackRaw(&uv[0], 8);\n\t\t\tmdef->m_pVertices[i].m_UVs.push_back(uv[0]);\n\t\t\tmdef->m_pVertices[i].m_UVs.push_back(uv[1]);\n\t\t}\n\t\t\n\t\tunpacker.UnpackRaw(&mdef->m_pVertices[i].m_Blend, sizeof(SVertexBlend));\n\t}\n\t\n\tmdef->m_NumFaces = unpacker.UnpackSize();\n\tmdef->m_pFaces=new SModelFace[mdef->m_NumFaces];\n\tunpacker.UnpackRaw(mdef->m_pFaces,sizeof(SModelFace)*mdef->m_NumFaces);\n\t\n\tmdef->m_NumBones = unpacker.UnpackSize();\n\tif (mdef->m_NumBones)\n\t{\n\t\tmdef->m_Bones=new CBoneState[mdef->m_NumBones];\n\t\tunpacker.UnpackRaw(mdef->m_Bones,mdef->m_NumBones*sizeof(CBoneState));\n\n\t\tmdef->m_pBlendIndices = new size_t[mdef->m_NumVertices];\n\t\tstd::vector<SVertexBlend> blends;\n\t\tfor (size_t i = 0; i < mdef->m_NumVertices; i++)\n\t\t{\n\t\t\tconst SVertexBlend &blend = mdef->m_pVertices[i].m_Blend;\n\t\t\tif (blend.m_Bone[1] == 0xFF)\n\t\t\t{\n\t\t\t\tmdef->m_pBlendIndices[i] = blend.m_Bone[0];\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// If there's already a vertex using the same blend as this, then\n\t\t\t\t// reuse its entry from blends; otherwise add the new one to blends\n\t\t\t\tsize_t j;\n\t\t\t\tfor (j = 0; j < blends.size(); j++)\n\t\t\t\t{\n\t\t\t\t\tif (blend == blends[j]) break;\n\t\t\t\t}\n\t\t\t\tif (j >= blends.size())\n\t\t\t\t\tblends.push_back(blend);\n\t\t\t\t// This index is offset by one to allow the special case of a\n\t\t\t\t//\tweighted influence relative to the bind-shape rather than\n\t\t\t\t//\ta particular bone. See comment in BlendBoneMatrices.\n\t\t\t\tmdef->m_pBlendIndices[i] = mdef->m_NumBones + 1 + j;\n\t\t\t}\n\t\t}\n\n\t\tmdef->m_NumBlends = blends.size();\n\t\tmdef->m_pBlends = new SVertexBlend[mdef->m_NumBlends];\n\t\tstd::copy(blends.begin(), blends.end(), mdef->m_pBlends);\n\t}\n\n\tif (unpacker.GetVersion() >= 2)\n\t{\n\t\t// versions >=2 also have prop point data\n\t\tsize_t numPropPoints = unpacker.UnpackSize();\n\t\tmdef->m_PropPoints.resize(numPropPoints);\n\t\tif (numPropPoints)\n\t\t{\n\t\t\tfor (size_t i = 0; i < numPropPoints; i++)\n\t\t\t{\n\t\t\t\tunpacker.UnpackString(mdef->m_PropPoints[i].m_Name);\n\t\t\t\tunpacker.UnpackRaw(&mdef->m_PropPoints[i].m_Position.X, sizeof(mdef->m_PropPoints[i].m_Position));\n\t\t\t\tunpacker.UnpackRaw(&mdef->m_PropPoints[i].m_Rotation.m_V.X, sizeof(mdef->m_PropPoints[i].m_Rotation));\n\t\t\t\tunpacker.UnpackRaw(&mdef->m_PropPoints[i].m_BoneIndex, sizeof(mdef->m_PropPoints[i].m_BoneIndex));\n\n\t\t\t\t// build prop point transform\n\t\t\t\tmdef->m_PropPoints[i].m_Transform.SetIdentity();\n\t\t\t\tmdef->m_PropPoints[i].m_Transform.Rotate(mdef->m_PropPoints[i].m_Rotation);\n\t\t\t\tmdef->m_PropPoints[i].m_Transform.Translate(mdef->m_PropPoints[i].m_Position);\n\t\t\t}\n\t\t}\n\t}\n\n\tif (unpacker.GetVersion() <= 2)\n\t{\n\t\t// Versions <=2 don't include the default 'root' prop point, so add it here\n\n\t\tSPropPoint prop;\n\t\tprop.m_Name = \"root\";\n\t\tprop.m_Transform.SetIdentity();\n\t\tprop.m_BoneIndex = 0xFF;\n\n\t\tmdef->m_PropPoints.push_back(prop);\n\t}\n\n\tif (unpacker.GetVersion() <= 2)\n\t{\n\t\t// Versions <=2 store the vertexes relative to the bind pose. That\n\t\t// isn't useful when you want to do correct skinning, so later versions\n\t\t// store them in world space. So, fix the old models by skinning each\n\t\t// vertex:\n\n\t\tif (mdef->m_NumBones) // only do skinned models\n\t\t{\n\t\t\tstd::vector<CMatrix3D> bindPose (mdef->m_NumBones);\n\n\t\t\tfor (size_t i = 0; i < mdef->m_NumBones; ++i)\n\t\t\t{\n\t\t\t\tbindPose[i].SetIdentity();\n\t\t\t\tbindPose[i].Rotate(mdef->m_Bones[i].m_Rotation);\n\t\t\t\tbindPose[i].Translate(mdef->m_Bones[i].m_Translation);\n\t\t\t}\n\n\t\t\tfor (size_t i = 0; i < mdef->m_NumVertices; ++i)\n\t\t\t{\n\t\t\t\tmdef->m_pVertices[i].m_Coords = SkinPoint(mdef->m_pVertices[i], &bindPose[0]);\n\t\t\t\tmdef->m_pVertices[i].m_Norm = SkinNormal(mdef->m_pVertices[i], &bindPose[0]);\n\t\t\t}\n\t\t}\n\t}\n\n\t// Compute the inverse bind-pose matrices, needed by the skinning code\n\tmdef->m_InverseBindBoneMatrices = new CMatrix3D[mdef->m_NumBones];\n\tCBoneState* defpose = mdef->GetBones();\n\tfor (size_t i = 0; i < mdef->m_NumBones; ++i)\n\t{\n\t\tmdef->m_InverseBindBoneMatrices[i].SetIdentity();\n\t\tmdef->m_InverseBindBoneMatrices[i].Translate(-defpose[i].m_Translation);\n\t\tmdef->m_InverseBindBoneMatrices[i].Rotate(defpose[i].m_Rotation.GetInverse());\n\t}\n\n\treturn mdef.release();\n}\n\n// Save: write the given CModelDef to the given file\nvoid CModelDef::Save(const VfsPath& filename, const CModelDef* mdef)\n{\n\tCFilePacker packer(FILE_VERSION, \"PSMD\");\n\n\t// pack everything up\n\tconst size_t numVertices = mdef->GetNumVertices();\n\tpacker.PackSize(numVertices);\n\tpacker.PackRaw(mdef->GetVertices(), sizeof(SModelVertex) * numVertices);\n\n\tconst size_t numFaces = mdef->GetNumFaces();\n\tpacker.PackSize(numFaces);\n\tpacker.PackRaw(mdef->GetFaces(), sizeof(SModelFace) * numFaces);\n\t\n\tconst size_t numBones = mdef->m_NumBones;\n\tpacker.PackSize(numBones);\n\tif (numBones)\n\t\tpacker.PackRaw(mdef->m_Bones, sizeof(CBoneState) * numBones);\n\n\tconst size_t numPropPoints = mdef->m_PropPoints.size();\n\tpacker.PackSize(numPropPoints);\n\tfor (size_t i = 0; i < numPropPoints; i++)\n\t{\n\t\tpacker.PackString(mdef->m_PropPoints[i].m_Name);\n\t\tpacker.PackRaw(&mdef->m_PropPoints[i].m_Position.X, sizeof(mdef->m_PropPoints[i].m_Position));\n\t\tpacker.PackRaw(&mdef->m_PropPoints[i].m_Rotation.m_V.X, sizeof(mdef->m_PropPoints[i].m_Rotation));\n\t\tpacker.PackRaw(&mdef->m_PropPoints[i].m_BoneIndex, sizeof(mdef->m_PropPoints[i].m_BoneIndex));\n\t}\n\t\n\t// flush everything out to file\n\tpacker.Write(filename);\n}\n\n// SetRenderData: Set the render data object for the given key,\nvoid CModelDef::SetRenderData(const void* key, CModelDefRPrivate* data)\n{\n\tdelete m_RenderData[key];\n\tm_RenderData[key] = data;\n}\n\n// GetRenderData: Get the render data object for the given key,\n// or 0 if no such object exists.\n// Reference count of the render data object is automatically increased.\nCModelDefRPrivate* CModelDef::GetRenderData(const void* key) const\n{\n\tRenderDataMap::const_iterator it = m_RenderData.find(key);\n\t\n\tif (it != m_RenderData.end())\n\t\treturn it->second;\n\t\n\treturn 0;\n}\n"
  },
  {
    "path": "fpsgame/graphics/ModelDef.h",
    "content": "/* Copyright (C) 2011 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n/*\n * Defines a raw 3d model.\n */\n\n#ifndef INCLUDED_MODELDEF\n#define INCLUDED_MODELDEF\n\n#include \"ps/CStr.h\"\n#include \"maths/Vector2D.h\"\n#include \"maths/Vector3D.h\"\n#include \"maths/Quaternion.h\"\n#include \"lib/file/vfs/vfs_path.h\"\n#include \"renderer/VertexArray.h\"\n#include <map>\n#include <cstring>\n\nclass CBoneState;\n\n/**\n * Describes the position of a prop point within its parent model. A prop point is the location within a parent model\n * where the prop's origin will be attached.\n * \n * A prop point is specified by its transformation matrix (or separately by its position and rotation), which\n * can be relative to either the parent model's origin, or one of the parent's bones. If the parent model is boned, \n * then the @ref m_BoneIndex field may specify a bone to which the transformation matrix is relative (see \n * @ref CModel::m_BoneMatrices). Otherwise, the transformation matrix is assumed to be relative to the parent model's \n * origin.\n * \n * @see CModel::m_BoneMatrices\n */\nstruct SPropPoint\n{\n\t/// Name of the prop point\n\tCStr m_Name;\n\n\t/**\n\t * Position of the point within the parent model, relative to either the parent model's origin or one of the parent \n\t * model's bones if applicable. Also specified as part of @ref m_Transform.\n\t * @see m_Transform\n\t */\n\tCVector3D m_Position;\n\n\t/**\n\t * Rotation of the prop model that will be attached at this point. Also specified as part of @ref m_Transform.\n\t * @see m_Transform\n\t */\n\tCQuaternion m_Rotation;\n\n\t/**\n\t * Object to parent space transformation. Combines both @ref m_Position and @ref m_Rotation in a single\n\t * transformation matrix. This transformation is relative to either the parent model's origin, or one of its\n\t * bones, depending on whether it is skeletal. If relative to a bone, then the bone in the parent model to\n\t * which this transformation is relative may be found by m_BoneIndex.\n\t * @see m_Position, m_Rotation\n\t */\n\tCMatrix3D m_Transform;\n\n\t/**\n\t * Index of parent bone to which this prop point is relative, if any. The value 0xFF specifies that either the parent\n\t * model is unboned, or that this prop point is relative to the parent model's origin rather than one if its bones.\n\t */\n\tu8 m_BoneIndex;\n};\n\n///////////////////////////////////////////////////////////////////////////////\n// SVertexBlend: structure containing the necessary data for blending vertices \n// with multiple bones \nstruct SVertexBlend\n{\n\tenum { SIZE = 4 };\n\t// index of the influencing bone, or 0xff if none\n\tu8 m_Bone[SIZE];\n\t// weight of the influence; all weights sum to 1\n\tfloat m_Weight[SIZE];\n\n\tbool operator==(const SVertexBlend& o) const\n\t{\n\t\treturn !memcmp(m_Bone, o.m_Bone, sizeof(m_Bone)) && !memcmp(m_Weight, o.m_Weight, sizeof(m_Weight));\n\t}\n};\n\n///////////////////////////////////////////////////////////////////////////////\n// SModelVertex: structure containing per-vertex data\nstruct SModelVertex\n{\n\t// vertex position\n\tCVector3D m_Coords;\n\t// vertex normal\n\tCVector3D m_Norm;\n\t// vertex UVs\n\tstd::vector<float> m_UVs;\n\t// vertex blend data\n\tSVertexBlend m_Blend;\n};\n\n\n///////////////////////////////////////////////////////////////////////////////\n// SModelFace: structure containing per-face data\nstruct SModelFace\n{\n\t// indices of the 3 vertices on this face\n\tu16 m_Verts[3];\n};\n\n\n////////////////////////////////////////////////////////////////////////////////////////\n// CModelDefRPrivate\nclass CModelDefRPrivate\n{\npublic:\n\tCModelDefRPrivate() { }\n\tvirtual ~CModelDefRPrivate() { }\n};\n\n\n////////////////////////////////////////////////////////////////////////////////////////\n// CModelDef: a raw 3D model; describes the vertices, faces, skinning and skeletal \n// information of a model\nclass CModelDef\n{\n\tNONCOPYABLE(CModelDef);\n\npublic:\n\t// current file version given to saved animations\n\tenum { FILE_VERSION = 3 };\n\t// supported file read version - files with a version less than this will be rejected\n\tenum { FILE_READ_VERSION = 1 };\n\n\npublic:\n\tCModelDef();\n\t~CModelDef();\n\n\t// model I/O functions\n\n\tstatic void Save(const VfsPath& filename,const CModelDef* mdef);\n\n\t/**\n\t * Loads a PMD file.\n\t * @param filename VFS path of .pmd file to load\n\t * @param name arbitrary name to give the model for debugging purposes (usually pathname)\n\t * @return the model - always non-NULL\n\t * @throw PSERROR_File if it can't load the model\n\t */\n\tstatic CModelDef* Load(const VfsPath& filename, const VfsPath& name);\n\t\npublic:\n\t// accessor: get vertex data\n\tsize_t GetNumVertices() const { return m_NumVertices; }\n\tSModelVertex* GetVertices() const { return m_pVertices; }\n\t\n\t// accessor: get number of UV sets\n\tsize_t GetNumUVsPerVertex() const { return m_NumUVsPerVertex; }\n\n\t// accessor: get face data\n\tsize_t GetNumFaces() const { return m_NumFaces; }\n\tSModelFace* GetFaces() const { return m_pFaces; }\n\n\t// accessor: get bone data\n\tsize_t GetNumBones() const { return m_NumBones; }\n\tCBoneState* GetBones() const { return m_Bones; }\n\tCMatrix3D* GetInverseBindBoneMatrices() { return m_InverseBindBoneMatrices; }\n\n\t// accessor: get blend data\n\tsize_t GetNumBlends() const { return m_NumBlends; }\n\tSVertexBlend* GetBlends() const { return m_pBlends; }\n\tsize_t* GetBlendIndices() const { return m_pBlendIndices; }\n\n\t// find and return pointer to prop point matching given name; return\n\t// null if no match (case insensitive search)\n\tconst SPropPoint* FindPropPoint(const char* name) const;\n\n\t/**\n\t * Transform the given vertex's position from the bind pose into the new pose.\n\t *\n\t * @return new world-space vertex coordinates\n\t */\n\tstatic CVector3D SkinPoint(const SModelVertex& vtx,\n\t\tconst CMatrix3D newPoseMatrices[]);\n\n\t/**\n\t * Transform the given vertex's normal from the bind pose into the new pose.\n\t *\n\t * @return new world-space vertex normal\n\t */\n\tstatic CVector3D SkinNormal(const SModelVertex& vtx,\n\t\tconst CMatrix3D newPoseMatrices[]);\n\n\t/**\n\t * Transform vertices' positions and normals.\n\t * (This is equivalent to looping over SkinPoint and SkinNormal,\n\t * but slightly more efficient.)\n\t */\n\tstatic void SkinPointsAndNormals(\n\t\tsize_t numVertices,\n\t\tconst VertexArrayIterator<CVector3D>& Position,\n\t\tconst VertexArrayIterator<CVector3D>& Normal,\n\t\tconst SModelVertex* vertices,\n\t\tconst size_t* blendIndices,\n\t\tconst CMatrix3D newPoseMatrices[]);\n\n#if HAVE_SSE\n\t/**\n\t * SSE-optimised version of SkinPointsAndNormals.\n\t */\n\tstatic void SkinPointsAndNormals_SSE(\n\t\tsize_t numVertices,\n\t\tconst VertexArrayIterator<CVector3D>& Position,\n\t\tconst VertexArrayIterator<CVector3D>& Normal,\n\t\tconst SModelVertex* vertices,\n\t\tconst size_t* blendIndices,\n\t\tconst CMatrix3D newPoseMatrices[]);\n#endif\n\n\t/**\n\t * Blend bone matrices together to fill bone palette.\n\t */\n\tvoid BlendBoneMatrices(CMatrix3D boneMatrices[]);\n\n\t/**\n\t * Register renderer private data. Use the key to\n\t * distinguish between private data used by different render paths.\n\t * The private data will be managed by this CModelDef object:\n\t * It will be deleted when CModelDef is destructed or when private\n\t * data is registered using the same key.\n\t *\n\t * @param key The opaque key that is used to identify the caller.\n\t * The given private data can be retrieved by passing key to GetRenderData.\n\t * @param data The private data.\n\t *\n\t * postconditions : data is bound to the lifetime of this CModelDef\n\t * object.\n\t */\n\tvoid SetRenderData(const void* key, CModelDefRPrivate* data);\n\t\n\t// accessor: render data\n\tCModelDefRPrivate* GetRenderData(const void* key) const;\n\n\t// accessor: get model name (for debugging)\n\tconst VfsPath& GetName() const { return m_Name; }\n\npublic:\n\t// vertex data\n\tsize_t m_NumVertices;\n\tSModelVertex* m_pVertices;\n\tsize_t m_NumUVsPerVertex; // number of UV pairs per vertex\n\t// face data\n\tsize_t m_NumFaces;\n\tSModelFace* m_pFaces;\n\t// bone data - default model pose\n\tsize_t m_NumBones;\n\tCBoneState* m_Bones;\n\tCMatrix3D* m_InverseBindBoneMatrices;\n\t// blend data\n\tsize_t m_NumBlends;\n\tSVertexBlend *m_pBlends;\n\tsize_t* m_pBlendIndices;\n\t// prop point data\n\tstd::vector<SPropPoint> m_PropPoints;\n\nprivate:\n\tVfsPath m_Name;\t// filename\n\n\t// renderdata shared by models of the same modeldef,\n\t// by render path\n\ttypedef std::map<const void*, CModelDefRPrivate*> RenderDataMap;\n\tRenderDataMap m_RenderData;\n};\n\n#endif\n \n"
  },
  {
    "path": "fpsgame/graphics/ObjectBase.cpp",
    "content": "/* Copyright (C) 2016 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"precompiled.h\"\n\n#include <algorithm>\n#include <queue>\n\n#include \"ObjectBase.h\"\n\n#include \"ObjectManager.h\"\n#include \"ps/XML/Xeromyces.h\"\n#include \"ps/Filesystem.h\"\n#include \"ps/CLogger.h\"\n#include \"lib/timer.h\"\n#include \"maths/MathUtil.h\"\n\n#include <boost/random/uniform_int.hpp>\n\nCObjectBase::CObjectBase(CObjectManager& objectManager)\n: m_ObjectManager(objectManager)\n{\n\tm_Properties.m_CastShadows = false;\n\tm_Properties.m_FloatOnWater = false;\n}\n\nvoid CObjectBase::LoadVariant(const CXeromyces& XeroFile, const XMBElement& variant, Variant& currentVariant)\n{\n\t#define EL(x) int el_##x = XeroFile.GetElementID(#x)\n\t#define AT(x) int at_##x = XeroFile.GetAttributeID(#x)\n\tEL(animation);\n\tEL(animations);\n\tEL(color);\n\tEL(decal);\n\tEL(mesh);\n\tEL(particles);\n\tEL(prop);\n\tEL(props);\n\tEL(texture);\n\tEL(textures);\n\tEL(variant);\n\tAT(actor);\n\tAT(angle);\n\tAT(attachpoint);\n\tAT(depth);\n\tAT(event);\n\tAT(file);\n\tAT(frequency);\n\tAT(load);\n\tAT(maxheight);\n\tAT(minheight);\n\tAT(name);\n\tAT(offsetx);\n\tAT(offsetz);\n\tAT(selectable);\n\tAT(sound);\n\tAT(speed);\n\tAT(width);\n\t#undef AT\n\t#undef EL\n\n\tif (variant.GetNodeName() != el_variant)\n\t{\n\t\tLOGERROR(\"Invalid variant format (unrecognised root element '%s')\", XeroFile.GetElementString(variant.GetNodeName()).c_str());\n\t\treturn;\n\t}\n\n\tXERO_ITER_ATTR(variant, attr)\n\t{\n\t\tif (attr.Name == at_file)\n\t\t{\n\t\t\t// Open up an external file to load.\n\t\t\t// Don't crash hard when failures happen, but log them and continue\n\t\t\tm_UsedFiles.insert(attr.Value);\n\t\t\tCXeromyces XeroVariant;\n\t\t\tif (XeroVariant.Load(g_VFS, \"art/variants/\" + attr.Value) == PSRETURN_OK)\n\t\t\t{\n\t\t\t\tXMBElement variantRoot = XeroVariant.GetRoot();\n\t\t\t\tLoadVariant(XeroVariant, variantRoot, currentVariant);\n\t\t\t}\n\t\t\telse\n\t\t\t\tLOGERROR(\"Could not open path %s\", attr.Value);\n\t\t\t// Continue loading extra definitions in this variant to allow nested files\n\t\t}\n\t\telse if (attr.Name == at_name)\n\t\t\tcurrentVariant.m_VariantName = attr.Value.LowerCase();\n\t\telse if (attr.Name == at_frequency)\n\t\t\tcurrentVariant.m_Frequency = attr.Value.ToInt();\n\t}\n\n\tXERO_ITER_EL(variant, option)\n\t{\n\t\tint option_name = option.GetNodeName();\n\n\t\tif (option_name == el_mesh)\n\t\t{\n\t\t\tcurrentVariant.m_ModelFilename = VfsPath(\"art/meshes\") / option.GetText().FromUTF8();\n\t\t}\n\t\telse if (option_name == el_textures)\n\t\t{\n\t\t\tXERO_ITER_EL(option, textures_element)\n\t\t\t{\n\t\t\t\tENSURE(textures_element.GetNodeName() == el_texture);\n\t\t\t\t\n\t\t\t\tSamp samp;\n\t\t\t\tXERO_ITER_ATTR(textures_element, se)\n\t\t\t\t{\n\t\t\t\t\tif (se.Name == at_file)\n\t\t\t\t\t\tsamp.m_SamplerFile = VfsPath(\"art/textures/skins\") / se.Value.FromUTF8();\n\t\t\t\t\telse if (se.Name == at_name)\n\t\t\t\t\t\tsamp.m_SamplerName = CStrIntern(se.Value);\n\t\t\t\t}\n\t\t\t\tcurrentVariant.m_Samplers.push_back(samp);\n\t\t\t}\n\t\t}\n\t\telse if (option_name == el_decal)\n\t\t{\n\t\t\tXMBAttributeList attrs = option.GetAttributes();\n\t\t\tDecal decal;\n\t\t\tdecal.m_SizeX = attrs.GetNamedItem(at_width).ToFloat();\n\t\t\tdecal.m_SizeZ = attrs.GetNamedItem(at_depth).ToFloat();\n\t\t\tdecal.m_Angle = DEGTORAD(attrs.GetNamedItem(at_angle).ToFloat());\n\t\t\tdecal.m_OffsetX = attrs.GetNamedItem(at_offsetx).ToFloat();\n\t\t\tdecal.m_OffsetZ = attrs.GetNamedItem(at_offsetz).ToFloat();\n\t\t\tcurrentVariant.m_Decal = decal;\n\t\t}\n\t\telse if (option_name == el_particles)\n\t\t{\n\t\t\tXMBAttributeList attrs = option.GetAttributes();\n\t\t\tVfsPath file = VfsPath(\"art/particles\") / attrs.GetNamedItem(at_file).FromUTF8();\n\t\t\tcurrentVariant.m_Particles = file;\n\n\t\t\t// For particle hotloading, it's easiest to reload the entire actor,\n\t\t\t// so remember the relevant particle file as a dependency for this actor\n\t\t\tm_UsedFiles.insert(file);\n\t\t}\n\t\telse if (option_name == el_color)\n\t\t{\n\t\t\tcurrentVariant.m_Color = option.GetText();\n\t\t}\n\t\telse if (option_name == el_animations)\n\t\t{\n\t\t\tXERO_ITER_EL(option, anim_element)\n\t\t\t{\n\t\t\t\tENSURE(anim_element.GetNodeName() == el_animation);\n\n\t\t\t\tAnim anim;\n\t\t\t\tXERO_ITER_ATTR(anim_element, ae)\n\t\t\t\t{\n\t\t\t\t\tif (ae.Name == at_name)\n\t\t\t\t\t\tanim.m_AnimName = ae.Value;\n\t\t\t\t\telse if (ae.Name == at_frequency)\n\t\t\t\t\t\tanim.m_Frequency = ae.Value.ToInt();\n\t\t\t\t\telse if (ae.Name == at_file)\n\t\t\t\t\t\tanim.m_FileName = VfsPath(\"art/animation\") / ae.Value.FromUTF8();\n\t\t\t\t\telse if (ae.Name == at_speed)\n\t\t\t\t\t\tanim.m_Speed = ae.Value.ToInt() > 0 ? ae.Value.ToInt() / 100.f : 1.f;\n\t\t\t\t\telse if (ae.Name == at_event)\n\t\t\t\t\t\tanim.m_ActionPos = clamp(ae.Value.ToFloat(), 0.f, 1.f);\n\t\t\t\t\telse if (ae.Name == at_load)\n\t\t\t\t\t\tanim.m_ActionPos2 = clamp(ae.Value.ToFloat(), 0.f, 1.f);\n\t\t\t\t\telse if (ae.Name == at_sound)\n\t\t\t\t\t\tanim.m_SoundPos = clamp(ae.Value.ToFloat(), 0.f, 1.f);\n\t\t\t\t}\n\t\t\t\tcurrentVariant.m_Anims.push_back(anim);\n\t\t\t}\n\t\t}\n\t\telse if (option_name == el_props)\n\t\t{\n\t\t\tXERO_ITER_EL(option, prop_element)\n\t\t\t{\n\t\t\t\tENSURE(prop_element.GetNodeName() == el_prop);\n\n\t\t\t\tProp prop;\n\t\t\t\tXERO_ITER_ATTR(prop_element, pe)\n\t\t\t\t{\n\t\t\t\t\tif (pe.Name == at_attachpoint)\n\t\t\t\t\t\tprop.m_PropPointName = pe.Value;\n\t\t\t\t\telse if (pe.Name == at_actor)\n\t\t\t\t\t\tprop.m_ModelName = pe.Value.FromUTF8();\n\t\t\t\t\telse if (pe.Name == at_minheight)\n\t\t\t\t\t\tprop.m_minHeight = pe.Value.ToFloat();\n\t\t\t\t\telse if (pe.Name == at_maxheight)\n\t\t\t\t\t\tprop.m_maxHeight = pe.Value.ToFloat();\n\t\t\t\t\telse if (pe.Name == at_selectable)\n\t\t\t\t\t\tprop.m_selectable = pe.Value != \"false\";\n\t\t\t\t}\n\t\t\t\tcurrentVariant.m_Props.push_back(prop);\n\t\t\t}\n\t\t}\n\t}\n}\n\nbool CObjectBase::Load(const VfsPath& pathname)\n{\n\tm_UsedFiles.clear();\n\tm_UsedFiles.insert(pathname);\n\n\tCXeromyces XeroFile;\n\tif (XeroFile.Load(g_VFS, pathname, \"actor\") != PSRETURN_OK)\n\t\treturn false;\n\n\t// Define all the elements used in the XML file\n\t#define EL(x) int el_##x = XeroFile.GetElementID(#x)\n\t#define AT(x) int at_##x = XeroFile.GetAttributeID(#x)\n\tEL(actor);\n\tEL(castshadow);\n\tEL(float);\n\tEL(group);\n\tEL(material);\n\t#undef AT\n\t#undef EL\n\n\tXMBElement root = XeroFile.GetRoot();\n\n\tif (root.GetNodeName() != el_actor)\n\t{\n\t\tLOGERROR(\"Invalid actor format (unrecognised root element '%s')\", XeroFile.GetElementString(root.GetNodeName()).c_str());\n\t\treturn false;\n\t}\n\n\tm_VariantGroups.clear();\n\n\tm_Pathname = pathname;\n\tm_ShortName = pathname.Basename().string();\n\n\n\t// Set up the vector<vector<T>> m_Variants to contain the right number\n\t// of elements, to avoid wasteful copying/reallocation later.\n\t{\n\t\t// Count the variants in each group\n\t\tstd::vector<int> variantGroupSizes;\n\t\tXERO_ITER_EL(root, child)\n\t\t{\n\t\t\tif (child.GetNodeName() == el_group)\n\t\t\t\tvariantGroupSizes.push_back(child.GetChildNodes().size());\n\t\t}\n\n\t\tm_VariantGroups.resize(variantGroupSizes.size());\n\t\t// Set each vector to match the number of variants\n\t\tfor (size_t i = 0; i < variantGroupSizes.size(); ++i)\n\t\t\tm_VariantGroups[i].resize(variantGroupSizes[i]);\n\t}\n\n\n\t// (This XML-reading code is rather worryingly verbose...)\n\n\tstd::vector<std::vector<Variant> >::iterator currentGroup = m_VariantGroups.begin();\n\n\tXERO_ITER_EL(root, child)\n\t{\n\t\tint child_name = child.GetNodeName();\n\n\t\tif (child_name == el_group)\n\t\t{\n\t\t\tstd::vector<Variant>::iterator currentVariant = currentGroup->begin();\n\t\t\tXERO_ITER_EL(child, variant)\n\t\t\t{\n\t\t\t\tLoadVariant(XeroFile, variant, *currentVariant);\n\t\t\t\t++currentVariant;\n\t\t\t}\n\n\t\t\tif (currentGroup->size() == 0)\n\t\t\t\tLOGERROR(\"Actor group has zero variants ('%s')\", pathname.string8());\n\n\t\t\t++currentGroup;\n\t\t}\n\t\telse if (child_name == el_castshadow)\n\t\t\tm_Properties.m_CastShadows = true;\n\t\telse if (child_name == el_float)\n\t\t\tm_Properties.m_FloatOnWater = true;\n\t\telse if (child_name == el_material)\n\t\t\tm_Material = VfsPath(\"art/materials\") / child.GetText().FromUTF8();\n\t}\n\n\tif (m_Material.empty())\n\t\tm_Material = VfsPath(\"art/materials/default.xml\");\n\n\treturn true;\n}\n\nbool CObjectBase::Reload()\n{\n\treturn Load(m_Pathname);\n}\n\nbool CObjectBase::UsesFile(const VfsPath& pathname)\n{\n\treturn m_UsedFiles.find(pathname) != m_UsedFiles.end();\n}\n\nstd::vector<u8> CObjectBase::CalculateVariationKey(const std::vector<std::set<CStr> >& selections)\n{\n\t// (TODO: see CObjectManager::FindObjectVariation for an opportunity to\n\t// call this function a bit less frequently)\n\n\t// Calculate a complete list of choices, one per group, based on the\n\t// supposedly-complete selections (i.e. not making random choices at this\n\t// stage).\n\t// In each group, if one of the variants has a name matching a string in the\n\t// first 'selections', set use that one.\n\t// Otherwise, try with the next (lower priority) selections set, and repeat.\n\t// Otherwise, choose the first variant (arbitrarily).\n\n\tstd::vector<u8> choices;\n\n\tstd::multimap<CStr, CStrW> chosenProps;\n\n\tfor (std::vector<std::vector<CObjectBase::Variant> >::iterator grp = m_VariantGroups.begin();\n\t\tgrp != m_VariantGroups.end();\n\t\t++grp)\n\t{\n\t\t// Ignore groups with nothing inside. (A warning will have been\n\t\t// emitted by the loading code.)\n\t\tif (grp->size() == 0)\n\t\t\tcontinue;\n\n\t\tint match = -1; // -1 => none found yet\n\n\t\t// If there's only a single variant, choose that one\n\t\tif (grp->size() == 1)\n\t\t{\n\t\t\tmatch = 0;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Determine the first variant that matches the provided strings,\n\t\t\t// starting with the highest priority selections set:\n\n\t\t\tfor (std::vector<std::set<CStr> >::const_iterator selset = selections.begin(); selset < selections.end(); ++selset)\n\t\t\t{\n\t\t\t\tENSURE(grp->size() < 256); // else they won't fit in 'choices'\n\n\t\t\t\tfor (size_t i = 0; i < grp->size(); ++i)\n\t\t\t\t{\n\t\t\t\t\tif (selset->count((*grp)[i].m_VariantName))\n\t\t\t\t\t{\n\t\t\t\t\t\tmatch = (u8)i;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Stop after finding the first match\n\t\t\t\tif (match != -1)\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t// If no match, just choose the first\n\t\t\tif (match == -1)\n\t\t\t\tmatch = 0;\n\t\t}\n\n\t\tchoices.push_back(match);\n\n\t\t// Remember which props were chosen, so we can call CalculateVariationKey on them\n\t\t// at the end.\n\t\tVariant& var ((*grp)[match]);\n\t\tfor (std::vector<Prop>::iterator it = var.m_Props.begin(); it != var.m_Props.end(); ++it)\n\t\t{\n\t\t\t// Erase all existing props which are overridden by this variant:\n\t\t\tfor (std::vector<Prop>::iterator it = var.m_Props.begin(); it != var.m_Props.end(); ++it)\n\t\t\t\tchosenProps.erase(it->m_PropPointName);\n\t\t\t// and then insert the new ones:\n\t\t\tfor (std::vector<Prop>::iterator it = var.m_Props.begin(); it != var.m_Props.end(); ++it)\n\t\t\t\tif (! it->m_ModelName.empty())\n\t\t\t\t\tchosenProps.insert(make_pair(it->m_PropPointName, it->m_ModelName));\n\t\t}\n\t}\n\n\t// Load each prop, and add their CalculateVariationKey to our key:\n\tfor (std::multimap<CStr, CStrW>::iterator it = chosenProps.begin(); it != chosenProps.end(); ++it)\n\t{\n\t\tCObjectBase* prop = m_ObjectManager.FindObjectBase(it->second);\n\t\tif (prop)\n\t\t{\n\t\t\tstd::vector<u8> propChoices = prop->CalculateVariationKey(selections);\n\t\t\tchoices.insert(choices.end(), propChoices.begin(), propChoices.end());\n\t\t}\n\t}\n\n\treturn choices;\n}\n\nconst CObjectBase::Variation CObjectBase::BuildVariation(const std::vector<u8>& variationKey)\n{\n\tVariation variation;\n\n\t// variationKey should correspond with m_Variants, giving the id of the\n\t// chosen variant from each group. (Except variationKey has some bits stuck\n\t// on the end for props, but we don't care about those in here.)\n\n\tstd::vector<std::vector<CObjectBase::Variant> >::iterator grp = m_VariantGroups.begin();\n\tstd::vector<u8>::const_iterator match = variationKey.begin();\n\tfor ( ;\n\t\tgrp != m_VariantGroups.end() && match != variationKey.end();\n\t\t++grp, ++match)\n\t{\n\t\t// Ignore groups with nothing inside. (A warning will have been\n\t\t// emitted by the loading code.)\n\t\tif (grp->size() == 0)\n\t\t\tcontinue;\n\n\t\tsize_t id = *match;\n\t\tif (id >= grp->size())\n\t\t{\n\t\t\t// This should be impossible\n\t\t\tdebug_warn(L\"BuildVariation: invalid variant id\");\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Get the matched variant\n\t\tCObjectBase::Variant& var ((*grp)[id]);\n\n\t\t// Apply its data:\n\n\t\tif (! var.m_ModelFilename.empty())\n\t\t\tvariation.model = var.m_ModelFilename;\n\n\t\tif (var.m_Decal.m_SizeX && var.m_Decal.m_SizeZ)\n\t\t\tvariation.decal = var.m_Decal;\n\n\t\tif (! var.m_Particles.empty())\n\t\t\tvariation.particles = var.m_Particles;\n\n\t\tif (! var.m_Color.empty())\n\t\t\tvariation.color = var.m_Color;\n\n\t\t// If one variant defines one prop attached to e.g. \"root\", and this\n\t\t// variant defines two different props with the same attachpoint, the one\n\t\t// original should be erased, and replaced by the two new ones.\n\t\t//\n\t\t// So, erase all existing props which are overridden by this variant:\n\t\tfor (std::vector<CObjectBase::Prop>::iterator it = var.m_Props.begin(); it != var.m_Props.end(); ++it)\n\t\t\tvariation.props.erase(it->m_PropPointName);\n\t\t// and then insert the new ones:\n\t\tfor (std::vector<CObjectBase::Prop>::iterator it = var.m_Props.begin(); it != var.m_Props.end(); ++it)\n\t\t\tif (! it->m_ModelName.empty()) // if the name is empty then the overridden prop is just deleted\n\t\t\t\tvariation.props.insert(make_pair(it->m_PropPointName, *it));\n\n\t\t// Same idea applies for animations.\n\t\t// So, erase all existing animations which are overridden by this variant:\n\t\tfor (std::vector<CObjectBase::Anim>::iterator it = var.m_Anims.begin(); it != var.m_Anims.end(); ++it)\n\t\t\tvariation.anims.erase(it->m_AnimName);\n\t\t// and then insert the new ones:\n\t\tfor (std::vector<CObjectBase::Anim>::iterator it = var.m_Anims.begin(); it != var.m_Anims.end(); ++it)\n\t\t\tvariation.anims.insert(make_pair(it->m_AnimName, *it));\n\t\t\n\t\t// Same for samplers, though perhaps not strictly necessary:\n\t\tfor (std::vector<CObjectBase::Samp>::iterator it = var.m_Samplers.begin(); it != var.m_Samplers.end(); ++it)\n\t\t\tvariation.samplers.erase(it->m_SamplerName.string());\n\t\tfor (std::vector<CObjectBase::Samp>::iterator it = var.m_Samplers.begin(); it != var.m_Samplers.end(); ++it)\n\t\t\tvariation.samplers.insert(make_pair(it->m_SamplerName.string(), *it));\n\t}\n\n\treturn variation;\n}\n\nstd::set<CStr> CObjectBase::CalculateRandomVariation(uint32_t seed, const std::set<CStr>& initialSelections)\n{\n\trng_t rng;\n\trng.seed(seed);\n\n\tstd::set<CStr> remainingSelections = CalculateRandomRemainingSelections(rng, std::vector<std::set<CStr> >(1, initialSelections));\n\tremainingSelections.insert(initialSelections.begin(), initialSelections.end());\n\n\treturn remainingSelections; // now actually a complete set of selections\n}\n\nstd::set<CStr> CObjectBase::CalculateRandomRemainingSelections(uint32_t seed, const std::vector<std::set<CStr> >& initialSelections)\n{\n\trng_t rng;\n\trng.seed(seed);\n\treturn CalculateRandomRemainingSelections(rng, initialSelections);\n}\n\nstd::set<CStr> CObjectBase::CalculateRandomRemainingSelections(rng_t& rng, const std::vector<std::set<CStr> >& initialSelections)\n{\n\tstd::set<CStr> remainingSelections;\n\tstd::multimap<CStr, CStrW> chosenProps;\n\n\t// Calculate a complete list of selections, so there is at least one\n\t// (and in most cases only one) per group.\n\t// In each group, if one of the variants has a name matching a string in\n\t// 'selections', use that one.\n\t// If more than one matches, choose randomly from those matching ones.\n\t// If none match, choose randomly from all variants.\n\t//\n\t// When choosing randomly, make use of each variant's frequency. If all\n\t// variants have frequency 0, treat them as if they were 1.\n\n\tfor (std::vector<std::vector<Variant> >::iterator grp = m_VariantGroups.begin();\n\t\tgrp != m_VariantGroups.end();\n\t\t++grp)\n\t{\n\t\t// Ignore groups with nothing inside. (A warning will have been\n\t\t// emitted by the loading code.)\n\t\tif (grp->size() == 0)\n\t\t\tcontinue;\n\n\t\tint match = -1; // -1 => none found yet\n\n\t\t// If there's only a single variant, choose that one\n\t\tif (grp->size() == 1)\n\t\t{\n\t\t\tmatch = 0;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// See if a variant (or several, but we only care about the first)\n\t\t\t// is already matched by the selections we've made, keeping their\n\t\t\t// priority order into account\n\n\t\t\tfor (size_t s = 0; s < initialSelections.size(); ++s)\n\t\t\t{\n\t\t\t\tfor (size_t i = 0; i < grp->size(); ++i)\n\t\t\t\t{\n\t\t\t\t\tif (initialSelections[s].count((*grp)[i].m_VariantName))\n\t\t\t\t\t{\n\t\t\t\t\t\tmatch = (int)i;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (match >= 0)\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t// If there was one, we don't need to do anything now because there's\n\t\t\t// already something to choose. Otherwise, choose randomly from the others.\n\t\t\tif (match == -1)\n\t\t\t{\n\t\t\t\t// Sum the frequencies\n\t\t\t\tint totalFreq = 0;\n\t\t\t\tfor (size_t i = 0; i < grp->size(); ++i)\n\t\t\t\t\ttotalFreq += (*grp)[i].m_Frequency;\n\n\t\t\t\t// Someone might be silly and set all variants to have freq==0, in\n\t\t\t\t// which case we just pretend they're all 1\n\t\t\t\tbool allZero = (totalFreq == 0);\n\t\t\t\tif (allZero) totalFreq = (int)grp->size();\n\n\t\t\t\t// Choose a random number in the interval [0..totalFreq)\n\t\t\t\tint randNum = boost::uniform_int<>(0, totalFreq-1)(rng);\n\n\t\t\t\t// and use that to choose one of the variants\n\t\t\t\tfor (size_t i = 0; i < grp->size(); ++i)\n\t\t\t\t{\n\t\t\t\t\trandNum -= (allZero ? 1 : (*grp)[i].m_Frequency);\n\t\t\t\t\tif (randNum < 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tremainingSelections.insert((*grp)[i].m_VariantName);\n\t\t\t\t\t\t// (If this change to 'remainingSelections' interferes with earlier choices, then \n\t\t\t\t\t\t// we'll get some non-fatal inconsistencies that just break the randomness. But that\n\t\t\t\t\t\t// shouldn't happen, much.)\n\t\t\t\t\t\t// (As an example, suppose you have a group with variants \"a\" and \"b\", and another\n\t\t\t\t\t\t// with variants \"a\" and \"c\"; now if random selection choses \"b\" for the first\n\t\t\t\t\t\t// and \"a\" for the second, then the selection of \"a\" from the second group will\n\t\t\t\t\t\t// cause \"a\" to be used in the first instead of the \"b\").\n\t\t\t\t\t\tmatch = (int)i;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tENSURE(randNum < 0);\n\t\t\t\t// This should always succeed; otherwise it\n\t\t\t\t// wouldn't have chosen any of the variants.\n\t\t\t}\n\t\t}\n\n\t\t// Remember which props were chosen, so we can call CalculateRandomVariation on them\n\t\t// at the end.\n\t\tVariant& var ((*grp)[match]);\n\t\tfor (std::vector<Prop>::iterator it = var.m_Props.begin(); it != var.m_Props.end(); ++it)\n\t\t{\n\t\t\t// Erase all existing props which are overridden by this variant:\n\t\t\tfor (std::vector<Prop>::iterator it = var.m_Props.begin(); it != var.m_Props.end(); ++it)\n\t\t\t\tchosenProps.erase(it->m_PropPointName);\n\t\t\t// and then insert the new ones:\n\t\t\tfor (std::vector<Prop>::iterator it = var.m_Props.begin(); it != var.m_Props.end(); ++it)\n\t\t\t\tif (! it->m_ModelName.empty())\n\t\t\t\t\tchosenProps.insert(make_pair(it->m_PropPointName, it->m_ModelName));\n\t\t}\n\t}\n\n\t// Load each prop, and add their required selections to ours:\n\tfor (std::multimap<CStr, CStrW>::iterator it = chosenProps.begin(); it != chosenProps.end(); ++it)\n\t{\n\t\tCObjectBase* prop = m_ObjectManager.FindObjectBase(it->second);\n\t\tif (prop)\n\t\t{\n\t\t\tstd::vector<std::set<CStr> > propInitialSelections = initialSelections;\n\t\t\tif (!remainingSelections.empty())\n\t\t\t\tpropInitialSelections.push_back(remainingSelections);\n\n\t\t\tstd::set<CStr> propRemainingSelections = prop->CalculateRandomRemainingSelections(rng, propInitialSelections);\n\t\t\tremainingSelections.insert(propRemainingSelections.begin(), propRemainingSelections.end());\n\n\t\t\t// Add the prop's used files to our own (recursively) so we can hotload\n\t\t\t// when any prop is changed\n\t\t\tm_UsedFiles.insert(prop->m_UsedFiles.begin(), prop->m_UsedFiles.end());\n\t\t}\n\t}\n\n\treturn remainingSelections;\n}\n\nstd::vector<std::vector<CStr> > CObjectBase::GetVariantGroups() const\n{\n\tstd::vector<std::vector<CStr> > groups;\n\n\t// Queue of objects (main actor plus props (recursively)) to be processed\n\tstd::queue<const CObjectBase*> objectsQueue;\n\tobjectsQueue.push(this);\n\n\t// Set of objects already processed, so we don't do them more than once\n\tstd::set<const CObjectBase*> objectsProcessed;\n\n\twhile (!objectsQueue.empty())\n\t{\n\t\tconst CObjectBase* obj = objectsQueue.front();\n\t\tobjectsQueue.pop();\n\t\t// Ignore repeated objects (likely to be props)\n\t\tif (objectsProcessed.find(obj) != objectsProcessed.end())\n\t\t\tcontinue;\n\n\t\tobjectsProcessed.insert(obj);\n\n\t\t// Iterate through the list of groups\n\t\tfor (size_t i = 0; i < obj->m_VariantGroups.size(); ++i)\n\t\t{\n\t\t\t// Copy the group's variant names into a new vector\n\t\t\tstd::vector<CStr> group;\n\t\t\tgroup.reserve(obj->m_VariantGroups[i].size());\n\t\t\tfor (size_t j = 0; j < obj->m_VariantGroups[i].size(); ++j)\n\t\t\t\tgroup.push_back(obj->m_VariantGroups[i][j].m_VariantName);\n\n\t\t\t// If this group is identical to one elsewhere, don't bother listing\n\t\t\t// it twice.\n\t\t\t// Linear search is theoretically not very efficient, but hopefully\n\t\t\t// we don't have enough props for that to matter...\n\t\t\tbool dupe = false;\n\t\t\tfor (size_t j = 0; j < groups.size(); ++j)\n\t\t\t{\n\t\t\t\tif (groups[j] == group)\n\t\t\t\t{\n\t\t\t\t\tdupe = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (dupe)\n\t\t\t\tcontinue;\n\n\t\t\t// Add non-trivial groups (i.e. not just one entry) to the returned list\n\t\t\tif (obj->m_VariantGroups[i].size() > 1)\n\t\t\t\tgroups.push_back(group);\n\n\t\t\t// Add all props onto the queue to be considered\n\t\t\tfor (size_t j = 0; j < obj->m_VariantGroups[i].size(); ++j)\n\t\t\t{\n\t\t\t\tconst std::vector<Prop>& props = obj->m_VariantGroups[i][j].m_Props;\n\t\t\t\tfor (size_t k = 0; k < props.size(); ++k)\n\t\t\t\t{\n\t\t\t\t\tif (! props[k].m_ModelName.empty())\n\t\t\t\t\t{\n\t\t\t\t\t\tCObjectBase* prop = m_ObjectManager.FindObjectBase(props[k].m_ModelName.c_str());\n\t\t\t\t\t\tif (prop)\n\t\t\t\t\t\t\tobjectsQueue.push(prop);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn groups;\n}\n"
  },
  {
    "path": "fpsgame/graphics/ObjectBase.h",
    "content": "/* Copyright (C) 2016 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef INCLUDED_OBJECTBASE\n#define INCLUDED_OBJECTBASE\n\nclass CModel;\nclass CSkeletonAnim;\nclass CObjectManager;\nclass CXeromyces;\nclass XMBElement;\n\n#include <vector>\n#include <set>\n#include <map>\n#include <boost/unordered_set.hpp>\n#include \"lib/file/vfs/vfs_path.h\"\n#include \"ps/CStr.h\"\n#include \"ps/CStrIntern.h\"\n\n#include <boost/random/mersenne_twister.hpp>\n\nclass CObjectBase\n{\n\tNONCOPYABLE(CObjectBase);\npublic:\n\n\tstruct Anim\n\t{\n\t\t// constructor\n\t\tAnim() : m_Frequency(0), m_Speed(1.f), m_ActionPos(-1.f), m_ActionPos2(-1.f), m_SoundPos(-1.f) {}\n\t\t// name of the animation - \"Idle\", \"Run\", etc\n\t\tCStr m_AnimName;\n\t\tint m_Frequency;\n\t\t// filename of the animation - manidle.psa, manrun.psa, etc\n\t\tVfsPath m_FileName;\n\t\t// animation speed, as specified in XML actor file\n\t\tfloat m_Speed;\n\t\t// fraction [0.0, 1.0] of the way through the animation that the interesting bit(s)\n\t\t// happens, or -1.0 if unspecified\n\t\tfloat m_ActionPos;\n\t\tfloat m_ActionPos2;\n\t\tfloat m_SoundPos;\n\t};\n\n\tstruct Prop\n\t{\n\t\t// constructor\n\t\tProp() : m_minHeight(0.f), m_maxHeight(0.f), m_selectable(true) {}\n\t\t// name of the prop point to attach to - \"Prop01\", \"Prop02\", \"Head\", \"LeftHand\", etc ..\n\t\tCStr m_PropPointName;\n\t\t// name of the model file - art/actors/props/sword.xml or whatever\n\t\tCStrW m_ModelName;\n\t\t// allow the prop to ajust the height from minHeight to maxHeight relative to the main model\n\t\tfloat m_minHeight;\n\t\tfloat m_maxHeight;\n\t\tbool m_selectable;\n\t};\n\t\n\tstruct Samp\n\t{\n\t\t// identifier name of sampler in GLSL shaders\n\t\tCStrIntern m_SamplerName;\n\t\t// path to load from\n\t\tVfsPath m_SamplerFile;\n\t};\n\n\tstruct Decal\n\t{\n\t\tDecal() : m_SizeX(0.f), m_SizeZ(0.f), m_Angle(0.f), m_OffsetX(0.f), m_OffsetZ(0.f) {}\n\n\t\tfloat m_SizeX;\n\t\tfloat m_SizeZ;\n\t\tfloat m_Angle;\n\t\tfloat m_OffsetX;\n\t\tfloat m_OffsetZ;\n\t};\n\n\tstruct Variant\n\t{\n\t\tVariant() : m_Frequency(0) {}\n\n\t\tCStr m_VariantName; // lowercase name\n\t\tint m_Frequency;\n\t\tVfsPath m_ModelFilename;\n\t\tDecal m_Decal;\n\t\tVfsPath m_Particles;\n\t\tCStr m_Color;\n\n\t\tstd::vector<Anim> m_Anims;\n\t\tstd::vector<Prop> m_Props;\n\t\tstd::vector<Samp> m_Samplers;\n\t};\n\n\tstruct Variation\n\t{\n\t\tVfsPath model;\n\t\tDecal decal;\n\t\tVfsPath particles;\n\t\tCStr color;\n\t\tstd::multimap<CStr, Prop> props;\n\t\tstd::multimap<CStr, Anim> anims;\n\t\tstd::multimap<CStr, Samp> samplers;\n\t};\n\n\tCObjectBase(CObjectManager& objectManager);\n\n\t// Get the variation key (indices of chosen variants from each group)\n\t// based on the selection strings\n\tstd::vector<u8> CalculateVariationKey(const std::vector<std::set<CStr> >& selections);\n\n\t// Get the final actor data, combining all selected variants\n\tconst Variation BuildVariation(const std::vector<u8>& variationKey);\n\n\t// Get a set of selection strings that are complete enough to specify an\n\t// exact variation of the actor, using the initial selections wherever possible\n\t// and choosing randomly where a choice is necessary. \n\tstd::set<CStr> CalculateRandomVariation(uint32_t seed, const std::set<CStr>& initialSelections);\n\n\t// Given a prioritized vector of selection string sets that partially specify\n\t// a variation, calculates a remaining set of selection strings such that the resulting \n\t// set merged with the initial selections fully specifies an exact variation of\n\t// the actor. The resulting selections are selected randomly, but only where a choice\n\t// is necessary (i.e. where there are multiple variants but the initial selections,\n\t// applied in priority order, fail to select one).\n\tstd::set<CStr> CalculateRandomRemainingSelections(uint32_t seed, const std::vector<std::set<CStr> >& initialSelections);\n\n\t// Get a list of variant groups for this object, plus for all possible\n\t// props. Duplicated groups are removed, if several props share the same\n\t// variant names.\n\tstd::vector<std::vector<CStr> > GetVariantGroups() const;\n\n\t/**\n\t * Initialise this object by loading from the given file.\n\t * Returns false on error.\n\t */\n\tbool Load(const VfsPath& pathname);\n\n\t/**\n\t * Reload this object from the file that it was previously loaded from.\n\t * Returns false on error.\n\t */\n\tbool Reload();\n\n\t/**\n\t * Returns whether this object (including any possible props)\n\t * uses the given file. (This is used for hotloading.)\n\t */\n\tbool UsesFile(const VfsPath& pathname);\n\n\t// filename that this was loaded from\n\tVfsPath m_Pathname;\n\n\t// short human-readable name\n\tCStrW m_ShortName;\n\n\tstruct {\n\t\t// cast shadows from this object\n\t\tbool m_CastShadows;\n\t\t// float on top of water\n\t\tbool m_FloatOnWater;\n\t} m_Properties;\n\n\t// the material file\n\tVfsPath m_Material;\n\nprivate:\n\t// A low-quality RNG like rand48 causes visible non-random patterns (particularly\n\t// in large grids of the same actor with consecutive seeds, e.g. forests),\n\t// so use a better one that appears to avoid those patterns\n\ttypedef boost::mt19937 rng_t;\n\n\tstd::set<CStr> CalculateRandomRemainingSelections(rng_t& rng, const std::vector<std::set<CStr> >& initialSelections);\n\n\tstd::vector< std::vector<Variant> > m_VariantGroups;\n\tCObjectManager& m_ObjectManager;\n\n\tboost::unordered_set<VfsPath> m_UsedFiles;\n\n\tvoid LoadVariant(const CXeromyces& XeroFile, const XMBElement& variant, Variant& currentVariant);\n};\n\n#endif\n"
  },
  {
    "path": "fpsgame/graphics/ObjectEntry.cpp",
    "content": "/* Copyright (C) 2016 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"precompiled.h\"\n\n#include \"ObjectEntry.h\"\n\n#include \"graphics/Decal.h\"\n#include \"graphics/Material.h\"\n#include \"graphics/MaterialManager.h\"\n#include \"graphics/MeshManager.h\"\n#include \"graphics/Model.h\"\n#include \"graphics/ModelDef.h\"\n#include \"graphics/ObjectBase.h\"\n#include \"graphics/ObjectManager.h\"\n#include \"graphics/ParticleManager.h\"\n#include \"graphics/SkeletonAnim.h\"\n#include \"graphics/TextureManager.h\"\n#include \"lib/rand.h\"\n#include \"ps/CLogger.h\"\n#include \"ps/Game.h\"\n#include \"ps/World.h\"\n#include \"renderer/Renderer.h\"\n#include \"simulation2/Simulation2.h\"\n\n#include <sstream>\n\nCObjectEntry::CObjectEntry(CObjectBase* base, CSimulation2& simulation) :\n\tm_Base(base), m_Color(1.0f, 1.0f, 1.0f, 1.0f), m_Model(NULL), m_Outdated(false), m_Simulation(simulation)\n{\n}\n\nCObjectEntry::~CObjectEntry()\n{\n\tfor (const std::pair<CStr, CSkeletonAnim*>& anim : m_Animations)\n\t\tdelete anim.second;\n\n\tdelete m_Model;\n}\n\n\nbool CObjectEntry::BuildVariation(const std::vector<std::set<CStr> >& selections,\n\t\t\t\t\t\t\t\t  const std::vector<u8>& variationKey,\n\t\t\t\t\t\t\t\t  CObjectManager& objectManager)\n{\n\tCObjectBase::Variation variation = m_Base->BuildVariation(variationKey);\n\n\t// Copy the chosen data onto this model:\n\n\tfor (std::multimap<CStr, CObjectBase::Samp>::iterator it = variation.samplers.begin(); it != variation.samplers.end(); ++it)\n\t\tm_Samplers.push_back(it->second);\n\t\n\tm_ModelName = variation.model;\n\n\tif (! variation.color.empty())\n\t{\n\t\tstd::stringstream str;\n\t\tstr << variation.color;\n\t\tint r, g, b;\n\t\tif (! (str >> r >> g >> b)) // Any trailing data is ignored\n\t\t\tLOGERROR(\"Actor '%s' has invalid RGB color '%s'\", utf8_from_wstring(m_Base->m_ShortName), variation.color);\n\t\telse\n\t\t\tm_Color = CColor(r/255.0f, g/255.0f, b/255.0f, 1.0f);\n\t}\n\n\tif (variation.decal.m_SizeX && variation.decal.m_SizeZ)\n\t{\n\t\tCMaterial material = g_Renderer.GetMaterialManager().LoadMaterial(m_Base->m_Material);\n\t\t\n\t\tfor (const CObjectBase::Samp& samp : m_Samplers)\n\t\t{\n\t\t\tCTextureProperties textureProps(samp.m_SamplerFile);\n\t\t\ttextureProps.SetWrap(GL_CLAMP_TO_BORDER);\n\t\t\tCTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps);\n\t\t\t// TODO: Should check which renderpath is selected and only preload the necessary textures.\n\t\t\ttexture->Prefetch(); \n\t\t\tmaterial.AddSampler(CMaterial::TextureSampler(samp.m_SamplerName, texture));\n\t\t}\n\n\t\tSDecal decal(material,\n\t\t\tvariation.decal.m_SizeX, variation.decal.m_SizeZ,\n\t\t\tvariation.decal.m_Angle, variation.decal.m_OffsetX, variation.decal.m_OffsetZ,\n\t\t\tm_Base->m_Properties.m_FloatOnWater);\n\t\tm_Model = new CModelDecal(objectManager.GetTerrain(), decal);\n\n\t\treturn true;\n\t}\n\n\tif (!variation.particles.empty())\n\t{\n\t\tm_Model = new CModelParticleEmitter(g_Renderer.GetParticleManager().LoadEmitterType(variation.particles));\n\t\treturn true;\n\t}\n\n\tstd::vector<CObjectBase::Prop> props;\n\n\tfor (std::multimap<CStr, CObjectBase::Prop>::iterator it = variation.props.begin(); it != variation.props.end(); ++it)\n\t\tprops.push_back(it->second);\n\n\t// Build the model:\n\n\t// try and create a model\n\tCModelDefPtr modeldef (objectManager.GetMeshManager().GetMesh(m_ModelName));\n\tif (!modeldef)\n\t{\n\t\tLOGERROR(\"CObjectEntry::BuildVariation(): Model %s failed to load\", m_ModelName.string8());\n\t\treturn false;\n\t}\n\n\t// delete old model, create new \n\tCModel* model = new CModel(objectManager.GetSkeletonAnimManager(), m_Simulation);\n\tdelete m_Model;\n\tm_Model = model;\n\tmodel->SetMaterial(g_Renderer.GetMaterialManager().LoadMaterial(m_Base->m_Material));\n\tmodel->GetMaterial().AddStaticUniform(\"objectColor\", CVector4D(m_Color.r, m_Color.g, m_Color.b, m_Color.a));\n\tmodel->InitModel(modeldef);\n\t\n\tif (m_Samplers.empty())\n\t\tLOGERROR(\"Actor '%s' has no textures.\", utf8_from_wstring(m_Base->m_ShortName));\n\t\n\tfor (const CObjectBase::Samp& samp : m_Samplers)\n\t{\n\t\tCTextureProperties textureProps(samp.m_SamplerFile);\n\t\ttextureProps.SetWrap(GL_CLAMP_TO_EDGE);\n\t\tCTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps);\n\t\t// if we've loaded this model we're probably going to render it soon, so prefetch its texture. \n\t\t// All textures are prefetched even in the fixed pipeline, including the normal maps etc.\n\t\t// TODO: Should check which renderpath is selected and only preload the necessary textures.\n\t\ttexture->Prefetch(); \n\t\tmodel->GetMaterial().AddSampler(CMaterial::TextureSampler(samp.m_SamplerName, texture));\n\t}\n\n\tfor (const CStrIntern& requSampName : model->GetMaterial().GetRequiredSampler())\n\t{\n\t\tif (std::find_if(m_Samplers.begin(), m_Samplers.end(),\n\t\t                 [&](const CObjectBase::Samp& sampler) { return sampler.m_SamplerName == requSampName; }) == m_Samplers.end())\n\t\t\tLOGERROR(\"Actor %s: required texture sampler %s not found (material %s)\", utf8_from_wstring(m_Base->m_ShortName), requSampName.string().c_str(), m_Base->m_Material.string8().c_str());\n\t}\n\t\n\t// calculate initial object space bounds, based on vertex positions\n\tmodel->CalcStaticObjectBounds();\n\n\t// load the animations\n\tfor (std::multimap<CStr, CObjectBase::Anim>::iterator it = variation.anims.begin(); it != variation.anims.end(); ++it)\n\t{\n\t\tCStr name = it->first.LowerCase();\n\n\t\tif (it->second.m_FileName.empty())\n\t\t\tcontinue;\n\t\tCSkeletonAnim* anim = model->BuildAnimation(\n\t\t\tit->second.m_FileName,\n\t\t\tname,\n\t\t\tit->second.m_Frequency,\n\t\t\tit->second.m_Speed,\n\t\t\tit->second.m_ActionPos,\n\t\t\tit->second.m_ActionPos2,\n\t\t\tit->second.m_SoundPos);\n\t\tif (anim)\n\t\t\tm_Animations.insert(std::make_pair(name, anim));\n\t}\n\n\t// ensure there's always an idle animation\n\tif (m_Animations.find(\"idle\") == m_Animations.end())\n\t{\n\t\tCSkeletonAnim* anim = new CSkeletonAnim();\n\t\tanim->m_Name = \"idle\";\n\t\tanim->m_AnimDef = NULL;\n\t\tanim->m_Frequency = 0;\n\t\tanim->m_Speed = 0.f;\n\t\tanim->m_ActionPos = 0.f;\n\t\tanim->m_ActionPos2 = 0.f;\n\t\tanim->m_SoundPos = 0.f;\n\t\tm_Animations.insert(std::make_pair(\"idle\", anim));\n\n\t\t// Ignore errors, since they're probably saying this is a non-animated model\n\t\tmodel->SetAnimation(anim);\n\t}\n\telse\n\t{\n\t\t// start up idling\n\t\tif (!model->SetAnimation(GetRandomAnimation(\"idle\")))\n\t\t\tLOGERROR(\"Failed to set idle animation in model \\\"%s\\\"\", m_ModelName.string8());\n\t}\n\n\t// build props - TODO, RC - need to fix up bounds here\n\t// TODO: Make sure random variations get handled correctly when a prop fails\n\tfor (const CObjectBase::Prop& prop : props)\n\t{\n\t\t// Pluck out the special attachpoint 'projectile'\n\t\tif (prop.m_PropPointName == \"projectile\")\n\t\t{\n\t\t\tm_ProjectileModelName = prop.m_ModelName;\n\t\t\tcontinue;\n\t\t}\n\n\t\tCObjectEntry* oe = objectManager.FindObjectVariation(prop.m_ModelName.c_str(), selections);\n\t\tif (!oe)\n\t\t{\n\t\t\tLOGERROR(\"Failed to build prop model \\\"%s\\\" on actor \\\"%s\\\"\", utf8_from_wstring(prop.m_ModelName), utf8_from_wstring(m_Base->m_ShortName));\n\t\t\tcontinue;\n\t\t}\n\n\t\t// If we don't have a projectile but this prop does (e.g. it's our rider), then\n\t\t// use that as our projectile too\n\t\tif (m_ProjectileModelName.empty() && !oe->m_ProjectileModelName.empty())\n\t\t\tm_ProjectileModelName = oe->m_ProjectileModelName;\n\n\t\tCStr ppn = prop.m_PropPointName;\n\t\tbool isAmmo = false;\n\n\t\t// Handle the special attachpoint 'loaded-<proppoint>'\n\t\tif (ppn.Find(\"loaded-\") == 0)\n\t\t{\n\t\t\tppn = prop.m_PropPointName.substr(7);\n\t\t\tisAmmo = true;\n\t\t}\n\n\t\tconst SPropPoint* proppoint = modeldef->FindPropPoint(ppn.c_str());\n\t\tif (proppoint)\n\t\t{\n\t\t\tCModelAbstract* propmodel = oe->m_Model->Clone();\n\t\t\tif (isAmmo)\n\t\t\t\tmodel->AddAmmoProp(proppoint, propmodel, oe);\n\t\t\telse\n\t\t\t\tmodel->AddProp(proppoint, propmodel, oe, prop.m_minHeight, prop.m_maxHeight, prop.m_selectable);\n\t\t\tif (propmodel->ToCModel())\n\t\t\t\tpropmodel->ToCModel()->SetAnimation(oe->GetRandomAnimation(\"idle\"));\n\t\t}\n\t\telse\n\t\t\tLOGERROR(\"Failed to find matching prop point called \\\"%s\\\" in model \\\"%s\\\" for actor \\\"%s\\\"\", ppn, m_ModelName.string8(), utf8_from_wstring(m_Base->m_ShortName));\n\t}\n\n\t// setup flags\n\tif (m_Base->m_Properties.m_CastShadows)\n\t{\n\t\tmodel->SetFlags(model->GetFlags()|MODELFLAG_CASTSHADOWS);\n\t}\n\n\treturn true;\n}\n\nCSkeletonAnim* CObjectEntry::GetRandomAnimation(const CStr& animationName) const\n{\n\tstd::vector<CSkeletonAnim*> anims = GetAnimations(animationName);\n\n\tint totalFreq = 0;\n\tfor (CSkeletonAnim* anim : anims)\n\t\ttotalFreq += anim->m_Frequency;\n\n\tif (totalFreq == 0)\n\t\treturn anims[rand(0, anims.size())];\n\n\tint r = rand(0, totalFreq);\n\tfor (CSkeletonAnim* anim : anims)\n\t{\n\t\tr -= anim->m_Frequency;\n\t\tif (r < 0)\n\t\t\treturn anim;\n\t}\n\tLOGERROR(\"No animation found for name %s\", animationName);\n\treturn NULL;\n}\n\nstd::vector<CSkeletonAnim*> CObjectEntry::GetAnimations(const CStr& animationName) const\n{\n\tstd::vector<CSkeletonAnim*> anims;\n\n\tSkeletonAnimMap::const_iterator lower = m_Animations.lower_bound(animationName);\n\tSkeletonAnimMap::const_iterator upper = m_Animations.upper_bound(animationName);\n\n\tfor (SkeletonAnimMap::const_iterator it = lower; it != upper; ++it)\n\t\tanims.push_back(it->second);\n\n\tif (anims.empty())\n\t\tfor (const std::pair<CStr, CSkeletonAnim*>& anim : m_Animations)\n\t\t\tif (anim.second->m_Frequency > 0)\n\t\t\t\tanims.push_back(anim.second);\n\n\tif (anims.empty())\n\t{\n\t\tlower = m_Animations.lower_bound(\"idle\");\n\t\tupper = m_Animations.upper_bound(\"idle\");\n\t\tfor (SkeletonAnimMap::const_iterator it = lower; it != upper; ++it)\n\t\t\tanims.push_back(it->second);\n\t}\n\n\tENSURE(!anims.empty());\n\treturn anims;\n}\n"
  },
  {
    "path": "fpsgame/graphics/ObjectEntry.h",
    "content": "/* Copyright (C) 2016 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef INCLUDED_OBJECTENTRY\n#define INCLUDED_OBJECTENTRY\n\nclass CModelAbstract;\nclass CSkeletonAnim;\nclass CObjectBase;\nclass CObjectManager;\nclass CSimulation2;\nstruct SPropPoint;\n\n#include <map>\n#include <set>\n#include <vector>\n\n#include \"lib/file/vfs/vfs_path.h\"\n#include \"ps/CStr.h\"\n#include \"ps/Shapes.h\"\n\n#include \"graphics/ObjectBase.h\"\n\nclass CObjectEntry\n{\n\tNONCOPYABLE(CObjectEntry);\n\npublic:\n\tCObjectEntry(CObjectBase* base, CSimulation2& simulation);\n\t~CObjectEntry();\n\n\t// Construct this actor, using the specified variation selections\n\tbool BuildVariation(const std::vector<std::set<CStr> >& selections,\n\t\tconst std::vector<u8>& variationKey, CObjectManager& objectManager);\n\n\t// Base actor. Contains all the things that don't change between\n\t// different variations of the actor.\n\tCObjectBase* m_Base;\n\n\t// samplers list\n\tstd::vector<CObjectBase::Samp> m_Samplers;\n\t// model name\n\tVfsPath m_ModelName;\n\t// color (used when doing alpha-channel coloring, but not doing player-color)\n\tCColor m_Color;\n\t\t// (probable TODO: make color a per-model thing, rather than per-objectEntry,\n\t\t// so we can have lots of color variations without wasting memory on\n\t\t// lots of objectEntries)\n\n\tstd::wstring m_ProjectileModelName;\n\n\t/**\n\t * Returns a randomly-chosen animation matching the given name.\n\t * The chosen animation is picked randomly from the GetAnimations list\n\t * with the frequencies as weights (if there are any defined).\n\t * This method should always return an animation\n\t */\n\tCSkeletonAnim* GetRandomAnimation(const CStr& animationName) const;\n\n\t/**\n\t * Returns all the animations matching the given name.\n\t * - Prefers the animations names like the animationName\n\t * - Second choice are animations with a frequency\n\t * - Last choice are the Idle animations (which are always added)\n\t */\n\tstd::vector<CSkeletonAnim*> GetAnimations(const CStr& animationName) const;\n\n\t// corresponding model\n\tCModelAbstract* m_Model;\n\n\t// Whether this object is outdated, due to hotloading of its base object.\n\t// (If true then CObjectManager won't reuse this object from its cache.)\n\tbool m_Outdated;\n\nprivate:\n\tCSimulation2& m_Simulation;\n\n\ttypedef std::multimap<CStr, CSkeletonAnim*> SkeletonAnimMap;\n\tSkeletonAnimMap m_Animations;\n\t\t// TODO: something more memory-efficient than storing loads of similar strings for each unit?\n};\n\n\n#endif\n"
  },
  {
    "path": "fpsgame/graphics/ObjectManager.cpp",
    "content": "/* Copyright (C) 2015 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"precompiled.h\"\n\n#include \"ObjectManager.h\"\n\n#include \"graphics/ObjectBase.h\"\n#include \"graphics/ObjectEntry.h\"\n#include \"ps/CLogger.h\"\n#include \"ps/Game.h\"\n#include \"ps/Profile.h\"\n#include \"ps/Filesystem.h\"\n#include \"ps/XML/Xeromyces.h\"\n#include \"simulation2/Simulation2.h\"\n#include \"simulation2/components/ICmpTerrain.h\"\n#include \"simulation2/components/ICmpVisual.h\"\n\n\nbool CObjectManager::ObjectKey::operator< (const CObjectManager::ObjectKey& a) const\n{\n\tif (ActorName < a.ActorName)\n\t\treturn true;\n\telse if (ActorName > a.ActorName)\n\t\treturn false;\n\telse\n\t\treturn ActorVariation < a.ActorVariation;\n}\n\nstatic Status ReloadChangedFileCB(void* param, const VfsPath& path)\n{\n\treturn static_cast<CObjectManager*>(param)->ReloadChangedFile(path);\n}\n\nCObjectManager::CObjectManager(CMeshManager& meshManager, CSkeletonAnimManager& skeletonAnimManager, CSimulation2& simulation)\n: m_MeshManager(meshManager), m_SkeletonAnimManager(skeletonAnimManager), m_Simulation(simulation)\n{\n\tRegisterFileReloadFunc(ReloadChangedFileCB, this);\n\n\tif (!CXeromyces::AddValidator(g_VFS, \"actor\", \"art/actors/actor.rng\"))\n\t\tLOGERROR(\"CObjectManager: failed to load actor grammar file 'art/actors/actor.rng'\");\n}\n\nCObjectManager::~CObjectManager()\n{\n\tUnloadObjects();\n\n\tUnregisterFileReloadFunc(ReloadChangedFileCB, this);\n}\n\n\nCObjectBase* CObjectManager::FindObjectBase(const CStrW& objectname)\n{\n\tENSURE(!objectname.empty());\n\n\t// See if the base type has been loaded yet:\n\n\tstd::map<CStrW, CObjectBase*>::iterator it = m_ObjectBases.find(objectname);\n\tif (it != m_ObjectBases.end())\n\t\treturn it->second;\n\n\t// Not already loaded, so try to load it:\n\n\tCObjectBase* obj = new CObjectBase(*this);\n\n\tVfsPath pathname = VfsPath(\"art/actors/\") / objectname;\n\n\tif (obj->Load(pathname))\n\t{\n\t\tm_ObjectBases[objectname] = obj;\n\t\treturn obj;\n\t}\n\telse\n\t\tdelete obj;\n\n\tLOGERROR(\"CObjectManager::FindObjectBase(): Cannot find object '%s'\", utf8_from_wstring(objectname));\n\n\treturn 0;\n}\n\nCObjectEntry* CObjectManager::FindObject(const CStrW& objname)\n{\n\tstd::vector<std::set<CStr> > selections; // TODO - should this really be empty?\n\treturn FindObjectVariation(objname, selections);\n}\n\nCObjectEntry* CObjectManager::FindObjectVariation(const CStrW& objname, const std::vector<std::set<CStr> >& selections)\n{\n\tCObjectBase* base = FindObjectBase(objname);\n\n\tif (! base)\n\t\treturn NULL;\n\n\treturn FindObjectVariation(base, selections);\n}\n\nCObjectEntry* CObjectManager::FindObjectVariation(CObjectBase* base, const std::vector<std::set<CStr> >& selections)\n{\n\tPROFILE(\"object variation loading\");\n\n\t// Look to see whether this particular variation has already been loaded\n\n\tstd::vector<u8> choices = base->CalculateVariationKey(selections);\n\tObjectKey key (base->m_Pathname.string(), choices);\n\n\tstd::map<ObjectKey, CObjectEntry*>::iterator it = m_Objects.find(key);\n\tif (it != m_Objects.end() && !it->second->m_Outdated)\n\t\treturn it->second;\n\n\t// If it hasn't been loaded, load it now\n\n\t// TODO: If there was an existing ObjectEntry, but it's outdated (due to hotloading),\n\t// we'll get a memory leak when replacing its entry in m_Objects. The problem is\n\t// some CUnits might still have a pointer to the old ObjectEntry so we probably can't\n\t// just delete it now. Probably we need to redesign the caching/hotloading system so it\n\t// makes more sense (e.g. use shared_ptr); for now I'll just leak, to avoid making the logic\n\t// more complex than it is already is, since this only matters for the rare case of hotloading.\n\n\tCObjectEntry* obj = new CObjectEntry(base, m_Simulation); // TODO: type ?\n\n\t// TODO (for some efficiency): use the pre-calculated choices for this object,\n\t// which has already worked out what to do for props, instead of passing the\n\t// selections into BuildVariation and having it recalculate the props' choices.\n\n\tif (! obj->BuildVariation(selections, choices, *this))\n\t{\n\t\tDeleteObject(obj);\n\t\treturn NULL;\n\t}\n\n\tm_Objects[key] = obj;\n\n\treturn obj;\n}\n\nCTerrain* CObjectManager::GetTerrain()\n{\n\tCmpPtr<ICmpTerrain> cmpTerrain(m_Simulation, SYSTEM_ENTITY);\n\tif (!cmpTerrain)\n\t\treturn NULL;\n\treturn cmpTerrain->GetCTerrain();\n}\n\nvoid CObjectManager::DeleteObject(CObjectEntry* entry)\n{\n\tstd::function<bool(const std::pair<ObjectKey, CObjectEntry*>&)> second_equals =\n\t\t[&entry](const std::pair<ObjectKey, CObjectEntry*>& a) { return a.second == entry; };\n\n\tstd::map<ObjectKey, CObjectEntry*>::iterator it = m_Objects.begin();\n\twhile (m_Objects.end() != (it = find_if(it, m_Objects.end(), second_equals)))\n\t\tit = m_Objects.erase(it);\n\n\tdelete entry;\n}\n\n\nvoid CObjectManager::UnloadObjects()\n{\n\tfor (const std::pair<ObjectKey, CObjectEntry*>& p : m_Objects)\n\t\tdelete p.second;\n\tm_Objects.clear();\n\n\tfor (const std::pair<CStrW, CObjectBase*>& p : m_ObjectBases)\n\t\tdelete p.second;\n\tm_ObjectBases.clear();\n}\n\nStatus CObjectManager::ReloadChangedFile(const VfsPath& path)\n{\n\t// Mark old entries as outdated so we don't reload them from the cache\n\tfor (std::map<ObjectKey, CObjectEntry*>::iterator it = m_Objects.begin(); it != m_Objects.end(); ++it)\n\t\tif (it->second->m_Base->UsesFile(path))\n\t\t\tit->second->m_Outdated = true;\n\n\t// Reload actors that use a changed object\n\tfor (std::map<CStrW, CObjectBase*>::iterator it = m_ObjectBases.begin(); it != m_ObjectBases.end(); ++it)\n\t{\n\t\tif (it->second->UsesFile(path))\n\t\t{\n\t\t\tit->second->Reload();\n\n\t\t\t// Slightly ugly hack: The graphics system doesn't preserve enough information to regenerate the\n\t\t\t// object with all correct variations, and we don't want to waste space storing it just for the\n\t\t\t// rare occurrence of hotloading, so we'll tell the component (which does preserve the information)\n\t\t\t// to do the reloading itself\n\t\t\tconst CSimulation2::InterfaceListUnordered& cmps = m_Simulation.GetEntitiesWithInterfaceUnordered(IID_Visual);\n\t\t\tfor (CSimulation2::InterfaceListUnordered::const_iterator eit = cmps.begin(); eit != cmps.end(); ++eit)\n\t\t\t\tstatic_cast<ICmpVisual*>(eit->second)->Hotload(it->first);\n\t\t}\n\t}\n\n\treturn INFO::OK;\n}\n"
  },
  {
    "path": "fpsgame/graphics/ObjectManager.h",
    "content": "/* Copyright (C) 2009 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef INCLUDED_OBJECTMANAGER\n#define INCLUDED_OBJECTMANAGER\n\n#include <vector>\n#include <map>\n#include <set>\n\n#include \"ps/CStr.h\"\n#include \"lib/file/vfs/vfs_path.h\"\n\nclass CMeshManager;\nclass CObjectBase;\nclass CObjectEntry;\nclass CSkeletonAnimManager;\nclass CSimulation2;\nclass CTerrain;\n\n///////////////////////////////////////////////////////////////////////////////////////////\n// CObjectManager: manager class for all possible actor types\nclass CObjectManager\n{\n\tNONCOPYABLE(CObjectManager);\npublic:\n\t// Unique identifier of an actor variation\n\tstruct ObjectKey\n\t{\n\t\tObjectKey(const CStrW& name, const std::vector<u8>& var)\n\t\t\t: ActorName(name), ActorVariation(var) {}\n\n\t\tbool operator< (const CObjectManager::ObjectKey& a) const;\n\n\tprivate:\n\t\tCStrW ActorName;\n\t\tstd::vector<u8> ActorVariation;\n\t};\n\npublic:\n\n\t// constructor, destructor\n\tCObjectManager(CMeshManager& meshManager, CSkeletonAnimManager& skeletonAnimManager, CSimulation2& simulation);\n\t~CObjectManager();\n\n\t// Provide access to the manager classes for meshes and animations - they're\n\t// needed when objects are being created and so this seems like a convenient\n\t// place to centralise access.\n\tCMeshManager& GetMeshManager() const { return m_MeshManager; }\n\tCSkeletonAnimManager& GetSkeletonAnimManager() const { return m_SkeletonAnimManager; }\n\n\tvoid UnloadObjects();\n\n\tCObjectEntry* FindObject(const CStrW& objname);\n\tvoid DeleteObject(CObjectEntry* entry);\n\t\n\tCObjectBase* FindObjectBase(const CStrW& objname);\n\n\tCObjectEntry* FindObjectVariation(const CStrW& objname, const std::vector<std::set<CStr> >& selections);\n\tCObjectEntry* FindObjectVariation(CObjectBase* base, const std::vector<std::set<CStr> >& selections);\n\n\t/**\n\t * Get the terrain object that actors managed by this manager should be linked\n\t * with (primarily for the purpose of decals)\n\t */\n\tCTerrain* GetTerrain();\n\n\t/**\n\t * Reload any scripts that were loaded from the given filename.\n\t * (This is used to implement hotloading.)\n\t */\n\tStatus ReloadChangedFile(const VfsPath& path);\n\nprivate:\n\tCMeshManager& m_MeshManager;\n\tCSkeletonAnimManager& m_SkeletonAnimManager;\n\tCSimulation2& m_Simulation;\n\n\tstd::map<ObjectKey, CObjectEntry*> m_Objects;\n\tstd::map<CStrW, CObjectBase*> m_ObjectBases;\n};\n\n#endif\n"
  },
  {
    "path": "fpsgame/graphics/Overlay.cpp",
    "content": "/* Copyright (C) 2011 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"precompiled.h\"\n\n#include \"Overlay.h\"\n\n#include \"ps/CStr.h\"\n\nSOverlayTexturedLine::LineCapType SOverlayTexturedLine::StrToLineCapType(const std::wstring& str)\n{\n\tif (str == L\"round\")\n\t\treturn LINECAP_ROUND;\n\telse if (str == L\"sharp\")\n\t\treturn LINECAP_SHARP;\n\telse if (str == L\"square\")\n\t\treturn LINECAP_SQUARE;\n\telse if (str == L\"flat\")\n\t\treturn LINECAP_FLAT;\n\telse {\n\t\tdebug_warn(L\"[Overlay] Unrecognized line cap type identifier\");\n\t\treturn LINECAP_FLAT;\n\t}\n}\n\n"
  },
  {
    "path": "fpsgame/graphics/Overlay.h",
    "content": "/* Copyright (C) 2015 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef INCLUDED_GRAPHICS_OVERLAY\n#define INCLUDED_GRAPHICS_OVERLAY\n\n#include \"graphics/Texture.h\"\n#include \"maths/Vector2D.h\"\n#include \"maths/Vector3D.h\"\n#include \"maths/FixedVector3D.h\"\n#include \"ps/Shapes.h\"\n\nclass CTerrain;\nclass CSimContext;\nclass CTexturedLineRData;\n\n/**\n * Line-based overlay, with world-space coordinates, rendered in the world\n * potentially behind other objects. Designed for selection circles and debug info.\n */\nstruct SOverlayLine\n{\n\tSOverlayLine() : m_Thickness(1) { }\n\n\tCColor m_Color;\n\tstd::vector<float> m_Coords; // (x, y, z) vertex coordinate triples; shape is not automatically closed\n\tu8 m_Thickness; // in pixels\n\n\tvoid PushCoords(const CVector3D& v) { PushCoords(v.X, v.Y, v.Z); }\n\tvoid PushCoords(const float x, const float y, const float z)\n\t{\n\t\tm_Coords.push_back(x);\n\t\tm_Coords.push_back(y);\n\t\tm_Coords.push_back(z);\n\t}\n};\n\n/**\n * Textured line overlay, with world-space coordinates, rendered in the world onto the terrain.\n * Designed for relatively static textured lines, i.e. territory borders, originally.\n * \n * Once submitted for rendering, instances must not be copied afterwards. The reason is that they\n * are assigned rendering data that is unique to the submitted instance, and non-transferable to\n * any copies that would otherwise be made. Amongst others, this restraint includes that they must\n * not be submitted by their address inside a std::vector storing them by value.\n */\nstruct SOverlayTexturedLine\n{\n\tenum LineCapType\n\t{\n\t\tLINECAP_FLAT, ///< no line ending; abrupt stop of the line (aka. butt ending)\n\n\t\t/**\n\t\t * Semi-circular line ending. The texture is mapped by curving the left vertical edge\n\t\t * around the semi-circle's rim. That is, the center point has UV coordinates (0.5;0.5),\n\t\t * and the rim vertices all have U coordinate 0 and a V coordinate that ranges from 0 to\n\t\t * 1 as the rim is traversed.\n\t\t */\n\t\tLINECAP_ROUND,\n\t\tLINECAP_SHARP, ///< sharp point ending\n\t\tLINECAP_SQUARE, ///< square end that extends half the line width beyond the line end\n\t};\n\n\tSOverlayTexturedLine()\n\t\t: m_Thickness(1.0f), m_Closed(false), m_AlwaysVisible(false),\n\t\t  m_StartCapType(LINECAP_FLAT), m_EndCapType(LINECAP_FLAT), m_SimContext(NULL)\n\t{ }\n\n\tCTexturePtr m_TextureBase;\n\tCTexturePtr m_TextureMask;\n\n\t/// Color to apply to the line texture, where indicated by the mask.\n\tCColor m_Color;\n\t/// (x, z) vertex coordinate pairs; y is computed automatically.\n\tstd::vector<float> m_Coords;\n\t/// Half-width of the line, in world-space units.\n\tfloat m_Thickness;\n\t/// Should this line be treated as a closed loop? If set, any end cap settings are ignored.\n\tbool m_Closed;\n\t/// Should this line be rendered fully visible at all times, even under the SoD?\n\tbool m_AlwaysVisible;\n\n\tLineCapType m_StartCapType;\n\tLineCapType m_EndCapType;\n\n\t/**\n\t * Simulation context applicable for this overlay line; used to obtain terrain information\n\t * during automatic computation of Y coordinates.\n\t */\n\tconst CSimContext* m_SimContext;\n\n\t/**\n\t * Cached renderer data, because expensive to compute. Allocated by the renderer when necessary \n\t * for rendering purposes.\n\t * \n\t * Note: the rendering data may be shared between copies of this object to prevent having to \n\t * recompute it, while at the same time maintaining copyability of this object (see also docs on \n\t * CTexturedLineRData).\n\t */\n\tshared_ptr<CTexturedLineRData> m_RenderData;\n\n\t/**\n\t * Converts a string line cap type into its corresponding LineCap enum value, and returns\n\t * the resulting value. If the input string is unrecognized, a warning is issued and a\n\t * default value is returned.\n\t */\n\tstatic LineCapType StrToLineCapType(const std::wstring& str);\n\n\tvoid PushCoords(const float x, const float z) { m_Coords.push_back(x); m_Coords.push_back(z); }\n\tvoid PushCoords(const CVector2D& v) { PushCoords(v.X, v.Y); }\n\tvoid PushCoords(const std::vector<CVector2D>& points)\n\t{\n\t\tfor (size_t i = 0; i < points.size(); ++i)\n\t\t\tPushCoords(points[i]);\n\t}\n};\n\n/**\n * Billboard sprite overlay, with world-space coordinates, rendered on top\n * of all other objects. Designed for health bars and rank icons.\n */\nstruct SOverlaySprite\n{\n\tCTexturePtr m_Texture;\n\tCColor m_Color;\n\tCVector3D m_Position; // base position\n\tfloat m_X0, m_Y0, m_X1, m_Y1; // billboard corner coordinates, relative to base position\n};\n\n/**\n * Rectangular single-quad terrain overlay, in world space coordinates. The vertices of the quad\n * are not required to be coplanar; the quad is arbitrarily triangulated with no effort being made\n * to find a best fit to the underlying terrain.\n */\nstruct SOverlayQuad\n{\n\tCTexturePtr m_Texture;\n\tCTexturePtr m_TextureMask;\n\tCVector3D m_Corners[4];\n\tCColor m_Color;\n};\n\nstruct SOverlaySphere\n{\n\tSOverlaySphere() : m_Radius(0) { }\n\n\tCVector3D m_Center;\n\tfloat m_Radius;\n\tCColor m_Color;\n};\n\n// TODO: OverlayText\n\n#endif // INCLUDED_GRAPHICS_OVERLAY\n"
  },
  {
    "path": "fpsgame/graphics/ParticleEmitter.cpp",
    "content": "/* Copyright (C) 2015 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"precompiled.h\"\n\n#include \"ParticleEmitter.h\"\n\n#include \"graphics/LightEnv.h\"\n#include \"graphics/LOSTexture.h\"\n#include \"graphics/ParticleEmitterType.h\"\n#include \"graphics/ParticleManager.h\"\n#include \"graphics/ShaderProgram.h\"\n#include \"graphics/TextureManager.h\"\n\n#include \"renderer/Renderer.h\"\n\nCParticleEmitter::CParticleEmitter(const CParticleEmitterTypePtr& type) :\n\tm_Type(type), m_Active(true), m_NextParticleIdx(0), m_EmissionRoundingError(0.f),\n\tm_LastUpdateTime(type->m_Manager.GetCurrentTime()),\n\tm_IndexArray(GL_STATIC_DRAW),\n\tm_VertexArray(GL_DYNAMIC_DRAW),\n\tm_LastFrameNumber(-1)\n{\n\t// If we should start with particles fully emitted, pretend that we\n\t// were created in the past so the first update will produce lots of\n\t// particles.\n\t// TODO: instead of this, maybe it would make more sense to do a full\n\t// lifetime-length update of all emitters when the game first starts\n\t// (so that e.g. buildings constructed later on won't have fully-started\n\t// emitters, but those at the start will)?\n\tif (m_Type->m_StartFull)\n\t\tm_LastUpdateTime -= m_Type->m_MaxLifetime;\n\n\tm_Particles.reserve(m_Type->m_MaxParticles);\n\n\tm_AttributePos.type = GL_FLOAT;\n\tm_AttributePos.elems = 3;\n\tm_VertexArray.AddAttribute(&m_AttributePos);\n\n\tm_AttributeAxis.type = GL_FLOAT;\n\tm_AttributeAxis.elems = 2;\n\tm_VertexArray.AddAttribute(&m_AttributeAxis);\n\n\tm_AttributeUV.type = GL_FLOAT;\n\tm_AttributeUV.elems = 2;\n\tm_VertexArray.AddAttribute(&m_AttributeUV);\n\n\tm_AttributeColor.type = GL_UNSIGNED_BYTE;\n\tm_AttributeColor.elems = 4;\n\tm_VertexArray.AddAttribute(&m_AttributeColor);\n\n\tm_VertexArray.SetNumVertices(m_Type->m_MaxParticles * 4);\n\tm_VertexArray.Layout();\n\n\tm_IndexArray.SetNumVertices(m_Type->m_MaxParticles * 6);\n\tm_IndexArray.Layout();\n\tVertexArrayIterator<u16> index = m_IndexArray.GetIterator();\n\tfor (size_t i = 0; i < m_Type->m_MaxParticles; ++i)\n\t{\n\t\t*index++ = i*4 + 0;\n\t\t*index++ = i*4 + 1;\n\t\t*index++ = i*4 + 2;\n\t\t*index++ = i*4 + 2;\n\t\t*index++ = i*4 + 3;\n\t\t*index++ = i*4 + 0;\n\t}\n\tm_IndexArray.Upload();\n\tm_IndexArray.FreeBackingStore();\n}\n\nvoid CParticleEmitter::UpdateArrayData(int frameNumber)\n{\n\tif (m_LastFrameNumber == frameNumber)\n\t\treturn;\n\n\tm_LastFrameNumber = frameNumber;\n\n\t// Update m_Particles\n\tm_Type->UpdateEmitter(*this, m_Type->m_Manager.GetCurrentTime() - m_LastUpdateTime);\n\tm_LastUpdateTime = m_Type->m_Manager.GetCurrentTime();\n\n\t// Regenerate the vertex array data:\n\n\tVertexArrayIterator<CVector3D> attrPos = m_AttributePos.GetIterator<CVector3D>();\n\tVertexArrayIterator<float[2]> attrAxis = m_AttributeAxis.GetIterator<float[2]>();\n\tVertexArrayIterator<float[2]> attrUV = m_AttributeUV.GetIterator<float[2]>();\n\tVertexArrayIterator<SColor4ub> attrColor = m_AttributeColor.GetIterator<SColor4ub>();\n\n\tENSURE(m_Particles.size() <= m_Type->m_MaxParticles);\n\n\tCBoundingBoxAligned bounds;\n\n\tfor (size_t i = 0; i < m_Particles.size(); ++i)\n\t{\n\t\t// TODO: for more efficient rendering, maybe we should replace this with\n\t\t// a degenerate quad if alpha is 0\n\n\t\tbounds += m_Particles[i].pos;\n\n\t\t*attrPos++ = m_Particles[i].pos;\n\t\t*attrPos++ = m_Particles[i].pos;\n\t\t*attrPos++ = m_Particles[i].pos;\n\t\t*attrPos++ = m_Particles[i].pos;\n\n\t\t// Compute corner offsets, split into sin/cos components so the vertex\n\t\t// shader can multiply by the camera-right (or left?) and camera-up vectors\n\t\t// to get rotating billboards:\n\n\t\tfloat s = sin(m_Particles[i].angle) * m_Particles[i].size/2.f;\n\t\tfloat c = cos(m_Particles[i].angle) * m_Particles[i].size/2.f;\n\n\t\t(*attrAxis)[0] = c;\n\t\t(*attrAxis)[1] = s;\n\t\t++attrAxis;\n\t\t(*attrAxis)[0] = s;\n\t\t(*attrAxis)[1] = -c;\n\t\t++attrAxis;\n\t\t(*attrAxis)[0] = -c;\n\t\t(*attrAxis)[1] = -s;\n\t\t++attrAxis;\n\t\t(*attrAxis)[0] = -s;\n\t\t(*attrAxis)[1] = c;\n\t\t++attrAxis;\n\n\t\t(*attrUV)[0] = 1;\n\t\t(*attrUV)[1] = 0;\n\t\t++attrUV;\n\t\t(*attrUV)[0] = 0;\n\t\t(*attrUV)[1] = 0;\n\t\t++attrUV;\n\t\t(*attrUV)[0] = 0;\n\t\t(*attrUV)[1] = 1;\n\t\t++attrUV;\n\t\t(*attrUV)[0] = 1;\n\t\t(*attrUV)[1] = 1;\n\t\t++attrUV;\n\n\t\tSColor4ub color = m_Particles[i].color;\n\n\t\t// Special case: If the blending depends on the source color, not the source alpha,\n\t\t// then pre-multiply by the alpha. (This is kind of a hack.)\n\t\tif (m_Type->m_BlendFuncDst == GL_ONE_MINUS_SRC_COLOR)\n\t\t{\n\t\t\tcolor.R = (color.R * color.A) / 255;\n\t\t\tcolor.G = (color.G * color.A) / 255;\n\t\t\tcolor.B = (color.B * color.A) / 255;\n\t\t}\n\n\t\t*attrColor++ = color;\n\t\t*attrColor++ = color;\n\t\t*attrColor++ = color;\n\t\t*attrColor++ = color;\n\t}\n\n\tm_ParticleBounds = bounds;\n\n\tm_VertexArray.Upload();\n}\n\nvoid CParticleEmitter::PrepareForRendering()\n{\n\tm_VertexArray.PrepareForRendering();\n}\n\nvoid CParticleEmitter::Bind(const CShaderProgramPtr& shader)\n{\n\tCLOSTexture& los = g_Renderer.GetScene().GetLOSTexture();\n\tshader->BindTexture(str_losTex, los.GetTextureSmooth());\n\tshader->Uniform(str_losTransform, los.GetTextureMatrix()[0], los.GetTextureMatrix()[12], 0.f, 0.f);\n\t\n\tconst CLightEnv& lightEnv = g_Renderer.GetLightEnv();\n\tshader->Uniform(str_sunColor, lightEnv.m_SunColor);\n\tshader->Uniform(str_fogColor, lightEnv.m_FogColor);\n\tshader->Uniform(str_fogParams, lightEnv.m_FogFactor, lightEnv.m_FogMax, 0.f, 0.f);\n\n\tshader->BindTexture(str_baseTex, m_Type->m_Texture);\n\tpglBlendEquationEXT(m_Type->m_BlendEquation);\n\tglBlendFunc(m_Type->m_BlendFuncSrc, m_Type->m_BlendFuncDst);\n}\n\nvoid CParticleEmitter::RenderArray(const CShaderProgramPtr& shader)\n{\n\t// Some drivers apparently don't like count=0 in glDrawArrays here,\n\t// so skip all drawing in that case\n\tif (m_Particles.empty())\n\t\treturn;\n\n\tu8* indexBase = m_IndexArray.Bind();\n\tu8* base = m_VertexArray.Bind();\n\n\tGLsizei stride = (GLsizei)m_VertexArray.GetStride();\n\n\tshader->VertexPointer(3, GL_FLOAT, stride, base + m_AttributePos.offset);\n\n\t// Pass the sin/cos axis components as texcoords for no particular reason\n\t// other than that they fit. (Maybe this should be glVertexAttrib* instead?)\n\tshader->TexCoordPointer(GL_TEXTURE0, 2, GL_FLOAT, stride, base + m_AttributeUV.offset);\n\tshader->TexCoordPointer(GL_TEXTURE1, 2, GL_FLOAT, stride, base + m_AttributeAxis.offset);\n\n\tshader->ColorPointer(4, GL_UNSIGNED_BYTE, stride, base + m_AttributeColor.offset);\n\n\tshader->AssertPointersBound();\n\tglDrawElements(GL_TRIANGLES, (GLsizei)(m_Particles.size() * 6), GL_UNSIGNED_SHORT, indexBase);\n\n\tg_Renderer.GetStats().m_DrawCalls++;\n\tg_Renderer.GetStats().m_Particles += m_Particles.size();\n}\n\nvoid CParticleEmitter::Unattach(const CParticleEmitterPtr& self)\n{\n\tm_Active = false;\n\tm_Type->m_Manager.AddUnattachedEmitter(self);\n}\n\nvoid CParticleEmitter::AddParticle(const SParticle& particle)\n{\n\tif (m_NextParticleIdx >= m_Particles.size())\n\t\tm_Particles.push_back(particle);\n\telse\n\t\tm_Particles[m_NextParticleIdx] = particle;\n\n\tm_NextParticleIdx = (m_NextParticleIdx + 1) % m_Type->m_MaxParticles;\n}\n\nvoid CParticleEmitter::SetEntityVariable(const std::string& name, float value)\n{\n\tm_EntityVariables[name] = value;\n}\n\n\n\nCModelParticleEmitter::CModelParticleEmitter(const CParticleEmitterTypePtr& type) :\n\tm_Type(type)\n{\n\tm_Emitter = CParticleEmitterPtr(new CParticleEmitter(m_Type));\n}\n\nCModelParticleEmitter::~CModelParticleEmitter()\n{\n\tm_Emitter->Unattach(m_Emitter);\n}\n\nvoid CModelParticleEmitter::SetEntityVariable(const std::string& name, float value)\n{\n\tm_Emitter->SetEntityVariable(name, value);\n}\n\nCModelAbstract* CModelParticleEmitter::Clone() const\n{\n\treturn new CModelParticleEmitter(m_Type);\n}\n\nvoid CModelParticleEmitter::CalcBounds()\n{\n\t// TODO: we ought to compute sensible bounds here, probably based on the\n\t// current computed particle positions plus the emitter type's largest\n\t// potential bounding box at the current position\n\n\tm_WorldBounds = m_Type->CalculateBounds(m_Emitter->GetPosition(), m_Emitter->GetParticleBounds());\n}\n\nvoid CModelParticleEmitter::ValidatePosition()\n{\n\t// TODO: do we need to do anything here?\n\n\t// This is a convenient (though possibly not particularly appropriate) place\n\t// to invalidate bounds so they'll be recomputed from the recent particle data\n\tInvalidateBounds();\n}\n\nvoid CModelParticleEmitter::InvalidatePosition()\n{\n}\n\nvoid CModelParticleEmitter::SetTransform(const CMatrix3D& transform)\n{\n\tif (m_Transform == transform)\n\t\treturn;\n\n\tm_Emitter->SetPosition(transform.GetTranslation());\n\tm_Emitter->SetRotation(transform.GetRotation());\n\n\t// call base class to set transform on this object\n\tCRenderableObject::SetTransform(transform);\n}\n"
  },
  {
    "path": "fpsgame/graphics/ParticleEmitter.h",
    "content": "/* Copyright (C) 2011 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef INCLUDED_PARTICLEEMITTER\n#define INCLUDED_PARTICLEEMITTER\n\n#include \"graphics/ModelAbstract.h\"\n#include \"graphics/ParticleEmitterType.h\"\n#include \"graphics/Texture.h\"\n#include \"maths/Quaternion.h\"\n#include \"renderer/VertexArray.h\"\n\n#include <map>\n\n/**\n * Simulation state for a single particle.\n */\nstruct SParticle\n{\n\tCVector3D pos;\n\tCVector3D velocity;\n\tfloat angle;\n\tfloat angleSpeed;\n\tfloat size;\n\tfloat sizeGrowthRate;\n\tSColor4ub color;\n\tfloat age;\n\tfloat maxAge;\n};\n\ntypedef shared_ptr<CParticleEmitter> CParticleEmitterPtr;\n\n/**\n * Particle emitter.\n *\n * Emitters store particle data in two forms:\n *  * m_Particles contains the raw data used for the CPU particle simulation.\n *  * m_VertexArray contains the data required for rendering.\n * Particles are rendered as billboard quads, so the vertex array contains four vertices\n * per particle with different UV coordinates. The billboard position computation is\n * performed by a vertex shader.\n *\n * The number of particles is a constant for the entire life of the emitter,\n * to simplify the updating and rendering.\n * m_Particles acts like a ring buffer, so we don't have to worry about dynamically\n * allocating particles. If particles have variable lifetimes, they'll exist in the\n * array with alpha=0 until they're overwritten by a new particle after the maximum\n * lifetime.\n *\n * (It's quite likely this could be made more efficient, if the overhead of any added\n * complexity is not high.)\n */\nclass CParticleEmitter\n{\npublic:\n\tCParticleEmitter(const CParticleEmitterTypePtr& type);\n\n\t/**\n\t * Set the position to be used for emission of new particles.\n\t */\n\tvoid SetPosition(const CVector3D& pos)\n\t{\n\t\tm_Pos = pos;\n\t}\n\n\tCVector3D GetPosition() const\n\t{\n\t\treturn m_Pos;\n\t}\n\n\t/**\n\t * Set the rotation to be used for emission of new particles (note: depends on particles).\n\t */\n\tvoid SetRotation(const CQuaternion& rot)\n\t{\n\t\tm_Rot = rot;\n\t}\n\t\n\tCQuaternion GetRotation() const\n\t{\n\t\treturn m_Rot;\n\t}\n\n\t/**\n\t * Get the bounding box of the center points of particles at their current positions.\n\t */\n\tCBoundingBoxAligned GetParticleBounds() { return m_ParticleBounds; }\n\n\t/**\n\t * Push a new particle onto the ring buffer. (May overwrite an old particle.)\n\t */\n\tvoid AddParticle(const SParticle& particle);\n\n\t/**\n\t * Update particle and vertex array data. Must be called before RenderArray.\n\t *\n\t * If frameNumber is the same as the previous call to UpdateArrayData,\n\t * then the function will do no work and return immediately.\n\t */\n\tvoid UpdateArrayData(int frameNumber);\n\n\t/**\n\t * Make the vertex data available for subsequent binding and rendering.\n\t */\n\tvoid PrepareForRendering();\n\n\t/**\n\t * Bind rendering state (textures and blend modes).\n\t */\n\tvoid Bind(const CShaderProgramPtr& shader);\n\n\t/**\n\t * Draw the vertex array.\n\t */\n\tvoid RenderArray(const CShaderProgramPtr& shader);\n\n\t/**\n\t * Stop this emitter emitting new particles, and pass responsibility for rendering\n\t * to the CParticleManager. This should be called before dropping the last shared_ptr\n\t * to this object so that it will carry on rendering (until all particles have dissipated)\n\t * even when it's no longer attached to a model.\n\t * @param self the shared_ptr you're about to drop\n\t */\n\tvoid Unattach(const CParticleEmitterPtr& self);\n\n\tvoid SetEntityVariable(const std::string& name, float value);\n\n\tCParticleEmitterTypePtr m_Type;\n\n\t/// Whether this emitter is still emitting new particles\n\tbool m_Active;\n\n\tCVector3D m_Pos;\n\tCQuaternion m_Rot;\n\n\tstd::map<std::string, float> m_EntityVariables;\n\n\tstd::vector<SParticle> m_Particles;\n\tsize_t m_NextParticleIdx;\n\n\tfloat m_LastUpdateTime;\n\tfloat m_EmissionRoundingError;\n\nprivate:\n\t/// Bounding box of the current particle center points\n\tCBoundingBoxAligned m_ParticleBounds;\n\n\tVertexIndexArray m_IndexArray;\n\n\tVertexArray m_VertexArray;\n\tVertexArray::Attribute m_AttributePos;\n\tVertexArray::Attribute m_AttributeAxis;\n\tVertexArray::Attribute m_AttributeUV;\n\tVertexArray::Attribute m_AttributeColor;\n\n\tint m_LastFrameNumber;\n};\n\n/**\n * Particle emitter model, for attaching emitters as props on other models.\n */\nclass CModelParticleEmitter : public CModelAbstract\n{\npublic:\n\tCModelParticleEmitter(const CParticleEmitterTypePtr& type);\n\t~CModelParticleEmitter();\n\n\t/// Dynamic cast\n\tvirtual CModelParticleEmitter* ToCModelParticleEmitter()\n\t{\n\t\treturn this;\n\t}\n\n\tvirtual CModelAbstract* Clone() const;\n\n\tvirtual void SetDirtyRec(int dirtyflags)\n\t{\n\t\tSetDirty(dirtyflags);\n\t}\n\n\tvirtual void SetTerrainDirty(ssize_t UNUSED(i0), ssize_t UNUSED(j0), ssize_t UNUSED(i1), ssize_t UNUSED(j1))\n\t{\n\t}\n\n\tvirtual void SetEntityVariable(const std::string& name, float value);\n\n\tvirtual void CalcBounds();\n\tvirtual void ValidatePosition();\n\tvirtual void InvalidatePosition();\n\tvirtual void SetTransform(const CMatrix3D& transform);\n\n\tCParticleEmitterTypePtr m_Type;\n\tCParticleEmitterPtr m_Emitter;\n};\n\n#endif // INCLUDED_PARTICLEEMITTER\n"
  },
  {
    "path": "fpsgame/graphics/ParticleEmitterType.cpp",
    "content": "/* Copyright (C) 2015 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"precompiled.h\"\n\n#include \"ParticleEmitterType.h\"\n\n#include \"graphics/Color.h\"\n#include \"graphics/ParticleEmitter.h\"\n#include \"graphics/ParticleManager.h\"\n#include \"graphics/TextureManager.h\"\n#include \"lib/rand.h\"\n#include \"maths/MathUtil.h\"\n#include \"ps/CLogger.h\"\n#include \"ps/Filesystem.h\"\n#include \"ps/XML/Xeromyces.h\"\n#include \"renderer/Renderer.h\"\n\n#include <boost/random/uniform_real.hpp>\n\n\n/**\n * Interface for particle state variables, which get evaluated for each newly\n * constructed particle.\n */\nclass IParticleVar\n{\npublic:\n\tIParticleVar() : m_LastValue(0) { }\n\tvirtual ~IParticleVar() {}\n\n\t/// Computes and returns a new value.\n\tfloat Evaluate(CParticleEmitter& emitter)\n\t{\n\t\tm_LastValue = Compute(*emitter.m_Type, emitter);\n\t\treturn m_LastValue;\n\t}\n\n\t/**\n\t * Returns the last value that Evaluate returned.\n\t * This is used for variables that depend on other variables (which is kind\n\t * of fragile since it's very order-dependent), so they don't get re-randomised\n\t * and don't have a danger of infinite recursion.\n\t */\n\tfloat LastValue() { return m_LastValue; }\n\n\t/**\n\t * Returns the minimum value that Evaluate might ever return,\n\t * for computing bounds.\n\t */\n\tvirtual float Min(CParticleEmitterType& type) = 0;\n\n\t/**\n\t * Returns the maximum value that Evaluate might ever return,\n\t * for computing bounds.\n\t */\n\tvirtual float Max(CParticleEmitterType& type) = 0;\n\nprotected:\n\tvirtual float Compute(CParticleEmitterType& type, CParticleEmitter& emitter) = 0;\n\nprivate:\n\tfloat m_LastValue;\n};\n\n/**\n * Particle variable that returns a constant value.\n */\nclass CParticleVarConstant : public IParticleVar\n{\npublic:\n\tCParticleVarConstant(float val) :\n\t\tm_Value(val)\n\t{\n\t}\n\n\tvirtual float Compute(CParticleEmitterType& UNUSED(type), CParticleEmitter& UNUSED(emitter))\n\t{\n\t\treturn m_Value;\n\t}\n\n\tvirtual float Min(CParticleEmitterType& UNUSED(type))\n\t{\n\t\treturn m_Value;\n\t}\n\n\tvirtual float Max(CParticleEmitterType& UNUSED(type))\n\t{\n\t\treturn m_Value;\n\t}\n\nprivate:\n\tfloat m_Value;\n};\n\n/**\n * Particle variable that returns a uniformly-distributed random value.\n */\nclass CParticleVarUniform : public IParticleVar\n{\npublic:\n\tCParticleVarUniform(float min, float max) :\n\t\tm_Min(min), m_Max(max)\n\t{\n\t}\n\n\tvirtual float Compute(CParticleEmitterType& type, CParticleEmitter& UNUSED(emitter))\n\t{\n\t\treturn boost::uniform_real<>(m_Min, m_Max)(type.m_Manager.m_RNG);\n\t}\n\n\tvirtual float Min(CParticleEmitterType& UNUSED(type))\n\t{\n\t\treturn m_Min;\n\t}\n\n\tvirtual float Max(CParticleEmitterType& UNUSED(type))\n\t{\n\t\treturn m_Max;\n\t}\n\nprivate:\n\tfloat m_Min;\n\tfloat m_Max;\n};\n\n/**\n * Particle variable that returns the same value as some other variable\n * (assuming that variable was evaluated before this one).\n */\nclass CParticleVarCopy : public IParticleVar\n{\npublic:\n\tCParticleVarCopy(int from) :\n\t\tm_From(from)\n\t{\n\t}\n\n\tvirtual float Compute(CParticleEmitterType& type, CParticleEmitter& UNUSED(emitter))\n\t{\n\t\treturn type.m_Variables[m_From]->LastValue();\n\t}\n\n\tvirtual float Min(CParticleEmitterType& type)\n\t{\n\t\treturn type.m_Variables[m_From]->Min(type);\n\t}\n\n\tvirtual float Max(CParticleEmitterType& type)\n\t{\n\t\treturn type.m_Variables[m_From]->Max(type);\n\t}\n\nprivate:\n\tint m_From;\n};\n\n/**\n * A terrible ad-hoc attempt at handling some particular variable calculation,\n * which really needs to be cleaned up and generalised.\n */\nclass CParticleVarExpr : public IParticleVar\n{\npublic:\n\tCParticleVarExpr(const CStr& from, float mul, float max) :\n\t\tm_From(from), m_Mul(mul), m_Max(max)\n\t{\n\t}\n\n\tvirtual float Compute(CParticleEmitterType& UNUSED(type), CParticleEmitter& emitter)\n\t{\n\t\treturn std::min(m_Max, emitter.m_EntityVariables[m_From] * m_Mul);\n\t}\n\n\tvirtual float Min(CParticleEmitterType& UNUSED(type))\n\t{\n\t\treturn 0.f;\n\t}\n\n\tvirtual float Max(CParticleEmitterType& UNUSED(type))\n\t{\n\t\treturn m_Max;\n\t}\n\nprivate:\n\tCStr m_From;\n\tfloat m_Mul;\n\tfloat m_Max;\n};\n\n\n\n/**\n * Interface for particle effectors, which get evaluated every frame to\n * update particles.\n */\nclass IParticleEffector\n{\npublic:\n\tIParticleEffector() { }\n\tvirtual ~IParticleEffector() {}\n\n\t/// Updates all particles.\n\tvirtual void Evaluate(std::vector<SParticle>& particles, float dt) = 0;\n\n\t/// Returns maximum acceleration caused by this effector.\n\tvirtual CVector3D Max() = 0;\n\n};\n\n/**\n * Particle effector that applies a constant acceleration.\n */\nclass CParticleEffectorForce : public IParticleEffector\n{\npublic:\n\tCParticleEffectorForce(float x, float y, float z) :\n\t\tm_Accel(x, y, z)\n\t{\n\t}\n\n\tvirtual void Evaluate(std::vector<SParticle>& particles, float dt)\n\t{\n\t\tCVector3D dv = m_Accel * dt;\n\n\t\tfor (size_t i = 0; i < particles.size(); ++i)\n\t\t\tparticles[i].velocity += dv;\n\t}\n\n\tvirtual CVector3D Max()\n\t{\n\t\treturn m_Accel;\n\t}\n\nprivate:\n\tCVector3D m_Accel;\n};\n\n\n\n\nCParticleEmitterType::CParticleEmitterType(const VfsPath& path, CParticleManager& manager) :\n\tm_Manager(manager)\n{\n\tLoadXML(path);\n\t// TODO: handle load failure\n\n\t// Upper bound on number of particles depends on maximum rate and lifetime\n\tm_MaxLifetime = m_Variables[VAR_LIFETIME]->Max(*this);\n\tm_MaxParticles = ceil(m_Variables[VAR_EMISSIONRATE]->Max(*this) * m_MaxLifetime);\n\n\n\t// Compute the worst-case bounds of all possible particles,\n\t// based on the min/max values of positions and velocities and accelerations\n\t// and sizes. (This isn't a guaranteed bound but it should be sufficient for\n\t// culling.)\n\n\t// Assuming constant acceleration,\n\t//        p = p0 + v0*t + 1/2 a*t^2\n\t// => dp/dt = v0 + a*t\n\t//          = 0 at t = -v0/a\n\t// max(p) is at t=0, or t=tmax, or t=-v0/a if that's between 0 and tmax\n\t// => max(p) = max(p0, p0 + v0*tmax + 1/2 a*tmax, p0 - 1/2 v0^2/a)\n\n\t// Compute combined acceleration (assume constant)\n\tCVector3D accel;\n\tfor (size_t i = 0; i < m_Effectors.size(); ++i)\n\t\taccel += m_Effectors[i]->Max();\n\n\tCVector3D vmin(m_Variables[VAR_VELOCITY_X]->Min(*this), m_Variables[VAR_VELOCITY_Y]->Min(*this), m_Variables[VAR_VELOCITY_Z]->Min(*this));\n\tCVector3D vmax(m_Variables[VAR_VELOCITY_X]->Max(*this), m_Variables[VAR_VELOCITY_Y]->Max(*this), m_Variables[VAR_VELOCITY_Z]->Max(*this));\n\n\t// Start by assuming p0 = 0; compute each XYZ component individually\n\tm_MaxBounds.SetEmpty();\n\t// Lower/upper bounds at t=0, t=tmax\n\tm_MaxBounds[0].X = std::min(0.f, vmin.X*m_MaxLifetime + 0.5f*accel.X*m_MaxLifetime*m_MaxLifetime);\n\tm_MaxBounds[0].Y = std::min(0.f, vmin.Y*m_MaxLifetime + 0.5f*accel.Y*m_MaxLifetime*m_MaxLifetime);\n\tm_MaxBounds[0].Z = std::min(0.f, vmin.Z*m_MaxLifetime + 0.5f*accel.Z*m_MaxLifetime*m_MaxLifetime);\n\tm_MaxBounds[1].X = std::max(0.f, vmax.X*m_MaxLifetime + 0.5f*accel.X*m_MaxLifetime*m_MaxLifetime);\n\tm_MaxBounds[1].Y = std::max(0.f, vmax.Y*m_MaxLifetime + 0.5f*accel.Y*m_MaxLifetime*m_MaxLifetime);\n\tm_MaxBounds[1].Z = std::max(0.f, vmax.Z*m_MaxLifetime + 0.5f*accel.Z*m_MaxLifetime*m_MaxLifetime);\n\t// Extend bounds to include position at t where dp/dt=0, if 0 < t < tmax\n\tif (accel.X && 0 < -vmin.X/accel.X && -vmin.X/accel.X < m_MaxLifetime)\n\t\tm_MaxBounds[0].X = std::min(m_MaxBounds[0].X, -0.5f*vmin.X*vmin.X / accel.X);\n\tif (accel.Y && 0 < -vmin.Y/accel.Y && -vmin.Y/accel.Y < m_MaxLifetime)\n\t\tm_MaxBounds[0].Y = std::min(m_MaxBounds[0].Y, -0.5f*vmin.Y*vmin.Y / accel.Y);\n\tif (accel.Z && 0 < -vmin.Z/accel.Z && -vmin.Z/accel.Z < m_MaxLifetime)\n\t\tm_MaxBounds[0].Z = std::min(m_MaxBounds[0].Z, -0.5f*vmin.Z*vmin.Z / accel.Z);\n\tif (accel.X && 0 < -vmax.X/accel.X && -vmax.X/accel.X < m_MaxLifetime)\n\t\tm_MaxBounds[1].X = std::max(m_MaxBounds[1].X, -0.5f*vmax.X*vmax.X / accel.X);\n\tif (accel.Y && 0 < -vmax.Y/accel.Y && -vmax.Y/accel.Y < m_MaxLifetime)\n\t\tm_MaxBounds[1].Y = std::max(m_MaxBounds[1].Y, -0.5f*vmax.Y*vmax.Y / accel.Y);\n\tif (accel.Z && 0 < -vmax.Z/accel.Z && -vmax.Z/accel.Z < m_MaxLifetime)\n\t\tm_MaxBounds[1].Z = std::max(m_MaxBounds[1].Z, -0.5f*vmax.Z*vmax.Z / accel.Z);\n\n\t// Offset by the initial positions\n\tm_MaxBounds[0] += CVector3D(m_Variables[VAR_POSITION_X]->Min(*this), m_Variables[VAR_POSITION_Y]->Min(*this), m_Variables[VAR_POSITION_Z]->Min(*this));\n\tm_MaxBounds[1] += CVector3D(m_Variables[VAR_POSITION_X]->Max(*this), m_Variables[VAR_POSITION_Y]->Max(*this), m_Variables[VAR_POSITION_Z]->Max(*this));\n}\n\nint CParticleEmitterType::GetVariableID(const std::string& name)\n{\n\tif (name == \"emissionrate\")\t\treturn VAR_EMISSIONRATE;\n\tif (name == \"lifetime\")\t\t\treturn VAR_LIFETIME;\n\tif (name == \"position.x\")\t\treturn VAR_POSITION_X;\n\tif (name == \"position.y\")\t\treturn VAR_POSITION_Y;\n\tif (name == \"position.z\")\t\treturn VAR_POSITION_Z;\n\tif (name == \"angle\")\t\t\treturn VAR_ANGLE;\n\tif (name == \"velocity.x\")\t\treturn VAR_VELOCITY_X;\n\tif (name == \"velocity.y\")\t\treturn VAR_VELOCITY_Y;\n\tif (name == \"velocity.z\")\t\treturn VAR_VELOCITY_Z;\n\tif (name == \"velocity.angle\")\treturn VAR_VELOCITY_ANGLE;\n\tif (name == \"size\")\t\t\t\treturn VAR_SIZE;\n\tif (name == \"size.growthRate\")\treturn VAR_SIZE_GROWTHRATE;\n\tif (name == \"color.r\")\t\t\treturn VAR_COLOR_R;\n\tif (name == \"color.g\")\t\t\treturn VAR_COLOR_G;\n\tif (name == \"color.b\")\t\t\treturn VAR_COLOR_B;\n\tLOGWARNING(\"Particle sets unknown variable '%s'\", name.c_str());\n\treturn -1;\n}\n\nbool CParticleEmitterType::LoadXML(const VfsPath& path)\n{\n\t// Initialise with sane defaults\n\tm_Variables.clear();\n\tm_Variables.resize(VAR__MAX);\n\tm_Variables[VAR_EMISSIONRATE] = IParticleVarPtr(new CParticleVarConstant(10.f));\n\tm_Variables[VAR_LIFETIME] = IParticleVarPtr(new CParticleVarConstant(3.f));\n\tm_Variables[VAR_POSITION_X] = IParticleVarPtr(new CParticleVarConstant(0.f));\n\tm_Variables[VAR_POSITION_Y] = IParticleVarPtr(new CParticleVarConstant(0.f));\n\tm_Variables[VAR_POSITION_Z] = IParticleVarPtr(new CParticleVarConstant(0.f));\n\tm_Variables[VAR_ANGLE] = IParticleVarPtr(new CParticleVarConstant(0.f));\n\tm_Variables[VAR_VELOCITY_X] = IParticleVarPtr(new CParticleVarConstant(0.f));\n\tm_Variables[VAR_VELOCITY_Y] = IParticleVarPtr(new CParticleVarConstant(1.f));\n\tm_Variables[VAR_VELOCITY_Z] = IParticleVarPtr(new CParticleVarConstant(0.f));\n\tm_Variables[VAR_VELOCITY_ANGLE] = IParticleVarPtr(new CParticleVarConstant(0.f));\n\tm_Variables[VAR_SIZE] = IParticleVarPtr(new CParticleVarConstant(1.f));\n\tm_Variables[VAR_SIZE_GROWTHRATE] = IParticleVarPtr(new CParticleVarConstant(0.f));\n\tm_Variables[VAR_COLOR_R] = IParticleVarPtr(new CParticleVarConstant(1.f));\n\tm_Variables[VAR_COLOR_G] = IParticleVarPtr(new CParticleVarConstant(1.f));\n\tm_Variables[VAR_COLOR_B] = IParticleVarPtr(new CParticleVarConstant(1.f));\n\tm_BlendEquation = GL_FUNC_ADD;\n\tm_BlendFuncSrc = GL_SRC_ALPHA;\n\tm_BlendFuncDst = GL_ONE_MINUS_SRC_ALPHA;\n\tm_StartFull = false;\n\tm_UseRelativeVelocity = false;\n\tm_Texture = g_Renderer.GetTextureManager().GetErrorTexture();\n\n\tCXeromyces XeroFile;\n\tPSRETURN ret = XeroFile.Load(g_VFS, path, \"particle\");\n\tif (ret != PSRETURN_OK)\n\t\treturn false;\n\n\t// Define all the elements and attributes used in the XML file\n#define EL(x) int el_##x = XeroFile.GetElementID(#x)\n#define AT(x) int at_##x = XeroFile.GetAttributeID(#x)\n\tEL(texture);\n\tEL(blend);\n\tEL(start_full);\n\tEL(use_relative_velocity);\n\tEL(constant);\n\tEL(uniform);\n\tEL(copy);\n\tEL(expr);\n\tEL(force);\n\tAT(mode);\n\tAT(name);\n\tAT(value);\n\tAT(min);\n\tAT(max);\n\tAT(mul);\n\tAT(from);\n\tAT(x);\n\tAT(y);\n\tAT(z);\n#undef AT\n#undef EL\n\n\tXMBElement Root = XeroFile.GetRoot();\n\n\tXERO_ITER_EL(Root, Child)\n\t{\n\t\tif (Child.GetNodeName() == el_texture)\n\t\t{\n\t\t\tCTextureProperties textureProps(Child.GetText().FromUTF8());\n\t\t\ttextureProps.SetWrap(GL_CLAMP_TO_EDGE);\n\t\t\tm_Texture = g_Renderer.GetTextureManager().CreateTexture(textureProps);\n\t\t}\n\t\telse if (Child.GetNodeName() == el_blend)\n\t\t{\n\t\t\tCStr mode = Child.GetAttributes().GetNamedItem(at_mode);\n\t\t\tif (mode == \"add\")\n\t\t\t{\n\t\t\t\tm_BlendEquation = GL_FUNC_ADD;\n\t\t\t\tm_BlendFuncSrc = GL_SRC_ALPHA;\n\t\t\t\tm_BlendFuncDst = GL_ONE;\n\t\t\t}\n\t\t\telse if (mode == \"subtract\")\n\t\t\t{\n\t\t\t\tm_BlendEquation = GL_FUNC_REVERSE_SUBTRACT;\n\t\t\t\tm_BlendFuncSrc = GL_SRC_ALPHA;\n\t\t\t\tm_BlendFuncDst = GL_ONE;\n\t\t\t}\n\t\t\telse if (mode == \"over\")\n\t\t\t{\n\t\t\t\tm_BlendEquation = GL_FUNC_ADD;\n\t\t\t\tm_BlendFuncSrc = GL_SRC_ALPHA;\n\t\t\t\tm_BlendFuncDst = GL_ONE_MINUS_SRC_ALPHA;\n\t\t\t}\n\t\t\telse if (mode == \"multiply\")\n\t\t\t{\n\t\t\t\tm_BlendEquation = GL_FUNC_ADD;\n\t\t\t\tm_BlendFuncSrc = GL_ZERO;\n\t\t\t\tm_BlendFuncDst = GL_ONE_MINUS_SRC_COLOR;\n\t\t\t}\n\t\t}\n\t\telse if (Child.GetNodeName() == el_start_full)\n\t\t{\n\t\t\tm_StartFull = true;\n\t\t}\n\t\telse if (Child.GetNodeName() == el_use_relative_velocity)\n\t\t{\n\t\t\tm_UseRelativeVelocity = true;\n\t\t}\n\t\telse if (Child.GetNodeName() == el_constant)\n\t\t{\n\t\t\tint id = GetVariableID(Child.GetAttributes().GetNamedItem(at_name));\n\t\t\tif (id != -1)\n\t\t\t{\n\t\t\t\tm_Variables[id] = IParticleVarPtr(new CParticleVarConstant(\n\t\t\t\t\tChild.GetAttributes().GetNamedItem(at_value).ToFloat()\n\t\t\t\t));\n\t\t\t}\n\t\t}\n\t\telse if (Child.GetNodeName() == el_uniform)\n\t\t{\n\t\t\tint id = GetVariableID(Child.GetAttributes().GetNamedItem(at_name));\n\t\t\tif (id != -1)\n\t\t\t{\n\t\t\t\tfloat min = Child.GetAttributes().GetNamedItem(at_min).ToFloat();\n\t\t\t\tfloat max = Child.GetAttributes().GetNamedItem(at_max).ToFloat();\n\t\t\t\t// To avoid hangs in the RNG, only use it if [min, max) is non-empty\n\t\t\t\tif (min < max)\n\t\t\t\t\tm_Variables[id] = IParticleVarPtr(new CParticleVarUniform(min, max));\n\t\t\t\telse\n\t\t\t\t\tm_Variables[id] = IParticleVarPtr(new CParticleVarConstant(min));\n\t\t\t}\n\t\t}\n\t\telse if (Child.GetNodeName() == el_copy)\n\t\t{\n\t\t\tint id = GetVariableID(Child.GetAttributes().GetNamedItem(at_name));\n\t\t\tint from = GetVariableID(Child.GetAttributes().GetNamedItem(at_from));\n\t\t\tif (id != -1 && from != -1)\n\t\t\t\tm_Variables[id] = IParticleVarPtr(new CParticleVarCopy(from));\n\t\t}\n\t\telse if (Child.GetNodeName() == el_expr)\n\t\t{\n\t\t\tint id = GetVariableID(Child.GetAttributes().GetNamedItem(at_name));\n\t\t\tCStr from = Child.GetAttributes().GetNamedItem(at_from);\n\t\t\tfloat mul = Child.GetAttributes().GetNamedItem(at_mul).ToFloat();\n\t\t\tfloat max = Child.GetAttributes().GetNamedItem(at_max).ToFloat();\n\t\t\tif (id != -1)\n\t\t\t\tm_Variables[id] = IParticleVarPtr(new CParticleVarExpr(from, mul, max));\n\t\t}\n\t\telse if (Child.GetNodeName() == el_force)\n\t\t{\n\t\t\tfloat x = Child.GetAttributes().GetNamedItem(at_x).ToFloat();\n\t\t\tfloat y = Child.GetAttributes().GetNamedItem(at_y).ToFloat();\n\t\t\tfloat z = Child.GetAttributes().GetNamedItem(at_z).ToFloat();\n\t\t\tm_Effectors.push_back(IParticleEffectorPtr(new CParticleEffectorForce(x, y, z)));\n\t\t}\n\t}\n\n\treturn true;\n}\n\nvoid CParticleEmitterType::UpdateEmitter(CParticleEmitter& emitter, float dt)\n{\n\t// If dt is very large, we should do the update in multiple small\n\t// steps to prevent all the particles getting clumped together at\n\t// low framerates\n\n\tconst float maxStepLength = 0.2f;\n\n\t// Avoid wasting time by computing periods longer than the lifetime\n\t// period of the particles\n\tdt = std::min(dt, m_MaxLifetime);\n\n\twhile (dt > maxStepLength)\n\t{\n\t\tUpdateEmitterStep(emitter, maxStepLength);\n\t\tdt -= maxStepLength;\n\t}\n\n\tUpdateEmitterStep(emitter, dt);\n}\n\nvoid CParticleEmitterType::UpdateEmitterStep(CParticleEmitter& emitter, float dt)\n{\n\tENSURE(emitter.m_Type.get() == this);\n\n\tif (emitter.m_Active)\n\t{\n\t\tfloat emissionRate = m_Variables[VAR_EMISSIONRATE]->Evaluate(emitter);\n\n\t\t// Find how many new particles to spawn, and accumulate any rounding errors\n\t\t// (to maintain a constant emission rate even if dt is very small)\n\t\tint newParticles = floor(emitter.m_EmissionRoundingError + dt*emissionRate);\n\t\temitter.m_EmissionRoundingError += dt*emissionRate - newParticles;\n\n\t\t// If dt was very large, there's no point spawning new particles that\n\t\t// we'll immediately overwrite, so clamp it\n\t\tnewParticles = std::min(newParticles, (int)m_MaxParticles);\n\n\t\tfor (int i = 0; i < newParticles; ++i)\n\t\t{\n\t\t\t// Compute new particle state based on variables\n\t\t\tSParticle particle;\n\n\t\t\tparticle.pos.X = m_Variables[VAR_POSITION_X]->Evaluate(emitter);\n\t\t\tparticle.pos.Y = m_Variables[VAR_POSITION_Y]->Evaluate(emitter);\n\t\t\tparticle.pos.Z = m_Variables[VAR_POSITION_Z]->Evaluate(emitter);\n\t\t\tparticle.pos += emitter.m_Pos;\n\n\t\t\tif (m_UseRelativeVelocity)\n\t\t\t{\n\t\t\t\tfloat xVel = m_Variables[VAR_VELOCITY_X]->Evaluate(emitter);\n\t\t\t\tfloat yVel = m_Variables[VAR_VELOCITY_Y]->Evaluate(emitter);\n\t\t\t\tfloat zVel = m_Variables[VAR_VELOCITY_Z]->Evaluate(emitter);\n\t\t\t\tCVector3D EmitterAngle = emitter.GetRotation().ToMatrix().Transform(CVector3D(xVel,yVel,zVel));\n\t\t\t\tparticle.velocity.X = EmitterAngle.X;\n\t\t\t\tparticle.velocity.Y = EmitterAngle.Y;\n\t\t\t\tparticle.velocity.Z = EmitterAngle.Z;\n\t\t\t} else {\n\t\t\t\tparticle.velocity.X = m_Variables[VAR_VELOCITY_X]->Evaluate(emitter);\n\t\t\t\tparticle.velocity.Y = m_Variables[VAR_VELOCITY_Y]->Evaluate(emitter);\n\t\t\t\tparticle.velocity.Z = m_Variables[VAR_VELOCITY_Z]->Evaluate(emitter);\n\t\t\t}\n\t\t\tparticle.angle = m_Variables[VAR_ANGLE]->Evaluate(emitter);\n\t\t\tparticle.angleSpeed = m_Variables[VAR_VELOCITY_ANGLE]->Evaluate(emitter);\n\n\t\t\tparticle.size = m_Variables[VAR_SIZE]->Evaluate(emitter);\n\t\t\tparticle.sizeGrowthRate = m_Variables[VAR_SIZE_GROWTHRATE]->Evaluate(emitter);\n\n\t\t\tRGBColor color;\n\t\t\tcolor.X = m_Variables[VAR_COLOR_R]->Evaluate(emitter);\n\t\t\tcolor.Y = m_Variables[VAR_COLOR_G]->Evaluate(emitter);\n\t\t\tcolor.Z = m_Variables[VAR_COLOR_B]->Evaluate(emitter);\n\t\t\tparticle.color = ConvertRGBColorTo4ub(color);\n\n\t\t\tparticle.age = 0.f;\n\t\t\tparticle.maxAge = m_Variables[VAR_LIFETIME]->Evaluate(emitter);\n\n\t\t\temitter.AddParticle(particle);\n\t\t}\n\t}\n\n\t// Update particle states\n\tfor (size_t i = 0; i < emitter.m_Particles.size(); ++i)\n\t{\n\t\tSParticle& p = emitter.m_Particles[i];\n\n\t\t// Don't bother updating particles already at the end of their life\n\t\tif (p.age > p.maxAge)\n\t\t\tcontinue;\n\n\t\tp.pos += p.velocity * dt;\n\t\tp.angle += p.angleSpeed * dt;\n\t\tp.age += dt;\n\t\tp.size += p.sizeGrowthRate * dt;\n\n\t\t// Make alpha fade in/out nicely\n\t\t// TODO: this should probably be done as a variable or something,\n\t\t// instead of hardcoding\n\t\tfloat ageFrac = p.age / p.maxAge;\n\t\tfloat a = std::min(1.f-ageFrac, 5.f*ageFrac);\n\t\tp.color.A = clamp((int)(a*255.f), 0, 255);\n\t}\n\n\tfor (size_t i = 0; i < m_Effectors.size(); ++i)\n\t{\n\t\tm_Effectors[i]->Evaluate(emitter.m_Particles, dt);\n\t}\n}\n\nCBoundingBoxAligned CParticleEmitterType::CalculateBounds(CVector3D emitterPos, CBoundingBoxAligned emittedBounds)\n{\n\tCBoundingBoxAligned bounds = m_MaxBounds;\n\tbounds[0] += emitterPos;\n\tbounds[1] += emitterPos;\n\n\tbounds += emittedBounds;\n\n\t// The current bounds is for the particles' centers, so expand by\n\t// sqrt(2) * max_size/2 to ensure any rotated billboards fit in\n\tbounds.Expand(m_Variables[VAR_SIZE]->Max(*this)/2.f * sqrt(2.f));\n\n\treturn bounds;\n}\n"
  },
  {
    "path": "fpsgame/graphics/ParticleEmitterType.h",
    "content": "/* Copyright (C) 2011 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef INCLUDED_PARTICLEEMITTERTYPE\n#define INCLUDED_PARTICLEEMITTERTYPE\n\n#include \"graphics/Texture.h\"\n#include \"lib/ogl.h\"\n#include \"lib/file/vfs/vfs_path.h\"\n#include \"maths/BoundingBoxAligned.h\"\n\nclass CVector3D;\nclass CParticleEmitter;\nclass CParticleManager;\nclass IParticleVar;\nclass IParticleEffector;\n\n/**\n * Particle emitter type - stores the common state data for all emitters of that\n * type, and uses that data to update the emitter states.\n *\n * The data is initialised from XML files.\n *\n * Most of the emitter type data is represented as subclasses of IParticleVar,\n * which will typically return a constant value or a random value each time it's\n * evaluated. New subclasses can be added to support different random distributions,\n * etc.\n */\nclass CParticleEmitterType\n{\n\tNONCOPYABLE(CParticleEmitterType);\t// reference member\npublic:\n\tCParticleEmitterType(const VfsPath& path, CParticleManager& manager);\n\nprivate:\n\tfriend class CModelParticleEmitter;\n\tfriend class CParticleEmitter;\n\tfriend class CParticleVarConstant;\n\tfriend class CParticleVarUniform;\n\tfriend class CParticleVarCopy;\n\tfriend class ParticleRenderer;\n\n\tenum\n\t{\n\t\tVAR_EMISSIONRATE,\n\t\tVAR_LIFETIME,\n\t\tVAR_POSITION_X,\n\t\tVAR_POSITION_Y,\n\t\tVAR_POSITION_Z,\n\t\tVAR_ANGLE,\n\t\tVAR_VELOCITY_X,\n\t\tVAR_VELOCITY_Y,\n\t\tVAR_VELOCITY_Z,\n\t\tVAR_VELOCITY_ANGLE,\n\t\tVAR_SIZE,\n\t\tVAR_SIZE_GROWTHRATE,\n\t\tVAR_COLOR_R,\n\t\tVAR_COLOR_G,\n\t\tVAR_COLOR_B,\n\t\tVAR__MAX\n\t};\n\n\tint GetVariableID(const std::string& name);\n\n\tbool LoadXML(const VfsPath& path);\n\n\t/**\n\t * Update the state of an emitter's particles, by a potentially long time @p dt.\n\t */\n\tvoid UpdateEmitter(CParticleEmitter& emitter, float dt);\n\n\t/**\n\t * Update the state of an emitter's particles, by a short time @p dt that can\n\t * be computed in a single step.\n\t */\n\tvoid UpdateEmitterStep(CParticleEmitter& emitter, float dt);\n\n\tCBoundingBoxAligned CalculateBounds(CVector3D emitterPos, CBoundingBoxAligned emittedBounds);\n\n\tCTexturePtr m_Texture;\n\n\tGLenum m_BlendEquation;\n\tGLenum m_BlendFuncSrc;\n\tGLenum m_BlendFuncDst;\n\tbool m_StartFull;\n\tbool m_UseRelativeVelocity;\n\t\n\tfloat m_MaxLifetime;\n\tsize_t m_MaxParticles;\n\tCBoundingBoxAligned m_MaxBounds;\n\n\ttypedef shared_ptr<IParticleVar> IParticleVarPtr;\n\tstd::vector<IParticleVarPtr> m_Variables;\n\n\ttypedef shared_ptr<IParticleEffector> IParticleEffectorPtr;\n\tstd::vector<IParticleEffectorPtr> m_Effectors;\n\n\tCParticleManager& m_Manager;\n};\n\ntypedef shared_ptr<CParticleEmitterType> CParticleEmitterTypePtr;\n\n#endif // INCLUDED_PARTICLEEMITTERTYPE\n"
  },
  {
    "path": "fpsgame/graphics/ParticleManager.cpp",
    "content": "/* Copyright (C) 2011 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"precompiled.h\"\n\n#include \"ParticleManager.h\"\n\n#include \"ps/Filesystem.h\"\n#include \"ps/Profile.h\"\n#include \"renderer/Scene.h\"\n\nstatic Status ReloadChangedFileCB(void* param, const VfsPath& path)\n{\n\treturn static_cast<CParticleManager*>(param)->ReloadChangedFile(path);\n}\n\nCParticleManager::CParticleManager() :\n\tm_CurrentTime(0.f)\n{\n\tRegisterFileReloadFunc(ReloadChangedFileCB, this);\n}\n\nCParticleManager::~CParticleManager()\n{\n\tUnregisterFileReloadFunc(ReloadChangedFileCB, this);\n}\n\nCParticleEmitterTypePtr CParticleManager::LoadEmitterType(const VfsPath& path)\n{\n\tboost::unordered_map<VfsPath, CParticleEmitterTypePtr>::iterator it = m_EmitterTypes.find(path);\n\tif (it != m_EmitterTypes.end())\n\t\treturn it->second;\n\n\tCParticleEmitterTypePtr emitterType(new CParticleEmitterType(path, *this));\n\tm_EmitterTypes[path] = emitterType;\n\treturn emitterType;\n}\n\nvoid CParticleManager::AddUnattachedEmitter(const CParticleEmitterPtr& emitter)\n{\n\tm_UnattachedEmitters.push_back(emitter);\n}\n\nvoid CParticleManager::ClearUnattachedEmitters()\n{\n\tm_UnattachedEmitters.clear();\n}\n\nvoid CParticleManager::Interpolate(const float simFrameLength)\n{\n\tm_CurrentTime += simFrameLength;\n}\n\nstruct EmitterHasNoParticles\n{\n\tbool operator()(const CParticleEmitterPtr& emitterPtr)\n\t{\n\t\tCParticleEmitter& emitter = *emitterPtr.get();\n\t\tfor (size_t i = 0; i < emitter.m_Particles.size(); ++i)\n\t\t{\n\t\t\tSParticle& p = emitter.m_Particles[i];\n\t\t\tif (p.age < p.maxAge)\n\t\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n};\n\nvoid CParticleManager::RenderSubmit(SceneCollector& collector, const CFrustum& UNUSED(frustum))\n{\n\tPROFILE(\"submit unattached particles\");\n\n\t// Delete any unattached emitters that have no particles left\n\tm_UnattachedEmitters.remove_if(EmitterHasNoParticles());\n\n\t// TODO: should do some frustum culling\n\n\tfor (std::list<CParticleEmitterPtr>::iterator it = m_UnattachedEmitters.begin(); it != m_UnattachedEmitters.end(); ++it)\n\t\tcollector.Submit(it->get());\n}\n\nStatus CParticleManager::ReloadChangedFile(const VfsPath& path)\n{\n\tm_EmitterTypes.erase(path);\n\treturn INFO::OK;\n}\n"
  },
  {
    "path": "fpsgame/graphics/ParticleManager.h",
    "content": "/* Copyright (C) 2011 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef INCLUDED_PARTICLEMANAGER\n#define INCLUDED_PARTICLEMANAGER\n\n#include \"graphics/ParticleEmitter.h\"\n#include \"graphics/ParticleEmitterType.h\"\n\n#include <boost/random/mersenne_twister.hpp>\n#include <boost/unordered_map.hpp>\n\nclass SceneCollector;\n\nclass CParticleManager\n{\npublic:\n\tCParticleManager();\n\t~CParticleManager();\n\n\tCParticleEmitterTypePtr LoadEmitterType(const VfsPath& path);\n\n\t/**\n\t * Tell the manager to handle rendering of an emitter that is no longer\n\t * attached to a unit.\n\t */\n\tvoid AddUnattachedEmitter(const CParticleEmitterPtr& emitter);\n\n\t/**\n\t * Delete unattached emitters if we don't wish to see them anymore (like in actor viewer)\n\t */\n\tvoid ClearUnattachedEmitters();\n\n\tvoid RenderSubmit(SceneCollector& collector, const CFrustum& frustum);\n\n\tvoid Interpolate(const float simFrameLength);\n\n\tfloat GetCurrentTime() const { return m_CurrentTime; }\n\n\tStatus ReloadChangedFile(const VfsPath& path);\n\n\t/// Random number generator shared between all particle emitters.\n\tboost::mt19937 m_RNG;\n\nprivate:\n\tfloat m_CurrentTime;\n\n\tstd::list<CParticleEmitterPtr> m_UnattachedEmitters;\n\n\tboost::unordered_map<VfsPath, CParticleEmitterTypePtr> m_EmitterTypes;\n};\n\n#endif // INCLUDED_PARTICLEMANAGER\n"
  },
  {
    "path": "fpsgame/graphics/Patch.cpp",
    "content": "/* Copyright (C) 2010 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n/*\n * A patch of terrain holding NxN MiniPatch tiles\n */\n\n#include \"precompiled.h\"\n\n#include \"Patch.h\"\n#include \"Terrain.h\"\n\n\n///////////////////////////////////////////////////////////////////////////////\n// CPatch constructor\nCPatch::CPatch()\n: m_Parent(0)\n{\n}\n\n///////////////////////////////////////////////////////////////////////////////\n// CPatch destructor\nCPatch::~CPatch()\n{\n\t\n}\n\n///////////////////////////////////////////////////////////////////////////////\n// Initialize: setup patch data\nvoid CPatch::Initialize(CTerrain* parent,ssize_t x,ssize_t z)\n{\n\tdelete m_RenderData;\n\tm_RenderData=0;\n\n\tm_Parent=parent;\n\tm_X=x;\n\tm_Z=z;\n\n\tInvalidateBounds();\n}\n\n///////////////////////////////////////////////////////////////////////////////\n// CalcBounds: calculating the bounds of this patch\nvoid CPatch::CalcBounds()\n{\n\tm_WorldBounds.SetEmpty();\n\n\tfor (ssize_t j=0;j<PATCH_SIZE+1;j++)\n\t{\n\t\tfor (ssize_t i=0;i<PATCH_SIZE+1;i++)\n\t\t{\n\t\t\tCVector3D pos;\n\t\t\tm_Parent->CalcPosition(m_X*PATCH_SIZE+i,m_Z*PATCH_SIZE+j,pos);\n\t\t\tm_WorldBounds+=pos;\n\t\t}\n\t}\n\n\t// If this a side patch, the sides go down to height 0, so add them\n\t// into the bounds\n\tif (GetSideFlags())\n\t\tm_WorldBounds[0].Y = std::min(m_WorldBounds[0].Y, 0.f);\n}\n\nint CPatch::GetSideFlags()\n{\n\tint flags = 0;\n\tif (m_X == 0)\n\t\tflags |= CPATCH_SIDE_NEGX;\n\tif (m_Z == 0)\n\t\tflags |= CPATCH_SIDE_NEGZ;\n\tif (m_X == m_Parent->GetPatchesPerSide()-1)\n\t\tflags |= CPATCH_SIDE_POSX;\n\tif (m_Z == m_Parent->GetPatchesPerSide()-1)\n\t\tflags |= CPATCH_SIDE_POSZ;\n\treturn flags;\n}\n"
  },
  {
    "path": "fpsgame/graphics/Patch.h",
    "content": "/* Copyright (C) 2009 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n/*\n * A patch of terrain holding NxN MiniPatch tiles\n */\n\n#ifndef INCLUDED_PATCH\n#define INCLUDED_PATCH\n\n#include \"MiniPatch.h\"\n#include \"RenderableObject.h\"\n\nclass CTerrain;\n\n///////////////////////////////////////////////////////////////////////////////\n// Terrain Constants:\n//\n// PATCH_SIZE: number of tiles in each patch\nconst ssize_t PATCH_SIZE = 16;\n\n// Flags for whether the patch should be drawn with a solid plane\n// on each side\nenum CPatchSideFlags\n{\n\tCPATCH_SIDE_NEGX = (1 << 0),\n\tCPATCH_SIDE_POSX = (1 << 1),\n\tCPATCH_SIDE_NEGZ = (1 << 2),\n\tCPATCH_SIDE_POSZ = (1 << 3),\n};\n\n///////////////////////////////////////////////////////////////////////////////\n// CPatch: a single terrain patch, PATCH_SIZE tiles square\nclass CPatch : public CRenderableObject\n{\npublic:\n\t// constructor\n\tCPatch();\n\t// destructor\n\t~CPatch();\n\n\t// initialize the patch\n\tvoid Initialize(CTerrain* parent,ssize_t x,ssize_t z);\n\t// calculate and store bounds of this patch\n\tvoid CalcBounds();\n\npublic:\n\t// minipatches (tiles) making up the patch\n\tCMiniPatch m_MiniPatches[PATCH_SIZE][PATCH_SIZE];\n\t// position of patch in parent terrain grid\n\tint m_X,m_Z;\n\t// parent terrain\n\tCTerrain* m_Parent;\n\n\tint GetSideFlags();\n};\n\n\n#endif\n\n\n"
  },
  {
    "path": "fpsgame/graphics/RenderableObject.h",
    "content": "/* Copyright (C) 2012 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n/*\n * Base class for renderable objects\n */\n\n#ifndef INCLUDED_RENDERABLEOBJECT\n#define INCLUDED_RENDERABLEOBJECT\n\n\n#include \"maths/BoundingBoxAligned.h\"\n#include \"maths/Matrix3D.h\"\n\n\n// dirty flags - used as notification to the renderer that some bit of data\n// need updating\n#define RENDERDATA_UPDATE_VERTICES\t\t(1<<1)\n#define RENDERDATA_UPDATE_INDICES\t\t(1<<2)\n#define RENDERDATA_UPDATE_COLOR\t\t\t(1<<4)\n\n\n///////////////////////////////////////////////////////////////////////////////\n// CRenderData: base class of all the renderer's renderdata classes - the\n// derived class stores necessary information for rendering an object of a\n// particular type\nclass CRenderData\n{\npublic:\n\tCRenderData() : m_UpdateFlags(0) {}\n\tvirtual ~CRenderData() {}\n\n\tint m_UpdateFlags;\n};\n\n///////////////////////////////////////////////////////////////////////////////\n// CRenderableObject: base class of all renderable objects - patches, models,\n// sprites, etc; stores position and bound information, and a pointer to\n// some renderdata necessary for the renderer to actually render it\nclass CRenderableObject\n{\n\tNONCOPYABLE(CRenderableObject);\n\npublic:\n\t// constructor\n\tCRenderableObject() : m_RenderData(0), m_BoundsValid(false)\n\t{\n\t\tm_Transform.SetIdentity();\n\t}\n\t// destructor\n\tvirtual ~CRenderableObject() { delete m_RenderData; }\n\n\t// set object transform\n\tvirtual void SetTransform(const CMatrix3D& transform)\n\t{\n\t\tif (m_Transform == transform)\n\t\t\treturn;\n\t\t// store transform, calculate inverse\n\t\tm_Transform=transform;\n\t\tm_Transform.GetInverse(m_InvTransform);\n\t\t// normal recalculation likely required on transform change; flag it\n\t\tSetDirty(RENDERDATA_UPDATE_VERTICES);\n\t\t// need to rebuild world space bounds\n\t\tInvalidateBounds();\n\t}\n\t// get object to world space transform\n\tconst CMatrix3D& GetTransform() const { return m_Transform; }\n\t// get world to object space transform\n\tconst CMatrix3D& GetInvTransform() const { return m_InvTransform; }\n\n\t// mark some part of the renderdata as dirty, and requiring\n\t// an update on next render\n\tvoid SetDirty(u32 dirtyflags)\n\t{\n\t\tif (m_RenderData)\n\t\t\tm_RenderData->m_UpdateFlags |= dirtyflags;\n\t}\n\n\t/**\n\t * (Re)calculates and stores any bounds or bound-dependent data for this object. At this abstraction level, this is only the world-space \n\t * bounds stored in @ref m_WorldBounds; subclasses may use this method to (re)compute additional bounds if necessary, or any data that \n\t * depends on the bounds. Whenever bound-dependent data is requested through a public interface, @ref RecalculateBoundsIfNecessary should \n\t * be called first to ensure bound correctness, which will in turn call this method if it turns out that they're outdated.\n\t * \n\t * @see m_BoundsValid\n\t * @see RecalculateBoundsIfNecessary\n\t */\n\tvirtual void CalcBounds() = 0;\n\n\t/// Returns the world-space axis-aligned bounds of this object.\n\tconst CBoundingBoxAligned& GetWorldBounds()\n\t{\n\t\tRecalculateBoundsIfNecessary();\n\t\treturn m_WorldBounds;\n\t}\n\n\t/**\n\t * Marks the bounds as invalid. This will trigger @ref RecalculateBoundsIfNecessary to recompute any bound-related data the next time\n\t * any bound-related data is requested through a public interface -- at least, if you've made sure to call it before returning the\n\t * stored data.\n\t */\n\tvirtual void InvalidateBounds() { m_BoundsValid = false; }\n\n\t// Set the object renderdata and free previous renderdata, if any.\n\tvoid SetRenderData(CRenderData* renderdata)\n\t{\n\t\tdelete m_RenderData;\n\t\tm_RenderData = renderdata;\n\t}\n\n\t/// Return object renderdata - can be null if renderer hasn't yet created the renderdata\n\tCRenderData* GetRenderData() { return m_RenderData; }\n\nprotected:\n\t/// Factored out so subclasses don't need to repeat this if they want to add additional getters for bounds-related methods\n\t/// (since they'll have to make sure to recalc the bounds if necessary before they return it).\n\tvoid RecalculateBoundsIfNecessary()\n\t{\n\t\tif (!m_BoundsValid) {\n\t\t\tCalcBounds();\n\t\t\tm_BoundsValid = true;\n\t\t}\n\t}\n\nprotected:\n\t/// World-space bounds of this object\n\tCBoundingBoxAligned m_WorldBounds;\n\t// local->world space transform\n\tCMatrix3D m_Transform;\n\t// world->local space transform\n\tCMatrix3D m_InvTransform;\n\t// object renderdata\n\tCRenderData* m_RenderData;\n\n\t/**\n\t * Remembers whether any bounds need to be recalculated. Subclasses that add any data that depends on the bounds should \n\t * take care to consider the validity of the bounds and recalculate their data when necessary -- overriding @ref CalcBounds\n\t * to do so would be a good idea, since it's already set up to be called by @ref RecalculateBoundsIfNecessary whenever the\n\t * bounds are marked as invalid. The latter should then be called before returning any bounds or bounds-derived data through\n\t * a public interface (see the implementation of @ref GetWorldBounds for an example).\n\t * \n\t * @see CalcBounds\n\t * @see InvalidateBounds\n\t * @see RecalculateBoundsIfNecessary\n\t */\n\tbool m_BoundsValid;\n};\n\n#endif\n"
  },
  {
    "path": "fpsgame/graphics/SColor.h",
    "content": "#ifndef INCLUDED_SCOLOR\n#define INCLUDED_SCOLOR\n\nstruct SColor3ub\n{\n\tu8 R;\n\tu8 G;\n\tu8 B;\n};\n\nstruct SColor4ub\n{\n\tu8 R;\n\tu8 G;\n\tu8 B;\n\tu8 A;\n\n\tSColor4ub() { }\n\tSColor4ub(u8 _r, u8 _g, u8 _b, u8 _a) : R(_r), G(_g), B(_b), A(_a) { }\n};\n\n#endif"
  },
  {
    "path": "fpsgame/graphics/ShaderDefines.cpp",
    "content": "/* Copyright (C) 2015 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"precompiled.h\"\n\n#include \"ShaderDefines.h\"\n\n#include \"graphics/ShaderProgram.h\"\n#include \"maths/Vector4D.h\"\n#include \"ps/ThreadUtil.h\"\n\n#include <sstream>\n\nsize_t hash_value(const CVector4D& v)\n{\n\tsize_t hash = 0;\n\tboost::hash_combine(hash, v.X);\n\tboost::hash_combine(hash, v.Y);\n\tboost::hash_combine(hash, v.Z);\n\tboost::hash_combine(hash, v.W);\n\treturn hash;\n}\n\nsize_t hash_value(const CShaderParams<CStrIntern>::SItems& items)\n{\n\treturn items.hash;\n}\n\nsize_t hash_value(const CShaderParams<CVector4D>::SItems& items)\n{\n\treturn items.hash;\n}\n\nbool operator==(const CShaderParams<CStrIntern>::SItems& a, const CShaderParams<CStrIntern>::SItems& b)\n{\n\treturn a.items == b.items;\n}\n\nbool operator==(const CShaderParams<CVector4D>::SItems& a, const CShaderParams<CVector4D>::SItems& b)\n{\n\treturn a.items == b.items;\n}\n\ntemplate<typename value_t>\nstruct ItemNameCmp\n{\n\ttypedef typename CShaderParams<value_t>::SItems::Item Item;\n\n\ttypedef Item first_argument_type;\n\ttypedef Item second_argument_type;\n\tbool operator()(const Item& a, const Item& b) const\n\t{\n\t\treturn a.first < b.first;\n\t}\n};\n\ntemplate<typename value_t>\nstruct ItemNameGeq\n{\n\ttypedef typename CShaderParams<value_t>::SItems::Item Item;\n\n\tbool operator()(const Item& a, const Item& b) const\n\t{\n\t\treturn !(b.first < a.first);\n\t}\n};\n\ntemplate<typename value_t>\ntypename CShaderParams<value_t>::SItems* CShaderParams<value_t>::GetInterned(const SItems& items)\n{\n\tENSURE(ThreadUtil::IsMainThread()); // s_InternedItems is not thread-safe\n\n\ttypename InternedItems_t::iterator it = s_InternedItems.find(items);\n\tif (it != s_InternedItems.end())\n\t\treturn it->second.get();\n\n\t// Sanity test: the items list is meant to be sorted by name.\n\t// This is a reasonable place to verify that, since this will be called once per distinct SItems.\n\ttypedef ItemNameCmp<value_t> Cmp;\n\tENSURE(std::adjacent_find(items.items.begin(), items.items.end(), std::binary_negate<Cmp>(Cmp())) == items.items.end());\n\n\tshared_ptr<SItems> ptr(new SItems(items));\n\ts_InternedItems.insert(std::make_pair(items, ptr));\n\treturn ptr.get();\n}\n\ntemplate<typename value_t>\nCShaderParams<value_t>::CShaderParams()\n{\n\t*this = s_Empty;\n}\n\ntemplate<typename value_t>\nCShaderParams<value_t>::CShaderParams(SItems* items) : m_Items(items)\n{\n}\n\ntemplate<typename value_t>\nCShaderParams<value_t> CShaderParams<value_t>::CreateEmpty()\n{\n\tSItems items;\n\titems.RecalcHash();\n\treturn CShaderParams(GetInterned(items));\n}\n\ntemplate<typename value_t>\nvoid CShaderParams<value_t>::Set(CStrIntern name, const value_t& value)\n{\n\tSItems items = *m_Items;\n\n\ttypename SItems::Item addedItem = std::make_pair(name, value);\n\n\t// Add the new item in a way that preserves the sortedness and uniqueness of item names\n\tfor (typename std::vector<typename SItems::Item>::iterator it = items.items.begin(); ; ++it)\n\t{\n\t\tif (it == items.items.end() || addedItem.first < it->first)\n\t\t{\n\t\t\titems.items.insert(it, addedItem);\n\t\t\tbreak;\n\t\t}\n\t\telse if (addedItem.first == it->first)\n\t\t{\n\t\t\tit->second = addedItem.second;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\titems.RecalcHash();\n\tm_Items = GetInterned(items);\n}\n\ntemplate<typename value_t>\nvoid CShaderParams<value_t>::SetMany(const CShaderParams& params)\n{\n\tSItems items;\n\t// set_union merges the two sorted lists into a new sorted list;\n\t// if two items are equivalent (i.e. equal names, possibly different values)\n\t// then the one from the first list is kept\n\tstd::set_union(\n\t\tparams.m_Items->items.begin(), params.m_Items->items.end(),\n\t\tm_Items->items.begin(), m_Items->items.end(),\n\t\tstd::inserter(items.items, items.items.begin()),\n\t\tItemNameCmp<value_t>());\n\titems.RecalcHash();\n\tm_Items = GetInterned(items);\n}\n\ntemplate<typename value_t>\nstd::map<CStrIntern, value_t> CShaderParams<value_t>::GetMap() const\n{\n\tstd::map<CStrIntern, value_t> ret;\n\tfor (size_t i = 0; i < m_Items->items.size(); ++i)\n\t\tret[m_Items->items[i].first] = m_Items->items[i].second;\n\treturn ret;\n}\n\ntemplate<typename value_t>\nsize_t CShaderParams<value_t>::GetHash() const\n{\n\treturn m_Items->hash;\n}\n\ntemplate<typename value_t>\nvoid CShaderParams<value_t>::SItems::RecalcHash()\n{\n\tsize_t h = 0;\n\tfor (size_t i = 0; i < items.size(); ++i)\n\t{\n\t\tboost::hash_combine(h, items[i].first);\n\t\tboost::hash_combine(h, items[i].second);\n\t}\n\thash = h;\n}\n\n\nvoid CShaderDefines::Add(CStrIntern name, CStrIntern value)\n{\n\tSet(name, value);\n}\n\nint CShaderDefines::GetInt(const char* name) const\n{\n\tCStrIntern nameIntern(name);\n\tfor (size_t i = 0; i < m_Items->items.size(); ++i)\n\t{\n\t\tif (m_Items->items[i].first == nameIntern)\n\t\t{\n\t\t\tint ret;\n\t\t\tstd::stringstream str(m_Items->items[i].second.c_str());\n\t\t\tstr >> ret;\n\t\t\treturn ret;\n\t\t}\n\t}\n\treturn 0;\n}\n\n\nvoid CShaderUniforms::Add(const char* name, const CVector4D& value)\n{\n\tSet(CStrIntern(name), value);\n}\n\nCVector4D CShaderUniforms::GetVector(const char* name) const\n{\n\tCStrIntern nameIntern(name);\n\tfor (size_t i = 0; i < m_Items->items.size(); ++i)\n\t{\n\t\tif (m_Items->items[i].first == nameIntern)\n\t\t{\n\t\t\treturn m_Items->items[i].second;\n\t\t}\n\t}\n\treturn CVector4D();\n}\n\nvoid CShaderUniforms::BindUniforms(const CShaderProgramPtr& shader) const\n{\n\tconst std::vector<SItems::Item>& items = m_Items->items;\n\tfor (size_t i = 0; i < items.size(); ++i)\n\t{\n\t\tCShaderProgram::Binding binding = shader->GetUniformBinding(items[i].first);\n\t\tif (binding.Active())\n\t\t{\n\t\t\tCVector4D v = items[i].second;\n\t\t\tshader->Uniform(binding, v.X, v.Y, v.Z, v.W);\n\t\t}\n\t}\n}\n\nvoid CShaderRenderQueries::Add(const char* name)\n{\n\tif (name == CStr(\"sim_time\"))\n\t{\n\t\tm_Items.emplace_back(RQUERY_TIME, CStrIntern(name));\n\t}\n\telse if (name == CStr(\"water_tex\"))\n\t{\n\t\tm_Items.emplace_back(RQUERY_WATER_TEX, CStrIntern(name));\n\t}\n\telse if (name == CStr(\"sky_cube\"))\n\t{\n\t\tm_Items.emplace_back(RQUERY_SKY_CUBE, CStrIntern(name));\n\t}\n}\n\nvoid CShaderConditionalDefines::Add(const char* defname, const char* defvalue, int type, std::vector<float> &args)\n{\n\tCondDefine cd;\n\tcd.m_DefName = CStrIntern(defname);\n\tcd.m_DefValue = CStrIntern(defvalue);\n\tcd.m_CondArgs = args;\n\tcd.m_CondType = type;\n\t\n\tm_Defines.push_back(cd);\n}\n\n\n// Explicit instantiations:\n\ntemplate<> CShaderParams<CStrIntern>::InternedItems_t CShaderParams<CStrIntern>::s_InternedItems = CShaderParams<CStrIntern>::InternedItems_t();\ntemplate<> CShaderParams<CVector4D>::InternedItems_t CShaderParams<CVector4D>::s_InternedItems = CShaderParams<CVector4D>::InternedItems_t();\n\ntemplate<> CShaderParams<CStrIntern> CShaderParams<CStrIntern>::s_Empty = CShaderParams<CStrIntern>::CreateEmpty();\ntemplate<> CShaderParams<CVector4D> CShaderParams<CVector4D>::s_Empty = CShaderParams<CVector4D>::CreateEmpty();\n\ntemplate class CShaderParams<CStrIntern>;\ntemplate class CShaderParams<CVector4D>;\n"
  },
  {
    "path": "fpsgame/graphics/ShaderDefines.h",
    "content": "/* Copyright (C) 2012 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef INCLUDED_SHADERDEFINES\n#define INCLUDED_SHADERDEFINES\n\n#include \"graphics/ShaderProgramPtr.h\"\n#include \"ps/CStr.h\"\n#include \"ps/CStrIntern.h\"\n\n#include <boost/unordered_map.hpp>\n\nclass CVector4D;\n\n/**\n * Represents a mapping of name strings to value, for use with\n * CShaderDefines (values are strings) and CShaderUniforms (values are vec4s).\n * \n * Stored as interned vectors of name-value pairs, to support high performance\n * comparison operators.\n *\n * Not thread-safe - must only be used from the main thread.\n */\ntemplate<typename value_t>\nclass CShaderParams\n{\npublic:\n\t/**\n\t * Create an empty map of defines.\n\t */\n\tCShaderParams();\n\n\t/**\n\t * Add a name and associated value to the map of parameters.\n\t * If the name is already defined, its value will be replaced.\n\t */\n\tvoid Set(CStrIntern name, const value_t& value);\n\n\t/**\n\t * Add all the names and values from another set of parameters.\n\t * If any name is already defined in this object, its value will be replaced.\n\t */\n\tvoid SetMany(const CShaderParams& params);\n\n\t/**\n\t * Return a copy of the current name/value mapping.\n\t */\n\tstd::map<CStrIntern, value_t> GetMap() const;\n\n\t/**\n\t * Return a hash of the current mapping.\n\t */\n\tsize_t GetHash() const;\n\n\t/**\n\t * Compare with some arbitrary total order.\n\t * The order may be different each time the application is run\n\t * (it is based on interned memory addresses).\n\t */\n\tbool operator<(const CShaderParams& b) const\n\t{\n\t\treturn m_Items < b.m_Items;\n\t}\n\n\t/**\n\t * Fast equality comparison.\n\t */\n\tbool operator==(const CShaderParams& b) const\n\t{\n\t\treturn m_Items == b.m_Items;\n\t}\n\n\t/**\n\t * Fast inequality comparison.\n\t */\n\tbool operator!=(const CShaderParams& b) const\n\t{\n\t\treturn m_Items != b.m_Items;\n\t}\n\n\tstruct SItems\n\t{\n\t\t// Name/value pair\n\t\ttypedef std::pair<CStrIntern, value_t> Item;\n\t\t\n\t\t// Sorted by name; no duplicated names\n\t\tstd::vector<Item> items;\n\t\t\n\t\tsize_t hash;\n\t\t\n\t\tvoid RecalcHash();\n\t};\n\nprotected:\n \tSItems* m_Items; // interned value\n\nprivate:\n\ttypedef boost::unordered_map<SItems, shared_ptr<SItems> > InternedItems_t;\n\tstatic InternedItems_t s_InternedItems;\n\n\t/**\n\t * Returns a pointer to an SItems equal to @p items.\n\t * The pointer will be valid forever, and the same pointer will be returned\n\t * for any subsequent requests for an equal items list.\n\t */\n\tstatic SItems* GetInterned(const SItems& items);\n\n\tCShaderParams(SItems* items);\n\tstatic CShaderParams CreateEmpty();\n\tstatic CShaderParams s_Empty;\n};\n\n/**\n * Represents a mapping of name strings to value strings, for use with\n * \\#if and \\#ifdef and similar conditionals in shaders.\n *\n * Not thread-safe - must only be used from the main thread.\n */\nclass CShaderDefines : public CShaderParams<CStrIntern>\n{\npublic:\n\t/**\n\t * Add a name and associated value to the map of defines.\n\t * If the name is already defined, its value will be replaced.\n\t */\n\tvoid Add(CStrIntern name, CStrIntern value);\n\n\t/**\n\t * Return the value for the given name as an integer, or 0 if not defined.\n\t */\n\tint GetInt(const char* name) const;\n};\n\n/**\n * Represents a mapping of name strings to value CVector4Ds, for use with\n * uniforms in shaders.\n *\n * Not thread-safe - must only be used from the main thread.\n */\nclass CShaderUniforms : public CShaderParams<CVector4D>\n{\npublic:\n\t/**\n\t * Add a name and associated value to the map of uniforms.\n\t * If the name is already defined, its value will be replaced.\n\t */\n\tvoid Add(const char* name, const CVector4D& value);\n\n\t/**\n\t * Return the value for the given name, or (0,0,0,0) if not defined.\n\t */\n\tCVector4D GetVector(const char* name) const;\n\n\t/**\n\t * Bind the collection of uniforms onto the given shader.\n\t */\n\tvoid BindUniforms(const CShaderProgramPtr& shader) const;\n};\n\n// Add here the types of queries we can make in the renderer\nenum RENDER_QUERIES\n{\n\tRQUERY_TIME,\n\tRQUERY_WATER_TEX,\n\tRQUERY_SKY_CUBE\n};\n\n/**\n * Uniform values that need to be evaluated in the renderer.\n * \n * Not thread-safe - must only be used from the main thread.\n */\nclass CShaderRenderQueries\n{\npublic:\n\ttypedef std::pair<int, CStrIntern> RenderQuery;\n\t\n\tvoid Add(const char* name);\n\tsize_t GetSize() const { return m_Items.size(); }\n\tRenderQuery GetItem(size_t i) const { return m_Items[i]; }\nprivate:\n\tstd::vector<RenderQuery> m_Items;\n};\n\n\nenum DEFINE_CONDITION_TYPES\n{\n\tDCOND_DISTANCE\n};\n\nclass CShaderConditionalDefines\n{\npublic:\n\tstruct CondDefine\n\t{\n\t\tCStrIntern m_DefName;\n\t\tCStrIntern m_DefValue;\n\t\tint m_CondType;\n\t\tstd::vector<float> m_CondArgs;\n\t};\n\t\n\tvoid Add(const char* defname, const char* defvalue, int type, std::vector<float> &args);\n\tsize_t GetSize() const { return m_Defines.size(); }\n\tconst CondDefine& GetItem(size_t i) const { return m_Defines[i]; }\n\t\nprivate:\n\tstd::vector<CondDefine> m_Defines;\n};\n\n#endif // INCLUDED_SHADERDEFINES\n"
  },
  {
    "path": "fpsgame/graphics/ShaderManager.cpp",
    "content": "/* Copyright (C) 2015 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"precompiled.h\"\n\n#include \"ShaderManager.h\"\n\n#include \"graphics/ShaderTechnique.h\"\n#include \"lib/config2.h\"\n#include \"lib/timer.h\"\n#include \"lib/utf8.h\"\n#include \"ps/CLogger.h\"\n#include \"ps/CStrIntern.h\"\n#include \"ps/Filesystem.h\"\n#include \"ps/PreprocessorWrapper.h\"\n#include \"ps/Profile.h\"\n#if USE_SHADER_XML_VALIDATION\n# include \"ps/XML/RelaxNG.h\"\n#endif\n#include \"ps/XML/Xeromyces.h\"\n#include \"ps/XML/XMLWriter.h\"\n#include \"renderer/Renderer.h\"\n\nTIMER_ADD_CLIENT(tc_ShaderValidation);\n\nCShaderManager::CShaderManager()\n{\n#if USE_SHADER_XML_VALIDATION\n\t{\n\t\tTIMER_ACCRUE(tc_ShaderValidation);\n\n\t\tif (!CXeromyces::AddValidator(g_VFS, \"shader\", \"shaders/program.rng\"))\n\t\t\tLOGERROR(\"CShaderManager: failed to load grammar shaders/program.rng\");\n\t}\n#endif\n\n\t// Allow hotloading of textures\n\tRegisterFileReloadFunc(ReloadChangedFileCB, this);\n}\n\nCShaderManager::~CShaderManager()\n{\n\tUnregisterFileReloadFunc(ReloadChangedFileCB, this);\n}\n\nCShaderProgramPtr CShaderManager::LoadProgram(const char* name, const CShaderDefines& defines)\n{\n\tCacheKey key = { name, defines };\n\tstd::map<CacheKey, CShaderProgramPtr>::iterator it = m_ProgramCache.find(key);\n\tif (it != m_ProgramCache.end())\n\t\treturn it->second;\n\n\tCShaderProgramPtr program;\n\tif (!NewProgram(name, defines, program))\n\t{\n\t\tLOGERROR(\"Failed to load shader '%s'\", name);\n\t\tprogram = CShaderProgramPtr();\n\t}\n\n\tm_ProgramCache[key] = program;\n\treturn program;\n}\n\nstatic GLenum ParseAttribSemantics(const CStr& str)\n{\n\t// Map known semantics onto the attribute locations documented by NVIDIA\n\tif (str == \"gl_Vertex\") return 0;\n\tif (str == \"gl_Normal\") return 2;\n\tif (str == \"gl_Color\") return 3;\n\tif (str == \"gl_SecondaryColor\") return 4;\n\tif (str == \"gl_FogCoord\") return 5;\n\tif (str == \"gl_MultiTexCoord0\") return 8;\n\tif (str == \"gl_MultiTexCoord1\") return 9;\n\tif (str == \"gl_MultiTexCoord2\") return 10;\n\tif (str == \"gl_MultiTexCoord3\") return 11;\n\tif (str == \"gl_MultiTexCoord4\") return 12;\n\tif (str == \"gl_MultiTexCoord5\") return 13;\n\tif (str == \"gl_MultiTexCoord6\") return 14;\n\tif (str == \"gl_MultiTexCoord7\") return 15;\n\n\t// Define some arbitrary names for user-defined attribute locations\n\t// that won't conflict with any standard semantics\n\tif (str == \"CustomAttribute0\") return 1;\n\tif (str == \"CustomAttribute1\") return 6;\n\tif (str == \"CustomAttribute2\") return 7;\n\n\tdebug_warn(\"Invalid attribute semantics\");\n\treturn 0;\n}\n\nbool CShaderManager::NewProgram(const char* name, const CShaderDefines& baseDefines, CShaderProgramPtr& program)\n{\n\tPROFILE2(\"loading shader\");\n\tPROFILE2_ATTR(\"name: %s\", name);\n\n\tif (strncmp(name, \"fixed:\", 6) == 0)\n\t{\n\t\tprogram = CShaderProgramPtr(CShaderProgram::ConstructFFP(name+6, baseDefines));\n\t\tif (!program)\n\t\t\treturn false;\n\t\tprogram->Reload();\n\t\treturn true;\n\t}\n\n\tVfsPath xmlFilename = L\"shaders/\" + wstring_from_utf8(name) + L\".xml\";\n\n\tCXeromyces XeroFile;\n\tPSRETURN ret = XeroFile.Load(g_VFS, xmlFilename);\n\tif (ret != PSRETURN_OK)\n\t\treturn false;\n\n#if USE_SHADER_XML_VALIDATION\n\t{\n\t\tTIMER_ACCRUE(tc_ShaderValidation);\n\n\t\t// Serialize the XMB data and pass it to the validator\n\t\tXML_Start();\n\t\tXML_SetPrettyPrint(false);\n\t\tXML_WriteXMB(XeroFile);\n\t\tbool ok = CXeromyces::ValidateEncoded(\"shader\", wstring_from_utf8(name), XML_GetOutput());\n\t\tif (!ok)\n\t\t\treturn false;\n\t}\n#endif\n\n\t// Define all the elements and attributes used in the XML file\n#define EL(x) int el_##x = XeroFile.GetElementID(#x)\n#define AT(x) int at_##x = XeroFile.GetAttributeID(#x)\n\tEL(attrib);\n\tEL(define);\n\tEL(fragment);\n\tEL(stream);\n\tEL(uniform);\n\tEL(vertex);\n\tAT(file);\n\tAT(if);\n\tAT(loc);\n\tAT(name);\n\tAT(semantics);\n\tAT(type);\n\tAT(value);\n#undef AT\n#undef EL\n\n\tCPreprocessorWrapper preprocessor;\n\tpreprocessor.AddDefines(baseDefines);\n\n\tXMBElement Root = XeroFile.GetRoot();\n\n\tbool isGLSL = (Root.GetAttributes().GetNamedItem(at_type) == \"glsl\");\n\tVfsPath vertexFile;\n\tVfsPath fragmentFile;\n\tCShaderDefines defines = baseDefines;\n\tstd::map<CStrIntern, int> vertexUniforms;\n\tstd::map<CStrIntern, CShaderProgram::frag_index_pair_t> fragmentUniforms;\n\tstd::map<CStrIntern, int> vertexAttribs;\n\tint streamFlags = 0;\n\n\tXERO_ITER_EL(Root, Child)\n\t{\n\t\tif (Child.GetNodeName() == el_define)\n\t\t{\n\t\t\tdefines.Add(CStrIntern(Child.GetAttributes().GetNamedItem(at_name)), CStrIntern(Child.GetAttributes().GetNamedItem(at_value)));\n\t\t}\n\t\telse if (Child.GetNodeName() == el_vertex)\n\t\t{\n\t\t\tvertexFile = L\"shaders/\" + Child.GetAttributes().GetNamedItem(at_file).FromUTF8();\n\n\t\t\tXERO_ITER_EL(Child, Param)\n\t\t\t{\n\t\t\t\tXMBAttributeList Attrs = Param.GetAttributes();\n\n\t\t\t\tCStr cond = Attrs.GetNamedItem(at_if);\n\t\t\t\tif (!cond.empty() && !preprocessor.TestConditional(cond))\n\t\t\t\t\tcontinue;\n\n\t\t\t\tif (Param.GetNodeName() == el_uniform)\n\t\t\t\t{\n\t\t\t\t\tvertexUniforms[CStrIntern(Attrs.GetNamedItem(at_name))] = Attrs.GetNamedItem(at_loc).ToInt();\n\t\t\t\t}\n\t\t\t\telse if (Param.GetNodeName() == el_stream)\n\t\t\t\t{\n\t\t\t\t\tCStr StreamName = Attrs.GetNamedItem(at_name);\n\t\t\t\t\tif (StreamName == \"pos\")\n\t\t\t\t\t\tstreamFlags |= STREAM_POS;\n\t\t\t\t\telse if (StreamName == \"normal\")\n\t\t\t\t\t\tstreamFlags |= STREAM_NORMAL;\n\t\t\t\t\telse if (StreamName == \"color\")\n\t\t\t\t\t\tstreamFlags |= STREAM_COLOR;\n\t\t\t\t\telse if (StreamName == \"uv0\")\n\t\t\t\t\t\tstreamFlags |= STREAM_UV0;\n\t\t\t\t\telse if (StreamName == \"uv1\")\n\t\t\t\t\t\tstreamFlags |= STREAM_UV1;\n\t\t\t\t\telse if (StreamName == \"uv2\")\n\t\t\t\t\t\tstreamFlags |= STREAM_UV2;\n\t\t\t\t\telse if (StreamName == \"uv3\")\n\t\t\t\t\t\tstreamFlags |= STREAM_UV3;\n\t\t\t\t}\n\t\t\t\telse if (Param.GetNodeName() == el_attrib)\n\t\t\t\t{\n\t\t\t\t\tint attribLoc = ParseAttribSemantics(Attrs.GetNamedItem(at_semantics));\n\t\t\t\t\tvertexAttribs[CStrIntern(Attrs.GetNamedItem(at_name))] = attribLoc;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (Child.GetNodeName() == el_fragment)\n\t\t{\n\t\t\tfragmentFile = L\"shaders/\" + Child.GetAttributes().GetNamedItem(at_file).FromUTF8();\n\n\t\t\tXERO_ITER_EL(Child, Param)\n\t\t\t{\n\t\t\t\tXMBAttributeList Attrs = Param.GetAttributes();\n\n\t\t\t\tCStr cond = Attrs.GetNamedItem(at_if);\n\t\t\t\tif (!cond.empty() && !preprocessor.TestConditional(cond))\n\t\t\t\t\tcontinue;\n\n\t\t\t\tif (Param.GetNodeName() == el_uniform)\n\t\t\t\t{\n\t\t\t\t\t// A somewhat incomplete listing, missing \"shadow\" and \"rect\" versions\n\t\t\t\t\t// which are interpreted as 2D (NB: our shadowmaps may change\n\t\t\t\t\t// type based on user config).\n\t\t\t\t\tGLenum type = GL_TEXTURE_2D;\n\t\t\t\t\tCStr t = Attrs.GetNamedItem(at_type);\n\t\t\t\t\tif (t == \"sampler1D\")\n#if CONFIG2_GLES\n\t\t\t\t\t\tdebug_warn(L\"sampler1D not implemented on GLES\");\n#else\n\t\t\t\t\t\ttype = GL_TEXTURE_1D;\n#endif\n\t\t\t\t\telse if (t == \"sampler2D\")\n\t\t\t\t\t\ttype = GL_TEXTURE_2D;\n\t\t\t\t\telse if (t == \"sampler3D\")\n#if CONFIG2_GLES\n\t\t\t\t\t\tdebug_warn(L\"sampler3D not implemented on GLES\");\n#else\n\t\t\t\t\t\ttype = GL_TEXTURE_3D;\n#endif\n\t\t\t\t\telse if (t == \"samplerCube\")\n\t\t\t\t\t\ttype = GL_TEXTURE_CUBE_MAP;\n\t\t\t\t\t\n\t\t\t\t\tfragmentUniforms[CStrIntern(Attrs.GetNamedItem(at_name))] = \n\t\t\t\t\t\tstd::make_pair(Attrs.GetNamedItem(at_loc).ToInt(), type);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif (isGLSL)\n\t\tprogram = CShaderProgramPtr(CShaderProgram::ConstructGLSL(vertexFile, fragmentFile, defines, vertexAttribs, streamFlags));\n\telse\n\t\tprogram = CShaderProgramPtr(CShaderProgram::ConstructARB(vertexFile, fragmentFile, defines, vertexUniforms, fragmentUniforms, streamFlags));\n\n\tprogram->Reload();\n\n//\tm_HotloadFiles[xmlFilename].insert(program); // TODO: should reload somehow when the XML changes\n\tm_HotloadFiles[vertexFile].insert(program);\n\tm_HotloadFiles[fragmentFile].insert(program);\n\n\treturn true;\n}\n\nstatic GLenum ParseComparisonFunc(const CStr& str)\n{\n\tif (str == \"never\")\n\t\treturn GL_NEVER;\n\tif (str == \"always\")\n\t\treturn GL_ALWAYS;\n\tif (str == \"less\")\n\t\treturn GL_LESS;\n\tif (str == \"lequal\")\n\t\treturn GL_LEQUAL;\n\tif (str == \"equal\")\n\t\treturn GL_EQUAL;\n\tif (str == \"gequal\")\n\t\treturn GL_GEQUAL;\n\tif (str == \"greater\")\n\t\treturn GL_GREATER;\n\tif (str == \"notequal\")\n\t\treturn GL_NOTEQUAL;\n\tdebug_warn(\"Invalid comparison func\");\n\treturn GL_ALWAYS;\n}\n\nstatic GLenum ParseBlendFunc(const CStr& str)\n{\n\tif (str == \"zero\")\n\t\treturn GL_ZERO;\n\tif (str == \"one\")\n\t\treturn GL_ONE;\n\tif (str == \"src_color\")\n\t\treturn GL_SRC_COLOR;\n\tif (str == \"one_minus_src_color\")\n\t\treturn GL_ONE_MINUS_SRC_COLOR;\n\tif (str == \"dst_color\")\n\t\treturn GL_DST_COLOR;\n\tif (str == \"one_minus_dst_color\")\n\t\treturn GL_ONE_MINUS_DST_COLOR;\n\tif (str == \"src_alpha\")\n\t\treturn GL_SRC_ALPHA;\n\tif (str == \"one_minus_src_alpha\")\n\t\treturn GL_ONE_MINUS_SRC_ALPHA;\n\tif (str == \"dst_alpha\")\n\t\treturn GL_DST_ALPHA;\n\tif (str == \"one_minus_dst_alpha\")\n\t\treturn GL_ONE_MINUS_DST_ALPHA;\n\tif (str == \"constant_color\")\n\t\treturn GL_CONSTANT_COLOR;\n\tif (str == \"one_minus_constant_color\")\n\t\treturn GL_ONE_MINUS_CONSTANT_COLOR;\n\tif (str == \"constant_alpha\")\n\t\treturn GL_CONSTANT_ALPHA;\n\tif (str == \"one_minus_constant_alpha\")\n\t\treturn GL_ONE_MINUS_CONSTANT_ALPHA;\n\tif (str == \"src_alpha_saturate\")\n\t\treturn GL_SRC_ALPHA_SATURATE;\n\tdebug_warn(\"Invalid blend func\");\n\treturn GL_ZERO;\n}\n\nsize_t CShaderManager::EffectCacheKeyHash::operator()(const EffectCacheKey& key) const\n{\n\tsize_t hash = 0;\n\tboost::hash_combine(hash, key.name.GetHash());\n\tboost::hash_combine(hash, key.defines1.GetHash());\n\tboost::hash_combine(hash, key.defines2.GetHash());\n\treturn hash;\n}\n\nbool CShaderManager::EffectCacheKey::operator==(const EffectCacheKey& b) const\n{\n\treturn (name == b.name && defines1 == b.defines1 && defines2 == b.defines2);\n}\n\nCShaderTechniquePtr CShaderManager::LoadEffect(CStrIntern name)\n{\n\treturn LoadEffect(name, g_Renderer.GetSystemShaderDefines(), CShaderDefines());\n}\n\nCShaderTechniquePtr CShaderManager::LoadEffect(CStrIntern name, const CShaderDefines& defines1, const CShaderDefines& defines2)\n{\n\t// Return the cached effect, if there is one\n\tEffectCacheKey key = { name, defines1, defines2 };\n\tEffectCacheMap::iterator it = m_EffectCache.find(key);\n\tif (it != m_EffectCache.end())\n\t\treturn it->second;\n\n\t// First time we've seen this key, so construct a new effect:\n\n\t// Merge the two sets of defines, so NewEffect doesn't have to care about the split\n\tCShaderDefines defines(defines1);\n\tdefines.SetMany(defines2);\n\n\tCShaderTechniquePtr tech(new CShaderTechnique());\n\tif (!NewEffect(name.c_str(), defines, tech))\n\t{\n\t\tLOGERROR(\"Failed to load effect '%s'\", name.c_str());\n\t\ttech = CShaderTechniquePtr();\n\t}\n\n\tm_EffectCache[key] = tech;\n\treturn tech;\n}\n\nbool CShaderManager::NewEffect(const char* name, const CShaderDefines& baseDefines, CShaderTechniquePtr& tech)\n{\n\tPROFILE2(\"loading effect\");\n\tPROFILE2_ATTR(\"name: %s\", name);\n\n\t// Shortcut syntax for effects that just contain a single shader\n\tif (strncmp(name, \"shader:\", 7) == 0)\n\t{\n\t\tCShaderProgramPtr program = LoadProgram(name+7, baseDefines);\n\t\tif (!program)\n\t\t\treturn false;\n\t\tCShaderPass pass;\n\t\tpass.SetShader(program);\n\t\ttech->AddPass(pass);\n\t\treturn true;\n\t}\n\n\tVfsPath xmlFilename = L\"shaders/effects/\" + wstring_from_utf8(name) + L\".xml\";\n\n\tCXeromyces XeroFile;\n\tPSRETURN ret = XeroFile.Load(g_VFS, xmlFilename);\n\tif (ret != PSRETURN_OK)\n\t\treturn false;\n\n\t// Define all the elements and attributes used in the XML file\n#define EL(x) int el_##x = XeroFile.GetElementID(#x)\n#define AT(x) int at_##x = XeroFile.GetAttributeID(#x)\n\tEL(alpha);\n\tEL(blend);\n\tEL(define);\n\tEL(depth);\n\tEL(pass);\n\tEL(require);\n\tEL(sort_by_distance);\n\tAT(context);\n\tAT(dst);\n\tAT(func);\n\tAT(ref);\n\tAT(shader);\n\tAT(shaders);\n\tAT(src);\n\tAT(mask);\n\tAT(name);\n\tAT(value);\n#undef AT\n#undef EL\n\n\t// Read some defines that influence how we pick techniques\n\tbool hasARB = (baseDefines.GetInt(\"SYS_HAS_ARB\") != 0);\n\tbool hasGLSL = (baseDefines.GetInt(\"SYS_HAS_GLSL\") != 0);\n\tbool preferGLSL = (baseDefines.GetInt(\"SYS_PREFER_GLSL\") != 0);\n\n\t// Prepare the preprocessor for conditional tests\n\tCPreprocessorWrapper preprocessor;\n\tpreprocessor.AddDefines(baseDefines);\n\n\tXMBElement Root = XeroFile.GetRoot();\n\n\t// Find all the techniques that we can use, and their preference\n\n\tstd::vector<std::pair<XMBElement, int> > usableTechs;\n\n\tXERO_ITER_EL(Root, Technique)\n\t{\n\t\tint preference = 0;\n\t\tbool isUsable = true;\n\t\tXERO_ITER_EL(Technique, Child)\n\t\t{\n\t\t\tXMBAttributeList Attrs = Child.GetAttributes();\n\n\t\t\tif (Child.GetNodeName() == el_require)\n\t\t\t{\n\t\t\t\tif (Attrs.GetNamedItem(at_shaders) == \"fixed\")\n\t\t\t\t{\n\t\t\t\t\t// FFP not supported by OpenGL ES\n\t\t\t\t\t#if CONFIG2_GLES\n\t\t\t\t\t\tisUsable = false;\n\t\t\t\t\t#endif\n\t\t\t\t}\n\t\t\t\telse if (Attrs.GetNamedItem(at_shaders) == \"arb\")\n\t\t\t\t{\n\t\t\t\t\tif (!hasARB)\n\t\t\t\t\t\tisUsable = false;\n\t\t\t\t}\n\t\t\t\telse if (Attrs.GetNamedItem(at_shaders) == \"glsl\")\n\t\t\t\t{\n\t\t\t\t\tif (!hasGLSL)\n\t\t\t\t\t\tisUsable = false;\n\n\t\t\t\t\tif (preferGLSL)\n\t\t\t\t\t\tpreference += 100;\n\t\t\t\t\telse\n\t\t\t\t\t\tpreference -= 100;\n\t\t\t\t}\n\t\t\t\telse if (!Attrs.GetNamedItem(at_context).empty())\n\t\t\t\t{\n\t\t\t\t\tCStr cond = Attrs.GetNamedItem(at_context);\n\t\t\t\t\tif (!preprocessor.TestConditional(cond))\n\t\t\t\t\t\tisUsable = false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (isUsable)\n\t\t\tusableTechs.emplace_back(Technique, preference);\n\t}\n\n\tif (usableTechs.empty())\n\t{\n\t\tdebug_warn(L\"Can't find a usable technique\");\n\t\treturn false;\n\t}\n\n\t// Sort by preference, tie-break on order of specification\n\tstd::stable_sort(usableTechs.begin(), usableTechs.end(),\n\t\t[](const std::pair<XMBElement, int>& a, const std::pair<XMBElement, int>& b) {\n\t\t\treturn b.second < a.second;\n\t\t});\n\n\tCShaderDefines techDefines = baseDefines;\n\t\n\tXERO_ITER_EL(usableTechs[0].first, Child)\n\t{\n\t\tif (Child.GetNodeName() == el_define)\n\t\t{\n\t\t\ttechDefines.Add(CStrIntern(Child.GetAttributes().GetNamedItem(at_name)), CStrIntern(Child.GetAttributes().GetNamedItem(at_value)));\n\t\t}\n\t\telse if (Child.GetNodeName() == el_sort_by_distance)\n\t\t{\n\t\t\ttech->SetSortByDistance(true);\n\t\t}\n\t\telse if (Child.GetNodeName() == el_pass)\n\t\t{\n\t\t\tCShaderDefines passDefines = techDefines;\n\n\t\t\tCShaderPass pass;\n\n\t\t\tXERO_ITER_EL(Child, Element)\n\t\t\t{\n\t\t\t\tif (Element.GetNodeName() == el_define)\n\t\t\t\t{\n\t\t\t\t\tpassDefines.Add(CStrIntern(Element.GetAttributes().GetNamedItem(at_name)), CStrIntern(Element.GetAttributes().GetNamedItem(at_value)));\n\t\t\t\t}\n\t\t\t\telse if (Element.GetNodeName() == el_alpha)\n\t\t\t\t{\n\t\t\t\t\tGLenum func = ParseComparisonFunc(Element.GetAttributes().GetNamedItem(at_func));\n\t\t\t\t\tfloat ref = Element.GetAttributes().GetNamedItem(at_ref).ToFloat();\n\t\t\t\t\tpass.AlphaFunc(func, ref);\n\t\t\t\t}\n\t\t\t\telse if (Element.GetNodeName() == el_blend)\n\t\t\t\t{\n\t\t\t\t\tGLenum src = ParseBlendFunc(Element.GetAttributes().GetNamedItem(at_src));\n\t\t\t\t\tGLenum dst = ParseBlendFunc(Element.GetAttributes().GetNamedItem(at_dst));\n\t\t\t\t\tpass.BlendFunc(src, dst);\n\t\t\t\t}\n\t\t\t\telse if (Element.GetNodeName() == el_depth)\n\t\t\t\t{\n\t\t\t\t\tif (!Element.GetAttributes().GetNamedItem(at_func).empty())\n\t\t\t\t\t\tpass.DepthFunc(ParseComparisonFunc(Element.GetAttributes().GetNamedItem(at_func)));\n\n\t\t\t\t\tif (!Element.GetAttributes().GetNamedItem(at_mask).empty())\n\t\t\t\t\t\tpass.DepthMask(Element.GetAttributes().GetNamedItem(at_mask) == \"true\" ? 1 : 0);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Load the shader program after we've read all the possibly-relevant <define>s\n\t\t\tpass.SetShader(LoadProgram(Child.GetAttributes().GetNamedItem(at_shader).c_str(), passDefines));\n\n\t\t\ttech->AddPass(pass);\n\t\t}\n\t}\n\n\treturn true;\n}\n\nsize_t CShaderManager::GetNumEffectsLoaded()\n{\n\treturn m_EffectCache.size();\n}\n\n/*static*/ Status CShaderManager::ReloadChangedFileCB(void* param, const VfsPath& path)\n{\n\treturn static_cast<CShaderManager*>(param)->ReloadChangedFile(path);\n}\n\nStatus CShaderManager::ReloadChangedFile(const VfsPath& path)\n{\n\t// Find all shaders using this file\n\tHotloadFilesMap::iterator files = m_HotloadFiles.find(path);\n\tif (files != m_HotloadFiles.end())\n\t{\n\t\t// Reload all shaders using this file\n\t\tfor (std::set<std::weak_ptr<CShaderProgram> >::iterator it = files->second.begin(); it != files->second.end(); ++it)\n\t\t{\n\t\t\tif (std::shared_ptr<CShaderProgram> program = it->lock())\n\t\t\t\tprogram->Reload();\n\t\t}\n\t}\n\n\t// TODO: hotloading changes to shader XML files and effect XML files would be nice\n\n\treturn INFO::OK;\n}\n"
  },
  {
    "path": "fpsgame/graphics/ShaderManager.h",
    "content": "/* Copyright (C) 2015 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef INCLUDED_SHADERMANAGER\n#define INCLUDED_SHADERMANAGER\n\n#define USE_SHADER_XML_VALIDATION 1\n\n#include <boost/unordered_map.hpp>\n#include <memory>\n#include <set>\n\n#include \"graphics/ShaderDefines.h\"\n#include \"graphics/ShaderProgram.h\"\n#include \"graphics/ShaderTechnique.h\"\n\n#if USE_SHADER_XML_VALIDATION\n# include \"ps/XML/RelaxNG.h\"\n#endif\n\n\n/**\n * Shader manager: loads and caches shader programs.\n * \n * For a high-level overview of shaders and materials, see\n * http://trac.wildfiregames.com/wiki/MaterialSystem\n */\nclass CShaderManager\n{\npublic:\n\tCShaderManager();\n\t~CShaderManager();\n\n\t/**\n\t * Load a shader program.\n\t * @param name name of shader XML specification (file is loaded from shaders/${name}.xml)\n\t * @param defines key/value set of preprocessor definitions\n\t * @return loaded program, or null pointer on error\n\t */\n\tCShaderProgramPtr LoadProgram(const char* name, const CShaderDefines& defines);\n\n\t/**\n\t * Load a shader effect.\n\t * Effects can be implemented via many techniques; this returns the best usable technique.\n\t * @param name name of effect XML specification (file is loaded from shaders/effects/${name}.xml)\n\t * @param defines1,defines2 key/value set of preprocessor definitions; defines2 has higher precedence\n\t * @return loaded technique, or empty technique on error\n\t */\n\tCShaderTechniquePtr LoadEffect(CStrIntern name, const CShaderDefines& defines1, const CShaderDefines& defines2);\n\n\t/**\n\t * Load a shader effect, with default system defines (from CRenderer::GetSystemShaderDefines).\n\t */\n\tCShaderTechniquePtr LoadEffect(CStrIntern name);\n\n\t/**\n\t * Returns the number of shader effects that are currently loaded.\n\t */\n\tsize_t GetNumEffectsLoaded();\n\nprivate:\n\n\tstruct CacheKey\n\t{\n\t\tstd::string name;\n\t\tCShaderDefines defines;\n\n\t\tbool operator<(const CacheKey& k) const\n\t\t{\n\t\t\tif (name < k.name) return true;\n\t\t\tif (k.name < name) return false;\n\t\t\treturn defines < k.defines;\n\t\t}\n\t};\n\n\t// A CShaderProgram contains expensive GL state, so we ought to cache it.\n\t// The compiled state depends solely on the filename and list of defines,\n\t// so we store that in CacheKey.\n\t// TODO: is this cache useful when we already have an effect cache?\n\tstd::map<CacheKey, CShaderProgramPtr> m_ProgramCache;\n\n\t/**\n\t * Key for effect cache lookups.\n\t * This stores two separate CShaderDefines because the renderer typically\n\t * has one set from the rendering context and one set from the material;\n\t * by handling both separately here, we avoid the cost of having to merge\n\t * the two sets into a single one before doing the cache lookup.\n\t */\n\tstruct EffectCacheKey\n\t{\n\t\tCStrIntern name;\n\t\tCShaderDefines defines1;\n\t\tCShaderDefines defines2;\n\n\t\tbool operator==(const EffectCacheKey& b) const;\n\t};\n\n\tstruct EffectCacheKeyHash\n\t{\n\t\tsize_t operator()(const EffectCacheKey& key) const;\n\t};\n\n\ttypedef boost::unordered_map<EffectCacheKey, CShaderTechniquePtr, EffectCacheKeyHash> EffectCacheMap;\n\tEffectCacheMap m_EffectCache;\n\n\t// Store the set of shaders that need to be reloaded when the given file is modified\n\ttypedef boost::unordered_map<VfsPath, std::set<std::weak_ptr<CShaderProgram>, std::owner_less<std::weak_ptr<CShaderProgram>>>> HotloadFilesMap;\n\tHotloadFilesMap m_HotloadFiles;\n\n\tbool NewProgram(const char* name, const CShaderDefines& defines, CShaderProgramPtr& program);\n\tbool NewEffect(const char* name, const CShaderDefines& defines, CShaderTechniquePtr& tech);\n\n\tstatic Status ReloadChangedFileCB(void* param, const VfsPath& path);\n\tStatus ReloadChangedFile(const VfsPath& path);\n};\n\n#endif // INCLUDED_SHADERMANAGER\n"
  },
  {
    "path": "fpsgame/graphics/ShaderProgram.cpp",
    "content": "/* Copyright (C) 2013 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"precompiled.h\"\n\n#include \"ShaderProgram.h\"\n\n#include \"graphics/ShaderManager.h\"\n#include \"graphics/TextureManager.h\"\n#include \"lib/res/graphics/ogl_tex.h\"\n#include \"maths/Matrix3D.h\"\n#include \"maths/Vector3D.h\"\n#include \"ps/CLogger.h\"\n#include \"ps/Filesystem.h\"\n#include \"ps/PreprocessorWrapper.h\"\n#include \"ps/Shapes.h\"\n\n#if !CONFIG2_GLES\n\nclass CShaderProgramARB : public CShaderProgram\n{\npublic:\n\tCShaderProgramARB(const VfsPath& vertexFile, const VfsPath& fragmentFile,\n\t\tconst CShaderDefines& defines,\n\t\tconst std::map<CStrIntern, int>& vertexIndexes, const std::map<CStrIntern, frag_index_pair_t>& fragmentIndexes,\n\t\tint streamflags) :\n\t\tCShaderProgram(streamflags),\n\t\tm_VertexFile(vertexFile), m_FragmentFile(fragmentFile),\n\t\tm_Defines(defines),\n\t\tm_VertexIndexes(vertexIndexes), m_FragmentIndexes(fragmentIndexes)\n\t{\n\t\tpglGenProgramsARB(1, &m_VertexProgram);\n\t\tpglGenProgramsARB(1, &m_FragmentProgram);\n\t}\n\n\t~CShaderProgramARB()\n\t{\n\t\tUnload();\n\n\t\tpglDeleteProgramsARB(1, &m_VertexProgram);\n\t\tpglDeleteProgramsARB(1, &m_FragmentProgram);\n\t}\n\n\tbool Compile(GLuint target, const char* targetName, GLuint program, const VfsPath& file, const CStr& code)\n\t{\n\t\togl_WarnIfError();\n\n\t\tpglBindProgramARB(target, program);\n\n\t\togl_WarnIfError();\n\n\t\tpglProgramStringARB(target, GL_PROGRAM_FORMAT_ASCII_ARB, (GLsizei)code.length(), code.c_str());\n\n\t\tif (ogl_SquelchError(GL_INVALID_OPERATION))\n\t\t{\n\t\t\tGLint errPos = 0;\n\t\t\tglGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &errPos);\n\t\t\tint errLine = std::count(code.begin(), code.begin() + std::min((int)code.length(), errPos + 1), '\\n') + 1;\n\t\t\tchar* errStr = (char*)glGetString(GL_PROGRAM_ERROR_STRING_ARB);\n\t\t\tLOGERROR(\"Failed to compile %s program '%s' (line %d):\\n%s\", targetName, file.string8(), errLine, errStr);\n\t\t\treturn false;\n\t\t}\n\n\t\tpglBindProgramARB(target, 0);\n\n\t\togl_WarnIfError();\n\n\t\treturn true;\n\t}\n\n\tvirtual void Reload()\n\t{\n\t\tUnload();\n\n\t\tCVFSFile vertexFile;\n\t\tif (vertexFile.Load(g_VFS, m_VertexFile) != PSRETURN_OK)\n\t\t\treturn;\n\n\t\tCVFSFile fragmentFile;\n\t\tif (fragmentFile.Load(g_VFS, m_FragmentFile) != PSRETURN_OK)\n\t\t\treturn;\n\n\t\tCPreprocessorWrapper preprocessor;\n\t\tpreprocessor.AddDefines(m_Defines);\n\n\t\tCStr vertexCode = preprocessor.Preprocess(vertexFile.GetAsString());\n\t\tCStr fragmentCode = preprocessor.Preprocess(fragmentFile.GetAsString());\n\n//\t\tprintf(\">>>\\n%s<<<\\n\", vertexCode.c_str());\n//\t\tprintf(\">>>\\n%s<<<\\n\", fragmentCode.c_str());\n\n\t\tif (!Compile(GL_VERTEX_PROGRAM_ARB, \"vertex\", m_VertexProgram, m_VertexFile, vertexCode))\n\t\t\treturn;\n\n\t\tif (!Compile(GL_FRAGMENT_PROGRAM_ARB, \"fragment\", m_FragmentProgram, m_FragmentFile, fragmentCode))\n\t\t\treturn;\n\n\t\tm_IsValid = true;\n\t}\n\n\tvoid Unload()\n\t{\n\t\tm_IsValid = false;\n\t}\n\n\tvirtual void Bind()\n\t{\n\t\tglEnable(GL_VERTEX_PROGRAM_ARB);\n\t\tglEnable(GL_FRAGMENT_PROGRAM_ARB);\n\t\tpglBindProgramARB(GL_VERTEX_PROGRAM_ARB, m_VertexProgram);\n\t\tpglBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, m_FragmentProgram);\n\n\t\tBindClientStates();\n\t}\n\n\tvirtual void Unbind()\n\t{\n\t\tglDisable(GL_VERTEX_PROGRAM_ARB);\n\t\tglDisable(GL_FRAGMENT_PROGRAM_ARB);\n\t\tpglBindProgramARB(GL_VERTEX_PROGRAM_ARB, 0);\n\t\tpglBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, 0);\n\n\t\tUnbindClientStates();\n\n\t\t// TODO: should unbind textures, probably\n\t}\n\n\tint GetUniformVertexIndex(CStrIntern id)\n\t{\n\t\tstd::map<CStrIntern, int>::iterator it = m_VertexIndexes.find(id);\n\t\tif (it == m_VertexIndexes.end())\n\t\t\treturn -1;\n\t\treturn it->second;\n\t}\n\n\tfrag_index_pair_t GetUniformFragmentIndex(CStrIntern id)\n\t{\n\t\tstd::map<CStrIntern, frag_index_pair_t>::iterator it = m_FragmentIndexes.find(id);\n\t\tif (it == m_FragmentIndexes.end())\n\t\t\treturn std::make_pair(-1, 0);\n\t\treturn it->second;\n\t}\n\n\tvirtual Binding GetTextureBinding(texture_id_t id)\n\t{\n\t\tfrag_index_pair_t fPair = GetUniformFragmentIndex(id);\n\t\tint index = fPair.first;\n\t\tif (index == -1)\n\t\t\treturn Binding();\n\t\telse\n\t\t\treturn Binding((int)fPair.second, index);\n\t}\n\n\tvirtual void BindTexture(texture_id_t id, Handle tex)\n\t{\n\t\tfrag_index_pair_t fPair = GetUniformFragmentIndex(id);\n\t\tint index = fPair.first;\n\t\tif (index != -1)\n\t\t{\n\t\t\tGLuint h;\n\t\t\togl_tex_get_texture_id(tex, &h);\n\t\t\tpglActiveTextureARB(GL_TEXTURE0+index);\n\t\t\tglBindTexture(fPair.second, h);\n\t\t}\n\t}\n\n\tvirtual void BindTexture(texture_id_t id, GLuint tex)\n\t{\n\t\tfrag_index_pair_t fPair = GetUniformFragmentIndex(id);\n\t\tint index = fPair.first;\n\t\tif (index != -1)\n\t\t{\n\t\t\tpglActiveTextureARB(GL_TEXTURE0+index);\n\t\t\tglBindTexture(fPair.second, tex);\n\t\t}\n\t}\n\n\tvirtual void BindTexture(Binding id, Handle tex)\n\t{\n\t\tint index = id.second;\n\t\tif (index != -1)\n\t\t\togl_tex_bind(tex, index);\n\t}\n\n\tvirtual Binding GetUniformBinding(uniform_id_t id)\n\t{\n\t\treturn Binding(GetUniformVertexIndex(id), GetUniformFragmentIndex(id).first);\n\t}\n\n\tvirtual void Uniform(Binding id, float v0, float v1, float v2, float v3)\n\t{\n\t\tif (id.first != -1)\n\t\t\tpglProgramLocalParameter4fARB(GL_VERTEX_PROGRAM_ARB, (GLuint)id.first, v0, v1, v2, v3);\n\n\t\tif (id.second != -1)\n\t\t\tpglProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, (GLuint)id.second, v0, v1, v2, v3);\n\t}\n\n\tvirtual void Uniform(Binding id, const CMatrix3D& v)\n\t{\n\t\tif (id.first != -1)\n\t\t{\n\t\t\tpglProgramLocalParameter4fARB(GL_VERTEX_PROGRAM_ARB, (GLuint)id.first+0, v._11, v._12, v._13, v._14);\n\t\t\tpglProgramLocalParameter4fARB(GL_VERTEX_PROGRAM_ARB, (GLuint)id.first+1, v._21, v._22, v._23, v._24);\n\t\t\tpglProgramLocalParameter4fARB(GL_VERTEX_PROGRAM_ARB, (GLuint)id.first+2, v._31, v._32, v._33, v._34);\n\t\t\tpglProgramLocalParameter4fARB(GL_VERTEX_PROGRAM_ARB, (GLuint)id.first+3, v._41, v._42, v._43, v._44);\n\t\t}\n\n\t\tif (id.second != -1)\n\t\t{\n\t\t\tpglProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, (GLuint)id.second+0, v._11, v._12, v._13, v._14);\n\t\t\tpglProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, (GLuint)id.second+1, v._21, v._22, v._23, v._24);\n\t\t\tpglProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, (GLuint)id.second+2, v._31, v._32, v._33, v._34);\n\t\t\tpglProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, (GLuint)id.second+3, v._41, v._42, v._43, v._44);\n\t\t}\n\t}\n\n\tvirtual void Uniform(Binding id, size_t count, const CMatrix3D* v)\n\t{\n\t\tENSURE(count == 1);\n\t\tUniform(id, v[0]);\n\t}\n\nprivate:\n\tVfsPath m_VertexFile;\n\tVfsPath m_FragmentFile;\n\tCShaderDefines m_Defines;\n\n\tGLuint m_VertexProgram;\n\tGLuint m_FragmentProgram;\n\n\tstd::map<CStrIntern, int> m_VertexIndexes;\n\t\n\t// pair contains <index, gltype>\n\tstd::map<CStrIntern, frag_index_pair_t> m_FragmentIndexes;\n};\n\n#endif // #if !CONFIG2_GLES\n\n//////////////////////////////////////////////////////////////////////////\n\nTIMER_ADD_CLIENT(tc_ShaderGLSLCompile);\nTIMER_ADD_CLIENT(tc_ShaderGLSLLink);\n\nclass CShaderProgramGLSL : public CShaderProgram\n{\npublic:\n\tCShaderProgramGLSL(const VfsPath& vertexFile, const VfsPath& fragmentFile,\n\t\tconst CShaderDefines& defines,\n\t\tconst std::map<CStrIntern, int>& vertexAttribs,\n\t\tint streamflags) :\n\tCShaderProgram(streamflags),\n\t\tm_VertexFile(vertexFile), m_FragmentFile(fragmentFile),\n\t\tm_Defines(defines),\n\t\tm_VertexAttribs(vertexAttribs)\n\t{\n\t\tm_Program = 0;\n\t\tm_VertexShader = pglCreateShaderObjectARB(GL_VERTEX_SHADER);\n\t\tm_FragmentShader = pglCreateShaderObjectARB(GL_FRAGMENT_SHADER);\n\t}\n\n\t~CShaderProgramGLSL()\n\t{\n\t\tUnload();\n\n\t\tpglDeleteShader(m_VertexShader);\n\t\tpglDeleteShader(m_FragmentShader);\n\t}\n\n\tbool Compile(GLhandleARB shader, const VfsPath& file, const CStr& code)\n\t{\n\t\tTIMER_ACCRUE(tc_ShaderGLSLCompile);\n\n\t\togl_WarnIfError();\n\n\t\tconst char* code_string = code.c_str();\n\t\tGLint code_length = code.length();\n\t\tpglShaderSourceARB(shader, 1, &code_string, &code_length);\n\n\t\tpglCompileShaderARB(shader);\n\n\t\tGLint ok = 0;\n\t\tpglGetShaderiv(shader, GL_COMPILE_STATUS, &ok);\n\n\t\tGLint length = 0;\n\t\tpglGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length);\n\n\t\t// Apparently sometimes GL_INFO_LOG_LENGTH is incorrectly reported as 0\n\t\t// (http://code.google.com/p/android/issues/detail?id=9953)\n\t\tif (!ok && length == 0)\n\t\t\tlength = 4096;\n\n\t\tif (length > 1)\n\t\t{\n\t\t\tchar* infolog = new char[length];\n\t\t\tpglGetShaderInfoLog(shader, length, NULL, infolog);\n\n\t\t\tif (ok)\n\t\t\t\tLOGMESSAGE(\"Info when compiling shader '%s':\\n%s\", file.string8(), infolog);\n\t\t\telse\n\t\t\t\tLOGERROR(\"Failed to compile shader '%s':\\n%s\", file.string8(), infolog);\n\n\t\t\tdelete[] infolog;\n\t\t}\n\n\t\togl_WarnIfError();\n\n\t\treturn (ok ? true : false);\n\t}\n\n\tbool Link()\n\t{\n\t\tTIMER_ACCRUE(tc_ShaderGLSLLink);\n\n\t\tENSURE(!m_Program);\n\t\tm_Program = pglCreateProgramObjectARB();\n\n\t\tpglAttachObjectARB(m_Program, m_VertexShader);\n\t\togl_WarnIfError();\n\t\tpglAttachObjectARB(m_Program, m_FragmentShader);\n\t\togl_WarnIfError();\n\n\t\t// Set up the attribute bindings explicitly, since apparently drivers\n\t\t// don't always pick the most efficient bindings automatically,\n\t\t// and also this lets us hardcode indexes into VertexPointer etc\n\t\tfor (std::map<CStrIntern, int>::iterator it = m_VertexAttribs.begin(); it != m_VertexAttribs.end(); ++it)\n\t\t\tpglBindAttribLocationARB(m_Program, it->second, it->first.c_str());\n\n\t\tpglLinkProgramARB(m_Program);\n\n\t\tGLint ok = 0;\n\t\tpglGetProgramiv(m_Program, GL_LINK_STATUS, &ok);\n\n\t\tGLint length = 0;\n\t\tpglGetProgramiv(m_Program, GL_INFO_LOG_LENGTH, &length);\n\n\t\tif (!ok && length == 0)\n\t\t\tlength = 4096;\n\n\t\tif (length > 1)\n\t\t{\n\t\t\tchar* infolog = new char[length];\n\t\t\tpglGetProgramInfoLog(m_Program, length, NULL, infolog);\n\n\t\t\tif (ok)\n\t\t\t\tLOGMESSAGE(\"Info when linking program '%s'+'%s':\\n%s\", m_VertexFile.string8(), m_FragmentFile.string8(), infolog);\n\t\t\telse\n\t\t\t\tLOGERROR(\"Failed to link program '%s'+'%s':\\n%s\", m_VertexFile.string8(), m_FragmentFile.string8(), infolog);\n\t\t\t\n\t\t\tdelete[] infolog;\n\t\t}\n\n\t\togl_WarnIfError();\n\n\t\tif (!ok)\n\t\t\treturn false;\n\n\t\tm_Uniforms.clear();\n\t\tm_Samplers.clear();\n\n\t\tBind();\n\n\t\togl_WarnIfError();\n\n\t\tGLint numUniforms = 0;\n\t\tpglGetProgramiv(m_Program, GL_ACTIVE_UNIFORMS, &numUniforms);\n\t\togl_WarnIfError();\n\t\tfor (GLint i = 0; i < numUniforms; ++i)\n\t\t{\n\t\t\tchar name[256] = {0};\n\t\t\tGLsizei nameLength = 0;\n\t\t\tGLint size = 0;\n\t\t\tGLenum type = 0;\n\t\t\tpglGetActiveUniformARB(m_Program, i, ARRAY_SIZE(name), &nameLength, &size, &type, name);\n\t\t\togl_WarnIfError();\n\n\t\t\tGLint loc = pglGetUniformLocationARB(m_Program, name);\n\n\t\t\tCStrIntern nameIntern(name);\n\t\t\tm_Uniforms[nameIntern] = std::make_pair(loc, type);\n\n\t\t\t// Assign sampler uniforms to sequential texture units\n\t\t\tif (type == GL_SAMPLER_2D\n\t\t\t || type == GL_SAMPLER_CUBE\n#if !CONFIG2_GLES\n\t\t\t || type == GL_SAMPLER_2D_SHADOW\n#endif\n\t\t\t)\n\t\t\t{\n\t\t\t\tint unit = (int)m_Samplers.size();\n\t\t\t\tm_Samplers[nameIntern].first = (type == GL_SAMPLER_CUBE ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D);\n\t\t\t\tm_Samplers[nameIntern].second = unit;\n\t\t\t\tpglUniform1iARB(loc, unit); // link uniform to unit\n\t\t\t\togl_WarnIfError();\n\t\t\t}\n\t\t}\n\n\t\t// TODO: verify that we're not using more samplers than is supported\n\n\t\tUnbind();\n\n\t\togl_WarnIfError();\n\n\t\treturn true;\n\t}\n\n\tvirtual void Reload()\n\t{\n\t\tUnload();\n\n\t\tCVFSFile vertexFile;\n\t\tif (vertexFile.Load(g_VFS, m_VertexFile) != PSRETURN_OK)\n\t\t\treturn;\n\n\t\tCVFSFile fragmentFile;\n\t\tif (fragmentFile.Load(g_VFS, m_FragmentFile) != PSRETURN_OK)\n\t\t\treturn;\n\n\t\tCPreprocessorWrapper preprocessor;\n\t\tpreprocessor.AddDefines(m_Defines);\n\n#if CONFIG2_GLES\n\t\t// GLES defines the macro \"GL_ES\" in its GLSL preprocessor,\n\t\t// but since we run our own preprocessor first, we need to explicitly\n\t\t// define it here\n\t\tpreprocessor.AddDefine(\"GL_ES\", \"1\");\n#endif\n\n\t\tCStr vertexCode = preprocessor.Preprocess(vertexFile.GetAsString());\n\t\tCStr fragmentCode = preprocessor.Preprocess(fragmentFile.GetAsString());\n\n#if CONFIG2_GLES\n\t\t// Ugly hack to replace desktop GLSL 1.10/1.20 with GLSL ES 1.00,\n\t\t// and also to set default float precision for fragment shaders\n\t\tvertexCode.Replace(\"#version 110\\n\", \"#version 100\\n\");\n\t\tvertexCode.Replace(\"#version 110\\r\\n\", \"#version 100\\n\");\n\t\tvertexCode.Replace(\"#version 120\\n\", \"#version 100\\n\");\n\t\tvertexCode.Replace(\"#version 120\\r\\n\", \"#version 100\\n\");\n\t\tfragmentCode.Replace(\"#version 110\\n\", \"#version 100\\nprecision mediump float;\\n\");\n\t\tfragmentCode.Replace(\"#version 110\\r\\n\", \"#version 100\\nprecision mediump float;\\n\");\n\t\tfragmentCode.Replace(\"#version 120\\n\", \"#version 100\\nprecision mediump float;\\n\");\n\t\tfragmentCode.Replace(\"#version 120\\r\\n\", \"#version 100\\nprecision mediump float;\\n\");\n#endif\n\n\t\tif (!Compile(m_VertexShader, m_VertexFile, vertexCode))\n\t\t\treturn;\n\n\t\tif (!Compile(m_FragmentShader, m_FragmentFile, fragmentCode))\n\t\t\treturn;\n\n\t\tif (!Link())\n\t\t\treturn;\n\n\t\tm_IsValid = true;\n\t}\n\n\tvoid Unload()\n\t{\n\t\tm_IsValid = false;\n\n\t\tif (m_Program)\n\t\t\tpglDeleteProgram(m_Program);\n\t\tm_Program = 0;\n\n\t\t// The shader objects can be reused and don't need to be deleted here\n\t}\n\n\tvirtual void Bind()\n\t{\n\t\tpglUseProgramObjectARB(m_Program);\n\n\t\tfor (std::map<CStrIntern, int>::iterator it = m_VertexAttribs.begin(); it != m_VertexAttribs.end(); ++it)\n\t\t\tpglEnableVertexAttribArrayARB(it->second);\n\t}\n\n\tvirtual void Unbind()\n\t{\n\t\tpglUseProgramObjectARB(0);\n\n\t\tfor (std::map<CStrIntern, int>::iterator it = m_VertexAttribs.begin(); it != m_VertexAttribs.end(); ++it)\n\t\t\tpglDisableVertexAttribArrayARB(it->second);\n\n\t\t// TODO: should unbind textures, probably\n\t}\n\n\tvirtual Binding GetTextureBinding(texture_id_t id)\n\t{\n\t\tstd::map<CStrIntern, std::pair<GLenum, int> >::iterator it = m_Samplers.find(CStrIntern(id));\n\t\tif (it == m_Samplers.end())\n\t\t\treturn Binding();\n\t\telse\n\t\t\treturn Binding((int)it->second.first, it->second.second);\n\t}\n\n\tvirtual void BindTexture(texture_id_t id, Handle tex)\n\t{\n\t\tstd::map<CStrIntern, std::pair<GLenum, int> >::iterator it = m_Samplers.find(CStrIntern(id));\n\t\tif (it == m_Samplers.end())\n\t\t\treturn;\n\n\t\tGLuint h;\n\t\togl_tex_get_texture_id(tex, &h);\n\t\tpglActiveTextureARB(GL_TEXTURE0 + it->second.second);\n\t\tglBindTexture(it->second.first, h);\n\t}\n\n\tvirtual void BindTexture(texture_id_t id, GLuint tex)\n\t{\n\t\tstd::map<CStrIntern, std::pair<GLenum, int> >::iterator it = m_Samplers.find(CStrIntern(id));\n\t\tif (it == m_Samplers.end())\n\t\t\treturn;\n\n\t\tpglActiveTextureARB(GL_TEXTURE0 + it->second.second);\n\t\tglBindTexture(it->second.first, tex);\n\t}\n\n\tvirtual void BindTexture(Binding id, Handle tex)\n\t{\n\t\tif (id.second == -1)\n\t\t\treturn;\n\n\t\tGLuint h;\n\t\togl_tex_get_texture_id(tex, &h);\n\t\tpglActiveTextureARB(GL_TEXTURE0 + id.second);\n\t\tglBindTexture(id.first, h);\n\t}\n\n\tvirtual Binding GetUniformBinding(uniform_id_t id)\n\t{\n\t\tstd::map<CStrIntern, std::pair<int, GLenum> >::iterator it = m_Uniforms.find(id);\n\t\tif (it == m_Uniforms.end())\n\t\t\treturn Binding();\n\t\telse\n\t\t\treturn Binding(it->second.first, (int)it->second.second);\n\t}\n\n\tvirtual void Uniform(Binding id, float v0, float v1, float v2, float v3)\n\t{\n\t\tif (id.first != -1)\n\t\t{\n\t\t\tif (id.second == GL_FLOAT)\n\t\t\t\tpglUniform1fARB(id.first, v0);\n\t\t\telse if (id.second == GL_FLOAT_VEC2)\n\t\t\t\tpglUniform2fARB(id.first, v0, v1);\n\t\t\telse if (id.second == GL_FLOAT_VEC3)\n\t\t\t\tpglUniform3fARB(id.first, v0, v1, v2);\n\t\t\telse if (id.second == GL_FLOAT_VEC4)\n\t\t\t\tpglUniform4fARB(id.first, v0, v1, v2, v3);\n\t\t\telse\n\t\t\t\tLOGERROR(\"CShaderProgramGLSL::Uniform(): Invalid uniform type (expected float, vec2, vec3, vec4)\");\n\t\t}\n\t}\n\n\tvirtual void Uniform(Binding id, const CMatrix3D& v)\n\t{\n\t\tif (id.first != -1)\n\t\t{\n\t\t\tif (id.second == GL_FLOAT_MAT4)\n\t\t\t\tpglUniformMatrix4fvARB(id.first, 1, GL_FALSE, &v._11);\n\t\t\telse\n\t\t\t\tLOGERROR(\"CShaderProgramGLSL::Uniform(): Invalid uniform type (expected mat4)\");\n\t\t}\n\t}\n\n\tvirtual void Uniform(Binding id, size_t count, const CMatrix3D* v)\n\t{\n\t\tif (id.first != -1)\n\t\t{\n\t\t\tif (id.second == GL_FLOAT_MAT4)\n\t\t\t\tpglUniformMatrix4fvARB(id.first, count, GL_FALSE, &v->_11);\n\t\t\telse\n\t\t\t\tLOGERROR(\"CShaderProgramGLSL::Uniform(): Invalid uniform type (expected mat4)\");\n\t\t}\n\t}\n\n\t// Map the various fixed-function Pointer functions onto generic vertex attributes\n\t// (matching the attribute indexes from ShaderManager's ParseAttribSemantics):\n\n\tvirtual void VertexPointer(GLint size, GLenum type, GLsizei stride, void* pointer)\n\t{\n\t\tpglVertexAttribPointerARB(0, size, type, GL_FALSE, stride, pointer);\n\t\tm_ValidStreams |= STREAM_POS;\n\t}\n\n\tvirtual void NormalPointer(GLenum type, GLsizei stride, void* pointer)\n\t{\n\t\tpglVertexAttribPointerARB(2, 3, type, GL_TRUE, stride, pointer);\n\t\tm_ValidStreams |= STREAM_NORMAL;\n\t}\n\n\tvirtual void ColorPointer(GLint size, GLenum type, GLsizei stride, void* pointer)\n\t{\n\t\tpglVertexAttribPointerARB(3, size, type, GL_TRUE, stride, pointer);\n\t\tm_ValidStreams |= STREAM_COLOR;\n\t}\n\n\tvirtual void TexCoordPointer(GLenum texture, GLint size, GLenum type, GLsizei stride, void* pointer)\n\t{\n\t\tpglVertexAttribPointerARB(8 + texture - GL_TEXTURE0, size, type, GL_FALSE, stride, pointer);\n\t\tm_ValidStreams |= STREAM_UV0 << (texture - GL_TEXTURE0);\n\t}\n\n\tvirtual void VertexAttribPointer(attrib_id_t id, GLint size, GLenum type, GLboolean normalized, GLsizei stride, void* pointer)\n\t{\n\t\tstd::map<CStrIntern, int>::iterator it = m_VertexAttribs.find(id);\n\t\tif (it != m_VertexAttribs.end())\n\t\t{\n\t\t\tpglVertexAttribPointerARB(it->second, size, type, normalized, stride, pointer);\n\t\t}\n\t}\n\n\tvirtual void VertexAttribIPointer(attrib_id_t id, GLint size, GLenum type, GLsizei stride, void* pointer)\n\t{\n\t\tstd::map<CStrIntern, int>::iterator it = m_VertexAttribs.find(id);\n\t\tif (it != m_VertexAttribs.end())\n\t\t{\n#if CONFIG2_GLES\n\t\t\tdebug_warn(L\"glVertexAttribIPointer not supported on GLES\");\n#else\n\t\t\tpglVertexAttribIPointerEXT(it->second, size, type, stride, pointer);\n#endif\n\t\t}\n\t}\n\nprivate:\n\tVfsPath m_VertexFile;\n\tVfsPath m_FragmentFile;\n\tCShaderDefines m_Defines;\n\tstd::map<CStrIntern, int> m_VertexAttribs;\n\n\tGLhandleARB m_Program;\n\tGLhandleARB m_VertexShader;\n\tGLhandleARB m_FragmentShader;\n\n\tstd::map<CStrIntern, std::pair<int, GLenum> > m_Uniforms;\n\tstd::map<CStrIntern, std::pair<GLenum, int> > m_Samplers; // texture target & unit chosen for each uniform sampler\n};\n\n//////////////////////////////////////////////////////////////////////////\n\nCShaderProgram::CShaderProgram(int streamflags)\n\t: m_IsValid(false), m_StreamFlags(streamflags), m_ValidStreams(0)\n{\n}\n\n#if CONFIG2_GLES\n/*static*/ CShaderProgram* CShaderProgram::ConstructARB(const VfsPath& vertexFile, const VfsPath& fragmentFile,\n\tconst CShaderDefines& UNUSED(defines),\n\tconst std::map<CStrIntern, int>& UNUSED(vertexIndexes), const std::map<CStrIntern, frag_index_pair_t>& UNUSED(fragmentIndexes),\n\tint UNUSED(streamflags))\n{\n\tLOGERROR(\"CShaderProgram::ConstructARB: '%s'+'%s': ARB shaders not supported on this device\",\n\t\tvertexFile.string8(), fragmentFile.string8());\n\treturn NULL;\n}\n#else\n/*static*/ CShaderProgram* CShaderProgram::ConstructARB(const VfsPath& vertexFile, const VfsPath& fragmentFile,\n\tconst CShaderDefines& defines,\n\tconst std::map<CStrIntern, int>& vertexIndexes, const std::map<CStrIntern, frag_index_pair_t>& fragmentIndexes,\n\tint streamflags)\n{\n\treturn new CShaderProgramARB(vertexFile, fragmentFile, defines, vertexIndexes, fragmentIndexes, streamflags);\n}\n#endif\n\n/*static*/ CShaderProgram* CShaderProgram::ConstructGLSL(const VfsPath& vertexFile, const VfsPath& fragmentFile,\n\tconst CShaderDefines& defines,\n\tconst std::map<CStrIntern, int>& vertexAttribs,\n\tint streamflags)\n{\n\treturn new CShaderProgramGLSL(vertexFile, fragmentFile, defines, vertexAttribs, streamflags);\n}\n\nbool CShaderProgram::IsValid() const\n{\n\treturn m_IsValid;\n}\n\nint CShaderProgram::GetStreamFlags() const\n{\n\treturn m_StreamFlags;\n}\n\nvoid CShaderProgram::BindTexture(texture_id_t id, CTexturePtr tex)\n{\n\tBindTexture(id, tex->GetHandle());\n}\n\nvoid CShaderProgram::Uniform(Binding id, int v)\n{\n\tUniform(id, (float)v, (float)v, (float)v, (float)v);\n}\n\nvoid CShaderProgram::Uniform(Binding id, float v)\n{\n\tUniform(id, v, v, v, v);\n}\n\nvoid CShaderProgram::Uniform(Binding id, float v0, float v1)\n{\n\tUniform(id, v0, v1, 0.0f, 0.0f);\n}\n\nvoid CShaderProgram::Uniform(Binding id, const CVector3D& v)\n{\n\tUniform(id, v.X, v.Y, v.Z, 0.0f);\n}\n\nvoid CShaderProgram::Uniform(Binding id, const CColor& v)\n{\n\tUniform(id, v.r, v.g, v.b, v.a);\n}\n\nvoid CShaderProgram::Uniform(uniform_id_t id, int v)\n{\n\tUniform(GetUniformBinding(id), (float)v, (float)v, (float)v, (float)v);\n}\n\nvoid CShaderProgram::Uniform(uniform_id_t id, float v)\n{\n\tUniform(GetUniformBinding(id), v, v, v, v);\n}\n\nvoid CShaderProgram::Uniform(uniform_id_t id, float v0, float v1)\n{\n\tUniform(GetUniformBinding(id), v0, v1, 0.0f, 0.0f);\n}\n\nvoid CShaderProgram::Uniform(uniform_id_t id, const CVector3D& v)\n{\n\tUniform(GetUniformBinding(id), v.X, v.Y, v.Z, 0.0f);\n}\n\nvoid CShaderProgram::Uniform(uniform_id_t id, const CColor& v)\n{\n\tUniform(GetUniformBinding(id), v.r, v.g, v.b, v.a);\n}\n\nvoid CShaderProgram::Uniform(uniform_id_t id, float v0, float v1, float v2, float v3)\n{\n\tUniform(GetUniformBinding(id), v0, v1, v2, v3);\n}\n\nvoid CShaderProgram::Uniform(uniform_id_t id, const CMatrix3D& v)\n{\n\tUniform(GetUniformBinding(id), v);\n}\n\nvoid CShaderProgram::Uniform(uniform_id_t id, size_t count, const CMatrix3D* v)\n{\n\tUniform(GetUniformBinding(id), count, v);\n}\n\n\n// These should all be overridden by CShaderProgramGLSL, and not used\n// if a non-GLSL shader was loaded instead:\n\nvoid CShaderProgram::VertexAttribPointer(attrib_id_t UNUSED(id), GLint UNUSED(size), GLenum UNUSED(type),\n\tGLboolean UNUSED(normalized), GLsizei UNUSED(stride), void* UNUSED(pointer))\n{\n\tdebug_warn(\"Shader type doesn't support VertexAttribPointer\");\n}\n\nvoid CShaderProgram::VertexAttribIPointer(attrib_id_t UNUSED(id), GLint UNUSED(size), GLenum UNUSED(type),\n\tGLsizei UNUSED(stride), void* UNUSED(pointer))\n{\n\tdebug_warn(\"Shader type doesn't support VertexAttribIPointer\");\n}\n\n#if CONFIG2_GLES\n\n// These should all be overridden by CShaderProgramGLSL\n// (GLES doesn't support any other types of shader program):\n\nvoid CShaderProgram::VertexPointer(GLint UNUSED(size), GLenum UNUSED(type), GLsizei UNUSED(stride), void* UNUSED(pointer))\n{\n\tdebug_warn(\"CShaderProgram::VertexPointer should be overridden\");\n}\nvoid CShaderProgram::NormalPointer(GLenum UNUSED(type), GLsizei UNUSED(stride), void* UNUSED(pointer))\n{\n\tdebug_warn(\"CShaderProgram::NormalPointer should be overridden\");\n}\nvoid CShaderProgram::ColorPointer(GLint UNUSED(size), GLenum UNUSED(type), GLsizei UNUSED(stride), void* UNUSED(pointer))\n{\n\tdebug_warn(\"CShaderProgram::ColorPointer should be overridden\");\n}\nvoid CShaderProgram::TexCoordPointer(GLenum UNUSED(texture), GLint UNUSED(size), GLenum UNUSED(type), GLsizei UNUSED(stride), void* UNUSED(pointer))\n{\n\tdebug_warn(\"CShaderProgram::TexCoordPointer should be overridden\");\n}\n\n#else\n\n// These are overridden by CShaderProgramGLSL, but fixed-function and ARB shaders\n// both use the fixed-function vertex attribute pointers so we'll share their\n// definitions here:\n\nvoid CShaderProgram::VertexPointer(GLint size, GLenum type, GLsizei stride, void* pointer)\n{\n\tglVertexPointer(size, type, stride, pointer);\n\tm_ValidStreams |= STREAM_POS;\n}\n\nvoid CShaderProgram::NormalPointer(GLenum type, GLsizei stride, void* pointer)\n{\n\tglNormalPointer(type, stride, pointer);\n\tm_ValidStreams |= STREAM_NORMAL;\n}\n\nvoid CShaderProgram::ColorPointer(GLint size, GLenum type, GLsizei stride, void* pointer)\n{\n\tglColorPointer(size, type, stride, pointer);\n\tm_ValidStreams |= STREAM_COLOR;\n}\n\nvoid CShaderProgram::TexCoordPointer(GLenum texture, GLint size, GLenum type, GLsizei stride, void* pointer)\n{\n\tpglClientActiveTextureARB(texture);\n\tglTexCoordPointer(size, type, stride, pointer);\n\tpglClientActiveTextureARB(GL_TEXTURE0);\n\tm_ValidStreams |= STREAM_UV0 << (texture - GL_TEXTURE0);\n}\n\nvoid CShaderProgram::BindClientStates()\n{\n\tENSURE(m_StreamFlags == (m_StreamFlags & (STREAM_POS|STREAM_NORMAL|STREAM_COLOR|STREAM_UV0|STREAM_UV1)));\n\n\t// Enable all the desired client states for non-GLSL rendering\n\n\tif (m_StreamFlags & STREAM_POS)    glEnableClientState(GL_VERTEX_ARRAY);\n\tif (m_StreamFlags & STREAM_NORMAL) glEnableClientState(GL_NORMAL_ARRAY);\n\tif (m_StreamFlags & STREAM_COLOR)  glEnableClientState(GL_COLOR_ARRAY);\n\n\tif (m_StreamFlags & STREAM_UV0)\n\t{\n\t\tpglClientActiveTextureARB(GL_TEXTURE0);\n\t\tglEnableClientState(GL_TEXTURE_COORD_ARRAY);\n\t}\n\n\tif (m_StreamFlags & STREAM_UV1)\n\t{\n\t\tpglClientActiveTextureARB(GL_TEXTURE1);\n\t\tglEnableClientState(GL_TEXTURE_COORD_ARRAY);\n\t\tpglClientActiveTextureARB(GL_TEXTURE0);\n\t}\n\n\t// Rendering code must subsequently call VertexPointer etc for all of the streams\n\t// that were activated in this function, else AssertPointersBound will complain\n\t// that some arrays were unspecified\n\tm_ValidStreams = 0;\n}\n\nvoid CShaderProgram::UnbindClientStates()\n{\n\tif (m_StreamFlags & STREAM_POS)    glDisableClientState(GL_VERTEX_ARRAY);\n\tif (m_StreamFlags & STREAM_NORMAL) glDisableClientState(GL_NORMAL_ARRAY);\n\tif (m_StreamFlags & STREAM_COLOR)  glDisableClientState(GL_COLOR_ARRAY);\n\n\tif (m_StreamFlags & STREAM_UV0)\n\t{\n\t\tpglClientActiveTextureARB(GL_TEXTURE0);\n\t\tglDisableClientState(GL_TEXTURE_COORD_ARRAY);\n\t}\n\n\tif (m_StreamFlags & STREAM_UV1)\n\t{\n\t\tpglClientActiveTextureARB(GL_TEXTURE1);\n\t\tglDisableClientState(GL_TEXTURE_COORD_ARRAY);\n\t\tpglClientActiveTextureARB(GL_TEXTURE0);\n\t}\n}\n\n#endif // !CONFIG2_GLES\n\nvoid CShaderProgram::AssertPointersBound()\n{\n\tENSURE((m_StreamFlags & ~m_ValidStreams) == 0);\n}\n"
  },
  {
    "path": "fpsgame/graphics/ShaderProgram.h",
    "content": "/* Copyright (C) 2012 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef INCLUDED_SHADERPROGRAM\n#define INCLUDED_SHADERPROGRAM\n\n#include \"graphics/ShaderProgramPtr.h\"\n#include \"graphics/Texture.h\"\n#include \"lib/ogl.h\"\n#include \"lib/file/vfs/vfs_path.h\"\n#include \"lib/res/handle.h\"\n\n#include <map>\n\nstruct CColor;\nclass CMatrix3D;\nclass CVector3D;\nclass CShaderDefines;\nclass CStrIntern;\n\n// Vertex data stream flags\nenum\n{\n\tSTREAM_POS = (1 << 0),\n\tSTREAM_NORMAL = (1 << 1),\n\tSTREAM_COLOR = (1 << 2),\n\tSTREAM_UV0 = (1 << 3),\n\tSTREAM_UV1 = (1 << 4),\n\tSTREAM_UV2 = (1 << 5),\n\tSTREAM_UV3 = (1 << 6),\n\tSTREAM_POSTOUV0 = (1 << 7),\n\tSTREAM_POSTOUV1 = (1 << 8),\n\tSTREAM_POSTOUV2 = (1 << 9),\n\tSTREAM_POSTOUV3 = (1 << 10)\n};\n\n/**\n * A compiled vertex+fragment shader program.\n * The implementation may use GL_ARB_{vertex,fragment}_program (ARB assembly syntax)\n * or GL_ARB_{vertex,fragment}_shader (GLSL), or may use hard-coded fixed-function\n * multitexturing setup code; the difference is hidden from the caller.\n *\n * Texture/uniform IDs are typically strings, corresponding to the names defined in\n * the shader .xml file. Alternatively (and more efficiently, if used very frequently),\n * call GetTextureBinding/GetUniformBinding and pass its return value as the ID.\n * Setting uniforms that the shader .xml doesn't support is harmless.\n * \n * For a high-level overview of shaders and materials, see\n * http://trac.wildfiregames.com/wiki/MaterialSystem\n */\nclass CShaderProgram\n{\n\tNONCOPYABLE(CShaderProgram);\n\npublic:\n\ttypedef CStrIntern attrib_id_t;\n\ttypedef CStrIntern texture_id_t;\n\ttypedef CStrIntern uniform_id_t;\n\ttypedef std::pair<int, GLenum> frag_index_pair_t;\n\t\n\t/**\n\t * Construct based on ARB vertex/fragment program files.\n\t */\n\tstatic CShaderProgram* ConstructARB(const VfsPath& vertexFile, const VfsPath& fragmentFile,\n\t\tconst CShaderDefines& defines,\n\t\tconst std::map<CStrIntern, int>& vertexIndexes, const std::map<CStrIntern, frag_index_pair_t>& fragmentIndexes,\n\t\tint streamflags);\n\n\t/**\n\t * Construct based on GLSL vertex/fragment shader files.\n\t */\n\tstatic CShaderProgram* ConstructGLSL(const VfsPath& vertexFile, const VfsPath& fragmentFile,\n\t\tconst CShaderDefines& defines,\n\t\tconst std::map<CStrIntern, int>& vertexAttribs,\n\t\tint streamflags);\n\n\t/**\n\t * Construct an instance of a pre-defined fixed-function pipeline setup.\n\t */\n\tstatic CShaderProgram* ConstructFFP(const std::string& id, const CShaderDefines& defines);\n\t\n\t/**\n\t * Represents a uniform attribute or texture binding.\n\t * For uniforms:\n\t *  - ARB shaders store vertex location in 'first', fragment location in 'second'.\n\t *  - GLSL shaders store uniform location in 'first', data type in 'second'.\n\t *  - FFP shaders store -1 in 'first', index in 'second'.\n\t * For textures, all store texture target (e.g. GL_TEXTURE_2D) in 'first', texture unit in 'second'.\n\t * Non-existent bindings must store -1 in both.\n\t */\n\tstruct Binding\n\t{\n\t\tBinding(int a, int b) : first(a), second(b) { }\n\n\t\tBinding() : first(-1), second(-1) { }\n\n\t\t/**\n\t\t * Returns whether this uniform attribute is active in the shader.\n\t\t * If not then there's no point calling Uniform() to set its value.\n\t\t */\n\t\tbool Active() { return first != -1 || second != -1; }\n\n\t\tint first;\n\t\tint second;\n\t};\n\n\tvirtual ~CShaderProgram() { }\n\n\tvirtual void Reload() = 0;\n\n\t/**\n\t * Returns whether this shader was successfully loaded.\n\t */\n\tbool IsValid() const;\n\n\t/**\n\t * Binds the shader into the GL context. Call this before calling Uniform()\n\t * or trying to render with it.\n\t */\n\tvirtual void Bind() = 0;\n\n\t/**\n\t * Unbinds the shader from the GL context. Call this after rendering with it.\n\t */\n\tvirtual void Unbind() = 0;\n\n\t/**\n\t * Returns bitset of STREAM_* value, indicating what vertex data streams the\n\t * vertex shader needs (e.g. position, color, UV, ...).\n\t */\n\tint GetStreamFlags() const;\n\n\n\tvirtual Binding GetTextureBinding(texture_id_t id) = 0;\n\n\t// Variants of texture binding:\n\tvoid BindTexture(texture_id_t id, CTexturePtr tex);\n\tvirtual void BindTexture(texture_id_t id, Handle tex) = 0;\n\tvirtual void BindTexture(texture_id_t id, GLuint tex) = 0;\n\tvirtual void BindTexture(Binding id, Handle tex) = 0;\n\n\n\tvirtual Binding GetUniformBinding(uniform_id_t id) = 0;\n\n\t// Uniform-setting methods that subclasses must define:\n\tvirtual void Uniform(Binding id, float v0, float v1, float v2, float v3) = 0;\n\tvirtual void Uniform(Binding id, const CMatrix3D& v) = 0;\n\tvirtual void Uniform(Binding id, size_t count, const CMatrix3D* v) = 0;\n\n\t// Convenient uniform-setting wrappers:\n\n\tvoid Uniform(Binding id, int v);\n\tvoid Uniform(Binding id, float v);\n\tvoid Uniform(Binding id, float v0, float v1);\n\tvoid Uniform(Binding id, const CVector3D& v);\n\tvoid Uniform(Binding id, const CColor& v);\n\n\tvoid Uniform(uniform_id_t id, int v);\n\tvoid Uniform(uniform_id_t id, float v);\n\tvoid Uniform(uniform_id_t id, float v0, float v1);\n\tvoid Uniform(uniform_id_t id, const CVector3D& v);\n\tvoid Uniform(uniform_id_t id, const CColor& v);\n\tvoid Uniform(uniform_id_t id, float v0, float v1, float v2, float v3);\n\tvoid Uniform(uniform_id_t id, const CMatrix3D& v);\n\tvoid Uniform(uniform_id_t id, size_t count, const CMatrix3D* v);\n\n\t// Vertex attribute pointers (equivalent to glVertexPointer etc):\n\n\tvirtual void VertexPointer(GLint size, GLenum type, GLsizei stride, void* pointer);\n\tvirtual void NormalPointer(GLenum type, GLsizei stride, void* pointer);\n\tvirtual void ColorPointer(GLint size, GLenum type, GLsizei stride, void* pointer);\n\tvirtual void TexCoordPointer(GLenum texture, GLint size, GLenum type, GLsizei stride, void* pointer);\n\tvirtual void VertexAttribPointer(attrib_id_t id, GLint size, GLenum type, GLboolean normalized, GLsizei stride, void* pointer);\n\tvirtual void VertexAttribIPointer(attrib_id_t id, GLint size, GLenum type, GLsizei stride, void* pointer);\n\n\t/**\n\t * Checks that all the required vertex attributes have been set.\n\t * Call this before calling glDrawArrays/glDrawElements etc to avoid potential crashes.\n\t */\n\tvoid AssertPointersBound();\n\nprotected:\n\tCShaderProgram(int streamflags);\n\n\tbool m_IsValid;\n\tint m_StreamFlags;\n\n\t// Non-GLSL client state handling:\n\tvoid BindClientStates();\n\tvoid UnbindClientStates();\n\tint m_ValidStreams; // which streams have been specified via VertexPointer etc since the last Bind\n};\n\n#endif // INCLUDED_SHADERPROGRAM\n"
  },
  {
    "path": "fpsgame/graphics/ShaderProgramFFP.cpp",
    "content": "/* Copyright (C) 2014 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"precompiled.h\"\n\n#include \"ShaderProgram.h\"\n\n#include \"graphics/ShaderDefines.h\"\n#include \"graphics/TextureManager.h\"\n#include \"lib/res/graphics/ogl_tex.h\"\n#include \"maths/Matrix3D.h\"\n#include \"maths/Vector3D.h\"\n#include \"ps/CLogger.h\"\n#include \"renderer/Renderer.h\"\n\n#if !CONFIG2_GLES\n\n/**\n * CShaderProgramFFP allows rendering code to use the shader-based API\n * even if the 'shader' is actually implemented with the fixed-function\n * pipeline instead of anything programmable.\n *\n * Currently we just hard-code a number of FFP programs as subclasses of this.\n * If we have lots, it might be nicer to abstract out the common functionality\n * and load these from text files or something.\n */\nclass CShaderProgramFFP : public CShaderProgram\n{\npublic:\n\tCShaderProgramFFP(int streamflags) :\n\t\tCShaderProgram(streamflags)\n\t{\n\t}\n\n\t~CShaderProgramFFP()\n\t{\n\t}\n\n\tvirtual void Reload()\n\t{\n\t\tm_IsValid = true;\n\t}\n\n\tint GetUniformIndex(CStrIntern id)\n\t{\n\t\tstd::map<CStrIntern, int>::iterator it = m_UniformIndexes.find(id);\n\t\tif (it == m_UniformIndexes.end())\n\t\t\treturn -1;\n\t\treturn it->second;\n\t}\n\n\tvirtual Binding GetTextureBinding(uniform_id_t id)\n\t{\n\t\tint index = GetUniformIndex(CStrIntern(id));\n\t\tif (index == -1)\n\t\t\treturn Binding();\n\t\telse\n\t\t\treturn Binding((int)GL_TEXTURE_2D, index);\n\t}\n\n\tvirtual void BindTexture(texture_id_t id, Handle tex)\n\t{\n\t\tint index = GetUniformIndex(CStrIntern(id));\n\t\tif (index != -1)\n\t\t\togl_tex_bind(tex, index);\n\t}\n\n\tvirtual void BindTexture(texture_id_t id, GLuint tex)\n\t{\n\t\tint index = GetUniformIndex(CStrIntern(id));\n\t\tif (index != -1)\n\t\t{\n\t\t\tpglActiveTextureARB((int)(GL_TEXTURE0+index));\n\t\t\tglBindTexture(GL_TEXTURE_2D, tex);\n\t\t}\n\t}\n\n\tvirtual void BindTexture(Binding id, Handle tex)\n\t{\n\t\tint index = id.second;\n\t\tif (index != -1)\n\t\t\togl_tex_bind(tex, index);\n\t}\n\n\tvirtual Binding GetUniformBinding(uniform_id_t id)\n\t{\n\t\treturn Binding(-1, GetUniformIndex(id));\n\t}\n\n\tvirtual void Uniform(Binding UNUSED(id), float UNUSED(v0), float UNUSED(v1), float UNUSED(v2), float UNUSED(v3))\n\t{\n\t}\n\n\tvirtual void Uniform(Binding UNUSED(id), const CMatrix3D& UNUSED(v))\n\t{\n\t}\n\n\tvirtual void Uniform(Binding UNUSED(id), size_t UNUSED(count), const CMatrix3D* UNUSED(v))\n\t{\n\t}\n\nprotected:\n\tstd::map<CStrIntern, int> m_UniformIndexes;\n\n\tvoid SetUniformIndex(const char* id, int value)\n\t{\n\t\tm_UniformIndexes[CStrIntern(id)] = value;\n\t}\n};\n\n//////////////////////////////////////////////////////////////////////////\n\n/**\n * A shader that does nothing but provide a shader-compatible interface to\n * fixed-function features, for compatibility with existing fixed-function\n * code that isn't fully ported to the shader API.\n */\nclass CShaderProgramFFP_Dummy : public CShaderProgramFFP\n{\npublic:\n\tCShaderProgramFFP_Dummy() :\n\t\tCShaderProgramFFP(0)\n\t{\n\t\t// Texture units, for when this shader is used with terrain:\n\t\tSetUniformIndex(\"baseTex\", 0);\n\t}\n\n\tvirtual void Bind()\n\t{\n\t\tBindClientStates();\n\t}\n\n\tvirtual void Unbind()\n\t{\n\t\tUnbindClientStates();\n\t}\n};\n\n//////////////////////////////////////////////////////////////////////////\n\nclass CShaderProgramFFP_OverlayLine : public CShaderProgramFFP\n{\n\t// Uniforms\n\tenum\n\t{\n\t\tID_losTransform,\n\t\tID_objectColor\n\t};\n\n\tbool m_IgnoreLos;\n\tbool m_UseObjectColor;\n\npublic:\n\tCShaderProgramFFP_OverlayLine(const CShaderDefines& defines) :\n\t\tCShaderProgramFFP(0) // will be set manually in initializer below\n\t{\n\t\tSetUniformIndex(\"losTransform\", ID_losTransform);\n\t\tSetUniformIndex(\"objectColor\", ID_objectColor);\n\n\t\t// Texture units:\n\t\tSetUniformIndex(\"baseTex\", 0);\n\t\tSetUniformIndex(\"maskTex\", 1);\n\t\tSetUniformIndex(\"losTex\", 2);\n\n\t\tm_IgnoreLos = (defines.GetInt(\"IGNORE_LOS\") != 0);\n\t\tm_UseObjectColor = (defines.GetInt(\"USE_OBJECTCOLOR\") != 0);\n\n\t\tm_StreamFlags = STREAM_POS | STREAM_UV0 | STREAM_UV1;\n\t\tif (!m_UseObjectColor)\n\t\t\tm_StreamFlags |= STREAM_COLOR;\n\t}\n\n\tbool IsIgnoreLos()\n\t{\n\t\treturn m_IgnoreLos;\n\t}\n\n\tvirtual void Uniform(Binding id, float v0, float v1, float v2, float v3)\n\t{\n\t\tif (id.second == ID_losTransform)\n\t\t{\n\t\t\tpglActiveTextureARB(GL_TEXTURE2);\n\t\t\tGLfloat texgenS1[4] = { v0, 0, 0, v1 };\n\t\t\tGLfloat texgenT1[4] = { 0, 0, v0, v1 };\n\t\t\tglTexGenfv(GL_S, GL_OBJECT_PLANE, texgenS1);\n\t\t\tglTexGenfv(GL_T, GL_OBJECT_PLANE, texgenT1);\n\t\t}\n\t\telse if (id.second == ID_objectColor)\n\t\t{\n\t\t\tfloat c[] = { v0, v1, v2, v3 };\n\t\t\tpglActiveTextureARB(GL_TEXTURE1);\n\t\t\tglTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, c);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tdebug_warn(L\"Invalid id\");\n\t\t}\n\t}\n\n\tvirtual void Uniform(Binding UNUSED(id), const CMatrix3D& UNUSED(v))\n\t{\n\t\tdebug_warn(L\"Not implemented\");\n\t}\n\n\tvirtual void Bind()\n\t{\n\t\t// RGB channels:\n\t\t//   Unit 0: Sample base texture\n\t\t//   Unit 1: Sample mask texture; interpolate with [objectColor or vertexColor] and base, depending on USE_OBJECTCOLOR\n\t\t//   Unit 2: (Load LOS texture; multiply) if not #IGNORE_LOS, pass through otherwise\n\t\t// Alpha channel:\n\t\t//   Unit 0: Sample base texture\n\t\t//   Unit 1: Multiply by objectColor\n\t\t//   Unit 2: Pass through\n\n\t\tpglActiveTextureARB(GL_TEXTURE0);\n\t\tglEnable(GL_TEXTURE_2D);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);\n\n\t\t// Sample base texture RGB\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);\n\n\t\t// Sample base texture Alpha\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);\n\n\t\t// -----------------------------------------------------------------------------\n\n\t\tpglActiveTextureARB(GL_TEXTURE1);\n\t\tglEnable(GL_TEXTURE_2D);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);\n\n\t\t// RGB: interpolate component-wise between [objectColor or vertexColor] and base using mask:\n\t\t//   a0 * a2 + a1 * (1 - a2)\n\t\t// Overridden implementation of Uniform() sets GL_TEXTURE_ENV_COLOR to objectColor, which\n\t\t// is referenced as GL_CONSTANT (see spec)\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_INTERPOLATE);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, m_UseObjectColor ? GL_CONSTANT : GL_PRIMARY_COLOR);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PREVIOUS);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB_ARB, GL_TEXTURE);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB_ARB, GL_SRC_COLOR);\n\n\t\t// ALPHA: Multiply base alpha with [objectColor or vertexColor] alpha\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_MODULATE);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, m_UseObjectColor ? GL_CONSTANT : GL_PRIMARY_COLOR);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA_ARB, GL_PREVIOUS);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA_ARB, GL_SRC_ALPHA);\n\n\t\t// -----------------------------------------------------------------------------\n\n\t\tpglActiveTextureARB(GL_TEXTURE2);\n\t\tglEnable(GL_TEXTURE_2D);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);\n\n\t\tbool ignoreLos = IsIgnoreLos();\n\t\tif (ignoreLos)\n\t\t{\n\t\t\t// RGB pass through\n\t\t\tglTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE);\n\t\t\tglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS);\n\t\t\tglTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Multiply RGB result up till now with LoS texture alpha channel\n\t\t\tglEnable(GL_TEXTURE_GEN_S);\n\t\t\tglEnable(GL_TEXTURE_GEN_T);\n\t\t\tglTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);\n\t\t\tglTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);\n\t\t\t// Overridden implementation of Uniform() sets GL_OBJECT_PLANE values\n\n\t\t\tglTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);\n\t\t\tglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS);\n\t\t\tglTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);\n\t\t\tglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE);\n\t\t\tglTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_ALPHA);\n\t\t}\n\n\t\t// alpha pass through\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);\n\n\t\tBindClientStates();\n\t}\n\n\tvirtual void Unbind()\n\t{\n\t\tUnbindClientStates();\n\n\t\tpglActiveTextureARB(GL_TEXTURE2);\n\t\tglDisable(GL_TEXTURE_2D);\n\n\t\tglDisable(GL_TEXTURE_GEN_S);\n\t\tglDisable(GL_TEXTURE_GEN_T);\n\n\t\tpglActiveTextureARB(GL_TEXTURE1);\n\t\tglDisable(GL_TEXTURE_2D);\n\n\t\tpglActiveTextureARB(GL_TEXTURE0);\n\t\tglDisable(GL_TEXTURE_2D);\n\t}\n};\n\n//////////////////////////////////////////////////////////////////////////\n\nclass CShaderProgramFFP_GuiText : public CShaderProgramFFP\n{\n\t// Uniforms\n\tenum\n\t{\n\t\tID_transform,\n\t\tID_colorMul\n\t};\n\npublic:\n\tCShaderProgramFFP_GuiText() :\n\t\tCShaderProgramFFP(STREAM_POS | STREAM_UV0)\n\t{\n\t\tSetUniformIndex(\"transform\", ID_transform);\n\t\tSetUniformIndex(\"colorMul\", ID_colorMul);\n\n\t\t// Texture units:\n\t\tSetUniformIndex(\"tex\", 0);\n\t}\n\n\tvirtual void Uniform(Binding id, float v0, float v1, float v2, float v3)\n\t{\n\t\tif (id.second == ID_colorMul)\n\t\t\tglColor4f(v0, v1, v2, v3);\n\t}\n\n\tvirtual void Uniform(Binding id, const CMatrix3D& v)\n\t{\n\t\tif (id.second == ID_transform)\n\t\t\tglLoadMatrixf(&v._11);\n\t}\n\n\tvirtual void Bind()\n\t{\n\t\tglMatrixMode(GL_PROJECTION);\n\t\tglPushMatrix();\n\t\tglLoadIdentity();\n\t\tglMatrixMode(GL_MODELVIEW);\n\t\tglPushMatrix();\n\n\t\tpglActiveTextureARB(GL_TEXTURE0);\n\t\tglEnable(GL_TEXTURE_2D);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);\n\n\t\tBindClientStates();\n\t}\n\n\tvirtual void Unbind()\n\t{\n\t\tUnbindClientStates();\n\n\t\tpglActiveTextureARB(GL_TEXTURE0);\n\t\tglDisable(GL_TEXTURE_2D);\n\n\t\tglMatrixMode(GL_PROJECTION);\n\t\tglPopMatrix();\n\t\tglMatrixMode(GL_MODELVIEW);\n\t\tglPopMatrix();\n\t}\n};\n\n//////////////////////////////////////////////////////////////////////////\n\nclass CShaderProgramFFP_Gui_Base : public CShaderProgramFFP\n{\nprotected:\n\t// Uniforms\n\tenum\n\t{\n\t\tID_transform,\n\t\tID_color\n\t};\n\npublic:\n\tCShaderProgramFFP_Gui_Base(int streamflags) :\n\t\tCShaderProgramFFP(streamflags)\n\t{\n\t\tSetUniformIndex(\"transform\", ID_transform);\n\t\tSetUniformIndex(\"color\", ID_color);\n\n\t\t// Texture units:\n\t\tSetUniformIndex(\"tex\", 0);\n\t}\n\n\tvirtual void Uniform(Binding id, const CMatrix3D& v)\n\t{\n\t\tif (id.second == ID_transform)\n\t\t\tglLoadMatrixf(&v._11);\n\t}\n\n\tvirtual void Bind()\n\t{\n\t\tglMatrixMode(GL_PROJECTION);\n\t\tglPushMatrix();\n\t\tglLoadIdentity();\n\t\tglMatrixMode(GL_MODELVIEW);\n\t\tglPushMatrix();\n\n\t\tBindClientStates();\n\t}\n\n\tvirtual void Unbind()\n\t{\n\t\tUnbindClientStates();\n\n\t\tglMatrixMode(GL_PROJECTION);\n\t\tglPopMatrix();\n\t\tglMatrixMode(GL_MODELVIEW);\n\t\tglPopMatrix();\n\t}\n};\n\nclass CShaderProgramFFP_GuiMinimap : public CShaderProgramFFP\n{\nprotected:\n\tCShaderDefines m_Defines;\n\t// Uniforms\n\tenum\n\t{\n\t\tID_transform,\n\t\tID_textureTransform,\n\t\tID_color,\n\t\tID_pointSize,\n\t};\npublic:\n\tCShaderProgramFFP_GuiMinimap(const CShaderDefines& defines) :\n\t\tCShaderProgramFFP(0) // We set the streamflags later, during initialization.\n\t{\n\t\tm_Defines = defines;\n\t\tSetUniformIndex(\"transform\", ID_transform);\n\t\tSetUniformIndex(\"textureTransform\", ID_textureTransform);\n\t\tSetUniformIndex(\"color\", ID_color);\n\t\tSetUniformIndex(\"pointSize\", ID_pointSize);\n\n\t\tif (m_Defines.GetInt(\"MINIMAP_BASE\") || m_Defines.GetInt(\"MINIMAP_LOS\"))\n\t\t{\n\t\t\tSetUniformIndex(\"baseTex\", 0);\n\t\t\tm_StreamFlags = STREAM_POS | STREAM_UV0;\n\t\t}\n\t\telse if (m_Defines.GetInt(\"MINIMAP_POINT\"))\n\t\t\tm_StreamFlags = STREAM_POS | STREAM_COLOR;\n\t\telse\n\t\t\tm_StreamFlags = STREAM_POS;\n\t}\n\n\tvirtual void Uniform(Binding id, const CMatrix3D& v)\n\t{\n\t\tif (id.second == ID_textureTransform)\n\t\t{\n\t\t\tglMatrixMode(GL_TEXTURE);\n\t\t\tglLoadMatrixf(&v._11);\n\t\t}\n\t\telse if (id.second == ID_transform)\n\t\t{\n\t\t\tglMatrixMode(GL_MODELVIEW);\n\t\t\tglLoadMatrixf(&v._11);\n\t\t}\n\t}\n\n\tvirtual void Uniform(Binding id, float v0, float v1, float v2, float v3)\n\t{\n\t\tif (id.second == ID_color)\n\t\t\tglColor4f(v0, v1, v2, v3);\n\t\telse if (id.second == ID_pointSize)\n\t\t\tglPointSize(v0);\n\t}\n\n\tvirtual void Bind()\n\t{\n\t\t// Setup matrix environment\n\t\tglMatrixMode(GL_PROJECTION);\n\t\tglPushMatrix();\n\t\tglLoadIdentity();\n\t\tglMatrixMode(GL_MODELVIEW);\n\t\tglPushMatrix();\n\t\tglLoadIdentity();\n\t\tglMatrixMode(GL_TEXTURE);\n\t\tglPushMatrix();\n\t\tglLoadIdentity();\n\n\t\tBindClientStates();\n\n\t\t// Setup texture environment\n\t\tif (m_Defines.GetInt(\"MINIMAP_BASE\"))\n\t\t{\n\t\t\tpglActiveTextureARB(GL_TEXTURE0);\n\t\t\tglEnable(GL_TEXTURE_2D);\n\t\t\tglTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);\n\t\t\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);\n\t\t\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);\n\t\t}\n\t\telse if (m_Defines.GetInt(\"MINIMAP_LOS\"))\n\t\t{\n\t\t\tpglActiveTextureARB(GL_TEXTURE0);\n\t\t\tglEnable(GL_TEXTURE_2D);\n\t\t\tglTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);\n\t\t\tglTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE);\n\t\t\tglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PRIMARY_COLOR_ARB);\n\t\t\tglTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);\n\t\t\tglTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);\n\t\t\tglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE);\n\t\t\tglTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_ONE_MINUS_SRC_ALPHA);\n\t\t\tglColor3f(0.0f, 0.0f, 0.0f);\n\t\t}\n\t\telse if (m_Defines.GetInt(\"MINIMAP_POINT\"))\n\t\t{\n\t\t\tpglActiveTextureARB(GL_TEXTURE0);\n\t\t\tglDisable(GL_TEXTURE_2D);\n\t\t\tglEnableClientState(GL_VERTEX_ARRAY);\n\t\t\tglEnableClientState(GL_COLOR_ARRAY);\n\t\t}\n\t\telse if (m_Defines.GetInt(\"MINIMAP_LINE\"))\n\t\t{\n\t\t\t// JoshuaJB 13.7.2014: This doesn't seem to do anything on my drivers.\n\t\t\tglEnable(GL_LINE_SMOOTH);\n\t\t}\n\t}\n\n\tvirtual void Unbind()\n\t{\n\t\t// Reset texture environment\n\t\tif (m_Defines.GetInt(\"MINIMAP_POINT\"))\n\t\t{\n\t\t\tpglActiveTextureARB(GL_TEXTURE0);\n\t\t\tglEnable(GL_TEXTURE_2D);\n\t\t\tglDisableClientState(GL_VERTEX_ARRAY);\n\t\t\tglDisableClientState(GL_COLOR_ARRAY);\n\t\t}\n\t\telse if (m_Defines.GetInt(\"MINIMAP_LINE\"))\n\t\t{\n\t\t\tglDisable(GL_LINE_SMOOTH);\n\t\t}\n\n\t\tUnbindClientStates();\n\n\t\t// Clear matrix stack\n\t\tglMatrixMode(GL_TEXTURE);\n\t\tglPopMatrix();\n\t\tglMatrixMode(GL_PROJECTION);\n\t\tglPopMatrix();\n\t\tglMatrixMode(GL_MODELVIEW);\n\t\tglPopMatrix();\n\t}\n};\n\nclass CShaderProgramFFP_GuiBasic : public CShaderProgramFFP_Gui_Base\n{\npublic:\n\tCShaderProgramFFP_GuiBasic() :\n\t\tCShaderProgramFFP_Gui_Base(STREAM_POS | STREAM_UV0)\n\t{\n\t}\n\n\tvirtual void Bind()\n\t{\n\t\tCShaderProgramFFP_Gui_Base::Bind();\n\n\t\tpglActiveTextureARB(GL_TEXTURE0);\n\t\tglEnable(GL_TEXTURE_2D);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);\n\t}\n\n\tvirtual void Unbind()\n\t{\n\t\tpglActiveTextureARB(GL_TEXTURE0);\n\t\tglDisable(GL_TEXTURE_2D);\n\n\t\tCShaderProgramFFP_Gui_Base::Unbind();\n\t}\n};\n\nclass CShaderProgramFFP_GuiAdd : public CShaderProgramFFP_Gui_Base\n{\npublic:\n\tusing CShaderProgramFFP_Gui_Base::Uniform;\n\n\tCShaderProgramFFP_GuiAdd() :\n\t\tCShaderProgramFFP_Gui_Base(STREAM_POS | STREAM_UV0)\n\t{\n\t}\n\n\tvirtual void Uniform(Binding id, float v0, float v1, float v2, float v3)\n\t{\n\t\tif (id.second == ID_color)\n\t\t\tglColor4f(v0, v1, v2, v3);\n\t}\n\n\tvirtual void Bind()\n\t{\n\t\tCShaderProgramFFP_Gui_Base::Bind();\n\n\t\tpglActiveTextureARB(GL_TEXTURE0);\n\t\tglEnable(GL_TEXTURE_2D);\n\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);\n\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_ADD);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_ADD);\n\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);\n\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PREVIOUS);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_PREVIOUS);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA);\n\t}\n\n\tvirtual void Unbind()\n\t{\n\t\tglColor4f(1.f, 1.f, 1.f, 1.f);\n\n\t\tpglActiveTextureARB(GL_TEXTURE0);\n\t\tglDisable(GL_TEXTURE_2D);\n\n\t\tCShaderProgramFFP_Gui_Base::Unbind();\n\t}\n};\n\nclass CShaderProgramFFP_GuiGrayscale : public CShaderProgramFFP_Gui_Base\n{\npublic:\n\tCShaderProgramFFP_GuiGrayscale() :\n\t\tCShaderProgramFFP_Gui_Base(STREAM_POS | STREAM_UV0)\n\t{\n\t}\n\n\tvirtual void Bind()\n\t{\n\t\tCShaderProgramFFP_Gui_Base::Bind();\n\n\t\t/*\n\n\t\tFor the main conversion, use GL_DOT3_RGB, which is defined as\n\t\t    L = 4 * ((Arg0r - 0.5) * (Arg1r - 0.5)+\n\t\t             (Arg0g - 0.5) * (Arg1g - 0.5)+\n\t\t\t         (Arg0b - 0.5) * (Arg1b - 0.5))\n\t\twhere each of the RGB components is given the value 'L'.\n\n\t\tUse the magical luminance formula\n\t\t    L = 0.3R + 0.59G + 0.11B\n\t\tto calculate the greyscale value.\n\n\t\tBut to work around the annoying \"Arg0-0.5\", we need to calculate\n\t\tArg0+0.5. But we also need to scale it into the range 0.5-1.0, else\n\t\tArg0>0.5 will be clamped to 1.0. So use GL_INTERPOLATE, which outputs:\n\t\t    A0 * A2 + A1 * (1 - A2)\n\t\tand set A2 = 0.5, A1 = 1.0, and A0 = texture (i.e. interpolating halfway\n\t\tbetween the texture and {1,1,1}) giving\n\t\t    A0/2 + 0.5\n\t\tand use that as Arg0.\n\t\t\n\t\tSo L = 4*(A0/2 * (Arg1-.5))\n\t\t     = 2 (Rx+Gy+Bz)      (where Arg1 = {x+0.5, y+0.5, z+0.5})\n\t\t\t = 2x R + 2y G + 2z B\n\t\t\t = 0.3R + 0.59G + 0.11B\n\t\tso e.g. 2y = 0.59 = 2(Arg1g-0.5) => Arg1g = 0.59/2+0.5\n\t\twhich fortunately doesn't get clamped.\n\n\t\tSo, just implement that:\n\n\t\t*/\n\n\t\tstatic const float GreyscaleDotColor[4] = {\n\t\t\t0.3f / 2.f + 0.5f,\n\t\t\t0.59f / 2.f + 0.5f,\n\t\t\t0.11f / 2.f + 0.5f,\n\t\t\t1.0f\n\t\t};\n\t\tstatic const float GreyscaleInterpColor0[4] = { 1.0f, 1.0f, 1.0f, 1.0f };\n\t\tstatic const float GreyscaleInterpColor1[4] = { 0.5f, 0.5f, 0.5f, 1.0f };\n\n\t\tpglActiveTextureARB(GL_TEXTURE0);\n\t\tglEnable(GL_TEXTURE_2D);\n\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);\n\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);\n\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE);\n\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);\n\t\tglTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, GreyscaleInterpColor0);\n\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_PREVIOUS);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_COLOR);\n\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);\n\n\t\tglColor4fv(GreyscaleInterpColor1);\n\n\t\tpglActiveTextureARB(GL_TEXTURE1);\n\t\tglEnable(GL_TEXTURE_2D);\n\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);\n\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);\n\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_DOT3_RGB);\n\n\t\t// GL_DOT3_RGB requires GL_(EXT|ARB)_texture_env_dot3.\n\t\t// We currently don't bother implementing a fallback because it's\n\t\t// only lacking on Riva-class HW, but at least want the rest of the\n\t\t// game to run there without errors. Therefore, squelch the\n\t\t// OpenGL error that's raised if they aren't actually present.\n\t\t// Note: higher-level code checks for this extension, but\n\t\t// allows users the choice of continuing even if not present.\n\t\togl_SquelchError(GL_INVALID_ENUM);\n\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);\n\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);\n\t\tglTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, GreyscaleDotColor);\n\n\t\t// To activate the second texture unit, we have to have some kind\n\t\t// of texture bound into it, but we don't actually use the texture data,\n\t\t// so bind a dummy texture\n\t\tg_Renderer.GetTextureManager().GetErrorTexture()->Bind(1);\n\t}\n\n\tvirtual void Unbind()\n\t{\n\t\tglColor4f(1.f, 1.f, 1.f, 1.f);\n\n\t\tpglActiveTextureARB(GL_TEXTURE1);\n\t\tglDisable(GL_TEXTURE_2D);\n\n\t\tpglActiveTextureARB(GL_TEXTURE0);\n\t\tglDisable(GL_TEXTURE_2D);\n\n\t\tCShaderProgramFFP_Gui_Base::Unbind();\n\t}\n};\n\nclass CShaderProgramFFP_GuiSolid : public CShaderProgramFFP_Gui_Base\n{\npublic:\n\tusing CShaderProgramFFP_Gui_Base::Uniform;\n\n\tCShaderProgramFFP_GuiSolid() :\n\t\tCShaderProgramFFP_Gui_Base(STREAM_POS)\n\t{\n\t}\n\n\tvirtual void Uniform(Binding id, float v0, float v1, float v2, float v3)\n\t{\n\t\tif (id.second == ID_color)\n\t\t\tglColor4f(v0, v1, v2, v3);\n\t}\n\n\tvirtual void Bind()\n\t{\n\t\tCShaderProgramFFP_Gui_Base::Bind();\n\n\t\tpglActiveTextureARB(GL_TEXTURE0);\n\t\tglDisable(GL_TEXTURE_2D);\n\t}\n};\n\n//////////////////////////////////////////////////////////////////////////\n\n/**\n * Common functionality for model rendering in the fixed renderpath.\n */\nclass CShaderProgramFFP_Model_Base : public CShaderProgramFFP\n{\nprotected:\n\t// Uniforms\n\tenum\n\t{\n\t\tID_transform,\n\t\tID_objectColor,\n\t\tID_playerColor,\n\t\tID_losTransform\n\t};\n\t\n\tbool m_IgnoreLos;\n\npublic:\n\tCShaderProgramFFP_Model_Base(const CShaderDefines& defines, int streamflags)\n\t\t: CShaderProgramFFP(streamflags)\n\t{\n\t\tSetUniformIndex(\"transform\", ID_transform);\n\t\tSetUniformIndex(\"losTransform\", ID_losTransform);\n\n\t\tif (defines.GetInt(\"USE_OBJECTCOLOR\"))\n\t\t\tSetUniformIndex(\"objectColor\", ID_objectColor);\n\n\t\tif (defines.GetInt(\"USE_PLAYERCOLOR\"))\n\t\t\tSetUniformIndex(\"playerColor\", ID_playerColor);\n\n\t\tm_IgnoreLos = (defines.GetInt(\"IGNORE_LOS\") != 0);\n\n\t\t// Texture units:\n\t\tSetUniformIndex(\"baseTex\", 0);\n\t\tSetUniformIndex(\"losTex\", 3);\n\t}\n\n\tvirtual void Uniform(Binding id, const CMatrix3D& v)\n\t{\n\t\tif (id.second == ID_transform)\n\t\t\tglLoadMatrixf(&v._11);\n\t}\n\t\n\tvirtual void Uniform(Binding id, float v0, float v1, float UNUSED(v2), float UNUSED(v3))\n\t{\n\t\tif (id.second == ID_losTransform)\n\t\t{\n\t\t\tpglActiveTextureARB(GL_TEXTURE3);\n\t\t\tGLfloat texgenS1[4] = { v0, 0, 0, v1 };\n\t\t\tGLfloat texgenT1[4] = { 0, 0, v0, v1 };\n\t\t\tglTexGenfv(GL_S, GL_OBJECT_PLANE, texgenS1);\n\t\t\tglTexGenfv(GL_T, GL_OBJECT_PLANE, texgenT1);\n\t\t}\n\t}\n\n\tvirtual void Bind()\n\t{\n\t\tglMatrixMode(GL_PROJECTION);\n\t\tglPushMatrix();\n\t\tglLoadIdentity();\n\t\tglMatrixMode(GL_MODELVIEW);\n\t\tglPushMatrix();\n\t\t\n\t\t// -----------------------------------------------------------------------------\n\t\t\n\t\tpglActiveTextureARB(GL_TEXTURE3);\n\t\tglEnable(GL_TEXTURE_2D);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);\n\n\t\tif (m_IgnoreLos)\n\t\t{\n\t\t\t// RGB pass through\n\t\t\tglTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE);\n\t\t\tglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS);\n\t\t\tglTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Multiply RGB result up till now with LoS texture alpha channel\n\t\t\tglEnable(GL_TEXTURE_GEN_S);\n\t\t\tglEnable(GL_TEXTURE_GEN_T);\n\t\t\tglTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);\n\t\t\tglTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);\n\t\t\t// Overridden implementation of Uniform() sets GL_OBJECT_PLANE values\n\t\t\t\n\t\t\tglTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);\n\t\t\tglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS);\n\t\t\tglTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);\n\t\t\tglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE);\n\t\t\tglTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_ALPHA);\n\t\t}\n\n\t\t// alpha pass through\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);\n\t\t\n\t\t// -----------------------------------------------------------------------------\n\n\t\tBindClientStates();\n\t}\n\n\tvirtual void Unbind()\n\t{\n\t\tUnbindClientStates();\n\t\t\n\t\tpglActiveTextureARB(GL_TEXTURE3);\n\t\tglDisable(GL_TEXTURE_2D);\n\n\t\tglDisable(GL_TEXTURE_GEN_S);\n\t\tglDisable(GL_TEXTURE_GEN_T);\n\t\t\n\t\tpglActiveTextureARB(GL_TEXTURE0);\n\n\t\tglMatrixMode(GL_PROJECTION);\n\t\tglPopMatrix();\n\t\tglMatrixMode(GL_MODELVIEW);\n\t\tglPopMatrix();\n\t}\n};\n\n/**\n * Basic non-recolored diffuse-textured model rendering.\n */\nclass CShaderProgramFFP_Model : public CShaderProgramFFP_Model_Base\n{\npublic:\n\tCShaderProgramFFP_Model(const CShaderDefines& defines)\n\t\t: CShaderProgramFFP_Model_Base(defines, STREAM_POS | STREAM_COLOR | STREAM_UV0)\n\t{\n\t}\n\n\tvirtual void Bind()\n\t{\n\t\t// Set up texture environment for base pass - modulate texture and vertex color\n\t\tpglActiveTextureARB(GL_TEXTURE0);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);\n\n\t\t// Copy alpha channel from texture\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);\n\n\t\t// The vertex color is scaled by 0.5 to permit overbrightness without clamping.\n\t\t// We therefore need to scale by 2.0 after the modulation, and before any\n\t\t// further clamping, to get the right color.\n\t\tfloat scale2[] = { 2.0f, 2.0f, 2.0f };\n\t\tglTexEnvfv(GL_TEXTURE_ENV, GL_RGB_SCALE, scale2);\n\n\t\tCShaderProgramFFP_Model_Base::Bind();\n\t}\n\n\tvirtual void Unbind()\n\t{\n\t\tCShaderProgramFFP_Model_Base::Unbind();\n\n\t\tpglActiveTextureARB(GL_TEXTURE0);\n\n\t\t// Revert the scaling to default\n\t\tfloat scale1[] = { 1.0f, 1.0f, 1.0f };\n\t\tglTexEnvfv(GL_TEXTURE_ENV, GL_RGB_SCALE, scale1);\n\t}\n};\n\n/**\n * Player-coloring diffuse-textured model rendering.\n */\nclass CShaderProgramFFP_ModelColor : public CShaderProgramFFP_Model_Base\n{\npublic:\n\tCShaderProgramFFP_ModelColor(const CShaderDefines& defines)\n\t\t: CShaderProgramFFP_Model_Base(defines, STREAM_POS | STREAM_COLOR | STREAM_UV0)\n\t{\n\t}\n\n\tvirtual void Uniform(Binding id, float v0, float v1, float v2, float v3)\n\t{\n\t\tif (id.second == ID_objectColor || id.second == ID_playerColor)\n\t\t{\n\t\t\t// (Player color and object color are mutually exclusive)\n\t\t\tfloat color[] = { v0, v1, v2, v3 };\n\t\t\tglTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color);\n\t\t}\n\t}\n\n\tvirtual void Bind()\n\t{\n\t\t// Player color uses a single pass with three texture environments\n\t\t// Note: This uses ARB_texture_env_crossbar (which is checked in GameSetup),\n\t\t// and it requires MAX_TEXTURE_IMAGE_UNITS >= 3 (which only excludes GF2MX/GF4MX)\n\t\t//\n\t\t// We calculate: Result = Color*Texture*(PlayerColor*(1-Texture.a) + 1.0*Texture.a)\n\t\t// Algebra gives us:\n\t\t// Result = (1 - ((1 - PlayerColor) * (1 - Texture.a)))*Texture*Color\n\n\t\t// TexEnv #0\n\t\tpglActiveTextureARB(GL_TEXTURE0);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_ONE_MINUS_SRC_ALPHA);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_ONE_MINUS_SRC_COLOR);\n\n\t\t// Don't care about alpha; set it to something harmless\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);\n\n\t\t// TexEnv #1\n\t\tpglActiveTextureARB(GL_TEXTURE1);\n\t\tglEnable(GL_TEXTURE_2D);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_ONE_MINUS_SRC_COLOR);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);\n\n\t\t// Don't care about alpha; set it to something harmless\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);\n\n\t\t// TexEnv #2\n\t\tpglActiveTextureARB(GL_TEXTURE2);\n\t\tglEnable(GL_TEXTURE_2D);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_TEXTURE0);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);\n\n\t\t// Don't care about alpha; set it to something harmless\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);\n\n\t\tfloat color[] = { 1.0f, 1.0f, 1.0f, 1.0f };\n\t\tglTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color);\n\n\t\t// Scale colors at the end of all the computation (see CShaderProgramFFP_Model::Bind)\n\t\tfloat scale[] = { 2.0f, 2.0f, 2.0f };\n\t\tglTexEnvfv(GL_TEXTURE_ENV, GL_RGB_SCALE, scale);\n\n\t\t// Need to bind some kind of texture to enable the texture units.\n\t\t// Unit 0 has baseTex, but the others need a texture.\n\t\tg_Renderer.GetTextureManager().GetErrorTexture()->Bind(1);\n\t\tg_Renderer.GetTextureManager().GetErrorTexture()->Bind(2);\n\n\t\tCShaderProgramFFP_Model_Base::Bind();\n\t}\n\n\tvirtual void Unbind()\n\t{\n\t\tCShaderProgramFFP_Model_Base::Unbind();\n\n\t\tpglActiveTextureARB(GL_TEXTURE2);\n\t\tglDisable(GL_TEXTURE_2D);\n\n\t\tfloat scale[] = { 1.0f, 1.0f, 1.0f };\n\t\tglTexEnvfv(GL_TEXTURE_ENV, GL_RGB_SCALE, scale);\n\n\t\tpglActiveTextureARB(GL_TEXTURE1);\n\t\tglDisable(GL_TEXTURE_2D);\n\n\t\tpglActiveTextureARB(GL_TEXTURE0);\n\t}\n};\n\n/**\n * Optionally-player-colored untextured model rendering.\n */\nclass CShaderProgramFFP_ModelSolid : public CShaderProgramFFP_Model_Base\n{\npublic:\n\tCShaderProgramFFP_ModelSolid(const CShaderDefines& defines)\n\t\t: CShaderProgramFFP_Model_Base(defines, STREAM_POS)\n\t{\n\t}\n\n\tvirtual void Uniform(Binding id, float v0, float v1, float v2, float v3)\n\t{\n\t\tif (id.second == ID_playerColor)\n\t\t{\n\t\t\tfloat color[] = { v0, v1, v2, v3 };\n\t\t\tglTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color);\n\t\t}\n\t}\n\n\tvirtual void Bind()\n\t{\n\t\tfloat color[] = { 1.0f, 1.0f, 1.0f, 1.0f };\n\t\tglTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color);\n\n\t\tpglActiveTextureARB(GL_TEXTURE0);\n\t\tglEnable(GL_TEXTURE_2D);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_CONSTANT);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_CONSTANT);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);\n\n\t\tCShaderProgramFFP_Model_Base::Bind();\n\t}\n};\n\n/**\n * Plain unlit texture model rendering, for e.g. alpha-blended shadow casters.\n */\nclass CShaderProgramFFP_ModelSolidTex : public CShaderProgramFFP_Model_Base\n{\npublic:\n\tCShaderProgramFFP_ModelSolidTex(const CShaderDefines& defines)\n\t\t: CShaderProgramFFP_Model_Base(defines, STREAM_POS | STREAM_UV0)\n\t{\n\t}\n\n\tvirtual void Bind()\n\t{\n\t\tpglActiveTextureARB(GL_TEXTURE0);\n\t\tglEnable(GL_TEXTURE_2D);\n\t\tglTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);\n\n\t\tCShaderProgramFFP_Model_Base::Bind();\n\t}\n};\n\n//////////////////////////////////////////////////////////////////////////\n\n/*static*/ CShaderProgram* CShaderProgram::ConstructFFP(const std::string& id, const CShaderDefines& defines)\n{\n\tif (id == \"dummy\")\n\t\treturn new CShaderProgramFFP_Dummy();\n\tif (id == \"overlayline\")\n\t\treturn new CShaderProgramFFP_OverlayLine(defines);\n\tif (id == \"gui_text\")\n\t\treturn new CShaderProgramFFP_GuiText();\n\tif (id == \"gui_basic\")\n\t\treturn new CShaderProgramFFP_GuiBasic();\n\tif (id == \"gui_add\")\n\t\treturn new CShaderProgramFFP_GuiAdd();\n\tif (id == \"gui_grayscale\")\n\t\treturn new CShaderProgramFFP_GuiGrayscale();\n\tif (id == \"gui_solid\")\n\t\treturn new CShaderProgramFFP_GuiSolid();\n\tif (id == \"minimap\")\n\t\treturn new CShaderProgramFFP_GuiMinimap(defines);\n\tif (id == \"solid\")\n\t\treturn new CShaderProgramFFP_GuiSolid(); // works for non-GUI objects too\n\tif (id == \"model\")\n\t\treturn new CShaderProgramFFP_Model(defines);\n\tif (id == \"model_color\")\n\t\treturn new CShaderProgramFFP_ModelColor(defines);\n\tif (id == \"model_solid\")\n\t\treturn new CShaderProgramFFP_ModelSolid(defines);\n\tif (id == \"model_solid_tex\")\n\t\treturn new CShaderProgramFFP_ModelSolidTex(defines);\n\n\tLOGERROR(\"CShaderProgram::ConstructFFP: '%s': Invalid id\", id.c_str());\n\tdebug_warn(L\"CShaderProgram::ConstructFFP: Invalid id\");\n\treturn NULL;\n}\n\n#else // CONFIG2_GLES\n\n/*static*/ CShaderProgram* CShaderProgram::ConstructFFP(const std::string& UNUSED(id), const CShaderDefines& UNUSED(defines))\n{\n\tdebug_warn(L\"CShaderProgram::ConstructFFP: FFP not supported on this device\");\n\treturn NULL;\n}\n\n#endif // CONFIG2_GLES\n"
  },
  {
    "path": "fpsgame/graphics/ShaderProgramPtr.h",
    "content": "/* Copyright (C) 2015 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef INCLUDED_SHADERPROGRAMPTR\n#define INCLUDED_SHADERPROGRAMPTR\n\n/*\n * Forward declaration, to reduce the number of header files that have to pull\n * in the whole of ShaderProgram.h\n */\nclass CShaderProgram;\ntypedef std::shared_ptr<CShaderProgram> CShaderProgramPtr;\n\n#endif // INCLUDED_SHADERPROGRAMPTR\n"
  },
  {
    "path": "fpsgame/graphics/ShaderTechnique.cpp",
    "content": "/* Copyright (C) 2012 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"precompiled.h\"\n\n#include \"ShaderTechnique.h\"\n\n#include \"graphics/ShaderProgram.h\"\n\nCShaderPass::CShaderPass() :\n\tm_HasAlpha(false), m_HasBlend(false), m_HasColorMask(false), m_HasDepthMask(false), m_HasDepthFunc(false)\n{\n}\n\nvoid CShaderPass::Bind()\n{\n\tm_Shader->Bind();\n\n#if !CONFIG2_GLES\n\tif (m_HasAlpha)\n\t{\n\t\tglEnable(GL_ALPHA_TEST);\n\t\tglAlphaFunc(m_AlphaFunc, m_AlphaRef);\n\t}\n#endif\n\t// TODO: maybe emit some warning if GLSL shaders try to use alpha test;\n\t// the test should be done inside the shader itself\n\n\tif (m_HasBlend)\n\t{\n\t\tglEnable(GL_BLEND);\n\t\tglBlendFunc(m_BlendSrc, m_BlendDst);\n\t}\n\n\tif (m_HasColorMask)\n\t\tglColorMask(m_ColorMaskR, m_ColorMaskG, m_ColorMaskB, m_ColorMaskA);\n\n\tif (m_HasDepthMask)\n\t\tglDepthMask(m_DepthMask);\n\n\tif (m_HasDepthFunc)\n\t\tglDepthFunc(m_DepthFunc);\n}\n\nvoid CShaderPass::Unbind()\n{\n\tm_Shader->Unbind();\n\n#if !CONFIG2_GLES\n\tif (m_HasAlpha)\n\t\tglDisable(GL_ALPHA_TEST);\n#endif\n\n\tif (m_HasBlend)\n\t\tglDisable(GL_BLEND);\n\n\tif (m_HasColorMask)\n\t\tglColorMask(1, 1, 1, 1);\n\n\tif (m_HasDepthMask)\n\t\tglDepthMask(1);\n\n\tif (m_HasDepthFunc)\n\t\tglDepthFunc(GL_LEQUAL);\n}\n\nvoid CShaderPass::AlphaFunc(GLenum func, GLclampf ref)\n{\n\tm_HasAlpha = true;\n\tm_AlphaFunc = func;\n\tm_AlphaRef = ref;\n}\n\nvoid CShaderPass::BlendFunc(GLenum src, GLenum dst)\n{\n\tm_HasBlend = true;\n\tm_BlendSrc = src;\n\tm_BlendDst = dst;\n}\n\nvoid CShaderPass::ColorMask(GLboolean r, GLboolean g, GLboolean b, GLboolean a)\n{\n\tm_HasColorMask = true;\n\tm_ColorMaskR = r;\n\tm_ColorMaskG = g;\n\tm_ColorMaskB = b;\n\tm_ColorMaskA = a;\n}\n\nvoid CShaderPass::DepthMask(GLboolean mask)\n{\n\tm_HasDepthMask = true;\n\tm_DepthMask = mask;\n}\n\nvoid CShaderPass::DepthFunc(GLenum func)\n{\n\tm_HasDepthFunc = true;\n\tm_DepthFunc = func;\n}\n\n\nCShaderTechnique::CShaderTechnique()\n\t: m_SortByDistance(false)\n{\n}\n\nvoid CShaderTechnique::AddPass(const CShaderPass& pass)\n{\n\tm_Passes.push_back(pass);\n}\n\nint CShaderTechnique::GetNumPasses() const\n{\n\treturn m_Passes.size();\n}\n\nvoid CShaderTechnique::BeginPass(int pass)\n{\n\tENSURE(0 <= pass && pass < (int)m_Passes.size());\n\tm_Passes[pass].Bind();\n}\n\nvoid CShaderTechnique::EndPass(int pass)\n{\n\tENSURE(0 <= pass && pass < (int)m_Passes.size());\n\tm_Passes[pass].Unbind();\n}\n\nconst CShaderProgramPtr& CShaderTechnique::GetShader(int pass) const\n{\n\tENSURE(0 <= pass && pass < (int)m_Passes.size());\n\treturn m_Passes[pass].GetShader();\n}\n\nbool CShaderTechnique::GetSortByDistance() const\n{\n\treturn m_SortByDistance;\n}\n\nvoid CShaderTechnique::SetSortByDistance(bool enable)\n{\n\tm_SortByDistance = enable;\n}\n\nvoid CShaderTechnique::Reset()\n{\n\tm_SortByDistance = false;\n\tm_Passes.clear();\n}\n"
  },
  {
    "path": "fpsgame/graphics/ShaderTechnique.h",
    "content": "/* Copyright (C) 2012 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef INCLUDED_SHADERTECHNIQUE\n#define INCLUDED_SHADERTECHNIQUE\n\n#include \"graphics/ShaderProgramPtr.h\"\n#include \"lib/ogl.h\"\n\n/**\n * Implements a render pass consisting of various GL state changes and a shader,\n * used by CShaderTechnique.\n */\nclass CShaderPass\n{\npublic:\n\tCShaderPass();\n\n\t/**\n\t * Set the shader program used for rendering with this pass.\n\t */\n\tvoid SetShader(const CShaderProgramPtr& shader) { m_Shader = shader; }\n\n\t// Add various bits of GL state to the pass:\n\tvoid AlphaFunc(GLenum func, GLclampf ref);\n\tvoid BlendFunc(GLenum src, GLenum dst);\n\tvoid ColorMask(GLboolean r, GLboolean g, GLboolean b, GLboolean a);\n\tvoid DepthMask(GLboolean mask);\n\tvoid DepthFunc(GLenum func);\n\n\t/**\n\t * Set up all the GL state that was previously specified on this pass.\n\t */\n\tvoid Bind();\n\n\t/**\n\t * Reset the GL state to the default.\n\t */\n\tvoid Unbind();\n\n\tconst CShaderProgramPtr& GetShader() const { return m_Shader; }\n\nprivate:\n\tCShaderProgramPtr m_Shader;\n\n\tbool m_HasAlpha;\n\tGLenum m_AlphaFunc;\n\tGLclampf m_AlphaRef;\n\n\tbool m_HasBlend;\n\tGLenum m_BlendSrc;\n\tGLenum m_BlendDst;\n\n\tbool m_HasColorMask;\n\tGLboolean m_ColorMaskR;\n\tGLboolean m_ColorMaskG;\n\tGLboolean m_ColorMaskB;\n\tGLboolean m_ColorMaskA;\n\n\tbool m_HasDepthMask;\n\tGLboolean m_DepthMask;\n\n\tbool m_HasDepthFunc;\n\tGLenum m_DepthFunc;\n};\n\n/**\n * Implements a render technique consisting of a sequence of passes.\n * CShaderManager loads these from shader effect XML files.\n */\nclass CShaderTechnique\n{\npublic:\n\tCShaderTechnique();\n\tvoid AddPass(const CShaderPass& pass);\n\n\tint GetNumPasses() const;\n\n\tvoid BeginPass(int pass = 0);\n\tvoid EndPass(int pass = 0);\n\tconst CShaderProgramPtr& GetShader(int pass = 0) const;\n\n\t/**\n\t * Whether this technique uses alpha blending that requires objects to be\n\t * drawn from furthest to nearest.\n\t */\n\tbool GetSortByDistance() const;\n\n\tvoid SetSortByDistance(bool enable);\n\n\tvoid Reset();\n\nprivate:\n\tstd::vector<CShaderPass> m_Passes;\n\n\tbool m_SortByDistance;\n};\n\ntypedef shared_ptr<CShaderTechnique> CShaderTechniquePtr;\n\n#endif // INCLUDED_SHADERTECHNIQUE\n"
  },
  {
    "path": "fpsgame/graphics/SkeletonAnim.h",
    "content": "/* Copyright (C) 2016 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n/*\n * Instance of CSkeletonAnimDef for application onto a model\n */\n\n#ifndef INCLUDED_SKELETONANIM\n#define INCLUDED_SKELETONANIM\n\n#include \"maths/BoundingBoxAligned.h\"\n\nclass CSkeletonAnimDef;\n\n\n////////////////////////////////////////////////////////////////////////////////////////\n// CSkeletonAnim: an instance of a CSkeletonAnimDef, for application onto a model\nclass CSkeletonAnim\n{\npublic:\n\t// the name of the action which uses this animation (e.g. \"idle\")\n\tCStr m_Name;\n\t// frequency of the animation\n\tint m_Frequency;\n\t// the raw animation frame data; may be NULL if this is a static 'animation'\n\tCSkeletonAnimDef* m_AnimDef;\n\t// speed at which this animation runs, as a factor of the AnimDef default speed\n\t// (treated as 0 if m_AnimDef == NULL)\n\tfloat m_Speed;\n\t// Times during the animation at which the interesting bits happen,\n\t// as msec times in the range [0, AnimDef->GetDuration],\n\t// or special value -1 if unspecified.\n\t// ActionPos is used for melee hits, projectile launches, etc.\n\t// ActionPos2 is used for loading projectile ammunition.\n\tfloat m_ActionPos;\n\tfloat m_ActionPos2;\n\tfloat m_SoundPos;\n\t// object space bounds of the model when this animation is applied to it\n\tCBoundingBoxAligned m_ObjectBounds;\n};\n\n#endif\n"
  },
  {
    "path": "fpsgame/graphics/SkeletonAnimDef.cpp",
    "content": "/* Copyright (C) 2009 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n/*\n * Raw description of a skeleton animation\n */\n\n#include \"precompiled.h\"\n\n#include \"SkeletonAnimDef.h\"\n#include \"maths/MathUtil.h\"\n#include \"ps/FileIo.h\"\n\n\n///////////////////////////////////////////////////////////////////////////////////////////\n// CSkeletonAnimDef constructor\nCSkeletonAnimDef::CSkeletonAnimDef() : m_FrameTime(0), m_NumKeys(0), m_NumFrames(0), m_Keys(0)\n{\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////\n// CSkeletonAnimDef destructor\nCSkeletonAnimDef::~CSkeletonAnimDef() \n{\n\tdelete[] m_Keys;\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////\n// BuildBoneMatrices: build matrices for all bones at the given time (in MS) in this \n// animation\nvoid CSkeletonAnimDef::BuildBoneMatrices(float time, CMatrix3D* matrices, bool loop) const\n{\n\tfloat fstartframe = time/m_FrameTime;\n\tsize_t startframe = (size_t)(int)(time/m_FrameTime);\n\tfloat deltatime = fstartframe-startframe;\n\n\tstartframe %= m_NumFrames; \n\n\tsize_t endframe = startframe + 1;\n\tendframe %= m_NumFrames; \n\n\tif (!loop && endframe == 0)\n\t{\n\t\t// This might be something like a death animation, and interpolating\n\t\t// between the final frame and the initial frame is wrong, because they're\n\t\t// totally different. So if we've looped around to endframe==0, just display\n\t\t// the animation's final frame with no interpolation.\n\t\tfor (size_t i = 0; i < m_NumKeys; i++)\n\t\t{\n\t\t\tconst Key& key = GetKey(startframe, i);\n\t\t\tmatrices[i].SetIdentity();\n\t\t\tmatrices[i].Rotate(key.m_Rotation);\n\t\t\tmatrices[i].Translate(key.m_Translation);\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor (size_t i = 0; i < m_NumKeys; i++)\n\t\t{\n\t\t\tconst Key& startkey = GetKey(startframe, i);\n\t\t\tconst Key& endkey = GetKey(endframe, i);\n\n\t\t\tCVector3D trans = Interpolate(startkey.m_Translation, endkey.m_Translation, deltatime);\n\t\t\t// TODO: is slerp the best thing to use here?\n\t\t\tCQuaternion rot;\n\t\t\trot.Slerp(startkey.m_Rotation, endkey.m_Rotation, deltatime);\n\n\t\t\trot.ToMatrix(matrices[i]);\n\t\t\tmatrices[i].Translate(trans);\n\t\t}\n\t}\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////\n// Load: try to load the anim from given file; return a new anim if successful\nCSkeletonAnimDef* CSkeletonAnimDef::Load(const VfsPath& filename)\n{\n\tCFileUnpacker unpacker;\n\tunpacker.Read(filename,\"PSSA\");\n\t\t\t\n\t// check version\n\tif (unpacker.GetVersion()<FILE_READ_VERSION) {\n\t\tthrow PSERROR_File_InvalidVersion();\n\t}\n\n\t// unpack the data\n\tCSkeletonAnimDef* anim=new CSkeletonAnimDef;\n\ttry {\n\t\tCStr name; // unused - just here to maintain compatibility with the animation files\n\t\tunpacker.UnpackString(name);\n\t\tunpacker.UnpackRaw(&anim->m_FrameTime,sizeof(anim->m_FrameTime));\n\t\tanim->m_NumKeys = unpacker.UnpackSize();\n\t\tanim->m_NumFrames = unpacker.UnpackSize();\n\t\tanim->m_Keys=new Key[anim->m_NumKeys*anim->m_NumFrames];\n\t\tunpacker.UnpackRaw(anim->m_Keys,anim->m_NumKeys*anim->m_NumFrames*sizeof(Key));\n\t} catch (PSERROR_File&) {\n\t\tdelete anim;\n\t\tthrow;\n\t}\n\n\treturn anim;\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////\n// Save: try to save anim to file\nvoid CSkeletonAnimDef::Save(const VfsPath& pathname,const CSkeletonAnimDef* anim)\n{\n\tCFilePacker packer(FILE_VERSION, \"PSSA\");\n\n\t// pack up all the data\n\tpacker.PackString(\"\");\n\tpacker.PackRaw(&anim->m_FrameTime,sizeof(anim->m_FrameTime));\n\tconst size_t numKeys = anim->m_NumKeys;\n\tpacker.PackSize(numKeys);\n\tconst size_t numFrames = anim->m_NumFrames;\n\tpacker.PackSize(numFrames);\n\tpacker.PackRaw(anim->m_Keys,numKeys*numFrames*sizeof(Key));\n\n\t// now write it\n\tpacker.Write(pathname);\n}\n"
  },
  {
    "path": "fpsgame/graphics/SkeletonAnimDef.h",
    "content": "/* Copyright (C) 2009 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n/*\n * Raw description of a skeleton animation\n */\n\n#ifndef INCLUDED_SKELETONANIMDEF\n#define INCLUDED_SKELETONANIMDEF\n\n#include \"maths/Vector3D.h\"\n#include \"maths/Quaternion.h\"\n#include \"lib/file/vfs/vfs_path.h\"\n\n////////////////////////////////////////////////////////////////////////////////////////\n// CBoneState: structure describing state of a bone at some point\nclass CBoneState \n{\npublic:\n\t// translation of bone relative to root\n\tCVector3D m_Translation;\n\t// rotation of bone relative to root\n\tCQuaternion m_Rotation;\n};\n\n\n////////////////////////////////////////////////////////////////////////////////////////\n// CSkeletonAnimDef: raw description - eg bonestates - of an animation that plays upon \n// a skeleton\nclass CSkeletonAnimDef\n{\npublic:\n\t// current file version given to saved animations\n\tenum { FILE_VERSION = 1 };\n\t// supported file read version - files with a version less than this will be rejected\n\tenum { FILE_READ_VERSION = 1 };\n\n\npublic:\n\t// Key: description of a single key in a skeleton animation\n\ttypedef CBoneState Key;\n\npublic:\n\t// CSkeletonAnimDef constructor + destructor\n\tCSkeletonAnimDef();\n\t~CSkeletonAnimDef();\n\n\t// return the number of keys in this animation\n\tsize_t GetNumKeys() const { return (size_t)m_NumKeys; }\n\n\t// accessors: get a key for given bone at given time\n\tKey& GetKey(size_t frame, size_t bone) { return m_Keys[frame*m_NumKeys+bone]; }\n\tconst Key& GetKey(size_t frame, size_t bone) const { return m_Keys[frame*m_NumKeys+bone]; }\t\n\n\t// get duration of this anim, in ms\n\tfloat GetDuration() const { return m_NumFrames*m_FrameTime; }\n\n\t// return length of each frame, in ms\n\tfloat GetFrameTime() const { return m_FrameTime; }\n\t// return number of frames in animation\n\tsize_t GetNumFrames() const { return (size_t)m_NumFrames; }\n\n\t// build matrices for all bones at the given time (in MS) in this animation\n\tvoid BuildBoneMatrices(float time, CMatrix3D* matrices, bool loop) const;\n\n\t// anim I/O functions\n\tstatic CSkeletonAnimDef* Load(const VfsPath& filename);\n\tstatic void Save(const VfsPath& pathname, const CSkeletonAnimDef* anim);\n\npublic:\n\t// frame time - time between successive frames, in ms\n\tfloat m_FrameTime;\n\t// number of keys in each frame - should match number of bones in the skeleton\n\tsize_t m_NumKeys;\n\t// number of frames in the animation\n\tsize_t m_NumFrames;\n\t// animation data - m_NumKeys*m_NumFrames total keys\n\tKey* m_Keys;\n};\n\n#endif\n"
  },
  {
    "path": "fpsgame/graphics/SkeletonAnimManager.cpp",
    "content": "/* Copyright (C) 2012 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n/*\n * Owner of all skeleton animations\n */\n\n#include \"precompiled.h\"\n\n#include \"SkeletonAnimManager.h\"\n\n#include \"graphics/ColladaManager.h\"\n#include \"graphics/Model.h\"\n#include \"graphics/SkeletonAnimDef.h\"\n#include \"ps/CLogger.h\"\n#include \"ps/FileIo.h\"\n\n\n///////////////////////////////////////////////////////////////////////////////\n// CSkeletonAnimManager constructor\nCSkeletonAnimManager::CSkeletonAnimManager(CColladaManager& colladaManager)\n: m_ColladaManager(colladaManager)\n{\n}\n\n///////////////////////////////////////////////////////////////////////////////\n// CSkeletonAnimManager destructor\nCSkeletonAnimManager::~CSkeletonAnimManager()\n{\n\ttypedef boost::unordered_map<VfsPath,CSkeletonAnimDef*>::iterator Iter;\n\tfor (Iter i = m_Animations.begin(); i != m_Animations.end(); ++i)\n\t\tdelete i->second;\n}\n\n///////////////////////////////////////////////////////////////////////////////\n// GetAnimation: return a given animation by filename; return null if filename \n// doesn't refer to valid animation file\nCSkeletonAnimDef* CSkeletonAnimManager::GetAnimation(const VfsPath& pathname)\n{\n\tVfsPath name = pathname.ChangeExtension(L\"\");\n\n\t// Find if it's already been loaded\n\tboost::unordered_map<VfsPath, CSkeletonAnimDef*>::iterator iter = m_Animations.find(name);\n\tif (iter != m_Animations.end())\n\t\treturn iter->second;\n\n\tCSkeletonAnimDef* def = NULL;\n\n\t// Find the file to load\n\tVfsPath psaFilename = m_ColladaManager.GetLoadablePath(name, CColladaManager::PSA);\n\n\tif (psaFilename.empty())\n\t{\n\t\tLOGERROR(\"Could not load animation '%s'\", pathname.string8());\n\t\tdef = NULL;\n\t}\n\telse\n\t{\n\t\ttry\n\t\t{\n\t\t\tdef = CSkeletonAnimDef::Load(psaFilename);\n\t\t}\n\t\tcatch (PSERROR_File&)\n\t\t{\n\t\t\tLOGERROR(\"Could not load animation '%s'\", psaFilename.string8());\n\t\t}\n\t}\n\n\tif (def)\n\t\tLOGMESSAGE(\"CSkeletonAnimManager::GetAnimation(%s): Loaded successfully\", pathname.string8());\n\telse\n\t\tLOGERROR(\"CSkeletonAnimManager::GetAnimation(%s): Failed loading, marked file as bad\", pathname.string8());\n\n\t// Add to map\n\tm_Animations[name] = def; // NULL if failed to load - we won't try loading it again\n\treturn def;\n}\n"
  },
  {
    "path": "fpsgame/graphics/SkeletonAnimManager.h",
    "content": "/* Copyright (C) 2009 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n/*\n * Owner of all skeleton animations\n */\n\n#ifndef INCLUDED_SKELETONANIMMANAGER\n#define INCLUDED_SKELETONANIMMANAGER\n\n#include <map>\n#include <set>\n#include \"lib/file/vfs/vfs_path.h\"\n#include <boost/unordered_map.hpp>\n\nclass CColladaManager;\nclass CSkeletonAnimDef;\nclass CStr8;\n\n///////////////////////////////////////////////////////////////////////////////\n// CSkeletonAnimManager : owner class of all skeleton anims - manages creation, \n// loading and destruction of animation data\nclass CSkeletonAnimManager\n{\n\tNONCOPYABLE(CSkeletonAnimManager);\npublic:\n\t// constructor, destructor\n\tCSkeletonAnimManager(CColladaManager& colladaManager);\n\t~CSkeletonAnimManager();\n\t\n\t// return a given animation by filename; return null if filename doesn't\n\t// refer to valid animation file\n\tCSkeletonAnimDef* GetAnimation(const VfsPath& pathname);\n\nprivate:\n\t// map of all known animations. Value is NULL if it failed to load.\n\tboost::unordered_map<VfsPath, CSkeletonAnimDef*> m_Animations;\n\n\tCColladaManager& m_ColladaManager;\n};\n\n#endif\n"
  },
  {
    "path": "fpsgame/graphics/Terrain.cpp",
    "content": "/* Copyright (C) 2015 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n/*\n * Describes ground via heightmap and array of CPatch.\n */\n\n#include \"precompiled.h\"\n\n#include \"lib/res/graphics/ogl_tex.h\"\n#include \"lib/sysdep/cpu.h\"\n\n#include \"renderer/Renderer.h\"\n\n#include \"TerrainProperties.h\"\n#include \"TerrainTextureEntry.h\"\n#include \"TerrainTextureManager.h\"\n\n#include <string.h>\n#include \"Terrain.h\"\n#include \"Patch.h\"\n#include \"maths/FixedVector3D.h\"\n#include \"maths/MathUtil.h\"\n#include \"ps/CLogger.h\"\n#include \"simulation2/helpers/Pathfinding.h\"\n\n///////////////////////////////////////////////////////////////////////////////\n// CTerrain constructor\nCTerrain::CTerrain()\n: m_Heightmap(0), m_Patches(0), m_MapSize(0), m_MapSizePatches(0),\nm_BaseColor(255, 255, 255, 255)\n{\n}\n\n///////////////////////////////////////////////////////////////////////////////\n// CTerrain constructor\nCTerrain::~CTerrain()\n{\n\tReleaseData();\n}\n\n\n///////////////////////////////////////////////////////////////////////////////\n// ReleaseData: delete any data allocated by this terrain\nvoid CTerrain::ReleaseData()\n{\n\tm_HeightMipmap.ReleaseData();\n\n\tdelete[] m_Heightmap;\n\tdelete[] m_Patches;\n}\n\n\n///////////////////////////////////////////////////////////////////////////////\n// Initialise: initialise this terrain to the given size\n// using given heightmap to setup elevation data\nbool CTerrain::Initialize(ssize_t patchesPerSide, const u16* data)\n{\n\t// clean up any previous terrain\n\tReleaseData();\n\n\t// store terrain size\n\tm_MapSize = patchesPerSide*PATCH_SIZE+1;\n\tm_MapSizePatches = patchesPerSide;\n\t// allocate data for new terrain\n\tm_Heightmap = new u16[m_MapSize*m_MapSize];\n\tm_Patches = new CPatch[m_MapSizePatches*m_MapSizePatches];\n\n\t// given a heightmap?\n\tif (data)\n\t{\n\t\t// yes; keep a copy of it\n\t\tmemcpy(m_Heightmap, data, m_MapSize*m_MapSize*sizeof(u16));\n\t}\n\telse\n\t{\n\t\t// build a flat terrain\n\t\tmemset(m_Heightmap, 0, m_MapSize*m_MapSize*sizeof(u16));\n\t}\n\n\t// setup patch parents, indices etc\n\tInitialisePatches();\n\n\t// initialise mipmap\n\tm_HeightMipmap.Initialize(m_MapSize, m_Heightmap);\n\n\treturn true;\n}\n\n///////////////////////////////////////////////////////////////////////////////\n\nCStr8 CTerrain::GetMovementClass(ssize_t i, ssize_t j) const\n{\n\tCMiniPatch* tile = GetTile(i, j);\n\tif (tile && tile->GetTextureEntry())\n\t\treturn tile->GetTextureEntry()->GetProperties().GetMovementClass();\n\n\treturn \"default\";\n}\n\n///////////////////////////////////////////////////////////////////////////////\n// CalcPosition: calculate the world space position of the vertex at (i,j)\n// If i,j is off the map, it acts as if the edges of the terrain are extended\n// outwards to infinity\nvoid CTerrain::CalcPosition(ssize_t i, ssize_t j, CVector3D& pos) const\n{\n\tssize_t hi = clamp(i, (ssize_t)0, m_MapSize-1);\n\tssize_t hj = clamp(j, (ssize_t)0, m_MapSize-1);\n\tu16 height = m_Heightmap[hj*m_MapSize + hi];\n\tpos.X = float(i*TERRAIN_TILE_SIZE);\n\tpos.Y = float(height*HEIGHT_SCALE);\n\tpos.Z = float(j*TERRAIN_TILE_SIZE);\n}\n\n///////////////////////////////////////////////////////////////////////////////\n// CalcPositionFixed: calculate the world space position of the vertex at (i,j)\nvoid CTerrain::CalcPositionFixed(ssize_t i, ssize_t j, CFixedVector3D& pos) const\n{\n\tssize_t hi = clamp(i, (ssize_t)0, m_MapSize-1);\n\tssize_t hj = clamp(j, (ssize_t)0, m_MapSize-1);\n\tu16 height = m_Heightmap[hj*m_MapSize + hi];\n\tpos.X = fixed::FromInt(i) * (int)TERRAIN_TILE_SIZE;\n\t// fixed max value is 32767, but height is a u16, so divide by two to avoid overflow\n\tpos.Y = fixed::FromInt(height/ 2 ) / ((int)HEIGHT_UNITS_PER_METRE / 2);\n\tpos.Z = fixed::FromInt(j) * (int)TERRAIN_TILE_SIZE;\n}\n\n\n///////////////////////////////////////////////////////////////////////////////\n// CalcNormal: calculate the world space normal of the vertex at (i,j)\nvoid CTerrain::CalcNormal(ssize_t i, ssize_t j, CVector3D& normal) const\n{\n\tCVector3D left, right, up, down;\n\n\t// Calculate normals of the four half-tile triangles surrounding this vertex:\n\n\t// get position of vertex where normal is being evaluated\n\tCVector3D basepos;\n\tCalcPosition(i, j, basepos);\n\n\tif (i > 0) {\n\t\tCalcPosition(i-1, j, left);\n\t\tleft -= basepos;\n\t\tleft.Normalize();\n\t}\n\n\tif (i < m_MapSize-1) {\n\t\tCalcPosition(i+1, j, right);\n\t\tright -= basepos;\n\t\tright.Normalize();\n\t}\n\n\tif (j > 0) {\n\t\tCalcPosition(i, j-1, up);\n\t\tup -= basepos;\n\t\tup.Normalize();\n\t}\n\n\tif (j < m_MapSize-1) {\n\t\tCalcPosition(i, j+1, down);\n\t\tdown -= basepos;\n\t\tdown.Normalize();\n\t}\n\n\tCVector3D n0 = up.Cross(left);\n\tCVector3D n1 = left.Cross(down);\n\tCVector3D n2 = down.Cross(right);\n\tCVector3D n3 = right.Cross(up);\n\n\t// Compute the mean of the normals\n\tnormal = n0 + n1 + n2 + n3;\n\tfloat nlen=normal.Length();\n\tif (nlen>0.00001f) normal*=1.0f/nlen;\n}\n\n///////////////////////////////////////////////////////////////////////////////\n// CalcNormalFixed: calculate the world space normal of the vertex at (i,j)\nvoid CTerrain::CalcNormalFixed(ssize_t i, ssize_t j, CFixedVector3D& normal) const\n{\n\tCFixedVector3D left, right, up, down;\n\n\t// Calculate normals of the four half-tile triangles surrounding this vertex:\n\n\t// get position of vertex where normal is being evaluated\n\tCFixedVector3D basepos;\n\tCalcPositionFixed(i, j, basepos);\n\n\tif (i > 0) {\n\t\tCalcPositionFixed(i-1, j, left);\n\t\tleft -= basepos;\n\t\tleft.Normalize();\n\t}\n\n\tif (i < m_MapSize-1) {\n\t\tCalcPositionFixed(i+1, j, right);\n\t\tright -= basepos;\n\t\tright.Normalize();\n\t}\n\n\tif (j > 0) {\n\t\tCalcPositionFixed(i, j-1, up);\n\t\tup -= basepos;\n\t\tup.Normalize();\n\t}\n\n\tif (j < m_MapSize-1) {\n\t\tCalcPositionFixed(i, j+1, down);\n\t\tdown -= basepos;\n\t\tdown.Normalize();\n\t}\n\n\tCFixedVector3D n0 = up.Cross(left);\n\tCFixedVector3D n1 = left.Cross(down);\n\tCFixedVector3D n2 = down.Cross(right);\n\tCFixedVector3D n3 = right.Cross(up);\n\n\t// Compute the mean of the normals\n\tnormal = n0 + n1 + n2 + n3;\n\tnormal.Normalize();\n}\n\nCVector3D CTerrain::CalcExactNormal(float x, float z) const\n{\n\t// Clamp to size-2 so we can use the tiles (xi,zi)-(xi+1,zi+1)\n\tconst ssize_t xi = clamp((ssize_t)floor(x/TERRAIN_TILE_SIZE), (ssize_t)0, m_MapSize-2);\n\tconst ssize_t zi = clamp((ssize_t)floor(z/TERRAIN_TILE_SIZE), (ssize_t)0, m_MapSize-2);\n\n\tconst float xf = clamp(x/TERRAIN_TILE_SIZE-xi, 0.0f, 1.0f);\n\tconst float zf = clamp(z/TERRAIN_TILE_SIZE-zi, 0.0f, 1.0f);\n\n\tfloat h00 = m_Heightmap[zi*m_MapSize + xi];\n\tfloat h01 = m_Heightmap[(zi+1)*m_MapSize + xi];\n\tfloat h10 = m_Heightmap[zi*m_MapSize + (xi+1)];\n\tfloat h11 = m_Heightmap[(zi+1)*m_MapSize + (xi+1)];\n\n\t// Determine which terrain triangle this point is on,\n\t// then compute the normal of that triangle's plane\n\n\tif (GetTriangulationDir(xi, zi))\n\t{\n\t\tif (xf + zf <= 1.f)\n\t\t{\n\t\t\t// Lower-left triangle (don't use h11)\n\t\t\treturn -CVector3D(TERRAIN_TILE_SIZE, (h10-h00)*HEIGHT_SCALE, 0).Cross(CVector3D(0, (h01-h00)*HEIGHT_SCALE, TERRAIN_TILE_SIZE)).Normalized();\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Upper-right triangle (don't use h00)\n\t\t\treturn -CVector3D(TERRAIN_TILE_SIZE, (h11-h01)*HEIGHT_SCALE, 0).Cross(CVector3D(0, (h11-h10)*HEIGHT_SCALE, TERRAIN_TILE_SIZE)).Normalized();\n\t\t}\n\t}\n\telse\n\t{\n\t\tif (xf <= zf)\n\t\t{\n\t\t\t// Upper-left triangle (don't use h10)\n\t\t\treturn -CVector3D(TERRAIN_TILE_SIZE, (h11-h01)*HEIGHT_SCALE, 0).Cross(CVector3D(0, (h01-h00)*HEIGHT_SCALE, TERRAIN_TILE_SIZE)).Normalized();\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Lower-right triangle (don't use h01)\n\t\t\treturn -CVector3D(TERRAIN_TILE_SIZE, (h10-h00)*HEIGHT_SCALE, 0).Cross(CVector3D(0, (h11-h10)*HEIGHT_SCALE, TERRAIN_TILE_SIZE)).Normalized();\n\t\t}\n\t}\n}\n\n///////////////////////////////////////////////////////////////////////////////\n// GetPatch: return the patch at (i,j) in patch space, or null if the patch is\n// out of bounds\nCPatch* CTerrain::GetPatch(ssize_t i, ssize_t j) const\n{\n\t// range check (invalid indices are passed in by the culling and\n\t// patch blend code because they iterate from 0..#patches and examine\n\t// neighbors without checking if they're already on the edge)\n\tif( (size_t)i >= (size_t)m_MapSizePatches || (size_t)j >= (size_t)m_MapSizePatches )\n\t\treturn 0;\n\n\treturn &m_Patches[(j*m_MapSizePatches)+i];\n}\n\n\n///////////////////////////////////////////////////////////////////////////////\n// GetTile: return the tile at (i,j) in tile space, or null if the tile is out\n// of bounds\nCMiniPatch* CTerrain::GetTile(ssize_t i, ssize_t j) const\n{\n\t// see comment above\n\tif( (size_t)i >= (size_t)(m_MapSize-1) || (size_t)j >= (size_t)(m_MapSize-1) )\n\t\treturn 0;\n\n\tCPatch* patch=GetPatch(i/PATCH_SIZE, j/PATCH_SIZE);\t// can't fail (due to above check)\n\treturn &patch->m_MiniPatches[j%PATCH_SIZE][i%PATCH_SIZE];\n}\n\nfloat CTerrain::GetVertexGroundLevel(ssize_t i, ssize_t j) const\n{\n\ti = clamp(i, (ssize_t)0, m_MapSize-1);\n\tj = clamp(j, (ssize_t)0, m_MapSize-1);\n\treturn HEIGHT_SCALE * m_Heightmap[j*m_MapSize + i];\n}\n\nfixed CTerrain::GetVertexGroundLevelFixed(ssize_t i, ssize_t j) const\n{\n\ti = clamp(i, (ssize_t)0, m_MapSize-1);\n\tj = clamp(j, (ssize_t)0, m_MapSize-1);\n\t// Convert to fixed metres (being careful to avoid intermediate overflows)\n\treturn fixed::FromInt(m_Heightmap[j*m_MapSize + i] / 2) / (int)(HEIGHT_UNITS_PER_METRE / 2);\n}\n\nfixed CTerrain::GetSlopeFixed(ssize_t i, ssize_t j) const\n{\n\t// Clamp to size-2 so we can use the tiles (i,j)-(i+1,j+1)\n\ti = clamp(i, (ssize_t)0, m_MapSize-2);\n\tj = clamp(j, (ssize_t)0, m_MapSize-2);\n\n\tu16 h00 = m_Heightmap[j*m_MapSize + i];\n\tu16 h01 = m_Heightmap[(j+1)*m_MapSize + i];\n\tu16 h10 = m_Heightmap[j*m_MapSize + (i+1)];\n\tu16 h11 = m_Heightmap[(j+1)*m_MapSize + (i+1)];\n\t\n\t// Difference of highest point from lowest point\n\tu16 delta = std::max(std::max(h00, h01), std::max(h10, h11)) -\n\t            std::min(std::min(h00, h01), std::min(h10, h11));\n\n\t// Compute fractional slope (being careful to avoid intermediate overflows)\n\treturn fixed::FromInt(delta / TERRAIN_TILE_SIZE) / (int)HEIGHT_UNITS_PER_METRE;\n}\n\nfixed CTerrain::GetExactSlopeFixed(fixed x, fixed z) const\n{\n\t// Clamp to size-2 so we can use the tiles (xi,zi)-(xi+1,zi+1)\n\tconst ssize_t xi = clamp((ssize_t)(x / (int)TERRAIN_TILE_SIZE).ToInt_RoundToZero(), (ssize_t)0, m_MapSize-2);\n\tconst ssize_t zi = clamp((ssize_t)(z / (int)TERRAIN_TILE_SIZE).ToInt_RoundToZero(), (ssize_t)0, m_MapSize-2);\n\n\tconst fixed one = fixed::FromInt(1);\n\n\tconst fixed xf = clamp((x / (int)TERRAIN_TILE_SIZE) - fixed::FromInt(xi), fixed::Zero(), one);\n\tconst fixed zf = clamp((z / (int)TERRAIN_TILE_SIZE) - fixed::FromInt(zi), fixed::Zero(), one);\n\n\tu16 h00 = m_Heightmap[zi*m_MapSize + xi];\n\tu16 h01 = m_Heightmap[(zi+1)*m_MapSize + xi];\n\tu16 h10 = m_Heightmap[zi*m_MapSize + (xi+1)];\n\tu16 h11 = m_Heightmap[(zi+1)*m_MapSize + (xi+1)];\n\n\tu16 delta;\n\tif (GetTriangulationDir(xi, zi))\n\t{\n\t\tif (xf + zf <= one)\n\t\t{\n\t\t\t// Lower-left triangle (don't use h11)\n\t\t\tdelta = std::max(std::max(h00, h01), h10) -\n\t\t\t        std::min(std::min(h00, h01), h10);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Upper-right triangle (don't use h00)\n\t\t\tdelta = std::max(std::max(h01, h10), h11) -\n\t\t\t        std::min(std::min(h01, h10), h11);\n\t\t}\n\t}\n\telse\n\t{\n\t\tif (xf <= zf)\n\t\t{\n\t\t\t// Upper-left triangle (don't use h10)\n\t\t\tdelta = std::max(std::max(h00, h01), h11) -\n\t\t\t        std::min(std::min(h00, h01), h11);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Lower-right triangle (don't use h01)\n\t\t\tdelta = std::max(std::max(h00, h10), h11) -\n\t\t\t        std::min(std::min(h00, h10), h11);\n\t\t}\n\t}\n\n\t// Compute fractional slope (being careful to avoid intermediate overflows)\n\treturn fixed::FromInt(delta / TERRAIN_TILE_SIZE) / (int)HEIGHT_UNITS_PER_METRE;\n}\n\nfloat CTerrain::GetFilteredGroundLevel(float x, float z, float radius) const\n{\n\t// convert to [0,1] interval\n\tfloat nx = x / (TERRAIN_TILE_SIZE*m_MapSize);\n\tfloat nz = z / (TERRAIN_TILE_SIZE*m_MapSize);\n\tfloat nr = radius / (TERRAIN_TILE_SIZE*m_MapSize);\n\n\t// get trilinear filtered mipmap height\n\treturn HEIGHT_SCALE * m_HeightMipmap.GetTrilinearGroundLevel(nx, nz, nr);\n}\n\nfloat CTerrain::GetExactGroundLevel(float x, float z) const\n{\n\t// Clamp to size-2 so we can use the tiles (xi,zi)-(xi+1,zi+1)\n\tconst ssize_t xi = clamp((ssize_t)floor(x/TERRAIN_TILE_SIZE), (ssize_t)0, m_MapSize-2);\n\tconst ssize_t zi = clamp((ssize_t)floor(z/TERRAIN_TILE_SIZE), (ssize_t)0, m_MapSize-2);\n\n\tconst float xf = clamp(x/TERRAIN_TILE_SIZE-xi, 0.0f, 1.0f);\n\tconst float zf = clamp(z/TERRAIN_TILE_SIZE-zi, 0.0f, 1.0f);\n\n\tfloat h00 = m_Heightmap[zi*m_MapSize + xi];\n\tfloat h01 = m_Heightmap[(zi+1)*m_MapSize + xi];\n\tfloat h10 = m_Heightmap[zi*m_MapSize + (xi+1)];\n\tfloat h11 = m_Heightmap[(zi+1)*m_MapSize + (xi+1)];\n\n\t// Determine which terrain triangle this point is on,\n\t// then compute the linearly-interpolated height on that triangle's plane\n\n\tif (GetTriangulationDir(xi, zi))\n\t{\n\t\tif (xf + zf <= 1.f)\n\t\t{\n\t\t\t// Lower-left triangle (don't use h11)\n\t\t\treturn HEIGHT_SCALE * (h00 + (h10-h00)*xf + (h01-h00)*zf);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Upper-right triangle (don't use h00)\n\t\t\treturn HEIGHT_SCALE * (h11 + (h01-h11)*(1-xf) + (h10-h11)*(1-zf));\n\t\t}\n\t}\n\telse\n\t{\n\t\tif (xf <= zf)\n\t\t{\n\t\t\t// Upper-left triangle (don't use h10)\n\t\t\treturn HEIGHT_SCALE * (h00 + (h11-h01)*xf + (h01-h00)*zf);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Lower-right triangle (don't use h01)\n\t\t\treturn HEIGHT_SCALE * (h00 + (h10-h00)*xf + (h11-h10)*zf);\n\t\t}\n\t}\n}\n\nfixed CTerrain::GetExactGroundLevelFixed(fixed x, fixed z) const\n{\n\t// Clamp to size-2 so we can use the tiles (xi,zi)-(xi+1,zi+1)\n\tconst ssize_t xi = clamp((ssize_t)(x / (int)TERRAIN_TILE_SIZE).ToInt_RoundToZero(), (ssize_t)0, m_MapSize-2);\n\tconst ssize_t zi = clamp((ssize_t)(z / (int)TERRAIN_TILE_SIZE).ToInt_RoundToZero(), (ssize_t)0, m_MapSize-2);\n\n\tconst fixed one = fixed::FromInt(1);\n\n\tconst fixed xf = clamp((x / (int)TERRAIN_TILE_SIZE) - fixed::FromInt(xi), fixed::Zero(), one);\n\tconst fixed zf = clamp((z / (int)TERRAIN_TILE_SIZE) - fixed::FromInt(zi), fixed::Zero(), one);\n\n\tu16 h00 = m_Heightmap[zi*m_MapSize + xi];\n\tu16 h01 = m_Heightmap[(zi+1)*m_MapSize + xi];\n\tu16 h10 = m_Heightmap[zi*m_MapSize + (xi+1)];\n\tu16 h11 = m_Heightmap[(zi+1)*m_MapSize + (xi+1)];\n\n\t// Intermediate scaling of xf, so we don't overflow in the multiplications below\n\t// (h00 <= 65535, xf <= 1, max fixed is < 32768; divide by 2 here so xf1*h00 <= 32767.5)\n\tconst fixed xf0 = xf / 2;\n\tconst fixed xf1 = (one - xf) / 2;\n\n\t// Linearly interpolate\n\treturn ((one - zf).Multiply(xf1 * h00 + xf0 * h10)\n\t              + zf.Multiply(xf1 * h01 + xf0 * h11)) / (int)(HEIGHT_UNITS_PER_METRE / 2);\n\n\t// TODO: This should probably be more like GetExactGroundLevel()\n\t// in handling triangulation properly\n}\n\nbool CTerrain::GetTriangulationDir(ssize_t i, ssize_t j) const\n{\n\t// Clamp to size-2 so we can use the tiles (i,j)-(i+1,j+1)\n\ti = clamp(i, (ssize_t)0, m_MapSize-2);\n\tj = clamp(j, (ssize_t)0, m_MapSize-2);\n\n\tint h00 = m_Heightmap[j*m_MapSize + i];\n\tint h01 = m_Heightmap[(j+1)*m_MapSize + i];\n\tint h10 = m_Heightmap[j*m_MapSize + (i+1)];\n\tint h11 = m_Heightmap[(j+1)*m_MapSize + (i+1)];\n\n\t// Prefer triangulating in whichever direction means the midpoint of the diagonal\n\t// will be the highest. (In particular this means a diagonal edge will be straight\n\t// along the top, and jagged along the bottom, which makes sense for terrain.)\n\tint mid1 = h00+h11;\n\tint mid2 = h01+h10;\n\treturn (mid1 < mid2);\n}\n\n///////////////////////////////////////////////////////////////////////////////\n// Resize: resize this terrain to the given size (in patches per side)\nvoid CTerrain::Resize(ssize_t size)\n{\n\tif (size==m_MapSizePatches) {\n\t\t// inexplicable request to resize terrain to the same size .. ignore it\n\t\treturn;\n\t}\n\n\tif (!m_Heightmap) {\n\t\t// not yet created a terrain; build a default terrain of the given size now\n\t\tInitialize(size,0);\n\t\treturn;\n\t}\n\n\t// allocate data for new terrain\n\tssize_t newMapSize=size*PATCH_SIZE+1;\n\tu16* newHeightmap=new u16[newMapSize*newMapSize];\n\tCPatch* newPatches=new CPatch[size*size];\n\n\tif (size>m_MapSizePatches) {\n\t\t// new map is bigger than old one - zero the heightmap so we don't get uninitialised\n\t\t// height data along the expanded edges\n\t\tmemset(newHeightmap,0,newMapSize*newMapSize*sizeof(u16));\n\t}\n\n\t// now copy over rows of data\n\tu16* src=m_Heightmap;\n\tu16* dst=newHeightmap;\n\tssize_t copysize=std::min(newMapSize, m_MapSize);\n\tfor (ssize_t j=0;j<copysize;j++) {\n\t\tmemcpy(dst,src,copysize*sizeof(u16));\n\t\tdst+=copysize;\n\t\tsrc+=m_MapSize;\n\t\tif (newMapSize>m_MapSize) {\n\t\t\t// extend the last height to the end of the row\n\t\t\tfor (size_t i=0;i<newMapSize-(size_t)m_MapSize;i++) {\n\t\t\t\t*dst++=*(src-1);\n\t\t\t}\n\t\t}\n\t}\n\n\n\tif (newMapSize>m_MapSize) {\n\t\t// copy over heights of the last row to any remaining rows\n\t\tsrc=newHeightmap+((m_MapSize-1)*newMapSize);\n\t\tdst=src+newMapSize;\n\t\tfor (ssize_t i=0;i<newMapSize-m_MapSize;i++) {\n\t\t\tmemcpy(dst,src,newMapSize*sizeof(u16));\n\t\t\tdst+=newMapSize;\n\t\t}\n\t}\n\n\t// now build new patches\n\tfor (ssize_t j=0;j<size;j++) {\n\t\tfor (ssize_t i=0;i<size;i++) {\n\t\t\t// copy over texture data from existing tiles, if possible\n\t\t\tif (i<m_MapSizePatches && j<m_MapSizePatches) {\n\t\t\t\tmemcpy(newPatches[j*size+i].m_MiniPatches,m_Patches[j*m_MapSizePatches+i].m_MiniPatches,sizeof(CMiniPatch)*PATCH_SIZE*PATCH_SIZE);\n\t\t\t}\n\t\t}\n\n\t\tif (j<m_MapSizePatches && size>m_MapSizePatches) {\n\t\t\t// copy over the last tile from each column\n\t\t\tfor (ssize_t n=0;n<size-m_MapSizePatches;n++) {\n\t\t\t\tfor (ssize_t m=0;m<PATCH_SIZE;m++) {\n\t\t\t\t\tCMiniPatch& src=m_Patches[j*m_MapSizePatches+m_MapSizePatches-1].m_MiniPatches[m][15];\n\t\t\t\t\tfor (ssize_t k=0;k<PATCH_SIZE;k++) {\n\t\t\t\t\t\tCMiniPatch& dst=newPatches[j*size+m_MapSizePatches+n].m_MiniPatches[m][k];\n\t\t\t\t\t\tdst = src;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif (size>m_MapSizePatches) {\n\t\t// copy over the last tile from each column\n\t\tCPatch* srcpatch=&newPatches[(m_MapSizePatches-1)*size];\n\t\tCPatch* dstpatch=srcpatch+size;\n\t\tfor (ssize_t p=0;p<(ssize_t)size-m_MapSizePatches;p++) {\n\t\t\tfor (ssize_t n=0;n<(ssize_t)size;n++) {\n\t\t\t\tfor (ssize_t m=0;m<PATCH_SIZE;m++) {\n\t\t\t\t\tfor (ssize_t k=0;k<PATCH_SIZE;k++) {\n\t\t\t\t\t\tCMiniPatch& src=srcpatch->m_MiniPatches[15][k];\n\t\t\t\t\t\tCMiniPatch& dst=dstpatch->m_MiniPatches[m][k];\n\t\t\t\t\t\tdst = src;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tsrcpatch++;\n\t\t\t\tdstpatch++;\n\t\t\t}\n\t\t}\n\t}\n\n\n\t// release all the original data\n\tReleaseData();\n\n\t// store new data\n\tm_Heightmap=newHeightmap;\n\tm_Patches=newPatches;\n\tm_MapSize=(ssize_t)newMapSize;\n\tm_MapSizePatches=(ssize_t)size;\n\n\t// initialise all the new patches\n\tInitialisePatches();\n\n\t// initialise mipmap\n\tm_HeightMipmap.Initialize(m_MapSize,m_Heightmap);\n}\n\n///////////////////////////////////////////////////////////////////////////////\n// InitialisePatches: initialise patch data\nvoid CTerrain::InitialisePatches()\n{\n\tfor (ssize_t j = 0; j < m_MapSizePatches; j++)\n\t{\n\t\tfor (ssize_t i = 0; i < m_MapSizePatches; i++)\n\t\t{\n\t\t\tCPatch* patch = GetPatch(i, j);\t// can't fail\n\t\t\tpatch->Initialize(this, i, j);\n\t\t}\n\t}\n}\n\n///////////////////////////////////////////////////////////////////////////////\n// SetHeightMap: set up a new heightmap from 16-bit source data;\n// assumes heightmap matches current terrain size\nvoid CTerrain::SetHeightMap(u16* heightmap)\n{\n\t// keep a copy of the given heightmap\n\tmemcpy(m_Heightmap, heightmap, m_MapSize*m_MapSize*sizeof(u16));\n\n\t// recalculate patch bounds, invalidate vertices\n\tfor (ssize_t j = 0; j < m_MapSizePatches; j++)\n\t{\n\t\tfor (ssize_t i = 0; i < m_MapSizePatches; i++)\n\t\t{\n\t\t\tCPatch* patch = GetPatch(i, j);\t// can't fail\n\t\t\tpatch->InvalidateBounds();\n\t\t\tpatch->SetDirty(RENDERDATA_UPDATE_VERTICES);\n\t\t}\n\t}\n\n\t// update mipmap\n\tm_HeightMipmap.Update(m_Heightmap);\n}\n\n\n///////////////////////////////////////////////////////////////////////////////\n\nvoid CTerrain::MakeDirty(ssize_t i0, ssize_t j0, ssize_t i1, ssize_t j1, int dirtyFlags)\n{\n\t// Finds the inclusive limits of the patches that include the specified range of tiles\n\tssize_t pi0 = clamp( i0   /PATCH_SIZE, (ssize_t)0, m_MapSizePatches-1);\n\tssize_t pi1 = clamp((i1-1)/PATCH_SIZE, (ssize_t)0, m_MapSizePatches-1);\n\tssize_t pj0 = clamp( j0   /PATCH_SIZE, (ssize_t)0, m_MapSizePatches-1);\n\tssize_t pj1 = clamp((j1-1)/PATCH_SIZE, (ssize_t)0, m_MapSizePatches-1);\n\n\tfor (ssize_t j = pj0; j <= pj1; j++)\n\t{\n\t\tfor (ssize_t i = pi0; i <= pi1; i++)\n\t\t{\n\t\t\tCPatch* patch = GetPatch(i, j);\t// can't fail (i,j were clamped)\n\t\t\tif (dirtyFlags & RENDERDATA_UPDATE_VERTICES)\n\t\t\t\tpatch->CalcBounds();\n\t\t\tpatch->SetDirty(dirtyFlags);\n\t\t}\n\t}\n\n\tif (m_Heightmap)\n\t{\n\t\tm_HeightMipmap.Update(m_Heightmap,\n\t\t\tclamp(i0, (ssize_t)0, m_MapSize-1),\n\t\t\tclamp(j0, (ssize_t)0, m_MapSize-1),\n\t\t\tclamp(i1, (ssize_t)1, m_MapSize),\n\t\t\tclamp(j1, (ssize_t)1, m_MapSize)\n\t\t);\n\t}\n}\n\nvoid CTerrain::MakeDirty(int dirtyFlags)\n{\n\tfor (ssize_t j = 0; j < m_MapSizePatches; j++)\n\t{\n\t\tfor (ssize_t i = 0; i < m_MapSizePatches; i++)\n\t\t{\n\t\t\tCPatch* patch = GetPatch(i, j);\t// can't fail\n\t\t\tif (dirtyFlags & RENDERDATA_UPDATE_VERTICES)\n\t\t\t\tpatch->CalcBounds();\n\t\t\tpatch->SetDirty(dirtyFlags);\n\t\t}\n\t}\n\n\tif (m_Heightmap)\n\t\tm_HeightMipmap.Update(m_Heightmap);\n}\n\nCBoundingBoxAligned CTerrain::GetVertexesBound(ssize_t i0, ssize_t j0, ssize_t i1, ssize_t j1)\n{\n\ti0 = clamp(i0, (ssize_t)0, m_MapSize-1);\n\tj0 = clamp(j0, (ssize_t)0, m_MapSize-1);\n\ti1 = clamp(i1, (ssize_t)0, m_MapSize-1);\n\tj1 = clamp(j1, (ssize_t)0, m_MapSize-1);\n\n\tu16 minH = 65535;\n\tu16 maxH = 0;\n\n\tfor (ssize_t j = j0; j <= j1; ++j)\n\t{\n\t\tfor (ssize_t i = i0; i <= i1; ++i)\n\t\t{\n\t\t\tminH = std::min(minH, m_Heightmap[j*m_MapSize + i]);\n\t\t\tmaxH = std::max(maxH, m_Heightmap[j*m_MapSize + i]);\n\t\t}\n\t}\n\n\tCBoundingBoxAligned bound;\n\tbound[0].X = (float)(i0*TERRAIN_TILE_SIZE);\n\tbound[0].Y = (float)(minH*HEIGHT_SCALE);\n\tbound[0].Z = (float)(j0*TERRAIN_TILE_SIZE);\n\tbound[1].X = (float)(i1*TERRAIN_TILE_SIZE);\n\tbound[1].Y = (float)(maxH*HEIGHT_SCALE);\n\tbound[1].Z = (float)(j1*TERRAIN_TILE_SIZE);\n\treturn bound;\n}\n"
  },
  {
    "path": "fpsgame/graphics/Terrain.h",
    "content": "/* Copyright (C) 2011 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n/*\n * Describes ground via heightmap and array of CPatch.\n */\n\n#ifndef INCLUDED_TERRAIN\n#define INCLUDED_TERRAIN\n\n#include \"maths/Vector3D.h\"\n#include \"maths/Fixed.h\"\n#include \"graphics/SColor.h\"\n#include \"graphics/HeightMipmap.h\"\n\nclass CPatch;\nclass CMiniPatch;\nclass CFixedVector3D;\nclass CStr8;\nclass CBoundingBoxAligned;\n\n///////////////////////////////////////////////////////////////////////////////\n// Terrain Constants:\n\n/// metres [world space units] per tile in x and z\nconst ssize_t TERRAIN_TILE_SIZE = 4;\n\n/// number of u16 height units per metre\nconst ssize_t HEIGHT_UNITS_PER_METRE = 92;\n\n/// metres per u16 height unit\nconst float HEIGHT_SCALE = 1.f / HEIGHT_UNITS_PER_METRE;\n\n///////////////////////////////////////////////////////////////////////////////\n// CTerrain: main terrain class; contains the heightmap describing elevation\n// data, and the smaller subpatches that form the terrain\nclass CTerrain\n{\npublic:\n\tCTerrain();\n\t~CTerrain();\n\n\t// Coordinate naming convention: world-space coordinates are float x,z;\n\t// tile-space coordinates are ssize_t i,j. rationale: signed types can\n\t// more efficiently be converted to/from floating point. use ssize_t\n\t// instead of int/long because these are sizes.\n\n\tbool Initialize(ssize_t patchesPerSide, const u16* ptr);\n\n\t// return number of vertices along edge of the terrain\n\tssize_t GetVerticesPerSide() const { return m_MapSize; }\n\t// return number of tiles along edge of the terrain\n\tssize_t GetTilesPerSide() const { return GetVerticesPerSide()-1; }\n\t// return number of patches along edge of the terrain\n\tssize_t GetPatchesPerSide() const { return m_MapSizePatches; }\n\n\tfloat GetMinX() const { return 0.0f; }\n\tfloat GetMinZ() const { return 0.0f; }\n\tfloat GetMaxX() const { return (float)((m_MapSize-1) * TERRAIN_TILE_SIZE); }\n\tfloat GetMaxZ() const { return (float)((m_MapSize-1) * TERRAIN_TILE_SIZE); }\n\n\tbool IsOnMap(float x, float z) const\n\t{\n\t\treturn ((x >= GetMinX()) && (x < GetMaxX())\n\t\t     && (z >= GetMinZ()) && (z < GetMaxZ()));\n\t}\n\n\tCStr8 GetMovementClass(ssize_t i, ssize_t j) const;\n\n\tfloat GetVertexGroundLevel(ssize_t i, ssize_t j) const;\n\tfixed GetVertexGroundLevelFixed(ssize_t i, ssize_t j) const;\n\tfloat GetExactGroundLevel(float x, float z) const;\n\tfixed GetExactGroundLevelFixed(fixed x, fixed z) const;\n\tfloat GetFilteredGroundLevel(float x, float z, float radius) const;\n\n\t// get the approximate slope of a tile\n\t// (0 = horizontal, 0.5 = 30 degrees, 1.0 = 45 degrees, etc)\n\tfixed GetSlopeFixed(ssize_t i, ssize_t j) const;\n\n\t// get the precise slope of a point, accounting for triangulation direction\n\tfixed GetExactSlopeFixed(fixed x, fixed z) const;\n\n\t// Returns true if the triangulation diagonal for tile (i, j)\n\t// should be in the direction (1,-1); false if it should be (1,1)\n\tbool GetTriangulationDir(ssize_t i, ssize_t j) const;\n\n\t// resize this terrain such that each side has given number of patches\n\tvoid Resize(ssize_t size);\n\n\t// set up a new heightmap from 16 bit data; assumes heightmap matches current terrain size\n\tvoid SetHeightMap(u16* heightmap);\n\t// return a pointer to the heightmap\n\tu16* GetHeightMap() const { return m_Heightmap; }\n\n\t// get patch at given coordinates, expressed in patch-space; return 0 if\n\t// coordinates represent patch off the edge of the map\n\tCPatch* GetPatch(ssize_t i, ssize_t j) const; \n\t// get tile at given coordinates, expressed in tile-space; return 0 if\n\t// coordinates represent tile off the edge of the map\n\tCMiniPatch* GetTile(ssize_t i, ssize_t j) const;\n\n\t// calculate the position of a given vertex\n\tvoid CalcPosition(ssize_t i, ssize_t j, CVector3D& pos) const;\n\tvoid CalcPositionFixed(ssize_t i, ssize_t j, CFixedVector3D& pos) const;\n\t// calculate the vertex under a given position (rounding down coordinates)\n\tstatic void CalcFromPosition(const CVector3D& pos, ssize_t& i, ssize_t& j)\n\t{\n\t\ti = (ssize_t)(pos.X/TERRAIN_TILE_SIZE);\n\t\tj = (ssize_t)(pos.Z/TERRAIN_TILE_SIZE);\n\t}\n\t// calculate the vertex under a given position (rounding down coordinates)\n\tstatic void CalcFromPosition(float x, float z, ssize_t& i, ssize_t& j)\n\t{\n\t\ti = (ssize_t)(x/TERRAIN_TILE_SIZE);\n\t\tj = (ssize_t)(z/TERRAIN_TILE_SIZE);\n\t}\n\t// calculate the normal at a given vertex\n\tvoid CalcNormal(ssize_t i, ssize_t j, CVector3D& normal) const;\n\tvoid CalcNormalFixed(ssize_t i, ssize_t j, CFixedVector3D& normal) const;\n\n\tCVector3D CalcExactNormal(float x, float z) const;\n\n\t// Mark a specific square of tiles (inclusive lower bound, exclusive upper bound)\n\t// as dirty - use this after modifying the heightmap.\n\t// If you modify a vertex (i,j), you should dirty tiles\n\t// from (i-1, j-1) [inclusive] to (i+1, j+1) [exclusive]\n\t// since their geometry depends on that vertex.\n\t// If you modify a tile (i,j), you should dirty tiles\n\t// from (i-1, j-1) [inclusive] to (i+2, j+2) [exclusive]\n\t// since their texture blends depend on that tile.\n\tvoid MakeDirty(ssize_t i0, ssize_t j0, ssize_t i1, ssize_t j1, int dirtyFlags);\n\t// mark the entire map as dirty\n\tvoid MakeDirty(int dirtyFlags);\n\n\t/**\n\t * Returns a 3D bounding box encompassing the given vertex range (inclusive)\n\t */\n\tCBoundingBoxAligned GetVertexesBound(ssize_t i0, ssize_t j0, ssize_t i1, ssize_t j1);\n\n\t// get the base color for the terrain (typically pure white - other colors\n\t// will interact badly with LOS - but used by the Actor Viewer tool)\n\tSColor4ub GetBaseColor() const { return m_BaseColor; }\n\t// set the base color for the terrain\n\tvoid SetBaseColor(SColor4ub color) { m_BaseColor = color; }\n\n\tconst CHeightMipmap& GetHeightMipmap() const { return m_HeightMipmap; }\n\nprivate:\n\t// delete any data allocated by this terrain\n\tvoid ReleaseData();\n\t// setup patch pointers etc\n\tvoid InitialisePatches();\n\n\t// size of this map in each direction, in vertices; ie. total tiles = sqr(m_MapSize-1)\n\tssize_t m_MapSize;\n\t// size of this map in each direction, in patches; total patches = sqr(m_MapSizePatches)\n\tssize_t m_MapSizePatches;\n\t// the patches comprising this terrain\n\tCPatch*\tm_Patches;\n\t// 16-bit heightmap data\n\tu16* m_Heightmap;\n\t// base color (usually white)\n\tSColor4ub m_BaseColor;\n\t// heightmap mipmap\n\tCHeightMipmap m_HeightMipmap;\n};\n\n#endif\n"
  },
  {
    "path": "fpsgame/graphics/TerrainProperties.cpp",
    "content": "/* Copyright (C) 2015 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"precompiled.h\"\n#include \"TerrainProperties.h\"\n\n#include <string>\n#include <vector>\n\n#include <boost/tokenizer.hpp>\n\n#include \"TerrainTextureManager.h\"\n#include \"maths/MathUtil.h\"\n#include \"ps/CLogger.h\"\n#include \"ps/Filesystem.h\"\n#include \"ps/Shapes.h\"\n#include \"ps/XML/XeroXMB.h\"\n#include \"ps/XML/Xeromyces.h\"\n\nCTerrainProperties::CTerrainProperties(CTerrainPropertiesPtr parent):\n\tm_pParent(parent),\n\tm_BaseColor(0),\n\tm_HasBaseColor(false),\n\tm_TextureAngle((float)M_PI / 4.f),\n\tm_TextureSize(32.f),\n\tm_MovementClass(\"default\")\n{\n\tif (m_pParent)\n\t\tm_Groups = m_pParent->m_Groups;\t\n}\n\nCTerrainPropertiesPtr CTerrainProperties::FromXML(const CTerrainPropertiesPtr& parent, const VfsPath& pathname)\n{\n\tCXeromyces XeroFile;\n\tif (XeroFile.Load(g_VFS, pathname, \"terrain\") != PSRETURN_OK)\n\t\treturn CTerrainPropertiesPtr();\n\n\tXMBElement root = XeroFile.GetRoot();\n\tCStr rootName = XeroFile.GetElementString(root.GetNodeName());\n\n\t// Check that we've got the right kind of xml document\n\tif (rootName != \"Terrains\")\n\t{\n\t\tLOGERROR(\"TerrainProperties: Loading %s: Root node is not terrains (found \\\"%s\\\")\",\n\t\t\tpathname.string8(),\n\t\t\trootName);\n\t\treturn CTerrainPropertiesPtr();\n\t}\n\t\n\t#define ELMT(x) int el_##x = XeroFile.GetElementID(#x)\n\tELMT(terrain);\n\t#undef ELMT\n\t\n\t// Ignore all non-terrain nodes, loading the first terrain node and\n\t// returning it.\n\t// Really, we only expect there to be one child and it to be of the right\n\t// type, though.\n\tXERO_ITER_EL(root, child)\n\t{\n\t\tif (child.GetNodeName() == el_terrain)\n\t\t{\n\t\t\tCTerrainPropertiesPtr ret (new CTerrainProperties(parent));\n\t\t\tret->LoadXml(child, &XeroFile, pathname);\n\t\t\treturn ret;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tLOGWARNING(\"TerrainProperties: Loading %s: Unexpected node %s\\n\",\n\t\t\t\tpathname.string8(),\n\t\t\t\tXeroFile.GetElementString(child.GetNodeName()));\n\t\t\t// Keep reading - typos shouldn't be showstoppers\n\t\t}\n\t}\n\t\n\treturn CTerrainPropertiesPtr();\n}\n\nvoid CTerrainProperties::LoadXml(XMBElement node, CXeromyces *pFile, const VfsPath& UNUSED(pathname))\n{\n\t#define ELMT(x) int elmt_##x = pFile->GetElementID(#x)\n\t#define ATTR(x) int attr_##x = pFile->GetAttributeID(#x)\n\t// Terrain Attribs\n\tATTR(mmap);\n\tATTR(groups);\n\tATTR(movementclass);\n\tATTR(angle);\n\tATTR(size);\n\t#undef ELMT\n\t#undef ATTR\n\n\tXERO_ITER_ATTR(node, attr)\n\t{\n\t\tif (attr.Name == attr_groups)\n\t\t{\n\t\t\t// Parse a comma-separated list of groups, add the new entry to\n\t\t\t// each of them\n\t\t\tm_Groups.clear();\n\t\t\tboost::char_separator<char> sep(\", \");\n\t\t\ttypedef boost::tokenizer<boost::char_separator<char> > tokenizer;\n\t\t\ttokenizer tok(attr.Value, sep);\n\t\t\tfor(tokenizer::iterator it = tok.begin(); it != tok.end(); ++it)\n\t\t\t\tm_Groups.push_back(g_TexMan.FindGroup(*it));\n\t\t}\n\t\telse if (attr.Name == attr_mmap)\n\t\t{\n\t\t\tCColor col;\n\t\t\tif (!col.ParseString(attr.Value, 255))\n\t\t\t\tcontinue;\n\t\t\t\n\t\t\t// m_BaseColor is BGRA\n\t\t\tu8 *baseColor = (u8*)&m_BaseColor;\n\t\t\tbaseColor[0] = (u8)(col.b*255);\n\t\t\tbaseColor[1] = (u8)(col.g*255);\n\t\t\tbaseColor[2] = (u8)(col.r*255);\n\t\t\tbaseColor[3] = (u8)(col.a*255);\n\t\t\tm_HasBaseColor = true;\n\t\t}\n\t\telse if (attr.Name == attr_angle)\n\t\t{\n\t\t\tm_TextureAngle = DEGTORAD(attr.Value.ToFloat());\n\t\t}\n\t\telse if (attr.Name == attr_size)\n\t\t{\n\t\t\tm_TextureSize = attr.Value.ToFloat();\n\t\t}\n\t\telse if (attr.Name == attr_movementclass)\n\t\t{\n\t\t\tm_MovementClass = attr.Value;\n\t\t}\n\t}\n}\n\nbool CTerrainProperties::HasBaseColor()\n{\n\treturn m_HasBaseColor || (m_pParent && m_pParent->HasBaseColor());\n}\n\nu32 CTerrainProperties::GetBaseColor()\n{\n\tif (m_HasBaseColor || !m_pParent)\n\t\treturn m_BaseColor;\n\telse if (m_pParent)\n\t\treturn m_pParent->GetBaseColor();\n\telse\n\t\t// White, full opacity.. but this value shouldn't ever be used\n\t\treturn 0xFFFFFFFF;\n}\n"
  },
  {
    "path": "fpsgame/graphics/TerrainProperties.h",
    "content": "/* Copyright (C) 2015 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n/*\n///////////////////////////////////////////////\n\tCTerrainProperties\n\n\tBasically represents a set of terrain attributes loaded from XML. These\n\tobjects are organized in an inheritance tree, determined at load time.\n\n*/\n\n#ifndef INCLUDED_TERRAINPROPERTIES\n#define INCLUDED_TERRAINPROPERTIES\n\n#include <memory>\n\n#include \"ps/CStr.h\"\n#include \"lib/file/vfs/vfs_path.h\"\n\nclass CTerrainGroup;\nclass XMBElement;\nclass CXeromyces;\nclass CTerrainProperties;\n\ntypedef shared_ptr<CTerrainProperties> CTerrainPropertiesPtr;\n\nclass CTerrainProperties\n{\npublic:\n\ttypedef std::vector<CTerrainGroup *> GroupVector;\n\nprivate:\n\tCTerrainPropertiesPtr m_pParent;\n\n\t// BGRA color of topmost mipmap level, for coloring minimap, or a color\n\t// manually specified in the Terrain XML (or by any parent)\n\t// ..Valid is true if the base color is specified in this terrain XML\n\t// No caching here, since ideally, a saved XML file of an object should\n\t// produce be equivalent to the source file\n\tu32 m_BaseColor;\n\tbool m_HasBaseColor;\n\n\tCStr m_MovementClass;\n\n\t// Orientation of texture (in radians) (default pi/4 = 45 degrees)\n\tfloat m_TextureAngle;\n\n\t// Size of texture in metres (default 32m = 8 tiles)\n\tfloat m_TextureSize;\n\n\t// All terrain type groups we're a member of\n\tGroupVector m_Groups;\n\npublic:\n\tCTerrainProperties(CTerrainPropertiesPtr parent);\n\n\t// Create a new object and load the XML file specified. Returns NULL upon\n\t// failure\n\t// The parent pointer may be NULL, for the \"root\" terrainproperties object.\n\tstatic CTerrainPropertiesPtr FromXML(const CTerrainPropertiesPtr& parent, const VfsPath& pathname);\n\t\n\tvoid LoadXml(XMBElement node, CXeromyces *pFile, const VfsPath& pathname);\n\n\t// Save the object to an XML file. Implement when needed! ;-)\n\t// bool WriteXML(const CStr& path);\n\n\tinline CTerrainPropertiesPtr GetParent() const\n\t{\n\t\treturn m_pParent;\n\t}\n\n\t// Return true if this property object or any of its parents has a basecolor\n\t// override (mmap attribute in the XML file)\n\tbool HasBaseColor();\n\t// Return the minimap color specified in this property object or in any of\n\t// its parents. If no minimap color is specified, return garbage.\n\t// Use HasBaseColor() to see if the value is valid.\n\t// The color value is in BGRA format\n\tu32 GetBaseColor();\n\n\tfloat GetTextureAngle()\n\t{\n\t\treturn m_TextureAngle;\n\t}\n\n\tfloat GetTextureSize()\n\t{\n\t\treturn m_TextureSize;\n\t}\n\n\tCStr GetMovementClass() const\n\t{\n\t\treturn m_MovementClass;\n\t}\n\n\tconst GroupVector &GetGroups() const\n\t{\n\t\treturn m_Groups;\n\t}\n};\n\n#endif\n"
  },
  {
    "path": "fpsgame/graphics/TerrainTextureEntry.cpp",
    "content": "/* Copyright (C) 2015 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"precompiled.h\"\n\n#include \"TerrainTextureEntry.h\"\n\n#include \"lib/utf8.h\"\n#include \"lib/ogl.h\"\n#include \"lib/allocators/shared_ptr.h\"\n#include \"lib/res/graphics/ogl_tex.h\"\n\n#include \"ps/CLogger.h\"\n#include \"ps/Filesystem.h\"\n#include \"ps/XML/Xeromyces.h\"\n\n#include \"graphics/MaterialManager.h\"\n#include \"graphics/Terrain.h\"\n#include \"graphics/TerrainTextureManager.h\"\n#include \"graphics/TerrainProperties.h\"\n#include \"graphics/Texture.h\"\n#include \"renderer/Renderer.h\"\n\n#include <map>\n\nCTerrainTextureEntry::CTerrainTextureEntry(CTerrainPropertiesPtr properties, const VfsPath& path):\n\tm_pProperties(properties),\n\tm_BaseColor(0),\n\tm_BaseColorValid(false)\n{\n\tENSURE(properties);\n\t\n\tCXeromyces XeroFile;\n\tif (XeroFile.Load(g_VFS, path, \"terrain_texture\") != PSRETURN_OK)\n\t{\n\t\tLOGERROR(\"Terrain xml not found (%s)\", path.string8());\n\t\treturn;\n\t}\n\n\t#define EL(x) int el_##x = XeroFile.GetElementID(#x)\n\t#define AT(x) int at_##x = XeroFile.GetAttributeID(#x)\n\tEL(tag);\n\tEL(terrain);\n\tEL(texture);\n\tEL(textures);\n\tEL(material);\n\tEL(props);\n\tEL(alphamap);\n\tAT(file);\n\tAT(name);\n\t#undef AT\n\t#undef EL\n\t\n\t\n\tXMBElement root = XeroFile.GetRoot();\n\n\tif (root.GetNodeName() != el_terrain)\n\t{\n\t\tLOGERROR(\"Invalid terrain format (unrecognised root element '%s')\", XeroFile.GetElementString(root.GetNodeName()).c_str());\n\t\treturn;\n\t}\n\t\n\t\n\tstd::vector<std::pair<CStr, VfsPath> > samplers;\n\tVfsPath alphamap(\"standard\");\n\tm_Tag = utf8_from_wstring(path.Basename().string());\n\t\n\t\n\tXERO_ITER_EL(root, child)\n\t{\n\t\tint child_name = child.GetNodeName();\n\n\t\tif (child_name == el_textures)\n\t\t{\n\t\t\tXERO_ITER_EL(child, textures_element)\n\t\t\t{\n\t\t\t\tENSURE(textures_element.GetNodeName() == el_texture);\n\t\t\t\t\n\t\t\t\tCStr name;\n\t\t\t\tVfsPath path;\n\t\t\t\tXERO_ITER_ATTR(textures_element, se)\n\t\t\t\t{\n\t\t\t\t\tif (se.Name == at_file)\n\t\t\t\t\t\tpath = VfsPath(\"art/textures/terrain\") / se.Value.FromUTF8();\n\t\t\t\t\telse if (se.Name == at_name)\n\t\t\t\t\t\tname = se.Value;\n\t\t\t\t}\n\t\t\t\tsamplers.emplace_back(name, path);\n\t\t\t}\n\t\t\n\t\t}\n\t\telse if (child_name == el_material)\n\t\t{\n\t\t\tVfsPath mat = VfsPath(\"art/materials\") / child.GetText().FromUTF8();\n\t\t\tif (CRenderer::IsInitialised())\n\t\t\t\tm_Material = g_Renderer.GetMaterialManager().LoadMaterial(mat);\n\t\t}\n\t\telse if (child_name == el_alphamap)\n\t\t{\n\t\t\talphamap = child.GetText().FromUTF8();\n\t\t}\n\t\telse if (child_name == el_props)\n\t\t{\n\t\t\tCTerrainPropertiesPtr ret (new CTerrainProperties(properties));\n\t\t\tret->LoadXml(child, &XeroFile, path);\n\t\t\tif (ret) m_pProperties = ret;\n\t\t}\n\t\telse if (child_name == el_tag)\n\t\t{\n\t\t\tm_Tag = child.GetText();\n\t\t}\n\t}\n\t\n\t\n\tfor (size_t i = 0; i < samplers.size(); ++i)\n\t{\n\t\tCTextureProperties texture(samplers[i].second);\n\t\ttexture.SetWrap(GL_REPEAT);\n\n\t\t// TODO: anisotropy should probably be user-configurable, but we want it to be\n\t\t// at least 2 for terrain else the ground looks very blurry when you tilt the\n\t\t// camera upwards\n\t\ttexture.SetMaxAnisotropy(2.0f);\n\n\t\tif (CRenderer::IsInitialised())\n\t\t{\n\t\t\tCTexturePtr texptr = g_Renderer.GetTextureManager().CreateTexture(texture);\n\t\t\tm_Material.AddSampler(CMaterial::TextureSampler(samplers[i].first, texptr));\n\t\t}\n\t}\n\n\tif (CRenderer::IsInitialised())\n\t\tLoadAlphaMaps(alphamap);\n\n\tfloat texAngle = 0.f;\n\tfloat texSize = 1.f;\n\n\tif (m_pProperties)\n\t{\n\t\tm_Groups = m_pProperties->GetGroups();\n\t\ttexAngle = m_pProperties->GetTextureAngle();\n\t\ttexSize = m_pProperties->GetTextureSize();\n\t}\n\t\n\tm_TextureMatrix.SetZero();\n\tm_TextureMatrix._11 = cosf(texAngle) / texSize;\n\tm_TextureMatrix._13 = -sinf(texAngle) / texSize;\n\tm_TextureMatrix._21 = -sinf(texAngle) / texSize;\n\tm_TextureMatrix._23 = -cosf(texAngle) / texSize;\n\tm_TextureMatrix._44 = 1.f;\n\n\tGroupVector::iterator it=m_Groups.begin();\n\tfor (;it!=m_Groups.end();++it)\n\t\t(*it)->AddTerrain(this);\n}\n\nCTerrainTextureEntry::~CTerrainTextureEntry()\n{\n\tfor (GroupVector::iterator it=m_Groups.begin();it!=m_Groups.end();++it)\n\t\t(*it)->RemoveTerrain(this);\n}\n\n// BuildBaseColor: calculate the root color of the texture, used for coloring minimap, and store\n// in m_BaseColor member\nvoid CTerrainTextureEntry::BuildBaseColor()\n{\n\t// Use the explicit properties value if possible\n\tif (m_pProperties && m_pProperties->HasBaseColor())\n\t{\n\t\tm_BaseColor=m_pProperties->GetBaseColor();\n\t\tm_BaseColorValid = true;\n\t\treturn;\n\t}\n\n\t// Use the texture color if available\n\tif (GetTexture()->TryLoad())\n\t{\n\t\tm_BaseColor = GetTexture()->GetBaseColor();\n\t\tm_BaseColorValid = true;\n\t}\n}\n\nconst float* CTerrainTextureEntry::GetTextureMatrix()\n{\n\treturn &m_TextureMatrix._11;\n}\n\n// LoadAlphaMaps: load the 14 default alpha maps, pack them into one composite texture and\n// calculate the coordinate of each alphamap within this packed texture\nvoid CTerrainTextureEntry::LoadAlphaMaps(VfsPath &amtype)\n{\n\tstd::wstring key = L\"(alpha map composite\" + amtype.string() + L\")\";\n\n\tCTerrainTextureManager::TerrainAlphaMap::iterator it = g_TexMan.m_TerrainAlphas.find(amtype);\n\t\n\tif (it != g_TexMan.m_TerrainAlphas.end())\n\t{\n\t\tm_TerrainAlpha = it;\n\t\treturn;\n\t}\n\t\n\tg_TexMan.m_TerrainAlphas[amtype] = TerrainAlpha();\n\tit = g_TexMan.m_TerrainAlphas.find(amtype);\n\t\n\tTerrainAlpha &result = it->second;\n\n\t//\n\t// load all textures and store Handle in array\n\t//\n\tHandle textures[NUM_ALPHA_MAPS] = {0};\n\tVfsPath path(L\"art/textures/terrain/alphamaps\");\n\tpath = path / amtype;\n\t\n\tconst wchar_t* fnames[NUM_ALPHA_MAPS] = {\n\t\tL\"blendcircle.png\",\n\t\tL\"blendlshape.png\",\n\t\tL\"blendedge.png\",\n\t\tL\"blendedgecorner.png\",\n\t\tL\"blendedgetwocorners.png\",\n\t\tL\"blendfourcorners.png\",\n\t\tL\"blendtwooppositecorners.png\",\n\t\tL\"blendlshapecorner.png\",\n\t\tL\"blendtwocorners.png\",\n\t\tL\"blendcorner.png\",\n\t\tL\"blendtwoedges.png\",\n\t\tL\"blendthreecorners.png\",\n\t\tL\"blendushape.png\",\n\t\tL\"blendbad.png\"\n\t};\n\tsize_t base = 0;\t// texture width/height (see below)\n\t// for convenience, we require all alpha maps to be of the same BPP\n\t// (avoids another ogl_tex_get_size call, and doesn't hurt)\n\tsize_t bpp = 0;\n\tfor(size_t i=0;i<NUM_ALPHA_MAPS;i++)\n\t{\n\t\t// note: these individual textures can be discarded afterwards;\n\t\t// we cache the composite.\n\t\ttextures[i] = ogl_tex_load(g_VFS, path / fnames[i]);\n\t\tif (textures[i] < 0)\n\t\t{\n\t\t\tg_TexMan.m_TerrainAlphas.erase(it);\n\t\t\tLOGERROR(\"Failed to load alphamap: %s\", amtype.string8());\n\t\t\t\n\t\t\tVfsPath standard(\"standard\");\n\t\t\tif (path != standard)\n\t\t\t{\n\t\t\t\tLoadAlphaMaps(standard);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// get its size and make sure they are all equal.\n\t\t// (the packing algo assumes this)\n\t\tsize_t this_width = 0, this_height = 0, this_bpp = 0;\t// fail-safe\n\t\t(void)ogl_tex_get_size(textures[i], &this_width, &this_height, &this_bpp);\n\t\tif(this_width != this_height)\n\t\t\tDEBUG_DISPLAY_ERROR(L\"Alpha maps are not square\");\n\t\t// .. first iteration: establish size\n\t\tif(i == 0)\n\t\t{\n\t\t\tbase = this_width;\n\t\t\tbpp  = this_bpp;\n\t\t}\n\t\t// .. not first: make sure texture size matches\n\t\telse if(base != this_width || bpp != this_bpp)\n\t\t\tDEBUG_DISPLAY_ERROR(L\"Alpha maps are not identically sized (including pixel depth)\");\n\t}\n\n\t//\n\t// copy each alpha map (tile) into one buffer, arrayed horizontally.\n\t//\n\tsize_t tile_w = 2+base+2;\t// 2 pixel border (avoids bilinear filtering artifacts)\n\tsize_t total_w = round_up_to_pow2(tile_w * NUM_ALPHA_MAPS);\n\tsize_t total_h = base; ENSURE(is_pow2(total_h));\n\tshared_ptr<u8> data;\n\tAllocateAligned(data, total_w*total_h, maxSectorSize);\n\t// for each tile on row\n\tfor (size_t i = 0; i < NUM_ALPHA_MAPS; i++)\n\t{\n\t\t// get src of copy\n\t\tu8* src = 0;\n\t\t(void)ogl_tex_get_data(textures[i], &src);\n\n\t\tsize_t srcstep = bpp/8;\n\n\t\t// get destination of copy\n\t\tu8* dst = data.get() + (i*tile_w);\n\n\t\t// for each row of image\n\t\tfor (size_t j = 0; j < base; j++)\n\t\t{\n\t\t\t// duplicate first pixel\n\t\t\t*dst++ = *src;\n\t\t\t*dst++ = *src;\n\n\t\t\t// copy a row\n\t\t\tfor (size_t k = 0; k < base; k++)\n\t\t\t{\n\t\t\t\t*dst++ = *src;\n\t\t\t\tsrc += srcstep;\n\t\t\t}\n\n\t\t\t// duplicate last pixel\n\t\t\t*dst++ = *(src-srcstep);\n\t\t\t*dst++ = *(src-srcstep);\n\n\t\t\t// advance write pointer for next row\n\t\t\tdst += total_w-tile_w;\n\t\t}\n\n\t\tresult.m_AlphaMapCoords[i].u0 = float(i*tile_w+2) / float(total_w);\n\t\tresult.m_AlphaMapCoords[i].u1 = float((i+1)*tile_w-2) / float(total_w);\n\t\tresult.m_AlphaMapCoords[i].v0 = 0.0f;\n\t\tresult.m_AlphaMapCoords[i].v1 = 1.0f;\n\t}\n\n\tfor (size_t i = 0; i < NUM_ALPHA_MAPS; i++)\n\t\t(void)ogl_tex_free(textures[i]);\n\n\t// upload the composite texture\n\tTex t;\n\t(void)t.wrap(total_w, total_h, 8, TEX_GREY, data, 0);\n\t\n\t// uncomment the following to save a png of the generated texture\n\t// in the public/ directory, for debugging\n\t/*VfsPath filename(\"blendtex.png\");\n\t\n\tDynArray da;\n\tRETURN_STATUS_IF_ERR(tex_encode(&t, filename.Extension(), &da));\n\n\t// write to disk\n\t//Status ret = INFO::OK;\n\t{\n\t\tshared_ptr<u8> file = DummySharedPtr(da.base);\n\t\tconst ssize_t bytes_written = g_VFS->CreateFile(filename, file, da.pos);\n\t\tif(bytes_written > 0)\n\t\t\tENSURE(bytes_written == (ssize_t)da.pos);\n\t\t//else\n\t\t//\tret = (Status)bytes_written;\n\t}\n\n\t(void)da_free(&da);*/\n\t\n\tHandle hCompositeAlphaMap = ogl_tex_wrap(&t, g_VFS, key);\n\t(void)ogl_tex_set_filter(hCompositeAlphaMap, GL_LINEAR);\n\t(void)ogl_tex_set_wrap  (hCompositeAlphaMap, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE);\n\togl_tex_upload(hCompositeAlphaMap, GL_ALPHA, 0, 0);\n\tresult.m_hCompositeAlphaMap = hCompositeAlphaMap;\n\t\n\tm_TerrainAlpha = it;\n}\n"
  },
  {
    "path": "fpsgame/graphics/TerrainTextureEntry.h",
    "content": "/* Copyright (C) 2012 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef INCLUDED_TERRAINTEXTUREENTRY\n#define INCLUDED_TERRAINTEXTUREENTRY\n\n#include <map>\n\n#include \"TerrainTextureManager.h\"\n#include \"TextureManager.h\"\n#include \"Material.h\"\n\n#include \"lib/res/handle.h\"\n#include \"lib/file/vfs/vfs_path.h\"\n#include \"maths/Matrix3D.h\"\n#include \"ps/CStr.h\"\n\nclass XMBElement;\nclass CXeromyces;\n\n//////////////////////////////////////////////////////////////////////////////////////////////////////////\n// CTerrainTextureEntry: class wrapping a terrain texture object; contains various other required\n// elements - color of minimap, terrain \"group\" it belongs to, etc\nclass CTerrainTextureEntry\n{\npublic:\n\ttypedef std::vector<CTerrainGroup *> GroupVector;\n\nprivate:\n\t// Tag = file name stripped of path and extension (grass_dark_1)\n\tCStr m_Tag;\n\t\n\t// The property sheet used by this texture\n\tCTerrainPropertiesPtr m_pProperties;\n\t\n\tCMaterial m_Material;\n\n\tCMatrix3D m_TextureMatrix;\n\t\n\t// BGRA color of topmost mipmap level, for coloring minimap, or a color\n\t// specified by the terrain properties\n\tu32 m_BaseColor;\n\t// ..Valid is true if the base color has been cached\n\tbool m_BaseColorValid;\n\t\n\t// All terrain type groups we're a member of\n\tGroupVector m_Groups;\n\n\t// calculate the root color of the texture, used for coloring minimap\n\tvoid BuildBaseColor();\n\t\n\tvoid LoadAlphaMaps(VfsPath &amtype);\n\npublic:\n\t// Most of the texture's data is delay-loaded, so after the constructor has\n\t// been called, the texture entry is ready to be used.\n\tCTerrainTextureEntry(CTerrainPropertiesPtr props, const VfsPath& path);\n\t~CTerrainTextureEntry();\n\n\tCStr GetTag() const\n\t{ return m_Tag; }\n\t\n\tconst CTerrainProperties& GetProperties() const\n\t{ return *m_pProperties; }\n\t\n\t// Get texture handle, load texture if not loaded.\n\tconst CTexturePtr& GetTexture() {\n\t\treturn m_Material.GetDiffuseTexture();\n\t}\n\t\n\tconst CMaterial& GetMaterial() {\n\t\treturn m_Material;\n\t}\n\n\t// Returns a matrix of the form [c 0 -s 0; -s 0 -c 0; 0 0 0 0; 0 0 0 1]\n\t// mapping world-space (x,y,z,1) coordinates onto (u,v,0,1) texcoords\n\tconst float* GetTextureMatrix();\n\n\t// Get mipmap color in BGRA format\n\tu32 GetBaseColor() {\n\t\tif (!m_BaseColorValid) BuildBaseColor();\n\t\treturn m_BaseColor;\n\t}\n\t\n\t//TerrainAlpha *m_TerrainAlpha;\n\tCTerrainTextureManager::TerrainAlphaMap::iterator m_TerrainAlpha;\n};\n\n#endif \n"
  },
  {
    "path": "fpsgame/graphics/TerrainTextureManager.cpp",
    "content": "/* Copyright (C) 2016 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"precompiled.h\"\n\n#include <algorithm>\n#include <vector>\n\n#include \"TerrainTextureManager.h\"\n#include \"TerrainTextureEntry.h\"\n#include \"TerrainProperties.h\"\n\n#include \"lib/res/graphics/ogl_tex.h\"\n#include \"lib/ogl.h\"\n#include \"lib/timer.h\"\n\n#include \"ps/CLogger.h\"\n#include \"ps/Filesystem.h\"\n#include \"ps/XML/Xeromyces.h\"\n\n#include <boost/algorithm/string.hpp>\n\n\nCTerrainTextureManager::CTerrainTextureManager()\n\t: m_LastGroupIndex(0)\n{\n\tif (!VfsDirectoryExists(L\"art/terrains/\"))\n\t\treturn;\n\tif (!CXeromyces::AddValidator(g_VFS, \"terrain\", \"art/terrains/terrain.rng\"))\n\t\tLOGERROR(\"CTerrainTextureManager: failed to load grammar file 'art/terrains/terrain.rng'\");\n\tif (!CXeromyces::AddValidator(g_VFS, \"terrain_texture\", \"art/terrains/terrain_texture.rng\"))\n\t\tLOGERROR(\"CTerrainTextureManager: failed to load grammar file 'art/terrains/terrain_texture.rng'\");\n}\n\nCTerrainTextureManager::~CTerrainTextureManager()\n{\n\tUnloadTerrainTextures();\n\t\n\tfor (std::pair<const VfsPath, TerrainAlpha>& ta : m_TerrainAlphas)\n\t{\n\t\togl_tex_free(ta.second.m_hCompositeAlphaMap);\n\t\tta.second.m_hCompositeAlphaMap = 0;\n\t}\n}\n\nvoid CTerrainTextureManager::UnloadTerrainTextures()\n{\n\tfor (CTerrainTextureEntry* const& te : m_TextureEntries)\n\t\tdelete te;\n\tm_TextureEntries.clear();\n\n\tfor (const std::pair<CStr, CTerrainGroup*>& tg : m_TerrainGroups)\n\t\tdelete tg.second;\n\tm_TerrainGroups.clear();\n\n\tm_LastGroupIndex = 0;\n}\n\nCTerrainTextureEntry* CTerrainTextureManager::FindTexture(const CStr& tag_) const\n{\n\tCStr tag = tag_.BeforeLast(\".\"); // Strip extension\n\n\tfor (CTerrainTextureEntry* const& te : m_TextureEntries)\n\t\tif (te->GetTag() == tag)\n\t\t\treturn te;\n\n\tLOGWARNING(\"CTerrainTextureManager: Couldn't find terrain %s\", tag.c_str());\n\treturn 0;\n}\n\nCTerrainTextureEntry* CTerrainTextureManager::AddTexture(const CTerrainPropertiesPtr& props, const VfsPath& path)\n{\n\tCTerrainTextureEntry* entry = new CTerrainTextureEntry(props, path);\n\tm_TextureEntries.push_back(entry);\n\treturn entry;\n}\n\nvoid CTerrainTextureManager::DeleteTexture(CTerrainTextureEntry* entry)\n{\n\tstd::vector<CTerrainTextureEntry*>::iterator it = std::find(m_TextureEntries.begin(), m_TextureEntries.end(), entry);\n\tif (it != m_TextureEntries.end())\n\t\tm_TextureEntries.erase(it);\n\n\tdelete entry;\n}\n\nstruct AddTextureCallbackData\n{\n\tCTerrainTextureManager* self;\n\tCTerrainPropertiesPtr props;\n};\n\nstatic Status AddTextureDirCallback(const VfsPath& pathname, const uintptr_t cbData)\n{\n\tAddTextureCallbackData& data = *(AddTextureCallbackData*)cbData;\n\tVfsPath path = pathname / L\"terrains.xml\";\n\tif (!VfsFileExists(path))\n\t\tLOGMESSAGE(\"'%s' does not exist. Using previous properties.\", path.string8());\n\telse\n\t\tdata.props = CTerrainProperties::FromXML(data.props, path);\n\n\treturn INFO::OK;\n}\n\nstatic Status AddTextureCallback(const VfsPath& pathname, const CFileInfo& UNUSED(fileInfo), const uintptr_t cbData)\n{\n\tAddTextureCallbackData& data = *(AddTextureCallbackData*)cbData;\n\tif (pathname.Basename() != L\"terrains\")\n\t\tdata.self->AddTexture(data.props, pathname);\n\n\treturn INFO::OK;\n}\n\nint CTerrainTextureManager::LoadTerrainTextures()\n{\n\tAddTextureCallbackData data = {this, CTerrainPropertiesPtr(new CTerrainProperties(CTerrainPropertiesPtr()))};\n\tvfs::ForEachFile(g_VFS, L\"art/terrains/\", AddTextureCallback, (uintptr_t)&data, L\"*.xml\", vfs::DIR_RECURSIVE, AddTextureDirCallback, (uintptr_t)&data);\n\treturn 0;\n}\n\nCTerrainGroup* CTerrainTextureManager::FindGroup(const CStr& name)\n{\n\tTerrainGroupMap::const_iterator it = m_TerrainGroups.find(name);\n\tif (it != m_TerrainGroups.end())\n\t\treturn it->second;\n\telse\n\t\treturn m_TerrainGroups[name] = new CTerrainGroup(name, ++m_LastGroupIndex);\n}\n\nvoid CTerrainGroup::AddTerrain(CTerrainTextureEntry* pTerrain)\n{\n\tm_Terrains.push_back(pTerrain);\n}\n\nvoid CTerrainGroup::RemoveTerrain(CTerrainTextureEntry* pTerrain)\n{\n\tstd::vector<CTerrainTextureEntry*>::iterator it = find(m_Terrains.begin(), m_Terrains.end(), pTerrain);\n\tif (it != m_Terrains.end())\n\t\tm_Terrains.erase(it);\n}\n"
  },
  {
    "path": "fpsgame/graphics/TerrainTextureManager.h",
    "content": "/* Copyright (C) 2015 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef INCLUDED_TERRAINTEXTUREMANAGER\n#define INCLUDED_TERRAINTEXTUREMANAGER\n\n#include <map>\n#include <memory>\n#include <vector>\n\n#include \"lib/res/graphics/ogl_tex.h\"\n#include \"lib/res/handle.h\"\n#include \"lib/file/vfs/vfs_path.h\"\n#include \"ps/CStr.h\"\n#include \"ps/Singleton.h\"\n\n// access to sole CTerrainTextureManager object\n#define g_TexMan CTerrainTextureManager::GetSingleton()\n\n#define NUM_ALPHA_MAPS 14\n\nclass XMBElement;\nclass CXeromyces;\nclass CTerrainTextureEntry;\nclass CTerrainProperties;\n\ntypedef shared_ptr<CTerrainProperties> CTerrainPropertiesPtr;\n\nclass CTerrainGroup\n{\n\t// name of this terrain group (as specified by the terrain XML)\n\tCStr m_Name;\n\t// \"index\".. basically a bogus integer that can be used by ScEd to set texture\n\t// priorities\n\tsize_t m_Index;\n\t// list of textures of this type (found from the texture directory)\n\tstd::vector<CTerrainTextureEntry*> m_Terrains;\n\npublic:\n\tCTerrainGroup(CStr name, size_t index):\n\t\tm_Name(name),\n\t\tm_Index(index)\n\t{}\n\t\n\t// Add a texture entry to this terrain type\n\tvoid AddTerrain(CTerrainTextureEntry*);\n\t// Remove a texture entry\n\tvoid RemoveTerrain(CTerrainTextureEntry*);\n\n\tsize_t GetIndex() const\n\t{ return m_Index; }\n\tCStr GetName() const\n\t{ return m_Name; }\n\n\tconst std::vector<CTerrainTextureEntry*> &GetTerrains() const\n\t{ return m_Terrains; }\n};\n\n\nstruct TerrainAlpha\n{\n\t// ogl_tex handle of composite alpha map (all the alpha maps packed into one texture)\n\tHandle m_hCompositeAlphaMap;\n\t// coordinates of each (untransformed) alpha map within the packed texture\n\tstruct {\n\t\tfloat u0,u1,v0,v1;\n\t} m_AlphaMapCoords[NUM_ALPHA_MAPS];\n};\n\n\n///////////////////////////////////////////////////////////////////////////////////////////\n// CTerrainTextureManager : manager class for all terrain texture objects\nclass CTerrainTextureManager : public Singleton<CTerrainTextureManager>\n{\n\tfriend class CTerrainTextureEntry;\n\t\npublic:\n\ttypedef std::map<CStr, CTerrainGroup*> TerrainGroupMap;\n\ttypedef std::map<VfsPath, TerrainAlpha> TerrainAlphaMap;\n\nprivate:\n\t// All texture entries created by this class, for easy freeing now that\n\t// textures may be in several STextureType's\n\tstd::vector<CTerrainTextureEntry*> m_TextureEntries;\n\n\tTerrainGroupMap m_TerrainGroups;\n\t\n\tTerrainAlphaMap m_TerrainAlphas;\n\n\tsize_t m_LastGroupIndex;\n\npublic:\n\t// constructor, destructor\n\tCTerrainTextureManager();\n\t~CTerrainTextureManager();\n\n\t// Find all XML's in the directory (with subdirs) and try to load them as\n\t// terrain XML's\n\tint LoadTerrainTextures();\n\n\tvoid UnloadTerrainTextures();\n\t\n\tCTerrainTextureEntry* FindTexture(const CStr& tag) const;\n\t\n\t// Create a texture object for a new terrain texture at path, using the\n\t// property sheet props.\n\tCTerrainTextureEntry* AddTexture(const CTerrainPropertiesPtr& props, const VfsPath& path);\n\t\n\t// Remove the texture from all our maps and lists and delete it afterwards.\n\tvoid DeleteTexture(CTerrainTextureEntry* entry);\n\t\n\t// Find or create a new texture group. All terrain groups are owned by the\n\t// texturemanager (TerrainTypeManager)\n\tCTerrainGroup* FindGroup(const CStr& name);\n\n\tconst TerrainGroupMap& GetGroups() const\n\t{ return m_TerrainGroups; }\n};\n\n\n#endif\n"
  },
  {
    "path": "fpsgame/graphics/TerritoryBoundary.cpp",
    "content": "/* Copyright (C) 2012 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"precompiled.h\"\n#include \"TerritoryBoundary.h\"\n\n#include <algorithm> // for reverse\n\n#include \"graphics/Terrain.h\"\n#include \"simulation2/helpers/Pathfinding.h\"\n#include \"simulation2/components/ICmpTerritoryManager.h\"\n\nstd::vector<STerritoryBoundary> CTerritoryBoundaryCalculator::ComputeBoundaries(const Grid<u8>* territory)\n{\n\tstd::vector<STerritoryBoundary> boundaries;\n\n\t// Copy the territories grid so we can mess with it\n\tGrid<u8> grid(*territory);\n\n\t// Some constants for the border walk\n\tCVector2D edgeOffsets[] = {\n\t\tCVector2D(0.5f, 0.0f),\n\t\tCVector2D(1.0f, 0.5f),\n\t\tCVector2D(0.5f, 1.0f),\n\t\tCVector2D(0.0f, 0.5f)\n\t};\n\n\t// syntactic sugar\n\tconst u8 TILE_BOTTOM = 0;\n\tconst u8 TILE_RIGHT = 1;\n\tconst u8 TILE_TOP = 2;\n\tconst u8 TILE_LEFT = 3;\n\n\tconst int CURVE_CW = -1;\n\tconst int CURVE_CCW = 1;\n\n\t// === Find territory boundaries ===\n\t// \n\t// The territory boundaries delineate areas of tiles that belong to the same player, and that all have the same \n\t// connected-to-a-root-influence-entity status (see also STerritoryBoundary for a more wordy definition). Note that the grid \n\t// values contain bit-packed information (i.e. not just the owning player ID), so we must be careful to only compare grid \n\t// values using the player ID and connected flag bits. The joint mask to select these is referred to as the discriminator mask.\n\t// \n\t// The idea is to scan the (i,j)-grid going up row by row and look for tiles that have a different territory assignment from \n\t// the one right underneath it (or, if it's a tile on the first row, they need only have a territory assignment). These tiles \n\t// are necessarily edge tiles of a territory, and hence a territory boundary must pass through their bottom edge. Therefore, \n\t// we start tracing the outline of the territory starting from said bottom edge, and go CCW around the territory boundary.\n\t// Tracing continues until the starting point is reached, at which point the boundary is complete.\n\t// \n\t// While tracing a boundary, every tile in which the boundary passes through the bottom edge are marked as 'processed', so that\n\t// we know not to start a new run from these tiles when scanning continues (when the boundary is complete). This information \n\t// is maintained in the grid values themselves by means of the 'processed' bit mask (stressing the importance of using the \n\t// discriminator mask to compare only player ID and connected flag).\n\t// \n\t// Thus, we can identify the following conditions for starting a trace from a tile (i,j). Let g(i,j) indicate the \n\t// discriminator grid value at position (i,j); then the conditions are:\n\t//     - g(i,j) != 0; the tile must not be neutral\n\t//     - j=0 or g(i,j) != g(i,j-1);  the tile directly underneath it must have a different owner and/or connected flag\n\t//     - the tile must not already be marked as 'processed'\n\t// \n\t// Additionally, there is one more point to be made; the algorithm initially assumes it's tracing CCW around the territory.\n\t// If it's tracing an inner edge, however, this will actually cause it to trace in the CW direction (because inner edges curve \n\t// 'backwards' compared to the outer edges when starting the trace in the same direction). This turns out to actually be \n\t// exactly what the renderer needs to render two territory boundaries on the same edge back-to-back (instead of overlapping\n\t// each other).\n\t// \n\t// In either case, we keep track of the way the outline curves while we're tracing to determine whether we're going CW or CCW.\n\t// If at some point we ever need to revert the winding order or external code needs to know about it explicitly, then we can\n\t// do this by looking at a curvature value which we define to start at 0, and which is incremented by 1 for every CCW turn and\n\t// decremented by 1 for every CW turn. Hence, a negative multiple of 4 means a CW winding order, and a positive one means CCW.\n\t\n\tconst int TERRITORY_DISCR_MASK = (ICmpTerritoryManager::TERRITORY_BLINKING_MASK | ICmpTerritoryManager::TERRITORY_PLAYER_MASK);\n\n\t// Try to find an assigned tile\n\tfor (u16 j = 0; j < grid.m_H; ++j)\n\t{\n\t\tfor (u16 i = 0; i < grid.m_W; ++i)\n\t\t{\n\t\t\t// saved tile state; from MSB to LSB:\n\t\t\t// processed bit, blinking bit, player ID\n\t\t\tu8 tileState = grid.get(i, j);\n\t\t\tu8 tileDiscr = (tileState & TERRITORY_DISCR_MASK);\n\n\t\t\t// ignore neutral tiles (note that tiles without an owner should never have the blinking bit set)\n\t\t\tif (!tileDiscr)\n\t\t\t\tcontinue;\n\n\t\t\tbool tileProcessed = ((tileState & ICmpTerritoryManager::TERRITORY_PROCESSED_MASK) != 0);\n\t\t\tbool tileEligible = (j == 0 || tileDiscr != (grid.get(i, j-1) & TERRITORY_DISCR_MASK));\n\n\t\t\tif (tileProcessed || !tileEligible)\n\t\t\t\tcontinue;\n\n\t\t\t// Found the first tile (which must be the lowest j value of any non-zero tile);\n\t\t\t// start at the bottom edge of it and chase anticlockwise around the border until\n\t\t\t// we reach the starting point again\n\n\t\t\tint curvature = 0; // +1 for every CCW 90 degree turn, -1 for every CW 90 degree turn; must be multiple of 4 at the end\n\n\t\t\tboundaries.push_back(STerritoryBoundary());\n\t\t\tboundaries.back().owner = (tileState & ICmpTerritoryManager::TERRITORY_PLAYER_MASK);\n\t\t\tboundaries.back().blinking = (tileState & ICmpTerritoryManager::TERRITORY_BLINKING_MASK) != 0;\n\t\t\tstd::vector<CVector2D>& points = boundaries.back().points;\n\n\t\t\tu8 dir = TILE_BOTTOM;\n\n\t\t\tu8 cdir = dir;\n\t\t\tu16 ci = i, cj = j;\n\n\t\t\tu16 maxi = (u16)(grid.m_W-1);\n\t\t\tu16 maxj = (u16)(grid.m_H-1);\n\n\t\t\t// Size of a territory tile in metres\n\t\t\tfloat territoryTileSize = (Pathfinding::NAVCELL_SIZE * ICmpTerritoryManager::NAVCELLS_PER_TERRITORY_TILE).ToFloat();\n\n\t\t\twhile (true)\n\t\t\t{\n\t\t\t\tpoints.push_back((CVector2D(ci, cj) + edgeOffsets[cdir]) * territoryTileSize);\n\n\t\t\t\t// Given that we're on an edge on a continuous boundary and aiming anticlockwise,\n\t\t\t\t// we can either carry on straight or turn left or turn right, so examine each\n\t\t\t\t// of the three possible cases (depending on initial direction):\n\t\t\t\tswitch (cdir)\n\t\t\t\t{\n\t\t\t\tcase TILE_BOTTOM:\n\n\t\t\t\t\t// mark tile as processed so we don't start a new run from it after this one is complete\n\t\t\t\t\tENSURE(!(grid.get(ci, cj) & ICmpTerritoryManager::TERRITORY_PROCESSED_MASK));\n\t\t\t\t\tgrid.set(ci, cj, grid.get(ci, cj) | ICmpTerritoryManager::TERRITORY_PROCESSED_MASK);\n\n\t\t\t\t\tif (ci < maxi && cj > 0 && (grid.get(ci+1, cj-1) & TERRITORY_DISCR_MASK) == tileDiscr)\n\t\t\t\t\t{\n\t\t\t\t\t\t++ci;\n\t\t\t\t\t\t--cj;\n\t\t\t\t\t\tcdir = TILE_LEFT;\n\t\t\t\t\t\tcurvature += CURVE_CW;\n\t\t\t\t\t}\n\t\t\t\t\telse if (ci < maxi && (grid.get(ci+1, cj) & TERRITORY_DISCR_MASK) == tileDiscr)\n\t\t\t\t\t\t++ci;\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tcdir = TILE_RIGHT;\n\t\t\t\t\t\tcurvature += CURVE_CCW;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase TILE_RIGHT:\n\t\t\t\t\tif (ci < maxi && cj < maxj && (grid.get(ci+1, cj+1) & TERRITORY_DISCR_MASK) == tileDiscr)\n\t\t\t\t\t{\n\t\t\t\t\t\t++ci;\n\t\t\t\t\t\t++cj;\n\t\t\t\t\t\tcdir = TILE_BOTTOM;\n\t\t\t\t\t\tcurvature += CURVE_CW;\n\t\t\t\t\t}\n\t\t\t\t\telse if (cj < maxj && (grid.get(ci, cj+1) & TERRITORY_DISCR_MASK) == tileDiscr)\n\t\t\t\t\t\t++cj;\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tcdir = TILE_TOP;\n\t\t\t\t\t\tcurvature += CURVE_CCW;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase TILE_TOP:\n\t\t\t\t\tif (ci > 0 && cj < maxj && (grid.get(ci-1, cj+1) & TERRITORY_DISCR_MASK) == tileDiscr)\n\t\t\t\t\t{\n\t\t\t\t\t\t--ci;\n\t\t\t\t\t\t++cj;\n\t\t\t\t\t\tcdir = TILE_RIGHT;\n\t\t\t\t\t\tcurvature += CURVE_CW;\n\t\t\t\t\t}\n\t\t\t\t\telse if (ci > 0 && (grid.get(ci-1, cj) & TERRITORY_DISCR_MASK) == tileDiscr)\n\t\t\t\t\t\t--ci;\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tcdir = TILE_LEFT;\n\t\t\t\t\t\tcurvature += CURVE_CCW;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase TILE_LEFT:\n\t\t\t\t\tif (ci > 0 && cj > 0 && (grid.get(ci-1, cj-1) & TERRITORY_DISCR_MASK) == tileDiscr)\n\t\t\t\t\t{\n\t\t\t\t\t\t--ci;\n\t\t\t\t\t\t--cj;\n\t\t\t\t\t\tcdir = TILE_TOP;\n\t\t\t\t\t\tcurvature += CURVE_CW;\n\t\t\t\t\t}\n\t\t\t\t\telse if (cj > 0 && (grid.get(ci, cj-1) & TERRITORY_DISCR_MASK) == tileDiscr)\n\t\t\t\t\t\t--cj;\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tcdir = TILE_BOTTOM;\n\t\t\t\t\t\tcurvature += CURVE_CCW;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\t// Stop when we've reached the starting point again\n\t\t\t\tif (ci == i && cj == j && cdir == dir)\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tENSURE(curvature != 0 && abs(curvature) % 4 == 0);\n\t\t}\n\t}\n\n\treturn boundaries;\n}\n"
  },
  {
    "path": "fpsgame/graphics/TerritoryBoundary.h",
    "content": "/* Copyright (C) 2012 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef INCLUDED_TERRITORYBOUNDARY\n#define INCLUDED_TERRITORYBOUNDARY\n\n#include <vector>\n\n#include \"maths/Vector2D.h\"\n#include \"simulation2/helpers/Grid.h\"\n#include \"simulation2/helpers/Player.h\"\n\n/**\n * Describes an outline of a territory, where the latter are understood to mean the largest sets of mutually connected tiles\n * ('connected' as in the mathematical sense from graph theory) that are either all reachable or all unreachable from a root\n * influence entity.\n * \n * Note that the latter property is also called the 'connected' flag in the territory manager terminology, because for tiles\n * to be reachable from a root influence entity they must in fact be mathematically connected. Hence, you should not confuse\n * the 'connected' flag with the pure mathematical concept of connectedness, because in the former it is implicitly\n * understood that the connection is to a root influence entity.\n */\nstruct STerritoryBoundary\n{\n\t/// Set if this boundary should blink\n\tbool blinking;\n\tplayer_id_t owner;\n\t/// The boundary points, in clockwise order for inner boundaries and counter-clockwise order for outer boundaries.\n\t/// Note: if you need a way to explicitly find out which winding order these are in, you can have\n\t/// CTerritoryBoundCalculator::ComputeBoundaries set it during computation -- see its implementation for details.\n\tstd::vector<CVector2D> points;\n};\n\n/**\n * Responsible for calculating territory boundaries, given an input territory map. Factored out for testing.\n */\nclass CTerritoryBoundaryCalculator\n{\nprivate:\n\tCTerritoryBoundaryCalculator(){} // private ctor\n\npublic:\n\t/**\n\t * Computes and returns all territory boundaries on the provided territory map (see STerritoryBoundary for a definition).\n\t * The result includes both inner and outer territory boundaries. Outer boundaries have their points in CCW order, inner\n\t * boundaries have them in CW order (because this matches the winding orders needed by the renderer to offset them\n\t * inwards/outwards appropriately).\n\t */\n\tstatic std::vector<STerritoryBoundary> ComputeBoundaries(const Grid<u8>* territories);\n};\n\n#endif // INCLUDED_TERRITORYBOUNDARY\n"
  },
  {
    "path": "fpsgame/graphics/TerritoryTexture.cpp",
    "content": "/* Copyright (C) 2013 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"precompiled.h\"\n\n#include \"TerritoryTexture.h\"\n\n#include \"graphics/Terrain.h\"\n#include \"lib/bits.h\"\n#include \"ps/Profile.h\"\n#include \"ps/Shapes.h\"\n#include \"renderer/Renderer.h\"\n#include \"simulation2/Simulation2.h\"\n#include \"simulation2/helpers/Pathfinding.h\"\n#include \"simulation2/components/ICmpPlayer.h\"\n#include \"simulation2/components/ICmpPlayerManager.h\"\n#include \"simulation2/components/ICmpTerrain.h\"\n#include \"simulation2/components/ICmpTerritoryManager.h\"\n\n// TODO: There's a lot of duplication with CLOSTexture - might be nice to refactor a bit\n\nCTerritoryTexture::CTerritoryTexture(CSimulation2& simulation) :\n\tm_Simulation(simulation), m_DirtyID(0), m_Texture(0), m_MapSize(0), m_TextureSize(0)\n{\n}\n\nCTerritoryTexture::~CTerritoryTexture()\n{\n\tif (m_Texture)\n\t\tDeleteTexture();\n}\n\nvoid CTerritoryTexture::DeleteTexture()\n{\n\tglDeleteTextures(1, &m_Texture);\n\tm_Texture = 0;\n}\n\nbool CTerritoryTexture::UpdateDirty()\n{\n\tCmpPtr<ICmpTerritoryManager> cmpTerritoryManager(m_Simulation, SYSTEM_ENTITY);\n\tif (!cmpTerritoryManager)\n\t\treturn false;\n\n\treturn cmpTerritoryManager->NeedUpdate(&m_DirtyID);\n}\n\nvoid CTerritoryTexture::BindTexture(int unit)\n{\n\tif (UpdateDirty())\n\t\tRecomputeTexture(unit);\n\n\tg_Renderer.BindTexture(unit, m_Texture);\n}\n\nGLuint CTerritoryTexture::GetTexture()\n{\n\tif (UpdateDirty())\n\t\tRecomputeTexture(0);\n\n\treturn m_Texture;\n}\n\nconst float* CTerritoryTexture::GetTextureMatrix()\n{\n\tENSURE(!UpdateDirty());\n\treturn &m_TextureMatrix._11;\n}\n\nconst CMatrix3D* CTerritoryTexture::GetMinimapTextureMatrix()\n{\n\tENSURE(!UpdateDirty());\n\treturn &m_MinimapTextureMatrix;\n}\n\nvoid CTerritoryTexture::ConstructTexture(int unit)\n{\n\tCmpPtr<ICmpTerrain> cmpTerrain(m_Simulation, SYSTEM_ENTITY);\n\tif (!cmpTerrain)\n\t\treturn;\n\n\t// Convert size from terrain tiles to territory tiles\n\tm_MapSize = cmpTerrain->GetTilesPerSide() * Pathfinding::NAVCELLS_PER_TILE / ICmpTerritoryManager::NAVCELLS_PER_TERRITORY_TILE;\n\n\tm_TextureSize = (GLsizei)round_up_to_pow2((size_t)m_MapSize);\n\n\tglGenTextures(1, &m_Texture);\n\tg_Renderer.BindTexture(unit, m_Texture);\n\n\t// Initialise texture with transparency, for the areas we don't\n\t// overwrite with glTexSubImage2D later\n\tu8* texData = new u8[m_TextureSize * m_TextureSize * 4];\n\tmemset(texData, 0x00, m_TextureSize * m_TextureSize * 4);\n\tglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_TextureSize, m_TextureSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, texData);\n\tdelete[] texData;\n\n\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);\n\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);\n\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);\n\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);\n\n\t{\n\t\t// Texture matrix: We want to map\n\t\t//   world pos (0, y, 0)  (i.e. bottom-left of first tile)\n\t\t//     onto texcoord (0, 0)  (i.e. bottom-left of first texel);\n\t\t//   world pos (mapsize*cellsize, y, mapsize*cellsize)  (i.e. top-right of last tile)\n\t\t//     onto texcoord (mapsize / texsize, mapsize / texsize)  (i.e. top-right of last texel)\n\n\t\tfloat s = 1.f / (float)(m_TextureSize * TERRAIN_TILE_SIZE);\n\t\tfloat t = 0.f;\n\t\tm_TextureMatrix.SetZero();\n\t\tm_TextureMatrix._11 = s;\n\t\tm_TextureMatrix._23 = s;\n\t\tm_TextureMatrix._14 = t;\n\t\tm_TextureMatrix._24 = t;\n\t\tm_TextureMatrix._44 = 1;\n\t}\n\n\t{\n\t\t// Minimap matrix: We want to map UV (0,0)-(1,1) onto (0,0)-(mapsize/texsize, mapsize/texsize)\n\n\t\tfloat s = m_MapSize / (float)m_TextureSize;\n\t\tm_MinimapTextureMatrix.SetZero();\n\t\tm_MinimapTextureMatrix._11 = s;\n\t\tm_MinimapTextureMatrix._22 = s;\n\t\tm_MinimapTextureMatrix._44 = 1;\n\t}\n}\n\nvoid CTerritoryTexture::RecomputeTexture(int unit)\n{\n\t// If the map was resized, delete and regenerate the texture\n\tif (m_Texture)\n\t{\n\t\tCmpPtr<ICmpTerrain> cmpTerrain(m_Simulation, SYSTEM_ENTITY);\n\t\tif (cmpTerrain && m_MapSize != (ssize_t)cmpTerrain->GetVerticesPerSide())\n\t\t\tDeleteTexture();\n\t}\n\n\tif (!m_Texture)\n\t\tConstructTexture(unit);\n\n\tPROFILE(\"recompute territory texture\");\n\n\tstd::vector<u8> bitmap;\n\tbitmap.resize(m_MapSize * m_MapSize * 4);\n\n\tCmpPtr<ICmpTerritoryManager> cmpTerritoryManager(m_Simulation, SYSTEM_ENTITY);\n\tif (!cmpTerritoryManager)\n\t\treturn;\n\n\tconst Grid<u8> territories = cmpTerritoryManager->GetTerritoryGrid();\n\n\tGenerateBitmap(territories, &bitmap[0], m_MapSize, m_MapSize);\n\n\tg_Renderer.BindTexture(unit, m_Texture);\n\tglTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_MapSize, m_MapSize, GL_RGBA, GL_UNSIGNED_BYTE, &bitmap[0]);\n}\n\nvoid CTerritoryTexture::GenerateBitmap(const Grid<u8>& territories, u8* bitmap, ssize_t w, ssize_t h)\n{\n\tint alphaMax = 0xC0;\n\tint alphaFalloff = 0x20;\n\n\tCmpPtr<ICmpPlayerManager> cmpPlayerManager(m_Simulation, SYSTEM_ENTITY);\n\n\tstd::vector<CColor> colors;\n\ti32 numPlayers = cmpPlayerManager->GetNumPlayers();\n\tfor (i32 p = 0; p < numPlayers; ++p)\n\t{\n\t\tCColor color(1, 0, 1, 1);\n\t\tCmpPtr<ICmpPlayer> cmpPlayer(m_Simulation, cmpPlayerManager->GetPlayerByID(p));\n\t\tif (cmpPlayer)\n\t\t\tcolor = cmpPlayer->GetColor();\n\t\tcolors.push_back(color);\n\t}\n\n\tu8* p = bitmap;\n\tfor (ssize_t j = 0; j < h; ++j)\n\t{\n\t\tfor (ssize_t i = 0; i < w; ++i)\n\t\t{\n\t\t\tu8 val = territories.get(i, j) & ICmpTerritoryManager::TERRITORY_PLAYER_MASK;\n\n\t\t\tCColor color(1, 0, 1, 1);\n\t\t\t// Force neutral territories to pure white, so that later we can omit them from the texture\n\t\t\tif (val == 0)\n\t\t\t\tcolor = CColor(1, 1, 1, 0);\n\t\t\telse if (val < colors.size())\n\t\t\t\tcolor = colors[val];\n\n\t\t\t*p++ = (int)(color.r*255.f);\n\t\t\t*p++ = (int)(color.g*255.f);\n\t\t\t*p++ = (int)(color.b*255.f);\n\n\t\t\tif ((i > 0 && (territories.get(i-1, j) & ICmpTerritoryManager::TERRITORY_PLAYER_MASK) != val)\n\t\t\t || (i < w-1 && (territories.get(i+1, j) & ICmpTerritoryManager::TERRITORY_PLAYER_MASK) != val)\n\t\t\t || (j > 0 && (territories.get(i, j-1) & ICmpTerritoryManager::TERRITORY_PLAYER_MASK) != val)\n\t\t\t || (j < h-1 && (territories.get(i, j+1) & ICmpTerritoryManager::TERRITORY_PLAYER_MASK) != val)\n\t\t\t)\n\t\t\t{\n\t\t\t\t*p++ = alphaMax;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t*p++ = 0x00;\n\t\t\t}\n\t\t}\n\t}\n\n\t// Do a low-quality cheap blur effect\n\n\tfor (ssize_t j = 0; j < h; ++j)\n\t{\n\t\tint a;\n\n\t\ta = 0;\n\t\tfor (ssize_t i = 0; i < w; ++i)\n\t\t{\n\t\t\ta = std::max(a - alphaFalloff, (int)bitmap[(j*w+i)*4 + 3]);\n\t\t\tbitmap[(j*w+i)*4 + 3] = a;\n\t\t}\n\n\t\ta = 0;\n\t\tfor (ssize_t i = w-1; i >= 0; --i)\n\t\t{\n\t\t\ta = std::max(a - alphaFalloff, (int)bitmap[(j*w+i)*4 + 3]);\n\t\t\tbitmap[(j*w+i)*4 + 3] = a;\n\t\t}\n\t}\n\n\tfor (ssize_t i = 0; i < w; ++i)\n\t{\n\t\tint a;\n\n\t\ta = 0;\n\t\tfor (ssize_t j = 0; j < w; ++j)\n\t\t{\n\t\t\ta = std::max(a - alphaFalloff, (int)bitmap[(j*w+i)*4 + 3]);\n\t\t\tbitmap[(j*w+i)*4 + 3] = a;\n\t\t}\n\n\t\ta = 0;\n\t\tfor (ssize_t j = w-1; j >= 0; --j)\n\t\t{\n\t\t\ta = std::max(a - alphaFalloff, (int)bitmap[(j*w+i)*4 + 3]);\n\t\t\tbitmap[(j*w+i)*4 + 3] = a;\n\t\t}\n\t}\n\n\t// Add a gap between the boundaries, by deleting the max-alpha tiles\n\tfor (ssize_t j = 0; j < h; ++j)\n\t{\n\t\tfor (ssize_t i = 0; i < w; ++i)\n\t\t{\n\t\t\tif (bitmap[(j*w+i)*4 + 3] == alphaMax)\n\t\t\t\tbitmap[(j*w+i)*4 + 3] = 0;\n\t\t}\n\t}\n\n\t// Don't show neutral territory boundaries\n\tfor (ssize_t j = 0; j < h; ++j)\n\t{\n\t\tfor (ssize_t i = 0; i < w; ++i)\n\t\t{\n\t\t\tssize_t idx = (j*w+i)*4;\n\t\t\tif (bitmap[idx] == 255 && bitmap[idx+1] == 255 && bitmap[idx+2] == 255)\n\t\t\t\tbitmap[idx+3] = 0;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "fpsgame/graphics/TerritoryTexture.h",
    "content": "/* Copyright (C) 2011 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"lib/ogl.h\"\n\n#include \"maths/Matrix3D.h\"\n#include \"simulation2/helpers/Grid.h\"\n\nclass CSimulation2;\n\n/**\n * Maintains the territory boundary texture, used for\n * rendering and for the minimap.\n */\nclass CTerritoryTexture\n{\n\tNONCOPYABLE(CTerritoryTexture);\n\npublic:\n\tCTerritoryTexture(CSimulation2& simulation);\n\t~CTerritoryTexture();\n\n\t/**\n\t * Recomputes the territory texture if necessary, and binds it to the requested\n\t * texture unit.\n\t * Also switches the current active texture unit, and enables texturing on it.\n\t * The texture is in 32-bit BGRA format.\n\t */\n\tvoid BindTexture(int unit);\n\n\t/**\n\t * Recomputes the territory texture if necessary, and returns the texture handle.\n\t * Also potentially switches the current active texture unit, and enables texturing on it.\n\t * The texture is in 32-bit BGRA format.\n\t */\n\tGLuint GetTexture();\n\n\t/**\n\t * Returns a matrix to map (x,y,z) world coordinates onto (u,v) texture\n\t * coordinates, in the form expected by glLoadMatrixf.\n\t * This must only be called after BindTexture.\n\t */\n\tconst float* GetTextureMatrix();\n\n\t/**\n\t * Returns a matrix to map (0,0)-(1,1) texture coordinates onto texture\n\t * coordinates, in the form expected by glLoadMatrixf.\n\t * This must only be called after BindTexture.\n\t */\n\tconst CMatrix3D* GetMinimapTextureMatrix();\n\nprivate:\n\t/**\n\t * Returns true if the territory state has changed since the last call to this function\n\t */\n\tbool UpdateDirty();\n\n\tvoid DeleteTexture();\n\tvoid ConstructTexture(int unit);\n\tvoid RecomputeTexture(int unit);\n\n\tvoid GenerateBitmap(const Grid<u8>& territories, u8* bitmap, ssize_t w, ssize_t h);\n\n\tCSimulation2& m_Simulation;\n\n\tsize_t m_DirtyID;\n\n\tGLuint m_Texture;\n\n\tssize_t m_MapSize; // tiles per side\n\tGLsizei m_TextureSize; // texels per side\n\n\tCMatrix3D m_TextureMatrix;\n\tCMatrix3D m_MinimapTextureMatrix;\n};\n"
  },
  {
    "path": "fpsgame/graphics/TextRenderer.cpp",
    "content": "/* Copyright (C) 2012 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"precompiled.h\"\n\n#include \"TextRenderer.h\"\n\n#include \"graphics/Font.h\"\n#include \"graphics/FontManager.h\"\n#include \"graphics/ShaderProgram.h\"\n#include \"lib/ogl.h\"\n#include \"ps/CStrIntern.h\"\n#include \"ps/GameSetup/Config.h\"\n#include \"renderer/Renderer.h\"\n\nCTextRenderer::CTextRenderer(const CShaderProgramPtr& shader) :\n\tm_Shader(shader)\n{\n\tResetTransform();\n\tColor(CColor(1.0f, 1.0f, 1.0f, 1.0f));\n\tFont(str_sans_10);\n}\n\nvoid CTextRenderer::ResetTransform()\n{\n\tfloat xres = g_xres * g_GuiScale;\n\tfloat yres = g_yres * g_GuiScale;\n\n\tm_Transform.SetIdentity();\n\tm_Transform.Scale(1.0f, -1.f, 1.0f);\n\tm_Transform.Translate(0.0f, yres, -1000.0f);\n\n\tCMatrix3D proj;\n\tproj.SetOrtho(0.f, xres, 0.f, yres, -1.f, 1000.f);\n\tm_Transform = proj * m_Transform;\n\tm_Dirty = true;\n}\n\nCMatrix3D CTextRenderer::GetTransform()\n{\n\treturn m_Transform;\n}\n\nvoid CTextRenderer::SetTransform(const CMatrix3D& transform)\n{\n\tm_Transform = transform;\n\tm_Dirty = true;\n}\n\nvoid CTextRenderer::Translate(float x, float y, float z)\n{\n\tCMatrix3D m;\n\tm.SetTranslation(x, y, z);\n\tm_Transform = m_Transform * m;\n\tm_Dirty = true;\n}\n\nvoid CTextRenderer::SetClippingRect(const CRect& rect)\n{\n\tm_Clipping = rect;\n}\n\nvoid CTextRenderer::Color(const CColor& color)\n{\n\tif (m_Color != color)\n\t{\n\t\tm_Color = color;\n\t\tm_Dirty = true;\n\t}\n}\n\nvoid CTextRenderer::Color(float r, float g, float b, float a)\n{\n\tColor(CColor(r, g, b, a));\n}\n\nvoid CTextRenderer::Font(CStrIntern font)\n{\n\tif (font != m_FontName)\n\t{\n\t\tm_FontName = font;\n\t\tm_Font = g_Renderer.GetFontManager().LoadFont(font);\n\t\tm_Dirty = true;\n\t}\n}\n\nvoid CTextRenderer::PrintfAdvance(const wchar_t* fmt, ...)\n{\n\twchar_t buf[1024] = {0};\n\n\tva_list args;\n\tva_start(args, fmt);\n\tint ret = vswprintf(buf, ARRAY_SIZE(buf)-1, fmt, args);\n\tva_end(args);\n\n\tif (ret < 0)\n\t\tdebug_printf(\"CTextRenderer::Printf vswprintf failed (buffer size exceeded?) - return value %d, errno %d\\n\", ret, errno);\n\n\tPutAdvance(buf);\n}\n\n\nvoid CTextRenderer::PrintfAt(float x, float y, const wchar_t* fmt, ...)\n{\n\twchar_t buf[1024] = {0};\n\n\tva_list args;\n\tva_start(args, fmt);\n\tint ret = vswprintf(buf, ARRAY_SIZE(buf)-1, fmt, args);\n\tva_end(args);\n\n\tif (ret < 0)\n\t\tdebug_printf(\"CTextRenderer::PrintfAt vswprintf failed (buffer size exceeded?) - return value %d, errno %d\\n\", ret, errno);\n\n\tPut(x, y, buf);\n}\n\nvoid CTextRenderer::PutAdvance(const wchar_t* buf)\n{\n\tPut(0.0f, 0.0f, buf);\n\n\tint w, h;\n\tm_Font->CalculateStringSize(buf, w, h);\n\tTranslate((float)w, 0.0f, 0.0f);\n}\n\nvoid CTextRenderer::Put(float x, float y, const wchar_t* buf)\n{\n\tif (buf[0] == 0)\n\t\treturn; // empty string; don't bother storing\n\n\tPutString(x, y, new std::wstring(buf), true);\n}\n\nvoid CTextRenderer::Put(float x, float y, const char* buf)\n{\n\tif (buf[0] == 0)\n\t\treturn; // empty string; don't bother storing\n\n\tPutString(x, y, new std::wstring(wstring_from_utf8(buf)), true);\n}\n\nvoid CTextRenderer::Put(float x, float y, const std::wstring* buf)\n{\n\tif (buf->empty())\n\t\treturn; // empty string; don't bother storing\n\n\tPutString(x, y, buf, false);\n}\n\nvoid CTextRenderer::PutString(float x, float y, const std::wstring* buf, bool owned)\n{\n\tif (!m_Font)\n\t\treturn; // invalid font; can't render\n\n\tif (m_Clipping != CRect())\n\t{\n\t\tfloat x0, y0, x1, y1;\n\t\tm_Font->GetGlyphBounds(x0, y0, x1, y1);\n\t\tif (y + y1 < m_Clipping.top)\n\t\t\treturn;\n\t\tif (y + y0 > m_Clipping.bottom)\n\t\t\treturn;\n\t}\n\n\t// If any state has changed since the last batch, start a new batch\n\tif (m_Dirty)\n\t{\n\t\tSBatch batch;\n\t\tbatch.chars = 0;\n\t\tbatch.transform = m_Transform;\n\t\tbatch.color = m_Color;\n\t\tbatch.font = m_Font;\n\t\tm_Batches.push_back(batch);\n\t\tm_Dirty = false;\n\t}\n\n\t// Push a new run onto the latest batch\n\tSBatchRun run;\n\trun.x = x;\n\trun.y = y;\n\tm_Batches.back().runs.push_back(run);\n\tm_Batches.back().runs.back().text = buf;\n\tm_Batches.back().runs.back().owned = owned;\n\tm_Batches.back().chars += buf->size();\n}\n\n\nstruct t2f_v2i\n{\n\tt2f_v2i() : u(0), v(0), x(0), y(0) { }\n\tfloat u, v;\n\ti16 x, y;\n};\n\nstruct SBatchCompare\n{\n\tbool operator()(const CTextRenderer::SBatch& a, const CTextRenderer::SBatch& b)\n\t{\n\t\tif (a.font < b.font)\n\t\t\treturn true;\n\t\tif (b.font < a.font)\n\t\t\treturn false;\n\t\t// TODO: is it worth sorting by color/transform too?\n\t\treturn false;\n\t}\n};\n\nvoid CTextRenderer::Render()\n{\n\tstd::vector<u16> indexes;\n\tstd::vector<t2f_v2i> vertexes;\n\n\t// Try to merge non-consecutive batches that share the same font/color/transform:\n\t// sort the batch list by font, then merge the runs of adjacent compatible batches\n\tm_Batches.sort(SBatchCompare());\n\tfor (std::list<SBatch>::iterator it = m_Batches.begin(); it != m_Batches.end(); )\n\t{\n\t\tstd::list<SBatch>::iterator next = it;\n\t\t++next;\n\t\tif (next != m_Batches.end() && it->font == next->font && it->color == next->color && it->transform == next->transform)\n\t\t{\n\t\t\tit->chars += next->chars;\n\t\t\tit->runs.splice(it->runs.end(), next->runs);\n\t\t\tm_Batches.erase(next);\n\t\t}\n\t\telse\n\t\t\t++it;\n\t}\n\n\tfor (std::list<SBatch>::iterator it = m_Batches.begin(); it != m_Batches.end(); ++it)\n\t{\n\t\tSBatch& batch = *it;\n\n\t\tconst CFont::GlyphMap& glyphs = batch.font->GetGlyphs();\n\n\t\tm_Shader->BindTexture(str_tex, batch.font->GetTexture());\n\n\t\tm_Shader->Uniform(str_transform, batch.transform);\n\n\t\t// ALPHA-only textures will have .rgb sampled as 0, so we need to\n\t\t// replace it with white (but not affect RGBA textures)\n\t\tif (batch.font->HasRGB())\n\t\t\tm_Shader->Uniform(str_colorAdd, CColor(0.0f, 0.0f, 0.0f, 0.0f));\n\t\telse\n\t\t\tm_Shader->Uniform(str_colorAdd, CColor(1.0f, 1.0f, 1.0f, 0.0f));\n\n\t\tm_Shader->Uniform(str_colorMul, batch.color);\n\n\t\tvertexes.resize(batch.chars*4);\n\t\tindexes.resize(batch.chars*6);\n\n\t\tsize_t idx = 0;\n\n\t\tfor (std::list<SBatchRun>::iterator runit = batch.runs.begin(); runit != batch.runs.end(); ++runit)\n\t\t{\n\t\t\tSBatchRun& run = *runit;\n\t\t\ti16 x = run.x;\n\t\t\ti16 y = run.y;\n\t\t\tfor (size_t i = 0; i < run.text->size(); ++i)\n\t\t\t{\n\t\t\t\tconst CFont::GlyphData* g = glyphs.get((*run.text)[i]);\n\n\t\t\t\tif (!g)\n\t\t\t\t\tg = glyphs.get(0xFFFD); // Use the missing glyph symbol\n\t\t\t\tif (!g) // Missing the missing glyph symbol - give up\n\t\t\t\t\tcontinue;\n\n\t\t\t\tvertexes[idx*4].u = g->u1;\n\t\t\t\tvertexes[idx*4].v = g->v0;\n\t\t\t\tvertexes[idx*4].x = g->x1 + x;\n\t\t\t\tvertexes[idx*4].y = g->y0 + y;\n\n\t\t\t\tvertexes[idx*4+1].u = g->u0;\n\t\t\t\tvertexes[idx*4+1].v = g->v0;\n\t\t\t\tvertexes[idx*4+1].x = g->x0 + x;\n\t\t\t\tvertexes[idx*4+1].y = g->y0 + y;\n\n\t\t\t\tvertexes[idx*4+2].u = g->u0;\n\t\t\t\tvertexes[idx*4+2].v = g->v1;\n\t\t\t\tvertexes[idx*4+2].x = g->x0 + x;\n\t\t\t\tvertexes[idx*4+2].y = g->y1 + y;\n\n\t\t\t\tvertexes[idx*4+3].u = g->u1;\n\t\t\t\tvertexes[idx*4+3].v = g->v1;\n\t\t\t\tvertexes[idx*4+3].x = g->x1 + x;\n\t\t\t\tvertexes[idx*4+3].y = g->y1 + y;\n\n\t\t\t\tindexes[idx*6+0] = idx*4+0;\n\t\t\t\tindexes[idx*6+1] = idx*4+1;\n\t\t\t\tindexes[idx*6+2] = idx*4+2;\n\t\t\t\tindexes[idx*6+3] = idx*4+2;\n\t\t\t\tindexes[idx*6+4] = idx*4+3;\n\t\t\t\tindexes[idx*6+5] = idx*4+0;\n\n\t\t\t\tx += g->xadvance;\n\n\t\t\t\tidx++;\n\t\t\t}\n\t\t}\n\n\t\tm_Shader->VertexPointer(2, GL_SHORT, sizeof(t2f_v2i), &vertexes[0].x);\n\t\tm_Shader->TexCoordPointer(GL_TEXTURE0, 2, GL_FLOAT, sizeof(t2f_v2i), &vertexes[0].u);\n\n\t\tglDrawElements(GL_TRIANGLES, indexes.size(), GL_UNSIGNED_SHORT, &indexes[0]);\n\t}\n\n\tm_Batches.clear();\n}\n"
  },
  {
    "path": "fpsgame/graphics/TextRenderer.h",
    "content": "/* Copyright (C) 2012 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef INCLUDED_TEXTRENDERER\n#define INCLUDED_TEXTRENDERER\n\n#include \"graphics/ShaderProgramPtr.h\"\n#include \"maths/Matrix3D.h\"\n#include \"ps/CStrIntern.h\"\n#include \"ps/Shapes.h\"\n\nclass CFont;\n\nclass CTextRenderer\n{\npublic:\n\tCTextRenderer(const CShaderProgramPtr& shader);\n\n\t/**\n\t * Reset the text transform to the default, with (0,0) in the top-left of the screen.\n\t */\n\tvoid ResetTransform();\n\n\tCMatrix3D GetTransform();\n\t\n\tvoid SetTransform(const CMatrix3D& transform);\n\n\tvoid Translate(float x, float y, float z);\n\n\t/**\n\t * Set clipping rectangle, in pre-transform coordinates (i.e. text is clipped against\n\t * this rect based purely on the x,y values passed into Put()). Text fully outside the\n\t * clipping rectangle may not be rendered. Should be used in conjunction with glScissor\n\t * for precise clipping - this is just an optimisation.\n\t */\n\tvoid SetClippingRect(const CRect& rect);\n\n\t/**\n\t * Set the color for subsequent print calls.\n\t */\n\tvoid Color(const CColor& color);\n\t\n\t/**\n\t * Set the color for subsequent print calls.\n\t */\n\tvoid Color(float r, float g, float b, float a = 1.0);\n\t\n\t/**\n\t * Set the font for subsequent print calls.\n\t */\n\tvoid Font(CStrIntern font);\n\n\t/**\n\t * Print formatted text at (0,0) under the current transform,\n\t * and advance the transform by the width of the text.\n\t */\n\tvoid PrintfAdvance(const wchar_t* fmt, ...);\n\n\t/**\n\t * Print formatted text at (x,y) under the current transform.\n\t * Does not alter the current transform.\n\t */\n\tvoid PrintfAt(float x, float y, const wchar_t* fmt, ...);\n\n\t/**\n\t * Print text at (0,0) under the current transform,\n\t * and advance the transform by the width of the text.\n\t */\n\tvoid PutAdvance(const wchar_t* buf);\n\n\t/**\n\t * Print text at (x,y) under the current transform.\n\t * Does not alter the current transform.\n\t */\n\tvoid Put(float x, float y, const wchar_t* buf);\n\n\t/**\n\t * Print text at (x,y) under the current transform.\n\t * Does not alter the current transform.\n\t * @p buf must be a UTF-8 string.\n\t */\n\tvoid Put(float x, float y, const char* buf);\n\n\t/**\n\t * Print text at (x,y) under the current transform.\n\t * Does not alter the current transform.\n\t * @p buf must remain valid until Render() is called.\n\t * (This should be used to minimise memory copies when possible.)\n\t */\n\tvoid Put(float x, float y, const std::wstring* buf);\n\n\t/**\n\t * Render all of the previously printed text calls.\n\t */\n\tvoid Render();\n\nprivate:\n\tfriend struct SBatchCompare;\n\n\t/**\n\t * A string (optionally owned by this object, or else pointing to an\n\t * externally-owned string) with a position.\n\t */\n\tstruct SBatchRun\n\t{\n\tprivate:\n\t\tSBatchRun& operator=(const SBatchRun&);\n\tpublic:\n\t\tSBatchRun()\n\t\t\t: text(NULL), owned(false)\n\t\t{\n\t\t}\n\n\t\tSBatchRun(const SBatchRun& str)\n\t\t\t: x(str.x), y(str.y), owned(str.owned)\n\t\t{\n\t\t\tif (owned)\n\t\t\t\ttext = new std::wstring(*str.text);\n\t\t\telse\n\t\t\t\ttext = str.text;\n\t\t}\n\n\t\t~SBatchRun()\n\t\t{\n\t\t\tif (owned)\n\t\t\t\tdelete text;\n\t\t}\n\n\t\tfloat x, y;\n\n\t\tconst std::wstring* text;\n\t\tbool owned;\n\t};\n\n\t/**\n\t * A list of SBatchRuns, with a single font/color/transform,\n\t * to be rendered in a single GL call.\n\t */\n\tstruct SBatch\n\t{\n\t\tsize_t chars; // sum of runs[i].text->size()\n\t\tCMatrix3D transform;\n\t\tCColor color;\n\t\tshared_ptr<CFont> font;\n\t\tstd::list<SBatchRun> runs;\n\t};\n\n\tvoid PutString(float x, float y, const std::wstring* buf, bool owned);\n\n\tCShaderProgramPtr m_Shader;\n\n\tCMatrix3D m_Transform;\n\tCRect m_Clipping;\n\n\tCColor m_Color;\n\tCStrIntern m_FontName;\n\tshared_ptr<CFont> m_Font;\n\n\tbool m_Dirty;\n\n\tstd::list<SBatch> m_Batches;\n};\n\n#endif // INCLUDED_TEXTRENDERER\n"
  },
  {
    "path": "fpsgame/graphics/Texture.h",
    "content": "/* Copyright (C) 2015 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef INCLUDED_TEXTURE\n#define INCLUDED_TEXTURE\n\n// Forward-declare for CTextureManager handles\nclass CTexture;\ntypedef std::shared_ptr<CTexture> CTexturePtr;\n\n#endif\n"
  },
  {
    "path": "fpsgame/graphics/TextureConverter.cpp",
    "content": "/* Copyright (C) 2015 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"precompiled.h\"\n\n#include \"TextureConverter.h\"\n\n#include \"lib/regex.h\"\n#include \"lib/timer.h\"\n#include \"lib/allocators/shared_ptr.h\"\n#include \"lib/tex/tex.h\"\n#include \"maths/MD5.h\"\n#include \"ps/CLogger.h\"\n#include \"ps/CStr.h\"\n#include \"ps/Profiler2.h\"\n#include \"ps/XML/Xeromyces.h\"\n\n#if CONFIG2_NVTT\n\n#include \"nvtt/nvtt.h\"\n\n/**\n * Output handler to collect NVTT's output into a simplistic buffer.\n * WARNING: Used in the worker thread - must be thread-safe.\n */\nstruct BufferOutputHandler : public nvtt::OutputHandler\n{\n\tstd::vector<u8> buffer;\n\n\tvirtual void beginImage(int UNUSED(size), int UNUSED(width), int UNUSED(height), int UNUSED(depth), int UNUSED(face), int UNUSED(miplevel))\n\t{\n\t}\n\n\tvirtual bool writeData(const void* data, int size)\n\t{\n\t\tsize_t off = buffer.size();\n\t\tbuffer.resize(off + size);\n\t\tmemcpy(&buffer[off], data, size);\n\t\treturn true;\n\t}\n};\n\n/**\n * Request for worker thread to process.\n */\nstruct CTextureConverter::ConversionRequest\n{\n\tVfsPath dest;\n\tCTexturePtr texture;\n\tnvtt::InputOptions inputOptions;\n\tnvtt::CompressionOptions compressionOptions;\n\tnvtt::OutputOptions outputOptions;\n\tbool isDXT1a; // see comment in RunThread\n\tbool is8bpp;\n};\n\n/**\n * Result from worker thread.\n */\nstruct CTextureConverter::ConversionResult\n{\n\tVfsPath dest;\n\tCTexturePtr texture;\n\tBufferOutputHandler output;\n\tbool ret; // true if the conversion succeeded\n};\n\n#endif // CONFIG2_NVTT\n\nvoid CTextureConverter::Settings::Hash(MD5& hash)\n{\n\thash.Update((const u8*)&format, sizeof(format));\n\thash.Update((const u8*)&mipmap, sizeof(mipmap));\n\thash.Update((const u8*)&normal, sizeof(normal));\n\thash.Update((const u8*)&alpha, sizeof(alpha));\n\thash.Update((const u8*)&filter, sizeof(filter));\n\thash.Update((const u8*)&kaiserWidth, sizeof(kaiserWidth));\n\thash.Update((const u8*)&kaiserAlpha, sizeof(kaiserAlpha));\n\thash.Update((const u8*)&kaiserStretch, sizeof(kaiserStretch));\n}\n\nCTextureConverter::SettingsFile* CTextureConverter::LoadSettings(const VfsPath& path) const\n{\n\tCXeromyces XeroFile;\n\tif (XeroFile.Load(m_VFS, path, \"texture\") != PSRETURN_OK)\n\t\treturn NULL;\n\n\t// Define all the elements used in the XML file\n\t#define EL(x) int el_##x = XeroFile.GetElementID(#x)\n\t#define AT(x) int at_##x = XeroFile.GetAttributeID(#x)\n\tEL(textures);\n\tEL(file);\n\tAT(pattern);\n\tAT(format);\n\tAT(mipmap);\n\tAT(normal);\n\tAT(alpha);\n\tAT(filter);\n\tAT(kaiserwidth);\n\tAT(kaiseralpha);\n\tAT(kaiserstretch);\n\t#undef AT\n\t#undef EL\n\n\tXMBElement root = XeroFile.GetRoot();\n\n\tif (root.GetNodeName() != el_textures)\n\t{\n\t\tLOGERROR(\"Invalid texture settings file \\\"%s\\\" (unrecognised root element)\", path.string8());\n\t\treturn NULL;\n\t}\n\n\tstd::unique_ptr<SettingsFile> settings(new SettingsFile());\n\n\tXERO_ITER_EL(root, child)\n\t{\n\t\tif (child.GetNodeName() == el_file)\n\t\t{\n\t\t\tMatch p;\n\n\t\t\tXERO_ITER_ATTR(child, attr)\n\t\t\t{\n\t\t\t\tif (attr.Name == at_pattern)\n\t\t\t\t{\n\t\t\t\t\tp.pattern = attr.Value.FromUTF8();\n\t\t\t\t}\n\t\t\t\telse if (attr.Name == at_format)\n\t\t\t\t{\n\t\t\t\t\tCStr v(attr.Value);\n\t\t\t\t\tif (v == \"dxt1\")\n\t\t\t\t\t\tp.settings.format = FMT_DXT1;\n\t\t\t\t\telse if (v == \"dxt3\")\n\t\t\t\t\t\tp.settings.format = FMT_DXT3;\n\t\t\t\t\telse if (v == \"dxt5\")\n\t\t\t\t\t\tp.settings.format = FMT_DXT5;\n\t\t\t\t\telse if (v == \"rgba\")\n\t\t\t\t\t\tp.settings.format = FMT_RGBA;\n\t\t\t\t\telse if (v == \"alpha\")\n\t\t\t\t\t\tp.settings.format = FMT_ALPHA;\n\t\t\t\t\telse\n\t\t\t\t\t\tLOGERROR(\"Invalid attribute value <file format='%s'>\", v.c_str());\n\t\t\t\t}\n\t\t\t\telse if (attr.Name == at_mipmap)\n\t\t\t\t{\n\t\t\t\t\tCStr v(attr.Value);\n\t\t\t\t\tif (v == \"true\")\n\t\t\t\t\t\tp.settings.mipmap = MIP_TRUE;\n\t\t\t\t\telse if (v == \"false\")\n\t\t\t\t\t\tp.settings.mipmap = MIP_FALSE;\n\t\t\t\t\telse\n\t\t\t\t\t\tLOGERROR(\"Invalid attribute value <file mipmap='%s'>\", v.c_str());\n\t\t\t\t}\n\t\t\t\telse if (attr.Name == at_normal)\n\t\t\t\t{\n\t\t\t\t\tCStr v(attr.Value);\n\t\t\t\t\tif (v == \"true\")\n\t\t\t\t\t\tp.settings.normal = NORMAL_TRUE;\n\t\t\t\t\telse if (v == \"false\")\n\t\t\t\t\t\tp.settings.normal = NORMAL_FALSE;\n\t\t\t\t\telse\n\t\t\t\t\t\tLOGERROR(\"Invalid attribute value <file normal='%s'>\", v.c_str());\n\t\t\t\t}\n\t\t\t\telse if (attr.Name == at_alpha)\n\t\t\t\t{\n\t\t\t\t\tCStr v(attr.Value);\n\t\t\t\t\tif (v == \"none\")\n\t\t\t\t\t\tp.settings.alpha = ALPHA_NONE;\n\t\t\t\t\telse if (v == \"player\")\n\t\t\t\t\t\tp.settings.alpha = ALPHA_PLAYER;\n\t\t\t\t\telse if (v == \"transparency\")\n\t\t\t\t\t\tp.settings.alpha = ALPHA_TRANSPARENCY;\n\t\t\t\t\telse\n\t\t\t\t\t\tLOGERROR(\"Invalid attribute value <file alpha='%s'>\", v.c_str());\n\t\t\t\t}\n\t\t\t\telse if (attr.Name == at_filter)\n\t\t\t\t{\n\t\t\t\t\tCStr v(attr.Value);\n\t\t\t\t\tif (v == \"box\")\n\t\t\t\t\t\tp.settings.filter = FILTER_BOX;\n\t\t\t\t\telse if (v == \"triangle\")\n\t\t\t\t\t\tp.settings.filter = FILTER_TRIANGLE;\n\t\t\t\t\telse if (v == \"kaiser\")\n\t\t\t\t\t\tp.settings.filter = FILTER_KAISER;\n\t\t\t\t\telse\n\t\t\t\t\t\tLOGERROR(\"Invalid attribute value <file filter='%s'>\", v.c_str());\n\t\t\t\t}\n\t\t\t\telse if (attr.Name == at_kaiserwidth)\n\t\t\t\t{\n\t\t\t\t\tp.settings.kaiserWidth = CStr(attr.Value).ToFloat();\n\t\t\t\t}\n\t\t\t\telse if (attr.Name == at_kaiseralpha)\n\t\t\t\t{\n\t\t\t\t\tp.settings.kaiserAlpha = CStr(attr.Value).ToFloat();\n\t\t\t\t}\n\t\t\t\telse if (attr.Name == at_kaiserstretch)\n\t\t\t\t{\n\t\t\t\t\tp.settings.kaiserStretch = CStr(attr.Value).ToFloat();\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tLOGERROR(\"Invalid attribute name <file %s='...'>\", XeroFile.GetAttributeString(attr.Name).c_str());\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tsettings->patterns.push_back(p);\n\t\t}\n\t}\n\n\treturn settings.release();\n}\n\nCTextureConverter::Settings CTextureConverter::ComputeSettings(const std::wstring& filename, const std::vector<SettingsFile*>& settingsFiles) const\n{\n\t// Set sensible defaults\n\tSettings settings;\n\tsettings.format = FMT_DXT1;\n\tsettings.mipmap = MIP_TRUE;\n\tsettings.normal = NORMAL_FALSE;\n\tsettings.alpha = ALPHA_NONE;\n\tsettings.filter = FILTER_BOX;\n\tsettings.kaiserWidth = 3.f;\n\tsettings.kaiserAlpha = 4.f;\n\tsettings.kaiserStretch = 1.f;\n\n\tfor (size_t i = 0; i < settingsFiles.size(); ++i)\n\t{\n\t\tfor (size_t j = 0; j < settingsFiles[i]->patterns.size(); ++j)\n\t\t{\n\t\t\tMatch p = settingsFiles[i]->patterns[j];\n\n\t\t\t// Check that the pattern matches the texture file\n\t\t\tif (!match_wildcard(filename.c_str(), p.pattern.c_str()))\n\t\t\t\tcontinue;\n\n\t\t\tif (p.settings.format != FMT_UNSPECIFIED)\n\t\t\t\tsettings.format = p.settings.format;\n\n\t\t\tif (p.settings.mipmap != MIP_UNSPECIFIED)\n\t\t\t\tsettings.mipmap = p.settings.mipmap;\n\n\t\t\tif (p.settings.normal != NORMAL_UNSPECIFIED)\n\t\t\t\tsettings.normal = p.settings.normal;\n\n\t\t\tif (p.settings.alpha != ALPHA_UNSPECIFIED)\n\t\t\t\tsettings.alpha = p.settings.alpha;\n\n\t\t\tif (p.settings.filter != FILTER_UNSPECIFIED)\n\t\t\t\tsettings.filter = p.settings.filter;\n\n\t\t\tif (p.settings.kaiserWidth != -1.f)\n\t\t\t\tsettings.kaiserWidth = p.settings.kaiserWidth;\n\n\t\t\tif (p.settings.kaiserAlpha != -1.f)\n\t\t\t\tsettings.kaiserAlpha = p.settings.kaiserAlpha;\n\n\t\t\tif (p.settings.kaiserStretch != -1.f)\n\t\t\t\tsettings.kaiserStretch = p.settings.kaiserStretch;\n\t\t}\n\t}\n\n\treturn settings;\n}\n\nCTextureConverter::CTextureConverter(PIVFS vfs, bool highQuality) :\n\tm_VFS(vfs), m_HighQuality(highQuality), m_Shutdown(false)\n{\n\t// Verify that we are running with at least the version we were compiled with,\n\t// to avoid bugs caused by ABI changes\n#if CONFIG2_NVTT\n\tENSURE(nvtt::version() >= NVTT_VERSION);\n#endif\n\n\t// Set up the worker thread:\n\n\tint ret;\n\n\t// Use SDL semaphores since OS X doesn't implement sem_init\n\tm_WorkerSem = SDL_CreateSemaphore(0);\n\tENSURE(m_WorkerSem);\n\n\tret = pthread_mutex_init(&m_WorkerMutex, NULL);\n\tENSURE(ret == 0);\n\n\tret = pthread_create(&m_WorkerThread, NULL, &RunThread, this);\n\tENSURE(ret == 0);\n\n\t// Maybe we should share some centralised pool of worker threads?\n\t// For now we'll just stick with a single thread for this specific use.\n}\n\nCTextureConverter::~CTextureConverter()\n{\n\t// Tell the thread to shut down\n\tpthread_mutex_lock(&m_WorkerMutex);\n\tm_Shutdown = true;\n\tpthread_mutex_unlock(&m_WorkerMutex);\n\n\t// Wake it up so it sees the notification\n\tSDL_SemPost(m_WorkerSem);\n\n\t// Wait for it to shut down cleanly\n\tpthread_join(m_WorkerThread, NULL);\n\n\t// Clean up resources\n\tSDL_DestroySemaphore(m_WorkerSem);\n\tpthread_mutex_destroy(&m_WorkerMutex);\n}\n\nbool CTextureConverter::ConvertTexture(const CTexturePtr& texture, const VfsPath& src, const VfsPath& dest, const Settings& settings)\n{\n\tshared_ptr<u8> file;\n\tsize_t fileSize;\n\tif (m_VFS->LoadFile(src, file, fileSize) < 0)\n\t{\n\t\tLOGERROR(\"Failed to load texture \\\"%s\\\"\", src.string8());\n\t\treturn false;\n\t}\n\n\tTex tex;\n\tif (tex.decode(file, fileSize) < 0)\n\t{\n\t\tLOGERROR(\"Failed to decode texture \\\"%s\\\"\", src.string8());\n\t\treturn false;\n\t}\n\n\t// Check whether there's any alpha channel\n\tbool hasAlpha = ((tex.m_Flags & TEX_ALPHA) != 0);\n\n\tif (settings.format == FMT_ALPHA)\n\t{\n\t\t// Convert to uncompressed 8-bit with no mipmaps\n\t\tif (tex.transform_to((tex.m_Flags | TEX_GREY) & ~(TEX_DXT | TEX_MIPMAPS | TEX_ALPHA)) < 0)\n\t\t{\n\t\t\tLOGERROR(\"Failed to transform texture \\\"%s\\\"\", src.string8());\n\t\t\treturn false;\n\t\t}\n\t}\n\telse\n\t{\n\t\t// Convert to uncompressed BGRA with no mipmaps\n\t\tif (tex.transform_to((tex.m_Flags | TEX_BGR | TEX_ALPHA) & ~(TEX_DXT | TEX_MIPMAPS)) < 0)\n\t\t{\n\t\t\tLOGERROR(\"Failed to transform texture \\\"%s\\\"\", src.string8());\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t// Check if the texture has all alpha=255, so we can automatically\n\t// switch from DXT3/DXT5 to DXT1 with no loss\n\tif (hasAlpha)\n\t{\n\t\thasAlpha = false;\n\t\tu8* data = tex.get_data();\n\t\tfor (size_t i = 0; i < tex.m_Width * tex.m_Height; ++i)\n\t\t{\n\t\t\tif (data[i*4+3] != 0xFF)\n\t\t\t{\n\t\t\t\thasAlpha = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n#if CONFIG2_NVTT\n\n\tshared_ptr<ConversionRequest> request(new ConversionRequest);\n\trequest->dest = dest;\n\trequest->texture = texture;\n\n\t// Apply the chosen settings:\n\n\trequest->inputOptions.setMipmapGeneration(settings.mipmap == MIP_TRUE);\n\n\tif (settings.alpha == ALPHA_TRANSPARENCY)\n\t\trequest->inputOptions.setAlphaMode(nvtt::AlphaMode_Transparency);\n\telse\n\t\trequest->inputOptions.setAlphaMode(nvtt::AlphaMode_None);\n\n\trequest->isDXT1a = false;\n\trequest->is8bpp = false;\n\n\tif (settings.format == FMT_RGBA)\n\t{\n\t\trequest->compressionOptions.setFormat(nvtt::Format_RGBA);\n\t\t// Change the default component order (see tex_dds.cpp decode_pf)\n\t\trequest->compressionOptions.setPixelFormat(32, 0xFF, 0xFF00, 0xFF0000, 0xFF000000u);\n\t}\n\telse if (settings.format == FMT_ALPHA)\n\t{\n\t\trequest->compressionOptions.setFormat(nvtt::Format_RGBA);\n\t\trequest->compressionOptions.setPixelFormat(8, 0x00, 0x00, 0x00, 0xFF);\n\t\trequest->is8bpp = true;\n\t}\n\telse if (!hasAlpha)\n\t{\n\t\t// if no alpha channel then there's no point using DXT3 or DXT5\n\t\trequest->compressionOptions.setFormat(nvtt::Format_DXT1);\n\t}\n\telse if (settings.format == FMT_DXT1)\n\t{\n\t\trequest->compressionOptions.setFormat(nvtt::Format_DXT1a);\n\t\trequest->isDXT1a = true;\n\t}\n\telse if (settings.format == FMT_DXT3)\n\t{\n\t\trequest->compressionOptions.setFormat(nvtt::Format_DXT3);\n\t}\n\telse if (settings.format == FMT_DXT5)\n\t{\n\t\trequest->compressionOptions.setFormat(nvtt::Format_DXT5);\n\t}\n\n\tif (settings.filter == FILTER_BOX)\n\t\trequest->inputOptions.setMipmapFilter(nvtt::MipmapFilter_Box);\n\telse if (settings.filter == FILTER_TRIANGLE)\n\t\trequest->inputOptions.setMipmapFilter(nvtt::MipmapFilter_Triangle);\n\telse if (settings.filter == FILTER_KAISER)\n\t\trequest->inputOptions.setMipmapFilter(nvtt::MipmapFilter_Kaiser);\n\n\tif (settings.normal == NORMAL_TRUE)\n\t\trequest->inputOptions.setNormalMap(true);\n\n\trequest->inputOptions.setKaiserParameters(settings.kaiserWidth, settings.kaiserAlpha, settings.kaiserStretch);\n\n\trequest->inputOptions.setWrapMode(nvtt::WrapMode_Mirror); // TODO: should this be configurable?\n\n\trequest->compressionOptions.setQuality(m_HighQuality ? nvtt::Quality_Production : nvtt::Quality_Fastest);\n\n\t// TODO: normal maps, gamma, etc\n\n\t// Load the texture data\n\trequest->inputOptions.setTextureLayout(nvtt::TextureType_2D, tex.m_Width, tex.m_Height);\n\tif (tex.m_Bpp == 32)\n\t{\n\t\trequest->inputOptions.setMipmapData(tex.get_data(), tex.m_Width, tex.m_Height);\n\t}\n\telse // bpp == 8\n\t{\n\t\t// NVTT requires 32-bit input data, so convert\n\t\tconst u8* input = tex.get_data();\n\t\tu8* rgba = new u8[tex.m_Width * tex.m_Height * 4];\n\t\tu8* p = rgba;\n\t\tfor (size_t i = 0; i < tex.m_Width * tex.m_Height; i++)\n\t\t{\n\t\t\tp[0] = p[1] = p[2] = p[3] = *input++;\n\t\t\tp += 4;\n\t\t}\n\t\trequest->inputOptions.setMipmapData(rgba, tex.m_Width, tex.m_Height);\n\t\tdelete[] rgba;\n\t}\n\n\tpthread_mutex_lock(&m_WorkerMutex);\n\tm_RequestQueue.push_back(request);\n\tpthread_mutex_unlock(&m_WorkerMutex);\n\n\t// Wake up the worker thread\n\tSDL_SemPost(m_WorkerSem);\n\n\treturn true;\n\n#else\n\tLOGERROR(\"Failed to convert texture \\\"%s\\\" (NVTT not available)\", src.string8());\n\treturn false;\n#endif\n}\n\nbool CTextureConverter::Poll(CTexturePtr& texture, VfsPath& dest, bool& ok)\n{\n#if CONFIG2_NVTT\n\tshared_ptr<ConversionResult> result;\n\n\t// Grab the first result (if any)\n\tpthread_mutex_lock(&m_WorkerMutex);\n\tif (!m_ResultQueue.empty())\n\t{\n\t\tresult = m_ResultQueue.front();\n\t\tm_ResultQueue.pop_front();\n\t}\n\tpthread_mutex_unlock(&m_WorkerMutex);\n\n\tif (!result)\n\t{\n\t\t// no work to do\n\t\treturn false;\n\t}\n\n\tif (!result->ret)\n\t{\n\t\t// conversion had failed\n\t\tok = false;\n\t\treturn true;\n\t}\n\n\t// Move output into a correctly-aligned buffer\n\tsize_t size = result->output.buffer.size();\n\tshared_ptr<u8> file;\n\tAllocateAligned(file, size, maxSectorSize);\n\tmemcpy(file.get(), &result->output.buffer[0], size);\n\tif (m_VFS->CreateFile(result->dest, file, size) < 0)\n\t{\n\t\t// error writing file\n\t\tok = false;\n\t\treturn true;\n\t}\n\n\t// Succeeded in converting texture\n\ttexture = result->texture;\n\tdest = result->dest;\n\tok = true;\n\treturn true;\n\n#else // #if CONFIG2_NVTT\n\treturn false;\n#endif\n}\n\nbool CTextureConverter::IsBusy()\n{\n\tpthread_mutex_lock(&m_WorkerMutex);\n\tbool busy = !m_RequestQueue.empty();\n\tpthread_mutex_unlock(&m_WorkerMutex);\n\n\treturn busy;\n}\n\nvoid* CTextureConverter::RunThread(void* data)\n{\n\tdebug_SetThreadName(\"TextureConverter\");\n\tg_Profiler2.RegisterCurrentThread(\"texconv\");\n\n\tCTextureConverter* textureConverter = static_cast<CTextureConverter*>(data);\n\n#if CONFIG2_NVTT\n\n\t// Wait until the main thread wakes us up\n\twhile (SDL_SemWait(textureConverter->m_WorkerSem) == 0)\n\t{\n\t\tg_Profiler2.RecordSyncMarker();\n\t\tPROFILE2_EVENT(\"wakeup\");\n\n\t\tpthread_mutex_lock(&textureConverter->m_WorkerMutex);\n\t\tif (textureConverter->m_Shutdown)\n\t\t{\n\t\t\tpthread_mutex_unlock(&textureConverter->m_WorkerMutex);\n\t\t\tbreak;\n\t\t}\n\t\t// If we weren't woken up for shutdown, we must have been woken up for\n\t\t// a new request, so grab it from the queue\n\t\tshared_ptr<ConversionRequest> request = textureConverter->m_RequestQueue.front();\n\t\ttextureConverter->m_RequestQueue.pop_front();\n\t\tpthread_mutex_unlock(&textureConverter->m_WorkerMutex);\n\n\t\t// Set up the result object\n\t\tshared_ptr<ConversionResult> result(new ConversionResult());\n\t\tresult->dest = request->dest;\n\t\tresult->texture = request->texture;\n\n\t\trequest->outputOptions.setOutputHandler(&result->output);\n\n//\t\tTIMER(L\"TextureConverter compress\");\n\n\t\t{\n\t\t\tPROFILE2(\"compress\");\n\n\t\t\t// Perform the compression\n\t\t\tnvtt::Compressor compressor;\n\t\t\tresult->ret = compressor.process(request->inputOptions, request->compressionOptions, request->outputOptions);\n\t\t}\n\n\t\t// Ugly hack: NVTT 2.0 doesn't set DDPF_ALPHAPIXELS for DXT1a, so we can't\n\t\t// distinguish it from DXT1. (It's fixed in trunk by\n\t\t// http://code.google.com/p/nvidia-texture-tools/source/detail?r=924&path=/trunk).\n\t\t// Rather than using a trunk NVTT (unstable, makes packaging harder)\n\t\t// or patching our copy (makes packaging harder), we'll just manually\n\t\t// set the flag here.\n\t\tif (request->isDXT1a && result->ret && result->output.buffer.size() > 80)\n\t\t\tresult->output.buffer[80] |= 1; // DDPF_ALPHAPIXELS in DDS_PIXELFORMAT.dwFlags\n\t\t// Ugly hack: NVTT always sets DDPF_RGB, even if we're trying to output 8-bit\n\t\t// alpha-only DDS with no RGB components. Unset that flag.\n\t\tif (request->is8bpp)\n\t\t\tresult->output.buffer[80] &= ~0x40; // DDPF_RGB in DDS_PIXELFORMAT.dwFlags\n\n\t\t// Push the result onto the queue\n\t\tpthread_mutex_lock(&textureConverter->m_WorkerMutex);\n\t\ttextureConverter->m_ResultQueue.push_back(result);\n\t\tpthread_mutex_unlock(&textureConverter->m_WorkerMutex);\n\t}\n\n#endif\n\n\treturn NULL;\n}\n"
  },
  {
    "path": "fpsgame/graphics/TextureConverter.h",
    "content": "/* Copyright (C) 2012 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef INCLUDED_TEXTURECONVERTER\n#define INCLUDED_TEXTURECONVERTER\n\n#include \"lib/file/vfs/vfs.h\"\n#include \"lib/posix/posix_pthread.h\"\n#include \"lib/external_libraries/libsdl.h\"\n\n#include \"TextureManager.h\"\n\nclass MD5;\n\n/**\n * Texture conversion helper class.\n * Provides an asynchronous API to convert input image files into compressed DDS,\n * given various conversion settings.\n * (The (potentially very slow) compression is a performed in a background thread,\n * so the game can remain responsive).\n * Also provides an API to load conversion settings from XML files.\n *\n * XML files are of the form:\n * @code\n * <Textures>\n *   <File pattern=\"*\" format=\"dxt5\" mipmap=\"false\" alpha=\"transparency\"/>\n *   <File pattern=\"button_wood.*\" format=\"rgba\"/>\n * </Entity>\n * @endcode\n *\n * 'pattern' is a wildcard expression, matching on filenames.\n * All other attributes are optional. Later elements override attributes from\n * earlier elements.\n *\n * 'format' is 'dxt1', 'dxt3', 'dxt5' or 'rgba'.\n *\n * 'mipmap' is 'true' or 'false'.\n *\n * 'normal' is 'true' or 'false'.\n *\n * 'alpha' is 'transparency' or 'player' (it determines whether the color value of\n * 0-alpha pixels is significant or not).\n *\n * 'filter' is 'box', 'triangle' or 'kaiser'.\n *\n * 'kaiserwidth', 'kaiseralpha', 'kaiserstretch' are floats\n * (see http://code.google.com/p/nvidia-texture-tools/wiki/ApiDocumentation#Mipmap_Generation)\n */\nclass CTextureConverter\n{\npublic:\n\tenum EFormat\n\t{\n\t\tFMT_UNSPECIFIED,\n\t\tFMT_DXT1,\n\t\tFMT_DXT3,\n\t\tFMT_DXT5,\n\t\tFMT_RGBA,\n\t\tFMT_ALPHA\n\t};\n\n\tenum EMipmap\n\t{\n\t\tMIP_UNSPECIFIED,\n\t\tMIP_TRUE,\n\t\tMIP_FALSE\n\t};\n\n\tenum ENormalMap\n\t{\n\t\tNORMAL_UNSPECIFIED,\n\t\tNORMAL_TRUE,\n\t\tNORMAL_FALSE\n\t};\n\n\tenum EAlpha\n\t{\n\t\tALPHA_UNSPECIFIED,\n\t\tALPHA_NONE,\n\t\tALPHA_PLAYER,\n\t\tALPHA_TRANSPARENCY\n\t};\n\n\tenum EFilter\n\t{\n\t\tFILTER_UNSPECIFIED,\n\t\tFILTER_BOX,\n\t\tFILTER_TRIANGLE,\n\t\tFILTER_KAISER\n\t};\n\n\t/**\n\t * Texture conversion settings.\n\t */\n\tstruct Settings\n\t{\n\t\tSettings() :\n\t\t\tformat(FMT_UNSPECIFIED), mipmap(MIP_UNSPECIFIED), normal(NORMAL_UNSPECIFIED),\n\t\t\talpha(ALPHA_UNSPECIFIED), filter(FILTER_UNSPECIFIED),\n\t\t\tkaiserWidth(-1.f), kaiserAlpha(-1.f), kaiserStretch(-1.f)\n\t\t{\n\t\t}\n\n\t\t/**\n\t\t * Append this object's state to the given hash.\n\t\t */\n\t\tvoid Hash(MD5& hash);\n\n\t\tEFormat format;\n\t\tEMipmap mipmap;\n\t\tENormalMap normal;\n\t\tEAlpha alpha;\n\t\tEFilter filter;\n\t\tfloat kaiserWidth;\n\t\tfloat kaiserAlpha;\n\t\tfloat kaiserStretch;\n\t};\n\n\t/**\n\t * Representation of \\<File\\> line from settings XML file.\n\t */\n\tstruct Match\n\t{\n\t\tstd::wstring pattern;\n\t\tSettings settings;\n\t};\n\n\t/**\n\t * Representation of settings XML file.\n\t */\n\tstruct SettingsFile\n\t{\n\t\tstd::vector<Match> patterns;\n\t};\n\n\t/**\n\t * Construct texture converter, for use with files in the given vfs.\n\t */\n\tCTextureConverter(PIVFS vfs, bool highQuality);\n\n\t/**\n\t * Destroy texture converter and wait to shut down worker thread.\n\t * This might take a long time (maybe seconds) if the worker is busy\n\t * processing a texture.\n\t */\n\t~CTextureConverter();\n\n\t/**\n\t * Load a texture conversion settings XML file.\n\t * Returns NULL on failure.\n\t */\n\tSettingsFile* LoadSettings(const VfsPath& path) const;\n\n\t/**\n\t * Match a sequence of settings files against a given texture filename,\n\t * and return the resulting settings.\n\t * Later entries in settingsFiles override earlier entries.\n\t */\n\tSettings ComputeSettings(const std::wstring& filename, const std::vector<SettingsFile*>& settingsFiles) const;\n\n\t/**\n\t * Begin converting a texture, using the given settings.\n\t * This will load src and return false on failure.\n\t * Otherwise it will return true and start an asynchronous conversion request,\n\t * whose result will be returned from Poll() (with the texture and dest passed\n\t * into this function).\n\t */\n\tbool ConvertTexture(const CTexturePtr& texture, const VfsPath& src, const VfsPath& dest, const Settings& settings);\n\n\t/**\n\t * Returns the result of a successful ConvertTexture call.\n\t * If no result is available yet, returns false.\n\t * Otherwise, if the conversion succeeded, it sets ok to true and sets\n\t * texture and dest to the corresponding values passed into ConvertTexture(),\n\t * then returns true.\n\t * If the conversion failed, it sets ok to false and doesn't touch the other arguments,\n\t * then returns true.\n\t */\n\tbool Poll(CTexturePtr& texture, VfsPath& dest, bool& ok);\n\n\t/**\n\t * Returns whether there is currently a queued request from ConvertTexture().\n\t * (Note this may return false while the worker thread is still converting the last texture.)\n\t */\n\tbool IsBusy();\n\nprivate:\n\tstatic void* RunThread(void* data);\n\n\tPIVFS m_VFS;\n\tbool m_HighQuality;\n\n\tpthread_t m_WorkerThread;\n\tpthread_mutex_t m_WorkerMutex;\n\tSDL_sem* m_WorkerSem;\n\n\tstruct ConversionRequest;\n\tstruct ConversionResult;\n\n\tstd::deque<shared_ptr<ConversionRequest> > m_RequestQueue; // protected by m_WorkerMutex\n\tstd::deque<shared_ptr<ConversionResult> > m_ResultQueue; // protected by m_WorkerMutex\n\tbool m_Shutdown; // protected by m_WorkerMutex\n};\n\n#endif // INCLUDED_TEXTURECONVERTER\n"
  },
  {
    "path": "fpsgame/graphics/TextureManager.cpp",
    "content": "/* Copyright (C) 2015 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"precompiled.h\"\n\n#include \"TextureManager.h\"\n\n#include <boost/functional/hash.hpp>\n#include <boost/unordered_map.hpp>\n#include <boost/unordered_set.hpp>\n#include <iomanip>\n\n#include \"graphics/TextureConverter.h\"\n#include \"lib/allocators/shared_ptr.h\"\n#include \"lib/res/h_mgr.h\"\n#include \"lib/file/vfs/vfs_tree.h\"\n#include \"lib/res/graphics/ogl_tex.h\"\n#include \"lib/timer.h\"\n#include \"maths/MD5.h\"\n#include \"ps/CacheLoader.h\"\n#include \"ps/CLogger.h\"\n#include \"ps/Filesystem.h\"\n#include \"ps/Profile.h\"\n\nstruct TPhash\n\t : std::unary_function<CTextureProperties, std::size_t>,\n\t std::unary_function<CTexturePtr, std::size_t>\n{\n\tstd::size_t operator()(CTextureProperties const& a) const\n\t{\n\t\tstd::size_t seed = 0;\n\t\tboost::hash_combine(seed, a.m_Path);\n\t\tboost::hash_combine(seed, a.m_Filter);\n\t\tboost::hash_combine(seed, a.m_WrapS);\n\t\tboost::hash_combine(seed, a.m_WrapT);\n\t\tboost::hash_combine(seed, a.m_Aniso);\n\t\tboost::hash_combine(seed, a.m_Format);\n\t\treturn seed;\n\t}\n\tstd::size_t operator()(CTexturePtr const& a) const\n\t{\n\t\treturn (*this)(a->m_Properties);\n\t}\n};\n\nstruct TPequal_to\n\t: std::binary_function<CTextureProperties, CTextureProperties, bool>,\n\tstd::binary_function<CTexturePtr, CTexturePtr, bool>\n{\t\n\tbool operator()(CTextureProperties const& a, CTextureProperties const& b) const\n\t{\n\t\treturn a.m_Path == b.m_Path && a.m_Filter == b.m_Filter\n\t\t\t&& a.m_WrapS == b.m_WrapS && a.m_WrapT == b.m_WrapT\n\t\t\t&& a.m_Aniso == b.m_Aniso && a.m_Format == b.m_Format;\n\t}\n\tbool operator()(CTexturePtr const& a, CTexturePtr const& b) const\n\t{\n\t\treturn (*this)(a->m_Properties, b->m_Properties);\n\t}\n};\n\nstd::size_t hash_value(const CTexturePtr& v)\n{\n\tTPhash h;\n\treturn h(v);\n}\nstd::size_t hash_value(const CTextureProperties& v)\n{\n\tTPhash h;\n\treturn h(v);\n}\n\nclass CTextureManagerImpl\n{\n\tfriend class CTexture;\npublic:\n\tCTextureManagerImpl(PIVFS vfs, bool highQuality, bool disableGL) :\n\t\tm_VFS(vfs), m_CacheLoader(vfs, L\".dds\"), m_DisableGL(disableGL), m_TextureConverter(vfs, highQuality),\n\t\tm_DefaultHandle(0), m_ErrorHandle(0)\n\t{\n\t\t// Initialise some textures that will always be available,\n\t\t// without needing to load any files\n\n\t\t// Default placeholder texture (grey)\n\t\tif (!m_DisableGL)\n\t\t{\n\t\t\t// Construct 1x1 24-bit texture\n\t\t\tshared_ptr<u8> data(new u8[3], ArrayDeleter());\n\t\t\tdata.get()[0] = 64;\n\t\t\tdata.get()[1] = 64;\n\t\t\tdata.get()[2] = 64;\n\t\t\tTex t;\n\t\t\t(void)t.wrap(1, 1, 24, 0, data, 0);\n\n\t\t\tm_DefaultHandle = ogl_tex_wrap(&t, m_VFS, L\"(default texture)\");\n\t\t\t(void)ogl_tex_set_filter(m_DefaultHandle, GL_LINEAR);\n\t\t\tif (!m_DisableGL)\n\t\t\t\t(void)ogl_tex_upload(m_DefaultHandle);\n\t\t}\n\n\t\t// Error texture (magenta)\n\t\tif (!m_DisableGL)\n\t\t{\n\t\t\t// Construct 1x1 24-bit texture\n\t\t\tshared_ptr<u8> data(new u8[3], ArrayDeleter());\n\t\t\tdata.get()[0] = 255;\n\t\t\tdata.get()[1] = 0;\n\t\t\tdata.get()[2] = 255;\n\t\t\tTex t;\n\t\t\t(void)t.wrap(1, 1, 24, 0, data, 0);\n\n\t\t\tm_ErrorHandle = ogl_tex_wrap(&t, m_VFS, L\"(error texture)\");\n\t\t\t(void)ogl_tex_set_filter(m_ErrorHandle, GL_LINEAR);\n\t\t\tif (!m_DisableGL)\n\t\t\t\t(void)ogl_tex_upload(m_ErrorHandle);\n\n\t\t\t// Construct a CTexture to return to callers who want an error texture\n\t\t\tCTextureProperties props(L\"(error texture)\");\n\t\t\tm_ErrorTexture = CTexturePtr(new CTexture(m_ErrorHandle, props, this));\n\t\t\tm_ErrorTexture->m_State = CTexture::LOADED;\n\t\t\tm_ErrorTexture->m_Self = m_ErrorTexture;\n\t\t}\n\n\t\t// Allow hotloading of textures\n\t\tRegisterFileReloadFunc(ReloadChangedFileCB, this);\n\t}\n\n\t~CTextureManagerImpl()\n\t{\n\t\tUnregisterFileReloadFunc(ReloadChangedFileCB, this);\n\n\t\t(void)ogl_tex_free(m_DefaultHandle);\n\t\t(void)ogl_tex_free(m_ErrorHandle);\n\t}\n\n\tCTexturePtr GetErrorTexture()\n\t{\n\t\treturn m_ErrorTexture;\n\t}\n\n\t/**\n\t * See CTextureManager::CreateTexture\n\t */\n\tCTexturePtr CreateTexture(const CTextureProperties& props)\n\t{\n\t\t// Construct a new default texture with the given properties to use as the search key\n\t\tCTexturePtr texture(new CTexture(m_DefaultHandle, props, this));\n\n\t\t// Try to find an existing texture with the given properties\n\t\tTextureCache::iterator it = m_TextureCache.find(texture);\n\t\tif (it != m_TextureCache.end())\n\t\t\treturn *it;\n\n\t\t// Can't find an existing texture - finish setting up this new texture\n\t\ttexture->m_Self = texture;\n\t\tm_TextureCache.insert(texture);\n\t\tm_HotloadFiles[props.m_Path].insert(texture);\n\n\t\treturn texture;\n\t}\n\n\t/**\n\t * Load the given file into the texture object and upload it to OpenGL.\n\t * Assumes the file already exists.\n\t */\n\tvoid LoadTexture(const CTexturePtr& texture, const VfsPath& path)\n\t{\n\t\tif (m_DisableGL)\n\t\t\treturn;\n\n\t\tPROFILE2(\"load texture\");\n\t\tPROFILE2_ATTR(\"name: %ls\", path.string().c_str());\n\n\t\tHandle h = ogl_tex_load(m_VFS, path, RES_UNIQUE);\n\t\tif (h <= 0)\n\t\t{\n\t\t\tLOGERROR(\"Texture failed to load; \\\"%s\\\"\", texture->m_Properties.m_Path.string8());\n\n\t\t\t// Replace with error texture to make it obvious\n\t\t\ttexture->SetHandle(m_ErrorHandle);\n\t\t\treturn;\n\t\t}\n\n\t\t// Get some flags for later use\n\t\tsize_t flags = 0;\n\t\t(void)ogl_tex_get_format(h, &flags, NULL);\n\n\t\t// Initialise base color from the texture\n\t\t(void)ogl_tex_get_average_color(h, &texture->m_BaseColor);\n\n\t\t// Set GL upload properties\n\t\t(void)ogl_tex_set_wrap(h, texture->m_Properties.m_WrapS, texture->m_Properties.m_WrapT);\n\t\t(void)ogl_tex_set_anisotropy(h, texture->m_Properties.m_Aniso);\n\n\t\t// Prevent ogl_tex automatically generating mipmaps (which is slow and unwanted),\n\t\t// by avoiding mipmapped filters unless the source texture already has mipmaps\n\t\tGLint filter = texture->m_Properties.m_Filter;\n\t\tif (!(flags & TEX_MIPMAPS))\n\t\t{\n\t\t\tswitch (filter)\n\t\t\t{\n\t\t\tcase GL_NEAREST_MIPMAP_NEAREST:\n\t\t\tcase GL_NEAREST_MIPMAP_LINEAR:\n\t\t\t\tfilter = GL_NEAREST;\n\t\t\t\tbreak;\n\t\t\tcase GL_LINEAR_MIPMAP_NEAREST:\n\t\t\tcase GL_LINEAR_MIPMAP_LINEAR:\n\t\t\t\tfilter = GL_LINEAR;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\t(void)ogl_tex_set_filter(h, filter);\n\n\t\t// Upload to GL\n\t\tif (!m_DisableGL && ogl_tex_upload(h, texture->m_Properties.m_Format) < 0)\n\t\t{\n\t\t\tLOGERROR(\"Texture failed to upload: \\\"%s\\\"\", texture->m_Properties.m_Path.string8());\n\n\t\t\togl_tex_free(h);\n\n\t\t\t// Replace with error texture to make it obvious\n\t\t\ttexture->SetHandle(m_ErrorHandle);\n\t\t\treturn;\n\t\t}\n\n\t\t// Let the texture object take ownership of this handle\n\t\ttexture->SetHandle(h, true);\n\t}\n\n\t/**\n\t * Set up some parameters for the loose cache filename code.\n\t */\n\tvoid PrepareCacheKey(const CTexturePtr& texture, MD5& hash, u32& version)\n\t{\n\t\t// Hash the settings, so we won't use an old loose cache file if the\n\t\t// settings have changed\n\t\tCTextureConverter::Settings settings = GetConverterSettings(texture);\n\t\tsettings.Hash(hash);\n\n\t\t// Arbitrary version number - change this if we update the code and\n\t\t// need to invalidate old users' caches\n\t\tversion = 1;\n\t}\n\n\t/**\n\t * Attempts to load a cached version of a texture.\n\t * If the texture is loaded (or there was an error), returns true.\n\t * Otherwise, returns false to indicate the caller should generate the cached version.\n\t */\n\tbool TryLoadingCached(const CTexturePtr& texture)\n\t{\n\t\tMD5 hash;\n\t\tu32 version;\n\t\tPrepareCacheKey(texture, hash, version);\n\n\t\tVfsPath loadPath;\n\t\tStatus ret = m_CacheLoader.TryLoadingCached(texture->m_Properties.m_Path, hash, version, loadPath);\n\n\t\tif (ret == INFO::OK)\n\t\t{\n\t\t\t// Found a cached texture - load it\n\t\t\tLoadTexture(texture, loadPath);\n\t\t\treturn true;\n\t\t}\n\t\telse if (ret == INFO::SKIPPED)\n\t\t{\n\t\t\t// No cached version was found - we'll need to create it\n\t\t\treturn false;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tENSURE(ret < 0);\n\n\t\t\t// No source file or archive cache was found, so we can't load the\n\t\t\t// real texture at all - return the error texture instead\n\t\t\tLOGERROR(\"CCacheLoader failed to find archived or source file for: \\\"%s\\\"\", texture->m_Properties.m_Path.string8());\n\t\t\ttexture->SetHandle(m_ErrorHandle);\n\t\t\treturn true;\n\t\t}\n\t}\n\n\t/**\n\t * Initiates an asynchronous conversion process, from the texture's\n\t * source file to the corresponding loose cache file.\n\t */\n\tvoid ConvertTexture(const CTexturePtr& texture)\n\t{\n\t\tVfsPath sourcePath = texture->m_Properties.m_Path;\n\n\t\tPROFILE2(\"convert texture\");\n\t\tPROFILE2_ATTR(\"name: %ls\", sourcePath.string().c_str());\n\n\t\tMD5 hash;\n\t\tu32 version;\n\t\tPrepareCacheKey(texture, hash, version);\n\t\tVfsPath looseCachePath = m_CacheLoader.LooseCachePath(sourcePath, hash, version);\n\n//\t\tLOGWARNING(\"Converting texture \\\"%s\\\"\", srcPath.c_str());\n\n\t\tCTextureConverter::Settings settings = GetConverterSettings(texture);\n\n\t\tm_TextureConverter.ConvertTexture(texture, sourcePath, looseCachePath, settings);\n\t}\n\n\tbool GenerateCachedTexture(const VfsPath& sourcePath, VfsPath& archiveCachePath)\n\t{\n\t\tarchiveCachePath = m_CacheLoader.ArchiveCachePath(sourcePath);\n\n\t\tCTextureProperties textureProps(sourcePath);\n\t\tCTexturePtr texture = CreateTexture(textureProps);\n\t\tCTextureConverter::Settings settings = GetConverterSettings(texture);\n\n\t\tif (!m_TextureConverter.ConvertTexture(texture, sourcePath, VfsPath(\"cache\") / archiveCachePath, settings))\n\t\t\treturn false;\n\n\t\twhile (true)\n\t\t{\n\t\t\tCTexturePtr textureOut;\n\t\t\tVfsPath dest;\n\t\t\tbool ok;\n\t\t\tif (m_TextureConverter.Poll(textureOut, dest, ok))\n\t\t\t\treturn ok;\n\n\t\t\t// Spin-loop is dumb but it works okay for now\n\t\t\tSDL_Delay(0);\n\t\t}\n\t}\n\n\tbool MakeProgress()\n\t{\n\t\t// Process any completed conversion tasks\n\t\t{\n\t\t\tCTexturePtr texture;\n\t\t\tVfsPath dest;\n\t\t\tbool ok;\n\t\t\tif (m_TextureConverter.Poll(texture, dest, ok))\n\t\t\t{\n\t\t\t\tif (ok)\n\t\t\t\t{\n\t\t\t\t\tLoadTexture(texture, dest);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tLOGERROR(\"Texture failed to convert: \\\"%s\\\"\", texture->m_Properties.m_Path.string8());\n\t\t\t\t\ttexture->SetHandle(m_ErrorHandle);\n\t\t\t\t}\n\t\t\t\ttexture->m_State = CTexture::LOADED;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\t// We'll only push new conversion requests if it's not already busy\n\t\tbool converterBusy = m_TextureConverter.IsBusy();\n\n\t\tif (!converterBusy)\n\t\t{\n\t\t\t// Look for all high-priority textures needing conversion.\n\t\t\t// (Iterating over all textures isn't optimally efficient, but it\n\t\t\t// doesn't seem to be a problem yet and it's simpler than maintaining\n\t\t\t// multiple queues.)\n\t\t\tfor (TextureCache::iterator it = m_TextureCache.begin(); it != m_TextureCache.end(); ++it)\n\t\t\t{\n\t\t\t\tif ((*it)->m_State == CTexture::HIGH_NEEDS_CONVERTING)\n\t\t\t\t{\n\t\t\t\t\t// Start converting this texture\n\t\t\t\t\t(*it)->m_State = CTexture::HIGH_IS_CONVERTING;\n\t\t\t\t\tConvertTexture(*it);\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Try loading prefetched textures from their cache\n\t\tfor (TextureCache::iterator it = m_TextureCache.begin(); it != m_TextureCache.end(); ++it)\n\t\t{\n\t\t\tif ((*it)->m_State == CTexture::PREFETCH_NEEDS_LOADING)\n\t\t\t{\n\t\t\t\tif (TryLoadingCached(*it))\n\t\t\t\t{\n\t\t\t\t\t(*it)->m_State = CTexture::LOADED;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t(*it)->m_State = CTexture::PREFETCH_NEEDS_CONVERTING;\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\t// If we've got nothing better to do, then start converting prefetched textures.\n\t\tif (!converterBusy)\n\t\t{\n\t\t\tfor (TextureCache::iterator it = m_TextureCache.begin(); it != m_TextureCache.end(); ++it)\n\t\t\t{\n\t\t\t\tif ((*it)->m_State == CTexture::PREFETCH_NEEDS_CONVERTING)\n\t\t\t\t{\n\t\t\t\t\t(*it)->m_State = CTexture::PREFETCH_IS_CONVERTING;\n\t\t\t\t\tConvertTexture(*it);\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t/**\n\t * Compute the conversion settings that apply to a given texture, by combining\n\t * the textures.xml files from its directory and all parent directories\n\t * (up to the VFS root).\n\t */\n\tCTextureConverter::Settings GetConverterSettings(const CTexturePtr& texture)\n\t{\n\t\tfs::wpath srcPath = texture->m_Properties.m_Path.string();\n\n\t\tstd::vector<CTextureConverter::SettingsFile*> files;\n\t\tVfsPath p;\n\t\tfor (fs::wpath::iterator it = srcPath.begin(); it != srcPath.end(); ++it)\n\t\t{\n\t\t\tVfsPath settingsPath = p / \"textures.xml\";\n\t\t\tm_HotloadFiles[settingsPath].insert(texture);\n\t\t\tCTextureConverter::SettingsFile* f = GetSettingsFile(settingsPath);\n\t\t\tif (f)\n\t\t\t\tfiles.push_back(f);\n\t\t\tp = p / GetWstringFromWpath(*it);\n\t\t}\n\t\treturn m_TextureConverter.ComputeSettings(GetWstringFromWpath(srcPath.leaf()), files);\n\t}\n\n\t/**\n\t * Return the (cached) settings file with the given filename,\n\t * or NULL if it doesn't exist.\n\t */\n\tCTextureConverter::SettingsFile* GetSettingsFile(const VfsPath& path)\n\t{\n\t\tSettingsFilesMap::iterator it = m_SettingsFiles.find(path);\n\t\tif (it != m_SettingsFiles.end())\n\t\t\treturn it->second.get();\n\n\t\tif (m_VFS->GetFileInfo(path, NULL) >= 0)\n\t\t{\n\t\t\tshared_ptr<CTextureConverter::SettingsFile> settings(m_TextureConverter.LoadSettings(path));\n\t\t\tm_SettingsFiles.insert(std::make_pair(path, settings));\n\t\t\treturn settings.get();\n\t\t}\n\t\telse\n\t\t{\n\t\t\tm_SettingsFiles.insert(std::make_pair(path, shared_ptr<CTextureConverter::SettingsFile>()));\n\t\t\treturn NULL;\n\t\t}\n\t}\n\n\tstatic Status ReloadChangedFileCB(void* param, const VfsPath& path)\n\t{\n\t\treturn static_cast<CTextureManagerImpl*>(param)->ReloadChangedFile(path);\n\t}\n\n\tStatus ReloadChangedFile(const VfsPath& path)\n\t{\n\t\t// Uncache settings file, if this is one\n\t\tm_SettingsFiles.erase(path);\n\n\t\t// Find all textures using this file\n\t\tHotloadFilesMap::iterator files = m_HotloadFiles.find(path);\n\t\tif (files != m_HotloadFiles.end())\n\t\t{\n\t\t\t// Flag all textures using this file as needing reloading\n\t\t\tfor (std::set<std::weak_ptr<CTexture> >::iterator it = files->second.begin(); it != files->second.end(); ++it)\n\t\t\t{\n\t\t\t\tif (std::shared_ptr<CTexture> texture = it->lock())\n\t\t\t\t{\n\t\t\t\t\ttexture->m_State = CTexture::UNLOADED;\n\t\t\t\t\ttexture->SetHandle(m_DefaultHandle);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn INFO::OK;\n\t}\n\n\tsize_t GetBytesUploaded() const\n\t{\n\t\tsize_t size = 0;\n\t\tfor (TextureCache::const_iterator it = m_TextureCache.begin(); it != m_TextureCache.end(); ++it)\n\t\t\tsize += (*it)->GetUploadedSize();\n\t\treturn size;\n\t}\n\nprivate:\n\tPIVFS m_VFS;\n\tCCacheLoader m_CacheLoader;\n\tbool m_DisableGL;\n\tCTextureConverter m_TextureConverter;\n\n\tHandle m_DefaultHandle;\n\tHandle m_ErrorHandle;\n\tCTexturePtr m_ErrorTexture;\n\n\t// Cache of all loaded textures\n\ttypedef boost::unordered_set<CTexturePtr, TPhash, TPequal_to> TextureCache;\n\tTextureCache m_TextureCache;\n\t// TODO: we ought to expire unused textures from the cache eventually\n\n\t// Store the set of textures that need to be reloaded when the given file\n\t// (a source file or settings.xml) is modified\n\ttypedef boost::unordered_map<VfsPath, std::set<std::weak_ptr<CTexture>, std::owner_less<std::weak_ptr<CTexture>>>> HotloadFilesMap;\n\tHotloadFilesMap m_HotloadFiles;\n\n\t// Cache for the conversion settings files\n\ttypedef boost::unordered_map<VfsPath, shared_ptr<CTextureConverter::SettingsFile> > SettingsFilesMap;\n\tSettingsFilesMap m_SettingsFiles;\n};\n\nCTexture::CTexture(Handle handle, const CTextureProperties& props, CTextureManagerImpl* textureManager) :\n\tm_Handle(handle), m_BaseColor(0), m_State(UNLOADED), m_Properties(props), m_TextureManager(textureManager)\n{\n\t// Add a reference to the handle (it might be shared by multiple CTextures\n\t// so we can't take ownership of it)\n\tif (m_Handle)\n\t\th_add_ref(m_Handle);\n}\n\nCTexture::~CTexture()\n{\n\tif (m_Handle)\n\t\togl_tex_free(m_Handle);\n}\n\nvoid CTexture::Bind(size_t unit)\n{\n\togl_tex_bind(GetHandle(), unit);\n}\n\nHandle CTexture::GetHandle()\n{\n\t// TODO: TryLoad might call ogl_tex_upload which enables GL_TEXTURE_2D\n\t// on texture unit 0, regardless of 'unit', which callers might\n\t// not be expecting. Ideally that wouldn't happen.\n\n\tTryLoad();\n\n\treturn m_Handle;\n}\n\nbool CTexture::TryLoad()\n{\n\t// If we haven't started loading, then try loading, and if that fails then request conversion.\n\t// If we have already tried prefetch loading, and it failed, bump the conversion request to HIGH priority.\n\tif (m_State == UNLOADED || m_State == PREFETCH_NEEDS_LOADING || m_State == PREFETCH_NEEDS_CONVERTING)\n\t{\n\t\tif (std::shared_ptr<CTexture> self = m_Self.lock())\n\t\t{\n\t\t\tif (m_State != PREFETCH_NEEDS_CONVERTING && m_TextureManager->TryLoadingCached(self))\n\t\t\t\tm_State = LOADED;\n\t\t\telse\n\t\t\t\tm_State = HIGH_NEEDS_CONVERTING;\n\t\t}\n\t}\n\n\treturn (m_State == LOADED);\n}\n\nvoid CTexture::Prefetch()\n{\n\tif (m_State == UNLOADED)\n\t{\n\t\tif (std::shared_ptr<CTexture> self = m_Self.lock())\n\t\t{\n\t\t\tm_State = PREFETCH_NEEDS_LOADING;\n\t\t}\n\t}\n}\n\nbool CTexture::IsLoaded()\n{\n\treturn (m_State == LOADED);\n}\n\nvoid CTexture::SetHandle(Handle handle, bool takeOwnership)\n{\n\tif (handle == m_Handle)\n\t\treturn;\n\n\tif (!takeOwnership)\n\t\th_add_ref(handle);\n\n\togl_tex_free(m_Handle);\n\tm_Handle = handle;\n}\n\nsize_t CTexture::GetWidth() const\n{\n\tsize_t w = 0;\n\t(void)ogl_tex_get_size(m_Handle, &w, 0, 0);\n\treturn w;\n}\n\nsize_t CTexture::GetHeight() const\n{\n\tsize_t h = 0;\n\t(void)ogl_tex_get_size(m_Handle, 0, &h, 0);\n\treturn h;\n}\n\nbool CTexture::HasAlpha() const\n{\n\tsize_t flags = 0;\n\t(void)ogl_tex_get_format(m_Handle, &flags, 0);\n\treturn (flags & TEX_ALPHA) != 0;\n}\n\nu32 CTexture::GetBaseColor() const\n{\n\treturn m_BaseColor;\n}\n\nsize_t CTexture::GetUploadedSize() const\n{\n\tsize_t size = 0;\n\t(void)ogl_tex_get_uploaded_size(m_Handle, &size);\n\treturn size;\n}\n\n\n// CTextureManager: forward all calls to impl:\n\nCTextureManager::CTextureManager(PIVFS vfs, bool highQuality, bool disableGL) :\n\tm(new CTextureManagerImpl(vfs, highQuality, disableGL))\n{\n}\n\nCTextureManager::~CTextureManager()\n{\n\tdelete m;\n}\n\nCTexturePtr CTextureManager::CreateTexture(const CTextureProperties& props)\n{\n\treturn m->CreateTexture(props);\n}\n\nCTexturePtr CTextureManager::GetErrorTexture()\n{\n\treturn m->GetErrorTexture();\n}\n\nbool CTextureManager::MakeProgress()\n{\n\treturn m->MakeProgress();\n}\n\nbool CTextureManager::GenerateCachedTexture(const VfsPath& path, VfsPath& outputPath)\n{\n\treturn m->GenerateCachedTexture(path, outputPath);\n}\n\nsize_t CTextureManager::GetBytesUploaded() const\n{\n\treturn m->GetBytesUploaded();\n}\n"
  },
  {
    "path": "fpsgame/graphics/TextureManager.h",
    "content": "/* Copyright (C) 2015 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef INCLUDED_TEXTUREMANAGER\n#define INCLUDED_TEXTUREMANAGER\n\n#include \"Texture.h\"\n\n#include <memory>\n\n#include \"lib/ogl.h\"\n#include \"lib/file/vfs/vfs.h\"\n#include \"lib/res/handle.h\"\n\nclass CTextureProperties;\nclass CTextureManagerImpl;\n\n/**\n * Texture manager with asynchronous loading and automatic DDS conversion/compression.\n *\n * Input textures can be any format. They will be converted to DDS using settings defined\n * in files named \"texture.xml\", in the same directory as the texture and in its parent\n * directories. See CTextureConverter for the XML syntax. The DDS file will be cached\n * for faster loading in the future.\n *\n * Typically the graphics code will initialise many textures at the start of the game,\n * mostly for off-screen objects, by calling CreateTexture().\n * Loading texture data may be very slow (especially if it needs to be converted\n * to DDS), and we don't want the game to become unresponsive.\n * CreateTexture therefore returns an object immediately, without loading the\n * texture. If the object is never used then the data will never be loaded.\n *\n * Typically, the renderer will call CTexture::Bind() when it wants to use the\n * texture. This will trigger the loading of the texture data. If it can be loaded\n * quickly (i.e. there is already a cached DDS version), then it will be loaded before\n * the function returns, and the texture can be rendered as normal.\n *\n * If loading will take a long time, then Bind() binds a default placeholder texture\n * and starts loading the texture in the background. It will use the correct texture\n * when the renderer next calls Bind() after the load has finished.\n *\n * It is also possible to prefetch textures which are not being rendered yet, but\n * are expected to be rendered soon (e.g. for off-screen terrain tiles).\n * These will be loaded in the background, when there are no higher-priority textures\n * to load.\n *\n * The same texture file can be safely loaded multiple times with different GL parameters\n * (but this should be avoided whenever possible, as it wastes VRAM).\n *\n * For release packages, DDS files can be precached by appending \".dds\" to their name,\n * which will be used instead of doing runtime conversion. This means most players should\n * never experience the slow asynchronous conversion behaviour.\n * These cache files will typically be packed into an archive for faster loading;\n * if no archive cache is available then the source file will be converted and stored\n * as a loose cache file on the user's disk.\n */\nclass CTextureManager\n{\n\tNONCOPYABLE(CTextureManager);\n\npublic:\n\t/**\n\t * Construct texture manager. vfs must be the VFS instance used for all textures\n\t * loaded from this object.\n\t * highQuality is slower and intended for batch-conversion modes.\n\t * disableGL is intended for tests, and will disable all GL uploads.\n\t */\n\tCTextureManager(PIVFS vfs, bool highQuality, bool disableGL);\n\n\t~CTextureManager();\n\n\t/**\n\t * Create a texture with the given GL properties.\n\t * The texture data will not be loaded immediately.\n\t */\n\tCTexturePtr CreateTexture(const CTextureProperties& props);\n\n\t/**\n\t * Returns a magenta texture. Use this for highlighting errors\n\t * (e.g. missing terrain textures).\n\t */\n\tCTexturePtr GetErrorTexture();\n\n\t/**\n\t * Work on asynchronous texture loading operations, if any.\n\t * Returns true if it did any work.\n\t * The caller should typically loop this per frame until it returns\n\t * false or exceeds the allocated time for this frame.\n\t */\n\tbool MakeProgress();\n\n\t/**\n\t * Synchronously converts and compresses and saves the texture,\n\t * and returns the output path (minus a \"cache/\" prefix). This\n\t * is intended for pre-caching textures in release archives.\n\t * @return true on success\n\t */\n\tbool GenerateCachedTexture(const VfsPath& path, VfsPath& outputPath);\n\n\t/**\n\t * Returns total number of bytes uploaded for all current texture.\n\t */\n\tsize_t GetBytesUploaded() const;\n\nprivate:\n\tCTextureManagerImpl* m;\n};\n\n/**\n * Represents the filename and GL parameters of a texture,\n * for passing to CTextureManager::CreateTexture.\n */\nclass CTextureProperties\n{\n\tfriend class CTextureManagerImpl;\n\tfriend struct TextureCacheCmp;\n\tfriend struct TPequal_to;\n\tfriend struct TPhash;\n\npublic:\n\t/**\n\t * Use the given texture name, and default GL parameters.\n\t */\n\texplicit CTextureProperties(const VfsPath& path) :\n\t\tm_Path(path), m_Filter(GL_LINEAR_MIPMAP_LINEAR),\n\t\tm_WrapS(GL_REPEAT), m_WrapT(GL_REPEAT), m_Aniso(1.0f), m_Format(0)\n\t{\n\t}\n\n\t/**\n\t * Set min/mag filter mode (typically GL_LINEAR_MIPMAP_LINEAR, GL_NEAREST, etc).\n\t */\n\tvoid SetFilter(GLint filter) { m_Filter = filter; }\n\n\t/**\n\t * Set wrapping mode (typically GL_REPEAT, GL_CLAMP_TO_EDGE, etc).\n\t */\n\tvoid SetWrap(GLint wrap) { m_WrapS = wrap; m_WrapT = wrap; }\n\n\t/**\n\t * Set wrapping mode (typically GL_REPEAT, GL_CLAMP_TO_EDGE, etc),\n\t * separately for S and T.\n\t */\n\tvoid SetWrap(GLint wrap_s, GLint wrap_t) { m_WrapS = wrap_s; m_WrapT = wrap_t; }\n\n\t/**\n\t * Set maximum anisotropy value. Must be >= 1.0. Should be a power of 2.\n\t */\n\tvoid SetMaxAnisotropy(float aniso) { m_Aniso = aniso; }\n\n\t/**\n\t * Set GL texture upload format, to override the default.\n\t * Typically GL_ALPHA or GL_LUMINANCE for 8-bit textures.\n\t */\n\tvoid SetFormatOverride(GLenum format) { m_Format = format; }\n\n\t// TODO: rather than this static definition of texture properties\n\t// (especially anisotropy), maybe we want something that can be more\n\t// easily tweaked in an Options menu? e.g. the caller just specifies\n\t// \"terrain texture mode\" and we combine it with the user's options.\n\t// That'd let us dynamically change texture properties easily.\n\t//\n\t// enum EQualityMode\n\t// {\n\t//   NONE,\n\t//   TERRAIN,\n\t//   MODEL,\n\t//   GUI\n\t// }\n\t// void SetQuality(EQualityMode mode, float anisotropy, float lodbias, int reducemipmaps, ...);\n\t//\n\t// or something a bit like that.\n\nprivate:\n\t// Must update TPhash, TPequal_to when changing these fields\n\tVfsPath m_Path;\n\tGLint m_Filter;\n\tGLint m_WrapS;\n\tGLint m_WrapT;\n\tfloat m_Aniso;\n\tGLenum m_Format;\n};\n\n/**\n * Represents a texture object.\n * The texture data may or may not have been loaded yet.\n * Before it has been loaded, all operations will act on a default\n * 1x1-pixel grey texture instead.\n */\nclass CTexture\n{\n\tfriend class CTextureManagerImpl;\n\tfriend struct TextureCacheCmp;\n\tfriend struct TPequal_to;\n\tfriend struct TPhash;\n\n\t// Only the texture manager can create these\n\texplicit CTexture(Handle handle, const CTextureProperties& props, CTextureManagerImpl* textureManager);\n\n\tNONCOPYABLE(CTexture);\n\npublic:\n\n\t~CTexture();\n\n\t/**\n\t * Returns the width (in pixels) of the current texture.\n\t */\n\tsize_t GetWidth() const;\n\n\t/**\n\t * Returns the height (in pixels) of the current texture.\n\t */\n\tsize_t GetHeight() const;\n\n\t/**\n\t * Returns whether the current texture has an alpha channel.\n\t */\n\tbool HasAlpha() const;\n\n\t/**\n\t * Returns the ARGB value of the lowest mipmap level (i.e. the\n\t * average of the whole texture).\n\t * Returns 0 if the texture has no mipmaps.\n\t */\n\tu32 GetBaseColor() const;\n\n\t/**\n\t * Returns total number of bytes uploaded for this texture.\n\t */\n\tsize_t GetUploadedSize() const;\n\n\t/**\n\t * Bind the texture to the given GL texture unit.\n\t * If the texture data hasn't been loaded yet, this may wait a short while to\n\t * load it. If loading takes too long then it will return sooner and the data will\n\t * be loaded in a background thread, so this does not guarantee the texture really\n\t * will be loaded.\n\t */\n\tvoid Bind(size_t unit = 0);\n\n\t/**\n\t * Returns a ogl_tex handle, for later binding. See comments from Bind().\n\t */\n\tHandle GetHandle();\n\n\t/**\n\t * Attempt to load the texture data quickly, as with Bind().\n\t * Returns whether the texture data is currently loaded.\n\t */\n\tbool TryLoad();\n\n\t/**\n\t * Returns whether the texture data is currently loaded.\n\t */\n\tbool IsLoaded();\n\n\t/**\n\t * Activate the prefetching optimisation for this texture.\n\t * Use this if it is likely the texture will be needed in the near future.\n\t * It will be loaded in the background so that it is likely to be ready when\n\t * it is used by Bind().\n\t */\n\tvoid Prefetch();\n\nprivate:\n\t/**\n\t * Replace the Handle stored by this object.\n\t * If takeOwnership is true, it will not increment the Handle's reference count.\n\t */\n\tvoid SetHandle(Handle handle, bool takeOwnership = false);\n\n\tconst CTextureProperties m_Properties;\n\n\tHandle m_Handle;\n\tu32 m_BaseColor;\n\n\tenum {\n\t\tUNLOADED, // loading has not started\n\t\tPREFETCH_NEEDS_LOADING, // was prefetched; currently waiting to try loading from cache\n\t\tPREFETCH_NEEDS_CONVERTING, // was prefetched; currently waiting to be sent to the texture converter\n\t\tPREFETCH_IS_CONVERTING, // was prefetched; currently being processed by the texture converter\n\t\tHIGH_NEEDS_CONVERTING, // high-priority; currently waiting to be sent to the texture converter\n\t\tHIGH_IS_CONVERTING, // high-priority; currently being processed by the texture converter\n\t\tLOADED // loading has completed (successfully or not)\n\t} m_State;\n\n\tCTextureManagerImpl* m_TextureManager;\n\n\t// Self-reference to let us recover the CTexturePtr for this object.\n\t// (weak pointer to avoid cycles)\n\tstd::weak_ptr<CTexture> m_Self;\n};\n\nstd::size_t hash_value(const CTexturePtr& v);\nstd::size_t hash_value(const CTextureProperties& v);\n\n#endif // INCLUDED_TEXTUREMANAGER\n"
  },
  {
    "path": "fpsgame/graphics/Unit.cpp",
    "content": "/* Copyright (C) 2016 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"precompiled.h\"\n\n#include \"Unit.h\"\n#include \"Model.h\"\n#include \"ObjectBase.h\"\n#include \"ObjectEntry.h\"\n#include \"ObjectManager.h\"\n#include \"SkeletonAnim.h\"\n#include \"SkeletonAnimDef.h\"\n#include \"UnitAnimation.h\"\n\nCUnit::CUnit(CObjectEntry* object, CObjectManager& objectManager,\n\t\t\t const std::set<CStr>& actorSelections, uint32_t seed)\n: m_Object(object), m_Model(object->m_Model->Clone()),\n  m_ID(INVALID_ENTITY), m_ActorSelections(actorSelections),\n  m_ObjectManager(objectManager), m_Seed(seed)\n{\n\tif (m_Model->ToCModel())\n\t\tm_Animation = new CUnitAnimation(m_ID, m_Model->ToCModel(), m_Object);\n\telse\n\t\tm_Animation = NULL;\n}\n\nCUnit::~CUnit()\n{\n\tdelete m_Animation;\n\tdelete m_Model;\n}\n\nCUnit* CUnit::Create(const CStrW& actorName, uint32_t seed, const std::set<CStr>& selections, CObjectManager& objectManager)\n{\n\tCObjectBase* base = objectManager.FindObjectBase(actorName);\n\n\tif (! base)\n\t\treturn NULL;\n\n\tstd::set<CStr> actorSelections = base->CalculateRandomVariation(seed, selections);\n\n\tstd::vector<std::set<CStr> > selectionsVec;\n\tselectionsVec.push_back(actorSelections);\n\n\tCObjectEntry* obj = objectManager.FindObjectVariation(base, selectionsVec);\n\n\tif (! obj)\n\t\treturn NULL;\n\n\treturn new CUnit(obj, objectManager, actorSelections, seed);\n}\n\nvoid CUnit::UpdateModel(float frameTime)\n{\n\tif (m_Animation)\n\t\tm_Animation->Update(frameTime*1000.0f);\n}\n\nvoid CUnit::SetID(entity_id_t id)\n{\n\tm_ID = id;\n\tif (m_Animation)\n\t\tm_Animation->SetEntityID(id);\n}\n\nvoid CUnit::SetEntitySelection(const CStr& key, const CStr& selection)\n{\n\tCStr selection_lc = selection.LowerCase();\n\n\tif (m_EntitySelections[key] == selection_lc)\n\t\treturn;\n\tm_EntitySelections[key] = selection_lc;\n\n\tReloadObject();\n}\n\nvoid CUnit::SetEntitySelection(const std::map<CStr, CStr>& selections)\n{\n\tfor (const std::pair<CStr, CStr>& s : selections)\n\t\tm_EntitySelections[s.first] = s.second.LowerCase();\n\n\tReloadObject();\n}\n\nvoid CUnit::SetActorSelections(const std::set<CStr>& selections)\n{\n\tm_ActorSelections = selections;\n\tReloadObject();\n}\n\nvoid CUnit::ReloadObject()\n{\n\tstd::set<CStr> entitySelections;\n\tfor (const std::pair<CStr, CStr>& selection : m_EntitySelections)\n\t\tentitySelections.insert(selection.second);\n\tstd::vector<std::set<CStr> > selections;\n\tselections.push_back(entitySelections);\n\tselections.push_back(m_ActorSelections);\n\n\t// randomly select any remain selections necessary to completely identify a variation (e.g., the new selection\n\t// made might define some additional props that require a random variant choice). Also, FindObjectVariation\n\t// expects the selectors passed to it to be complete.\n\t// see http://trac.wildfiregames.com/ticket/979\n\t\n\t// Use the entity ID as randomization seed (same as when the unit was first created)\n\tstd::set<CStr> remainingSelections = m_Object->m_Base->CalculateRandomRemainingSelections(m_Seed, selections);\n\tif (!remainingSelections.empty())\n\t\tselections.push_back(remainingSelections);\n\n\t// If these selections give a different object, change this unit to use it\n\tCObjectEntry* newObject = m_ObjectManager.FindObjectVariation(m_Object->m_Base, selections);\n\tif (newObject && newObject != m_Object)\n\t{\n\t\t// Clone the new object's base (non-instance) model\n\t\tCModelAbstract* newModel = newObject->m_Model->Clone();\n\n\t\t// Copy the old instance-specific settings from the old model to the new instance\n\t\tnewModel->SetTransform(m_Model->GetTransform());\n\t\tnewModel->SetPlayerID(m_Model->GetPlayerID());\n\t\tif (newModel->ToCModel() && m_Model->ToCModel())\n\t\t{\n\t\t\tnewModel->ToCModel()->CopyAnimationFrom(m_Model->ToCModel());\n\n\t\t\t// Copy flags that belong to this model instance (not those defined by the actor XML)\n\t\t\tint instanceFlags = (MODELFLAG_SILHOUETTE_DISPLAY|MODELFLAG_SILHOUETTE_OCCLUDER|MODELFLAG_IGNORE_LOS) & m_Model->ToCModel()->GetFlags();\n\t\t\tnewModel->ToCModel()->AddFlagsRec(instanceFlags);\n\t\t}\n\n\t\tdelete m_Model;\n\t\tm_Model = newModel;\n\t\tm_Object = newObject;\n\n\t\tif (m_Model->ToCModel())\n\t\t{\n\t\t\tif (m_Animation)\n\t\t\t\tm_Animation->ReloadUnit(m_Model->ToCModel(), m_Object); // TODO: maybe this should try to preserve animation state?\n\t\t\telse\n\t\t\t\tm_Animation = new CUnitAnimation(m_ID, m_Model->ToCModel(), m_Object);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tSAFE_DELETE(m_Animation);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "fpsgame/graphics/Unit.h",
    "content": "/* Copyright (C) 2016 Wildfire Games.\n* This file is part of 0 A.D.\n*\n* 0 A.D. is free software: you can redistribute it and/or modify\n* it under the terms of the GNU General Public License as published by\n* the Free Software Foundation, either version 2 of the License, or\n* (at your option) any later version.\n*\n* 0 A.D. is distributed in the hope that it will be useful,\n* but WITHOUT ANY WARRANTY; without even the implied warranty of\n* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n* GNU General Public License for more details.\n*\n* You should have received a copy of the GNU General Public License\n* along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#ifndef INCLUDED_UNIT\n#define INCLUDED_UNIT\n\n#include <map>\n#include <set>\n\n#include \"ps/CStr.h\"\n#include \"simulation2/system/Entity.h\"\t// entity_id_t\n\nclass CModelAbstract;\nclass CObjectEntry;\nclass CObjectManager;\nclass CSkeletonAnim;\nclass CUnitAnimation;\n\n\n/////////////////////////////////////////////////////////////////////////////////////////////\n// CUnit: simple \"actor\" definition - defines a sole object within the world\nclass CUnit\n{\n\tNONCOPYABLE(CUnit);\nprivate:\n\t// Private constructor. Needs complete list of selections for the variation.\n\tCUnit(CObjectEntry* object, CObjectManager& objectManager,\n\t\tconst std::set<CStr>& actorSelections, uint32_t seed);\n\npublic:\n\t// Attempt to create a unit with the given actor, with a set of\n\t// suggested selections (with the rest being randomised using the\n\t// given random seed).\n\t// Returns NULL on failure.\n\tstatic CUnit* Create(const CStrW& actorName, uint32_t seed, const std::set<CStr>& selections, CObjectManager& objectManager);\n\n\t// destructor\n\t~CUnit();\n\n\t// get unit's template object\n\tconst CObjectEntry& GetObject() const { return *m_Object; }\n\t// get unit's model data\n\tCModelAbstract& GetModel() const { return *m_Model; }\n\n\tCUnitAnimation* GetAnimation() { return m_Animation; }\n\n\t/**\n\t* Update the model's animation.\n\t* @param frameTime time in seconds\n\t*/\n\tvoid UpdateModel(float frameTime);\n\n\t// Sets the entity-selection, and updates the unit to use the new\n\t// actor variation. Either set one key at a time, or a complete map.\n\tvoid SetEntitySelection(const CStr& key, const CStr& selection);\n\tvoid SetEntitySelection(const std::map<CStr, CStr>& selections);\n\n\t// Most units have a hopefully-unique ID number, so they can be referred to\n\t// persistently despite saving/loading maps. Default for new units is -1; should\n\t// usually be set to CUnitManager::GetNewID() after creation.\n\tentity_id_t GetID() const { return m_ID; }\n\tvoid SetID(entity_id_t id);\n\n\tconst std::set<CStr>& GetActorSelections() const { return m_ActorSelections; }\n\n\tvoid SetActorSelections(const std::set<CStr>& selections);\n\nprivate:\n\t// object from which unit was created; never NULL\n\tCObjectEntry* m_Object;\n\t// object model representation; never NULL\n\tCModelAbstract* m_Model;\n\n\tCUnitAnimation* m_Animation;\n\n\t// unique (per map) ID number for units created in the editor, as a\n\t// permanent way of referencing them.\n\tentity_id_t m_ID;\n\n\t// seed used when creating unit\n\tuint32_t m_Seed;\n\n\t// actor-level selections for this unit\n\tstd::set<CStr> m_ActorSelections;\n\t// entity-level selections for this unit\n\tstd::map<CStr, CStr> m_EntitySelections;\n\n\t// object manager which looks after this unit's objectentry\n\tCObjectManager& m_ObjectManager;\n\n\tvoid ReloadObject();\n\n\tfriend class CUnitAnimation;\n};\n\n#endif\n"
  },
  {
    "path": "fpsgame/graphics/UnitAnimation.cpp",
    "content": "/* Copyright (C) 2016 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"precompiled.h\"\n\n#include \"UnitAnimation.h\"\n\n#include \"graphics/Model.h\"\n#include \"graphics/ObjectEntry.h\"\n#include \"graphics/SkeletonAnim.h\"\n#include \"graphics/SkeletonAnimDef.h\"\n#include \"graphics/Unit.h\"\n#include \"lib/rand.h\"\n#include \"ps/CStr.h\"\n#include \"ps/Game.h\"\n#include \"simulation2/Simulation2.h\"\n#include \"simulation2/components/ICmpSoundManager.h\"\n\n// Randomly modify the speed, so that units won't stay perfectly\n// synchronised if they're playing animations of the same length\nstatic float DesyncSpeed(float speed, float desync)\n{\n\tif (desync == 0.0f)\n\t\treturn speed;\n\n\treturn speed * (1.f - desync + 2.f*desync*(rand(0, 256)/255.f));\n}\n\nCUnitAnimation::CUnitAnimation(entity_id_t ent, CModel* model, CObjectEntry* object)\n\t: m_Entity(ent), m_State(\"idle\"), m_AnimationName(\"idle\"), m_Looping(true),\n\t  m_Speed(1.f), m_SyncRepeatTime(0.f), m_OriginalSpeed(1.f), m_Desync(0.f)\n{\n\tReloadUnit(model, object);\n}\n\nvoid CUnitAnimation::SetEntityID(entity_id_t ent)\n{\n\tm_Entity = ent;\n}\n\nvoid CUnitAnimation::AddModel(CModel* model, const CObjectEntry* object)\n{\n\tSModelAnimState state;\n\n\tstate.model = model;\n\tstate.object = object;\n\tstate.anim = object->GetRandomAnimation(m_State);\n\tstate.time = 0.f;\n\tstate.pastLoadPos = false;\n\tstate.pastActionPos = false;\n\tstate.pastSoundPos = false;\n\n\tENSURE(state.anim != NULL); // there must always be an idle animation\n\n\tm_AnimStates.push_back(state);\n\n\tmodel->SetAnimation(state.anim, !m_Looping);\n\n\t// Detect if this unit has any non-static animations\n\tfor (CSkeletonAnim* anim : object->GetAnimations(m_State))\n\t\tif (anim->m_AnimDef != NULL)\n\t\t\tm_AnimStatesAreStatic = false;\n\n\t// Recursively add all props\n\tconst std::vector<CModel::Prop>& props = model->GetProps();\n\tfor (const CModel::Prop& prop : props)\n\t{\n\t\tCModel* propModel = prop.m_Model->ToCModel();\n\t\tif (propModel)\n\t\t\tAddModel(propModel, prop.m_ObjectEntry);\n\t}\n}\n\nvoid CUnitAnimation::ReloadUnit(CModel* model, const CObjectEntry* object)\n{\n\tm_Model = model;\n\tm_Object = object;\n\n\tm_AnimStates.clear();\n\tm_AnimStatesAreStatic = true;\n\tAddModel(m_Model, m_Object);\n}\n\nvoid CUnitAnimation::SetAnimationState(const CStr& name, bool once, float speed, float desync, const CStrW& actionSound)\n{\n\tm_Looping = !once;\n\tm_OriginalSpeed = speed;\n\tm_Desync = desync;\n\tm_ActionSound = actionSound;\n\n\tm_Speed = DesyncSpeed(m_OriginalSpeed, m_Desync);\n\tm_SyncRepeatTime = 0.f;\n\n\tif (name != m_State)\n\t{\n\t\tm_State = name;\n\t\tm_AnimationName = name;\n\n\t\tReloadUnit(m_Model, m_Object);\n\t}\n}\n\nvoid CUnitAnimation::SetAnimationSyncRepeat(float repeatTime)\n{\n\tm_SyncRepeatTime = repeatTime;\n}\n\nvoid CUnitAnimation::SetAnimationSyncOffset(float actionTime)\n{\n\tif (m_AnimStatesAreStatic)\n\t\treturn;\n\n\t// Update all the synced prop models to each coincide with actionTime\n\tfor (std::vector<SModelAnimState>::iterator it = m_AnimStates.begin(); it != m_AnimStates.end(); ++it)\n\t{\n\t\tCSkeletonAnimDef* animDef = it->anim->m_AnimDef;\n\t\tif (animDef == NULL)\n\t\t\tcontinue; // ignore static animations\n\n\t\tfloat duration = animDef->GetDuration();\n\n\t\tfloat actionPos = it->anim->m_ActionPos;\n\t\tbool hasActionPos = (actionPos != -1.f);\n\n\t\tif (!hasActionPos)\n\t\t\tcontinue;\n\n\t\tfloat speed = duration / m_SyncRepeatTime;\n\n\t\t// Need to offset so that start+actionTime*speed = actionPos\n\t\tfloat start = actionPos - actionTime*speed;\n\t\t// Wrap it so that it's within the animation\n\t\tstart = fmodf(start, duration);\n\t\tif (start < 0)\n\t\t\tstart += duration;\n\n\t\tit->time = start;\n\t}\n}\n\nvoid CUnitAnimation::Update(float time)\n{\n\tif (m_AnimStatesAreStatic)\n\t\treturn;\n\n\t// Advance all of the prop models independently\n\tfor (std::vector<SModelAnimState>::iterator it = m_AnimStates.begin(); it != m_AnimStates.end(); ++it)\n\t{\n\t\tCSkeletonAnimDef* animDef = it->anim->m_AnimDef;\n\t\tif (animDef == NULL)\n\t\t\tcontinue; // ignore static animations\n\n\t\tfloat duration = animDef->GetDuration();\n\n\t\tfloat actionPos = it->anim->m_ActionPos;\n\t\tfloat loadPos = it->anim->m_ActionPos2;\n\t\tfloat soundPos = it->anim->m_SoundPos;\n\t\tbool hasActionPos = (actionPos != -1.f);\n\t\tbool hasLoadPos = (loadPos != -1.f);\n\t\tbool hasSoundPos = (soundPos != -1.f);\n\n\t\t// Find the current animation speed\n\t\tfloat speed;\n\t\tif (m_SyncRepeatTime && hasActionPos)\n\t\t\tspeed = duration / m_SyncRepeatTime;\n\t\telse\n\t\t\tspeed = m_Speed * it->anim->m_Speed;\n\n\t\t// Convert from real time to scaled animation time\n\t\tfloat advance = time * speed;\n\n\t\t// If we're going to advance past the load point in this update, then load the ammo\n\t\tif (hasLoadPos && !it->pastLoadPos && it->time + advance >= loadPos)\n\t\t{\n\t\t\tit->model->ShowAmmoProp();\n\t\t\tit->pastLoadPos = true;\n\t\t}\n\n\t\t// If we're going to advance past the action point in this update, then perform the action\n\t\tif (hasActionPos && !it->pastActionPos && it->time + advance >= actionPos)\n\t\t{\n\t\t\tif (hasLoadPos)\n\t\t\t\tit->model->HideAmmoProp();\n\n\t\t\tif ( !hasSoundPos && !m_ActionSound.empty() )\n\t\t\t{\n\t\t\t\tCmpPtr<ICmpSoundManager> cmpSoundManager(*g_Game->GetSimulation2(), SYSTEM_ENTITY);\n\t\t\t\tif (cmpSoundManager)\n\t\t\t\t\tcmpSoundManager->PlaySoundGroup(m_ActionSound, m_Entity);\n\t\t\t}\n\n\t\t\tit->pastActionPos = true;\n\t\t}\n\t\tif (hasSoundPos && !it->pastSoundPos && it->time + advance >= soundPos)\n\t\t{\n\t\t\tif (!m_ActionSound.empty() )\n\t\t\t{\n\t\t\t\tCmpPtr<ICmpSoundManager> cmpSoundManager(*g_Game->GetSimulation2(), SYSTEM_ENTITY);\n\t\t\t\tif (cmpSoundManager)\n\t\t\t\t\tcmpSoundManager->PlaySoundGroup(m_ActionSound, m_Entity);\n\t\t\t}\n\n\t\t\tit->pastSoundPos = true;\n\t\t}\n\n\t\tif (it->time + advance < duration)\n\t\t{\n\t\t\t// If we're still within the current animation, then simply update it\n\t\t\tit->time += advance;\n\t\t\tit->model->UpdateTo(it->time);\n\t\t}\n\t\telse if (m_Looping)\n\t\t{\n\t\t\t// If we've finished the current animation and want to loop...\n\n\t\t\t// Wrap the timer around\n\t\t\tit->time = fmod(it->time + advance, duration);\n\n\t\t\t// Pick a new random animation\n\t\t\tCSkeletonAnim* anim;\n\t\t\tif (it->model == m_Model)\n\t\t\t{\n\t\t\t\t// we're handling the root model\n\t\t\t\t// choose animations from the complete state\n\t\t\t\tanim = it->object->GetRandomAnimation(m_State);\n\t\t\t\t// if we use a new animation name,\n\t\t\t\t// update the animations of all non-root models\n\t\t\t\t// sync with the root model, unless the root model could\n\t\t\t\t// only resort to the \"idle\" state.\n\t\t\t\tif (anim->m_Name != \"idle\")\n\t\t\t\t\tm_AnimationName = anim->m_Name;\n\t\t\t\telse\n\t\t\t\t\tm_AnimationName = m_State;\n\t\t\t\tif (it->anim->m_Name != m_AnimationName)\n\t\t\t\t\tfor (SModelAnimState animState : m_AnimStates)\n\t\t\t\t\t\tif (animState.model != m_Model)\n\t\t\t\t\t\t\tanimState.model->SetAnimation(animState.object->GetRandomAnimation(m_AnimationName));\n\t\t\t}\n\t\t\telse\n\t\t\t\t// choose animations that match the root\n\t\t\t\tanim = it->object->GetRandomAnimation(m_AnimationName);\n\n\t\t\tif (anim != it->anim)\n\t\t\t{\n\t\t\t\tit->anim = anim;\n\t\t\t\tit->model->SetAnimation(anim, !m_Looping);\n\t\t\t}\n\n\t\t\tit->pastActionPos = false;\n\t\t\tit->pastLoadPos = false;\n\t\t\tit->pastSoundPos = false;\n\n\t\t\tit->model->UpdateTo(it->time);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// If we've finished the current animation and don't want to loop...\n\n\t\t\t// Update to very nearly the end of the last frame (but not quite the end else we'll wrap around when skinning)\n\t\t\tfloat nearlyEnd = duration - 1.f;\n\t\t\tif (fabs(it->time - nearlyEnd) > 1.f)\n\t\t\t{\n\t\t\t\tit->time = nearlyEnd;\n\t\t\t\tit->model->UpdateTo(it->time);\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "fpsgame/graphics/UnitAnimation.h",
    "content": "/* Copyright (C) 2016 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef INCLUDED_UNITANIMATION\n#define INCLUDED_UNITANIMATION\n\n#include \"ps/CStr.h\"\n\n#include \"simulation2/system/Entity.h\"\n\nclass CUnit;\nclass CModel;\nclass CSkeletonAnim;\nclass CObjectEntry;\n\n/**\n * Deals with synchronisation issues between raw animation data (CModel, CSkeletonAnim)\n * and the simulation system (via CUnit), providing a simple fire-and-forget API to play animations.\n * (This is really just a component of CUnit and could probably be merged back into that class.)\n */\nclass CUnitAnimation\n{\n\tNONCOPYABLE(CUnitAnimation);\npublic:\n\t/**\n\t * Construct for a given unit, defaulting to the \"idle\" animation.\n\t */\n\tCUnitAnimation(entity_id_t ent, CModel* model, CObjectEntry* object);\n\n\t/**\n\t * Change the entity ID associated with this animation\n\t * (currently used for playing locational sound effects).\n\t */\n\tvoid SetEntityID(entity_id_t ent);\n\n\t/**\n\t * Start playing an animation.\n\t * The unit's actor defines the available animations, and if more than one is available\n\t * then one is picked at random (with a new random choice each loop).\n\t * By default, animations start immediately and run at the given speed with no syncing.\n\t * Use SetAnimationSync after this to force a specific timing, if it needs to match the\n\t * simulation timing.\n\t * Alternatively, set @p desync to a non-zero value (e.g. 0.05) to slightly randomise the\n\t * offset and speed, so units don't all move in lockstep.\n\t * @param name animation's name (\"idle\", \"walk\", etc)\n\t * @param once if true then the animation freezes on its last frame; otherwise it loops\n\t * @param speed fraction of actor-defined speed to play back at (should typically be 1.0)\n\t * @param desync maximum fraction of length/speed to randomly adjust timings (or 0.0 for no desyncing)\n\t * @param actionSound sound group name to be played at the 'action' point in the animation, or empty string\n\t */\n\tvoid SetAnimationState(const CStr& name, bool once, float speed, float desync, const CStrW& actionSound);\n\n\t/**\n\t * Adjust the speed of the current animation, so that Update(repeatTime) will do a\n\t * complete animation loop.\n\t * @param repeatTime time for complete loop of animation, in msec\n\t */\n\tvoid SetAnimationSyncRepeat(float repeatTime);\n\n\t/**\n\t * Adjust the offset of the current animation, so that Update(actionTime) will advance it\n\t * to the 'action' point defined in the actor.\n\t * This must be called after SetAnimationSyncRepeat sets the speed.\n\t * @param actionTime time between now and when the action should occur, in msec\n\t */\n\tvoid SetAnimationSyncOffset(float actionTime);\n\n\t/**\n\t * Advance the animation state.\n\t * @param time advance time in msec\n\t */\n\tvoid Update(float time);\n\n\t/**\n\t * Regenerate internal animation state from the models in the current unit.\n\t * This should be called whenever the unit is changed externally, to keep this in sync.\n\t */\n\tvoid ReloadUnit(CModel* model, const CObjectEntry* object);\n\nprivate:\n\tstruct SModelAnimState\n\t{\n\t\tCModel* model;\n\t\tCSkeletonAnim* anim;\n\t\tconst CObjectEntry* object;\n\t\tfloat time;\n\t\tbool pastLoadPos;\n\t\tbool pastActionPos;\n\t\tbool pastSoundPos;\n\t};\n\n\tstd::vector<SModelAnimState> m_AnimStates;\n\n\t/**\n\t * True if all the current AnimStates are static, so Update() doesn't need\n\t * to do any work at all\n\t */\n\tbool m_AnimStatesAreStatic;\n\n\tvoid AddModel(CModel* model, const CObjectEntry* object);\n\n\tentity_id_t m_Entity;\n\tCModel* m_Model;\n\tconst CObjectEntry* m_Object;\n\tCStr m_State;\n\tCStr m_AnimationName;\n\tbool m_Looping;\n\tfloat m_OriginalSpeed;\n\tfloat m_Speed;\n\tfloat m_SyncRepeatTime;\n\tfloat m_Desync;\n\tCStrW m_ActionSound;\n};\n\n#endif // INCLUDED_UNITANIMATION\n"
  },
  {
    "path": "fpsgame/graphics/UnitManager.cpp",
    "content": "/* Copyright (C) 2012 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n/*\n * Container that owns all units\n */\n\n#include \"precompiled.h\"\n\n#include <float.h>\n\n#include \"Model.h\"\n#include \"UnitManager.h\"\n#include \"Unit.h\"\n#include \"ObjectManager.h\"\n#include \"ObjectEntry.h\"\n#include \"ps/Game.h\"\n#include \"ps/World.h\"\n\n#include <algorithm>\n\n///////////////////////////////////////////////////////////////////////////////\n// CUnitManager constructor\nCUnitManager::CUnitManager() :\n\tm_ObjectManager(NULL)\n{\n}\n\n///////////////////////////////////////////////////////////////////////////////\n// CUnitManager destructor\nCUnitManager::~CUnitManager()\n{\n\tDeleteAll();\n}\n\n\n///////////////////////////////////////////////////////////////////////////////\n// AddUnit: add given unit to world\nvoid CUnitManager::AddUnit(CUnit* unit)\n{\n\tm_Units.push_back(unit);\n}\n\n///////////////////////////////////////////////////////////////////////////////\n// RemoveUnit: remove given unit from world, but don't delete it\nvoid CUnitManager::RemoveUnit(CUnit* unit)\n{\n\t// find entry in list\n\ttypedef std::vector<CUnit*>::iterator Iter;\n\tIter i=std::find(m_Units.begin(),m_Units.end(),unit);\n\tif (i!=m_Units.end()) {\n\t\tm_Units.erase(i);\n\t}\n}\n\n///////////////////////////////////////////////////////////////////////////////\n// DeleteUnit: remove given unit from world and delete it\nvoid CUnitManager::DeleteUnit(CUnit* unit)\n{\n\tRemoveUnit(unit);\n\tdelete unit;\n}\n\n///////////////////////////////////////////////////////////////////////////////\n// DeleteAll: remove and delete all units\nvoid CUnitManager::DeleteAll()\n{\n\tfor (size_t i=0;i<m_Units.size();i++) {\n\t\tdelete m_Units[i];\n\t}\n\tm_Units.clear();\n}\n\n///////////////////////////////////////////////////////////////////////////////\n// CreateUnit: create a new unit and add it to the world\nCUnit* CUnitManager::CreateUnit(const CStrW& actorName, uint32_t seed, const std::set<CStr8>& selections)\n{\n\tif (! m_ObjectManager)\n\t\treturn NULL;\n\n\tCUnit* unit = CUnit::Create(actorName, seed, selections, *m_ObjectManager);\n\tif (unit)\n\t\tAddUnit(unit);\n\treturn unit;\n}\n"
  },
  {
    "path": "fpsgame/graphics/UnitManager.h",
    "content": "/* Copyright (C) 2012 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n/*\n * Container that owns all units\n */\n\n#ifndef INCLUDED_UNITMANAGER\n#define INCLUDED_UNITMANAGER\n\n#include <vector>\n#include <set>\n\nclass CUnit;\nclass CVector3D;\nclass CObjectManager;\nclass CStr8;\nclass CStrW;\n\n///////////////////////////////////////////////////////////////////////////////\n// CUnitManager: simple container class holding all units within the world\nclass CUnitManager\n{\npublic:\n\t// constructor, destructor\n\tCUnitManager();\n\t~CUnitManager();\n\n\t// add given unit to world\n\tvoid AddUnit(CUnit* unit);\n\t// remove given unit from world, but don't delete it\n\tvoid RemoveUnit(CUnit* unit);\n\t// remove given unit from world and delete it\n\tvoid DeleteUnit(CUnit* unit);\n\t// remove and delete all units\n\tvoid DeleteAll();\n\n\t// creates a new unit and adds it to the world\n\tCUnit* CreateUnit(const CStrW& actorName, uint32_t seed, const std::set<CStr8>& selections);\n\n\t// return the units\n\tconst std::vector<CUnit*>& GetUnits() const { return m_Units; }\n\t\n\tvoid SetObjectManager(CObjectManager& objectManager) { m_ObjectManager = &objectManager; }\n\nprivate:\n\t// list of all known units\n\tstd::vector<CUnit*> m_Units;\n\t// graphical object manager; may be NULL if not set up\n\tCObjectManager* m_ObjectManager;\n};\n\n#endif\n"
  },
  {
    "path": "fpsgame/gui/adts/bit_buf.h",
    "content": "/* Copyright (c) 2011 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * FIFO bit queue\n */\n\n#ifndef INCLUDED_ADTS_BIT_BUF\n#define INCLUDED_ADTS_BIT_BUF\n\n#include \"lib/bits.h\"\n\nstruct BitBuf\n{\n\tuintptr_t buf;\n\tuintptr_t cur;\t// bit to be appended (toggled by add())\n\tsize_t len;\t// |buf| [bits]\n\n\tvoid reset()\n\t{\n\t\tbuf = 0;\n\t\tcur = 0;\n\t\tlen = 0;\n\t}\n\n\t// toggle current bit if desired, and add to buffer (new bit is LSB)\n\tvoid add(uintptr_t toggle)\n\t{\n\t\tcur ^= toggle;\n\t\tbuf <<= 1;\n\t\tbuf |= cur;\n\t\tlen++;\n\t}\n\n\t// extract LS n bits\n\tsize_t extract(uintptr_t n)\n\t{\n\t\tconst uintptr_t bits = buf & bit_mask<uintptr_t>(n);\n\t\tbuf >>= n;\n\n\t\treturn bits;\n\t}\n};\n\n#endif\t// #ifndef INCLUDED_ADTS_BIT_BUF\n"
  },
  {
    "path": "fpsgame/gui/adts/cache_adt.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * Customizable cache data structure.\n */\n\n#ifndef INCLUDED_CACHE_ADT\n#define INCLUDED_CACHE_ADT\n\n#include <cfloat>\n\n#include <list>\n#include <map>\n#include <queue> // std::priority_queue\n\n#if CONFIG_ENABLE_BOOST\n# include <boost/unordered_map.hpp>\n# define MAP boost::unordered_map\n#else\n# define MAP stdext::hash_map\n#endif\n\n/*\nCache for items of variable size and value/\"cost\".\nunderlying displacement algorithm is pluggable; default is \"Landlord\".\n\ntemplate reference:\nEntry provides size, cost, credit and credit_density().\n  rationale:\n  - made a template instead of exposing Cache::Entry because\n    that would drag a lot of stuff out of Cache.\n  - calculates its own density since that entails a Divider functor,\n    which requires storage inside Entry.\nEntries is a collection with iterator and begin()/end() and\n  \"static Entry& entry_from_it(iterator)\".\n  rationale:\n  - STL map has pair<key, item> as its value_type, so this\n    function would return it->second. however, we want to support\n    other container types (where we'd just return *it).\nManager is a template parameterized on typename Key and class Entry.\n  its interface is as follows:\n\n\t// is the cache empty?\n\tbool empty() const;\n\n\t// add (key, entry) to cache.\n\tvoid add(const Key& key, const Entry& entry);\n\n\t// if the entry identified by <key> is not in cache, return false;\n\t// otherwise return true and pass back a pointer to it.\n\tbool find(const Key& key, const Entry** pentry) const;\n\n\t// remove an entry from cache, which is assumed to exist!\n\t// this makes sense because callers will typically first use find() to\n\t// return info about the entry; this also checks if present.\n\tvoid remove(const Key& key);\n\n\t// mark <entry> as just accessed for purpose of cache management.\n\t// it will tend to be kept in cache longer.\n\tvoid on_access(Entry& entry);\n\n\t// caller's intent is to remove the least valuable entry.\n\t// in implementing this, you have the latitude to \"shake loose\"\n\t// several entries (e.g. because their 'value' is equal).\n\t// they must all be push_back-ed into the list; Cache will dole\n\t// them out one at a time in FIFO order to callers.\n\t//\n\t// rationale:\n\t// - it is necessary for callers to receive a copy of the\n\t//   Entry being evicted - e.g. file_cache owns its items and\n\t//   they must be passed back to allocator when evicted.\n\t// - e.g. Landlord can potentially see several entries become\n\t//   evictable in one call to remove_least_valuable. there are\n\t//   several ways to deal with this:\n\t//   1) generator interface: we return one of { empty, nevermind,\n\t//      removed, remove-and-call-again }. this greatly complicates\n\t//      the call site.\n\t//   2) return immediately after finding an item to evict.\n\t//      this changes cache behavior - entries stored at the\n\t//      beginning would be charged more often (unfair).\n\t//      resuming charging at the next entry doesn't work - this\n\t//      would have to be flushed when adding, at which time there\n\t//      is no provision for returning any items that may be evicted.\n\t//   3) return list of all entries \"shaken loose\". this incurs\n\t//      frequent mem allocs, which can be alleviated via suballocator.\n\t//      note: an intrusive linked-list doesn't make sense because\n\t//      entries to be returned need to be copied anyway (they are\n\t//      removed from the manager's storage).\n\tvoid remove_least_valuable(std::list<Entry>& entry_list)\n*/\n\n\n//\n// functors to calculate minimum credit density (MCD)\n//\n\n// MCD is required for the Landlord algorithm's evict logic.\n// [Young02] calls it '\\delta'.\n\n// scan over all entries and return MCD.\ntemplate<class Entries> float ll_calc_min_credit_density(const Entries& entries)\n{\n\tfloat min_credit_density = FLT_MAX;\n\tfor(typename Entries::const_iterator it = entries.begin(); it != entries.end(); ++it)\n\t{\n\t\tconst float credit_density = Entries::entry_from_it(it).credit_density();\n\t\tmin_credit_density = std::min(min_credit_density, credit_density);\n\t}\n\treturn min_credit_density;\n}\n\n// note: no warning is given that the MCD entry is being removed!\n// (reduces overhead in remove_least_valuable)\n// these functors must account for that themselves (e.g. by resetting\n// their state directly after returning MCD).\n\n// determine MCD by scanning over all entries.\n// tradeoff: O(N) time complexity, but all notify* calls are no-ops.\ntemplate<class Entry, class Entries>\nclass McdCalc_Naive\n{\npublic:\n\tvoid notify_added(const Entry&) const {}\n\tvoid notify_decreased(const Entry&) const {}\n\tvoid notify_impending_increase_or_remove(const Entry&) const {}\n\tvoid notify_increased_or_removed(const Entry&) const {}\n\tfloat operator()(const Entries& entries) const\n\t{\n\t\tconst float mcd = ll_calc_min_credit_density(entries);\n\t\treturn mcd;\n\t}\n};\n\n// cache previous MCD and update it incrementally (when possible).\n// tradeoff: amortized O(1) time complexity, but notify* calls must\n// perform work whenever something in the cache changes.\ntemplate<class Entry, class Entries>\nclass McdCalc_Cached\n{\npublic:\n\tMcdCalc_Cached() : min_credit_density(FLT_MAX), min_valid(false) {}\n\n\tvoid notify_added(const Entry& entry)\n\t{\n\t\t// when adding a new item, the minimum credit density can only\n\t\t// decrease or remain the same; acting as if entry's credit had\n\t\t// been decreased covers both cases.\n\t\tnotify_decreased(entry);\n\t}\n\n\tvoid notify_decreased(const Entry& entry)\n\t{\n\t\tmin_credit_density = std::min(min_credit_density, entry.credit_density());\n\t}\n\n\tvoid notify_impending_increase_or_remove(const Entry& entry)\n\t{\n\t\t// remember if this entry had the smallest density\n\t\tis_min_entry = feq(min_credit_density, entry.credit_density());\n\t}\n\n\tvoid notify_increased_or_removed(const Entry& UNUSED(entry))\n\t{\n\t\t// .. it did and was increased or removed. we must invalidate\n\t\t// MCD and recalculate it next time.\n\t\tif(is_min_entry)\n\t\t{\n\t\t\tmin_valid = false;\n\t\t\tmin_credit_density = -1.0f;\n\t\t}\n\t}\n\n\tfloat operator()(const Entries& entries)\n\t{\n\t\tif(min_valid)\n\t\t{\n\t\t\t// the entry that has MCD will be removed anyway by caller;\n\t\t\t// we need to invalidate here because they don't call\n\t\t\t// notify_increased_or_removed.\n\t\t\tmin_valid = false;\n\t\t\treturn min_credit_density;\n\t\t}\n\n\t\t// this is somewhat counterintuitive. since we're calculating\n\t\t// MCD directly, why not mark our cached version of it valid\n\t\t// afterwards? reason is that our caller will remove the entry with\n\t\t// MCD, so it'll be invalidated anyway.\n\t\t// instead, our intent is to calculate MCD for the *next time*.\n\t\tconst float ret = ll_calc_min_credit_density(entries);\n\t\tmin_valid = true;\n\t\tmin_credit_density = FLT_MAX;\n\t\treturn ret;\n\t}\n\nprivate:\n\tfloat min_credit_density;\n\tbool min_valid;\n\n\t// temporary flag set by notify_impending_increase_or_remove\n\tbool is_min_entry;\n};\n\n\n//\n// Landlord cache management policy: see [Young02].\n//\n\n// in short, each entry has credit initially set to cost. when wanting to\n// remove an item, all are charged according to MCD and their size;\n// entries are evicted if their credit is exhausted. accessing an entry\n// restores \"some\" of its credit.\ntemplate<typename Key, typename Entry, template<class Entry_, class Entries> class McdCalc = McdCalc_Cached>\nclass Landlord\n{\npublic:\n\tbool empty() const\n\t{\n\t\treturn map.empty();\n\t}\n\n\tvoid add(const Key& key, const Entry& entry)\n\t{\n\t\t// adapter for add_ (which returns an iterator)\n\t\t(void)add_(key, entry);\n\t}\n\n\tbool find(const Key& key, const Entry** pentry) const\n\t{\n\t\tMapCIt it = map.find(key);\n\t\tif(it == map.end())\n\t\t\treturn false;\n\t\t*pentry = &it->second;\n\t\treturn true;\n\t}\n\n\tvoid remove(const Key& key)\n\t{\n\t\tMapIt it = map.find(key);\n\t\t// note: don't complain if not in the cache: this happens after\n\t\t// writing a file and invalidating its cache entry, which may\n\t\t// or may not exist.\n\t\tif(it != map.end())\n\t\t\tremove_(it);\n\t}\n\n\tvoid on_access(Entry& entry)\n\t{\n\t\tmcd_calc.notify_impending_increase_or_remove(entry);\n\n\t\t// Landlord algorithm calls for credit to be reset to anything\n\t\t// between its current value and the cost.\n\t\tconst float gain = 0.75f;\t// restore most credit\n\t\tentry.credit = gain*entry.cost + (1.0f-gain)*entry.credit;\n\n\t\tmcd_calc.notify_increased_or_removed(entry);\n\t}\n\n\tvoid remove_least_valuable(std::list<Entry>& entry_list)\n\t{\n\t\t// we are required to evict at least one entry. one iteration\n\t\t// ought to suffice, due to definition of min_credit_density and\n\t\t// tolerance; however, we provide for repeating if necessary.\nagain:\n\n\t\t// messing with this (e.g. raising if tiny) would result in\n\t\t// different evictions than Landlord_Lazy, which is unacceptable.\n\t\t// nor is doing so necessary: if mcd is tiny, so is credit.\n\t\tconst float min_credit_density = mcd_calc(map);\n\t\tENSURE(min_credit_density > 0.0f);\n\n\t\tfor(MapIt it = map.begin(); it != map.end();)\t// no ++it\n\t\t{\n\t\t\tEntry& entry = it->second;\n\n\t\t\tcharge(entry, min_credit_density);\n\t\t\tif(should_evict(entry))\n\t\t\t{\n\t\t\t\tentry_list.push_back(entry);\n\n\t\t\t\t// annoying: we have to increment <it> before erasing\n\t\t\t\tMapIt it_to_remove = it++;\n\t\t\t\tmap.erase(it_to_remove);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tmcd_calc.notify_decreased(entry);\n\t\t\t\t++it;\n\t\t\t}\n\t\t}\n\n\t\tif(entry_list.empty())\n\t\t\tgoto again;\n\t}\n\nprotected:\n\tclass Map : public MAP<Key, Entry>\n\t{\n\tpublic:\n\t\tstatic Entry& entry_from_it(typename Map::iterator it) { return it->second; }\n\t\tstatic const Entry& entry_from_it(typename Map::const_iterator it) { return it->second; }\n\t};\n\ttypedef typename Map::iterator MapIt;\n\ttypedef typename Map::const_iterator MapCIt;\n\tMap map;\n\n\t// add entry and return iterator pointing to it.\n\tMapIt add_(const Key& key, const Entry& entry)\n\t{\n\t\ttypedef std::pair<MapIt, bool> PairIB;\n\t\ttypename Map::value_type val = std::make_pair(key, entry);\n\t\tPairIB ret = map.insert(val);\n\t\tENSURE(ret.second);\t// must not already be in map\n\n\t\tmcd_calc.notify_added(entry);\n\n\t\treturn ret.first;\n\t}\n\n\t// remove entry (given by iterator) directly.\n\tvoid remove_(MapIt it)\n\t{\n\t\tconst Entry& entry = it->second;\n\t\tmcd_calc.notify_impending_increase_or_remove(entry);\n\t\tmcd_calc.notify_increased_or_removed(entry);\n\t\tmap.erase(it);\n\t}\n\n\tvoid charge(Entry& entry, float delta)\n\t{\n\t\tentry.credit -= delta * entry.size;\n\n\t\t// don't worry about entry.size being 0 - if so, cost\n\t\t// should also be 0, so credit will already be 0 anyway.\n\t}\n\n\t// for each entry, 'charge' it (i.e. reduce credit by) delta * its size.\n\t// delta is typically MCD (see above); however, several such updates\n\t// may be lumped together to save time. Landlord_Lazy does this.\n\tvoid charge_all(float delta)\n\t{\n\t\tfor(MapIt it = map.begin(); it != map.end(); ++it)\n\t\t{\n\t\t\tEntry& entry = it->second;\n\t\t\tentry.credit -= delta * entry.size;\n\t\t\tif(!should_evict(entry))\n\t\t\t\tmcd_calc.notify_decreased(entry);\n\t\t}\n\t}\n\n\t// is entry's credit exhausted? if so, it should be evicted.\n\tbool should_evict(const Entry& entry)\n\t{\n\t\t// we need a bit of leeway because density calculations may not\n\t\t// be exact. choose value carefully: must not be high enough to\n\t\t// trigger false positives.\n\t\treturn entry.credit < 0.0001f;\n\t}\n\nprivate:\n\tMcdCalc<Entry, Map> mcd_calc;\n};\n\n// Cache manger policies. (these are partial specializations of Landlord,\n// adapting it to the template params required by Cache)\ntemplate<class Key, class Entry> class Landlord_Naive : public Landlord<Key, Entry, McdCalc_Naive> {};\ntemplate<class Key, class Entry> class Landlord_Cached: public Landlord<Key, Entry, McdCalc_Cached> {};\n\n// variant of Landlord that adds a priority queue to directly determine\n// which entry to evict. this allows lumping several charge operations\n// together and thus reduces iteration over all entries.\n// tradeoff: O(logN) removal (instead of N), but additional O(N) storage.\ntemplate<typename Key, class Entry>\nclass Landlord_Lazy : public Landlord_Naive<Key, Entry>\n{\n\ttypedef typename Landlord_Naive<Key, Entry>::Map Map;\n\ttypedef typename Landlord_Naive<Key, Entry>::MapIt MapIt;\n\ttypedef typename Landlord_Naive<Key, Entry>::MapCIt MapCIt;\n\npublic:\n\tLandlord_Lazy() { pending_delta = 0.0f; }\n\n\tvoid add(const Key& key, const Entry& entry)\n\t{\n\t\t// we must apply pending_delta now - otherwise, the existing delta\n\t\t// would later be applied to this newly added item (incorrect).\n\t\tcommit_pending_delta();\n\n\t\tMapIt it = Parent::add_(key, entry);\n\t\tpri_q.push(it);\n\t}\n\n\tvoid remove(const Key& key)\n\t{\n\t\tParent::remove(key);\n\n\t\t// reconstruct pri_q from current map. this is slow (N*logN) and\n\t\t// could definitely be done better, but we don't bother since\n\t\t// remove is a very rare operation (e.g. invalidating entries).\n\t\twhile(!pri_q.empty())\n\t\t\tpri_q.pop();\n\t\tfor(MapCIt it = this->map.begin(); it != this->map.end(); ++it)\n\t\t\tpri_q.push(it);\n\t}\n\n\tvoid on_access(Entry& entry)\n\t{\n\t\tParent::on_access(entry);\n\n\t\t// entry's credit was changed. we now need to reshuffle the\n\t\t// pri queue to reflect this.\n\t\tpri_q.ensure_heap_order();\n\t}\n\n\tvoid remove_least_valuable(std::list<Entry>& entry_list)\n\t{\n\t\tMapIt least_valuable_it = pri_q.top(); pri_q.pop();\n\t\tEntry& entry = Map::entry_from_it(least_valuable_it);\n\n\t\tentry_list.push_back(entry);\n\n\t\t// add to pending_delta the MCD that would have resulted\n\t\t// if removing least_valuable_it normally.\n\t\t// first, calculate actual credit (i.e. apply pending_delta to\n\t\t// this entry); then add the resulting density to pending_delta.\n\t\tentry.credit -= pending_delta*entry.size;\n\t\tconst float credit_density = entry.credit_density();\n\t\tENSURE(credit_density > 0.0f);\n\t\tpending_delta += credit_density;\n\n\t\tParent::remove_(least_valuable_it);\n\t}\n\nprivate:\n\ttypedef Landlord_Naive<Key, Entry> Parent;\n\n\t// sort iterators by credit_density of the Entry they reference.\n\tstruct CD_greater\n\t{\n\t\tbool operator()(MapIt it1, MapIt it2) const\n\t\t{\n\t\t\treturn Map::entry_from_it(it1).credit_density() >\n\t\t\t       Map::entry_from_it(it2).credit_density();\n\t\t}\n\t};\n\t// wrapper on top of priority_queue that allows 'heap re-sift'\n\t// (see on_access).\n\t// notes:\n\t// - greater comparator makes pri_q.top() the one with\n\t//   LEAST credit_density, which is what we want.\n\t// - deriving from an STL container is a bit dirty, but we need this\n\t//   to get at the underlying data (priority_queue interface is not\n\t//   very capable).\n\tclass PriQ: public std::priority_queue<MapIt, std::vector<MapIt>, CD_greater>\n\t{\n\tpublic:\n\t\tvoid ensure_heap_order()\n\t\t{\n\t\t\t// TODO: this is actually N*logN - ouch! that explains high\n\t\t\t// CPU cost in profile. this is called after only 1 item has\n\t\t\t// changed, so a logN \"sift\" operation ought to suffice.\n\t\t\t// that's not supported by the STL heap functions, so we'd\n\t\t\t// need a better implementation. pending..\n\t\t\tstd::make_heap(this->c.begin(), this->c.end(), this->comp);\n\t\t}\n\t};\n\tPriQ pri_q;\n\n\t// delta values that have accumulated over several\n\t// remove_least_valuable() calls. applied during add().\n\tfloat pending_delta;\n\n\tvoid commit_pending_delta()\n\t{\n\t\tif(pending_delta > 0.0f)\n\t\t{\n\t\t\tthis->charge_all(pending_delta);\n\t\t\tpending_delta = 0.0f;\n\n\t\t\t// we've changed entry credit, so the heap order *may* have been\n\t\t\t// violated; reorder the pri queue. (I don't think so,\n\t\t\t// due to definition of delta, but we'll play it safe)\n\t\t\tpri_q.ensure_heap_order();\n\t\t}\n\t}\n};\n\n\n//\n// functor that implements division of first arg by second\n//\n\n// this is used to calculate credit_density(); performance matters\n// because this is called for each entry during each remove operation.\n\n// floating-point division (fairly slow)\nclass Divider_Naive\n{\npublic:\n\tDivider_Naive() {}\t// needed for default CacheEntry ctor\n\tDivider_Naive(float UNUSED(x)) {}\n\tfloat operator()(float val, float divisor) const\n\t{\n\t\treturn val / divisor;\n\t}\n};\n\n// caches reciprocal of divisor and multiplies by that.\n// tradeoff: only 4 clocks (instead of 20), but 4 bytes extra per entry.\nclass Divider_Recip\n{\n\tfloat recip;\npublic:\n\tDivider_Recip() {}\t// needed for default CacheEntry ctor\n\tDivider_Recip(float x) { recip = 1.0f / x; }\n\tfloat operator()(float val, float UNUSED(divisor)) const\n\t{\n\t\treturn val * recip;\n\t}\n};\n\n\n// initial implementation for testing purposes; quite inefficient.\ntemplate<typename Key, typename Entry>\nclass LRU\n{\npublic:\n\tbool empty() const\n\t{\n\t\treturn lru.empty();\n\t}\n\n\tvoid add(const Key& key, const Entry& entry)\n\t{\n\t\tlru.push_back(KeyAndEntry(key, entry));\n\t}\n\n\tbool find(const Key& key, const Entry** pentry) const\n\t{\n\t\tCIt it = std::find(lru.begin(), lru.end(), KeyAndEntry(key));\n\t\tif(it == lru.end())\n\t\t\treturn false;\n\t\t*pentry = &it->entry;\n\t\treturn true;\n\t}\n\n\tvoid remove(const Key& key)\n\t{\n\t\tlru.remove(KeyAndEntry(key));\n\t}\n\n\tvoid on_access(Entry& entry)\n\t{\n\t\tfor(It it = lru.begin(); it != lru.end(); ++it)\n\t\t{\n\t\t\tif(&entry == &it->entry)\n\t\t\t{\n\t\t\t\tadd(it->key, it->entry);\n\t\t\t\tlru.erase(it);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tDEBUG_WARN_ERR(ERR::LOGIC);\t// entry not found in list\n\t}\n\n\tvoid remove_least_valuable(std::list<Entry>& entry_list)\n\t{\n\t\tentry_list.push_back(lru.front().entry);\n\t\tlru.pop_front();\n\t}\n\nprivate:\n\tstruct KeyAndEntry\n\t{\n\t\tKeyAndEntry(const Key& key): key(key) {}\n\t\tKeyAndEntry(const Key& key, const Entry& entry): key(key), entry(entry) {}\n\n\t\tbool operator==(const KeyAndEntry& rhs) const { return key == rhs.key; }\n\t\tbool operator!=(const KeyAndEntry& rhs) const { return !operator==(rhs); }\n\n\t\tKey key;\n\t\tEntry entry;\n\t};\n\n\ttypedef std::list<KeyAndEntry> List;\n\ttypedef typename List::iterator It;\n\ttypedef typename List::const_iterator CIt;\n\tList lru;\n};\n\n\n// this is applicable to all cache management policies and stores all\n// required information. a Divider functor is used to implement\n// division for credit_density.\ntemplate<class Item, class Divider> struct CacheEntry\n{\n\tItem item;\n\tsize_t size;\n\tsize_t cost;\n\tfloat credit;\n\n\tDivider divider;\n\n\t// needed for mgr.remove_least_valuable's entry_copy\n\tCacheEntry()\n\t{\n\t}\n\n\tCacheEntry(const Item& item_, size_t size_, size_t cost_)\n\t\t: item(item_), divider((float)size_)\n\t{\n\t\tsize = size_;\n\t\tcost = cost_;\n\t\tcredit = (float)cost;\n\n\t\t// else divider will fail\n\t\tENSURE(size != 0);\n\t}\n\n\tfloat credit_density() const\n\t{\n\t\treturn divider(credit, (float)size);\n\t}\n};\n\n\n//\n// Cache\n//\n\ntemplate\n<\ntypename Key, typename Item,\n// see documentation above for Manager's interface.\ntemplate<typename Key_, class Entry> class Manager = Landlord_Cached,\nclass Divider = Divider_Naive\n>\nclass Cache\n{\npublic:\n\tCache() : mgr() {}\n\n\tvoid add(const Key& key, const Item& item, size_t size, size_t cost)\n\t{\n\t\treturn mgr.add(key, Entry(item, size, cost));\n\t}\n\n\t// remove the entry identified by <key>. expected usage is to check\n\t// if present and determine size via retrieve(), so no need for\n\t// error checking.\n\t// useful for invalidating single cache entries.\n\tvoid remove(const Key& key)\n\t{\n\t\tmgr.remove(key);\n\t}\n\n\t// if there is no entry for <key> in the cache, return false.\n\t// otherwise, return true and pass back item and (optionally) size.\n\t//\n\t// if refill_credit (default), the cache manager 'rewards' this entry,\n\t// tending to keep it in cache longer. this parameter is not used in\n\t// normal operation - it's only for special cases where we need to\n\t// make an end run around the cache accounting (e.g. for cache simulator).\n\tbool retrieve(const Key& key, Item& item, size_t* psize = 0, bool refill_credit = true)\n\t{\n\t\tconst Entry* entry;\n\t\tif(!mgr.find(key, &entry))\n\t\t\treturn false;\n\n\t\titem = entry->item;\n\t\tif(psize)\n\t\t\t*psize = entry->size;\n\n\t\tif(refill_credit)\n\t\t\tmgr.on_access((Entry&)*entry);\n\n\t\treturn true;\n\t}\n\n\tbool peek(const Key& key, Item& item, size_t* psize = 0)\n\t{\n\t\treturn retrieve(key, item, psize, false);\n\t}\n\n\t// toss out the least valuable entry. return false if cache is empty,\n\t// otherwise true and (optionally) pass back its item and size.\n\tbool remove_least_valuable(Item* pItem = 0, size_t* pSize = 0)\n\t{\n\t\t// as an artefact of the cache eviction policy, several entries\n\t\t// may be \"shaken loose\" by one call to remove_least_valuable.\n\t\t// we cache them in a list to disburden callers (they always get\n\t\t// exactly one).\n\t\tif(entries_awaiting_eviction.empty())\n\t\t{\n\t\t\tif(empty())\n\t\t\t\treturn false;\n\n\t\t\tmgr.remove_least_valuable(entries_awaiting_eviction);\n\t\t\tENSURE(!entries_awaiting_eviction.empty());\n\t\t}\n\n\t\tconst Entry& entry = entries_awaiting_eviction.front();\n\t\tif(pItem)\n\t\t\t*pItem = entry.item;\n\t\tif(pSize)\n\t\t\t*pSize = entry.size;\n\t\tentries_awaiting_eviction.pop_front();\n\n\t\treturn true;\n\t}\n\n\tbool empty() const\n\t{\n\t\treturn mgr.empty();\n\t}\n\nprivate:\n\ttypedef CacheEntry<Item, Divider> Entry;\n\n\t// see note in remove_least_valuable().\n\tstd::list<Entry> entries_awaiting_eviction;\n\n\tManager<Key, Entry> mgr;\n};\n\n#endif\t// #ifndef INCLUDED_CACHE_ADT\n"
  },
  {
    "path": "fpsgame/gui/adts/dyn_hash_tbl.h",
    "content": "/* Copyright (c) 2011 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * dynamic (grow-able) hash table\n */\n\n#ifndef INCLUDED_ADTS_DYN_HASH_TBL\n#define INCLUDED_ADTS_DYN_HASH_TBL\n\n#include <string.h>\t// strcmp\n\n#include \"lib/fnv_hash.h\"\n\ntemplate<typename Key, typename T> class DHT_Traits\n{\npublic:\n\tstatic const size_t initial_entries = 16;\n\tsize_t hash(Key key) const;\n\tbool equal(Key k1, Key k2) const;\n\tKey get_key(T t) const;\n};\n\ntemplate<> class DHT_Traits<const char*, const char*>\n{\npublic:\n\tstatic const size_t initial_entries = 512;\n\tsize_t hash(const char* key) const\n\t{\n\t\treturn (size_t)fnv_lc_hash(key);\n\t}\n\tbool equal(const char* k1, const char* k2) const\n\t{\n\t\treturn !strcmp(k1, k2);\n\t}\n\tconst char* get_key(const char* t) const\n\t{\n\t\treturn t;\n\t}\n};\n\n\n// intended for pointer types\ntemplate<typename Key, typename T, typename Traits=DHT_Traits<Key,T> >\nclass DynHashTbl\n{\n\tT* tbl;\n\tu16 num_entries;\n\tu16 max_entries;\t// when initialized, = 2**n for faster modulo\n\tTraits tr;\n\n\tT& get_slot(Key key) const\n\t{\n\t\tsize_t hash = tr.hash(key);\n\t\tENSURE(max_entries != 0);\t// otherwise, mask will be incorrect\n\t\tconst size_t mask = max_entries-1;\n\t\tfor(;;)\n\t\t{\n\t\t\tT& t = tbl[hash & mask];\n\t\t\t// empty slot encountered => not found\n\t\t\tif(!t)\n\t\t\t\treturn t;\n\t\t\t// keys are actually equal => found it\n\t\t\tif(tr.equal(key, tr.get_key(t)))\n\t\t\t\treturn t;\n\t\t\t// keep going (linear probing)\n\t\t\thash++;\n\t\t}\n\t}\n\n\tvoid expand_tbl()\n\t{\n\t\t// alloc a new table (but don't assign it to <tbl> unless successful)\n\t\tT* old_tbl = tbl;\n\t\ttbl = (T*)calloc(max_entries*2, sizeof(T));\n\t\tif(!tbl)\n\t\t{\n\t\t\ttbl = old_tbl;\n\t\t\tthrow std::bad_alloc();\n\t\t}\n\n\t\tmax_entries *= 2;\n\t\t// must be set before get_slot\n\n\t\t// newly initialized, nothing to copy - done\n\t\tif(!old_tbl)\n\t\t\treturn;\n\n\t\t// re-hash from old table into the new one\n\t\tfor(size_t i = 0; i < max_entries/2u; i++)\n\t\t{\n\t\t\tT t = old_tbl[i];\n\t\t\tif(t)\n\t\t\t\tget_slot(tr.get_key(t)) = t;\n\t\t}\n\t\tfree(old_tbl);\n\t}\n\n\npublic:\n\n\tDynHashTbl()\n\t{\n\t\ttbl = 0;\n\t\tclear();\n\t}\n\n\t~DynHashTbl()\n\t{\n\t\tfree(tbl);\n\t}\n\n\tvoid clear()\n\t{\n\t\t// must remain usable after calling clear, so shrink the table to\n\t\t// its initial size but don't deallocate it completely\n\t\tSAFE_FREE(tbl);\n\t\tnum_entries = 0;\n\t\t// rationale: must not set to 0 because expand_tbl only doubles the size.\n\t\t// don't keep the previous size when clearing because it may have become\n\t\t// huge and there is no provision for shrinking.\n\t\tmax_entries = tr.initial_entries/2;\t// will be doubled in expand_tbl\n\t\texpand_tbl();\n\t}\n\n\tvoid insert(const Key key, const T t)\n\t{\n\t\t// more than 75% full - increase table size.\n\t\t// do so before determining slot; this will invalidate previous pnodes.\n\t\tif(num_entries*4 >= max_entries*3)\n\t\t\texpand_tbl();\n\n\t\tT& slot = get_slot(key);\n\t\tENSURE(slot == 0);\t// not already present\n\t\tslot = t;\n\t\tnum_entries++;\n\t}\n\n\tT find(Key key) const\n\t{\n\t\treturn get_slot(key);\n\t}\n\n\tsize_t size() const\n\t{\n\t\treturn num_entries;\n\t}\n\n\n\tclass iterator\n\t{\n\tpublic:\n\t\ttypedef std::forward_iterator_tag iterator_category;\n\t\ttypedef T value_type;\n\t\ttypedef ptrdiff_t difference_type;\n\t\ttypedef const T* pointer;\n\t\ttypedef const T& reference;\n\n\t\titerator()\n\t\t{\n\t\t}\n\t\titerator(T* pos_, T* end_) : pos(pos_), end(end_)\n\t\t{\n\t\t}\n\t\tT& operator*() const\n\t\t{\n\t\t\treturn *pos;\n\t\t}\n\t\titerator& operator++()\t// pre\n\t\t{\n\t\t\tdo\n\t\t\tpos++;\n\t\t\twhile(pos != end && *pos == 0);\n\t\t\treturn (*this);\n\t\t}\n\t\tbool operator==(const iterator& rhs) const\n\t\t{\n\t\t\treturn pos == rhs.pos;\n\t\t}\n\t\tbool operator<(const iterator& rhs) const\n\t\t{\n\t\t\treturn (pos < rhs.pos);\n\t\t}\n\n\t\t// derived\n\t\tconst T* operator->() const\n\t\t{\n\t\t\treturn &**this;\n\t\t}\n\t\tbool operator!=(const iterator& rhs) const\n\t\t{\n\t\t\treturn !(*this == rhs);\n\t\t}\n\t\titerator operator++(int)\t// post\n\t\t{\n\t\t\titerator tmp =  *this; ++*this; return tmp;\n\t\t}\n\n\tprotected:\n\t\tT* pos;\n\t\tT* end;\n\t\t// only used when incrementing (avoid going beyond end of table)\n\t};\n\n\titerator begin() const\n\t{\n\t\tT* pos = tbl;\n\t\twhile(pos != tbl+max_entries && *pos == 0)\n\t\t\tpos++;\n\t\treturn iterator(pos, tbl+max_entries);\n\t}\n\titerator end() const\n\t{\n\t\treturn iterator(tbl+max_entries, 0);\n\t}\n};\n\n\n#endif\t// #ifndef INCLUDED_ADTS_DYN_HASH_TBL\n"
  },
  {
    "path": "fpsgame/gui/adts/ring_buf.h",
    "content": "/* Copyright (c) 2011 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * static array, accessible modulo n\n */\n\n#ifndef INCLUDED_ADTS_RING_BUF\n#define INCLUDED_ADTS_RING_BUF\n\ntemplate<class T, size_t n> class RingBuf\n{\n\tsize_t size_;\t// # of entries in buffer\n\tsize_t head;\t// index of oldest item\n\tsize_t tail;\t// index of newest item\n\tT data[n];\n\npublic:\n\tRingBuf() : data() { clear(); }\n\tvoid clear() { size_ = 0; head = 0; tail = n-1; }\n\n\tsize_t size() const { return size_; }\n\tbool empty() const { return size_ == 0; }\n\n\tconst T& operator[](int ofs) const\n\t{\n\t\tENSURE(!empty());\n\t\tsize_t idx = (size_t)(head + ofs);\n\t\treturn data[idx % n];\n\t}\n\tT& operator[](int ofs)\n\t{\n\t\tENSURE(!empty());\n\t\tsize_t idx = (size_t)(head + ofs);\n\t\treturn data[idx % n];\n\t}\n\n\tT& front()\n\t{\n\t\tENSURE(!empty());\n\t\treturn data[head];\n\t}\n\tconst T& front() const\n\t{\n\t\tENSURE(!empty());\n\t\treturn data[head];\n\t}\n\tT& back()\n\t{\n\t\tENSURE(!empty());\n\t\treturn data[tail];\n\t}\n\tconst T& back() const\n\t{\n\t\tENSURE(!empty());\n\t\treturn data[tail];\n\t}\n\n\tvoid push_back(const T& item)\n\t{\n\t\tif(size_ < n)\n\t\t\tsize_++;\n\t\t// do not complain - overwriting old values is legit\n\t\t// (e.g. sliding window).\n\t\telse\n\t\t\thead = (head + 1) % n;\n\n\t\ttail = (tail + 1) % n;\n\t\tdata[tail] = item;\n\t}\n\n\tvoid pop_front()\n\t{\n\t\tif(size_ != 0)\n\t\t{\n\t\t\tsize_--;\n\t\t\thead = (head + 1) % n;\n\t\t}\n\t\telse\n\t\t\tDEBUG_WARN_ERR(ERR::LOGIC);\t// underflow\n\t}\n\n\tclass iterator\n\t{\n\tpublic:\n\t\ttypedef std::random_access_iterator_tag iterator_category;\n\t\ttypedef T value_type;\n\t\ttypedef ptrdiff_t difference_type;\n\t\ttypedef T* pointer;\n\t\ttypedef T& reference;\n\n\t\titerator() : data(0), pos(0)\n\t\t{}\n\t\titerator(T* data_, size_t pos_) : data(data_), pos(pos_)\n\t\t{}\n\t\tT& operator[](int idx) const\n\t\t{ return data[(pos+idx) % n]; }\n\t\tT& operator*() const\n\t\t{ return data[pos % n]; }\n\t\tT* operator->() const\n\t\t{ return &**this; }\n\t\titerator& operator++()\t// pre\n\t\t{ ++pos; return (*this); }\n\t\titerator operator++(int)\t// post\n\t\t{ iterator tmp =  *this; ++*this; return tmp; }\n\t\tbool operator==(const iterator& rhs) const\n\t\t{ return data == rhs.data && pos == rhs.pos; }\n\t\tbool operator!=(const iterator& rhs) const\n\t\t{ return !(*this == rhs); }\n\t\tbool operator<(const iterator& rhs) const\n\t\t{ return (pos < rhs.pos); }\n\t\titerator& operator+=(difference_type ofs)\n\t\t{ pos += ofs; return *this; }\n\t\titerator& operator-=(difference_type ofs)\n\t\t{ return (*this += -ofs); }\n\t\titerator operator+(difference_type ofs) const\n\t\t{ iterator tmp = *this; return (tmp += ofs); }\n\t\titerator operator-(difference_type ofs) const\n\t\t{ iterator tmp = *this; return (tmp -= ofs); }\n\t\tdifference_type operator-(const iterator right) const\n\t\t{ return (difference_type)(pos - right.pos); }\n\n\tprotected:\n\t\tT* data;\n\t\tsize_t pos;\n\t\t// not mod-N so that begin != end when buffer is full.\n\t};\n\n\tclass const_iterator\n\t{\n\tpublic:\n\t\ttypedef std::random_access_iterator_tag iterator_category;\n\t\ttypedef T value_type;\n\t\ttypedef ptrdiff_t difference_type;\n\t\ttypedef const T* pointer;\n\t\ttypedef const T& reference;\n\n\t\tconst_iterator() : data(0), pos(0)\n\t\t{}\n\t\tconst_iterator(const T* data_, size_t pos_) : data(data_), pos(pos_)\n\t\t{}\n\t\tconst T& operator[](int idx) const\n\t\t{ return data[(pos+idx) % n]; }\n\t\tconst T& operator*() const\n\t\t{ return data[pos % n]; }\n\t\tconst T* operator->() const\n\t\t{ return &**this; }\n\t\tconst_iterator& operator++()\t// pre\n\t\t{ ++pos; return (*this); }\n\t\tconst_iterator operator++(int)\t// post\n\t\t{ const_iterator tmp =  *this; ++*this; return tmp; }\n\t\tbool operator==(const const_iterator& rhs) const\n\t\t{ return data == rhs.data && pos == rhs.pos; }\n\t\tbool operator!=(const const_iterator& rhs) const\n\t\t{ return !(*this == rhs); }\n\t\tbool operator<(const const_iterator& rhs) const\n\t\t{ return (pos < rhs.pos); }\n\t\titerator& operator+=(difference_type ofs)\n\t\t{ pos += ofs; return *this; }\n\t\titerator& operator-=(difference_type ofs)\n\t\t{ return (*this += -ofs); }\n\t\titerator operator+(difference_type ofs) const\n\t\t{ iterator tmp = *this; return (tmp += ofs); }\n\t\titerator operator-(difference_type ofs) const\n\t\t{ iterator tmp = *this; return (tmp -= ofs); }\n\t\tdifference_type operator-(const iterator right) const\n\t\t{ return (difference_type)(pos - right.pos); }\n\n\tprotected:\n\t\tconst T* data;\n\t\tsize_t pos;\n\t\t// not mod-N so that begin != end when buffer is full.\n\t};\n\n\titerator begin()\n\t{\n\t\treturn iterator(data, head);\n\t}\n\tconst_iterator begin() const\n\t{\n\t\treturn const_iterator(data, head);\n\t}\n\titerator end()\n\t{\n\t\treturn iterator(data, head+size_);\n\t}\n\tconst_iterator end() const\n\t{\n\t\treturn const_iterator(data, head+size_);\n\t}\n};\n\n#endif\t// #ifndef INCLUDED_ADTS_RING_BUF\n"
  },
  {
    "path": "fpsgame/gui/allocators/aligned_allocator.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * STL allocator for aligned memory\n */\n\n#ifndef ALIGNED_ALLOCATOR\n#define ALIGNED_ALLOCATOR\n\n#include \"lib/bits.h\"\t// round_up\n#include \"lib/sysdep/arch/x86_x64/cache.h\"\n#include \"lib/sysdep/rtl.h\"\t// rtl_AllocateAligned\n\n\n/**\n * stateless STL allocator that aligns elements to the L1 cache line size.\n *\n * note: the alignment is hard-coded to avoid any allocator state.\n * this avoids portability problems, which is important since allocators\n * are rather poorly specified.\n *\n * references:\n * http://www.tantalon.com/pete/customallocators.ppt\n * http://www.flipcode.com/archives/Aligned_Block_Allocation.shtml\n * http://www.josuttis.com/cppcode/allocator.html\n *\n * derived from code that bears the following copyright notice:\n * (C) Copyright Nicolai M. Josuttis 1999.\n * Permission to copy, use, modify, sell and distribute this software\n * is granted provided this copyright notice appears in all copies.\n * This software is provided \"as is\" without express or implied\n * warranty, and with no claim as to its suitability for any purpose.\n **/\ntemplate<class T>\nclass AlignedAllocator\n{\npublic:\n\t// type definitions\n\ttypedef T        value_type;\n\ttypedef T*       pointer;\n\ttypedef const T* const_pointer;\n\ttypedef T&       reference;\n\ttypedef const T& const_reference;\n\ttypedef std::size_t    size_type;\n\ttypedef std::ptrdiff_t difference_type;\n\n\t// rebind allocator to type U\n\ttemplate <class U>\n\tstruct rebind\n\t{\n\t\ttypedef AlignedAllocator<U> other;\n\t};\n\n\tpointer address(reference value) const\n\t{\n\t\treturn &value;\n\t}\n\n\tconst_pointer address(const_reference value) const\n\t{\n\t\treturn &value;\n\t}\n\n\tNOTHROW_DEFINE AlignedAllocator()\n\t{\n\t}\n\n\tNOTHROW_DEFINE AlignedAllocator(const AlignedAllocator&)\n\t{\n\t}\n\n\ttemplate <class U>\n\tNOTHROW_DEFINE AlignedAllocator (const AlignedAllocator<U>&)\n\t{\n\t}\n\n\tNOTHROW_DEFINE ~AlignedAllocator()\n\t{\n\t}\n\n\tNOTHROW_DEFINE size_type max_size() const\n\t{\n\t\t// maximum number of *elements* that can be allocated\n\t\treturn std::numeric_limits<std::size_t>::max() / sizeof(T);\n\t}\n\n\t// allocate uninitialized storage\n\tpointer allocate(size_type numElements)\n\t{\n\t\tconst size_type alignment = x86_x64::Caches(x86_x64::L1D)->entrySize;\n\t\tconst size_type elementSize = round_up(sizeof(T), alignment);\n\t\tconst size_type size = numElements * elementSize;\n\t\tpointer p = (pointer)rtl_AllocateAligned(size, alignment);\n\t\treturn p;\n\t}\n\n\t// deallocate storage of elements that have been destroyed\n\tvoid deallocate(pointer p, size_type UNUSED(num))\n\t{\n\t\trtl_FreeAligned((void*)p);\n\t}\n\n\tvoid construct(pointer p, const T& value)\n\t{\n\t\tnew((void*)p) T(value);\n\t}\n\n\tvoid destroy(pointer p)\n\t{\n\t\tp->~T();\n\t\tUNUSED2(p);\t// otherwise, warning is raised for reasons unknown\n\t}\n};\n\n// indicate that all specializations of this allocator are interchangeable\ntemplate <class T1, class T2>\nNOTHROW_DEFINE bool operator==(const AlignedAllocator<T1>&, const AlignedAllocator<T2>&)\n{\n\treturn true;\n}\n\ntemplate <class T1, class T2>\nNOTHROW_DEFINE bool operator!=(const AlignedAllocator<T1>&, const AlignedAllocator<T2>&)\n{\n\treturn false;\n}\n\n#endif\t// #ifndef ALIGNED_ALLOCATOR\n"
  },
  {
    "path": "fpsgame/gui/allocators/allocator_adapters.h",
    "content": "/* Copyright (c) 2011 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * adapters for allocators; provides a minimal subset of the\n * STL allocator interface.\n */\n\n#ifndef ALLOCATOR_ADAPTERS\n#define ALLOCATOR_ADAPTERS\n\n#include <memory>\n\n#include \"lib/sysdep/rtl.h\"\n#include \"lib/sysdep/vm.h\"\n\n// NB: STL allocators are parameterized on the object type and indicate\n// the number of elements to [de]allocate. however, these adapters are\n// only used for allocating storage and receive the number of bytes.\n\nstruct Allocator_Heap\n{\n\tvoid* allocate(size_t size)\n\t{\n\t\treturn malloc(size);\n\t}\n\n\tvoid deallocate(void* p, size_t UNUSED(size))\n\t{\n\t\treturn free(p);\n\t}\n};\n\ntemplate<size_t alignment = allocationAlignment>\nstruct Allocator_Aligned\n{\n\tvoid* allocate(size_t size)\n\t{\n\t\treturn rtl_AllocateAligned(size, alignment);\n\t}\n\n\tvoid deallocate(void* p, size_t UNUSED(size))\n\t{\n\t\treturn rtl_FreeAligned(p);\n\t}\n};\n\ntemplate<vm::PageType pageType = vm::kDefault, int prot = PROT_READ|PROT_WRITE>\nstruct Allocator_VM\n{\n\tvoid* allocate(size_t size)\n\t{\n\t\treturn vm::Allocate(size, pageType, prot);\n\t}\n\n\tvoid deallocate(void* p, size_t size)\n\t{\n\t\tvm::Free(p, size);\n\t}\n};\n\ntemplate<size_t commitSize = largePageSize, vm::PageType pageType = vm::kDefault, int prot = PROT_READ|PROT_WRITE>\nstruct Allocator_AddressSpace\n{\n\tvoid* allocate(size_t size)\n\t{\n\t\treturn vm::ReserveAddressSpace(size, commitSize, pageType, prot);\n\t}\n\n\tvoid deallocate(void* p, size_t size)\n\t{\n\t\tvm::ReleaseAddressSpace(p, size);\n\t}\n};\n\n\n/**\n * fully STL-compatible allocator that simply draws upon another Allocator.\n * this allows a single allocator to serve multiple STL containers.\n */\ntemplate<typename T, class Allocator>\nclass ProxyAllocator\n{\npublic:\n\ttypedef T value_type;\n\ttypedef T* pointer;\n\ttypedef const T* const_pointer;\n\ttypedef T& reference;\n\ttypedef const T& const_reference;\n\ttypedef std::size_t size_type;\n\ttypedef std::ptrdiff_t difference_type;\n\n\ttemplate<class U>\n\tstruct rebind\n\t{\n\t\ttypedef ProxyAllocator<U, Allocator> other;\n\t};\n\n\t// (required to be declared by boost::unordered_map, but should never be called)\n\texplicit NOTHROW_DEFINE ProxyAllocator();\n\n\texplicit NOTHROW_DEFINE ProxyAllocator(Allocator& allocator)\n\t\t: allocator(&allocator)\n\t{\n\t}\n\n\ttemplate<typename U, class A>\n\tNOTHROW_DEFINE ProxyAllocator(const ProxyAllocator<U,A>& rhs)\n\t\t: allocator(rhs.allocator)\n\t{\n\t}\n\t\n\t// (required by VC2010 std::vector)\n\tbool operator==(const ProxyAllocator& rhs) const\n\t{\n\t\treturn allocator == rhs.allocator;\n\t}\n\tbool operator!=(const ProxyAllocator& rhs) const\n\t{\n\t\treturn !operator==(rhs);\n\t}\n\n\tpointer address(reference r)\n\t{\n\t\treturn &r;\n\t}\n\n\tconst_pointer address(const_reference s)\n\t{\n\t\treturn &s;\n\t}\n\n\tsize_type max_size() const throw ()\n\t{\n\t\treturn std::numeric_limits<std::size_t>::max() / sizeof(T);\n\t}\n\n\tvoid construct(const pointer ptr, const value_type& t)\n\t{\n\t\tnew(ptr) T(t);\n\t}\n\n\tvoid destroy(pointer ptr)\n\t{\n\t\tptr->~T();\n\t\tUNUSED2(ptr); // silence MSVC warnings\n\t}\n\n\tpointer allocate(size_type n)\n\t{\n\t\t// safely handle zero-sized allocations (happens with GCC STL - see ticket #909).\n\t\tif(n == 0)\n\t\t\tn = 1;\n\t\treturn (pointer)allocator->allocate(n*sizeof(T));\n\t}\n\n\tpointer allocate(size_type n, const void* const)\n\t{\n\t\treturn allocate(n);\n\t}\n\n\tvoid deallocate(const pointer ptr, const size_type n)\n\t{\n\t\treturn allocator->deallocate(ptr, n*sizeof(T));\n\t}\n\n//private:\t// otherwise copy ctor cannot access it\n\tAllocator* allocator;\n};\n\n#endif\t// #ifndef ALLOCATOR_ADAPTERS\n"
  },
  {
    "path": "fpsgame/gui/allocators/allocator_checker.h",
    "content": "/* Copyright (c) 2011 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#ifndef INCLUDED_ALLOCATORS_ALLOCATOR_CHECKER\n#define INCLUDED_ALLOCATORS_ALLOCATOR_CHECKER\n\n#include <map>\n\n/**\n * allocator test rig.\n * call from each allocator operation to sanity-check them.\n * should only be used during debug mode due to serious overhead.\n **/\nclass AllocatorChecker\n{\npublic:\n\tvoid OnAllocate(void* p, size_t size)\n\t{\n\t\tconst Allocs::value_type item = std::make_pair(p, size);\n\t\tstd::pair<Allocs::iterator, bool> ret = allocs.insert(item);\n\t\tENSURE(ret.second == true);\t// wasn't already in map\n\t}\n\n\tvoid OnDeallocate(void* p, size_t size)\n\t{\n\t\tAllocs::iterator it = allocs.find(p);\n\t\tif(it == allocs.end())\n\t\t\tDEBUG_WARN_ERR(ERR::LOGIC);\t// freeing invalid pointer\n\t\telse\n\t\t{\n\t\t\t// size must match what was passed to OnAllocate\n\t\t\tconst size_t allocated_size = it->second;\n\t\t\tENSURE(size == allocated_size);\n\n\t\t\tallocs.erase(it);\n\t\t}\n\t}\n\n\t/**\n\t * allocator is resetting itself, i.e. wiping out all allocs.\n\t **/\n\tvoid OnClear()\n\t{\n\t\tallocs.clear();\n\t}\n\nprivate:\n\ttypedef std::map<void*, size_t> Allocs;\n\tAllocs allocs;\n};\n\n#endif\t// #ifndef INCLUDED_ALLOCATORS_ALLOCATOR_CHECKER\n"
  },
  {
    "path": "fpsgame/gui/allocators/allocator_policies.h",
    "content": "/* Copyright (c) 2011 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * policy class templates for allocators.\n */\n\n#ifndef ALLOCATOR_POLICIES\n#define ALLOCATOR_POLICIES\n\n#include \"lib/alignment.h\"\t// pageSize\n#include \"lib/allocators/allocator_adapters.h\"\n#include \"lib/allocators/freelist.h\"\n\n\nnamespace Allocators {\n\n\n//-----------------------------------------------------------------------------\n// Growth\n\n// O(N) allocations, O(1) wasted space.\ntemplate<size_t increment = pageSize>\nstruct Growth_Linear\n{\n\tsize_t operator()(size_t oldSize) const\n\t{\n\t\treturn oldSize + increment;\n\t}\n};\n\n// O(log r) allocations, O(N) wasted space. NB: the common choice of\n// expansion factor r = 2 (e.g. in the GCC STL) prevents\n// Storage_Reallocate from reusing previous memory blocks,\n// thus constantly growing the heap and decreasing locality.\n// Alexandrescu [C++ and Beyond 2010] recommends r < 33/25.\n// we approximate this with a power of two divisor to allow shifting.\n// C++ does allow reference-to-float template parameters, but\n// integer arithmetic is expected to be faster.\n// (Storage_Commit should use 2:1 because it is cheaper to\n// compute and retains power-of-two sizes.)\ntemplate<size_t multiplier = 21, size_t divisor = 16>\nstruct Growth_Exponential\n{\n\tsize_t operator()(size_t oldSize) const\n\t{\n\t\tconst size_t product = oldSize * multiplier;\n\n\t\t// detect overflow, but allow equality in case oldSize = 0,\n\t\t// which isn't a problem because Storage_Commit::Expand\n\t\t// raises it to requiredCapacity.\n\t\tASSERT(product >= oldSize);\n\n\t\treturn product / divisor;\n\t}\n};\n\n\n//-----------------------------------------------------------------------------\n// Storage\n\n// a contiguous region of memory (not just an \"array\", because\n// allocators such as Arena append variable-sized intervals).\n//\n// we don't store smart pointers because storage usually doesn't need\n// to be copied, and ICC 11 sometimes wasn't able to inline Address().\nstruct Storage\n{\n\t// @return starting address (alignment depends on the allocator).\n\tuintptr_t Address() const;\n\n\t// @return size [bytes] of currently accessible memory.\n\tsize_t Capacity() const;\n\n\t// @return largest possible capacity [bytes].\n\tsize_t MaxCapacity() const;\n\n\t// expand Capacity() to at least requiredCapacity (possibly more\n\t//   depending on GrowthPolicy).\n\t// @param requiredCapacity > Capacity()\n\t// @return false and leave Capacity() unchanged if expansion failed,\n\t//   which is guaranteed to happen if requiredCapacity > MaxCapacity().\n\tbool Expand(size_t requiredCapacity);\n};\n\n\n// allocate once and refuse subsequent expansion.\ntemplate<class Allocator = Allocator_Aligned<> >\nclass Storage_Fixed\n{\n\tNONCOPYABLE(Storage_Fixed);\npublic:\n\tStorage_Fixed(size_t size)\n\t\t: maxCapacity(size)\n\t\t, storage(allocator.allocate(maxCapacity))\n\t{\n\t}\n\n\t~Storage_Fixed()\n\t{\n\t\tallocator.deallocate(storage, maxCapacity);\n\t}\n\n\tuintptr_t Address() const\n\t{\n\t\treturn uintptr_t(storage);\n\t}\n\n\tsize_t Capacity() const\n\t{\n\t\treturn maxCapacity;\n\t}\n\n\tsize_t MaxCapacity() const\n\t{\n\t\treturn maxCapacity;\n\t}\n\n\tbool Expand(size_t UNUSED(requiredCapacity))\n\t{\n\t\treturn false;\n\t}\n\nprivate:\n\tAllocator allocator;\n\tsize_t maxCapacity;\t// must be initialized before storage\n\tvoid* storage;\n};\n\n\n// unlimited expansion by allocating larger storage and copying.\n// (basically equivalent to std::vector, although Growth_Exponential\n// is much more cache and allocator-friendly than the GCC STL)\ntemplate<class Allocator = Allocator_Heap, class GrowthPolicy = Growth_Exponential<> >\nclass Storage_Reallocate\n{\n\tNONCOPYABLE(Storage_Reallocate);\npublic:\n\tStorage_Reallocate(size_t initialCapacity)\n\t\t: capacity(initialCapacity)\n\t\t, storage(allocator.allocate(initialCapacity))\n\t{\n\t}\n\n\t~Storage_Reallocate()\n\t{\n\t\tallocator.deallocate(storage, capacity);\n\t}\n\n\tuintptr_t Address() const\n\t{\n\t\treturn uintptr_t(storage);\n\t}\n\n\tsize_t Capacity() const\n\t{\n\t\treturn capacity;\n\t}\n\n\tsize_t MaxCapacity() const\n\t{\n\t\treturn std::numeric_limits<size_t>::max();\n\t}\n\n\tbool Expand(size_t requiredCapacity)\n\t{\n\t\tsize_t newCapacity = std::max(requiredCapacity, GrowthPolicy()(capacity));\n\t\tvoid* newStorage = allocator.allocate(newCapacity);\n\t\tif(!newStorage)\n\t\t\treturn false;\n\t\tmemcpy(newStorage, storage, capacity);\n\t\tstd::swap(capacity, newCapacity);\n\t\tstd::swap(storage, newStorage);\n\t\tallocator.deallocate(newStorage, newCapacity);\t// free PREVIOUS storage\n\t\treturn true;\n\t}\n\nprivate:\n\tAllocator allocator;\n\tsize_t capacity;\t// must be initialized before storage\n\tvoid* storage;\n};\n\n\n// expand up to the limit of the allocated address space by\n// committing physical memory. this avoids copying and\n// reduces wasted physical memory.\ntemplate<class Allocator = Allocator_AddressSpace<>, class GrowthPolicy = Growth_Exponential<2,1> >\nclass Storage_Commit\n{\n\tNONCOPYABLE(Storage_Commit);\npublic:\n\tStorage_Commit(size_t maxCapacity_)\n\t\t: maxCapacity(Align<pageSize>(maxCapacity_))\t// see Expand\n\t\t, storage(allocator.allocate(maxCapacity))\n\t\t, capacity(0)\n\t{\n\t}\n\n\t~Storage_Commit()\n\t{\n\t\tallocator.deallocate(storage, maxCapacity);\n\t}\n\n\tuintptr_t Address() const\n\t{\n\t\treturn uintptr_t(storage);\n\t}\n\n\tsize_t Capacity() const\n\t{\n\t\treturn capacity;\n\t}\n\n\tsize_t MaxCapacity() const\n\t{\n\t\treturn maxCapacity;\n\t}\n\n\tbool Expand(size_t requiredCapacity)\n\t{\n\t\tsize_t newCapacity = std::max(requiredCapacity, GrowthPolicy()(capacity));\n\t\t// reduce the number of expensive commits by accurately\n\t\t// reflecting the actual capacity. this is safe because\n\t\t// we also round up maxCapacity.\n\t\tnewCapacity = Align<pageSize>(newCapacity);\n\t\tif(newCapacity > maxCapacity)\n\t\t\treturn false;\n\t\tif(!vm::Commit(Address()+capacity, newCapacity-capacity))\n\t\t\treturn false;\n\t\tcapacity = newCapacity;\n\t\treturn true;\n\t}\n\nprivate:\n\tAllocator allocator;\n\tsize_t maxCapacity;\t// must be initialized before storage\n\tvoid* storage;\n\tsize_t capacity;\n};\n\n\n// implicitly expand up to the limit of the allocated address space by\n// committing physical memory when a page is first accessed.\n// this is basically equivalent to Storage_Commit with Growth_Linear,\n// except that there is no need to call Expand.\ntemplate<class Allocator = Allocator_AddressSpace<> >\nclass Storage_AutoCommit\n{\n\tNONCOPYABLE(Storage_AutoCommit);\npublic:\n\tStorage_AutoCommit(size_t maxCapacity_)\n\t\t: maxCapacity(Align<pageSize>(maxCapacity_))\t// match user's expectation\n\t\t, storage(allocator.allocate(maxCapacity))\n\t{\n\t\tvm::BeginOnDemandCommits();\n\t}\n\n\t~Storage_AutoCommit()\n\t{\n\t\tvm::EndOnDemandCommits();\n\t\tallocator.deallocate(storage, maxCapacity);\n\t}\n\n\tuintptr_t Address() const\n\t{\n\t\treturn uintptr_t(storage);\n\t}\n\n\tsize_t Capacity() const\n\t{\n\t\treturn maxCapacity;\n\t}\n\n\tsize_t MaxCapacity() const\n\t{\n\t\treturn maxCapacity;\n\t}\n\n\tbool Expand(size_t UNUSED(requiredCapacity))\n\t{\n\t\treturn false;\n\t}\n\nprivate:\n\tAllocator allocator;\n\tsize_t maxCapacity;\t// must be initialized before storage\n\tvoid* storage;\n};\n\n\n// reserve and return a pointer to space at the end of storage,\n// expanding it if need be.\n// @param end total number of previously reserved bytes; will be\n//   increased by size if the allocation succeeds.\n// @param size [bytes] to reserve.\n// @return address of allocated space, or 0 if storage is full\n//   and cannot expand any further.\ntemplate<class Storage>\nstatic inline uintptr_t StorageAppend(Storage& storage, size_t& end, size_t size)\n{\n\tsize_t newEnd = end + size;\n\tif(newEnd > storage.Capacity())\n\t{\n\t\tif(!storage.Expand(newEnd))\t// NB: may change storage.Address()\n\t\t\treturn 0;\n\t}\n\n\tstd::swap(end, newEnd);\n\treturn storage.Address() + newEnd;\n}\n\n\n// invoke operator() on default-constructed instantiations of\n// Functor for reasonable combinations of Storage and their parameters.\ntemplate<template<class Storage> class Functor>\nstatic void ForEachStorage()\n{\n\tFunctor<Storage_Fixed<Allocator_Heap> >()();\n\tFunctor<Storage_Fixed<Allocator_Aligned<> > >()();\n\n\tFunctor<Storage_Reallocate<Allocator_Heap, Growth_Linear<> > >()();\n\tFunctor<Storage_Reallocate<Allocator_Heap, Growth_Exponential<> > >()();\n\tFunctor<Storage_Reallocate<Allocator_Aligned<>, Growth_Linear<> > >()();\n\tFunctor<Storage_Reallocate<Allocator_Aligned<>, Growth_Exponential<> > >()();\n\n\tFunctor<Storage_Commit<Allocator_AddressSpace<>, Growth_Linear<> > >()();\n\tFunctor<Storage_Commit<Allocator_AddressSpace<>, Growth_Exponential<> > >()();\n\n\tFunctor<Storage_AutoCommit<> >()();\n}\n\n}\t// namespace Allocators\n\n#endif\t// #ifndef ALLOCATOR_POLICIES\n"
  },
  {
    "path": "fpsgame/gui/allocators/arena.cpp",
    "content": "/* Copyright (c) 2011 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * arena allocator (variable-size blocks, no deallocation).\n */\n\n#include \"precompiled.h\"\n#include \"lib/allocators/arena.h\"\n\nnamespace Allocators {\n\ntemplate<class Storage>\nstruct BasicArenaTest\n{\n\tvoid operator()() const\n\t{\n\t\tArena<Storage> a(100);\n\t\tconst size_t initialSpace = a.RemainingBytes();\n\t\tvoid* p = a.allocate(100);\n\t\tENSURE(p != 0);\n\t\tENSURE(a.Contains(uintptr_t(p)));\n\t\tENSURE(a.RemainingBytes() == initialSpace-100);\n\t\tENSURE(a.Contains(uintptr_t(p)+1));\n\t\tENSURE(a.Contains(uintptr_t(p)+99));\n\t\tENSURE(!a.Contains(uintptr_t(p)-1));\n\t\tENSURE(!a.Contains(uintptr_t(p)+100));\n\t\tif(a.RemainingBytes() == 0)\n\t\t\tENSURE(a.allocate(1) == 0);\t// full\n\t\telse\n\t\t\tENSURE(a.allocate(1) != 0);\t// can still expand\n\t\ta.DeallocateAll();\n\t\tENSURE(!a.Contains(uintptr_t(p)));\n\n\t\tp = a.allocate(36);\n\t\tENSURE(p != 0);\n\t\tENSURE(a.Contains(uintptr_t(p)));\n\t\tENSURE(a.RemainingBytes() == initialSpace-36);\n\t\tvoid* p2 = a.allocate(64);\n\t\tENSURE(p2 != 0);\n\t\tENSURE(a.Contains(uintptr_t(p2)));\n\t\tENSURE(a.RemainingBytes() == initialSpace-36-64);\n\t\tENSURE(p2 == (void*)(uintptr_t(p)+36));\n\t\tif(a.RemainingBytes() == 0)\n\t\t\tENSURE(a.allocate(1) == 0);\t// full\n\t\telse\n\t\t\tENSURE(a.allocate(1) != 0);\t// can still expand\n\t}\n};\n\nvoid TestArena()\n{\n\tForEachStorage<BasicArenaTest>();\n}\n\n}\t// namespace Allocators\n"
  },
  {
    "path": "fpsgame/gui/allocators/arena.h",
    "content": "/* Copyright (c) 2013 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * arena allocator (variable-size blocks, no deallocation).\n */\n\n#ifndef INCLUDED_ALLOCATORS_ARENA\n#define INCLUDED_ALLOCATORS_ARENA\n\n#include \"lib/allocators/allocator_policies.h\"\n\nnamespace Allocators {\n\n/**\n * allocator design parameters:\n * - O(1) allocation;\n * - variable-size blocks;\n * - support for deallocating all objects;\n * - consecutive allocations are back-to-back;\n * - no extra alignment nor padding.\n **/\ntemplate<class Storage = Storage_Fixed<> >\nclass Arena\n{\n\tNONCOPYABLE(Arena);\npublic:\n\tArena(size_t maxSize)\n\t\t: storage(maxSize)\n\t{\n\t\tDeallocateAll();\n\t}\n\n\tsize_t RemainingBytes() const\n\t{\n\t\treturn storage.MaxCapacity() - end;\n\t}\n\n\tvoid* allocate(size_t size)\n\t{\n\t\treturn (void*)StorageAppend(storage, end, size);\n\t}\n\n\tvoid deallocate(void* UNUSED(p), size_t UNUSED(size))\n\t{\n\t\t// ignored\n\t}\n\n\tvoid DeallocateAll()\n\t{\n\t\tend = 0;\n\t}\n\n\t// @return whether the address lies within the previously allocated range.\n\tbool Contains(uintptr_t address) const\n\t{\n\t\treturn (address - storage.Address()) < end;\n\t}\n\nprivate:\n\tStorage storage;\n\tsize_t end;\n};\n\nLIB_API void TestArena();\n\n\n/**\n * allocator design parameters:\n * - grow dynamically with a fixed chunkSize\n * - for frequent allocations of size << chunkSize\n * - no reallocations, pointers remain valid\n **/\nclass DynamicArena\n{\n\tstruct ArenaChunk\n\t{\n\t\tbool Available(size_t size) const\n\t\t{\n\t\t\treturn size <= (capacity - end);\n\t\t}\n\n\t\t// Must check Available first or this may return an invalid address\n\t\tuintptr_t Allocate(size_t size)\n\t\t{\n\t\t\tuintptr_t ptr = storage + end;\n\t\t\tend += size;\n\t\t\treturn ptr;\n\t\t}\n\n\t\tuintptr_t storage;\n\t\tsize_t end;\n\t\tsize_t capacity;\n\t\tArenaChunk* next;\n\t};\n\n\tNONCOPYABLE(DynamicArena);\npublic:\n\tDynamicArena(size_t chunkSize) : chunkSize(chunkSize), head(NULL)\n\t{\n\t\tAllocateNewChunk();\n\t}\n\n\t~DynamicArena()\n\t{\n\t\tArenaChunk* chunk = head;\n\t\twhile (chunk != NULL)\n\t\t{\n\t\t\tArenaChunk* next = chunk->next;\n\t\t\tfree(chunk);\n\t\t\tchunk = next;\n\t\t}\n\t}\n\n\tvoid AllocateNewChunk()\n\t{\n\t\t// For efficiency, do a single allocation with the ArenaChunk and its storage\n\t\tArenaChunk* next = head;\n\t\thead = (ArenaChunk*)malloc(sizeof(ArenaChunk) + chunkSize);\n\t\tENSURE(head);\n\t\thead->storage = sizeof(ArenaChunk) + uintptr_t(head);\n\t\thead->end = 0;\n\t\thead->capacity = chunkSize;\n\t\thead->next = next;\n\t}\n\n\tvoid* allocate(size_t size)\n\t{\n\t\tif (size > chunkSize)\n\t\t{\n\t\t\tdebug_warn(L\"DynamicArena cannot allocate more than chunk size\");\n\t\t\tthrow std::bad_alloc();\n\t\t}\n\t\telse if (!head->Available(size))\n\t\t\tAllocateNewChunk();\n\n\t\treturn (void*)head->Allocate(size);\n\t}\n\n\tvoid deallocate(void* UNUSED(p), size_t UNUSED(size))\n\t{\n\t\t// ignored\n\t}\n\nprivate:\n\n\tconst size_t chunkSize;\n\tArenaChunk *head;\n};\n\n\n}\t// namespace Allocators\n\n#endif\t// #ifndef INCLUDED_ALLOCATORS_ARENA\n"
  },
  {
    "path": "fpsgame/gui/allocators/dynarray.cpp",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * dynamic (expandable) array\n */\n\n#include \"precompiled.h\"\n#include \"lib/allocators/dynarray.h\"\n\n#include \"lib/alignment.h\"\n#include \"lib/sysdep/vm.h\"\n\n\nstatic Status validate_da(DynArray* da)\n{\n\tif(!da)\n\t\tWARN_RETURN(ERR::INVALID_POINTER);\n//\tu8* const base           = da->base;\n\tconst size_t max_size_pa = da->max_size_pa;\n\tconst size_t cur_size    = da->cur_size;\n\tconst size_t pos         = da->pos;\n\n\t// note: this happens if max_size == 0\n//\tif(debug_IsPointerBogus(base))\n//\t\tWARN_RETURN(ERR::_1);\n\t// note: don't check if base is page-aligned -\n\t// might not be true for 'wrapped' mem regions.\n\tif(!IsAligned(max_size_pa, pageSize))\n\t\tWARN_RETURN(ERR::_3);\n\tif(cur_size > max_size_pa)\n\t\tWARN_RETURN(ERR::_4);\n\tif(pos > cur_size || pos > max_size_pa)\n\t\tWARN_RETURN(ERR::_5);\n\n\treturn INFO::OK;\n}\n\n#define CHECK_DA(da) RETURN_STATUS_IF_ERR(validate_da(da))\n\n\nStatus da_alloc(DynArray* da, size_t max_size)\n{\n\tENSURE(max_size != 0);\n\tconst size_t max_size_pa = Align<pageSize>(max_size);\n\n\tu8* p = (u8*)vm::ReserveAddressSpace(max_size_pa);\n\tif(!p)\n\t\treturn ERR::NO_MEM;\t// NOWARN (already done in vm)\n\n\tda->base        = p;\n\tda->max_size_pa = max_size_pa;\n\tda->cur_size    = 0;\n\tda->cur_size_pa = 0;\n\tda->pos         = 0;\n\tCHECK_DA(da);\n\treturn INFO::OK;\n}\n\n\nStatus da_free(DynArray* da)\n{\n\tCHECK_DA(da);\n\n\tvm::ReleaseAddressSpace(da->base, da->max_size_pa);\n\n\t// wipe out the DynArray for safety\n\tmemset(da, 0, sizeof(*da));\n\n\treturn INFO::OK;\n}\n\n\nStatus da_set_size(DynArray* da, size_t new_size)\n{\n\tCHECK_DA(da);\n\n\t// determine how much to add/remove\n\tconst size_t cur_size_pa = Align<pageSize>(da->cur_size);\n\tconst size_t new_size_pa = Align<pageSize>(new_size);\n\tconst ssize_t size_delta_pa = (ssize_t)new_size_pa - (ssize_t)cur_size_pa;\n\n\t// not enough memory to satisfy this expand request: abort.\n\t// note: do not complain - some allocators (e.g. file_cache)\n\t// legitimately use up all available space.\n\tif(new_size_pa > da->max_size_pa)\n\t\treturn ERR::LIMIT;\t// NOWARN\n\n\tu8* end = da->base + cur_size_pa;\n\tbool ok = true;\n\t// expanding\n\tif(size_delta_pa > 0)\n\t{\n\t\tok = vm::Commit(uintptr_t(end), size_delta_pa);\n\t\tif(!ok)\n\t\t\tdebug_printf(\"Commit failed (%p %lld)\\n\", (void *)end, (long long)size_delta_pa);\n\t}\n\t// shrinking\n\telse if(size_delta_pa < 0)\n\t\tok = vm::Decommit(uintptr_t(end+size_delta_pa), -size_delta_pa);\n\t// else: no change in page count, e.g. if going from size=1 to 2\n\t// (we don't want mem_* to have to handle size=0)\n\n\tda->cur_size = new_size;\n\tda->cur_size_pa = new_size_pa;\n\tCHECK_DA(da);\n\treturn ok? INFO::OK : ERR::FAIL;\n}\n\n\nStatus da_reserve(DynArray* da, size_t size)\n{\n\tif(da->pos+size > da->cur_size_pa)\n\t\tRETURN_STATUS_IF_ERR(da_set_size(da, da->cur_size_pa+size));\n\tda->cur_size = std::max(da->cur_size, da->pos+size);\n\treturn INFO::OK;\n}\n\n\nStatus da_append(DynArray* da, const void* data, size_t size)\n{\n\tRETURN_STATUS_IF_ERR(da_reserve(da, size));\n\tmemcpy(da->base+da->pos, data, size);\n\tda->pos += size;\n\treturn INFO::OK;\n}\n"
  },
  {
    "path": "fpsgame/gui/allocators/dynarray.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * dynamic (expandable) array\n */\n\n#ifndef INCLUDED_ALLOCATORS_DYNARRAY\n#define INCLUDED_ALLOCATORS_DYNARRAY\n\n#include \"lib/posix/posix_mman.h\"\t// PROT_*\n\n/**\n * provides a memory range that can be expanded but doesn't waste\n * physical memory or relocate itself.\n *\n * works by preallocating address space and committing as needed.\n * used as a building block for other allocators.\n **/\nstruct DynArray\n{\n\tu8* base;\n\tsize_t max_size_pa;\t /// reserved\n\tsize_t cur_size;\t /// committed\n\tsize_t cur_size_pa;\n\n\tsize_t pos;\n};\n\n\n/**\n * ready the DynArray object for use.\n *\n * no virtual memory is actually committed until calls to da_set_size.\n *\n * @param da DynArray.\n * @param max_size size [bytes] of address space to reserve (*);\n * the DynArray can never expand beyond this.\n * (* rounded up to next page size multiple)\n * @return Status.\n **/\nLIB_API Status da_alloc(DynArray* da, size_t max_size);\n\n/**\n * free all memory (address space + physical) that constitutes the\n * given array.\n *\n * use-after-free is impossible because the memory is unmapped.\n *\n * @param da DynArray* zeroed afterwards.\n * @return Status\n **/\nLIB_API Status da_free(DynArray* da);\n\n/**\n * expand or shrink the array: changes the amount of currently committed\n * (i.e. usable) memory pages.\n *\n * @param da DynArray.\n * @param new_size target size (rounded up to next page multiple).\n * pages are added/removed until this is met.\n * @return Status.\n **/\nLIB_API Status da_set_size(DynArray* da, size_t new_size);\n\n/**\n * Make sure at least \\<size\\> bytes starting at da->pos are committed and\n * ready for use.\n *\n * @param da DynArray*\n * @param size Minimum amount to guarantee [bytes]\n * @return Status\n **/\nLIB_API Status da_reserve(DynArray* da, size_t size);\n\n/**\n * \"write\" to array, i.e. copy from the given buffer.\n *\n * starts at offset DynArray.pos and advances this.\n *\n * @param da DynArray.\n * @param data_src source memory\n * @param size [bytes] to copy\n * @return Status.\n **/\nLIB_API Status da_append(DynArray* da, const void* data_src, size_t size);\n\n#endif\t// #ifndef INCLUDED_ALLOCATORS_DYNARRAY\n"
  },
  {
    "path": "fpsgame/gui/allocators/freelist.cpp",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#include \"precompiled.h\"\n#include \"lib/allocators/freelist.h\"\n\nvoid* mem_freelist_Sentinel()\n{\n\t// sentinel storing its own address\n\tstatic void* storageForPrevPtr = &storageForPrevPtr;\n\treturn &storageForPrevPtr;\n}\n"
  },
  {
    "path": "fpsgame/gui/allocators/freelist.h",
    "content": "/* Copyright (c) 2011 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#ifndef INCLUDED_ALLOCATORS_FREELIST\n#define INCLUDED_ALLOCATORS_FREELIST\n\n// \"freelist\" is a pointer to the first unused element or a sentinel.\n// their memory holds a pointer to the previous element in the freelist\n// (or its own address in the case of sentinels to avoid branches)\n//\n// rationale for the function-based interface: a class encapsulating the\n// freelist pointer would force each header to include this header,\n// whereas this approach only requires a void* pointer and calling\n// mem_freelist_Sentinel from the implementation.\n//\n// these functions are inlined because allocation is sometimes time-critical.\n\n// @return the address of a sentinel element, suitable for initializing\n// a freelist pointer. subsequent mem_freelist_Detach on that freelist\n// will return 0.\nLIB_API void* mem_freelist_Sentinel();\n\nstatic inline void mem_freelist_AddToFront(void*& freelist, void* el)\n{\n\tASSERT(freelist != 0);\n\tASSERT(el != 0);\n\n\tmemcpy(el, &freelist, sizeof(void*));\n\tfreelist = el;\n}\n\n// @return 0 if the freelist is empty, else a pointer that had\n// previously been passed to mem_freelist_AddToFront.\nstatic inline void* mem_freelist_Detach(void*& freelist)\n{\n\tASSERT(freelist != 0);\n\n\tvoid* prev_el;\n\tmemcpy(&prev_el, freelist, sizeof(void*));\n\tvoid* el = (freelist == prev_el)? 0 : freelist;\n\tfreelist = prev_el;\n\treturn el;\n}\n\n#endif\t// #ifndef INCLUDED_ALLOCATORS_FREELIST\n"
  },
  {
    "path": "fpsgame/gui/allocators/headerless.cpp",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * (header-less) pool-based heap allocator\n */\n\n#include \"precompiled.h\"\n#include \"lib/allocators/headerless.h\"\n\n#include \"lib/bits.h\"\n#include \"lib/allocators/pool.h\"\n\n\nstatic const bool performSanityChecks = true;\n\n// shared by Impl::Allocate and FreedBlock::Validate\nstatic bool IsValidSize(size_t size);\n\n\n//-----------------------------------------------------------------------------\n\n// this combines the boundary tags and link fields into one structure,\n// which is safer than direct pointer arithmetic.\n//\n// it is written to freed memory, which is fine because IsValidSize ensures\n// the allocations are large enough.\nclass FreedBlock\n{\n\tfriend class RangeList;\t// manipulates link fields directly\n\npublic:\n\t// (required for RangeList::m_sentinel)\n\tFreedBlock()\n\t{\n\t}\n\n\tvoid Setup(uintptr_t id, size_t size)\n\t{\n\t\tm_magic = s_magic;\n\t\tm_size = size;\n\t\tm_id = id;\n\t}\n\n\tvoid Reset()\n\t{\n\t\t// clear all fields to prevent accidental reuse\n\t\tprev = next = 0;\n\t\tm_id = 0;\n\t\tm_size = ~size_t(0);\n\t\tm_magic = 0;\n\t}\n\n\tsize_t Size() const\n\t{\n\t\treturn m_size;\n\t}\n\n\t/**\n\t * @return whether this appears to be a FreedBlock instance with the\n\t * desired ID. for additional safety, also call Validate().\n\t **/\n\tbool IsFreedBlock(uintptr_t id) const\n\t{\n\t\tif(m_id != id)\n\t\t\treturn false;\n\t\tif(m_magic != s_magic)\n\t\t\treturn false;\n\t\treturn true;\n\t}\n\n\t/**\n\t * warn if any invariant doesn't hold.\n\t **/\n\tvoid Validate(uintptr_t id) const\n\t{\n\t\tif(!performSanityChecks) return;\n\n\t\t// note: RangeList::Validate implicitly checks the prev and next\n\t\t// fields by iterating over the list.\n\n\t\t// note: we can't check for prev != next because we're called for\n\t\t// footers as well, and they don't have valid pointers.\n\n\t\tENSURE(IsValidSize(m_size));\n\t\tENSURE(IsFreedBlock(id));\n\t}\n\nprivate:\n\t// note: the magic and ID fields are stored at both ends of this\n\t// class to increase the chance of detecting memory corruption.\n\tstatic const u64 s_magic = 0xFF55AA00BBCCDDEEull;\n\tu64 m_magic;\n\n\tFreedBlock* prev;\n\tFreedBlock* next;\n\n\t// size [bytes] of the entire memory block, including header and footer\n\tsize_t m_size;\n\n\t// this differentiates between headers and footers.\n\tuintptr_t m_id;\n};\n\n\nstatic bool IsValidSize(size_t size)\n{\n\t// note: we disallow the questionable practice of zero-byte allocations\n\t// because they may be indicative of bugs.\n\tif(size < HeaderlessAllocator::minAllocationSize)\n\t\treturn false;\n\n\tif(size % HeaderlessAllocator::allocationAlignment)\n\t\treturn false;\n\n\treturn true;\n}\n\n\n//-----------------------------------------------------------------------------\n// freelists\n//-----------------------------------------------------------------------------\n\n// policy: address-ordered good fit\n// mechanism: segregated range lists of power-of-two size classes\n\nstruct AddressOrder\n{\n\tstatic bool ShouldInsertBefore(FreedBlock* current, FreedBlock* successor)\n\t{\n\t\treturn current < successor;\n\t}\n};\n\n// \"range list\" is a freelist of similarly-sized blocks.\nclass RangeList\n{\npublic:\n\tRangeList()\n\t{\n\t\tReset();\n\t}\n\n\tvoid Reset()\n\t{\n\t\tm_sentinel.prev = &m_sentinel;\n\t\tm_sentinel.next = &m_sentinel;\n\t\tm_freeBlocks = 0;\n\t\tm_freeBytes = 0;\n\t}\n\n\ttemplate<class InsertPolicy>\n\tvoid Insert(FreedBlock* freedBlock)\n\t{\n\t\t// find freedBlock before which to insert\n\t\tFreedBlock* successor;\n\t\tfor(successor = m_sentinel.next; successor != &m_sentinel; successor = successor->next)\n\t\t{\n\t\t\tif(InsertPolicy::ShouldInsertBefore(freedBlock, successor))\n\t\t\t\tbreak;\n\t\t}\n\n\t\tfreedBlock->prev = successor->prev;\n\t\tfreedBlock->next = successor;\n\t\tsuccessor->prev->next = freedBlock;\n\t\tsuccessor->prev = freedBlock;\n\n\t\tm_freeBlocks++;\n\t\tm_freeBytes += freedBlock->Size();\n\t}\n\n\t/**\n\t * @return the first freed block of size >= minSize or 0 if none exists.\n\t **/\n\tFreedBlock* Find(size_t minSize)\n\t{\n\t\tfor(FreedBlock* freedBlock = m_sentinel.next; freedBlock != &m_sentinel; freedBlock = freedBlock->next)\n\t\t{\n\t\t\tif(freedBlock->Size() >= minSize)\n\t\t\t\treturn freedBlock;\n\t\t}\n\n\t\t// none found, so average block size is less than the desired size\n\t\tENSURE(m_freeBytes/m_freeBlocks < minSize);\n\t\treturn 0;\n\t}\n\n\tvoid Remove(FreedBlock* freedBlock)\n\t{\n\t\tfreedBlock->next->prev = freedBlock->prev;\n\t\tfreedBlock->prev->next = freedBlock->next;\n\n\t\tENSURE(m_freeBlocks != 0);\n\t\tENSURE(m_freeBytes >= freedBlock->Size());\n\t\tm_freeBlocks--;\n\t\tm_freeBytes -= freedBlock->Size();\n\t}\n\n\tvoid Validate(uintptr_t id) const\n\t{\n\t\tif(!performSanityChecks) return;\n\n\t\tsize_t freeBlocks = 0, freeBytes = 0;\n\n\t\tfor(FreedBlock* freedBlock = m_sentinel.next; freedBlock != &m_sentinel; freedBlock = freedBlock->next)\n\t\t{\n\t\t\tfreedBlock->Validate(id);\n\t\t\tfreeBlocks++;\n\t\t\tfreeBytes += freedBlock->Size();\n\t\t}\n\n\t\tfor(FreedBlock* freedBlock = m_sentinel.prev; freedBlock != &m_sentinel; freedBlock = freedBlock->prev)\n\t\t{\n\t\t\tfreedBlock->Validate(id);\n\t\t\tfreeBlocks++;\n\t\t\tfreeBytes += freedBlock->Size();\n\t\t}\n\n\t\t// our idea of the number and size of free blocks is correct\n\t\tENSURE(freeBlocks == m_freeBlocks*2 && freeBytes == m_freeBytes*2);\n\t\t// if empty, state must be as established by Reset\n\t\tENSURE(!IsEmpty() || (m_sentinel.next == &m_sentinel && m_sentinel.prev == &m_sentinel));\n\t}\n\n\tbool IsEmpty() const\n\t{\n\t\treturn (m_freeBlocks == 0);\n\t}\n\n\tsize_t FreeBlocks() const\n\t{\n\t\treturn m_freeBlocks;\n\t}\n\n\tsize_t FreeBytes() const\n\t{\n\t\treturn m_freeBytes;\n\t}\n\nprivate:\n\t// a sentinel simplifies Insert and Remove. we store it here instead of\n\t// in a separate array to improve locality (it is actually accessed).\n\tmutable FreedBlock m_sentinel;\n\n\tsize_t m_freeBlocks;\n\tsize_t m_freeBytes;\n};\n\n\n//-----------------------------------------------------------------------------\n\nclass SegregatedRangeLists\n{\npublic:\n\tSegregatedRangeLists()\n\t{\n\t\tReset();\n\t}\n\n\tvoid Reset()\n\t{\n\t\tm_bitmap = 0;\n\t\tfor(size_t i = 0; i < numRangeLists; i++)\n\t\t\tm_rangeLists[i].Reset();\n\t}\n\n\tvoid Insert(FreedBlock* freedBlock)\n\t{\n\t\tconst size_t sizeClass = SizeClass(freedBlock->Size());\n\t\tm_rangeLists[sizeClass].Insert<AddressOrder>(freedBlock);\n\n\t\tm_bitmap |= Bit<uintptr_t>(sizeClass);\n\t}\n\n\t/**\n\t * @return the first freed block of size >= minSize or 0 if none exists.\n\t **/\n\tFreedBlock* Find(size_t minSize)\n\t{\n\t\t// iterate over all large enough, non-empty size classes\n\t\t// (zero overhead for empty size classes)\n\t\tconst size_t minSizeClass = SizeClass(minSize);\n\t\tsize_t sizeClassBits = m_bitmap & ((~size_t(0)) << minSizeClass);\n\t\twhile(sizeClassBits)\n\t\t{\n\t\t\tconst size_t size = ValueOfLeastSignificantOneBit(sizeClassBits);\n\t\t\tsizeClassBits &= ~size;\t// remove from sizeClassBits\n\t\t\tconst size_t sizeClass = SizeClass(size);\n\n\t\t\tFreedBlock* freedBlock = m_rangeLists[sizeClass].Find(minSize);\n\t\t\tif(freedBlock)\n\t\t\t\treturn freedBlock;\n\t\t}\n\n\t\t// apparently all classes above minSizeClass are empty,\n\t\t// or the above would have succeeded.\n\t\tENSURE(m_bitmap < Bit<uintptr_t>(minSizeClass+1));\n\t\treturn 0;\n\t}\n\n\tvoid Remove(FreedBlock* freedBlock)\n\t{\n\t\tconst size_t sizeClass = SizeClass(freedBlock->Size());\n\t\tm_rangeLists[sizeClass].Remove(freedBlock);\n\n\t\t// (masking with !IsEmpty() << sizeClass would probably be faster)\n\t\tif(m_rangeLists[sizeClass].IsEmpty())\n\t\t\tm_bitmap &= ~Bit<uintptr_t>(sizeClass);\n\t}\n\n\tvoid Validate(uintptr_t id) const\n\t{\n\t\tfor(size_t i = 0; i < numRangeLists; i++)\n\t\t{\n\t\t\tm_rangeLists[i].Validate(id);\n\n\t\t\t// both bitmap and list must agree on whether they are empty\n\t\t\tENSURE(((m_bitmap & Bit<uintptr_t>(i)) == 0) == m_rangeLists[i].IsEmpty());\n\t\t}\n\t}\n\n\tsize_t FreeBlocks() const\n\t{\n\t\tsize_t freeBlocks = 0;\n\t\tfor(size_t i = 0; i < numRangeLists; i++)\n\t\t\tfreeBlocks += m_rangeLists[i].FreeBlocks();\n\t\treturn freeBlocks;\n\t}\n\n\tsize_t FreeBytes() const\n\t{\n\t\tsize_t freeBytes = 0;\n\t\tfor(size_t i = 0; i < numRangeLists; i++)\n\t\t\tfreeBytes += m_rangeLists[i].FreeBytes();\n\t\treturn freeBytes;\n\t}\n\nprivate:\n\t/**\n\t * @return \"size class\" of a given size.\n\t * class i > 0 contains blocks of size (2**(i-1), 2**i].\n\t **/\n\tstatic size_t SizeClass(size_t size)\n\t{\n\t\treturn ceil_log2(size);\n\t}\n\n\tstatic uintptr_t ValueOfLeastSignificantOneBit(uintptr_t x)\n\t{\n\t\treturn (x & -(intptr_t)x);\n\t}\n\n\t// segregated, i.e. one list per size class.\n\tstatic const size_t numRangeLists = sizeof(uintptr_t)*CHAR_BIT;\n\tRangeList m_rangeLists[numRangeLists];\n\n\t// bit i set <==> size class i's freelist is not empty.\n\t// this allows finding a non-empty list in O(1).\n\tuintptr_t m_bitmap;\n};\n\n\n//-----------------------------------------------------------------------------\n// coalescing\n//-----------------------------------------------------------------------------\n\n// policy: immediately coalesce\n// mechanism: boundary tags\n\n// note: the id and magic values are all that differentiates tags from\n// user data. this isn't 100% reliable, but as with headers, we don't want\n// to insert extra boundary tags into the allocated memory.\n\n// note: footers consist of Tag{magic, ID, size}, while headers also\n// need prev/next pointers. this could comfortably fit in 64 bytes,\n// but we don't want to inherit headers from a base class because its\n// prev/next pointers should reside between the magic and ID fields.\n// maintaining separate FreedBlock and Footer classes is also undesirable;\n// we prefer to use FreedBlock for both, which increases the minimum\n// allocation size to 64 + allocationAlignment, e.g. 128.\n// that's not a problem because the allocator is designed for\n// returning pages or IO buffers (4..256 KB).\ncassert(HeaderlessAllocator::minAllocationSize >= 2*sizeof(FreedBlock));\n\n\nclass BoundaryTagManager\n{\npublic:\n\tBoundaryTagManager()\n\t\t: m_freeBlocks(0), m_freeBytes(0)\n\t{\n\t}\n\n\tFreedBlock* WriteTags(u8* p, size_t size)\n\t{\n\t\tFreedBlock* freedBlock = (FreedBlock*)p;\n\t\tfreedBlock->Setup(s_headerId, size);\n\t\tFooter(freedBlock)->Setup(s_footerId, size);\n\n\t\tm_freeBlocks++;\n\t\tm_freeBytes += size;\n\n\t\tValidate(freedBlock);\n\t\treturn freedBlock;\n\t}\n\n\tvoid RemoveTags(FreedBlock* freedBlock)\n\t{\n\t\tValidate(freedBlock);\n\n\t\tENSURE(m_freeBlocks != 0);\n\t\tENSURE(m_freeBytes >= freedBlock->Size());\n\t\tm_freeBlocks--;\n\t\tm_freeBytes -= freedBlock->Size();\n\n\t\tFreedBlock* footer = Footer(freedBlock);\n\t\tfreedBlock->Reset();\n\t\tfooter->Reset();\n\t}\n\n\tFreedBlock* PrecedingBlock(u8* p, u8* beginningOfPool) const\n\t{\n\t\tif(p == beginningOfPool)\t// avoid accessing invalid memory\n\t\t\treturn 0;\n\n\t\tFreedBlock* precedingBlock;\n\t\t{\n\t\t\tFreedBlock* const footer = (FreedBlock*)(p - sizeof(FreedBlock));\n\t\t\tif(!footer->IsFreedBlock(s_footerId))\n\t\t\t\treturn 0;\n\t\t\tfooter->Validate(s_footerId);\n\t\t\tprecedingBlock = (FreedBlock*)(p - footer->Size());\n\t\t}\n\n\t\tValidate(precedingBlock);\n\t\treturn precedingBlock;\n\t}\n\n\tFreedBlock* FollowingBlock(u8* p, size_t size, u8* endOfPool) const\n\t{\n\t\tif(p+size == endOfPool)\t// avoid accessing invalid memory\n\t\t\treturn 0;\n\n\t\tFreedBlock* const followingBlock = (FreedBlock*)(p + size);\n\t\tif(!followingBlock->IsFreedBlock(s_headerId))\n\t\t\treturn 0;\n\n\t\tValidate(followingBlock);\n\t\treturn followingBlock;\n\t}\n\n\tsize_t FreeBlocks() const\n\t{\n\t\treturn m_freeBlocks;\n\t}\n\n\tsize_t FreeBytes() const\n\t{\n\t\treturn m_freeBytes;\n\t}\n\n\t// (generated via GUID)\n\tstatic const uintptr_t s_headerId = 0x111E8E6Fu;\n\tstatic const uintptr_t s_footerId = 0x4D745342u;\n\nprivate:\n\tvoid Validate(FreedBlock* freedBlock) const\n\t{\n\t\tif(!performSanityChecks) return;\n\n\t\t// the existence of freedBlock means our bookkeeping better have\n\t\t// records of at least that much memory.\n\t\tENSURE(m_freeBlocks != 0);\n\t\tENSURE(m_freeBytes >= freedBlock->Size());\n\n\t\tfreedBlock->Validate(s_headerId);\n\t\tFooter(freedBlock)->Validate(s_footerId);\n\t}\n\n\tstatic FreedBlock* Footer(FreedBlock* freedBlock)\n\t{\n\t\tu8* const p = (u8*)freedBlock;\n\t\treturn (FreedBlock*)(p + freedBlock->Size() - sizeof(FreedBlock));\n\t}\n\n\tsize_t m_freeBlocks;\n\tsize_t m_freeBytes;\n};\n\n\n//-----------------------------------------------------------------------------\n// stats\n//-----------------------------------------------------------------------------\n\nclass Stats\n{\npublic:\n\tvoid OnReset()\n\t{\n\t\tif(!performSanityChecks) return;\n\n\t\tm_totalAllocatedBlocks = m_totalAllocatedBytes = 0;\n\t\tm_totalDeallocatedBlocks = m_totalDeallocatedBytes = 0;\n\t\tm_currentExtantBlocks = m_currentExtantBytes = 0;\n\t\tm_currentFreeBlocks = m_currentFreeBytes = 0;\n\t}\n\n\tvoid OnAllocate(size_t size)\n\t{\n\t\tif(!performSanityChecks) return;\n\n\t\tm_totalAllocatedBlocks++;\n\t\tm_totalAllocatedBytes += size;\n\n\t\tm_currentExtantBlocks++;\n\t\tm_currentExtantBytes += size;\n\t}\n\n\tvoid OnDeallocate(size_t size)\n\t{\n\t\tif(!performSanityChecks) return;\n\n\t\tm_totalDeallocatedBlocks++;\n\t\tm_totalDeallocatedBytes += size;\n\t\tENSURE(m_totalDeallocatedBlocks <= m_totalAllocatedBlocks);\n\t\tENSURE(m_totalDeallocatedBytes <= m_totalAllocatedBytes);\n\n\t\tENSURE(m_currentExtantBlocks != 0);\n\t\tENSURE(m_currentExtantBytes >= size);\n\t\tm_currentExtantBlocks--;\n\t\tm_currentExtantBytes -= size;\n\t}\n\n\tvoid OnAddToFreelist(size_t size)\n\t{\n\t\tm_currentFreeBlocks++;\n\t\tm_currentFreeBytes += size;\n\t}\n\n\tvoid OnRemoveFromFreelist(size_t size)\n\t{\n\t\tif(!performSanityChecks) return;\n\n\t\tENSURE(m_currentFreeBlocks != 0);\n\t\tENSURE(m_currentFreeBytes >= size);\n\t\tm_currentFreeBlocks--;\n\t\tm_currentFreeBytes -= size;\n\t}\n\n\tvoid Validate() const\n\t{\n\t\tif(!performSanityChecks) return;\n\n\t\tENSURE(m_totalDeallocatedBlocks <= m_totalAllocatedBlocks);\n\t\tENSURE(m_totalDeallocatedBytes <= m_totalAllocatedBytes);\n\n\t\tENSURE(m_currentExtantBlocks == m_totalAllocatedBlocks-m_totalDeallocatedBlocks);\n\t\tENSURE(m_currentExtantBytes == m_totalAllocatedBytes-m_totalDeallocatedBytes);\n\t}\n\n\tsize_t FreeBlocks() const\n\t{\n\t\treturn m_currentFreeBlocks;\n\t}\n\n\tsize_t FreeBytes() const\n\t{\n\t\treturn m_currentFreeBytes;\n\t}\n\nprivate:\n\tu64 m_totalAllocatedBlocks, m_totalAllocatedBytes;\n\tu64 m_totalDeallocatedBlocks, m_totalDeallocatedBytes;\n\tu64 m_currentExtantBlocks, m_currentExtantBytes;\n\tu64 m_currentFreeBlocks, m_currentFreeBytes;\n};\n\n\n//-----------------------------------------------------------------------------\n// HeaderlessAllocator::Impl\n//-----------------------------------------------------------------------------\n\nstatic void AssertEqual(size_t x1, size_t x2, size_t x3)\n{\n\tENSURE(x1 == x2 && x2 == x3);\n}\n\nclass HeaderlessAllocator::Impl\n{\npublic:\n\tImpl(size_t poolSize)\n\t{\n\t\t(void)pool_create(&m_pool, poolSize, 0);\n\n\t\tReset();\n\t}\n\n\t~Impl()\n\t{\n\t\tValidate();\n\n\t\t(void)pool_destroy(&m_pool);\n\t}\n\n\tvoid Reset()\n\t{\n\t\tpool_free_all(&m_pool);\n\t\tm_segregatedRangeLists.Reset();\n\t\tm_stats.OnReset();\n\n\t\tValidate();\n\t}\n\n\tNOTHROW_DEFINE void* Allocate(size_t size)\n\t{\n\t\tENSURE(IsValidSize(size));\n\t\tValidate();\n\n\t\tvoid* p = TakeAndSplitFreeBlock(size);\n\t\tif(!p)\n\t\t{\n\t\t\tp = pool_alloc(&m_pool, size);\n\t\t\tif(!p)\t\t\t// both failed; don't throw bad_alloc because\n\t\t\t\treturn 0;\t// this often happens with the file cache.\n\t\t}\n\n\t\t// (NB: we must not update the statistics if allocation failed)\n\t\tm_stats.OnAllocate(size);\n\n\t\tValidate();\n\t\tENSURE((uintptr_t)p % allocationAlignment == 0);\n\t\treturn p;\n\t}\n\n\tvoid Deallocate(u8* p, size_t size)\n\t{\n\t\tENSURE((uintptr_t)p % allocationAlignment == 0);\n\t\tENSURE(IsValidSize(size));\n\t\tENSURE(pool_contains(&m_pool, p));\n\t\tENSURE(pool_contains(&m_pool, p+size-1));\n\n\t\tValidate();\n\n\t\tm_stats.OnDeallocate(size);\n\t\tCoalesce(p, size);\n\t\tAddToFreelist(p, size);\n\n\t\tValidate();\n\t}\n\n\tvoid Validate() const\n\t{\n\t\tif(!performSanityChecks) return;\n\n\t\tm_segregatedRangeLists.Validate(BoundaryTagManager::s_headerId);\n\t\tm_stats.Validate();\n\n\t\tAssertEqual(m_stats.FreeBlocks(), m_segregatedRangeLists.FreeBlocks(), m_boundaryTagManager.FreeBlocks());\n\t\tAssertEqual(m_stats.FreeBytes(), m_segregatedRangeLists.FreeBytes(), m_boundaryTagManager.FreeBytes());\n\t}\n\nprivate:\n\tvoid AddToFreelist(u8* p, size_t size)\n\t{\n\t\tFreedBlock* freedBlock = m_boundaryTagManager.WriteTags(p, size);\n\t\tm_segregatedRangeLists.Insert(freedBlock);\n\t\tm_stats.OnAddToFreelist(size);\n\t}\n\n\tvoid RemoveFromFreelist(FreedBlock* freedBlock)\n\t{\n\t\tm_stats.OnRemoveFromFreelist(freedBlock->Size());\n\t\tm_segregatedRangeLists.Remove(freedBlock);\n\t\tm_boundaryTagManager.RemoveTags(freedBlock);\n\t}\n\n\t/**\n\t * expand a block by coalescing it with its free neighbor(s).\n\t **/\n\tvoid Coalesce(u8*& p, size_t& size)\n\t{\n\t\t{\n\t\t\tFreedBlock* precedingBlock = m_boundaryTagManager.PrecedingBlock(p, m_pool.da.base);\n\t\t\tif(precedingBlock)\n\t\t\t{\n\t\t\t\tp -= precedingBlock->Size();\n\t\t\t\tsize += precedingBlock->Size();\n\t\t\t\tRemoveFromFreelist(precedingBlock);\n\t\t\t}\n\t\t}\n\n\t\t{\n\t\t\tFreedBlock* followingBlock = m_boundaryTagManager.FollowingBlock(p, size, m_pool.da.base+m_pool.da.pos);\n\t\t\tif(followingBlock)\n\t\t\t{\n\t\t\t\tsize += followingBlock->Size();\n\t\t\t\tRemoveFromFreelist(followingBlock);\n\t\t\t}\n\t\t}\n\t}\n\n\tvoid* TakeAndSplitFreeBlock(size_t size)\n\t{\n\t\tu8* p;\n\t\tsize_t leftoverSize = 0;\n\t\t{\n\t\t\tFreedBlock* freedBlock = m_segregatedRangeLists.Find(size);\n\t\t\tif(!freedBlock)\n\t\t\t\treturn 0;\n\n\t\t\tp = (u8*)freedBlock;\n\t\t\tleftoverSize = freedBlock->Size() - size;\n\t\t\tRemoveFromFreelist(freedBlock);\n\t\t}\n\n\t\tif(IsValidSize(leftoverSize))\n\t\t\tAddToFreelist(p+size, leftoverSize);\n\n\t\treturn p;\n\t}\n\n\tPool m_pool;\n\tSegregatedRangeLists m_segregatedRangeLists;\n\tBoundaryTagManager m_boundaryTagManager;\n\tStats m_stats;\n};\n\n\n//-----------------------------------------------------------------------------\n\nHeaderlessAllocator::HeaderlessAllocator(size_t poolSize)\n\t: impl(new Impl(poolSize))\n{\n}\n\nvoid HeaderlessAllocator::Reset()\n{\n\treturn impl->Reset();\n}\n\nNOTHROW_DEFINE void* HeaderlessAllocator::Allocate(size_t size)\n{\n\treturn impl->Allocate(size);\n}\n\nvoid HeaderlessAllocator::Deallocate(void* p, size_t size)\n{\n\treturn impl->Deallocate((u8*)p, size);\n}\n\nvoid HeaderlessAllocator::Validate() const\n{\n\treturn impl->Validate();\n}\n"
  },
  {
    "path": "fpsgame/gui/allocators/headerless.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * (header-less) pool-based heap allocator\n */\n\n#ifndef INCLUDED_ALLOCATORS_HEADERLESS\n#define INCLUDED_ALLOCATORS_HEADERLESS\n\n/**\n * (header-less) pool-based heap allocator\n * provides Allocate and Deallocate without requiring in-band headers;\n * this is useful when allocating page-aligned I/O buffers\n * (headers would waste an entire page per buffer)\n *\n * policy:\n * - allocation: first exhaust the freelist, then allocate more\n * - freelist: address-ordered good fit, always split blocks\n * - coalescing: immediate\n * mechanism:\n * - coalescing: boundary tags in freed memory with distinct bit patterns\n * - freelist: segregated range lists of power-of-two size classes\n *\n * note: this module basically implements a (rather complex) freelist and\n * could be made independent of the Pool allocation scheme. however, reading\n * neighboring boundary tags may cause segmentation violations; knowing the\n * bounds of valid committed memory (i.e. Pool extents) avoids this.\n **/\nclass HeaderlessAllocator\n{\npublic:\n\t// allocators must 'naturally' align pointers, i.e. ensure they are\n\t// multiples of the largest native type (currently __m128).\n\t// since there are no headers, we can guarantee alignment by\n\t// requiring sizes to be multiples of allocationAlignment.\n\tstatic const size_t allocationAlignment = 16;\n\n\t// allocations must be large enough to hold our boundary tags\n\t// when freed. (see rationale above BoundaryTagManager)\n\tstatic const size_t minAllocationSize = 128;\n\n\t/**\n\t * @param poolSize maximum amount of memory that can be allocated.\n\t * this much virtual address space is reserved up-front (see Pool).\n\t **/\n\tHeaderlessAllocator(size_t poolSize);\n\n\t/**\n\t * restore the original state (as if newly constructed).\n\t * this includes reclaiming all extant allocations.\n\t **/\n\tvoid Reset();\n\n\t/**\n\t * @param size [bytes] (= minAllocationSize + i*allocationAlignment).\n\t * (this allocator is designed for requests on the order of several KiB)\n\t * @return allocated memory or 0 if the pool is too fragmented or full.\n\t **/\n\tNOTHROW_DECLARE void* Allocate(size_t size);\n\n\t/**\n\t * deallocate memory.\n\t * @param p must be exactly as returned by Allocate (in particular,\n\t * evenly divisible by allocationAlignment)\n\t * @param size must be exactly as specified to Allocate.\n\t **/\n\tvoid Deallocate(void* p, size_t size);\n\n\t/**\n\t * perform sanity checks; ensure allocator state is consistent.\n\t **/\n\tvoid Validate() const;\n\nprivate:\n\tclass Impl;\n\tshared_ptr<Impl> impl;\n};\n\n#endif\t// #ifndef INCLUDED_ALLOCATORS_HEADERLESS\n"
  },
  {
    "path": "fpsgame/gui/allocators/overrun_protector.h",
    "content": "/* Copyright (c) 2011 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#ifndef INCLUDED_ALLOCATORS_OVERRUN_PROTECTOR\n#define INCLUDED_ALLOCATORS_OVERRUN_PROTECTOR\n\n#include \"lib/config2.h\"\t// CONFIG2_ALLOCATORS_OVERRUN_PROTECTION\n#include \"lib/sysdep/vm.h\"\n\n/**\nOverrunProtector wraps an arbitrary object in isolated page(s) and\ncan detect inadvertent writes to it. this is useful for\ntracking down memory overruns.\n\nthe basic idea is to require users to request access to the object and\nnotify us when done; memory access permission is temporarily granted.\n(similar in principle to Software Transaction Memory).\n\nsince this is quite slow, the protection is disabled unless\nCONFIG2_ALLOCATORS_OVERRUN_PROTECTION == 1; this avoids having to remove the\nwrapper code in release builds and re-write when looking for overruns.\n\nexample usage:\nOverrunProtector\\<T\\> wrapper;\n..\nT* p = wrapper.get();   // unlock, make ready for use\nif(!p)                  // wrapper's one-time alloc of a T-\n\tabort();            // instance had failed - can't continue.\nDoSomethingWith(p);     // (read/write access)\nwrapper.lock();         // disallow further access until next .get()\n..\n**/\ntemplate<class T> class OverrunProtector\n{\n\tNONCOPYABLE(OverrunProtector);\t// const member\npublic:\n\tOverrunProtector()\n#if CONFIG2_ALLOCATORS_OVERRUN_PROTECTION\n\t\t: object(new(vm::Allocate(sizeof(T))) T())\n#else\n\t\t: object(new T())\n#endif\n\t{\n\t\tlock();\n\t}\n\n\t~OverrunProtector()\n\t{\n\t\tunlock();\n#if CONFIG2_ALLOCATORS_OVERRUN_PROTECTION\n\t\tobject->~T();\t// call dtor (since we used placement new)\n\t\tvm::Free(object, sizeof(T));\n#else\n\t\tdelete object;\n#endif\n\t}\n\n\tT* get() const\n\t{\n\t\tunlock();\n\t\treturn object;\n\t}\n\n\tvoid lock() const\n\t{\n#if CONFIG2_ALLOCATORS_OVERRUN_PROTECTION\n\t\tvm::Protect(object, sizeof(T), PROT_NONE);\n#endif\n\t}\n\nprivate:\n\tvoid unlock() const\n\t{\n#if CONFIG2_ALLOCATORS_OVERRUN_PROTECTION\n\t\tvm::Protect(object, sizeof(T), PROT_READ|PROT_WRITE);\n#endif\n\t}\n\n\tT* const object;\n};\n\n#endif\t// #ifndef INCLUDED_ALLOCATORS_OVERRUN_PROTECTOR\n"
  },
  {
    "path": "fpsgame/gui/allocators/page_aligned.cpp",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#include \"precompiled.h\"\n#include \"lib/allocators/page_aligned.h\"\n\n#include \"lib/alignment.h\"\n#include \"lib/sysdep/cpu.h\"\t// cpu_CAS\n\n\n//-----------------------------------------------------------------------------\n\nstatic inline Status StatusFromMap(void* ret)\n{\n\tif(ret != MAP_FAILED)\n\t\treturn INFO::OK;\n\tWARN_RETURN(StatusFromErrno());\n}\n\n// \"anonymous\" effectively means mapping /dev/zero, but is more efficient.\n// MAP_ANONYMOUS is not in SUSv3, but is a very common extension.\n// unfortunately, MacOS X only defines MAP_ANON, which Solaris says is\n// deprecated. workaround there: define MAP_ANONYMOUS in terms of MAP_ANON.\n#ifndef MAP_ANONYMOUS\n# define MAP_ANONYMOUS MAP_ANON\n#endif\n\nstatic const int mmap_flags = MAP_PRIVATE|MAP_ANONYMOUS;\n\nStatus mem_Reserve(size_t size, u8** pp)\n{\n\terrno = 0;\n\tvoid* ret = mmap(0, size, PROT_NONE, mmap_flags|MAP_NORESERVE, -1, 0);\n\t*pp = (u8*)ret;\n\treturn StatusFromMap(ret);\n}\n\nStatus mem_Release(u8* p, size_t size)\n{\n\terrno = 0;\n\tif(munmap(p, size) != 0)\n\t\tWARN_RETURN(StatusFromErrno());\n\treturn 0;\n}\n\nStatus mem_Commit(u8* p, size_t size, int prot)\n{\n\t// avoid misinterpretation by mmap.\n\tif(prot == PROT_NONE)\n\t\tWARN_RETURN(ERR::INVALID_PARAM);\n\n\terrno = 0;\n\tvoid* ret = mmap(p, size, prot, mmap_flags|MAP_FIXED, -1, 0);\n\treturn StatusFromMap(ret);\n}\n\nStatus mem_Decommit(u8* p, size_t size)\n{\n\terrno = 0;\n\tvoid* ret = mmap(p, size, PROT_NONE, mmap_flags|MAP_NORESERVE|MAP_FIXED, -1, 0);\n\treturn StatusFromMap(ret);\n}\n\nStatus mem_Protect(u8* p, size_t size, int prot)\n{\n\terrno = 0;\n\tif(mprotect(p, size, prot) != 0)\n\t\tWARN_RETURN(StatusFromErrno());\n\treturn 0;\n\n}\n\n\n//-----------------------------------------------------------------------------\n\nvoid* page_aligned_alloc(size_t size)\n{\n\tconst size_t alignedSize = Align<pageSize>(size);\n\tu8* p = 0;\n\tRETURN_0_IF_ERR(mem_Reserve(alignedSize, &p));\n\tRETURN_0_IF_ERR(mem_Commit(p, alignedSize, PROT_READ|PROT_WRITE));\n\treturn p;\n}\n\n\nvoid page_aligned_free(void* p, size_t size)\n{\n\tif(!p)\n\t\treturn;\n\tENSURE(IsAligned(p, pageSize));\n\tconst size_t alignedSize = Align<pageSize>(size);\n\t(void)mem_Release((u8*)p, alignedSize);\n}\n"
  },
  {
    "path": "fpsgame/gui/allocators/page_aligned.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#ifndef INCLUDED_ALLOCATORS_PAGE_ALIGNED\n#define INCLUDED_ALLOCATORS_PAGE_ALIGNED\n\n#include \"lib/posix/posix_mman.h\"\t// PROT_*\n\n// very thin wrapper on top of sys/mman.h that makes the intent more obvious\n// (its commit/decommit semantics are difficult to tell apart)\nLIB_API Status mem_Reserve(size_t size, u8** pp);\nLIB_API Status mem_Release(u8* p, size_t size);\nLIB_API Status mem_Commit(u8* p, size_t size, int prot);\nLIB_API Status mem_Decommit(u8* p, size_t size);\nLIB_API Status mem_Protect(u8* p, size_t size, int prot);\n\n\n/**\n * allocate memory aligned to the system page size.\n *\n * this is useful for file_cache_alloc, which uses this allocator to\n * get sector-aligned (hopefully; see sys_max_sector_size) IO buffers.\n *\n * note that this allocator is stateless and very little error checking\n * can be performed.\n *\n * the memory is initially writable and you can use mprotect to set other\n * access permissions if desired.\n *\n * @param unaligned_size minimum size [bytes] to allocate.\n * @return page-aligned and -padded memory or 0 on error / out of memory.\n **/\nLIB_API void* page_aligned_alloc(size_t unaligned_size);\n\n/**\n * free a previously allocated page-aligned region.\n *\n * @param p Exact value returned from page_aligned_alloc\n * @param unaligned_size Exact value passed to page_aligned_alloc\n **/\nLIB_API void page_aligned_free(void* p, size_t unaligned_size);\n\n#endif\t// #ifndef INCLUDED_ALLOCATORS_PAGE_ALIGNED\n"
  },
  {
    "path": "fpsgame/gui/allocators/pool.cpp",
    "content": "/* Copyright (c) 2011 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * pool allocator\n */\n\n#include \"precompiled.h\"\n#include \"lib/allocators/pool.h\"\n\n#include \"lib/alignment.h\"\n#include \"lib/allocators/freelist.h\"\n#include \"lib/allocators/allocator_adapters.h\"\n\n#include \"lib/timer.h\"\n\nnamespace Allocators {\n\ntemplate<class Storage>\nstruct BasicPoolTest\n{\n\tvoid operator()() const\n\t{\n\t\tPool<double, Storage> p(100);\n\t\tconst size_t initialSpace = p.RemainingObjects();\n\t\tdouble* p1 = p.Allocate();\n\t\tENSURE(p1 != 0);\n\t\tENSURE(p.Contains(uintptr_t(p1)));\n\t\tENSURE(p.RemainingObjects() == initialSpace-1);\n\t\tENSURE(p.Contains(uintptr_t(p1)+1));\n\t\tENSURE(p.Contains(uintptr_t(p1)+sizeof(double)-1));\n\t\tENSURE(!p.Contains(uintptr_t(p1)-1));\n\t\tENSURE(!p.Contains(uintptr_t(p1)+sizeof(double)));\n\t\tif(p.RemainingObjects() == 0)\n\t\t\tENSURE(p.Allocate() == 0);\t// full\n\t\telse\n\t\t\tENSURE(p.Allocate() != 0);\t// can still expand\n\t\tp.DeallocateAll();\n\t\tENSURE(!p.Contains(uintptr_t(p1)));\n\n\t\tp1 = p.Allocate();\n\t\tENSURE(p1 != 0);\n\t\tENSURE(p.Contains(uintptr_t(p1)));\n\t\tENSURE(p.RemainingObjects() == initialSpace-1);\n\t\tdouble* p2 = p.Allocate();\n\t\tENSURE(p2 != 0);\n\t\tENSURE(p.Contains(uintptr_t(p2)));\n\t\tENSURE(p.RemainingObjects() == initialSpace-2);\n\t\tENSURE(p2 == (double*)(uintptr_t(p1)+sizeof(double)));\n\t\tif(p.RemainingObjects() == 0)\n\t\t\tENSURE(p.Allocate() == 0);\t// full\n\t\telse\n\t\t\tENSURE(p.Allocate() != 0);\t// can still expand\n\t}\n};\n\nvoid TestPool()\n{\n\tForEachStorage<BasicPoolTest>();\n}\n\n}\t// namespace Allocators\n\n\nTIMER_ADD_CLIENT(tc_pool_alloc);\n\nStatus pool_create(Pool* p, size_t max_size, size_t el_size)\n{\n\tif(el_size == POOL_VARIABLE_ALLOCS)\n\t\tp->el_size = 0;\n\telse\n\t\tp->el_size = Align<allocationAlignment>(el_size);\n\tp->freelist = mem_freelist_Sentinel();\n\tRETURN_STATUS_IF_ERR(da_alloc(&p->da, max_size));\n\treturn INFO::OK;\n}\n\n\nStatus pool_destroy(Pool* p)\n{\n\t// don't be picky and complain if the freelist isn't empty;\n\t// we don't care since it's all part of the da anyway.\n\t// however, zero it to prevent further allocs from succeeding.\n\tp->freelist = mem_freelist_Sentinel();\n\treturn da_free(&p->da);\n}\n\n\nbool pool_contains(const Pool* p, void* el)\n{\n\t// outside of our range\n\tif(!(p->da.base <= el && el < p->da.base+p->da.pos))\n\t\treturn false;\n\t// sanity check: it should be aligned (if pool has fixed-size elements)\n\tif(p->el_size)\n\t\tENSURE((uintptr_t)((u8*)el - p->da.base) % p->el_size == 0);\n\treturn true;\n}\n\n\nvoid* pool_alloc(Pool* p, size_t size)\n{\n\tTIMER_ACCRUE(tc_pool_alloc);\n\t// if pool allows variable sizes, go with the size parameter,\n\t// otherwise the pool el_size setting.\n\tconst size_t el_size = p->el_size? p->el_size : Align<allocationAlignment>(size);\n\tASSERT(el_size != 0);\n\n\t// note: freelist is always empty in pools with variable-sized elements\n\t// because they disallow pool_free.\n\tvoid* el = mem_freelist_Detach(p->freelist);\n\tif(!el)\t// freelist empty, need to allocate a new entry\n\t{\n\t\t// expand, if necessary\n\t\tif(da_reserve(&p->da, el_size) < 0)\n\t\t\treturn 0;\n\n\t\tel = p->da.base + p->da.pos;\n\t\tp->da.pos += el_size;\n\t}\n\n\tASSERT(pool_contains(p, el));\t// paranoia\n\treturn el;\n}\n\n\nvoid pool_free(Pool* p, void* el)\n{\n\t// only allowed to free items if we were initialized with\n\t// fixed el_size. (this avoids having to pass el_size here and\n\t// check if requested_size matches that when allocating)\n\tif(p->el_size == 0)\n\t{\n\t\tDEBUG_WARN_ERR(ERR::LOGIC);\t// cannot free variable-size items\n\t\treturn;\n\t}\n\n\tif(pool_contains(p, el))\n\t\tmem_freelist_AddToFront(p->freelist, el);\n\telse\n\t\tDEBUG_WARN_ERR(ERR::LOGIC);\t// invalid pointer (not in pool)\n}\n\n\nvoid pool_free_all(Pool* p)\n{\n\tp->freelist = mem_freelist_Sentinel();\n\n\t// must be reset before da_set_size or CHECK_DA will complain.\n\tp->da.pos = 0;\n\n\tda_set_size(&p->da, 0);\n}\n\nsize_t pool_committed(Pool* p)\n{\n\treturn p->da.cur_size;\n}\n"
  },
  {
    "path": "fpsgame/gui/allocators/pool.h",
    "content": "/* Copyright (c) 2011 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * pool allocator (fixed-size blocks, freelist).\n */\n\n#ifndef INCLUDED_ALLOCATORS_POOL\n#define INCLUDED_ALLOCATORS_POOL\n\n#include \"lib/bits.h\"\t// ROUND_UP\n#include \"lib/allocators/allocator_policies.h\"\n\nnamespace Allocators {\n\t\n/**\n * allocator design parameters:\n * - O(1) allocation and deallocation;\n * - fixed-size objects;\n * - support for deallocating all objects;\n * - consecutive allocations are back-to-back;\n * - objects are aligned to the pointer size.\n **/\ntemplate<typename T, class Storage = Storage_Fixed<> >\nclass Pool\n{\n\tNONCOPYABLE(Pool);\npublic:\n\t// (must round up because freelist stores pointers inside objects)\n\tstatic const size_t objectSize = ROUND_UP(sizeof(T), sizeof(intptr_t));\n\n\tPool(size_t maxObjects)\n\t\t: storage(maxObjects*objectSize)\n\t{\n\t\tDeallocateAll();\n\t}\n\n\tsize_t RemainingObjects()\n\t{\n\t\treturn (storage.MaxCapacity() - end) / objectSize;\n\t}\n\n\tT* Allocate()\n\t{\n\t\tvoid* p = mem_freelist_Detach(freelist);\n\t\tif(p)\n\t\t{\n\t\t\tASSERT(Contains((uintptr_t)p));\n\t\t\treturn (T*)p;\n\t\t}\n\n\t\treturn (T*)StorageAppend(storage, end, objectSize);\n\t}\n\n\tvoid Deallocate(T* p)\n\t{\n\t\tASSERT(Contains((uintptr_t)p));\n\t\tmem_freelist_AddToFront(freelist, p);\n\t}\n\n\tvoid DeallocateAll()\n\t{\n\t\tfreelist = mem_freelist_Sentinel();\n\t\tend = 0;\n\t}\n\n\t// @return whether the address lies within the previously allocated range.\n\tbool Contains(uintptr_t address) const\n\t{\n\t\treturn (address - storage.Address()) < end;\n\t}\n\nprivate:\n\tStorage storage;\n\tsize_t end;\n\tvoid* freelist;\n};\n\nLIB_API void TestPool();\n\n}\t// namespace Allocators\n\n\n#include \"lib/allocators/dynarray.h\"\n\n/**\n * allocator design parameters:\n * - O(1) alloc and free;\n * - either fixed- or variable-sized blocks;\n * - doesn't preallocate the entire pool;\n * - returns sequential addresses.\n *\n * opaque! do not read/write any fields!\n **/\nstruct Pool\n{\n\tDynArray da;\n\n\t/**\n\t * size of elements. = 0 if pool set up for variable-sized\n\t * elements, otherwise rounded up to pool alignment.\n\t **/\n\tsize_t el_size;\n\n\t/**\n\t * pointer to freelist (opaque); see freelist_*.\n\t * never used (remains 0) if elements are of variable size.\n\t **/\n\tvoid* freelist;\n};\n\n/**\n * pass as pool_create's \\<el_size\\> param to indicate variable-sized allocs\n * are required (see below).\n **/\nconst size_t POOL_VARIABLE_ALLOCS = ~(size_t)0u;\n\n/**\n * Ready Pool for use.\n *\n * @param p Pool*\n * @param max_size Max size [bytes] of the Pool; this much\n * (rounded up to next page multiple) virtual address space is reserved.\n * no virtual memory is actually committed until calls to pool_alloc.\n * @param el_size Number of bytes that will be returned by each\n * pool_alloc (whose size parameter is then ignored). Can be 0 to\n * allow variable-sized allocations, but pool_free is then unusable.\n * @return Status\n **/\nLIB_API Status pool_create(Pool* p, size_t max_size, size_t el_size);\n\n/**\n * free all memory (address space + physical) that constitutes the\n * given Pool.\n *\n * future alloc and free calls on this pool will fail.\n * continued use of the allocated memory (*) is\n * impossible because it is marked not-present via MMU.\n * (* no matter if in freelist or unused or \"allocated\" to user)\n *\n * @param p Pool*\n * @return Status.\n **/\nLIB_API Status pool_destroy(Pool* p);\n\n/**\n * indicate whether a pointer was allocated from the given pool.\n *\n * this is useful for callers that use several types of allocators.\n *\n * @param p Pool*\n * @param el\n * @return bool.\n **/\nLIB_API bool pool_contains(const Pool* p, void* el);\n\n/**\n * Dole out memory from the pool.\n * exhausts the freelist before returning new entries to improve locality.\n *\n * @param p Pool*\n * @param size bytes to allocate; ignored if pool_create's el_size was not 0.\n * @return allocated memory, or 0 if the Pool would have to be expanded and\n * there isn't enough memory to do so.\n **/\nLIB_API void* pool_alloc(Pool* p, size_t size);\n\n/**\n * Make a fixed-size element available for reuse in the given Pool.\n *\n * this is not allowed if the Pool was created for variable-size elements.\n * rationale: avoids having to pass el_size here and compare with size when\n * allocating; also prevents fragmentation and leaking memory.\n *\n * @param p Pool*\n * @param el Element returned by pool_alloc.\n **/\nLIB_API void pool_free(Pool* p, void* el);\n\n/**\n * \"free\" all user allocations that ensued from the given Pool.\n *\n * this resets it as if freshly pool_create-d, but doesn't release the\n * underlying reserved virtual memory.\n *\n * @param p Pool*\n **/\nLIB_API void pool_free_all(Pool* p);\n\n/**\n * Return the number of bytes committed in the pool's backing array.\n *\n * This is roughly the number of bytes allocated in this pool plus the\n * unused freelist entries.\n *\n * @param p Pool*\n * @return number of bytes\n **/\nLIB_API size_t pool_committed(Pool* p);\n\n#endif\t// #ifndef INCLUDED_ALLOCATORS_POOL\n"
  },
  {
    "path": "fpsgame/gui/allocators/shared_ptr.cpp",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#include \"precompiled.h\"\n#include \"lib/allocators/shared_ptr.h\"\n\n#include \"lib/allocators/allocator_checker.h\"\n\n\n#ifndef NDEBUG\nstatic AllocatorChecker s_allocatorChecker;\n#endif\n\nclass CheckedArrayDeleter\n{\npublic:\n\tCheckedArrayDeleter(size_t size)\n\t\t: m_size(size)\n\t{\n\t}\n\n\tvoid operator()(u8* p)\n\t{\n\t\tENSURE(m_size != 0);\n#ifndef NDEBUG\n\t\ts_allocatorChecker.OnDeallocate(p, m_size);\n#endif\n\t\tdelete[] p;\n\t\tm_size = 0;\n\t}\n\nprivate:\n\tsize_t m_size;\n};\n\nshared_ptr<u8> Allocate(size_t size)\n{\n\tENSURE(size != 0);\n\n\tu8* p = new u8[size];\n#ifndef NDEBUG\n\ts_allocatorChecker.OnAllocate(p, size);\n#endif\n\n\treturn shared_ptr<u8>(p, CheckedArrayDeleter(size));\n}\n"
  },
  {
    "path": "fpsgame/gui/allocators/shared_ptr.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#ifndef INCLUDED_ALLOCATORS_SHARED_PTR\n#define INCLUDED_ALLOCATORS_SHARED_PTR\n\n#include \"lib/alignment.h\"\n#include \"lib/sysdep/rtl.h\" // rtl_AllocateAligned\n\nstruct DummyDeleter\n{\n\ttemplate<class T>\n\tvoid operator()(T*)\n\t{\n\t}\n};\n\ntemplate<class T>\ninline shared_ptr<T> DummySharedPtr(T* ptr)\n{\n\treturn shared_ptr<T>(ptr, DummyDeleter());\n}\n\nstruct ArrayDeleter\n{\n\ttemplate<class T>\n\tvoid operator()(T* p)\n\t{\n\t\tdelete[] p;\n\t}\n};\n\n// (note: uses CheckedArrayDeleter)\nLIB_API shared_ptr<u8> Allocate(size_t size);\n\n\nstruct AlignedDeleter\n{\n\ttemplate<class T>\n\tvoid operator()(T* t)\n\t{\n\t\trtl_FreeAligned(t);\n\t}\n};\n\ntemplate<class T>\nstatic inline Status AllocateAligned(shared_ptr<T>& p, size_t size, size_t alignment = cacheLineSize)\n{\n\tvoid* mem = rtl_AllocateAligned(size, alignment);\n\tif(!mem)\n\t\tWARN_RETURN(ERR::NO_MEM);\n\tp.reset((T*)mem, AlignedDeleter());\n\treturn INFO::OK;\n}\n\n#endif\t// #ifndef INCLUDED_ALLOCATORS_SHARED_PTR\n"
  },
  {
    "path": "fpsgame/gui/allocators/tests/test_allocators.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#include \"lib/self_test.h\"\n\n#include \"lib/allocators/dynarray.h\"\n#include \"lib/byte_order.h\"\n\nclass TestAllocators : public CxxTest::TestSuite \n{\npublic:\n\tvoid test_da()\n\t{\n\t\tDynArray da;\n\n\t\t// basic test of functionality (not really meaningful)\n\t\tTS_ASSERT_OK(da_alloc(&da, 1000));\n\t\tTS_ASSERT_OK(da_set_size(&da, 1000));\n\t\tTS_ASSERT_OK(da_free(&da));\n\t}\n};\n"
  },
  {
    "path": "fpsgame/gui/allocators/tests/test_headerless.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#include \"lib/self_test.h\"\n\n#include \"lib/bits.h\"\t// round_down\n#include \"lib/allocators/headerless.h\"\n\nvoid* const null = 0;\n\nclass TestHeaderless: public CxxTest::TestSuite \n{\npublic:\n\tvoid test_Basic()\n\t{\n\t\tHeaderlessAllocator a(8192);\n\n\t\t// (these are disabled because they raise an assert)\n#if 0\n\t\t// can't Allocate unaligned sizes\n\t\tTS_ASSERT_EQUALS(a.Allocate(HeaderlessAllocator::minAllocationSize+1), null);\n\n\t\t// can't Allocate too small amounts\n\t\tTS_ASSERT_EQUALS(a.Allocate(HeaderlessAllocator::minAllocationSize/2), null);\n#endif\n\n\t\t// can Allocate the entire pool\n\t\tchar* p1 = (char*)a.Allocate(4096);\n\t\tchar* p2 = (char*)a.Allocate(4096);\n\t\tTS_ASSERT_DIFFERS(p1, null);\n\t\tTS_ASSERT_DIFFERS(p2, null);\n\n\t\t// back-to-back (non-freelist) allocations should be contiguous\n\t\tTS_ASSERT_EQUALS(p1+4096, p2);\n\n\t\t// allocations are writable\n\t\tp1[0] = 11;\n\t\tp1[4095] = 12;\n\t}\n\n\tvoid test_Free()\n\t{\n\t\t// Deallocate allows immediate reuse of the freed pointer\n\t\tHeaderlessAllocator a(4096);\n\t\tvoid* p1 = a.Allocate(1024);\n\t\ta.Deallocate(p1, 1024);\n\t\tvoid* p2 = a.Allocate(1024);\n\t\tTS_ASSERT_EQUALS(p1, p2);\n\t}\n\n\tvoid test_Coalesce()\n\t{\n\t\tHeaderlessAllocator a(0x10000);\n\n\t\t// can Allocate non-power-of-two sizes (note: the current\n\t\t// implementation only allows sizes that are multiples of 0x10)\n\t\tvoid* p1 = a.Allocate(0x5680);\n\t\tvoid* p2 = a.Allocate(0x78A0);\n\t\tvoid* p3 = a.Allocate(0x1240);\n\t\tTS_ASSERT_DIFFERS(p1, null);\n\t\tTS_ASSERT_DIFFERS(p2, null);\n\t\tTS_ASSERT_DIFFERS(p3, null);\n\n\t\t// after freeing, must be able to allocate the total amount\n\t\t// note: we don't insist on being able to fill the entire\n\t\t// memory range. in this case, the problem is that the pool has some\n\t\t// virtual address space left, but the allocator doesn't grab that\n\t\t// and add it to the freelist. that feature is currently not\n\t\t// implemented.\n\t\ta.Deallocate(p1, 0x5680);\n\t\ta.Deallocate(p2, 0x78A0);\n\t\ta.Deallocate(p3, 0x1240);\n\t\tvoid* p4 = a.Allocate(0xE140);\n\t\tTS_ASSERT_DIFFERS(p4, null);\n\t}\n\n\tvoid test_Reset()\n\t{\n\t\t// after Reset, must return the same pointer as a freshly constructed instance\n\t\tHeaderlessAllocator a(4096);\n\t\tvoid* p1 = a.Allocate(128);\n\t\ta.Reset();\n\t\tvoid* p2 = a.Allocate(128);\n\t\tTS_ASSERT_EQUALS(p1, p2);\n\t}\n\n\t// will the allocator survive a series of random but valid Allocate/Deallocate?\n\tvoid test_Randomized()\n\t{\n\t\tconst size_t poolSize = 1024*1024;\n\t\tHeaderlessAllocator a(poolSize);\n\n\t\ttypedef std::map<void*, size_t> AllocMap;\n\t\tAllocMap allocs;\n\n\t\tsrand(1);\n\n\t\tfor(int i = 0; i < 1000; i++)\n\t\t{\n\t\t\t// allocate\n\t\t\tif(rand() >= RAND_MAX/2)\n\t\t\t{\n\t\t\t\tconst size_t maxSize = (size_t)((rand() / (float)RAND_MAX) * poolSize);\n\t\t\t\tconst size_t size = std::max((size_t)HeaderlessAllocator::minAllocationSize, round_down(maxSize, HeaderlessAllocator::allocationAlignment));\n\t\t\t\t// (the size_t cast on minAllocationSize prevents max taking a reference to the non-defined variable)\n\t\t\t\tvoid* p = a.Allocate(size);\n\t\t\t\tif(!p)\n\t\t\t\t\tcontinue;\n\t\t\t\tTS_ASSERT(allocs.find(p) == allocs.end());\n\t\t\t\tallocs[p] = size;\n\t\t\t}\n\t\t\t// free\n\t\t\telse\n\t\t\t{\n\t\t\t\tif(allocs.empty())\n\t\t\t\t\tcontinue;\n\t\t\t\t// find random allocation to deallocate\n\t\t\t\tAllocMap::iterator it = allocs.begin();\n\t\t\t\tconst int numToSkip = rand() % (int)allocs.size();\n\t\t\t\tfor(int skip = 0; skip < numToSkip; skip++)\n\t\t\t\t\t++it;\n\t\t\t\tvoid* p = (*it).first;\n\t\t\t\tsize_t size = (*it).second;\n\t\t\t\tallocs.erase(it);\n\t\t\t\ta.Deallocate(p, size);\n\t\t\t}\n\t\t}\n\t}\n};\n"
  },
  {
    "path": "fpsgame/gui/allocators/unique_range.cpp",
    "content": "/* Copyright (c) 2015 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#include \"precompiled.h\"\n#include \"lib/allocators/unique_range.h\"\n\n#include \"lib/bits.h\"\t// is_pow2, round_up\n#include \"lib/sysdep/cpu.h\"\t// cpu_AtomicAdd\n#include \"lib/sysdep/rtl.h\"\t// rtl_FreeAligned\n\n\nstatic void FreeNone(void* UNUSED(pointer), size_t UNUSED(size))\n{\n\t// (providing a deleter function for idxDeleterNone avoids\n\t// having to check whether deleters[idxDeleter] == 0)\n}\n\nstatic void FreeAligned(void* pointer, size_t UNUSED(size))\n{\n\treturn rtl_FreeAligned(pointer);\n}\n\n\nstatic UniqueRangeDeleter deleters[allocationAlignment] = { FreeNone, FreeAligned };\n\nstatic IdxDeleter numDeleters = 2;\n\n\n// NB: callers should skip this if *idxDeleterOut != 0 (avoids the overhead\n// of an unnecessary indirect function call)\nvoid RegisterUniqueRangeDeleter(UniqueRangeDeleter deleter, volatile IdxDeleter* idxDeleterOut)\n{\n\tENSURE(deleter);\n\n\tif(!cpu_CAS(idxDeleterOut, idxDeleterNone, -1))\t// not the first call for this deleter\n\t{\n\t\t// wait until an index has been assigned\n\t\twhile(*idxDeleterOut <= 0)\n\t\t\tcpu_Pause();\n\t\treturn;\n\t}\n\n\tconst IdxDeleter idxDeleter = cpu_AtomicAdd(&numDeleters, 1);\n\tENSURE(idxDeleter < (IdxDeleter)ARRAY_SIZE(deleters));\n\tdeleters[idxDeleter] = deleter;\n\tCOMPILER_FENCE;\n\t*idxDeleterOut = idxDeleter;\n}\n\n\nNOTHROW_DEFINE void CallUniqueRangeDeleter(void* pointer, size_t size, IdxDeleter idxDeleter)\n{\n\tASSERT(idxDeleter < numDeleters);\n\t// (some deleters do not tolerate null pointers)\n\tif(pointer)\n\t\tdeleters[idxDeleter](pointer, size);\n}\n\n\nUniqueRange AllocateAligned(size_t size, size_t alignment)\n{\n\tENSURE(is_pow2(alignment));\n\talignment = std::max(alignment, allocationAlignment);\n\n\tconst size_t alignedSize = round_up(size, alignment);\n\tconst UniqueRange::pointer p = rtl_AllocateAligned(alignedSize, alignment);\n\n\tstatic volatile IdxDeleter idxDeleterAligned;\n\tif(idxDeleterAligned == 0)\t// (optional optimization)\n\t\tRegisterUniqueRangeDeleter(FreeAligned, &idxDeleterAligned);\n\n\treturn std::move(UniqueRange(p, size, idxDeleterAligned));\n}\n\n\nUniqueRange AllocateVM(size_t size, vm::PageType pageType, int prot)\n{\n\tconst UniqueRange::pointer p = vm::Allocate(size, pageType, prot);\n\n\tstatic volatile IdxDeleter idxDeleter;\n\tif(idxDeleter == 0)\t// (optional optimization)\n\t\tRegisterUniqueRangeDeleter(vm::Free, &idxDeleter);\n\n\treturn std::move(UniqueRange(p, size, idxDeleter));\n}\n"
  },
  {
    "path": "fpsgame/gui/allocators/unique_range.h",
    "content": "/* Copyright (c) 2015 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#ifndef INCLUDED_ALLOCATORS_UNIQUE_RANGE\n#define INCLUDED_ALLOCATORS_UNIQUE_RANGE\n\n#include \"lib/lib_api.h\"\n#include \"lib/alignment.h\"\t// allocationAlignment\n#include \"lib/sysdep/vm.h\"\n\n// we usually don't hold multiple references to allocations, so unique_ptr\n// can be used instead of the more complex (ICC generated incorrect code on\n// 2 occasions) and expensive shared_ptr.\n// a custom deleter is required because allocators such as ReserveAddressSpace need to\n// pass the size to their deleter. we want to mix pointers from various allocators, but\n// unique_ptr's deleter is fixed at compile-time, so it would need to be general enough\n// to handle all allocators.\n// storing the size and a function pointer would be one such solution, with the added\n// bonus of no longer requiring a complete type at the invocation of ~unique_ptr.\n// however, this inflates the pointer size to 3 words. if only a few allocator types\n// are needed, we can replace the function pointer with an index stashed into the\n// lower bits of the pointer (safe because all allocations' addresses are multiples\n// of allocationAlignment).\ntypedef intptr_t IdxDeleter;\n\n// no-op deleter (use when returning part of an existing allocation)\nstatic const IdxDeleter idxDeleterNone = 0;\n\ntypedef void (*UniqueRangeDeleter)(void* pointer, size_t size);\n\n/**\n * register a deleter, returning its index within the table.\n *\n * @param deleter function pointer. must be uniquely associated with\n *   the idxDeleter storage location.\n * @param idxDeleter location where to store the next available index.\n *   if it is already non-zero, skip the call to this function to\n *   avoid overhead.\n *\n * thread-safe. idxDeleter is used for mutual exclusion between\n * multiple callers for the same deleter. concurrent registration of\n * different deleters is also safe due to atomic increments.\n *\n * halts the program if more than allocationAlignment deleters are\n * to be registered.\n **/\nLIB_API void RegisterUniqueRangeDeleter(UniqueRangeDeleter deleter, volatile IdxDeleter* idxDeleter);\n\nLIB_API NOTHROW_DECLARE void CallUniqueRangeDeleter(void* pointer, size_t size, IdxDeleter idxDeleter);\n\n\n// unfortunately, unique_ptr allows constructing without a custom deleter. to ensure callers can\n// rely upon pointers being associated with a size, we introduce a `UniqueRange' replacement.\n// its interface is identical to unique_ptr except for the constructors, the addition of\n// size() and the removal of operator bool (which avoids implicit casts to int).\nclass UniqueRange\n{\npublic:\n\ttypedef void* pointer;\n\ttypedef void element_type;\n\n\tUniqueRange()\n\t{\n\t\tClear();\n\t}\n\n\tUniqueRange(pointer p, size_t size, IdxDeleter deleter)\n\t{\n\t\tSet(p, size, deleter);\n\t}\n\n\tUniqueRange(UniqueRange&& rvalue)\n\t{\n\t\tPilfer(rvalue);\n\t}\n\n\tUniqueRange& operator=(UniqueRange&& rvalue)\n\t{\n\t\tUniqueRange& lvalue = rvalue;\n\t\tif(this != &lvalue)\n\t\t{\n\t\t\tDelete();\n\t\t\tPilfer(lvalue);\n\t\t}\n\t\treturn *this;\n\t}\n\n\t~UniqueRange()\n\t{\n\t\tDelete();\n\t}\n\n\tpointer get() const\n\t{\n\t\treturn pointer(address_ & ~(allocationAlignment-1));\n\t}\n\n\tIdxDeleter get_deleter() const\n\t{\n\t\treturn IdxDeleter(address_ % allocationAlignment);\n\t}\n\n\tsize_t size() const\n\t{\n\t\treturn size_;\n\t}\n\n\t// side effect: subsequent get_deleter will return idxDeleterNone\n\tpointer release()\t// relinquish ownership\n\t{\n\t\tpointer ret = get();\n\t\tClear();\n\t\treturn ret;\n\t}\n\n\tvoid reset()\n\t{\n\t\tDelete();\n\t\tClear();\n\t}\n\n\tvoid reset(pointer p, size_t size, IdxDeleter deleter)\n\t{\n\t\tDelete();\n\t\tSet(p, size, deleter);\n\t}\n\n\tvoid swap(UniqueRange& rhs)\n\t{\n\t\tstd::swap(address_, rhs.address_);\n\t\tstd::swap(size_, rhs.size_);\n\t}\n\n\t// don't define construction and assignment from lvalue,\n\t// but the declarations must be accessible\n\tUniqueRange(const UniqueRange&);\n\tUniqueRange& operator=(const UniqueRange&);\n\nprivate:\n\tvoid Set(pointer p, size_t size, IdxDeleter deleter)\n\t{\n\t\tASSERT((uintptr_t(p) % allocationAlignment) == 0);\n\t\tASSERT(size_t(deleter) < allocationAlignment);\n\n\t\taddress_ = uintptr_t(p) | deleter;\n\t\tsize_ = size;\n\n\t\tASSERT(get() == p);\n\t\tASSERT(get_deleter() == deleter);\n\t\tASSERT(this->size() == size);\n\t}\n\n\tvoid Clear()\n\t{\n\t\tSet(0, 0, idxDeleterNone);\n\t}\n\n\tvoid Pilfer(UniqueRange& victim)\n\t{\n\t\tconst size_t size = victim.size();\n\t\tconst IdxDeleter idxDeleter = victim.get_deleter();\n\t\tpointer p = victim.release();\n\t\tSet(p, size, idxDeleter);\n\t\tvictim.Clear();\n\t}\n\n\tvoid Delete()\n\t{\n\t\tCallUniqueRangeDeleter(get(), size(), get_deleter());\n\t}\n\n\t// (IdxDeleter is stored in the lower bits of address since size might not even be a multiple of 4.)\n\tuintptr_t address_;\n\tsize_t size_;\n};\n\nnamespace std {\n\nstatic inline void swap(UniqueRange& p1, UniqueRange& p2)\n{\n\tp1.swap(p2);\n}\n\nstatic inline void swap(UniqueRange&& p1, UniqueRange& p2)\n{\n\tp2.swap(p1);\n}\n\nstatic inline void swap(UniqueRange& p1, UniqueRange&& p2)\n{\n\tp1.swap(p2);\n}\n\n}\n\nLIB_API UniqueRange AllocateAligned(size_t size, size_t alignment);\n\nLIB_API UniqueRange AllocateVM(size_t size, vm::PageType pageSize = vm::kDefault, int prot = PROT_READ|PROT_WRITE);\n\n\n#endif\t// #ifndef INCLUDED_ALLOCATORS_UNIQUE_RANGE\n"
  },
  {
    "path": "fpsgame/gui/app_hooks.cpp",
    "content": "/* Copyright (c) 2015 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * hooks to allow customization / app-specific behavior.\n */\n\n#include \"precompiled.h\"\n#include \"lib/app_hooks.h\"\n\n#include \"lib/sysdep/sysdep.h\"\n\n#include <cstdio>\n\n//-----------------------------------------------------------------------------\n// default implementations\n//-----------------------------------------------------------------------------\n\nstatic void def_override_gl_upload_caps()\n{\n}\n\n\nstatic const OsPath& def_get_log_dir()\n{\n\tstatic OsPath logDir;\n\tif(logDir.empty())\n\t\tlogDir = sys_ExecutablePathname().Parent();\n\treturn logDir;\n}\n\n\nstatic void def_bundle_logs(FILE* UNUSED(f))\n{\n}\n\n\nstatic const wchar_t* def_translate(const wchar_t* text)\n{\n\treturn text;\n}\n\n\nstatic void def_translate_free(const wchar_t* UNUSED(text))\n{\n\t// no-op - translate() doesn't own the pointer.\n}\n\n\nstatic void def_log(const wchar_t* text)\n{\n#if ICC_VERSION\n#pragma warning(push)\n#pragma warning(disable:181)    // \"invalid printf conversion\" - but wchar_t* and %ls are legit\n#endif\n\tprintf(\"%ls\", text); // must not use wprintf, since stdout on Unix is byte-oriented\n#if ICC_VERSION\n#pragma warning(pop)\n#endif\n}\n\n\nstatic ErrorReactionInternal def_display_error(const wchar_t* UNUSED(text), size_t UNUSED(flags))\n{\n\treturn ERI_NOT_IMPLEMENTED;\n}\n\n\n//-----------------------------------------------------------------------------\n\n// contains the current set of hooks. starts with the default values and\n// may be changed via app_hooks_update.\n//\n// rationale: we don't ever need to switch \"hook sets\", so one global struct\n// is fine. by always having one defined, we also avoid the trampolines\n// having to check whether their function pointer is valid.\nstatic AppHooks ah =\n{\n\tdef_override_gl_upload_caps,\n\tdef_get_log_dir,\n\tdef_bundle_logs,\n\tdef_translate,\n\tdef_translate_free,\n\tdef_log,\n\tdef_display_error\n};\n\n// separate copy of ah; used to determine if a particular hook has been\n// redefined. the additional storage needed is negligible and this is\n// easier than comparing each value against its corresponding def_ value.\nstatic AppHooks default_ah = ah;\n\n// register the specified hook function pointers. any of them that\n// are non-zero override the previous function pointer value\n// (these default to the stub hooks which are functional but basic).\nvoid app_hooks_update(AppHooks* new_ah)\n{\n\tENSURE(new_ah);\n\n#define OVERRIDE_IF_NONZERO(HOOKNAME) if(new_ah->HOOKNAME) ah.HOOKNAME = new_ah->HOOKNAME;\n\tOVERRIDE_IF_NONZERO(override_gl_upload_caps)\n\tOVERRIDE_IF_NONZERO(get_log_dir)\n\tOVERRIDE_IF_NONZERO(bundle_logs)\n\tOVERRIDE_IF_NONZERO(translate)\n\tOVERRIDE_IF_NONZERO(translate_free)\n\tOVERRIDE_IF_NONZERO(log)\n\tOVERRIDE_IF_NONZERO(display_error)\n#undef OVERRIDE_IF_NONZERO\n}\n\nbool app_hook_was_redefined(size_t offset_in_struct)\n{\n\tconst u8* ah_bytes = (const u8*)&ah;\n\tconst u8* default_ah_bytes = (const u8*)&default_ah;\n\ttypedef void(*FP)();\t// a bit safer than comparing void* pointers\n\tif(*(FP)(ah_bytes+offset_in_struct) != *(FP)(default_ah_bytes+offset_in_struct))\n\t\treturn true;\n\treturn false;\n}\n\n\n//-----------------------------------------------------------------------------\n// trampoline implementations\n// (boilerplate code; hides details of how to call the app hook)\n//-----------------------------------------------------------------------------\n\nvoid ah_override_gl_upload_caps()\n{\n\tif(ah.override_gl_upload_caps)\n\t\tah.override_gl_upload_caps();\n}\n\nconst OsPath& ah_get_log_dir()\n{\n\treturn ah.get_log_dir();\n}\n\nvoid ah_bundle_logs(FILE* f)\n{\n\tah.bundle_logs(f);\n}\n\nconst wchar_t* ah_translate(const wchar_t* text)\n{\n\treturn ah.translate(text);\n}\n\nvoid ah_translate_free(const wchar_t* text)\n{\n\tah.translate_free(text);\n}\n\nvoid ah_log(const wchar_t* text)\n{\n\tah.log(text);\n}\n\nErrorReactionInternal ah_display_error(const wchar_t* text, size_t flags)\n{\n\treturn ah.display_error(text, flags);\n}\n"
  },
  {
    "path": "fpsgame/gui/app_hooks.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * hooks to allow customization / app-specific behavior.\n */\n\n/*\n\nBackground\n----------\n\nThis codebase is shared between several projects, each with differing needs.\nSome of them have e.g. complicated i18n/translation facilities and require\nall text output to go through it; others strive to minimize size and\ntherefore do not want to include that.\nSince commenting things out isn't an option with shared source and\nconditional compilation is ugly, we bridge the differences via \"hooks\".\nThese are functions whose behavior is expected to differ between projects;\nthey are defined by the app, registered here and called from lib code via\nour trampolines.\n\n\nIntroduction\n------------\n\nThis module provides a clean interface for other code to call hooks\nand allows the app to register them. It also defines default stub\nimplementations.\n\n\nUsage\n-----\n\nIn the simplest case, the stubs are already acceptable. Otherwise,\nyou need to implement a new version of some hooks, fill an\nAppHooks struct with pointers to those functions (zero the rest),\nand call app_hooks_update.\n\n\nAdding New Functions\n--------------------\n\nSeveral steps are needed (see below for rationale):\n0) HOOKNAME is the name of the desired procedure (e.g. \"bundle_logs\")\n1) add a 'trampoline' (user visible function) declaration to this header\n   (typically named ah_HOOKNAME)\n2) add the corresponding implementation, i.e. call to ah.HOOKNAME\n3) add a default implementation of the new functionality\n   (typically named def_HOOKNAME)\n4) add HOOKNAME member to struct AppHooks declaration\n5) set HOOKNAME member to def_HOOKNAME in initialization of\n   'struct AppHooks ah'\n6) add HOOKNAME to list in app_hooks_update code\n\n\nRationale\n---------\n\nWhile X-Macros would reduce the amount of work needed when adding new\nfunctions, they confuse static code analysis and VisualAssist X\n(the function definitions are not visible to them).\nWe prefer convenience during usage instead of in the rare cases\nwhere new app hook functions are defined.\n\nnote: an X-Macro would define the app hook as such:\nextern const wchar_t*, translate, (const wchar_t* text), (text), return)\n.. and in its various invocations perform the above steps automatically.\n\n*/\n\n#ifndef INCLUDED_APP_HOOKS\n#define INCLUDED_APP_HOOKS\n\n#include \"lib/os_path.h\"\n\n// trampolines for user code to call the hooks. they encapsulate\n// the details of how exactly to do this.\n\n/**\n * override default decision on using OpenGL extensions relating to\n * texture upload.\n *\n * this should call ogl_tex_override to disable/force their use if the\n * current card/driver combo respectively crashes or\n * supports it even though the extension isn't advertised.\n *\n * the default implementation works but is hardwired in code and therefore\n * not expandable.\n **/\nextern void ah_override_gl_upload_caps();\n\n/**\n * return path to directory into which crash dumps should be written.\n *\n * must be callable at any time - in particular, before VFS init.\n * paths are typically relative to sys_ExecutablePathname.\n *\n * @return path ending with directory separator (e.g. '/').\n **/\nextern const OsPath& ah_get_log_dir();\n\n/**\n * gather all app-related logs/information and write it to file.\n *\n * used when writing a crash log so that all relevant info is in one file.\n *\n * the default implementation attempts to gather 0ad data, but is\n * fail-safe (doesn't complain if file not found).\n *\n * @param f file into which to write.\n **/\nextern void ah_bundle_logs(FILE* f);\n\n/**\n * translate text to the current locale.\n *\n * @param text to translate.\n * @return pointer to localized text; must be freed via translate_free.\n *\n * the default implementation just returns the pointer unchanged.\n **/\nextern const wchar_t* ah_translate(const wchar_t* text);\n\n/**\n * free text that was returned by translate.\n *\n * @param text to free.\n *\n * the default implementation does nothing.\n **/\nextern void ah_translate_free(const wchar_t* text);\n\n/**\n * write text to the app's log.\n *\n * @param text to write.\n *\n * the default implementation uses stdout.\n **/\nextern void ah_log(const wchar_t* text);\n\n/**\n * display an error dialog, thus overriding sys_display_error.\n *\n * @param text error message.\n * @param flags see DebugDisplayErrorFlags.\n * @return ErrorReactionInternal.\n *\n * the default implementation just returns ERI_NOT_IMPLEMENTED, which\n * causes the normal sys_display_error to be used.\n **/\nextern ErrorReactionInternal ah_display_error(const wchar_t* text, size_t flags);\n\n\n/**\n * holds a function pointer (allowed to be NULL) for each hook.\n * passed to app_hooks_update.\n **/\nstruct AppHooks\n{\n\tvoid (*override_gl_upload_caps)();\n\tconst OsPath& (*get_log_dir)();\n\tvoid (*bundle_logs)(FILE* f);\n\tconst wchar_t* (*translate)(const wchar_t* text);\n\tvoid (*translate_free)(const wchar_t* text);\n\tvoid (*log)(const wchar_t* text);\n\tErrorReactionInternal (*display_error)(const wchar_t* text, size_t flags);\n};\n\n/**\n * update the app hook function pointers.\n *\n * @param ah AppHooks struct. any of its function pointers that are non-zero\n * override the previous function pointer value\n * (these default to the stub hooks which are functional but basic).\n **/\nLIB_API void app_hooks_update(AppHooks* ah);\n\n/**\n * was the app hook changed via app_hooks_update from its default value?\n *\n * @param offset_in_struct byte offset within AppHooks (determined via\n * offsetof) of the app hook function pointer.\n **/\nextern bool app_hook_was_redefined(size_t offset_in_struct);\n// name is identifier of the function pointer within AppHooks to test.\n#define AH_IS_DEFINED(name) app_hook_was_redefined(offsetof(AppHooks, name))\n\n#endif\t// #ifndef INCLUDED_APP_HOOKS\n"
  },
  {
    "path": "fpsgame/gui/base32.cpp",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * base32 conversion\n */\n\n#include \"precompiled.h\"\n\n\n// big endian!\nvoid base32(const size_t in_len, const u8* in, u8* out)\n{\n\tu32 pool = 0;\t// of bits from buffer\n\tssize_t pool_bits = 0;\t// # bits currently in buffer\n\n\tstatic const u8 tbl[33] = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567\";\n\n\tsize_t in_bytes_left = in_len;\t// to avoid overrunning input buffer\n\tconst size_t out_chars = (in_len*8 + 4) / 5;\t// = ceil(# 5-bit blocks)\n\tfor(size_t i = 0; i < out_chars; i++)\n\t{\n\t\tif(pool_bits < 5 && in_bytes_left)\n\t\t{\n\t\t\tpool <<= 8;\n\t\t\tpool |= *in++;\n\t\t\tpool_bits += 8;\n\t\t\tin_bytes_left--;\n\t\t}\n\n\t\tpool_bits -= 5;\n\t\tsize_t c;\n\t\tif (pool_bits < 0)\n\t\t\tc = (pool << -pool_bits) & 31;\n\t\telse\n\t\t\tc = (pool >> pool_bits) & 31;\n\t\t*out++ = tbl[c];\n\t}\n\n\t*out++ = '\\0';\n}\n"
  },
  {
    "path": "fpsgame/gui/base32.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * base32 conversion\n */\n\n#ifndef INCLUDED_BASE32\n#define INCLUDED_BASE32\n\n/**\n * generate the base32 textual representation of a buffer.\n *\n * @param len Size [bytes] of input\n * @param in Big-endian input data (assumed to be integral number of bytes)\n * @param out Output string; zero-terminated. must be big enough\n * (i.e. at least ceil(len*CHAR_BIT/5) + 1 chars)\n **/\nextern void base32(const size_t len, const u8* in, u8* out);\n\n#endif\t// #ifndef INCLUDED_BASE32\n"
  },
  {
    "path": "fpsgame/gui/bits.cpp",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * bit-twiddling.\n */\n\n#include \"precompiled.h\"\n#include \"lib/bits.h\"\n\nstatic inline u32 get_float_bits(const float x)\n{\n\tu32 ret;\n\tmemcpy(&ret, &x, 4);\n\treturn ret;\n}\n\nint floor_log2(const float x)\n{\n\tconst u32 i = get_float_bits(x);\n\tconst u32 biased_exp = (i >> 23) & 0xFF;\n\treturn (int)biased_exp - 127;\n}\n"
  },
  {
    "path": "fpsgame/gui/bits.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * bit-twiddling.\n */\n\n#ifndef INCLUDED_BITS\n#define INCLUDED_BITS\n\n/**\n * value of bit number \\<n\\>.\n *\n * @param n bit index.\n *\n * requirements:\n * - T should be an unsigned type\n * - n must be in [0, CHAR_BIT*sizeof(T)), else the result is undefined!\n **/\ntemplate<typename T>\ninline T Bit(size_t n)\n{\n\tconst T one = T(1);\n\treturn (T)(one << n);\n}\n\n/**\n * pretty much the same as Bit\\<unsigned\\>.\n * this is intended for the initialization of enum values, where a\n * compile-time constant is required.\n **/\n#define BIT(n) (1u << (n))\n\ntemplate<typename T>\ninline bool IsBitSet(T value, size_t index)\n{\n\tconst T bit = Bit<T>(index);\n\treturn (value & bit) != 0;\n}\n\n\n// these are declared in the header and inlined to aid compiler optimizations\n// (they can easily end up being time-critical).\n// note: GCC can't inline extern functions, while VC's \"Whole Program\n// Optimization\" can.\n\n/**\n * a mask that includes the lowest N bits\n *\n * @param numBits Number of bits in mask.\n **/\ntemplate<typename T>\ninline T bit_mask(size_t numBits)\n{\n\tconst T bitsInT = sizeof(T)*CHAR_BIT;\n\tconst T allBits = (T)~T(0);\n\t// (shifts of at least bitsInT are undefined)\n\tif(numBits >= bitsInT)\n\t\treturn allBits;\n\t// (note: the previous allBits >> (bitsInT-numBits) is not safe\n\t// because right-shifts of negative numbers are undefined.)\n\tconst T mask = (T)((T(1) << numBits)-1);\n\treturn mask;\n}\n\n\n/**\n * extract the value of bits hi_idx:lo_idx within num\n *\n * example: bits(0x69, 2, 5) == 0x0A\n *\n * @param num number whose bits are to be extracted\n * @param lo_idx bit index of lowest  bit to include\n * @param hi_idx bit index of highest bit to include\n * @return value of extracted bits.\n **/\ntemplate<typename T>\ninline T bits(T num, size_t lo_idx, size_t hi_idx)\n{\n\tconst size_t numBits = (hi_idx - lo_idx)+1;\t// # bits to return\n\tT result = T(num >> lo_idx);\n\tresult = T(result & bit_mask<T>(numBits));\n\treturn result;\n}\n\n/**\n * set the value of bits hi_idx:lo_idx\n *\n * @param lo_idx bit index of lowest  bit to include\n * @param hi_idx bit index of highest bit to include\n * @param value new value to be assigned to these bits\n **/\ntemplate<typename T>\ninline T SetBitsTo(T num, size_t lo_idx, size_t hi_idx, size_t value)\n{\n\tconst size_t numBits = (hi_idx - lo_idx)+1;\n\tASSERT(value < (T(1) << numBits));\n\tconst T mask = bit_mask<T>(numBits) << lo_idx;\n\tT result = num & ~mask;\n\tresult = T(result | (value << lo_idx));\n\treturn result;\n}\n\n\n/**\n * @return number of 1-bits in mask.\n * execution time is proportional to number of 1-bits in mask.\n **/\ntemplate<typename T>\ninline size_t SparsePopulationCount(T mask)\n{\n\tsize_t num1Bits = 0;\n\twhile(mask)\n\t{\n\t\tmask &= mask-1; // clear least significant 1-bit\n\t\tnum1Bits++;\n\t}\n\n\treturn num1Bits;\n}\n\n/**\n * @return number of 1-bits in mask.\n * execution time is logarithmic in the total number of bits.\n * supports up to 128-bit integers (if their arithmetic operators are defined).\n * [http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel]\n **/\ntemplate<typename T>\nstatic inline size_t PopulationCount(T x)\n{\n\tcassert(!std::numeric_limits<T>::is_signed);\n\tconst T mask = T(~T(0));\n\tx -= (x >> 1) & (mask/3);\t// count 2 bits\n\tx = (x & (mask/15*3)) + ((x >> 2) & (mask/15*3));\t// count 4 bits\n\tx = (x + (x >> 4)) & (mask/255*15);\t// count 8 bits\n\treturn T(x * (mask/255)) >> ((sizeof(T)-1)*CHAR_BIT);\n}\n\n\n\n/**\n * @return whether the given number is a power of two.\n **/\ntemplate<typename T>\ninline bool is_pow2(T n)\n{\n\t// 0 would pass the test below but isn't a POT.\n\tif(n == 0)\n\t\treturn false;\n\treturn (n & (n-1)) == 0;\n}\n\n// as above; intended for use in static_assert\n#define IS_POW2(n) (((n) != 0) && ((n) & ((n)-1)) == 0)\n\ntemplate<typename T>\ninline T LeastSignificantBit(T x)\n{\n\tconst T negX = T(~x + 1);\t// 2's complement (avoids 'negating unsigned type' warning)\n\treturn x & negX;\n}\n\ntemplate<typename T>\ninline T ClearLeastSignificantBit(T x)\n{\n\treturn x & (x-1);\n}\n\n\n/**\n * ceil(log2(x))\n *\n * @param x (unsigned integer)\n * @return ceiling of the base-2 logarithm (i.e. rounded up) or\n * zero if the input is zero.\n **/\ntemplate<typename T>\ninline size_t ceil_log2(T x)\n{\n\tT bit = 1;\n\tsize_t log = 0;\n\twhile(bit < x && bit != 0)\t// must detect overflow\n\t{\n\t\tlog++;\n\t\tbit *= 2;\n\t}\n\n\treturn log;\n}\n\n// compile-time variant of the above\ntemplate<size_t N>\nstruct CeilLog2\n{\n\tenum { value = 1 + CeilLog2<(N+1)/2>::value };\n};\n\ntemplate<>\nstruct CeilLog2<1>\n{\n\tenum { value = 0 };\n};\n\ntemplate<>\nstruct CeilLog2<0>\n{\n\tenum { value = 0 };\n};\n\n\n\n/**\n * floor(log2(f))\n * fast, uses the FPU normalization hardware.\n *\n * @param x (float) input; MUST be > 0, else results are undefined.\n * @return floor of the base-2 logarithm (i.e. rounded down).\n **/\nextern int floor_log2(const float x);\n\n/**\n * round up to next larger power of two.\n **/\ntemplate<typename T>\ninline T round_up_to_pow2(T x)\n{\n\treturn T(1) << ceil_log2(x);\n}\n\n/**\n * round down to next larger power of two.\n **/\ntemplate<typename T>\ninline T round_down_to_pow2(T x)\n{\n\treturn T(1) << floor_log2(x);\n}\n\n/**\n * round number up/down to the next given multiple.\n *\n * @param n Number to round.\n * @param multiple Must be a power of two.\n **/\ntemplate<typename T>\ninline T round_up(T n, T multiple)\n{\n\tASSERT(is_pow2(multiple));\n\tconst T result = (n + multiple-1) & ~(multiple-1);\n\tASSERT(n <= result && result < n+multiple);\n\treturn result;\n}\n\ntemplate<typename T>\ninline T round_down(T n, T multiple)\n{\n\tASSERT(is_pow2(multiple));\n\tconst T result = n & ~(multiple-1);\n\tASSERT(result <= n && n < result+multiple);\n\treturn result;\n}\n\n// evaluates to an expression suitable as an initializer\n// for constant static data members.\n#define ROUND_UP(n, multiple) (((n) + (multiple)-1) & ~((multiple)-1))\n\n\ntemplate<typename T>\ninline T MaxPowerOfTwoDivisor(T value)\n{\n\tASSERT(value != T(0));\n\n\tfor(size_t log2 = 0; log2 < sizeof(T)*CHAR_BIT; log2++)\n\t{\n\t\tif(IsBitSet(value, log2))\n\t\t\treturn T(1) << log2;\n\t}\n\n\tDEBUG_WARN_ERR(ERR::LOGIC);\t// unreachable (!= 0 => there is a set bit)\n\treturn 0;\n}\n\n#endif\t// #ifndef INCLUDED_BITS\n"
  },
  {
    "path": "fpsgame/gui/byte_order.cpp",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * byte order (endianness) support routines.\n */\n\n#include \"precompiled.h\"\n#include \"lib/byte_order.h\"\n\n#include \"lib/bits.h\"\n\n#ifndef swap16\nu16 swap16(const u16 x)\n{\n\treturn (u16)(((x & 0xff) << 8) | (x >> 8));\n}\n#endif\n\n#ifndef swap32\nu32 swap32(const u32 x)\n{\n\treturn (x << 24) |\n\t\t(x >> 24) |\n\t\t((x << 8) & 0x00ff0000) |\n\t\t((x >> 8) & 0x0000ff00);\n}\n#endif\n\n#ifndef swap64\nu64 swap64(const u64 x)\n{\n\tconst u32 lo = (u32)(x & 0xFFFFFFFF);\n\tconst u32 hi = (u32)(x >> 32);\n\tu64 ret = swap32(lo);\n\tret <<= 32;\n\t// careful: must shift var of type u64, not u32\n\tret |= swap32(hi);\n\treturn ret;\n}\n#endif\n\n\n//-----------------------------------------------------------------------------\n\n\nu16 read_le16(const void* p)\n{\n\tu16 n;\n\tmemcpy(&n, p, sizeof(n));\n\treturn to_le16(n);\n}\n\nu32 read_le32(const void* p)\n{\n\tu32 n;\n\tmemcpy(&n, p, sizeof(n));\n\treturn to_le32(n);\n}\n\nu64 read_le64(const void* p)\n{\n\tu64 n;\n\tmemcpy(&n, p, sizeof(n));\n\treturn to_le64(n);\n}\n\n\nu16 read_be16(const void* p)\n{\n\tu16 n;\n\tmemcpy(&n, p, sizeof(n));\n\treturn to_be16(n);\n}\n\nu32 read_be32(const void* p)\n{\n\tu32 n;\n\tmemcpy(&n, p, sizeof(n));\n\treturn to_be32(n);\n}\n\nu64 read_be64(const void* p)\n{\n\tu64 n;\n\tmemcpy(&n, p, sizeof(n));\n\treturn to_be64(n);\n}\n\n\nvoid write_le16(void* p, u16 x)\n{\n\tu16 n = to_le16(x);\n\tmemcpy(p, &n, sizeof(n));\n}\n\nvoid write_le32(void* p, u32 x)\n{\n\tu32 n = to_le32(x);\n\tmemcpy(p, &n, sizeof(n));\n}\n\nvoid write_le64(void* p, u64 x)\n{\n\tu64 n = to_le64(x);\n\tmemcpy(p, &n, sizeof(n));\n}\n\n\nvoid write_be16(void* p, u16 x)\n{\n\tu16 n = to_be16(x);\n\tmemcpy(p, &n, sizeof(n));\n}\n\nvoid write_be32(void* p, u32 x)\n{\n\tu32 n = to_be32(x);\n\tmemcpy(p, &n, sizeof(n));\n}\n\nvoid write_be64(void* p, u64 x)\n{\n\tu64 n = to_be64(x);\n\tmemcpy(p, &n, sizeof(n));\n}\n\n\nu64 movzx_le64(const u8* p, size_t size_bytes)\n{\n\tu64 number = 0;\n\tfor(size_t i = 0; i < std::min(size_bytes, (size_t)8u); i++)\n\t\tnumber |= ((u64)p[i]) << (i*8);\n\n\treturn number;\n}\n\nu64 movzx_be64(const u8* p, size_t size_bytes)\n{\n\tu64 number = 0;\n\tfor(size_t i = 0; i < std::min(size_bytes, (size_t)8u); i++)\n\t{\n\t\tnumber <<= 8;\n\t\tnumber |= p[i];\n\t}\n\n\treturn number;\n}\n\n\nstatic inline i64 SignExtend(u64 bits, size_t size_bytes)\n{\n\t// no point in sign-extending if >= 8 bytes were requested\n\tif(size_bytes < 8)\n\t{\n\t\tconst u64 sign_bit = Bit<u64>((size_bytes*8)-1);\n\n\t\t// number would be negative in the smaller type,\n\t\t// so sign-extend, i.e. set all more significant bits.\n\t\tif(bits & sign_bit)\n\t\t{\n\t\t\tconst u64 valid_bit_mask = (sign_bit+sign_bit)-1;\n\t\t\tbits |= ~valid_bit_mask;\n\t\t}\n\t}\n\n\tconst i64 number = static_cast<i64>(bits);\n\treturn number;\n}\n\ni64 movsx_le64(const u8* p, size_t size_bytes)\n{\n\tconst u64 number = movzx_le64(p, size_bytes);\n\treturn SignExtend(number, size_bytes);\n}\n\ni64 movsx_be64(const u8* p, size_t size_bytes)\n{\n\tconst u64 number = movzx_be64(p, size_bytes);\n\treturn SignExtend(number, size_bytes);\n}\n"
  },
  {
    "path": "fpsgame/gui/byte_order.h",
    "content": "/* Copyright (c) 2015 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * byte order (endianness) support routines.\n */\n\n#ifndef INCLUDED_BYTE_ORDER\n#define INCLUDED_BYTE_ORDER\n\n#include \"lib/sysdep/cpu.h\"\n\n// detect byte order via predefined macros.\n#ifndef BYTE_ORDER\n# define LITTLE_ENDIAN 0x4321\n# define BIG_ENDIAN    0x1234\n# if ARCH_IA32 || ARCH_IA64 || ARCH_AMD64 || ARCH_ALPHA || ARCH_ARM || ARCH_AARCH64 || ARCH_MIPS || defined(__LITTLE_ENDIAN__)\n#  define BYTE_ORDER LITTLE_ENDIAN\n# else\n#  define BYTE_ORDER BIG_ENDIAN\n# endif\n#endif\n\n\n/**\n * convert 4 characters to u32 (at compile time) for easy comparison.\n * output is in native byte order; e.g. FOURCC_LE can be used instead.\n **/\n#define FOURCC(a,b,c,d)\t// real definition is below\n#undef  FOURCC\n\n// implementation rationale:\n// - can't pass code as string, and use s[0]..s[3], because\n//   VC6/7 don't realize the macro is constant\n//   (it should be usable as a switch{} expression)\n// - the casts are ugly but necessary. u32 is required because u8 << 8 == 0;\n//   the additional u8 cast ensures each character is treated as unsigned\n//   (otherwise, they'd be promoted to signed int before the u32 cast,\n//   which would break things).\n\n/// big-endian version of FOURCC\n#define FOURCC_BE(a,b,c,d) ( ((u32)(u8)a) << 24 | ((u32)(u8)b) << 16 | \\\n\t((u32)(u8)c) << 8  | ((u32)(u8)d) << 0  )\n\n/// little-endian version of FOURCC\n#define FOURCC_LE(a,b,c,d) ( ((u32)(u8)a) << 0  | ((u32)(u8)b) << 8  | \\\n\t((u32)(u8)c) << 16 | ((u32)(u8)d) << 24 )\n\n#if BYTE_ORDER == BIG_ENDIAN\n# define FOURCC FOURCC_BE\n#else\n# define FOURCC FOURCC_LE\n#endif\n\n\n#if BYTE_ORDER == BIG_ENDIAN\n// convert a little-endian number to/from native byte order.\n# define to_le16(x) swap16(x)\n# define to_le32(x) swap32(x)\n# define to_le64(x) swap64(x)\n// convert a big-endian number to/from native byte order.\n# define to_be16(x) (x)\n# define to_be32(x) (x)\n# define to_be64(x) (x)\n#else // LITTLE_ENDIAN\n// convert a little-endian number to/from native byte order.\n# define to_le16(x) (x)\n# define to_le32(x) (x)\n# define to_le64(x) (x)\n// convert a big-endian number to/from native byte order.\n# define to_be16(x) swap16(x)\n# define to_be32(x) swap32(x)\n# define to_be64(x) swap64(x)\n#endif\n\n/// read a little-endian number from memory into native byte order.\nLIB_API u16 read_le16(const void* p);\nLIB_API u32 read_le32(const void* p);\t/// see read_le16\nLIB_API u64 read_le64(const void* p);\t/// see read_le16\n\n/// read a big-endian number from memory into native byte order.\nLIB_API u16 read_be16(const void* p);\nLIB_API u32 read_be32(const void* p);\t/// see read_be16\nLIB_API u64 read_be64(const void* p);\t/// see read_be16\n\n/// write a little-endian number to memory in native byte order.\nLIB_API void write_le16(void* p, u16 x);\nLIB_API void write_le32(void* p, u32 x);\t/// see write_le16\nLIB_API void write_le64(void* p, u64 x);\t/// see write_le16\n\n/// write a big-endian number to memory in native byte order.\nLIB_API void write_be16(void* p, u16 x);\nLIB_API void write_be32(void* p, u32 x);\t/// see write_be16\nLIB_API void write_be64(void* p, u64 x);\t/// see write_be16\n\n/**\n * zero-extend \\<size\\> (truncated to 8) bytes of little-endian data to u64,\n * starting at address \\<p\\> (need not be aligned).\n **/\nLIB_API u64 movzx_le64(const u8* p, size_t size);\nLIB_API u64 movzx_be64(const u8* p, size_t size);\n\n/**\n * sign-extend \\<size\\> (truncated to 8) bytes of little-endian data to i64,\n * starting at address \\<p\\> (need not be aligned).\n **/\nLIB_API i64 movsx_le64(const u8* p, size_t size);\nLIB_API i64 movsx_be64(const u8* p, size_t size);\n\n\n#if ICC_VERSION\n#define swap32 _bswap\n#define swap64 _bswap64\n#elif MSC_VERSION\nextern unsigned short _byteswap_ushort(unsigned short);\nextern unsigned long _byteswap_ulong(unsigned long);\nextern unsigned __int64 _byteswap_uint64(unsigned __int64);\n#pragma intrinsic(_byteswap_ushort)\n#pragma intrinsic(_byteswap_ulong)\n#pragma intrinsic(_byteswap_uint64)\n# define swap16 _byteswap_ushort\n# define swap32 _byteswap_ulong\n# define swap64 _byteswap_uint64\n#elif defined(linux)\n# include <asm/byteorder.h>\n# if defined(__arch__swab16) && !defined(swap16)\n#  define swap16 __arch__swab16\n# endif\n# if defined(__arch__swab32) && !defined(swap32)\n#  define swap32 __arch__swab32\n# endif\n# if defined(__arch__swab64) && !defined(swap64)\n#  define swap64 __arch__swab64\n# endif\n#endif\n\n#ifndef swap16\nLIB_API u16 swap16(const u16 x);\n#endif\n#ifndef swap32\nLIB_API u32 swap32(const u32 x);\n#endif\n#ifndef swap64\nLIB_API u64 swap64(const u64 x);\n#endif\n\n#endif\t// #ifndef INCLUDED_BYTE_ORDER\n"
  },
  {
    "path": "fpsgame/gui/code_annotation.h",
    "content": "/* Copyright (c) 2015 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * macros for code annotation.\n */\n\n#ifndef INCLUDED_CODE_ANNOTATION\n#define INCLUDED_CODE_ANNOTATION\n\n#include \"lib/sysdep/compiler.h\"\n#include \"lib/sysdep/arch.h\"\t// ARCH_AMD64\n\n/**\n * mark a function parameter as unused and avoid\n * the corresponding compiler warning.\n * wrap around the parameter name, e.g. void f(int UNUSED(x))\n **/\n#define UNUSED(param)\n\n/**\n * mark a function local variable or parameter as unused and avoid\n * the corresponding compiler warning.\n * note that UNUSED is not applicable to variable definitions that\n * involve initialization, nor is it sufficient in cases where\n * an argument is unused only in certain situations.\n * example: void f(int x) { ASSERT(x == 0); UNUSED2(x); }\n * this asserts in debug builds and avoids warnings in release.\n **/\n#if HAVE_C99 && GCC_VERSION\t// _Pragma from C99, unused from GCC\n# define UNUSED2(param) _Pragma(\"unused \" #param)\n#elif ICC_VERSION\n// ICC 12 still doesn't recognize pragma unused, casting to void\n// isn't sufficient, and self-assignment doesn't work for references.\n# define UNUSED2(param) do{ if(&param) {} } while(false)\n#else\n# define UNUSED2(param) ((void)(param))\n#endif\n\n\n/**\n * indicate a function will not throw any synchronous exceptions,\n * thus hopefully generating smaller and more efficient code.\n *\n * must be placed BEFORE return types because \"The [VC++] compiler\n * ignores, without warning, any __declspec keywords placed after *\".\n * such syntax is apparently also legal in GCC, per the example\n * \"__attribute__((noreturn)) void d0 (void)\".\n *\n * example:\n * NOTHROW_DECLARE void function();\n * NOTHROW_DEFINE void function() {}\n **/\n#if GCC_VERSION\n# define NOTHROW_DECLARE __attribute__((nothrow))\n# define NOTHROW_DEFINE\t// not supported for definitions\n#elif MSC_VERSION\n// Kevin Frei, 2006-03-23: \"I work on the Visual C++ compiler team,\n// and agree completely with Paul Parks: don't use throw(), because\n// there's a chance that we'll eventually implement it according to the standard\".\n# define NOTHROW_DECLARE __declspec(nothrow)\n# define NOTHROW_DEFINE\t__declspec(nothrow)\n#else\n// don't use throw() because it might result in ADDITIONAL checks\n// (the standard mandates calling unexpected())\n# define NOTHROW_DECLARE\n# define NOTHROW_DEFINE\n#endif\n\n\n/**\n * mark a function as noreturn for static analyzer purposes.\n * currently only for clang-analyzer.\n */\n#if __has_feature(attribute_analyzer_noreturn)\n# define ANALYZER_NORETURN __attribute__((analyzer_noreturn))\n#else\n# define ANALYZER_NORETURN\n#endif\n\n\n/**\n * \"unreachable code\" helpers\n *\n * unreachable lines of code are often the source or symptom of subtle bugs.\n * they are flagged by compiler warnings; however, the opposite problem -\n * erroneously reaching certain spots (e.g. due to missing return statement)\n * is worse and not detected automatically.\n *\n * to defend against this, the programmer can annotate their code to\n * indicate to humans that a particular spot should never be reached.\n * however, that isn't much help; better is a sentinel that raises an\n * error if if it is actually reached. hence, the UNREACHABLE macro.\n *\n * ironically, if the code guarded by UNREACHABLE works as it should,\n * compilers may flag the macro's code as unreachable. this would\n * distract from genuine warnings, which is unacceptable.\n *\n * even worse, compilers differ in their code checking: GCC only complains if\n * non-void functions end without returning a value (i.e. missing return\n * statement), while VC checks if lines are unreachable (e.g. if they are\n * preceded by a return on all paths).\n *\n * the implementation below enables optimization and automated checking\n * without raising warnings.\n **/\n#define UNREACHABLE\t// actually defined below.. this is for\n# undef UNREACHABLE\t// CppDoc's benefit only.\n\n// this macro should not generate any fallback code; it is merely the\n// compiler-specific backend for UNREACHABLE.\n// #define it to nothing if the compiler doesn't support such a hint.\n#define HAVE_ASSUME_UNREACHABLE 1\n#if MSC_VERSION && !ICC_VERSION // (ICC ignores this)\n# define ASSUME_UNREACHABLE __assume(0)\n#elif GCC_VERSION\n# define ASSUME_UNREACHABLE __builtin_unreachable()\n#else\n# define ASSUME_UNREACHABLE\n# undef HAVE_ASSUME_UNREACHABLE\n# define HAVE_ASSUME_UNREACHABLE 0\n#endif\n\n// compiler supports ASSUME_UNREACHABLE => allow it to assume the code is\n// never reached (improves optimization at the cost of undefined behavior\n// if the annotation turns out to be incorrect).\n#if HAVE_ASSUME_UNREACHABLE && !CONFIG_ENABLE_CHECKS\n# define UNREACHABLE ASSUME_UNREACHABLE\n// otherwise (or if CONFIG_ENABLE_CHECKS is set), add a user-visible\n// warning if the code is reached. note that abort() fails to stop\n// ICC from warning about the lack of a return statement, so we\n// use an infinite loop instead.\n#else\n# define UNREACHABLE\\\n\tSTMT(\\\n\t\tDEBUG_WARN_ERR(ERR::LOGIC);\t/* hit supposedly unreachable code */\\\n\t\tfor(;;){};\\\n\t)\n#endif\n\n/**\nconvenient specialization of UNREACHABLE for switch statements whose\ndefault can never be reached. example usage:\nint x;\nswitch(x % 2)\n{\n\tcase 0: break;\n\tcase 1: break;\n\tNODEFAULT;\n}\n**/\n#define NODEFAULT default: UNREACHABLE\n\n\n// generate a symbol containing the line number of the macro invocation.\n// used to give a unique name (per file) to types or variables.\n// we can't prepend __FILE__ to make it globally unique - the filename\n// may be enclosed in quotes. PASTE3_HIDDEN__ is needed to make sure\n// __LINE__ is expanded correctly.\n#define PASTE3_HIDDEN__(a, b, c) a ## b ## c\n#define PASTE3__(a, b, c) PASTE3_HIDDEN__(a, b, c)\n#define UID__  PASTE3__(LINE_, __LINE__, _)\n#define UID2__ PASTE3__(LINE_, __LINE__, _2)\n\n\n//-----------------------------------------------------------------------------\n// cassert\n\n/**\n * Compile-time assertion. Causes a compile error if the expression\n * evaluates to zero/false.\n *\n * No runtime overhead; may be used anywhere, including file scope.\n * Especially useful for testing sizeof types.\n *\n * @param expr Expression that is expected to evaluate to non-zero at compile-time.\n **/\n#define cassert(expr) static_assert((expr), #expr)\n\n\n/**\n * Indicates that a class is noncopyable (usually due to const or reference\n * members, or because the class works as a singleton).\n *\n * For example:\n *\n * @code\n * class ClassName {\n *   NONCOPYABLE(ClassName);\n * public: // etc.\n * };\n * @endcode\n *\n * This is preferable to inheritance from boost::noncopyable because it avoids\n * ICC 11 W4 warnings about non-virtual dtors and suppression of the copy\n * assignment operator.\n */\n#define NONCOPYABLE(className)\\\n\tpublic:\\\n\t\tclassName(const className&) = delete;\\\n\t\tconst className& operator=(const className&) = delete;\\\n\tprivate:\n\n#if ICC_VERSION\n# define ASSUME_ALIGNED(ptr, multiple) __assume_aligned(ptr, multiple)\n#else\n# define ASSUME_ALIGNED(ptr, multiple)\n#endif\n\n// annotate printf-style functions for compile-time type checking.\n// fmtpos is the index of the format argument, counting from 1 or\n// (if it's a non-static class function) 2; the '...' is assumed\n// to come directly after it.\n#if GCC_VERSION\n# define PRINTF_ARGS(fmtpos) __attribute__ ((format (printf, fmtpos, fmtpos+1)))\n# define VPRINTF_ARGS(fmtpos) __attribute__ ((format (printf, fmtpos, 0)))\n# if CONFIG_DEHYDRA\n#  define WPRINTF_ARGS(fmtpos) __attribute__ ((user(\"format, w, printf, \" #fmtpos \", +1\")))\n# else\n#  define WPRINTF_ARGS(fmtpos) /* not currently supported in GCC */\n# endif\n# define VWPRINTF_ARGS(fmtpos) /* not currently supported in GCC */\n#else\n# define PRINTF_ARGS(fmtpos)\n# define VPRINTF_ARGS(fmtpos)\n# define WPRINTF_ARGS(fmtpos)\n# define VWPRINTF_ARGS(fmtpos)\n// TODO: support _Printf_format_string_ for VC9+\n#endif\n\n// annotate vararg functions that expect to end with an explicit NULL\n#if GCC_VERSION\n# define SENTINEL_ARG __attribute__ ((sentinel))\n#else\n# define SENTINEL_ARG\n#endif\n\n/**\n * prevent the compiler from reordering loads or stores across this point.\n **/\n#if ICC_VERSION\n# define COMPILER_FENCE __memory_barrier()\n#elif MSC_VERSION\n# include <intrin.h>\n# pragma intrinsic(_ReadWriteBarrier)\n# define COMPILER_FENCE _ReadWriteBarrier()\n#elif GCC_VERSION\n# define COMPILER_FENCE asm volatile(\"\" : : : \"memory\")\n#else\n# define COMPILER_FENCE\n#endif\n\n\n// try to define _W64, if not already done\n// (this is useful for catching pointer size bugs)\n#ifndef _W64\n# if MSC_VERSION\n#  define _W64 __w64\n# elif GCC_VERSION\n#  define _W64 __attribute__((mode (__pointer__)))\n# else\n#  define _W64\n# endif\n#endif\n\n\n// C99-like restrict (non-standard in C++, but widely supported in various forms).\n//\n// May be used on pointers. May also be used on member functions to indicate\n// that 'this' is unaliased (e.g. \"void C::m() RESTRICT { ... }\").\n// Must not be used on references - GCC supports that but VC doesn't.\n//\n// We call this \"RESTRICT\" to avoid conflicts with VC's __declspec(restrict),\n// and because it's not really the same as C99's restrict.\n//\n// To be safe and satisfy the compilers' stated requirements: an object accessed\n// by a restricted pointer must not be accessed by any other pointer within the\n// lifetime of the restricted pointer, if the object is modified.\n// To maximise the chance of optimisation, any pointers that could potentially\n// alias with the restricted one should be marked as restricted too.\n//\n// It would probably be a good idea to write test cases for any code that uses\n// this in an even very slightly unclear way, in case it causes obscure problems\n// in a rare compiler due to differing semantics.\n//\n// .. GCC\n#if GCC_VERSION\n# define RESTRICT __restrict__\n// .. VC8 provides __restrict\n#elif MSC_VERSION\n# define RESTRICT __restrict\n// .. ICC supports the keyword 'restrict' when run with the /Qrestrict option,\n//    but it always also supports __restrict__ or __restrict to be compatible\n//    with GCC/MSVC, so we'll use the underscored version. One of {GCC,MSC}_VERSION\n//    should have been defined in addition to ICC_VERSION, so we should be using\n//    one of the above cases (unless it's an old VS7.1-emulating ICC).\n#elif ICC_VERSION\n# error ICC_VERSION defined without either GCC_VERSION or an adequate MSC_VERSION\n// .. unsupported; remove it from code\n#else\n# define RESTRICT\n#endif\n\n\n//\n// number of array elements\n//\n\n// (function taking a reference to an array and returning a pointer to\n// an array of characters. it's only declared and never defined; we just\n// need it to determine n, the size of the array that was passed.)\ntemplate<typename T, size_t n> char (*ArraySizeDeducer(T (&)[n]))[n];\n\n// (although requiring C++, this method is much better than the standard\n// sizeof(name) / sizeof(name[0]) because it doesn't compile when a\n// pointer is passed, which can easily happen under maintenance.)\n#define ARRAY_SIZE(name) (sizeof(*ArraySizeDeducer(name)))\n\n\n// C99-style __func__\n// .. newer GCC already have it\n#if GCC_VERSION\n// nothing need be done\n// .. MSVC have __FUNCTION__\n#elif MSC_VERSION\n# define __func__ __FUNCTION__\n// .. unsupported\n#else\n# define __func__ \"(unknown)\"\n#endif\n\n\n// extern \"C\", but does the right thing in pure-C mode\n#if defined(__cplusplus)\n# define EXTERN_C extern \"C\"\n#else\n# define EXTERN_C extern\n#endif\n\n\n#if MSC_VERSION\n# define INLINE __forceinline\n#else\n# define INLINE inline\n#endif\n\n\n#if MSC_VERSION\n# define CALL_CONV __cdecl\n#else\n# define CALL_CONV\n#endif\n\n\n#if MSC_VERSION && !ARCH_AMD64\n# define DECORATED_NAME(name) _##name\n#else\n# define DECORATED_NAME(name) name\n#endif\n\n\n// workaround for preprocessor limitation: macro args aren't expanded\n// before being pasted.\n#define STRINGIZE2(id) # id\n#define STRINGIZE(id) STRINGIZE2(id)\n\n// for widening non-literals (e.g. __FILE__)\n// note: C99 says __func__ is a magic *variable*, and GCC doesn't allow\n// widening it via preprocessor.\n#define WIDEN2(x) L ## x\n#define WIDEN(x) WIDEN2(x)\n\n#endif\t// #ifndef INCLUDED_CODE_ANNOTATION\n"
  },
  {
    "path": "fpsgame/gui/code_generation.h",
    "content": "/* Copyright (c) 2013 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#ifndef INCLUDED_CODE_GENERATION\n#define INCLUDED_CODE_GENERATION\n\n/**\n * package code into a single statement.\n *\n * @param STMT_code__ code to be bundled. (must be interpretable as\n * a macro argument, i.e. sequence of tokens).\n * the argument name is chosen to avoid conflicts.\n *\n * notes:\n * - for(;;) { break; } and {} don't work because invocations of macros\n *   implemented with STMT often end with \";\", thus breaking if() expressions.\n * - we'd really like to eliminate \"conditional expression is constant\"\n *   warnings. replacing 0 literals with extern volatile variables fools\n *   VC7 but isn't guaranteed to be free of overhead. we will just\n *   squelch the warning (unfortunately non-portable).\n **/\n#define STMT(STMT_code__) do { STMT_code__; } while(false)\n\n/**\n * execute the code passed as a parameter only the first time this is\n * reached.\n * may be called at any time (in particular before main), but is not\n * thread-safe. if that's important, use pthread_once() instead.\n **/\n#define ONCE(ONCE_code__)\\\nSTMT(\\\n\tstatic bool ONCE_done__ = false;\\\n\tif(!ONCE_done__)\\\n\t{\\\n\t\tONCE_done__ = true;\\\n\t\tONCE_code__;\\\n\t}\\\n)\n\n/**\n * execute the code passed as a parameter except the first time this is\n * reached.\n * may be called at any time (in particular before main), but is not\n * thread-safe.\n **/\n#define ONCE_NOT(ONCE_code__)\\\nSTMT(\\\n\tstatic bool ONCE_done__ = false;\\\n\tif(!ONCE_done__)\\\n\t\tONCE_done__ = true;\\\n\telse\\\n\t\tONCE_code__;\\\n)\n\n\n/**\n * execute the code passed as a parameter before main is entered.\n *\n * WARNING: if the source file containing this is not directly referenced\n * from anywhere, linkers may discard that object file (unless linking\n * statically). see http://www.cbloom.com/3d/techdocs/more_coding.txt\n **/\n#define AT_STARTUP(code__)\\\n\tnamespace { struct UID__\\\n\t{\\\n\t\tUID__() { code__; }\\\n\t} UID2__; }\n\n\n/**\n * C++ new wrapper: allocates an instance of the given type and stores a\n * pointer to it. sets pointer to 0 on allocation failure.\n *\n * this simplifies application code when on VC6, which may or\n * may not throw/return 0 on failure.\n **/\n#define SAFE_NEW(type, ptr)\\\n\ttype* ptr;\\\n\ttry\\\n\t{\\\n\t\tptr = new type();\\\n\t}\\\n\tcatch(std::bad_alloc&)\\\n\t{\\\n\t\tptr = 0;\\\n\t}\n\n/**\n * delete memory ensuing from new and set the pointer to zero\n * (thus making double-frees safe / a no-op)\n **/\n#define SAFE_DELETE(p)\\\nSTMT(\\\n\tdelete (p);\t/* if p == 0, delete is a no-op */ \\\n\t(p) = 0;\\\n)\n\n/**\n * delete memory ensuing from new[] and set the pointer to zero\n * (thus making double-frees safe / a no-op)\n **/\n#define SAFE_ARRAY_DELETE(p)\\\nSTMT(\\\n\tdelete[] (p);\t/* if p == 0, delete is a no-op */ \\\n\t(p) = 0;\\\n)\n\n/**\n * free memory ensuing from malloc and set the pointer to zero\n * (thus making double-frees safe / a no-op)\n **/\n#define SAFE_FREE(p)\\\nSTMT(\\\n\tfree((void*)p);\t/* if p == 0, free is a no-op */ \\\n\t(p) = 0;\\\n)\n\n#endif\t\t// #ifndef INCLUDED_CODE_GENERATION\n"
  },
  {
    "path": "fpsgame/gui/config.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * compile-time configuration for the entire project\n */\n\n#ifndef INCLUDED_CONFIG\n#define INCLUDED_CONFIG\n\n// notes:\n// - this file is included in the PCH and thus affects the entire project.\n//   to avoid unnecessary full rebuilds, place settings of more limited\n//   applicability in config2.h and explicitly include that header.\n// - config macros are always defined; their values (1 or 0) are tested\n//   with #if instead of #ifdef. this protects against typos by at least\n//   causing a warning if the tested macro is undefined.\n// - allow override via compiler settings by checking #ifndef.\n\n// precompiled headers\n#ifndef CONFIG_ENABLE_PCH\n# define CONFIG_ENABLE_PCH 1\t// improve build performance\n#endif\n\n// frame pointers\n#ifndef CONFIG_OMIT_FP\n# ifdef NDEBUG\n#  define CONFIG_OMIT_FP 1\t// improve performance\n# else\n#  define CONFIG_OMIT_FP 0\t// enable use of ia32's fast stack walk\n# endif\n#endif\n\n// try to prevent any exceptions from being thrown - even by the C++\n// standard library. useful only for performance tests.\n#ifndef CONFIG_DISABLE_EXCEPTIONS\n# define CONFIG_DISABLE_EXCEPTIONS 0\n#endif\n\n// enable additional debug checks (potentially rather slow).\n#ifndef CONFIG_ENABLE_CHECKS\n# define CONFIG_ENABLE_CHECKS 0\n#endif\n\n// static type checking with Dehydra\n#ifndef CONFIG_DEHYDRA\n# define CONFIG_DEHYDRA 0\n#endif\n\n// allow the use of Boost? (affects PCH and several individual modules)\n#ifndef CONFIG_ENABLE_BOOST\n# define CONFIG_ENABLE_BOOST 1\n#endif\n\n#endif\t// #ifndef INCLUDED_CONFIG\n"
  },
  {
    "path": "fpsgame/gui/config2.h",
    "content": "/* Copyright (c) 2015 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * compile-time configuration for isolated spots\n */\n\n#ifndef INCLUDED_CONFIG2\n#define INCLUDED_CONFIG2\n\n// rationale: a centralized header makes it much easier to see what all\n// can be changed. it is assumed that only a few modules will need\n// configuration choices, so rebuilding them all is acceptable.\n// use config.h when settings must apply to the entire project.\n\n// allow use of RDTSC for raw tick counts (otherwise, the slower but\n// more reliable on MP systems wall-clock will be used).\n#ifndef CONFIG2_TIMER_ALLOW_RDTSC\n# define CONFIG2_TIMER_ALLOW_RDTSC 1\n#endif\n\n// this enables/disables the actual checking done by OverrunProtector\n// (quite slow, entailing mprotect() before/after each access).\n// define to 1 here or in the relevant module if you suspect mem corruption.\n// we provide this option because OverrunProtector requires some changes to\n// the object being wrapped, and we want to leave those intact but not\n// significantly slow things down except when needed.\n#ifndef CONFIG2_ALLOCATORS_OVERRUN_PROTECTION\n# define CONFIG2_ALLOCATORS_OVERRUN_PROTECTION 0\n#endif\n\n// zero-copy IO means all clients share the cached buffer; changing their\n// contents is forbidden. this flag causes the buffers to be marked as\n// read-only via MMU (writes would cause an exception), which takes a\n// bit of extra time.\n#ifndef CONFIG2_CACHE_READ_ONLY\n#define CONFIG2_CACHE_READ_ONLY 1\n#endif\n\n#ifndef CONFIG2_FILE_ENABLE_AIO\n// work around a bug introduced in Linux 2.6.38\n// (http://www.wildfiregames.com/forum/index.php?showtopic=14561&view=findpost&p=217710)\n// OpenBSD doesn't provide aio.h so we disable its use\n# if OS_LINUX || OS_OPENBSD\n#  define CONFIG2_FILE_ENABLE_AIO 0\n# else\n#  define CONFIG2_FILE_ENABLE_AIO 1\n# endif\n#endif\n\n// allow an attempt to start the Aken driver (i.e. service) at runtime.\n// enable at your own risk on WinXP systems to allow access to\n// better timers than Windows provides. on newer Windows versions,\n// attempts to start the service from code fail unless the process\n// is elevated, and definitely fail due to lack of cross-signing unless\n// test-signing mode is active.\n// if the user has taken explicit action to install and start the\n// service via aken_install.bat, mahaf.cpp will be able to access it\n// even if this is defined to 0.\n#ifndef CONFIG2_MAHAF_ATTEMPT_DRIVER_START\n# define CONFIG2_MAHAF_ATTEMPT_DRIVER_START 0\n#endif\n\n// build in OpenGL ES 2.0 mode, instead of the default mode designed for\n// GL 1.1 + extensions.\n// this disables various features that are not supported by GLES.\n#ifndef CONFIG2_GLES\n# define CONFIG2_GLES 0\n#endif\n\n// allow use of OpenAL/Ogg/Vorbis APIs\n#ifndef CONFIG2_AUDIO\n# define CONFIG2_AUDIO 1\n#endif\n\n// allow use of NVTT\n#ifndef CONFIG2_NVTT\n# define CONFIG2_NVTT 1\n#endif\n\n// allow use of lobby\n#ifndef CONFIG2_LOBBY\n# define CONFIG2_LOBBY 1\n#endif\n\n// allow use of miniupnpc\n#ifndef CONFIG2_MINIUPNPC\n# define CONFIG2_MINIUPNPC 1\n#endif\n\n#endif\t// #ifndef INCLUDED_CONFIG2\n"
  },
  {
    "path": "fpsgame/gui/debug.cpp",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * platform-independent debug support code.\n */\n\n#include \"precompiled.h\"\n#include \"lib/debug.h\"\n\n#include <cstdarg>\n#include <cstring>\n#include <cstdio>\n\n#include \"lib/alignment.h\"\n#include \"lib/app_hooks.h\"\n#include \"lib/fnv_hash.h\"\n#include \"lib/sysdep/vm.h\"\n#include \"lib/sysdep/cpu.h\"\t// cpu_CAS\n#include \"lib/sysdep/sysdep.h\"\n\n#if OS_WIN\n# include \"lib/sysdep/os/win/wdbg_heap.h\"\n#endif\n\nstatic const StatusDefinition debugStatusDefinitions[] = {\n\t{ ERR::SYM_NO_STACK_FRAMES_FOUND, L\"No stack frames found\" },\n\t{ ERR::SYM_UNRETRIEVABLE_STATIC, L\"Value unretrievable (stored in external module)\" },\n\t{ ERR::SYM_UNRETRIEVABLE, L\"Value unretrievable\" },\n\t{ ERR::SYM_TYPE_INFO_UNAVAILABLE, L\"Error getting type_info\" },\n\t{ ERR::SYM_INTERNAL_ERROR, L\"Exception raised while processing a symbol\" },\n\t{ ERR::SYM_UNSUPPORTED, L\"Symbol type not (fully) supported\" },\n\t{ ERR::SYM_CHILD_NOT_FOUND, L\"Symbol does not have the given child\" },\n\t{ ERR::SYM_NESTING_LIMIT, L\"Symbol nesting too deep or infinite recursion\" },\n\t{ ERR::SYM_SINGLE_SYMBOL_LIMIT, L\"Symbol has produced too much output\" },\n\t{ INFO::SYM_SUPPRESS_OUTPUT, L\"Symbol was suppressed\" }\n};\nSTATUS_ADD_DEFINITIONS(debugStatusDefinitions);\n\n\n// need to shoehorn printf-style variable params into\n// the OutputDebugString call.\n// - don't want to split into multiple calls - would add newlines to output.\n// - fixing Win32 _vsnprintf to return # characters that would be written,\n//   as required by C99, looks difficult and unnecessary. if any other code\n//   needs that, implement GNU vasprintf.\n// - fixed size buffers aren't nice, but much simpler than vasprintf-style\n//   allocate+expand_until_it_fits. these calls are for quick debug output,\n//   not loads of data, anyway.\n\n// rationale: static data instead of std::set to allow setting at any time.\n// we store FNV hash of tag strings for fast comparison; collisions are\n// extremely unlikely and can only result in displaying more/less text.\nstatic const size_t MAX_TAGS = 20;\nstatic u32 tags[MAX_TAGS];\nstatic size_t num_tags;\n\nvoid debug_filter_add(const char* tag)\n{\n\tconst u32 hash = fnv_hash(tag, strlen(tag)*sizeof(tag[0]));\n\n\t// make sure it isn't already in the list\n\tfor(size_t i = 0; i < MAX_TAGS; i++)\n\t\tif(tags[i] == hash)\n\t\t\treturn;\n\n\t// too many already?\n\tif(num_tags == MAX_TAGS)\n\t{\n\t\tDEBUG_WARN_ERR(ERR::LOGIC);\t// increase MAX_TAGS\n\t\treturn;\n\t}\n\n\ttags[num_tags++] = hash;\n}\n\nvoid debug_filter_remove(const char* tag)\n{\n\tconst u32 hash = fnv_hash(tag, strlen(tag)*sizeof(tag[0]));\n\n\tfor(size_t i = 0; i < MAX_TAGS; i++)\n\t{\n\t\tif(tags[i] == hash)\t// found it\n\t\t{\n\t\t\t// replace with last element (avoid holes)\n\t\t\ttags[i] = tags[MAX_TAGS-1];\n\t\t\tnum_tags--;\n\n\t\t\t// can only happen once, so we're done.\n\t\t\treturn;\n\t\t}\n\t}\n}\n\nvoid debug_filter_clear()\n{\n\tstd::fill(tags, tags+MAX_TAGS, 0);\n}\n\nbool debug_filter_allows(const char* text)\n{\n\tsize_t i;\n\tfor(i = 0; ; i++)\n\t{\n\t\t// no | found => no tag => should always be displayed\n\t\tif(text[i] == ' ' || text[i] == '\\0')\n\t\t\treturn true;\n\t\tif(text[i] == '|' && i != 0)\n\t\t\tbreak;\n\t}\n\n\tconst u32 hash = fnv_hash(text, i*sizeof(text[0]));\n\n\t// check if entry allowing this tag is found\n\tfor(i = 0; i < MAX_TAGS; i++)\n\t\tif(tags[i] == hash)\n\t\t\treturn true;\n\n\treturn false;\n}\n\n#undef debug_printf\t// allowing #defining it out\nvoid debug_printf(const char* fmt, ...)\n{\n\tchar buf[16384];\n\n\tva_list ap;\n\tva_start(ap, fmt);\n\tconst int numChars = vsprintf_s(buf, ARRAY_SIZE(buf), fmt, ap);\n\tif (numChars < 0)\n\t\tdebug_break();  // poor man's assert - avoid infinite loop because ENSURE also uses debug_printf\n\tva_end(ap);\n\n\tdebug_puts_filtered(buf);\n}\n\nvoid debug_puts_filtered(const char* text)\n{\n\tif(debug_filter_allows(text))\n\t\tdebug_puts(text);\n}\n\n\n//-----------------------------------------------------------------------------\n\nStatus debug_WriteCrashlog(const wchar_t* text)\n{\n\t// (avoid infinite recursion and/or reentering this function if it\n\t// fails/reports an error)\n\tenum State\n\t{\n\t\tIDLE,\n\t\tBUSY,\n\t\tFAILED\n\t};\n\t// note: the initial state is IDLE. we rely on zero-init because\n\t// initializing local static objects from constants may happen when\n\t// this is first called, which isn't thread-safe. (see C++ 6.7.4)\n\tcassert(IDLE == 0);\n\tstatic volatile intptr_t state;\n\n\tif(!cpu_CAS(&state, IDLE, BUSY))\n\t\treturn ERR::REENTERED;\t// NOWARN\n\n\tOsPath pathname = ah_get_log_dir()/\"crashlog.txt\";\n\tFILE* f = sys_OpenFile(pathname, \"w\");\n\tif(!f)\n\t{\n\t\tstate = FAILED;\t// must come before DEBUG_DISPLAY_ERROR\n\t\tDEBUG_DISPLAY_ERROR(L\"Unable to open crashlog.txt for writing (please ensure the log directory is writable)\");\n\t\treturn ERR::FAIL;\t// NOWARN (the above text is more helpful than a generic error code)\n\t}\n\n\tfputwc(0xFEFF, f);\t// BOM\n\tfwprintf(f, L\"%ls\\n\", text);\n\tfwprintf(f, L\"\\n\\n====================================\\n\\n\");\n\n\t// allow user to bundle whatever information they want\n\tah_bundle_logs(f);\n\n\tfclose(f);\n\tstate = IDLE;\n\treturn INFO::OK;\n}\n\n\n//-----------------------------------------------------------------------------\n// error message\n//-----------------------------------------------------------------------------\n\n// (NB: this may appear obscene, but deep stack traces have been\n// observed to take up > 256 KiB)\nstatic const size_t messageSize = 512*KiB;\n\nvoid debug_FreeErrorMessage(ErrorMessageMem* emm)\n{\n\tvm::Free(emm->pa_mem, messageSize);\n}\n\n\n// a stream with printf-style varargs and the possibility of\n// writing directly to the output buffer.\nclass PrintfWriter\n{\npublic:\n\tPrintfWriter(wchar_t* buf, size_t maxChars)\n\t\t: m_pos(buf), m_charsLeft(maxChars)\n\t{\n\t}\n\n\tbool operator()(const wchar_t* fmt, ...) WPRINTF_ARGS(2)\n\t{\n\t\tva_list ap;\n\t\tva_start(ap, fmt);\n\t\tconst int len = vswprintf(m_pos, m_charsLeft, fmt, ap);\n\t\tva_end(ap);\n\t\tif(len < 0)\n\t\t\treturn false;\n\t\tm_pos += len;\n\t\tm_charsLeft -= len;\n\t\treturn true;\n\t}\n\n\twchar_t* Position() const\n\t{\n\t\treturn m_pos;\n\t}\n\n\tsize_t CharsLeft() const\n\t{\n\t\treturn m_charsLeft;\n\t}\n\n\tvoid CountAddedChars()\n\t{\n\t\tconst size_t len = wcslen(m_pos);\n\t\tm_pos += len;\n\t\tm_charsLeft -= len;\n\t}\n\nprivate:\n\twchar_t* m_pos;\n\tsize_t m_charsLeft;\n};\n\n\n// split out of debug_DisplayError because it's used by the self-test.\nconst wchar_t* debug_BuildErrorMessage(\n\tconst wchar_t* description,\n\tconst wchar_t* filename, int line, const char* func,\n\tvoid* context, const wchar_t* lastFuncToSkip,\n\tErrorMessageMem* emm)\n{\n\t// retrieve errno (might be relevant) before doing anything else\n\t// that might overwrite it.\n\twchar_t description_buf[100] = L\"?\";\n\twchar_t os_error[100] = L\"?\";\n\tStatus errno_equiv = StatusFromErrno();\t// NOWARN\n\tif(errno_equiv != ERR::FAIL)\t// meaningful translation\n\t\tStatusDescription(errno_equiv, description_buf, ARRAY_SIZE(description_buf));\n\tsys_StatusDescription(0, os_error, ARRAY_SIZE(os_error));\n\n\t// rationale: see ErrorMessageMem\n\temm->pa_mem = vm::Allocate(messageSize);\n\twchar_t* const buf = (wchar_t*)emm->pa_mem;\n\tif(!buf)\n\t\treturn L\"(insufficient memory to generate error message)\";\n\tPrintfWriter writer(buf, messageSize / sizeof(wchar_t));\n\n\t// header\n\tif(!writer(\n\t\tL\"%ls\\r\\n\"\n\t\tL\"Location: %ls:%d (%hs)\\r\\n\"\n\t\tL\"\\r\\n\"\n\t\tL\"Call stack:\\r\\n\"\n\t\tL\"\\r\\n\",\n\t\tdescription, filename, line, func\n\t))\n\t{\nfail:\n\t\treturn L\"(error while formatting error message)\";\n\t}\n\n\t// append stack trace\n\tStatus ret = debug_DumpStack(writer.Position(), writer.CharsLeft(), context, lastFuncToSkip);\n\tif(ret == ERR::REENTERED)\n\t{\n\t\tif(!writer(\n\t\t\tL\"While generating an error report, we encountered a second \"\n\t\t\tL\"problem. Please be sure to report both this and the subsequent \"\n\t\t\tL\"error messages.\"\n\t\t))\n\t\t\tgoto fail;\n\t}\n\telse if(ret != INFO::OK)\n\t{\n\t\twchar_t error_buf[100] = {'?'};\n\t\tif(!writer(\n\t\t\tL\"(error while dumping stack: %ls)\",\n\t\t\tStatusDescription(ret, error_buf, ARRAY_SIZE(error_buf))\n\t\t))\n\t\t\tgoto fail;\n\t}\n\telse\t// success\n\t{\n\t\twriter.CountAddedChars();\n\t}\n\n\t// append errno\n\tif(!writer(\n\t\tL\"\\r\\n\"\n\t\tL\"errno = %d (%ls)\\r\\n\"\n\t\tL\"OS error = %ls\\r\\n\",\n\t\terrno, description_buf, os_error\n\t))\n\t\tgoto fail;\n\n\treturn buf;\n}\n\n\n//-----------------------------------------------------------------------------\n// display error messages\n//-----------------------------------------------------------------------------\n\n// translates and displays the given strings in a dialog.\n// this is typically only used when debug_DisplayError has failed or\n// is unavailable because that function is much more capable.\n// implemented via sys_display_msg; see documentation there.\nvoid debug_DisplayMessage(const wchar_t* caption, const wchar_t* msg)\n{\n\tsys_display_msg(ah_translate(caption), ah_translate(msg));\n}\n\n\n// when an error has come up and user clicks Exit, we don't want any further\n// errors (e.g. caused by atexit handlers) to come up, possibly causing an\n// infinite loop. hiding errors isn't good, but we assume that whoever clicked\n// exit really doesn't want to see any more messages.\nstatic atomic_bool isExiting;\n\n// this logic is applicable to any type of error. special cases such as\n// suppressing certain expected WARN_ERRs are done there.\nstatic bool ShouldSuppressError(atomic_bool* suppress)\n{\n\tif(isExiting)\n\t\treturn true;\n\n\tif(!suppress)\n\t\treturn false;\n\n\tif(*suppress == DEBUG_SUPPRESS)\n\t\treturn true;\n\n\treturn false;\n}\n\nstatic ErrorReactionInternal CallDisplayError(const wchar_t* text, size_t flags)\n{\n\t// first try app hook implementation\n\tErrorReactionInternal er = ah_display_error(text, flags);\n\t// .. it's only a stub: default to normal implementation\n\tif(er == ERI_NOT_IMPLEMENTED)\n\t\ter = sys_display_error(text, flags);\n\n\treturn er;\n}\n\nstatic ErrorReaction PerformErrorReaction(ErrorReactionInternal er, size_t flags, atomic_bool* suppress)\n{\n\tconst bool shouldHandleBreak = (flags & DE_MANUAL_BREAK) == 0;\n\n\tswitch(er)\n\t{\n\tcase ERI_CONTINUE:\n\t\treturn ER_CONTINUE;\n\n\tcase ERI_BREAK:\n\t\t// handle \"break\" request unless the caller wants to (doing so here\n\t\t// instead of within the dlgproc yields a correct call stack)\n\t\tif(shouldHandleBreak)\n\t\t{\n\t\t\tdebug_break();\n\t\t\treturn ER_CONTINUE;\n\t\t}\n\t\telse\n\t\t\treturn ER_BREAK;\n\n\tcase ERI_SUPPRESS:\n\t\t(void)cpu_CAS(suppress, 0, DEBUG_SUPPRESS);\n\t\treturn ER_CONTINUE;\n\n\tcase ERI_EXIT:\n\t\tisExiting = 1;\t// see declaration\n\t\tCOMPILER_FENCE;\n\n#if OS_WIN\n\t\t// prevent (slow) heap reporting since we're exiting abnormally and\n\t\t// thus probably leaking like a sieve.\n\t\twdbg_heap_Enable(false);\n#endif\n\n\t\texit(EXIT_FAILURE);\n\n\tcase ERI_NOT_IMPLEMENTED:\n\tdefault:\n\t\tdebug_break();\t// not expected to be reached\n\t\treturn ER_CONTINUE;\n\t}\n}\n\nErrorReaction debug_DisplayError(const wchar_t* description,\n\tsize_t flags, void* context, const wchar_t* lastFuncToSkip,\n\tconst wchar_t* pathname, int line, const char* func,\n\tatomic_bool* suppress)\n{\n\t// \"suppressing\" this error means doing nothing and returning ER_CONTINUE.\n\tif(ShouldSuppressError(suppress))\n\t\treturn ER_CONTINUE;\n\n\t// fix up params\n\t// .. translate\n\tdescription = ah_translate(description);\n\t// .. caller supports a suppress flag; set the corresponding flag so that\n\t//    the error display implementation enables the Suppress option.\n\tif(suppress)\n\t\tflags |= DE_ALLOW_SUPPRESS;\n\n\tif(flags & DE_NO_DEBUG_INFO)\n\t{\n\t\t// in non-debug-info mode, simply display the given description\n\t\t// and then return immediately\n\t\tErrorReactionInternal er = CallDisplayError(description, flags);\n\t\treturn PerformErrorReaction(er, flags, suppress);\n\t}\n\n\t// .. deal with incomplete file/line info\n\tif(!pathname || pathname[0] == '\\0')\n\t\tpathname = L\"unknown\";\n\tif(line <= 0)\n\t\tline = 0;\n\tif(!func || func[0] == '\\0')\n\t\tfunc = \"?\";\n\t// .. _FILE__ evaluates to the full path (albeit without drive letter)\n\t//    which is rather long. we only display the base name for clarity.\n\tconst wchar_t* filename = path_name_only(pathname);\n\n\t// display in output window; double-click will navigate to error location.\n\tdebug_printf(\"%s(%d): %s\\n\", utf8_from_wstring(filename).c_str(), line, utf8_from_wstring(description).c_str());\n\n\tErrorMessageMem emm;\n\tconst wchar_t* text = debug_BuildErrorMessage(description, filename, line, func, context, lastFuncToSkip, &emm);\n\n\t(void)debug_WriteCrashlog(text);\n\tErrorReactionInternal er = CallDisplayError(text, flags);\n\n\t// note: debug_break-ing here to make sure the app doesn't continue\n\t// running is no longer necessary. debug_DisplayError now determines our\n\t// window handle and is modal.\n\n\t// must happen before PerformErrorReaction because that may exit.\n\tdebug_FreeErrorMessage(&emm);\n\n\treturn PerformErrorReaction(er, flags, suppress);\n}\n\n\n// is errorToSkip valid? (also guarantees mutual exclusion)\nenum SkipStatus\n{\n\tINVALID, VALID, BUSY\n};\nstatic intptr_t skipStatus = INVALID;\nstatic Status errorToSkip;\nstatic size_t numSkipped;\n\nvoid debug_SkipErrors(Status err)\n{\n\tif(cpu_CAS(&skipStatus, INVALID, BUSY))\n\t{\n\t\terrorToSkip = err;\n\t\tnumSkipped = 0;\n\t\tCOMPILER_FENCE;\n\t\tskipStatus = VALID;\t// linearization point\n\t}\n\telse\n\t\tDEBUG_WARN_ERR(ERR::REENTERED);\n}\n\nsize_t debug_StopSkippingErrors()\n{\n\tif(cpu_CAS(&skipStatus, VALID, BUSY))\n\t{\n\t\tconst size_t ret = numSkipped;\n\t\tCOMPILER_FENCE;\n\t\tskipStatus = INVALID;\t// linearization point\n\t\treturn ret;\n\t}\n\telse\n\t{\n\t\tDEBUG_WARN_ERR(ERR::REENTERED);\n\t\treturn 0;\n\t}\n}\n\nstatic bool ShouldSkipError(Status err)\n{\n\tif(cpu_CAS(&skipStatus, VALID, BUSY))\n\t{\n\t\tnumSkipped++;\n\t\tconst bool ret = (err == errorToSkip);\n\t\tCOMPILER_FENCE;\n\t\tskipStatus = VALID;\n\t\treturn ret;\n\t}\n\treturn false;\n}\n\n\nErrorReaction debug_OnError(Status err, atomic_bool* suppress, const wchar_t* file, int line, const char* func)\n{\n\tCACHE_ALIGNED(u8) context[DEBUG_CONTEXT_SIZE];\n\t(void)debug_CaptureContext(context);\n\n\tif(ShouldSkipError(err))\n\t\treturn ER_CONTINUE;\n\n\tconst wchar_t* lastFuncToSkip = L\"debug_OnError\";\n\twchar_t buf[400];\n\twchar_t err_buf[200]; StatusDescription(err, err_buf, ARRAY_SIZE(err_buf));\n\tswprintf_s(buf, ARRAY_SIZE(buf), L\"Function call failed: return value was %lld (%ls)\", (long long)err, err_buf);\n\treturn debug_DisplayError(buf, DE_MANUAL_BREAK, context, lastFuncToSkip, file,line,func, suppress);\n}\n\n\nErrorReaction debug_OnAssertionFailure(const wchar_t* expr, atomic_bool* suppress, const wchar_t* file, int line, const char* func)\n{\n\tCACHE_ALIGNED(u8) context[DEBUG_CONTEXT_SIZE];\n\t(void)debug_CaptureContext(context);\n\n\tconst std::wstring lastFuncToSkip = L\"debug_OnAssertionFailure\";\n\twchar_t buf[400];\n\tswprintf_s(buf, ARRAY_SIZE(buf), L\"Assertion failed: \\\"%ls\\\"\", expr);\n\treturn debug_DisplayError(buf, DE_MANUAL_BREAK, context, lastFuncToSkip.c_str(), file,line,func, suppress);\n}\n"
  },
  {
    "path": "fpsgame/gui/debug.h",
    "content": "/* Copyright (c) 2015 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * platform-independent debug support code.\n */\n\n#ifndef INCLUDED_DEBUG\n#define INCLUDED_DEBUG\n\n// this module provides platform-independent debug facilities, useful for\n// diagnosing and reporting program errors.\n// - a symbol engine provides access to compiler-generated debug information and\n//   can also give a stack trace including local variables;\n// - our more powerful assert() replacement gives a stack trace so\n//   that the underlying problem becomes apparent;\n// - the output routines make for platform-independent logging and\n//   crashlogs with \"last-known activity\" reporting.\n\n#include \"lib/lib_api.h\"\n#include \"lib/types.h\"\t// intptr_t\n#include \"lib/status.h\"\n#include \"lib/alignment.h\"\n#include \"lib/code_annotation.h\"\n#include \"lib/code_generation.h\"\n\n/**\n * trigger a breakpoint when reached/\"called\".\n * if defined as a macro, the debugger can break directly into the\n * target function instead of one frame below it as with a conventional\n * call-based implementation.\n **/\n#if MSC_VERSION\n# define debug_break __debugbreak\t// intrinsic \"function\"\n#else\nextern void debug_break();\n#endif\n\n\n//-----------------------------------------------------------------------------\n// output\n//-----------------------------------------------------------------------------\n\n/**\n * write a formatted string to the debug channel, subject to filtering\n * (see below). implemented via debug_puts - see performance note there.\n *\n * @param fmt Format string and varargs; see printf.\n **/\nLIB_API void debug_printf(const char* fmt, ...) PRINTF_ARGS(1);\n\n\n/**\n * translates and displays the given strings in a dialog.\n * this is typically only used when debug_DisplayError has failed or\n * is unavailable because that function is much more capable.\n * implemented via sys_display_msg; see documentation there.\n **/\nLIB_API void debug_DisplayMessage(const wchar_t* caption, const wchar_t* msg);\n\n/// flags to customize debug_DisplayError behavior\nenum DebugDisplayErrorFlags\n{\n\t/**\n\t * disallow the Continue button. used e.g. if an exception is fatal.\n\t **/\n\tDE_NO_CONTINUE = 1,\n\n\t/**\n\t * enable the Suppress button. set automatically by debug_DisplayError if\n\t * it receives a non-NULL suppress pointer. a flag is necessary because\n\t * the sys_display_error interface doesn't get that pointer.\n\t * rationale for automatic setting: this may prevent someone from\n\t * forgetting to specify it, and disabling Suppress despite having\n\t * passed a non-NULL pointer doesn't make much sense.\n\t **/\n\tDE_ALLOW_SUPPRESS = 2,\n\n\t/**\n\t * do not trigger a breakpoint inside debug_DisplayError; caller\n\t * will take care of this if ER_BREAK is returned. this is so that the\n\t * debugger can jump directly into the offending function.\n\t **/\n\tDE_MANUAL_BREAK = 4,\n\n\t/**\n\t * display just the given message; do not add any information about the\n\t * call stack, do not write crashlogs, etc.\n\t */\n\tDE_NO_DEBUG_INFO = 8\n};\n\n/**\n * a bool that is reasonably certain to be set atomically.\n * we cannot assume support for OpenMP (requires GCC 4.2) or C++0x,\n * so we'll have to resort to intptr_t, cpu_CAS and COMPILER_FENCE.\n **/\ntypedef volatile intptr_t atomic_bool;\n\n/**\n * value for suppress flag once set by debug_DisplayError.\n * rationale: this value is fairly distinctive and helps when\n * debugging the symbol engine.\n * use 0 as the initial value to avoid allocating .rdata space.\n **/\nstatic const atomic_bool DEBUG_SUPPRESS = 0xAB;\n\n/**\n * choices offered by the error dialog that are returned\n * by debug_DisplayError.\n **/\nenum ErrorReaction\n{\n\t/**\n\t * ignore, continue as if nothing happened.\n\t * note: value doesn't start at 0 because that is interpreted as a\n\t * DialogBoxParam failure.\n\t **/\n\tER_CONTINUE = 1,\n\n\t/**\n\t * trigger breakpoint, i.e. enter debugger.\n\t * only returned if DE_MANUAL_BREAK was passed; otherwise,\n\t * debug_DisplayError will trigger a breakpoint itself.\n\t **/\n\tER_BREAK\n};\n\n/**\n * all choices offered by the error dialog. those not defined in\n * ErrorReaction are acted upon by debug_DisplayError and\n * never returned to callers.\n * (this separation avoids enumerator-not-handled warnings)\n **/\nenum ErrorReactionInternal\n{\n\tERI_CONTINUE = ER_CONTINUE,\n\tERI_BREAK = ER_BREAK,\n\n\t/**\n\t * ignore and do not report again.\n\t * note: non-persistent; only applicable during this program run.\n\t **/\n\tERI_SUPPRESS,\n\n\t/**\n\t * exit the program immediately.\n\t **/\n\tERI_EXIT,\n\n\t/**\n\t * special return value for the display_error app hook stub to indicate\n\t * that it has done nothing and that the normal sys_display_error\n\t * implementation should be called instead.\n\t **/\n\tERI_NOT_IMPLEMENTED\n};\n\n\n/**\n * display an error dialog with a message and stack trace.\n *\n * @param description text to show.\n * @param flags: see DebugDisplayErrorFlags.\n * @param context, lastFuncToSkip: see debug_DumpStack.\n * @param file, line, func: location of the error (typically passed as\n * WIDEN(__FILE__), __LINE__, __func__ from a macro)\n * @param suppress pointer to a caller-allocated flag that can be used to\n * suppress this error. if NULL, this functionality is skipped and the\n * \"Suppress\" dialog button will be disabled.\n * note: this flag is read and written exclusively here; caller only\n * provides the storage. values: see DEBUG_SUPPRESS above.\n * @return ErrorReaction (user's choice: continue running or stop?)\n **/\nLIB_API ErrorReaction debug_DisplayError(const wchar_t* description, size_t flags, void* context, const wchar_t* lastFuncToSkip, const wchar_t* file, int line, const char* func, atomic_bool* suppress);\n\n// simplified version for just displaying an error message\n#define DEBUG_DISPLAY_ERROR(description)\\\n\tdo\\\n\t{\\\n\t\tCACHE_ALIGNED(u8) context[DEBUG_CONTEXT_SIZE];\\\n\t\t(void)debug_CaptureContext(context);\\\n\t\t(void)debug_DisplayError(description, 0, context, L\"debug_DisplayError\", WIDEN(__FILE__), __LINE__, __func__, 0);\\\n\t}\\\n\twhile(0)\n\n\n//\n// filtering\n//\n\n/**\n * debug output is very useful, but \"too much of a good thing can kill you\".\n * we don't want to require different LOGn() macros that are enabled\n * depending on \"debug level\", because changing that entails lengthy\n * compiles and it's too coarse-grained. instead, we require all\n * strings to start with \"tag_string|\" (exact case and no quotes;\n * the alphanumeric-only \\<tag_string\\> identifies output type).\n * they are then subject to filtering: only if the tag has been\n * \"added\" via debug_filter_add is the appendant string displayed.\n *\n * this approach is easiest to implement and is fine because we control\n * all logging code. LIMODS falls from consideration since it's not\n * portable and too complex.\n *\n * notes:\n * - filter changes only affect subsequent debug_*printf calls;\n *   output that didn't pass the filter is permanently discarded.\n * - strings not starting with a tag are always displayed.\n * - debug_filter_* can be called at any time and from the debugger,\n *   but are not reentrant.\n *\n * in future, allow output with the given tag to proceed.\n * no effect if already added.\n **/\nLIB_API void debug_filter_add(const char* tag);\n\n/**\n * in future, discard output with the given tag.\n * no effect if not currently added.\n **/\nLIB_API void debug_filter_remove(const char* tag);\n\n/**\n * clear all filter state; equivalent to debug_filter_remove for\n * each tag that was debug_filter_add-ed.\n **/\nLIB_API void debug_filter_clear();\n\n/**\n * indicate if the given text would be printed.\n * useful for a series of debug_printfs - avoids needing to add a tag to\n * each of their format strings.\n **/\nLIB_API bool debug_filter_allows(const char* text);\n\n/**\n * call debug_puts if debug_filter_allows allows the string.\n **/\nLIB_API void debug_puts_filtered(const char* text);\n\n/**\n * write an error description and all logs into crashlog.txt\n * (in unicode format).\n *\n * @param text description of the error (including stack trace);\n * typically generated by debug_BuildErrorMessage.\n *\n * @return Status; ERR::REENTERED if reentered via recursion or\n * multithreading (not allowed since an infinite loop may result).\n **/\nLIB_API Status debug_WriteCrashlog(const char* text);\n\n\n//-----------------------------------------------------------------------------\n// assertions\n//-----------------------------------------------------------------------------\n\n/**\n * ensure the expression \\<expr\\> evaluates to non-zero. used to validate\n * invariants in the program during development and thus gives a\n * very helpful warning if something isn't going as expected.\n * sprinkle these liberally throughout your code!\n *\n * to pass more information to users at runtime, you can write\n * ENSURE(expression && \"descriptive string\").\n **/\n#define ENSURE(expr)\\\n\tdo\\\n\t{\\\n\t\tstatic atomic_bool suppress__;\\\n\t\tif(!(expr))\\\n\t\t{\\\n\t\t\tswitch(debug_OnAssertionFailure(WIDEN(#expr), &suppress__, WIDEN(__FILE__), __LINE__, __func__))\\\n\t\t\t{\\\n\t\t\tcase ER_CONTINUE:\\\n\t\t\t\tbreak;\\\n\t\t\tcase ER_BREAK:\\\n\t\t\tdefault:\\\n\t\t\t\tdebug_break();\\\n\t\t\t\tbreak;\\\n\t\t\t}\\\n\t\t}\\\n\t}\\\n\twhile(0)\n\n/**\n * same as ENSURE in debug mode, does nothing in release mode.\n * (we don't override the `assert' macro because users may\n * inadvertently include \\<assert.h\\> afterwards)\n * (we do not provide an MFC-style VERIFY macro because the distinction\n * between ENSURE and VERIFY is unclear. to always run code but only\n * check for success in debug builds without raising unused-variable warnings,\n * use ASSERT + UNUSED2.)\n **/\n#define ASSERT(expr) ENSURE(expr)\n#ifdef NDEBUG\n# undef ASSERT\n# define ASSERT(expr)\n#endif\n\n/**\n * display the error dialog with the given text. this is less error-prone than\n * ENSURE(0 && \"text\"). note that \"conditional expression is constant\" warnings\n * are disabled anyway.\n *\n * if being able to suppress the warning is desirable (e.g. for self-tests),\n * then use DEBUG_WARN_ERR instead.\n **/\n#define debug_warn(expr) ENSURE(0 && (expr))\n\n/**\n * display the error dialog with text corresponding to the given error code.\n * used by WARN_RETURN_STATUS_IF_ERR et al., which wrap function calls and automatically\n * raise warnings and return to the caller.\n **/\n#define DEBUG_WARN_ERR(status)\\\n\tdo\\\n\t{\\\n\t\tstatic atomic_bool suppress__;\\\n\t\tswitch(debug_OnError(status, &suppress__, WIDEN(__FILE__), __LINE__, __func__))\\\n\t\t{\\\n\t\tcase ER_CONTINUE:\\\n\t\t\tbreak;\\\n\t\tcase ER_BREAK:\\\n\t\tdefault:\\\n\t\t\tdebug_break();\\\n\t\t\tbreak;\\\n\t\t}\\\n\t}\\\n\twhile(0)\n\n/**\n * called when a ENSURE/ASSERT fails;\n * notifies the user via debug_DisplayError.\n *\n * @param assert_expr the expression that failed; typically passed as\n * \\#expr in the assert macro.\n * @param suppress see debug_DisplayError.\n * @param file, line source file name and line number of the spot that failed\n * @param func name of the function containing it\n * @return ErrorReaction (user's choice: continue running or stop?)\n **/\nLIB_API ErrorReaction debug_OnAssertionFailure(const wchar_t* assert_expr, atomic_bool* suppress, const wchar_t* file, int line, const char* func) ANALYZER_NORETURN;\n\n/**\n * called when a DEBUG_WARN_ERR indicates an error occurred;\n * notifies the user via debug_DisplayError.\n *\n * @param err Status value indicating the error that occurred\n * @param suppress see debug_DisplayError.\n * @param file, line source file name and line number of the spot that failed\n * @param func name of the function containing it\n * @return ErrorReaction (user's choice: continue running or stop?)\n **/\nLIB_API ErrorReaction debug_OnError(Status err, atomic_bool* suppress, const wchar_t* file, int line, const char* func) ANALYZER_NORETURN;\n\n\n/**\n * suppress (prevent from showing) the error dialog from subsequent\n * debug_OnError for the given Status.\n *\n * rationale: for edge cases in some functions, warnings are raised in\n * addition to returning an error code. self-tests deliberately trigger\n * these cases and check for the latter but shouldn't cause the former.\n * we therefore need to squelch them.\n *\n * @param err the Status to skip.\n *\n * note: only one concurrent skip request is allowed; call\n * debug_StopSkippingErrors before the next debug_SkipErrors.\n */\nLIB_API void debug_SkipErrors(Status err);\n\n/**\n * @return how many errors were skipped since the call to debug_SkipErrors()\n **/\nLIB_API size_t debug_StopSkippingErrors();\n\n\n//-----------------------------------------------------------------------------\n// symbol access\n//-----------------------------------------------------------------------------\n\nnamespace ERR\n{\n\tconst Status SYM_NO_STACK_FRAMES_FOUND = -100400;\n\tconst Status SYM_UNRETRIEVABLE_STATIC  = -100401;\n\tconst Status SYM_UNRETRIEVABLE         = -100402;\n\tconst Status SYM_TYPE_INFO_UNAVAILABLE = -100403;\n\tconst Status SYM_INTERNAL_ERROR        = -100404;\n\tconst Status SYM_UNSUPPORTED           = -100405;\n\tconst Status SYM_CHILD_NOT_FOUND       = -100406;\n\t// this limit is to prevent infinite recursion.\n\tconst Status SYM_NESTING_LIMIT         = -100407;\n\t// this limit is to prevent large symbols (e.g. arrays or linked lists)\n\t// from taking up all available output space.\n\tconst Status SYM_SINGLE_SYMBOL_LIMIT   = -100408;\n}\n\nnamespace INFO\n{\n\t// one of the dump_sym* functions decided not to output anything at\n\t// all (e.g. for member functions in UDTs - we don't want those).\n\t// therefore, skip any post-symbol formatting (e.g. ) as well.\n\tconst Status SYM_SUPPRESS_OUTPUT       = +100409;\n}\n\n\n/**\n * Maximum number of characters (including null terminator) written to\n * user's buffers by debug_ResolveSymbol.\n **/\nstatic const size_t DEBUG_SYMBOL_CHARS = 1000;\nstatic const size_t DEBUG_FILE_CHARS = 100;\n\n/**\n * read and return symbol information for the given address.\n *\n * NOTE: the PDB implementation is rather slow (~500 us).\n *\n * @param ptr_of_interest address of symbol (e.g. function, variable)\n * @param sym_name optional out; holds at least DEBUG_SYMBOL_CHARS;\n *   receives symbol name returned via debug info.\n * @param file optional out; holds at least DEBUG_FILE_CHARS;\n *   receives base name only (no path; see rationale in wdbg_sym) of\n *   source file containing the symbol.\n * @param line optional out; receives source file line number of symbol.\n *\n * note: all of the output parameters are optional; we pass back as much\n * information as is available and desired.\n * @return Status; INFO::OK iff any information was successfully\n * retrieved and stored.\n **/\nLIB_API Status debug_ResolveSymbol(void* ptr_of_interest, wchar_t* sym_name, wchar_t* file, int* line);\n\nstatic const size_t DEBUG_CONTEXT_SIZE = 2048;\t// Win32 CONTEXT is currently 1232 bytes\n\n/**\n * @param context must point to an instance of the platform-specific type\n *   (e.g. CONTEXT) or CACHE_ALIGNED storage of DEBUG_CONTEXT_SIZE bytes.\n **/\nLIB_API Status debug_CaptureContext(void* context);\n\n\n/**\n * write a complete stack trace (including values of local variables) into\n * the specified buffer.\n *\n * @param buf Target buffer.\n * @param maxChars Max chars of buffer (should be several thousand).\n * @param context Platform-specific representation of execution state\n *\t\t  (e.g. Win32 CONTEXT). either specify an SEH exception's\n *        context record or use debug_CaptureContext to retrieve the current state.\n *        Rationale: intermediates such as debug_DisplayError change the\n *        context, so it should be captured as soon as possible.\n * @param lastFuncToSkip Is used for omitting error-reporting functions like\n *\t\t  debug_OnAssertionFailure from the stack trace. It is either 0 (skip nothing) or\n *\t\t  a substring of a function's name (this allows platform-independent\n *\t\t  matching of stdcall-decorated names).\n *\t\t  Rationale: this is safer than specifying a fixed number of frames,\n *\t\t  which can be incorrect due to inlining.\n * @return Status; ERR::REENTERED if reentered via recursion or\n *\t\t   multithreading (not allowed since static data is used).\n **/\nLIB_API Status debug_DumpStack(wchar_t* buf, size_t maxChars, void* context, const wchar_t* lastFuncToSkip);\n\n\n//-----------------------------------------------------------------------------\n// helper functions (used by implementation)\n//-----------------------------------------------------------------------------\n\n/**\n * [system-dependent] write a string to the debug channel.\n * this can be quite slow (~1 ms)! On Windows, it uses OutputDebugString\n * (entails context switch), otherwise stdout+fflush (waits for IO).\n **/\nLIB_API void debug_puts(const char* text);\n\n/**\n * return the caller of a certain function on the call stack.\n *\n * this function is useful for recording (partial) stack traces for\n * memory allocation tracking, etc.\n *\n * @param context, lastFuncToSkip - see debug_DumpStack\n * @return address of the caller\n **/\nLIB_API void* debug_GetCaller(void* context, const wchar_t* lastFuncToSkip);\n\n/**\n * check if a pointer appears to be totally invalid.\n *\n * this check is not authoritative (the pointer may be \"valid\" but incorrect)\n * but can be used to filter out obviously wrong values in a portable manner.\n *\n * @param p pointer\n * @return 1 if totally bogus, otherwise 0.\n **/\nLIB_API int debug_IsPointerBogus(const void* p);\n\n/// does the given pointer appear to point to code?\nLIB_API bool debug_IsCodePointer(void* p);\n\n/// does the given pointer appear to point to the stack?\nLIB_API bool debug_IsStackPointer(void* p);\n\n\n/**\n * inform the debugger of the current thread's name.\n *\n * (threads are easier to keep apart when they are identified by\n * name rather than TID.)\n **/\nLIB_API void debug_SetThreadName(const char* name);\n\n\n/**\n * holds memory for an error message.\n **/\nstruct ErrorMessageMem\n{\n\t// rationale:\n\t// - error messages with stack traces require a good deal of memory\n\t//   (hundreds of KB). static buffers of that size are undesirable.\n \t// - the heap may be corrupted, so don't use malloc.\n\t//   instead, \"lib/sysdep/vm.h\" functions should be safe.\n\t// - alloca is a bit iffy (the stack may be maxed out), non-portable and\n\t//   complicates the code because it can't be allocated by a subroutine.\n\t// - this method is probably slow, but error messages aren't built often.\n\t//   if necessary, first try malloc and use mmap if that fails.\n\tvoid* pa_mem;\n};\n\n/**\n * free memory from the error message.\n *\n * @param emm ErrorMessageMem*\n **/\nLIB_API void debug_FreeErrorMessage(ErrorMessageMem* emm);\n\n/**\n * build a string describing the given error.\n *\n * this is a helper function used by debug_DumpStack and is made available\n * so that the self-test doesn't have to display the error dialog.\n *\n * @param description: general description of the problem.\n * @param fn_only filename (no path) of source file that triggered the error.\n * @param line, func: exact position of the error.\n * @param context, lastFuncToSkip: see debug_DumpStack.\n * @param emm memory for the error message. caller should allocate\n * stack memory and set alloc_buf*; if not, there will be no\n * fallback in case heap alloc fails. should be freed via\n * debug_FreeErrorMessage when no longer needed.\n **/\nLIB_API const wchar_t* debug_BuildErrorMessage(const wchar_t* description, const wchar_t* fn_only, int line, const char* func, void* context, const wchar_t* lastFuncToSkip, ErrorMessageMem* emm);\n\n#endif\t// #ifndef INCLUDED_DEBUG\n"
  },
  {
    "path": "fpsgame/gui/debug_stl.cpp",
    "content": "/* Copyright (c) 2015 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * portable debugging helper functions specific to the STL.\n */\n\n#include \"precompiled.h\"\n#include \"lib/debug_stl.h\"\n\n#include <deque>\n#include <map>\n#include <set>\n#include <cassert>\n#include <list>\n\n#include \"lib/regex.h\"\n\nstatic const StatusDefinition debugStlStatusDefinitions[] = {\n\t{ ERR::STL_CNT_UNKNOWN, L\"Unknown STL container type_name\" },\n\t{ ERR::STL_CNT_UNSUPPORTED, L\"Unsupported STL container\" },\n\t{ ERR::STL_CNT_INVALID, L\"Container type is known but contents are invalid\" }\n};\nSTATUS_ADD_DEFINITIONS(debugStlStatusDefinitions);\n\n\n// used in debug_stl_simplify_name.\n// note: wcscpy_s is safe because replacement happens in-place and\n// <what> is longer than <with> (otherwise, we wouldn't be replacing).\n#define REPLACE(what, with)\\\n\telse if(!wcsncmp(src, (what), ARRAY_SIZE(what)-1))\\\n\t{\\\n\t\tsrc += ARRAY_SIZE(what)-1-1; /* see preincrement rationale*/\\\n\t\twcscpy_s(dst, ARRAY_SIZE(with), (with));\\\n\t\tdst += ARRAY_SIZE(with)-1;\\\n\t}\n#define STRIP(what)\\\n\telse if(!wcsncmp(src, (what), ARRAY_SIZE(what)-1))\\\n\t{\\\n\t\tsrc += ARRAY_SIZE(what)-1-1;/* see preincrement rationale*/\\\n\t}\n#define STRIP_NESTED(what)\\\n\telse if(!wcsncmp(src, (what), ARRAY_SIZE(what)-1))\\\n\t{\\\n\t\t/* remove preceding comma (if present) */\\\n\t\tif(src != name && src[-1] == ',')\\\n\t\t\tdst--;\\\n\t\tsrc += ARRAY_SIZE(what)-1;\\\n\t\t/* strip everything until trailing > is matched */\\\n\t\tENSURE(nesting == 0);\\\n\t\tnesting = 1;\\\n\t}\n\n// reduce complicated STL names to human-readable form (in place).\n// e.g. \"std::basic_string<char, char_traits<char>, std::allocator<char> >\" =>\n//  \"string\". algorithm: strip undesired strings in one pass (fast).\n// called from symbol_string_build.\n//\n// see http://www.bdsoft.com/tools/stlfilt.html and\n// http://www.moderncppdesign.com/publications/better_template_error_messages.html\nwchar_t* debug_stl_simplify_name(wchar_t* name)\n{\n\t// used when stripping everything inside a < > to continue until\n\t// the final bracket is matched (at the original nesting level).\n\tint nesting = 0;\n\n\tconst wchar_t* src = name-1;\t// preincremented; see below.\n\twchar_t* dst = name;\n\n\t// for each character: (except those skipped as parts of strings)\n\tfor(;;)\n\t{\n\t\twchar_t c = *(++src);\n\t\t// preincrement rationale: src++ with no further changes would\n\t\t// require all comparisons to subtract 1. incrementing at the\n\t\t// end of a loop would require a goto, instead of continue\n\t\t// (there are several paths through the loop, for speed).\n\t\t// therefore, preincrement. when skipping strings, subtract\n\t\t// 1 from the offset (since src is advanced directly after).\n\n\t\t// end of string reached - we're done.\n\t\tif(c == '\\0')\n\t\t{\n\t\t\t*dst = '\\0';\n\t\t\tbreak;\n\t\t}\n\n\t\t// we're stripping everything inside a < >; eat characters\n\t\t// until final bracket is matched (at the original nesting level).\n\t\tif(nesting)\n\t\t{\n\t\t\tif(c == '<')\n\t\t\t\tnesting++;\n\t\t\telse if(c == '>')\n\t\t\t{\n\t\t\t\tnesting--;\n\t\t\t\tENSURE(nesting >= 0);\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\t// start if chain (REPLACE and STRIP use else if)\n\t\tif(0) {}\n\t\telse if(!wcsncmp(src, L\"::_Node\", 7))\n\t\t{\n\t\t\t// add a space if not already preceded by one\n\t\t\t// (prevents replacing \">::_Node>\" with \">>\")\n\t\t\tif(src != name && src[-1] != ' ')\n\t\t\t\t*dst++ = ' ';\n\t\t\tsrc += 7;\n\t\t}\n\t\tREPLACE(L\"unsigned short\", L\"u16\")\n\t\tREPLACE(L\"unsigned int\", L\"size_t\")\n\t\tREPLACE(L\"unsigned __int64\", L\"u64\")\n\t\tSTRIP(L\",0> \")\n\t\t// early out: all tests after this start with s, so skip them\n\t\telse if(c != 's')\n\t\t{\n\t\t\t*dst++ = c;\n\t\t\tcontinue;\n\t\t}\n\t\tREPLACE(L\"std::_List_nod\", L\"list\")\n\t\tREPLACE(L\"std::_Tree_nod\", L\"map\")\n\t\tREPLACE(L\"std::basic_string<char, \", L\"string<\")\n\t\tREPLACE(L\"std::basic_string<__wchar_t, \", L\"wstring<\")\n\t\tREPLACE(L\"std::basic_string<unsigned short, \", L\"wstring<\")\n\t\tSTRIP(L\"std::char_traits<char>, \")\n\t\tSTRIP(L\"std::char_traits<unsigned short>, \")\n\t\tSTRIP(L\"std::char_traits<__wchar_t>, \")\n\t\tSTRIP(L\"std::_Tmap_traits\")\n\t\tSTRIP(L\"std::_Tset_traits\")\n\t\tSTRIP_NESTED(L\"std::allocator<\")\n\t\tSTRIP_NESTED(L\"std::less<\")\n\t\tSTRIP_NESTED(L\"stdext::hash_compare<\")\n\t\tSTRIP(L\"std::\")\n\t\tSTRIP(L\"stdext::\")\n\t\telse\n\t\t\t*dst++ = c;\n\t}\n\n\treturn name;\n}\n\n\n//-----------------------------------------------------------------------------\n// STL container debugging\n//-----------------------------------------------------------------------------\n\n// provide an iterator interface for arbitrary STL containers; this is\n// used to display their contents in stack traces. their type and\n// contents aren't known until runtime, so this is somewhat tricky.\n//\n// we assume STL containers aren't specialized on their content type and\n// use their int instantiations's memory layout. vector<bool> will therefore\n// not be displayed correctly, but it is frowned upon anyway (since\n// address of its elements can't be taken).\n// to be 100% correct, we'd have to write an Any_container_type__element_type\n// class for each combination, but that is clearly infeasible.\n//\n// containers might still be uninitialized when we call get_container_info on\n// them. we need to check if they are IsValid and only then use their contents.\n// to that end, we derive a validator class from each container,\n// cast the container's address to it, and call its IsValid() method.\n//\n// checks performed include: is size() realistic; does begin() come before\n// end(), etc. we need to leverage all invariants because the values are\n// random in release mode.\n//\n// we sometimes need to access protected members of the STL containers.\n// granting access via friend is not possible since the system headers\n// must not be changed. that leaves us with two alternatives:\n// 1) write a 'shadow' class that has the same memory layout. this would\n//    free us from the ugly Dinkumware naming conventions, but requires\n//    more maintenance when the STL implementation changes.\n// 2) derive from the container. while not entirely bulletproof due to the\n//    lack of virtual dtors, this is safe in practice because pointers are\n//    neither returned to users nor freed. the only requirement is that\n//    classes must not include virtual functions, because a vptr would\n//    change the memory layout in unknown ways.\n//\n// it is rather difficult to abstract away implementation details of various\n// STL versions. we currently only really support Dinkumware due to\n// significant differences in the implementations of set, map and string.\n\n\n//----------------------------------------------------------------------------\n// standard containers\n\n// base class (slightly simplifies code by providing default implementations\n// that can be used for most containers).\n// Container is the complete type of the STL container (we can't pass this\n// as a template because Dinkumware _Tree requires different parameters)\ntemplate<class Container>\nstruct ContainerBase : public Container\n{\n\tbool IsValid(size_t UNUSED(el_size)) const\n\t{\n\t\treturn true;\n\t}\n\n\tsize_t NumElements(size_t UNUSED(el_size)) const\n\t{\n\t\treturn this->size();\n\t}\n\n\tstatic const u8* DereferenceAndAdvance(typename Container::iterator& it, size_t UNUSED(el_size))\n\t{\n\t\tconst u8* p = (const u8*)&*it;\n\t\t++it;\n\t\treturn p;\n\t}\n};\n\n\nstruct Any_deque : public ContainerBase<std::deque<int> >\n{\n#if STL_DINKUMWARE == 405\n\n\tbool IsValid(size_t el_size) const\n\t{\n\t\tconst size_t el_per_bucket = ElementsPerBucket(el_size);\n\t\t// initial element is beyond end of first bucket\n\t\tif(_Myoff >= el_per_bucket)\n\t\t\treturn false;\n\t\t// more elements reported than fit in all buckets\n\t\tif(_Mysize > _Mapsize * el_per_bucket)\n\t\t\treturn false;\n\n\t\treturn true;\n\t}\n\n\tstatic const u8* DereferenceAndAdvance(iterator& stl_it, size_t el_size)\n\t{\n\t\tstruct Iterator : public iterator\n\t\t{\n\t\t\tAny_deque& Container() const\n\t\t\t{\n\t\t\t\treturn *(Any_deque*)_Mycont;\n\t\t\t}\n\n\t\t\tsize_t CurrentIndex() const\n\t\t\t{\n\t\t\t\treturn _Myoff;\n\t\t\t}\n\t\t};\n\n\t\tIterator& it = *(Iterator*)&stl_it;\n\t\tAny_deque& container = it.Container();\n\t\tconst size_t currentIndex = it.CurrentIndex();\n\t\tconst u8* p = container.GetNthElement(currentIndex, el_size);\n\t\t++it;\n\t\treturn p;\n\t}\n\nprivate:\n\tstatic size_t ElementsPerBucket(size_t el_size)\n\t{\n\t\treturn std::max(16u / el_size, (size_t)1u);\t// see _DEQUESIZ\n\t}\n\n\tconst u8* GetNthElement(size_t i, size_t el_size) const\n\t{\n\t\tconst size_t el_per_bucket = ElementsPerBucket(el_size);\n\t\tconst size_t bucket_idx = i / el_per_bucket;\n\t\tENSURE(bucket_idx < _Mapsize);\n\t\tconst size_t idx_in_bucket = i - bucket_idx * el_per_bucket;\n\t\tENSURE(idx_in_bucket < el_per_bucket);\n\t\tconst u8** map = (const u8**)_Map;\n\t\tconst u8* bucket = map[bucket_idx];\n\t\tconst u8* p = bucket + idx_in_bucket*el_size;\n\t\treturn p;\n\t}\n\n#endif\n};\n\n\nstruct Any_list : public ContainerBase<std::list<int> >\n{\n};\n\n\n#if STL_DINKUMWARE == 405\n\ntemplate<class _Traits>\nstruct Any_tree : public std::_Tree<_Traits>\n{\n\tAny_tree()\t// (required because default ctor cannot be generated)\n\t{\n\t}\n\n\tbool IsValid(size_t UNUSED(el_size)) const\n\t{\n\t\treturn true;\n\t}\n\n\tsize_t NumElements(size_t UNUSED(el_size)) const\n\t{\n\t\treturn size();\n\t}\n\n\tstatic const u8* DereferenceAndAdvance(iterator& stl_it, size_t el_size)\n\t{\n\t\tstruct Iterator : public const_iterator\n\t\t{\n\t\t\t_Nodeptr Node() const\n\t\t\t{\n\t\t\t\treturn _Ptr;\n\t\t\t}\n\n\t\t\tvoid SetNode(_Nodeptr node)\n\t\t\t{\n\t\t\t\t_Ptr = node;\n\t\t\t}\n\t\t};\n\n\t\tIterator& it = *(Iterator*)&stl_it;\n\t\t_Nodeptr node = it.Node();\n\t\tconst u8* p = (const u8*)&*it;\n\n\t\t// end() shouldn't be incremented, don't move\n\t\tif(_Isnil(node, el_size))\n\t\t\treturn p;\n\n\t\t// return smallest (leftmost) node of right subtree\n\t\t_Nodeptr _Pnode = _Right(node);\n\t\tif(!_Isnil(_Pnode, el_size))\n\t\t{\n\t\t\twhile(!_Isnil(_Left(_Pnode), el_size))\n\t\t\t\t_Pnode = _Left(_Pnode);\n\t\t}\n\t\t// climb looking for right subtree\n\t\telse\n\t\t{\n\t\t\twhile (!_Isnil(_Pnode = _Parent(node), el_size) && node == _Right(_Pnode))\n\t\t\t\tnode = _Pnode;\t// ==> parent while right subtree\n\t\t}\n\t\tit.SetNode(_Pnode);\n\t\treturn p;\n\t};\n\nprivate:\n\t// return reference to the given node's nil flag.\n\t// reimplemented because this member is stored after _Myval, so it's\n\t// dependent on el_size.\n\tstatic _Charref _Isnil(_Nodeptr _Pnode, size_t el_size)\n\t{\n\t\tconst u8* p = (const u8*)&_Pnode->_Isnil;\t// correct for int specialization\n\t\tp += el_size - sizeof(value_type);\t// adjust for difference in el_size\n\t\tassert(*p <= 1);\t// bool value\n\t\treturn (_Charref)*p;\n\t}\n};\n\n\nstruct Any_map : public Any_tree<std::_Tmap_traits<int, int, std::less<int>, std::allocator<std::pair<const int, int> >, false> >\n{\n};\n\n\nstruct Any_multimap : public Any_map\n{\n};\n\n\nstruct Any_set: public Any_tree<std::_Tset_traits<int, std::less<int>, std::allocator<int>, false> >\n{\n};\n\n\nstruct Any_multiset: public Any_set\n{\n};\n\n#endif\n\n\nstruct Any_vector: public ContainerBase<std::vector<int> >\n{\n\tbool IsValid(size_t UNUSED(el_size)) const\n\t{\n\t\t// more elements reported than reserved\n\t\tif(size() > capacity())\n\t\t\treturn false;\n\t\t// front/back pointers incorrect\n\t\tif(&front() > &back())\n\t\t\treturn false;\n\t\treturn true;\n\t}\n\n#if STL_DINKUMWARE == 405\n\n\tsize_t NumElements(size_t el_size) const\n\t{\n\t\t// vectors store front and back pointers and calculate their\n\t\t// element count as the difference between them. since we are\n\t\t// derived from a template specialization, the pointer arithmetic\n\t\t// is incorrect. we fix it by taking el_size into account.\n\t\treturn ((u8*)_Mylast - (u8*)_Myfirst) * el_size;\n\t}\n\n\tstatic const u8* DereferenceAndAdvance(iterator& stl_it, size_t el_size)\n\t{\n\t\tstruct Iterator : public const_iterator\n\t\t{\n\t\t\tvoid Advance(size_t numBytes)\n\t\t\t{\n\t\t\t\t(u8*&)_Myptr += numBytes;\n\t\t\t}\n\t\t};\n\n\t\tIterator& it = *(Iterator*)&stl_it;\n\t\tconst u8* p = (const u8*)&*it;\n\t\tit.Advance(el_size);\n\t\treturn p;\n\t}\n\n#endif\n};\n\n\n#if STL_DINKUMWARE == 405\n\nstruct Any_basic_string : public ContainerBase<std::string>\n{\n\tbool IsValid(size_t el_size) const\n\t{\n\t\t// less than the small buffer reserved - impossible\n\t\tif(_Myres < (16/el_size)-1)\n\t\t\treturn false;\n\t\t// more elements reported than reserved\n\t\tif(_Mysize > _Myres)\n\t\t\treturn false;\n\t\treturn true;\n\t}\n};\n\n#endif\n\n\n//\n// standard container adapters\n//\n\n// debug_stl_get_container_info makes sure this was actually instantiated with\n// container = deque as we assume.\nstruct Any_queue : public Any_deque\n{\n};\n\n\n// debug_stl_get_container_info makes sure this was actually instantiated with\n// container = deque as we assume.\nstruct Any_stack : public Any_deque\n{\n};\n\n\n//-----------------------------------------------------------------------------\n\n// generic iterator - returns next element. dereferences and increments the\n// specific container iterator stored in it_mem.\ntemplate<class T> const u8* stl_iterator(void* it_mem, size_t el_size)\n{\n\ttypedef typename T::iterator iterator;\n\titerator& stl_it = *(iterator*)it_mem;\n\treturn T::DereferenceAndAdvance(stl_it, el_size);\n}\n\n\n// basic sanity checks that apply to all containers.\ntemplate<class T>\nstatic bool IsContainerValid(const T& t, size_t el_count)\n{\n\t// note: don't test empty() because vector's implementation of it\n\t// depends on el_size.\n\n\t// size must be reasonable\n\tif(el_count > 0x1000000)\n\t\treturn false;\n\n\tif(el_count != 0)\n\t{\n\t\t// valid pointer\n\t\tconst u8* front = (const u8*)&*t.begin();\t// (note: map doesn't have front)\n\t\tif(debug_IsPointerBogus(front))\n\t\t\treturn false;\n\n\t\t// note: don't test back() because that depends on el_size and\n\t\t// requires container-specific code.\n\t}\n\n\treturn true;\n}\n\n// check if the container is IsValid and return # elements and an iterator;\n// this is instantiated once for each type of container.\n// we don't do this in the Any_* ctors because we need to return bool IsValid and\n// don't want to throw an exception (may confuse the debug code).\ntemplate<class T> bool get_container_info(const T& t, size_t size, size_t el_size,\n\tsize_t& el_count, DebugStlIterator& el_iterator, void* it_mem)\n{\n\ttypedef typename T::iterator iterator;\n\ttypedef typename T::const_iterator const_iterator;\n\t\n\tENSURE(sizeof(T) == size);\n\tENSURE(sizeof(iterator) < DEBUG_STL_MAX_ITERATOR_SIZE);\n\n\tel_count = t.NumElements(el_size);\n\n\t// bail if the container is uninitialized/invalid.\n\tif(!IsContainerValid(t, el_count))\n\t\treturn false;\n\n\tel_iterator = stl_iterator<T>;\n\n\t// construct a copy of begin() at it_mem. placement new is necessary\n\t// because VC8's secure copy ctor apparently otherwise complains about\n\t// invalid values in the (uninitialized) destination memory.\n\tnew(it_mem) const_iterator(t.begin());\n\treturn true;\n}\n\n\n// if <wtype_name> indicates the object <p, size> to be an STL container,\n// and given the size of its value_type (retrieved via debug information),\n// return number of elements and an iterator (any data it needs is stored in\n// it_mem, which must hold DEBUG_STL_MAX_ITERATOR_SIZE bytes).\n// returns 0 on success or an StlContainerError.\nStatus debug_stl_get_container_info(const wchar_t* type_name, const u8* p, size_t size,\n\tsize_t el_size, size_t* el_count, DebugStlIterator* el_iterator, void* it_mem)\n{\n#if MSC_VERSION\n\tUNUSED2(type_name);\n\tUNUSED2(p);\n\tUNUSED2(size);\n\tUNUSED2(el_size);\n\tUNUSED2(el_count);\n\tUNUSED2(el_iterator);\n\tUNUSED2(it_mem);\n\treturn ERR::STL_CNT_UNSUPPORTED;\t// NOWARN\n#else\n\n\tbool handled = false, IsValid = false;\n#define CONTAINER(name, type_name_pattern)\\\n\telse if(match_wildcard(type_name, type_name_pattern))\\\n\t{\\\n\t\thandled = true;\\\n\t\tIsValid = get_container_info<Any_##name>(*(Any_##name*)p, size, el_size, *el_count, *el_iterator, it_mem);\\\n\t}\n#define STD_CONTAINER(name) CONTAINER(name, L\"std::\" WIDEN(#name) L\"<*>\")\n\n\t// workaround for preprocessor limitation: what we're trying to do is\n\t// stringize the defined value of a macro. prepending and pasting L\n\t// apparently isn't possible because macro args aren't expanded before\n\t// being pasted; we therefore compare as chars[].\n#define STRINGIZE2(id) # id\n#define STRINGIZE(id) STRINGIZE2(id)\n\n\tif(0) {}\t// kickoff\n\t// standard containers\n\tSTD_CONTAINER(deque)\n\tSTD_CONTAINER(list)\n\tSTD_CONTAINER(vector)\n#if STL_DINKUMWARE == 405\n\tSTD_CONTAINER(map)\n\tSTD_CONTAINER(multimap)\n\tSTD_CONTAINER(set)\n\tSTD_CONTAINER(multiset)\n\tSTD_CONTAINER(basic_string)\n#endif\n\t// standard container adapters\n\t// (note: Any_queue etc. assumes the underlying container is a deque.\n\t// we make sure of that here and otherwise refuse to display it, because\n\t// doing so is lots of work for little gain.)\n\tCONTAINER(queue, L\"std::queue<*,std::deque<*> >\")\n\tCONTAINER(stack, L\"std::stack<*,std::deque<*> >\")\n\n\t// note: do not raise warnings - these can happen for new\n\t// STL classes or if the debuggee's memory is corrupted.\n\tif(!handled)\n\t\treturn ERR::STL_CNT_UNKNOWN;\t// NOWARN\n\tif(!IsValid)\n\t\treturn ERR::STL_CNT_INVALID;\t// NOWARN\n\treturn INFO::OK;\n\n#endif\n}\n"
  },
  {
    "path": "fpsgame/gui/debug_stl.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * portable debugging helper functions specific to the STL.\n */\n\n#ifndef INCLUDED_DEBUG_STL\n#define INCLUDED_DEBUG_STL\n\n\nnamespace ERR\n{\n\tconst Status STL_CNT_UNKNOWN = -100500;\n\tconst Status STL_CNT_UNSUPPORTED = -100501;\n\t// likely causes: not yet initialized or memory corruption.\n\tconst Status STL_CNT_INVALID = -100502;\n}\n\n\n/**\n * reduce complicated STL symbol names to human-readable form.\n *\n * algorithm: remove/replace undesired substrings in one pass (fast).\n * example: \"std::basic_string\\<char, char_traits\\<char\\>,\n * std::allocator\\<char\\> \\>\" => \"string\".\n *\n * @param name Buffer holding input symbol name; modified in-place.\n *\t\t  There is no length limit; must be large enough to hold typical STL\n *\t\t  strings. DEBUG_SYMBOL_CHARS chars is a good measure.\n * @return name for convenience.\n **/\nextern wchar_t* debug_stl_simplify_name(wchar_t* name);\n\n\n/**\n * abstraction of all STL iterators used by debug_stl.\n **/\ntypedef const u8* (*DebugStlIterator)(void* internal, size_t el_size);\n\n/**\n * no STL iterator is larger than this; see below.\n **/\nconst size_t DEBUG_STL_MAX_ITERATOR_SIZE = 64;\n\n/**\n * prepare to enumerate the elements of arbitrarily typed STL containers.\n *\n * works by retrieving element count&size via debug information and hiding\n * the container's iterator implementation behind a common interface.\n * a basic sanity check is performed to see if the container memory is\n * valid and appears to be initialized.\n *\n * @param type_name exact type of STL container (see example above)\n * @param p raw memory holding the container\n * @param size sizeof(container)\n * @param el_size sizeof(value_type)\n * @param el_count out; number of valid elements in container\n * @param el_iterator out; callback function that acts as an iterator\n * @param it_mem out; buffer holding the iterator state. must be\n * at least DEBUG_STL_MAX_ITERATOR_SIZE bytes.\n * @return Status (ERR::STL_*)\n **/\nextern Status debug_stl_get_container_info(const wchar_t* type_name, const u8* p, size_t size,\n\tsize_t el_size, size_t* el_count, DebugStlIterator* el_iterator, void* it_mem);\n\n#endif\t// #ifndef INCLUDED_DEBUG_STL\n"
  },
  {
    "path": "fpsgame/gui/external_libraries/curl.h",
    "content": "/* Copyright (c) 2011 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * bring in libcurl header, with compatibility fixes\n */\n\n#ifndef INCLUDED_CURL\n#define INCLUDED_CURL\n\n#if OS_WIN\n\n// curl.h wants to include winsock2.h which causes conflicts.\n// provide some required definitions from winsock.h, then pretend\n// we already included winsock.h\n\ntypedef uintptr_t SOCKET;\n\nstruct sockaddr\n{\n\tunsigned short sa_family;\n\tchar sa_data[14];\n};\n\nstruct fd_set;\n\n#define _WINSOCKAPI_\t// winsock.h include guard\n\n#endif\t// OS_WIN\n\n#include <curl/curl.h>\n\n#endif\t// #ifndef INCLUDED_CURL\n"
  },
  {
    "path": "fpsgame/gui/external_libraries/dbghelp.cpp",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#include \"precompiled.h\"\n\n#if OS_WIN\n\n#include \"lib/sysdep/sysdep.h\"\n#include \"lib/sysdep/os/win/wutil.h\"\n#include \"lib/external_libraries/dbghelp.h\"\n\n// define extension function pointers\nextern \"C\"\n{\n#define FUNC(ret, name, params) ret (__stdcall *p##name) params;\n#include \"lib/external_libraries/dbghelp_funcs.h\"\n#undef FUNC\n}\n\nvoid dbghelp_ImportFunctions()\n{\n\t// for reasons unknown, LoadLibrary first checks the Dropbox shell\n\t// extension's directory (instead of \"The directory from which the\n\t// application loaded.\") and then the system directory, whose\n\t// dbghelp.dll is too old. we therefore specify the full path\n\t// to our executable directory, which contains a newer dbghelp.dll.\n\tconst OsPath pathname = sys_ExecutablePathname().Parent()/\"dbghelp.dll\";\n\tHMODULE hDbghelp = LoadLibraryW(OsString(pathname).c_str());\n\tENSURE(hDbghelp);\n#define FUNC(ret, name, params) p##name = (ret (__stdcall*) params)GetProcAddress(hDbghelp, #name);\n#include \"lib/external_libraries/dbghelp_funcs.h\"\n#undef FUNC\n\n\t// if this function is missing, the DLL is too old.\n\tENSURE(pSymInitializeW);\n}\n\n#endif // OS_WIN\n"
  },
  {
    "path": "fpsgame/gui/external_libraries/dbghelp.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * bring in dbghelp library.\n */\n\n#ifndef INCLUDED_DBGHELP\n#define INCLUDED_DBGHELP\n\n#include \"lib/sysdep/os/win/win.h\"\n\n#include <OAIdl.h>\t// VARIANT\n\n#if MSC_VERSION\n# pragma comment(lib, \"oleaut32.lib\")\t// VariantChangeType\n#endif\n\n\n//-----------------------------------------------------------------------------\n// fix omissions in the VC PSDK's dbghelp.h\n\n// the macros defined \"for those without specstrings.h\" are incorrect -\n// parameter definition is missing.\n#ifndef __specstrings\n# define __specstrings\t// prevent dbghelp from changing these\n\n# define __in\n# define __out\n# define __inout\n# define __in_opt\n# define __out_opt\n# define __inout_opt\n# define __in_ecount(s)\n# define __out_ecount(s)\n# define __inout_ecount(s)\n# define __in_bcount(s)\n# define __out_bcount(s)\n# define __inout_bcount(s)\n# define __deref_opt_out\n# define __deref_out\n\n#endif\n\n// (VC2005 defines __specstrings, but doesn't define (or use) __out_xcount,\n// so this is not inside the above #ifndef section)\n//\n// missing from dbghelp's list\n#ifndef __out_xcount\n# define __out_xcount(s)\n#endif\n\n\n//\n// not defined by dbghelp; these values are taken from DIA cvconst.h\n//\n\nenum BasicType\n{\n\tbtNoType    = 0,\n\tbtVoid      = 1,\n\tbtChar      = 2,\n\tbtWChar     = 3,\n\tbtInt       = 6,\n\tbtUInt      = 7,\n\tbtFloat     = 8,\n\tbtBCD       = 9,\n\tbtBool      = 10,\n\tbtLong      = 13,\n\tbtULong     = 14,\n\tbtCurrency  = 25,\n\tbtDate      = 26,\n\tbtVariant   = 27,\n\tbtComplex   = 28,\n\tbtBit       = 29,\n\tbtBSTR      = 30,\n\tbtHresult   = 31\n};\n\nenum DataKind\n{\n\tDataIsUnknown,\n\tDataIsLocal,\n\tDataIsStaticLocal,\n\tDataIsParam,\n\tDataIsObjectPtr,\n\tDataIsFileStatic,\n\tDataIsGlobal,\n\tDataIsMember,\n\tDataIsStaticMember,\n\tDataIsConstant\n};\n\n\n//-----------------------------------------------------------------------------\n\n#if ICC_VERSION\n# pragma warning(push)\n# pragma warning(disable:94)\t// the size of an array must be greater than zero\n# pragma warning(disable:271)\t// trailing comma is nonstandard\n# pragma warning(disable:418)\t// declaration requires a typedef name\n# pragma warning(disable:791)\t// calling convention specified more than once\n#endif\n\n#pragma pack(push, 8)\t// seems to be required\n\n#define _NO_CVCONST_H\t// request SymTagEnum be defined\n#include <dbghelp.h>\t// must come after win.h and the above definitions\n\n#pragma pack(pop)\n\n#if ICC_VERSION\n# pragma warning(pop)\n#endif\n\n\n//-----------------------------------------------------------------------------\n\n// rationale: Debugging Tools For Windows includes newer header/lib files,\n// but they're not redistributable. since we don't want everyone to\n// have to install that, it's preferable to link dynamically.\n\n// declare function pointers\n#define FUNC(ret, name, params) EXTERN_C ret (__stdcall *p##name) params;\n#include \"lib/external_libraries/dbghelp_funcs.h\"\n#undef FUNC\n\nextern void dbghelp_ImportFunctions();\n\n#endif\t// #ifndef INCLUDED_DBGHELP\n"
  },
  {
    "path": "fpsgame/gui/external_libraries/dbghelp_funcs.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\nFUNC(BOOL, SymInitializeW, (__in HANDLE hProcess, __in_opt PCWSTR UserSearchPath, __in BOOL fInvadeProcess))\nFUNC(DWORD, SymGetOptions, (VOID))\nFUNC(DWORD, SymSetOptions, (__in DWORD SymOptions))\nFUNC(DWORD64, SymGetModuleBase64, (__in HANDLE hProcess, __in DWORD64 qwAddr))\nFUNC(BOOL, SymFromAddrW, (__in HANDLE hProcess, __in DWORD64 Address, __out_opt PDWORD64 Displacement, __inout PSYMBOL_INFOW Symbol))\nFUNC(BOOL, SymGetLineFromAddrW64, (__in HANDLE hProcess, __in DWORD64 qwAddr, __out PDWORD pdwDisplacement, __out PIMAGEHLP_LINEW64 Line64))\nFUNC(PVOID, SymFunctionTableAccess64, (__in HANDLE hProcess, __in DWORD64 AddrBase))\nFUNC(BOOL, SymGetTypeInfo, (__in HANDLE hProcess, __in DWORD64 ModBase, __in ULONG TypeId, __in IMAGEHLP_SYMBOL_TYPE_INFO GetType, __out PVOID pInfo))\nFUNC(BOOL, SymFromIndexW, (__in HANDLE hProcess, __in ULONG64 BaseOfDll, __in DWORD Index, __inout PSYMBOL_INFOW Symbol))\nFUNC(BOOL, SymSetContext, (__in HANDLE hProcess, __in PIMAGEHLP_STACK_FRAME StackFrame, __in_opt PIMAGEHLP_CONTEXT Context))\nFUNC(BOOL, SymEnumSymbolsW, (__in HANDLE hProcess, __in ULONG64 BaseOfDll, __in_opt PCWSTR Mask, __in PSYM_ENUMERATESYMBOLS_CALLBACKW EnumSymbolsCallback, __in_opt PVOID UserContext))\nFUNC(PIMAGE_NT_HEADERS, ImageNtHeader, (__in PVOID Base))\nFUNC(BOOL, MiniDumpWriteDump, (IN HANDLE hProcess, IN DWORD ProcessId, IN HANDLE hFile, IN MINIDUMP_TYPE DumpType, IN CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, OPTIONAL IN CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, OPTIONAL IN CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam OPTIONAL))\nFUNC(BOOL, StackWalk64, (__in DWORD MachineType, __in HANDLE hProcess, __in HANDLE hThread, __inout LPSTACKFRAME64 StackFrame, __inout PVOID ContextRecord, __in_opt PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine, __in_opt PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine, __in_opt PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine, __in_opt PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress))\n"
  },
  {
    "path": "fpsgame/gui/external_libraries/enet.h",
    "content": "/* Copyright (c) 2011 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * bring in ENet header, with version check\n */\n\n#ifndef INCLUDED_ENET\n#define INCLUDED_ENET\n\n#if OS_WIN\n\n// enet/win32.h wants to include winsock2.h which causes conflicts.\n// provide some required definitions from winsock.h, then pretend\n// we already included winsock.h\n\ntypedef uintptr_t SOCKET;\n#define INVALID_SOCKET (SOCKET)(~0)\nstruct fd_set;\n\n#define _WINSOCK2API_\t// winsock2.h include guard\n\n#ifndef WIN32\n# define WIN32\n#endif\n\n#endif\t// OS_WIN\n\n#include <enet/enet.h>\n\n#if defined(ENET_VERSION_MAJOR) && !(ENET_VERSION_MAJOR > 1 || ENET_VERSION_MINOR > 2)\n#error The game currently requires ENet 1.3.x. You are using an older version, which\\\n has an incompatible API and network protocol. Please switch to a newer version.\n#endif\n\n#endif\t// #ifndef INCLUDED_ENET\n"
  },
  {
    "path": "fpsgame/gui/external_libraries/glext_funcs.h",
    "content": "/* Copyright (c) 2013 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * OpenGL extension function declarations (X macros).\n */\n\n#include \"lib/config2.h\" // CONFIG2_GLES\n\n#if CONFIG2_GLES\n# include <GLES2/gl2ext.h>\n#elif OS_MACOSX\n# include <OpenGL/glext.h>\n#else\n# include <GL/glext.h>\n#endif\n\n#if OS_WIN\n# include <GL/wglext.h>\n#endif\n\n/*\n\nFUNC is used for functions that are only extensions.\nFUNC2 is used for functions that have been promoted to core features.\nFUNC3 is used for functions that have been promoted to core features\nbut have slightly changed semantics and need to be referred to by their\ncore name instead of extension name.\n\nThe FUNC2/FUNC3 calls include the version of OpenGL in which the extension was promoted,\nand the pre- and post-promotion names (e.g. \"glBindBufferARB\" vs \"glBindBuffer\").\n\nIf the GL driver is advertising a sufficiently high version, we load the promoted\nname; otherwise we use the *ARB name. (The spec says:\n\t\"GL implementations of such later revisions should continue to export the name\n\t strings of promoted extensions in the EXTENSIONS string, and continue to support\n\t the ARB-affixed versions of functions and enumerants as a transition aid.\"\nbut some drivers might be stupid/buggy and fail to do that, so we don't just use\nthe ARB names unconditionally.)\n\nThe names are made accessible to engine code only via the ARB name, to make it\nobvious that care must be taken (i.e. by being certain that the extension is\nactually supported).\n\n*/\n\n#if CONFIG2_GLES\n\n// no GLES extensions used yet\n\n// some functions that are extensions in GL are core functions in GLES,\n// so we should use them without the function pointer indirection\n#define pglActiveTextureARB glActiveTexture\n#define pglBlendColorEXT glBlendColor\n#define pglBlendEquationEXT glBlendEquation\n#define pglClientActiveTextureARB glClientActiveTexture\n#define pglCompressedTexImage2DARB glCompressedTexImage2D\n\n#define pglAttachObjectARB glAttachShader\n#define pglBindAttribLocationARB glBindAttribLocation\n#define pglCompileShaderARB glCompileShader\n#define pglCreateProgramObjectARB glCreateProgram\n#define pglCreateShaderObjectARB glCreateShader\n#define pglDeleteProgram glDeleteProgram\n#define pglDeleteShader glDeleteShader\n#define pglDisableVertexAttribArrayARB glDisableVertexAttribArray\n#define pglEnableVertexAttribArrayARB glEnableVertexAttribArray\n#define pglGetActiveUniformARB glGetActiveUniform\n#define pglGetProgramiv glGetProgramiv\n#define pglGetProgramInfoLog glGetProgramInfoLog\n#define pglGetShaderiv glGetShaderiv\n#define pglGetShaderInfoLog glGetShaderInfoLog\n#define pglGetUniformLocationARB glGetUniformLocation\n#define pglLinkProgramARB glLinkProgram\n#define pglShaderSourceARB glShaderSource\n#define pglUniform1fARB glUniform1f\n#define pglUniform2fARB glUniform2f\n#define pglUniform3fARB glUniform3f\n#define pglUniform4fARB glUniform4f\n#define pglUniform1iARB glUniform1i\n#define pglUniformMatrix4fvARB glUniformMatrix4fv\n#define pglUseProgramObjectARB glUseProgram\n#define pglVertexAttribPointerARB glVertexAttribPointer\n\n#define pglBindBufferARB glBindBuffer\n#define pglBufferDataARB glBufferData\n#define pglBufferSubDataARB glBufferSubData\n#define pglDeleteBuffersARB glDeleteBuffers\n#define pglGenBuffersARB glGenBuffers\n#define pglMapBufferARB glMapBuffer\n#define pglUnmapBufferARB glUnmapBuffer\n\n#define pglBindFramebufferEXT glBindFramebuffer\n#define pglCheckFramebufferStatusEXT glCheckFramebufferStatus\n#define pglDeleteFramebuffersEXT glDeleteFramebuffers\n#define pglFramebufferTexture2DEXT glFramebufferTexture2D\n#define pglGenFramebuffersEXT glGenFramebuffers\n\n#define GL_DEPTH_ATTACHMENT_EXT GL_DEPTH_ATTACHMENT\n#define GL_COLOR_ATTACHMENT0_EXT GL_COLOR_ATTACHMENT0\n#define GL_FRAMEBUFFER_BINDING_EXT GL_FRAMEBUFFER_BINDING\n#define GL_FRAMEBUFFER_COMPLETE_EXT GL_FRAMEBUFFER_COMPLETE\n#define GL_FRAMEBUFFER_EXT GL_FRAMEBUFFER\n\n#define GL_CLAMP_TO_BORDER GL_CLAMP_TO_EDGE // TODO: should fix code that relies on GL_CLAMP_TO_BORDER\n\ntypedef GLuint GLhandleARB;\n\n#else\n\n// were these defined as real functions in gl.h already?\n\n// GL_EXT_draw_range_elements / GL1.2:\nFUNC2(void, glDrawRangeElementsEXT, glDrawRangeElements, \"1.2\", (GLenum, GLuint, GLuint, GLsizei, GLenum, GLvoid*))\n\n// GL_ARB_multitexture / GL1.3:\nFUNC2(void, glMultiTexCoord2fARB, glMultiTexCoord2f, \"1.3\", (int, float, float))\nFUNC2(void, glMultiTexCoord3fARB, glMultiTexCoord3f, \"1.3\", (int, float, float, float))\nFUNC2(void, glActiveTextureARB, glActiveTexture, \"1.3\", (int))\nFUNC2(void, glClientActiveTextureARB, glClientActiveTexture, \"1.3\", (int))\n\n// GL_EXT_blend_color / GL1.4 (optional in 1.2):\nFUNC2(void, glBlendColorEXT, glBlendColor, \"1.4\", (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha))\n\n// GL_EXT_blend_minmax / GL1.4 (optional in 1.2):\nFUNC2(void, glBlendEquationEXT, glBlendEquation, \"1.4\", (GLenum mode))\n\n// GL_ARB_vertex_buffer_object / GL1.5:\nFUNC2(void, glBindBufferARB, glBindBuffer, \"1.5\", (int target, GLuint buffer))\nFUNC2(void, glDeleteBuffersARB, glDeleteBuffers, \"1.5\", (GLsizei n, const GLuint* buffers))\nFUNC2(void, glGenBuffersARB, glGenBuffers, \"1.5\", (GLsizei n, GLuint* buffers))\nFUNC2(bool, glIsBufferARB, glIsBuffer, \"1.5\", (GLuint buffer))\nFUNC2(void, glBufferDataARB, glBufferData, \"1.5\", (int target, GLsizeiptrARB size, const void* data, int usage))\nFUNC2(void, glBufferSubDataARB, glBufferSubData, \"1.5\", (int target, GLintptrARB offset, GLsizeiptrARB size, const void* data))\nFUNC2(void, glGetBufferSubDataARB, glGetBufferSubData, \"1.5\", (int target, GLintptrARB offset, GLsizeiptrARB size, void* data))\nFUNC2(void*, glMapBufferARB, glMapBuffer, \"1.5\", (int target, int access))\nFUNC2(bool, glUnmapBufferARB, glUnmapBuffer, \"1.5\", (int target))\nFUNC2(void, glGetBufferParameterivARB, glGetBufferParameteriv, \"1.5\", (int target, int pname, int* params))\nFUNC2(void, glGetBufferPointervARB, glGetBufferPointerv, \"1.5\", (int target, int pname, void** params))\n\n// GL_ARB_texture_compression / GL1.3\nFUNC2(void, glCompressedTexImage3DARB, glCompressedTexImage3D, \"1.3\", (GLenum, GLint, GLenum, GLsizei, GLsizei, GLsizei, GLint, GLsizei, const GLvoid*))\nFUNC2(void, glCompressedTexImage2DARB, glCompressedTexImage2D, \"1.3\", (GLenum, GLint, GLenum, GLsizei, GLsizei, GLint, GLsizei, const GLvoid*))\nFUNC2(void, glCompressedTexImage1DARB, glCompressedTexImage1D, \"1.3\", (GLenum, GLint, GLenum, GLsizei, GLint, GLsizei, const GLvoid*))\nFUNC2(void, glCompressedTexSubImage3DARB, glCompressedTexSubImage3D, \"1.3\", (GLenum, GLint, GLint, GLint, GLint, GLsizei, GLsizei, GLsizei, GLenum, GLsizei, const GLvoid*))\nFUNC2(void, glCompressedTexSubImage2DARB, glCompressedTexSubImage2D, \"1.3\", (GLenum, GLint, GLint, GLint, GLsizei, GLsizei, GLenum, GLsizei, const GLvoid*))\nFUNC2(void, glCompressedTexSubImage1DARB, glCompressedTexSubImage1D, \"1.3\", (GLenum, GLint, GLint, GLsizei, GLenum, GLsizei, const GLvoid*))\nFUNC2(void, glGetCompressedTexImageARB, glGetCompressedTexImage, \"1.3\", (GLenum, GLint, GLvoid*))\n\n// GL_EXT_framebuffer_object\nFUNC(GLboolean, glIsRenderbufferEXT, (GLuint renderbuffer))\nFUNC(void, glBindRenderbufferEXT, (GLenum target, GLuint renderbuffer))\nFUNC(void, glDeleteRenderbuffersEXT, (GLsizei n, const GLuint *renderbuffers))\nFUNC(void, glGenRenderbuffersEXT, (GLsizei n, GLuint *renderbuffers))\nFUNC(void, glRenderbufferStorageEXT, (GLenum target, GLenum internalformat, GLsizei width, GLsizei height))\nFUNC(void, glGetRenderbufferParameterivEXT, (GLenum target, GLenum pname, GLint *params))\nFUNC(GLboolean, glIsFramebufferEXT, (GLuint framebuffer))\nFUNC(void, glBindFramebufferEXT, (GLenum target, GLuint framebuffer))\nFUNC(void, glDeleteFramebuffersEXT, (GLsizei n, const GLuint *framebuffers))\nFUNC(void, glGenFramebuffersEXT, (GLsizei n, GLuint *framebuffers))\nFUNC(GLenum, glCheckFramebufferStatusEXT, (GLenum target))\nFUNC(void, glFramebufferTexture1DEXT, (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level))\nFUNC(void, glFramebufferTexture2DEXT, (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level))\nFUNC(void, glFramebufferTexture3DEXT, (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset))\nFUNC(void, glFramebufferRenderbufferEXT, (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer))\nFUNC(void, glGetFramebufferAttachmentParameterivEXT, (GLenum target, GLenum attachment, GLenum pname, GLint *params))\nFUNC(void, glGenerateMipmapEXT, (GLenum target))\nFUNC(void, glBlitFramebufferEXT, (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter))\nFUNC(void, glDrawBuffers, (GLsizei n, const GLenum *bufs))\n\n// GL_ARB_vertex_program, GL_ARB_fragment_program\nFUNC(void, glProgramStringARB, (GLenum target, GLenum format, GLsizei len, const GLvoid *string))\nFUNC(void, glBindProgramARB, (GLenum target, GLuint program))\nFUNC(void, glDeleteProgramsARB, (GLsizei n, const GLuint *programs))\nFUNC(void, glGenProgramsARB, (GLsizei n, GLuint *programs))\nFUNC(void, glProgramEnvParameter4dARB, (GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w))\nFUNC(void, glProgramEnvParameter4dvARB, (GLenum target, GLuint index, const GLdouble *params))\nFUNC(void, glProgramEnvParameter4fARB, (GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w))\nFUNC(void, glProgramEnvParameter4fvARB, (GLenum target, GLuint index, const GLfloat *params))\nFUNC(void, glProgramLocalParameter4dARB, (GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w))\nFUNC(void, glProgramLocalParameter4dvARB, (GLenum target, GLuint index, const GLdouble *params))\nFUNC(void, glProgramLocalParameter4fARB, (GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w))\nFUNC(void, glProgramLocalParameter4fvARB, (GLenum target, GLuint index, const GLfloat *params))\nFUNC(void, glGetProgramEnvParameterdvARB, (GLenum target, GLuint index, GLdouble *params))\nFUNC(void, glGetProgramEnvParameterfvARB, (GLenum target, GLuint index, GLfloat *params))\nFUNC(void, glGetProgramLocalParameterdvARB, (GLenum target, GLuint index, GLdouble *params))\nFUNC(void, glGetProgramLocalParameterfvARB, (GLenum target, GLuint index, GLfloat *params))\nFUNC(void, glGetProgramivARB, (GLenum target, GLenum pname, GLint *params))\nFUNC(void, glGetProgramStringARB, (GLenum target, GLenum pname, GLvoid *string))\nFUNC(GLboolean, glIsProgramARB, (GLuint program))\n\n// GL_ARB_shader_objects\n// (NOTE: Many of these have \"Object\" in their ARB names, but \"Program\" or \"Shader\" in their core names.\n// When both Program and Shader versions exist, we use FUNC3 here and the engine must call the specific\n// core name instead of the generic ARB name.)\nFUNC3(void, glDeleteObjectARB, glDeleteShader, \"2.0\", (GLhandleARB obj))\nFUNC3(void, glDeleteObjectARB, glDeleteProgram, \"2.0\", (GLhandleARB obj))\n// FUNC2(GLhandleARB, glGetHandleARB, glGetHandle, \"2.0\", (GLenum pname))\n  // there is no analog to the ARB function in GL 2.0 (the functionality is probably moved into glGetIntegerv(GL_CURRENT_PROGRAM))\n  // so we can't represent it in this FUNC2 system, so just pretend it doesn't exist\nFUNC2(void, glDetachObjectARB, glDetachShader, \"2.0\", (GLhandleARB containerObj, GLhandleARB attachedObj))\nFUNC2(GLhandleARB, glCreateShaderObjectARB, glCreateShader, \"2.0\", (GLenum shaderType))\nFUNC2(void, glShaderSourceARB, glShaderSource, \"2.0\", (GLhandleARB shaderObj, GLsizei count, const char **string, const GLint *length))\nFUNC2(void, glCompileShaderARB, glCompileShader, \"2.0\", (GLhandleARB shaderObj))\nFUNC2(GLhandleARB, glCreateProgramObjectARB, glCreateProgram, \"2.0\", (void))\nFUNC2(void, glAttachObjectARB, glAttachShader, \"2.0\", (GLhandleARB containerObj, GLhandleARB obj))\nFUNC2(void, glLinkProgramARB, glLinkProgram, \"2.0\", (GLhandleARB programObj))\nFUNC2(void, glUseProgramObjectARB, glUseProgram, \"2.0\", (GLhandleARB programObj))\nFUNC2(void, glValidateProgramARB, glValidateProgram, \"2.0\", (GLhandleARB programObj))\nFUNC2(void, glUniform1fARB, glUniform1f, \"2.0\", (GLint location, GLfloat v0))\nFUNC2(void, glUniform2fARB, glUniform2f, \"2.0\", (GLint location, GLfloat v0, GLfloat v1))\nFUNC2(void, glUniform3fARB, glUniform3f, \"2.0\", (GLint location, GLfloat v0, GLfloat v1, GLfloat v2))\nFUNC2(void, glUniform4fARB, glUniform4f, \"2.0\", (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3))\nFUNC2(void, glUniform1iARB, glUniform1i, \"2.0\", (GLint location, GLint v0))\nFUNC2(void, glUniform2iARB, glUniform2i, \"2.0\", (GLint location, GLint v0, GLint v1))\nFUNC2(void, glUniform3iARB, glUniform3i, \"2.0\", (GLint location, GLint v0, GLint v1, GLint v2))\nFUNC2(void, glUniform4iARB, glUniform4i, \"2.0\", (GLint location, GLint v0, GLint v1, GLint v2, GLint v3))\nFUNC2(void, glUniform1fvARB, glUniform1fv, \"2.0\", (GLint location, GLsizei count, const GLfloat *value))\nFUNC2(void, glUniform2fvARB, glUniform2fv, \"2.0\", (GLint location, GLsizei count, const GLfloat *value))\nFUNC2(void, glUniform3fvARB, glUniform3fv, \"2.0\", (GLint location, GLsizei count, const GLfloat *value))\nFUNC2(void, glUniform4fvARB, glUniform4fv, \"2.0\", (GLint location, GLsizei count, const GLfloat *value))\nFUNC2(void, glUniform1ivARB, glUniform1iv, \"2.0\", (GLint location, GLsizei count, const GLint *value))\nFUNC2(void, glUniform2ivARB, glUniform2iv, \"2.0\", (GLint location, GLsizei count, const GLint *value))\nFUNC2(void, glUniform3ivARB, glUniform3iv, \"2.0\", (GLint location, GLsizei count, const GLint *value))\nFUNC2(void, glUniform4ivARB, glUniform4iv, \"2.0\", (GLint location, GLsizei count, const GLint *value))\nFUNC2(void, glUniformMatrix2fvARB, glUniformMatrix2fv, \"2.0\", (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value))\nFUNC2(void, glUniformMatrix3fvARB, glUniformMatrix3fv, \"2.0\", (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value))\nFUNC2(void, glUniformMatrix4fvARB, glUniformMatrix4fv, \"2.0\", (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value))\nFUNC3(void, glGetObjectParameterfvARB, glGetProgramfv, \"2.0\", (GLhandleARB obj, GLenum pname, GLfloat *params))\nFUNC3(void, glGetObjectParameterfvARB, glGetShaderfv, \"2.0\", (GLhandleARB obj, GLenum pname, GLfloat *params))\nFUNC3(void, glGetObjectParameterivARB, glGetProgramiv, \"2.0\", (GLhandleARB obj, GLenum pname, GLint *params))\nFUNC3(void, glGetObjectParameterivARB, glGetShaderiv, \"2.0\", (GLhandleARB obj, GLenum pname, GLint *params))\nFUNC3(void, glGetInfoLogARB, glGetProgramInfoLog, \"2.0\", (GLhandleARB obj, GLsizei maxLength, GLsizei *length, char *infoLog))\nFUNC3(void, glGetInfoLogARB, glGetShaderInfoLog, \"2.0\", (GLhandleARB obj, GLsizei maxLength, GLsizei *length, char *infoLog))\nFUNC2(void, glGetAttachedObjectsARB, glGetAttachedShaders, \"2.0\", (GLhandleARB containerObj, GLsizei maxCount, GLsizei *count, GLhandleARB *obj))\nFUNC2(GLint, glGetUniformLocationARB, glGetUniformLocation, \"2.0\", (GLhandleARB programObj, const char *name))\nFUNC2(void, glGetActiveUniformARB, glGetActiveUniform, \"2.0\", (GLhandleARB programObj, GLuint index, GLsizei maxLength, GLsizei *length, GLint *size, GLenum *type, char *name))\nFUNC2(void, glGetUniformfvARB, glGetUniformfv, \"2.0\", (GLhandleARB programObj, GLint location, GLfloat *params))\nFUNC2(void, glGetUniformivARB, glGetUniformiv, \"2.0\", (GLhandleARB programObj, GLint location, GLint *params))\nFUNC2(void, glGetShaderSourceARB, glGetShaderSource, \"2.0\", (GLhandleARB obj, GLsizei maxLength, GLsizei *length, GLcharARB *source))\n\n// GL_ARB_vertex_shader\nFUNC2(void, glVertexAttrib1fARB, glVertexAttrib1f, \"2.0\", (GLuint index, GLfloat v0))\nFUNC2(void, glVertexAttrib1sARB, glVertexAttrib1s, \"2.0\", (GLuint index, GLshort v0))\nFUNC2(void, glVertexAttrib1dARB, glVertexAttrib1d, \"2.0\", (GLuint index, GLdouble v0))\nFUNC2(void, glVertexAttrib2fARB, glVertexAttrib2f, \"2.0\", (GLuint index, GLfloat v0, GLfloat v1))\nFUNC2(void, glVertexAttrib2sARB, glVertexAttrib2s, \"2.0\", (GLuint index, GLshort v0, GLshort v1))\nFUNC2(void, glVertexAttrib2dARB, glVertexAttrib2d, \"2.0\", (GLuint index, GLdouble v0, GLdouble v1))\nFUNC2(void, glVertexAttrib3fARB, glVertexAttrib3f, \"2.0\", (GLuint index, GLfloat v0, GLfloat v1, GLfloat v2))\nFUNC2(void, glVertexAttrib3sARB, glVertexAttrib3s, \"2.0\", (GLuint index, GLshort v0, GLshort v1, GLshort v2))\nFUNC2(void, glVertexAttrib3dARB, glVertexAttrib3d, \"2.0\", (GLuint index, GLdouble v0, GLdouble v1, GLdouble v2))\nFUNC2(void, glVertexAttrib4fARB, glVertexAttrib4f, \"2.0\", (GLuint index, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3))\nFUNC2(void, glVertexAttrib4sARB, glVertexAttrib4s, \"2.0\", (GLuint index, GLshort v0, GLshort v1, GLshort v2, GLshort v3))\nFUNC2(void, glVertexAttrib4dARB, glVertexAttrib4d, \"2.0\", (GLuint index, GLdouble v0, GLdouble v1, GLdouble v2, GLdouble v3))\nFUNC2(void, glVertexAttrib4NubARB, glVertexAttrib4Nub, \"2.0\", (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w))\nFUNC2(void, glVertexAttrib1fvARB, glVertexAttrib1fv, \"2.0\", (GLuint index, const GLfloat *v))\nFUNC2(void, glVertexAttrib1svARB, glVertexAttrib1sv, \"2.0\", (GLuint index, const GLshort *v))\nFUNC2(void, glVertexAttrib1dvARB, glVertexAttrib1dv, \"2.0\", (GLuint index, const GLdouble *v))\nFUNC2(void, glVertexAttrib2fvARB, glVertexAttrib2fv, \"2.0\", (GLuint index, const GLfloat *v))\nFUNC2(void, glVertexAttrib2svARB, glVertexAttrib2sv, \"2.0\", (GLuint index, const GLshort *v))\nFUNC2(void, glVertexAttrib2dvARB, glVertexAttrib2dv, \"2.0\", (GLuint index, const GLdouble *v))\nFUNC2(void, glVertexAttrib3fvARB, glVertexAttrib3fv, \"2.0\", (GLuint index, const GLfloat *v))\nFUNC2(void, glVertexAttrib3svARB, glVertexAttrib3sv, \"2.0\", (GLuint index, const GLshort *v))\nFUNC2(void, glVertexAttrib3dvARB, glVertexAttrib3dv, \"2.0\", (GLuint index, const GLdouble *v))\nFUNC2(void, glVertexAttrib4fvARB, glVertexAttrib4fv, \"2.0\", (GLuint index, const GLfloat *v))\nFUNC2(void, glVertexAttrib4svARB, glVertexAttrib4sv, \"2.0\", (GLuint index, const GLshort *v))\nFUNC2(void, glVertexAttrib4dvARB, glVertexAttrib4dv, \"2.0\", (GLuint index, const GLdouble *v))\nFUNC2(void, glVertexAttrib4ivARB, glVertexAttrib4iv, \"2.0\", (GLuint index, const GLint *v))\nFUNC2(void, glVertexAttrib4bvARB, glVertexAttrib4bv, \"2.0\", (GLuint index, const GLbyte *v))\nFUNC2(void, glVertexAttrib4ubvARB, glVertexAttrib4ubv, \"2.0\", (GLuint index, const GLubyte *v))\nFUNC2(void, glVertexAttrib4usvARB, glVertexAttrib4usv, \"2.0\", (GLuint index, const GLushort *v))\nFUNC2(void, glVertexAttrib4uivARB, glVertexAttrib4uiv, \"2.0\", (GLuint index, const GLuint *v))\nFUNC2(void, glVertexAttrib4NbvARB, glVertexAttrib4Nbv, \"2.0\", (GLuint index, const GLbyte *v))\nFUNC2(void, glVertexAttrib4NsvARB, glVertexAttrib4Nsv, \"2.0\", (GLuint index, const GLshort *v))\nFUNC2(void, glVertexAttrib4NivARB, glVertexAttrib4Niv, \"2.0\", (GLuint index, const GLint *v))\nFUNC2(void, glVertexAttrib4NubvARB, glVertexAttrib4Nubv, \"2.0\", (GLuint index, const GLubyte *v))\nFUNC2(void, glVertexAttrib4NusvARB, glVertexAttrib4Nusv, \"2.0\", (GLuint index, const GLushort *v))\nFUNC2(void, glVertexAttrib4NuivARB, glVertexAttrib4Nuiv, \"2.0\", (GLuint index, const GLuint *v))\nFUNC2(void, glVertexAttribPointerARB, glVertexAttribPointer, \"2.0\", (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer))\nFUNC2(void, glEnableVertexAttribArrayARB, glEnableVertexAttribArray, \"2.0\", (GLuint index))\nFUNC2(void, glDisableVertexAttribArrayARB, glDisableVertexAttribArray, \"2.0\", (GLuint index))\nFUNC2(void, glBindAttribLocationARB, glBindAttribLocation, \"2.0\", (GLhandleARB programObj, GLuint index, const char *name))\nFUNC2(void, glGetActiveAttribARB, glGetActiveAttrib, \"2.0\", (GLhandleARB programObj, GLuint index, GLsizei maxLength, GLsizei *length, int *size, GLenum *type, char *name))\nFUNC2(GLint, glGetAttribLocationARB, glGetAttribLocation, \"2.0\", (GLhandleARB programObj, const char *name))\nFUNC2(void, glGetVertexAttribdvARB, glGetVertexAttribdv, \"2.0\", (GLuint index, GLenum pname, GLdouble *params))\nFUNC2(void, glGetVertexAttribfvARB, glGetVertexAttribfv, \"2.0\", (GLuint index, GLenum pname, GLfloat *params))\nFUNC2(void, glGetVertexAttribivARB, glGetVertexAttribiv, \"2.0\", (GLuint index, GLenum pname, GLint *params))\nFUNC2(void, glGetVertexAttribPointervARB, glGetVertexAttribPointerv, \"2.0\", (GLuint index, GLenum pname, void **pointer))\n\n// GL_EXT_gpu_shader4 / GL3.0:\nFUNC2(void, glVertexAttribI1iEXT, glVertexAttribI1i, \"3.0\", (GLuint index, GLint x))\nFUNC2(void, glVertexAttribI2iEXT, glVertexAttribI2i, \"3.0\", (GLuint index, GLint x, GLint y))\nFUNC2(void, glVertexAttribI3iEXT, glVertexAttribI3i, \"3.0\", (GLuint index, GLint x, GLint y, GLint z))\nFUNC2(void, glVertexAttribI4iEXT, glVertexAttribI4i, \"3.0\", (GLuint index, GLint x, GLint y, GLint z, GLint w))\nFUNC2(void, glVertexAttribI1uiEXT, glVertexAttribI1ui, \"3.0\", (GLuint index, GLuint x))\nFUNC2(void, glVertexAttribI2uiEXT, glVertexAttribI2ui, \"3.0\", (GLuint index, GLuint x, GLuint y))\nFUNC2(void, glVertexAttribI3uiEXT, glVertexAttribI3ui, \"3.0\", (GLuint index, GLuint x, GLuint y, GLuint z))\nFUNC2(void, glVertexAttribI4uiEXT, glVertexAttribI4ui, \"3.0\", (GLuint index, GLuint x, GLuint y, GLuint z, GLuint w))\nFUNC2(void, glVertexAttribI1ivEXT, glVertexAttribI1iv, \"3.0\", (GLuint index, const GLint *v))\nFUNC2(void, glVertexAttribI2ivEXT, glVertexAttribI2iv, \"3.0\", (GLuint index, const GLint *v))\nFUNC2(void, glVertexAttribI3ivEXT, glVertexAttribI3iv, \"3.0\", (GLuint index, const GLint *v))\nFUNC2(void, glVertexAttribI4ivEXT, glVertexAttribI4iv, \"3.0\", (GLuint index, const GLint *v))\nFUNC2(void, glVertexAttribI1uivEXT, glVertexAttribI1uiv, \"3.0\", (GLuint index, const GLuint *v))\nFUNC2(void, glVertexAttribI2uivEXT, glVertexAttribI2uiv, \"3.0\", (GLuint index, const GLuint *v))\nFUNC2(void, glVertexAttribI3uivEXT, glVertexAttribI3uiv, \"3.0\", (GLuint index, const GLuint *v))\nFUNC2(void, glVertexAttribI4uivEXT, glVertexAttribI4uiv, \"3.0\", (GLuint index, const GLuint *v))\nFUNC2(void, glVertexAttribI4bvEXT, glVertexAttribI4bv, \"3.0\", (GLuint index, const GLbyte *v))\nFUNC2(void, glVertexAttribI4svEXT, glVertexAttribI4sv, \"3.0\", (GLuint index, const GLshort *v))\nFUNC2(void, glVertexAttribI4ubvEXT, glVertexAttribI4ubv, \"3.0\", (GLuint index, const GLubyte *v))\nFUNC2(void, glVertexAttribI4usvEXT, glVertexAttribI4usv, \"3.0\", (GLuint index, const GLushort *v))\nFUNC2(void, glVertexAttribIPointerEXT, glVertexAttribIPointer, \"3.0\", (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer))\nFUNC2(void, glGetVertexAttribIivEXT, glGetVertexAttribIiv, \"3.0\", (GLuint index, GLenum pname, GLint *params))\nFUNC2(void, glGetVertexAttribIuivEXT, glGetVertexAttribIuiv, \"3.0\", (GLuint index, GLenum pname, GLuint *params))\nFUNC2(void, glUniform1uiEXT, glUniform1ui, \"3.0\", (GLint location, GLuint v0))\nFUNC2(void, glUniform2uiEXT, glUniform2ui, \"3.0\", (GLint location, GLuint v0, GLuint v1))\nFUNC2(void, glUniform3uiEXT, glUniform3ui, \"3.0\", (GLint location, GLuint v0, GLuint v1, GLuint v2))\nFUNC2(void, glUniform4uiEXT, glUniform4ui, \"3.0\", (GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3))\nFUNC2(void, glUniform1uivEXT, glUniform1uiv, \"3.0\", (GLint location, GLsizei count, const GLuint *value))\nFUNC2(void, glUniform2uivEXT, glUniform2uiv, \"3.0\", (GLint location, GLsizei count, const GLuint *value))\nFUNC2(void, glUniform3uivEXT, glUniform3uiv, \"3.0\", (GLint location, GLsizei count, const GLuint *value))\nFUNC2(void, glUniform4uivEXT, glUniform4uiv, \"3.0\", (GLint location, GLsizei count, const GLuint *value))\nFUNC2(void, glGetUniformuivEXT, glGetUniformuiv, \"3.0\", (GLuint program, GLint location, GLuint *params))\nFUNC2(void, glBindFragDataLocationEXT, glBindFragDataLocation, \"3.0\", (GLuint program, GLuint colorNumber, const char *name))\nFUNC2(GLint, glGetFragDataLocationEXT, glGetFragDataLocation, \"3.0\", (GLuint program, const char *name))\n\n// GL_ARB_occlusion_query / GL1.5:\nFUNC2(void, glGenQueriesARB, glGenQueries, \"1.5\", (GLsizei n, GLuint *ids))\nFUNC2(void, glDeleteQueriesARB, glDeleteQueries, \"1.5\", (GLsizei n, const GLuint *ids))\nFUNC2(GLboolean, glIsQueryARB, glIsQuery, \"1.5\", (GLuint id))\nFUNC2(void, glBeginQueryARB, glBeginQuery, \"1.5\", (GLenum target, GLuint id))\nFUNC2(void, glEndQueryARB, glEndQuery, \"1.5\", (GLenum target))\nFUNC2(void, glGetQueryivARB, glGetQueryiv, \"1.5\", (GLenum target, GLenum pname, GLint *params))\nFUNC2(void, glGetQueryObjectivARB, glGetQueryObjectiv, \"1.5\", (GLuint id, GLenum pname, GLint *params))\nFUNC2(void, glGetQueryObjectuivARB, glGetQueryObjectuiv, \"1.5\", (GLuint id, GLenum pname, GLuint *params))\n\n// GL_ARB_sync / GL3.2:\nFUNC2(void, glGetInteger64v, glGetInteger64v, \"3.2\", (GLenum pname, GLint64 *params))\n\n// GL_EXT_timer_query:\nFUNC(void, glGetQueryObjecti64vEXT, (GLuint id, GLenum pname, GLint64 *params))\nFUNC(void, glGetQueryObjectui64vEXT, (GLuint id, GLenum pname, GLuint64 *params))\n\n// GL_ARB_timer_query / GL3.3:\nFUNC2(void, glQueryCounter, glQueryCounter, \"3.3\", (GLuint id, GLenum target))\nFUNC2(void, glGetQueryObjecti64v, glGetQueryObjecti64v, \"3.3\", (GLuint id, GLenum pname, GLint64 *params))\nFUNC2(void, glGetQueryObjectui64v, glGetQueryObjectui64v, \"3.3\", (GLuint id, GLenum pname, GLuint64 *params))\n\n// GL_ARB_map_buffer_range / GL3.0:\nFUNC2(void*, glMapBufferRange, glMapBufferRange, \"3.0\", (GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access))\nFUNC2(void, glFlushMappedBufferRange, glFlushMappedBufferRange, \"3.0\", (GLenum target, GLintptr offset, GLsizeiptr length))\n\n// GL_GREMEDY_string_marker (from gDEBugger)\nFUNC(int, glStringMarkerGREMEDY, (GLsizei len, const GLvoid *string))\n\n// GL_INTEL_performance_queries (undocumented, may be unstable, use at own risk;\n// see http://zaynar.co.uk/docs/gl-intel-performance-queries.html)\nFUNC(void, glGetFirstPerfQueryIdINTEL, (GLuint *queryId))\nFUNC(void, glGetNextPerfQueryIdINTEL, (GLuint prevQueryId, GLuint *queryId))\nFUNC(void, glGetPerfQueryInfoINTEL, (GLuint queryId, GLuint nameMaxLength, char *name, GLuint *counterBufferSize, GLuint *numCounters, GLuint *maxQueries, GLuint *))\nFUNC(void, glGetPerfCounterInfoINTEL, (GLuint queryId, GLuint counterId, GLuint nameMaxLength, char *name, GLuint descMaxLength, char *desc, GLuint *offset, GLuint *size, GLuint *usage, GLuint *type, GLuint64 *))\nFUNC(void, glCreatePerfQueryINTEL, (GLuint queryId, GLuint *id))\nFUNC(void, glBeginPerfQueryINTEL, (GLuint id))\nFUNC(void, glEndPerfQueryINTEL, (GLuint id))\nFUNC(void, glDeletePerfQueryINTEL, (GLuint id))\nFUNC(void, glGetPerfQueryDataINTEL, (GLuint id, GLenum requestType, GLuint maxLength, char *buffer, GLuint *length))\n\n#endif\t// #if CONFIG_GLES2\n\n\n#if OS_WIN\n// WGL_EXT_swap_control\nFUNC(int, wglSwapIntervalEXT, (int))\n\n// WGL_ARB_pbuffer\nFUNC(HPBUFFERARB, wglCreatePbufferARB, (HDC, int, int, int, const int*))\nFUNC(HDC, wglGetPbufferDCARB, (HPBUFFERARB))\nFUNC(int, wglReleasePbufferDCARB, (HPBUFFERARB, HDC))\nFUNC(int, wglDestroyPbufferARB, (HPBUFFERARB))\nFUNC(int, wglQueryPbufferARB, (HPBUFFERARB, int, int*))\n\n// GL_ARB_pixel_format\nFUNC(int, wglGetPixelFormatAttribivARB, (HDC, int, int, unsigned int, const int*, int*))\nFUNC(int, wglGetPixelFormatAttribfvARB, (HDC, int, int, unsigned int, const int*, float*))\nFUNC(int, wglChoosePixelFormatARB, (HDC, const int *, const float*, unsigned int, int*, unsigned int*))\n#endif // OS_WIN\n\n\n// GLX_MESA_query_renderer\nFUNC(int /*Bool*/, glXQueryRendererIntegerMESA, (void /*Display*/ *dpy, int screen, int renderer, int attribute, unsigned int *value))\nFUNC(int /*Bool*/, glXQueryCurrentRendererIntegerMESA, (int attribute, unsigned int *value))\nFUNC(const char *, glXQueryRendererStringMESA, (void /*Display*/ *dpy, int screen, int renderer, int attribute))\nFUNC(const char *, glXQueryCurrentRendererStringMESA, (int attribute))\n"
  },
  {
    "path": "fpsgame/gui/external_libraries/icu.h",
    "content": "/* Copyright (c) 2013 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#ifndef INCLUDED_ICU\n#define INCLUDED_ICU\n\n#include <unicode/smpdtfmt.h>\n\n#endif\t// #ifndef INCLUDED_ICU\n"
  },
  {
    "path": "fpsgame/gui/external_libraries/libsdl.h",
    "content": "/* Copyright (c) 2015 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * SDL header; uses emulator on Windows, otherwise libsdl.\n */\n\n#ifndef INCLUDED_SDL\n#define INCLUDED_SDL\n\n#include \"lib/external_libraries/libsdl_fwd.h\"\n\n# include \"SDL.h\"\n# include \"SDL_thread.h\"\n\n#if !SDL_VERSION_ATLEAST(2,0,2)\n#error You are using an old libsdl release. At least libsdl2 >= 2.0.2 is required.\n#endif\n\n// if the compiler doesn't support inlining, this header will pull\n// in static bswap routines. doesn't matter - modern compilers\n// will strip them if unused, and this is more convenient than\n// another header that toggles between wsdl and SDL_endian.h.\n# include \"SDL_endian.h\"\n\n# if MSC_VERSION\n#  pragma comment(lib, \"SDL2\")\n#  pragma comment(lib, \"SDL2main\")\n# endif\n\n// complete definition of our forward-declared SDL_Event (see sdl_fwd.h)\nstruct SDL_Event_\n{\n\tSDL_Event ev;\n};\n\n#endif // INCLUDED_SDL\n"
  },
  {
    "path": "fpsgame/gui/external_libraries/libsdl_fwd.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * forward declaration of SDL_Event\n */\n\n#ifndef INCLUDED_SDL_FWD\n#define INCLUDED_SDL_FWD\n\n// 2006-08-26 SDL is dragged into 6 of our 7 static library components.\n// it must be specified in each of their \"extern_libs\" so that the\n// include path is set and <SDL.h> can be found.\n//\n// obviously this is bad, so we work around the root cause. mostly only\n// SDL_Event is needed. unfortunately it cannot be forward-declared,\n// because it is a union (regrettable design mistake).\n// we fix this by wrapping it in a struct, which can safely be\n// forward-declared and used for SDL_Event_* parameters.\nstruct SDL_Event_;\n\n#endif\t// #ifndef INCLUDED_SDL_FWD\n"
  },
  {
    "path": "fpsgame/gui/external_libraries/openal.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * bring in OpenAL header+library, with compatibility fixes\n */\n\n#ifndef INCLUDED_OPENAL\n#define INCLUDED_OPENAL\n\n#if OS_MACOSX\n# include <OpenAL/al.h>\n# include <OpenAL/alc.h>\n#else\n# include <AL/al.h>\n# include <AL/alc.h>\n#endif\n\n// ALC strings (e.g. device and extension names) are typed differently\n// between OpenAL specifications\n#ifdef AL_VERSION_1_1\ntypedef ALCchar* alcString;\n#else\ntypedef ALCubyte* alcString;\n#endif\n\n#if MSC_VERSION\n# pragma comment(lib, \"openal32.lib\")\n#endif\n\n#endif\t// #ifndef INCLUDED_OPENAL\n"
  },
  {
    "path": "fpsgame/gui/external_libraries/opengl.h",
    "content": "/* Copyright (c) 2011 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * bring in OpenGL header+library, with compatibility fixes\n */\n\n#ifndef INCLUDED_OPENGL\n#define INCLUDED_OPENGL\n\n#include \"lib/config2.h\" // CONFIG2_GLES\n\n#if OS_WIN\n// wgl.h is a private header and should only be included from here.\n// if this isn't defined, it'll complain.\n#define WGL_HEADER_NEEDED\n#include \"lib/sysdep/os/win/wgl.h\"\n#endif\n\n#if CONFIG2_GLES\n# include <GLES2/gl2.h>\n#elif OS_MACOSX || OS_MAC\n# include <OpenGL/gl.h>\n#else\n# include <GL/gl.h>\n#endif\n\n// if gl.h provides real prototypes for 1.2 / 1.3 functions,\n// exclude the corresponding function pointers in glext_funcs.h\n#ifdef GL_VERSION_1_2\n#define REAL_GL_1_2\n#endif\n#ifdef GL_VERSION_1_3\n#define REAL_GL_1_3\n#endif\n\n// this must come after GL/gl.h include, so we can't combine the\n// including GL/glext.h.\n#undef GL_GLEXT_PROTOTYPES\n\n#if CONFIG2_GLES\n# include <GLES2/gl2ext.h>\n#elif OS_MACOSX || OS_MAC\n# include <OpenGL/glext.h>\n#else\n# include <GL/glext.h>\n# if OS_WIN\n#  include <GL/wglext.h>\n# endif\n#endif\n\n#endif\t// #ifndef INCLUDED_OPENGL\n"
  },
  {
    "path": "fpsgame/gui/external_libraries/openmp.h",
    "content": "/* Copyright (c) 2015 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#ifndef INCLUDED_EXTERNAL_LIBRARIES_OPENMP\n#define INCLUDED_EXTERNAL_LIBRARIES_OPENMP\n\n// allows removing all OpenMP-related code via #define ENABLE_OPENMP 0\n// before including this header. (useful during debugging, because the\n// VC debugger isn't able to display OpenMP private variables)\n#ifdef ENABLE_OPENMP\n# if ENABLE_OPENMP && !defined(_OPENMP)\n#  error \"either enable OpenMP in the compiler settings or don't set ENABLE_OPENMP to 1\"\n# endif\n#else\t// no user preference; default to compiler setting\n# ifdef _OPENMP\n#  define ENABLE_OPENMP 1\n# else\n#  define ENABLE_OPENMP 0\n# endif\n#endif\n\n#if ENABLE_OPENMP\n# include <omp.h>\n#else\n# define omp_get_num_threads() 1\n# define omp_get_thread_num() 0\n# define omp_in_parallel() 0\n#endif\n\n// wrapper macro that evaluates to nothing if !ENABLE_OPENMP\n// (much more convenient than individual #if ENABLE_OPENMP)\n#if ENABLE_OPENMP\n# if MSC_VERSION\n#  define OMP(args) __pragma(omp args)\n# elif GCC_VERSION\n#  define OMP _Pragma(\"omp \" #args)\n# else\n#  error \"port\"\n# endif\n#else\n# define OMP(args)\n#endif\n\n#endif\t// #ifndef INCLUDED_EXTERNAL_LIBRARIES_OPENMP\n"
  },
  {
    "path": "fpsgame/gui/external_libraries/png.h",
    "content": "/* Copyright (c) 2014 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * bring in LibPNG header+library, with compatibility fixes\n */\n\n#ifndef INCLUDED_PNG\n#define INCLUDED_PNG\n\n// <png.h> includes <zlib.h>, which requires some fixes by our header.\n#include \"lib/external_libraries/zlib.h\"\n\n#include <png.h>\n\n// automatically link against the required library\n#if MSC_VERSION\n# ifdef NDEBUG\n#  pragma comment(lib, \"libpng16.lib\")\n# else\n#  pragma comment(lib, \"libpng16d.lib\")\n# endif\t// NDEBUG\n#endif\t// MSC_VERSION\n\n#endif\t//\t#ifndef INCLUDED_PNG\n"
  },
  {
    "path": "fpsgame/gui/external_libraries/powrprof.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * bring in powrprof library.\n */\n\n#ifndef INCLUDED_POWRPROF\n#define INCLUDED_POWRPROF\n\n#include \"lib/sysdep/os/win/win.h\"\n\n#include <powrprof.h>\n\n// the VC7 headers are missing some parts:\n\n// MinGW headers are already correct; only change on VC\n#if MSC_VERSION && MSC_VERSION < 1400\n\n#ifndef NTSTATUS\n#define NTSTATUS long\n#endif\n#ifndef STATUS_SUCCESS\n#define STATUS_SUCCESS 0\n#endif\n\n#if WINVER < 0x500\n\ntypedef enum {\n\tSystemPowerPolicyAc,\n\tSystemPowerPolicyDc,\n\tVerifySystemPolicyAc,\n\tVerifySystemPolicyDc,\n\tSystemPowerCapabilities,\n\tSystemBatteryState,\n\tSystemPowerStateHandler,\n\tProcessorStateHandler,\n\tSystemPowerPolicyCurrent,\n\tAdministratorPowerPolicy,\n\tSystemReserveHiberFile,\n\tProcessorInformation,\n\tSystemPowerInformation,\n\tProcessorStateHandler2,\n\tLastWakeTime,                                   // Compare with KeQueryInterruptTime()\n\tLastSleepTime,                                  // Compare with KeQueryInterruptTime()\n\tSystemExecutionState,\n\tSystemPowerStateNotifyHandler,\n\tProcessorPowerPolicyAc,\n\tProcessorPowerPolicyDc,\n\tVerifyProcessorPowerPolicyAc,\n\tVerifyProcessorPowerPolicyDc,\n\tProcessorPowerPolicyCurrent,\n\tSystemPowerStateLogging,\n\tSystemPowerLoggingEntry\n} POWER_INFORMATION_LEVEL;\n\n\ntypedef struct {\n\tDWORD       Granularity;\n\tDWORD       Capacity;\n} BATTERY_REPORTING_SCALE, *PBATTERY_REPORTING_SCALE;\n\ntypedef enum _SYSTEM_POWER_STATE {\n\tPowerSystemUnspecified = 0,\n\tPowerSystemWorking     = 1,\n\tPowerSystemSleeping1   = 2,\n\tPowerSystemSleeping2   = 3,\n\tPowerSystemSleeping3   = 4,\n\tPowerSystemHibernate   = 5,\n\tPowerSystemShutdown    = 6,\n\tPowerSystemMaximum     = 7\n} SYSTEM_POWER_STATE, *PSYSTEM_POWER_STATE;\n\ntypedef struct {\n\t// Misc supported system features\n\tBOOLEAN             PowerButtonPresent;\n\tBOOLEAN             SleepButtonPresent;\n\tBOOLEAN             LidPresent;\n\tBOOLEAN             SystemS1;\n\tBOOLEAN             SystemS2;\n\tBOOLEAN             SystemS3;\n\tBOOLEAN             SystemS4;           // hibernate\n\tBOOLEAN             SystemS5;           // off\n\tBOOLEAN             HiberFilePresent;\n\tBOOLEAN             FullWake;\n\tBOOLEAN             VideoDimPresent;\n\tBOOLEAN             ApmPresent;\n\tBOOLEAN             UpsPresent;\n\n\t// Processors\n\tBOOLEAN             ThermalControl;\n\tBOOLEAN             ProcessorThrottle;\n\tBYTE                ProcessorMinThrottle;\n\tBYTE                ProcessorMaxThrottle;\n\tBYTE                spare2[4];\n\n\t// Disk\n\tBOOLEAN             DiskSpinDown;\n\tBYTE                spare3[8];\n\n\t// System Battery\n\tBOOLEAN             SystemBatteriesPresent;\n\tBOOLEAN             BatteriesAreShortTerm;\n\tBATTERY_REPORTING_SCALE BatteryScale[3];\n\n\t// Wake\n\tSYSTEM_POWER_STATE  AcOnLineWake;\n\tSYSTEM_POWER_STATE  SoftLidWake;\n\tSYSTEM_POWER_STATE  RtcWake;\n\tSYSTEM_POWER_STATE  MinDeviceWakeState; // note this may change on driver load\n\tSYSTEM_POWER_STATE  DefaultLowLatencyWake;\n} SYSTEM_POWER_CAPABILITIES, *PSYSTEM_POWER_CAPABILITIES;\n\n#endif\t// WINVER < 0x500\n\ntypedef struct _SYSTEM_POWER_INFORMATION\n{\n\tULONG MaxIdlenessAllowed;\n\tULONG Idleness;\n\tULONG TimeRemaining;\n\tUCHAR CoolingMode;\n} SYSTEM_POWER_INFORMATION, *PSYSTEM_POWER_INFORMATION;\n\n// SPI.CoolingMode\n#define PO_TZ_INVALID_MODE 0 // The system does not support CPU throttling,\n// or there is no thermal zone defined [..]\n\n#endif\t// #if MSC_VERSION\n\n// neither VC7.1 nor MinGW define this\ntypedef struct _PROCESSOR_POWER_INFORMATION\n{\n\tULONG Number;\n\tULONG MaxMhz;\n\tULONG CurrentMhz;\n\tULONG MhzLimit;\n\tULONG MaxIdleState;\n\tULONG CurrentIdleState;\n} PROCESSOR_POWER_INFORMATION, *PPROCESSOR_POWER_INFORMATION;\n\n#endif\t// #ifndef INCLUDED_POWRPROF\n"
  },
  {
    "path": "fpsgame/gui/external_libraries/suppress_boost_warnings.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n// rationale: boost is only included from the PCH, but warnings are still\n// raised when templates are instantiated (e.g. vfs_lookup.cpp),\n// so include this header from such source files as well.\n\n#include \"lib/sysdep/compiler.h\"\t// ICC_VERSION\n#include \"lib/sysdep/arch.h\"\t// ARCH_IA32\n\n#if MSC_VERSION\n# pragma warning(disable:4710) // function not inlined\n#endif\n#if ICC_VERSION\n# pragma warning(push)\n# pragma warning(disable:82)\t// storage class is not first\n# pragma warning(disable:193)\t// zero used for undefined preprocessing identifier \n# pragma warning(disable:304)\t// access control not specified\n# pragma warning(disable:367)\t// duplicate friend declaration\n# pragma warning(disable:444)\t// destructor for base class is not virtual\n# pragma warning(disable:522)\t// function redeclared inline after being called\n# pragma warning(disable:811)\t// exception specification for implicitly declared virtual function is incompatible with that of overridden function\n# pragma warning(disable:1879)\t// unimplemented pragma ignored\n# pragma warning(disable:2270)\t// the declaration of the copy assignment operator has been suppressed\n# pragma warning(disable:2273)\t// the declaration of the copy constructor has been suppressed\n# if ARCH_IA32\n#  pragma warning(disable:693)\t// calling convention specified here is ignored\n# endif\n#endif\n"
  },
  {
    "path": "fpsgame/gui/external_libraries/tinygettext.h",
    "content": "/* Copyright (c) 2013 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * Bring in the TinyGettext header file.\n */\n\n#ifndef INCLUDED_TINYGETTEXT\n#define INCLUDED_TINYGETTEXT\n\n#if MSC_VERSION\n# pragma warning(push)\n# pragma warning(disable:4251) // \"class X needs to have dll-interface to be used by clients of class Y\"\n# pragma warning(disable:4800) // \"forcing value to bool 'true' or 'false' (performance warning)\"\n#endif\n\n#include <tinygettext/tinygettext.hpp>\n#include <tinygettext/po_parser.hpp>\n#include <tinygettext/log.hpp>\n\n#if MSC_VERSION\n# pragma warning(pop)\n#endif\n\n#endif\t// INCLUDED_TINYGETTEXT\n"
  },
  {
    "path": "fpsgame/gui/external_libraries/vorbis.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * bring in OGG Vorbis header+library\n */\n\n#ifndef INCLUDED_VORBIS\n#define INCLUDED_VORBIS\n\n#include <vorbis/vorbisfile.h>\n\n#if MSC_VERSION\n# ifdef NDEBUG\n#  pragma comment(lib, \"vorbisfile.lib\")\n# else\n#  pragma comment(lib, \"vorbisfile_d.lib\")\n# endif\n#endif\n\n#endif\t// #ifndef INCLUDED_VORBIS\n"
  },
  {
    "path": "fpsgame/gui/external_libraries/wxwidgets.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * bring in wxWidgets headers, with compatibility fixes\n */\n\n#ifndef INCLUDED_WXWIDGETS\n#define INCLUDED_WXWIDGETS\n\n// prevent wxWidgets from pulling in windows.h - it's mostly unnecessary\n// and interferes with posix_sock's declarations.\n#define _WINDOWS_\t// <windows.h> include guard\n\n// manually define what is actually needed from windows.h:\nstruct HINSTANCE__\n{\n\tint unused;\n};\ntypedef struct HINSTANCE__* HINSTANCE;\t// definition as if STRICT were #defined\n\n\n#include \"wx/wxprec.h\"\n\n#include \"wx/file.h\"\n#include \"wx/ffile.h\"\n#include \"wx/filename.h\"\n#include \"wx/mimetype.h\"\n#include \"wx/statline.h\"\n#include \"wx/debugrpt.h\"\n\n#ifdef __WXMSW__\n#include \"wx/evtloop.h\"     // for SetCriticalWindow()\n#endif // __WXMSW__\n\n// note: wxWidgets already does #pragma comment(lib) to add link targets.\n\n#endif\t// #ifndef INCLUDED_WXWIDGETS\n"
  },
  {
    "path": "fpsgame/gui/external_libraries/zlib.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * bring in ZLib header+library, with compatibility fixes\n */\n\n#ifndef INCLUDED_ZLIB\n#define INCLUDED_ZLIB\n\n// zlib.h -> zconf.h includes <windows.h>, which causes conflicts.\n// define the include guard to prevent it from actually being included and\n// then manually define the few things that are actually needed.\n#define _WINDOWS_\t// windows.h include guard\n#ifndef WINAPI\n# define WINAPI __stdcall\n# define WINAPIV __cdecl\n#endif\n\n#ifndef ZLIB_STATIC\n#define ZLIB_DLL\n#endif\n\n#include <zlib.h>\n\n// automatically link against the required library\n#if MSC_VERSION\n# ifdef NDEBUG\n#  pragma comment(lib, \"zlib1.lib\")\n# else\n#  pragma comment(lib, \"zlib1d.lib\")\n# endif\n#endif\n\n#endif\t// #ifndef INCLUDED_ZLIB\n"
  },
  {
    "path": "fpsgame/gui/file/archive/archive.cpp",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * interface for reading from and creating archives.\n */\n\n#include \"precompiled.h\"\n#include \"lib/file/archive/archive.h\"\n\nstatic const StatusDefinition archiveStatusDefinitions[] = {\n\t{ ERR::ARCHIVE_UNKNOWN_FORMAT, L\"Unknown archive format\" },\n\t{ ERR::ARCHIVE_UNKNOWN_METHOD, L\"Unknown compression method\" }\n};\nSTATUS_ADD_DEFINITIONS(archiveStatusDefinitions);\n\nIArchiveReader::~IArchiveReader()\n{\n}\n\nIArchiveWriter::~IArchiveWriter()\n{\n}\n"
  },
  {
    "path": "fpsgame/gui/file/archive/archive.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * interface for reading from and creating archives.\n */\n\n#ifndef INCLUDED_ARCHIVE\n#define INCLUDED_ARCHIVE\n\n#include \"lib/file/file_system.h\"\t// FileInfo\n#include \"lib/file/common/file_loader.h\"\n#include \"lib/file/vfs/vfs_path.h\"\n\n// rationale: this module doesn't build a directory tree of the entries\n// within an archive. that task is left to the VFS; here, we are only\n// concerned with enumerating all archive entries.\n\nnamespace ERR\n{\n\tconst Status ARCHIVE_UNKNOWN_FORMAT = -110400;\n\tconst Status ARCHIVE_UNKNOWN_METHOD = -110401;\n}\n\nstruct IArchiveFile : public IFileLoader\n{\n};\n\ntypedef shared_ptr<IArchiveFile> PIArchiveFile;\n\nstruct IArchiveReader\n{\n\tvirtual ~IArchiveReader();\n\n\t/**\n\t * called for each archive entry.\n\t * @param pathname full pathname of entry; only valid during the callback.\n\t **/\n\ttypedef void (*ArchiveEntryCallback)(const VfsPath& pathname, const CFileInfo& fileInfo, PIArchiveFile archiveFile, uintptr_t cbData);\n\tvirtual Status ReadEntries(ArchiveEntryCallback cb, uintptr_t cbData) = 0;\n};\n\ntypedef shared_ptr<IArchiveReader> PIArchiveReader;\n\n// note: when creating an archive, any existing file with the given pathname\n// will be overwritten.\n\n// rationale: don't support partial adding, i.e. updating archive with\n// only one file. this would require overwriting parts of the Zip archive,\n// which is annoying and slow. also, archives are usually built in\n// seek-optimal order, which would break if we start inserting files.\n// while testing, loose files can be used, so there's no loss.\n\nstruct IArchiveWriter\n{\n\t/**\n\t * write out the archive to disk; only hereafter is it valid.\n\t **/\n\tvirtual ~IArchiveWriter();\n\n\t/**\n\t * add a file to the archive.\n\t *\n\t * rationale: passing in a filename instead of the compressed file\n\t * contents makes for better encapsulation because callers don't need\n\t * to know about the codec. one disadvantage is that loading the file\n\t * contents can no longer take advantage of the VFS cache nor previously\n\t * archived versions. however, the archive builder usually adds files\n\t * precisely because they aren't in archives, and the cache would\n\t * thrash anyway, so this is deemed acceptable.\n\t *\n\t * @param pathname the actual file to add\n\t * @param pathnameInArchive the name to store in the archive\n\t **/\n\tvirtual Status AddFile(const OsPath& pathname, const Path& pathnameInArchive) = 0;\n\n\t/**\n\t * add a file to the archive, when it is already in memory and not on disk.\n\t *\n\t * @param data the uncompressed file contents to add\n\t * @param size the length of data\n\t * @param mtime the last-modified-time to be stored in the archive\n\t * @param pathnameInArchive the name to store in the archive\n\t **/\n\tvirtual Status AddMemory(const u8* data, size_t size, time_t mtime, const OsPath& pathnameInArchive) = 0;\n};\n\ntypedef shared_ptr<IArchiveWriter> PIArchiveWriter;\n\n#endif\t// #ifndef INCLUDED_ARCHIVE\n"
  },
  {
    "path": "fpsgame/gui/file/archive/archive_zip.cpp",
    "content": "/* Copyright (c) 2015 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * archive backend for Zip files.\n */\n\n#include \"precompiled.h\"\n#include \"lib/file/archive/archive_zip.h\"\n\n#include <time.h>\n#include <limits>\n\n#include \"lib/utf8.h\"\n#include \"lib/bits.h\"\n#include \"lib/byte_order.h\"\n#include \"lib/allocators/pool.h\"\n#include \"lib/sysdep/filesystem.h\"\n#include \"lib/file/archive/archive.h\"\n#include \"lib/file/archive/codec_zlib.h\"\n#include \"lib/file/archive/stream.h\"\n#include \"lib/file/file.h\"\n#include \"lib/file/io/io.h\"\n\n//-----------------------------------------------------------------------------\n// timestamp conversion: DOS FAT <-> Unix time_t\n//-----------------------------------------------------------------------------\n\nstatic time_t time_t_from_FAT(u32 fat_timedate)\n{\n\tconst u32 fat_time = bits(fat_timedate, 0, 15);\n\tconst u32 fat_date = bits(fat_timedate, 16, 31);\n\n\tstruct tm t;\t\t\t\t\t\t\t// struct tm format:\n\tt.tm_sec   = bits(fat_time, 0,4) * 2;\t// [0,59]\n\tt.tm_min   = bits(fat_time, 5,10);\t\t// [0,59]\n\tt.tm_hour  = bits(fat_time, 11,15);\t\t// [0,23]\n\tt.tm_mday  = bits(fat_date, 0,4);\t\t// [1,31]\n\tt.tm_mon   = bits(fat_date, 5,8) - 1;\t// [0,11]\n\tt.tm_year  = bits(fat_date, 9,15) + 80;\t// since 1900\n\tt.tm_isdst = -1;\t// unknown - let libc determine\n\n\t// otherwise: totally bogus, and at the limit of 32-bit time_t\n\tENSURE(t.tm_year < 138);\n\n\ttime_t ret = mktime(&t);\n\tENSURE(ret != (time_t)-1);\t// mktime shouldn't fail\n\treturn ret;\n}\n\n\nstatic u32 FAT_from_time_t(time_t time)\n{\n\t// (values are adjusted for DST)\n\tstruct tm* t = localtime(&time);\n\n\tconst u16 fat_time = u16(\n\t\t(t->tm_sec/2) |\t\t    // 5\n\t\t(u16(t->tm_min) << 5) | // 6\n\t\t(u16(t->tm_hour) << 11)\t// 5\n\t\t);\n\n\tconst u16 fat_date = u16(\n\t\t(t->tm_mday) |            // 5\n\t\t(u16(t->tm_mon+1) << 5) | // 4\n\t\t(u16(t->tm_year-80) << 9) // 7\n\t\t);\n\n\tu32 fat_timedate = u32_from_u16(fat_date, fat_time);\n\treturn fat_timedate;\n}\n\n\n//-----------------------------------------------------------------------------\n// Zip archive definitions\n//-----------------------------------------------------------------------------\n\nstatic const u32 cdfh_magic = FOURCC_LE('P','K','\\1','\\2');\nstatic const u32  lfh_magic = FOURCC_LE('P','K','\\3','\\4');\nstatic const u32 ecdr_magic = FOURCC_LE('P','K','\\5','\\6');\n\nenum ZipMethod\n{\n\tZIP_METHOD_NONE    = 0,\n\tZIP_METHOD_DEFLATE = 8\n};\n\n#pragma pack(push, 1)\n\nclass LFH\n{\npublic:\n\tvoid Init(const CFileInfo& fileInfo, off_t csize, ZipMethod method, u32 checksum, const Path& pathname)\n\t{\n\t\tconst std::string pathnameUTF8 = utf8_from_wstring(pathname.string());\n\t\tconst size_t pathnameSize = pathnameUTF8.length();\n\n\t\tm_magic     = lfh_magic;\n\t\tm_x1        = to_le16(0);\n\t\tm_flags     = to_le16(0);\n\t\tm_method    = to_le16(u16_from_larger(method));\n\t\tm_fat_mtime = to_le32(FAT_from_time_t(fileInfo.MTime()));\n\t\tm_crc       = to_le32(checksum);\n\t\tm_csize     = to_le32(u32_from_larger(csize));\n\t\tm_usize     = to_le32(u32_from_larger(fileInfo.Size()));\n\t\tm_fn_len    = to_le16(u16_from_larger(pathnameSize));\n\t\tm_e_len     = to_le16(0);\n\n\t\tmemcpy((char*)this + sizeof(LFH), pathnameUTF8.c_str(), pathnameSize);\n\t}\n\n\tsize_t Size() const\n\t{\n\t\tENSURE(m_magic == lfh_magic);\n\t\tsize_t size = sizeof(LFH);\n\t\tsize += read_le16(&m_fn_len);\n\t\tsize += read_le16(&m_e_len);\n\t\t// note: LFH doesn't have a comment field!\n\t\treturn size;\n\t}\n\nprivate:\n\tu32 m_magic;\n\tu16 m_x1;\t\t\t// version needed\n\tu16 m_flags;\n\tu16 m_method;\n\tu32 m_fat_mtime;\t// last modified time (DOS FAT format)\n\tu32 m_crc;\n\tu32 m_csize;\n\tu32 m_usize;\n\tu16 m_fn_len;\n\tu16 m_e_len;\n};\n\ncassert(sizeof(LFH) == 30);\n\n\nclass CDFH\n{\npublic:\n\tvoid Init(const CFileInfo& fileInfo, off_t ofs, off_t csize, ZipMethod method, u32 checksum, const Path& pathname, size_t slack)\n\t{\n\t\tconst std::string pathnameUTF8 = utf8_from_wstring(pathname.string());\n\t\tconst size_t pathnameLength = pathnameUTF8.length();\n\n\t\tm_magic     = cdfh_magic;\n\t\tm_x1        = to_le32(0);\n\t\tm_flags     = to_le16(0);\n\t\tm_method    = to_le16(u16_from_larger(method));\n\t\tm_fat_mtime = to_le32(FAT_from_time_t(fileInfo.MTime()));\n\t\tm_crc       = to_le32(checksum);\n\t\tm_csize     = to_le32(u32_from_larger(csize));\n\t\tm_usize     = to_le32(u32_from_larger(fileInfo.Size()));\n\t\tm_fn_len    = to_le16(u16_from_larger(pathnameLength));\n\t\tm_e_len     = to_le16(0);\n\t\tm_c_len     = to_le16(u16_from_larger((size_t)slack));\n\t\tm_x2        = to_le32(0);\n\t\tm_x3        = to_le32(0);\n\t\tm_lfh_ofs   = to_le32(u32_from_larger(ofs));\n\n\t\tmemcpy((char*)this + sizeof(CDFH), pathnameUTF8.c_str(), pathnameLength);\n\t}\n\n\tPath Pathname() const\n\t{\n\t\tconst size_t length = (size_t)read_le16(&m_fn_len);\n\t\tconst char* pathname = (const char*)this + sizeof(CDFH); // not 0-terminated!\n\t\treturn Path(std::string(pathname, length));\n\t}\n\n\toff_t HeaderOffset() const\n\t{\n\t\treturn read_le32(&m_lfh_ofs);\n\t}\n\n\toff_t USize() const\n\t{\n\t\treturn (off_t)read_le32(&m_usize);\n\t}\n\n\toff_t CSize() const\n\t{\n\t\treturn (off_t)read_le32(&m_csize);\n\t}\n\n\tZipMethod Method() const\n\t{\n\t\treturn (ZipMethod)read_le16(&m_method);\n\t}\n\n\tu32 Checksum() const\n\t{\n\t\treturn read_le32(&m_crc);\n\t}\n\n\ttime_t MTime() const\n\t{\n\t\tconst u32 fat_mtime = read_le32(&m_fat_mtime);\n\t\treturn time_t_from_FAT(fat_mtime);\n\t}\n\n\tsize_t Size() const\n\t{\n\t\tsize_t size = sizeof(CDFH);\n\t\tsize += read_le16(&m_fn_len);\n\t\tsize += read_le16(&m_e_len);\n\t\tsize += read_le16(&m_c_len);\n\t\treturn size;\n\t}\n\nprivate:\n\tu32 m_magic;\n\tu32 m_x1;\t\t\t// versions\n\tu16 m_flags;\n\tu16 m_method;\n\tu32 m_fat_mtime;\t// last modified time (DOS FAT format)\n\tu32 m_crc;\n\tu32 m_csize;\n\tu32 m_usize;\n\tu16 m_fn_len;\n\tu16 m_e_len;\n\tu16 m_c_len;\n\tu32 m_x2;\t\t\t// spanning\n\tu32 m_x3;\t\t\t// attributes\n\tu32 m_lfh_ofs;\n};\n\ncassert(sizeof(CDFH) == 46);\n\n\nclass ECDR\n{\npublic:\n\tvoid Init(size_t cd_numEntries, off_t cd_ofs, size_t cd_size)\n\t{\n\t\tm_magic         = ecdr_magic;\n\t\tm_diskNum       = to_le16(0);\n\t\tm_cd_diskNum    = to_le16(0);\n\t\tm_cd_numEntriesOnDisk = to_le16(u16_from_larger(cd_numEntries));\n\t\tm_cd_numEntries = m_cd_numEntriesOnDisk;\n\t\tm_cd_size       = to_le32(u32_from_larger(cd_size));\n\t\tm_cd_ofs        = to_le32(u32_from_larger(cd_ofs));\n\t\tm_comment_len   = to_le16(0);\n\t}\n\n\tvoid Decompose(size_t& cd_numEntries, off_t& cd_ofs, size_t& cd_size) const\n\t{\n\t\tcd_numEntries = (size_t)read_le16(&m_cd_numEntries);\n\t\tcd_ofs       = (off_t)read_le32(&m_cd_ofs);\n\t\tcd_size      = (size_t)read_le32(&m_cd_size);\n\t}\n\nprivate:\n\tu32 m_magic;\n\tu16 m_diskNum;\n\tu16 m_cd_diskNum;\n\tu16 m_cd_numEntriesOnDisk;\n\tu16 m_cd_numEntries;\n\tu32 m_cd_size;\n\tu32 m_cd_ofs;\n\tu16 m_comment_len;\n};\n\ncassert(sizeof(ECDR) == 22);\n\n#pragma pack(pop)\n\n\n//-----------------------------------------------------------------------------\n// ArchiveFile_Zip\n//-----------------------------------------------------------------------------\n\nclass ArchiveFile_Zip : public IArchiveFile\n{\npublic:\n\tArchiveFile_Zip(const PFile& file, off_t ofs, off_t csize, u32 checksum, ZipMethod method)\n\t\t: m_file(file), m_ofs(ofs)\n\t\t, m_csize(csize), m_checksum(checksum), m_method((u16)method)\n\t\t, m_flags(NeedsFixup)\n\t{\n\t}\n\n\tvirtual size_t Precedence() const\n\t{\n\t\treturn 2u;\n\t}\n\n\tvirtual wchar_t LocationCode() const\n\t{\n\t\treturn 'A';\n\t}\n\n\tvirtual OsPath Path() const\n\t{\n\t\treturn m_file->Pathname();\n\t}\n\n\tvirtual Status Load(const OsPath& UNUSED(name), const shared_ptr<u8>& buf, size_t size) const\n\t{\n\t\tAdjustOffset();\n\n\t\tPICodec codec;\n\t\tswitch(m_method)\n\t\t{\n\t\tcase ZIP_METHOD_NONE:\n\t\t\tcodec = CreateCodec_ZLibNone();\n\t\t\tbreak;\n\t\tcase ZIP_METHOD_DEFLATE:\n\t\t\tcodec = CreateDecompressor_ZLibDeflate();\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tWARN_RETURN(ERR::ARCHIVE_UNKNOWN_METHOD);\n\t\t}\n\n\t\tStream stream(codec);\n\t\tstream.SetOutputBuffer(buf.get(), size);\n\t\tio::Operation op(*m_file.get(), 0, m_csize, m_ofs);\n\t\tStreamFeeder streamFeeder(stream);\n\t\tRETURN_STATUS_IF_ERR(io::Run(op, io::Parameters(), streamFeeder));\n\t\tRETURN_STATUS_IF_ERR(stream.Finish());\n#if CODEC_COMPUTE_CHECKSUM\n\t\tENSURE(m_checksum == stream.Checksum());\n#endif\n\n\t\treturn INFO::OK;\n\t}\n\nprivate:\n\tenum Flags\n\t{\n\t\t// indicates m_ofs points to a \"local file header\" instead of\n\t\t// the file data. a fixup routine is called when reading the file;\n\t\t// it skips past the LFH and clears this flag.\n\t\t// this is somewhat of a hack, but vital to archive open performance.\n\t\t// without it, we'd have to scan through the entire archive file,\n\t\t// which can take *seconds*.\n\t\t// (we cannot use the information in CDFH, because its 'extra' field\n\t\t// has been observed to differ from that of the LFH)\n\t\t// since we read the LFH right before the rest of the file, the block\n\t\t// cache will absorb the IO cost.\n\t\tNeedsFixup = 1\n\t};\n\n\tstruct LFH_Copier\n\t{\n\t\tLFH_Copier(u8* lfh_dst, size_t lfh_bytes_remaining)\n\t\t\t: lfh_dst(lfh_dst), lfh_bytes_remaining(lfh_bytes_remaining)\n\t\t{\n\t\t}\n\n\t\t// this code grabs an LFH struct from file block(s) that are\n\t\t// passed to the callback. usually, one call copies the whole thing,\n\t\t// but the LFH may straddle a block boundary.\n\t\t//\n\t\t// rationale: this allows using temp buffers for zip_fixup_lfh,\n\t\t// which avoids involving the file buffer manager and thus\n\t\t// avoids cluttering the trace and cache contents.\n\t\tStatus operator()(const u8* block, size_t size) const\n\t\t{\n\t\t\tENSURE(size <= lfh_bytes_remaining);\n\t\t\tmemcpy(lfh_dst, block, size);\n\t\t\tlfh_dst += size;\n\t\t\tlfh_bytes_remaining -= size;\n\n\t\t\treturn INFO::OK;\n\t\t}\n\n\t\tmutable u8* lfh_dst;\n\t\tmutable size_t lfh_bytes_remaining;\n\t};\n\n\t/**\n\t * fix up m_ofs (adjust it to point to cdata instead of the LFH).\n\t *\n\t * note: we cannot use CDFH filename and extra field lengths to skip\n\t * past LFH since that may not mirror CDFH (has happened).\n\t *\n\t * this is called at file-open time instead of while mounting to\n\t * reduce seeks: since reading the file will typically follow, the\n\t * block cache entirely absorbs the IO cost.\n\t **/\n\tvoid AdjustOffset() const\n\t{\n\t\tif(!(m_flags & NeedsFixup))\n\t\t\treturn;\n\t\tm_flags &= ~NeedsFixup;\n\n\t\t// performance note: this ends up reading one file block, which is\n\t\t// only in the block cache if the file starts in the same block as a\n\t\t// previously read file (i.e. both are small).\n\t\tLFH lfh;\n\t\tio::Operation op(*m_file.get(), 0, sizeof(LFH), m_ofs);\n\t\tif(io::Run(op, io::Parameters(), LFH_Copier((u8*)&lfh, sizeof(LFH))) == INFO::OK)\n\t\t\tm_ofs += (off_t)lfh.Size();\n\t}\n\n\tPFile m_file;\n\n\t// all relevant LFH/CDFH fields not covered by CFileInfo\n\tmutable off_t m_ofs;\n\toff_t m_csize;\n\tu32 m_checksum;\n\tu16 m_method;\n\tmutable u16 m_flags;\n};\n\n\n//-----------------------------------------------------------------------------\n// ArchiveReader_Zip\n//-----------------------------------------------------------------------------\n\nclass ArchiveReader_Zip : public IArchiveReader\n{\npublic:\n\tArchiveReader_Zip(const OsPath& pathname)\n\t\t: m_file(new File(pathname, O_RDONLY))\n\t{\n\t\tCFileInfo fileInfo;\n\t\tGetFileInfo(pathname, &fileInfo);\n\t\tm_fileSize = fileInfo.Size();\n\t\tconst size_t minFileSize = sizeof(LFH)+sizeof(CDFH)+sizeof(ECDR);\n\t\tENSURE(m_fileSize >= off_t(minFileSize));\n\t}\n\n\tvirtual Status ReadEntries(ArchiveEntryCallback cb, uintptr_t cbData)\n\t{\n\t\t// locate and read Central Directory\n\t\toff_t cd_ofs = 0;\n\t\tsize_t cd_numEntries = 0;\n\t\tsize_t cd_size = 0;\n\t\tRETURN_STATUS_IF_ERR(LocateCentralDirectory(m_file, m_fileSize, cd_ofs, cd_numEntries, cd_size));\n\t\tUniqueRange buf(std::move(io::Allocate(cd_size)));\n\n\t\tio::Operation op(*m_file.get(), buf.get(), cd_size, cd_ofs);\n\t\tRETURN_STATUS_IF_ERR(io::Run(op));\n\n\t\t// iterate over Central Directory\n\t\tconst u8* pos = (const u8*)buf.get();\n\t\tfor(size_t i = 0; i < cd_numEntries; i++)\n\t\t{\n\t\t\t// scan for next CDFH\n\t\t\tCDFH* cdfh = (CDFH*)FindRecord((const u8*)buf.get(), cd_size, pos, cdfh_magic, sizeof(CDFH));\n\t\t\tif(!cdfh)\n\t\t\t\tWARN_RETURN(ERR::CORRUPTED);\n\n\t\t\tconst Path relativePathname(cdfh->Pathname());\n\t\t\tif(!relativePathname.IsDirectory())\n\t\t\t{\n\t\t\t\tconst OsPath name = relativePathname.Filename();\n\t\t\t\tCFileInfo fileInfo(name, cdfh->USize(), cdfh->MTime());\n\t\t\t\tshared_ptr<ArchiveFile_Zip> archiveFile(new ArchiveFile_Zip(m_file, cdfh->HeaderOffset(), cdfh->CSize(), cdfh->Checksum(), cdfh->Method()));\n\t\t\t\tcb(relativePathname, fileInfo, archiveFile, cbData);\n\t\t\t}\n\n\t\t\tpos += cdfh->Size();\n\t\t}\n\n\t\treturn INFO::OK;\n\t}\n\nprivate:\n\t/**\n\t * Scan buffer for a Zip file record.\n\t *\n\t * @param buf\n\t * @param size\n\t * @param start position within buffer\n\t * @param magic signature of record\n\t * @param recordSize size of record (including signature)\n\t * @return pointer to record within buffer or 0 if not found.\n\t **/\n\tstatic const u8* FindRecord(const u8* buf, size_t size, const u8* start, u32 magic, size_t recordSize)\n\t{\n\t\t// (don't use <start> as the counter - otherwise we can't tell if\n\t\t// scanning within the buffer was necessary.)\n\t\tfor(const u8* p = start; p <= buf+size-recordSize; p++)\n\t\t{\n\t\t\t// found it\n\t\t\tif(*(u32*)p == magic)\n\t\t\t{\n\t\t\t\tENSURE(p == start);\t// otherwise, the archive is a bit broken\n\t\t\t\treturn p;\n\t\t\t}\n\t\t}\n\n\t\t// passed EOF, didn't find it.\n\t\t// note: do not warn - this happens in the initial ECDR search at\n\t\t// EOF if the archive contains a comment field.\n\t\treturn 0;\n\t}\n\n\t// search for ECDR in the last <maxScanSize> bytes of the file.\n\t// if found, fill <dst_ecdr> with a copy of the (little-endian) ECDR and\n\t// return INFO::OK, otherwise IO error or ERR::CORRUPTED.\n\tstatic Status ScanForEcdr(const PFile& file, off_t fileSize, u8* buf, size_t maxScanSize, size_t& cd_numEntries, off_t& cd_ofs, size_t& cd_size)\n\t{\n\t\t// don't scan more than the entire file\n\t\tconst size_t scanSize = std::min(maxScanSize, size_t(fileSize));\n\n\t\t// read desired chunk of file into memory\n\t\tconst off_t ofs = fileSize - off_t(scanSize);\n\t\tio::Operation op(*file.get(), buf, scanSize, ofs);\n\t\tRETURN_STATUS_IF_ERR(io::Run(op));\n\n\t\t// look for ECDR in buffer\n\t\tconst ECDR* ecdr = (const ECDR*)FindRecord(buf, scanSize, buf, ecdr_magic, sizeof(ECDR));\n\t\tif(!ecdr)\n\t\t\treturn INFO::CANNOT_HANDLE;\n\n\t\tecdr->Decompose(cd_numEntries, cd_ofs, cd_size);\n\t\treturn INFO::OK;\n\t}\n\n\tstatic Status LocateCentralDirectory(const PFile& file, off_t fileSize, off_t& cd_ofs, size_t& cd_numEntries, size_t& cd_size)\n\t{\n\t\tconst size_t maxScanSize = 66000u;\t// see below\n\t\tUniqueRange buf(std::move(io::Allocate(maxScanSize)));\n\n\t\t// expected case: ECDR at EOF; no file comment\n\t\tStatus ret = ScanForEcdr(file, fileSize, (u8*)buf.get(), sizeof(ECDR), cd_numEntries, cd_ofs, cd_size);\n\t\tif(ret == INFO::OK)\n\t\t\treturn INFO::OK;\n\t\t// worst case: ECDR precedes 64 KiB of file comment\n\t\tret = ScanForEcdr(file, fileSize, (u8*)buf.get(), maxScanSize, cd_numEntries, cd_ofs, cd_size);\n\t\tif(ret == INFO::OK)\n\t\t\treturn INFO::OK;\n\n\t\t// both ECDR scans failed - this is not a valid Zip file.\n\t\tio::Operation op(*file.get(), buf.get(), sizeof(LFH));\n\t\tRETURN_STATUS_IF_ERR(io::Run(op));\n\t\t// the Zip file has an LFH but lacks an ECDR. this can happen if\n\t\t// the user hard-exits while an archive is being written.\n\t\t// notes:\n\t\t// - return ERR::CORRUPTED so VFS will not include this file.\n\t\t// - we could work around this by scanning all LFHs, but won't bother\n\t\t//   because it'd be slow.\n\t\t// - do not warn - the corrupt archive will be deleted on next\n\t\t//   successful archive builder run anyway.\n\t\tif(FindRecord((const u8*)buf.get(), sizeof(LFH), (const u8*)buf.get(), lfh_magic, sizeof(LFH)))\n\t\t\treturn ERR::CORRUPTED;\t// NOWARN\n\t\t// totally bogus\n\t\telse\n\t\t\tWARN_RETURN(ERR::ARCHIVE_UNKNOWN_FORMAT);\n\t}\n\n\tPFile m_file;\n\toff_t m_fileSize;\n};\n\nPIArchiveReader CreateArchiveReader_Zip(const OsPath& archivePathname)\n{\n\ttry\n\t{\n\t\treturn PIArchiveReader(new ArchiveReader_Zip(archivePathname));\n\t}\n\tcatch(Status)\n\t{\n\t\treturn PIArchiveReader();\n\t}\n}\n\n\n//-----------------------------------------------------------------------------\n// ArchiveWriter_Zip\n//-----------------------------------------------------------------------------\n\nclass ArchiveWriter_Zip : public IArchiveWriter\n{\npublic:\n\tArchiveWriter_Zip(const OsPath& archivePathname, bool noDeflate)\n\t\t: m_file(new File(archivePathname, O_WRONLY)), m_fileSize(0)\n\t\t, m_numEntries(0), m_noDeflate(noDeflate)\n\t{\n\t\tTHROW_STATUS_IF_ERR(pool_create(&m_cdfhPool, 10*MiB, 0));\n\t}\n\n\t~ArchiveWriter_Zip()\n\t{\n\t\t// append an ECDR to the CDFH list (this allows us to\n\t\t// write out both to the archive file in one burst)\n\t\tconst size_t cd_size = m_cdfhPool.da.pos;\n\t\tECDR* ecdr = (ECDR*)pool_alloc(&m_cdfhPool, sizeof(ECDR));\n\t\tif(!ecdr)\n\t\t\tthrow std::bad_alloc();\n\t\tconst off_t cd_ofs = m_fileSize;\n\t\tecdr->Init(m_numEntries, cd_ofs, cd_size);\n\n\t\tif(write(m_file->Descriptor(), m_cdfhPool.da.base, cd_size+sizeof(ECDR)) < 0)\n\t\t\tDEBUG_WARN_ERR(ERR::IO);\t// no way to return error code\n\n\t\t(void)pool_destroy(&m_cdfhPool);\n\t}\n\n\tStatus AddFile(const OsPath& pathname, const OsPath& pathnameInArchive)\n\t{\n\t\tCFileInfo fileInfo;\n\t\tRETURN_STATUS_IF_ERR(GetFileInfo(pathname, &fileInfo));\n\n\t\tPFile file(new File);\n\t\tRETURN_STATUS_IF_ERR(file->Open(pathname, O_RDONLY));\n\n\t\treturn AddFileOrMemory(fileInfo, pathnameInArchive, file, NULL);\n\t}\n\n\tStatus AddMemory(const u8* data, size_t size, time_t mtime, const OsPath& pathnameInArchive)\n\t{\n\t\tCFileInfo fileInfo(pathnameInArchive, size, mtime);\n\n\t\treturn AddFileOrMemory(fileInfo, pathnameInArchive, PFile(), data);\n\t}\n\n\tStatus AddFileOrMemory(const CFileInfo& fileInfo, const OsPath& pathnameInArchive, const PFile& file, const u8* data)\n\t{\n\t\tENSURE((file && !data) || (data && !file));\n\n\t\tconst off_t usize = fileInfo.Size();\n\t\t// skip 0-length files.\n\t\t// rationale: zip.cpp needs to determine whether a CDFH entry is\n\t\t// a file or directory (the latter are written by some programs but\n\t\t// not needed - they'd only pollute the file table).\n\t\t// it looks like checking for usize=csize=0 is the safest way -\n\t\t// relying on file attributes (which are system-dependent!) is\n\t\t// even less safe.\n\t\t// we thus skip 0-length files to avoid confusing them with directories.\n\t\tif(!usize)\n\t\t\treturn INFO::SKIPPED;\n\n\t\tconst size_t pathnameLength = pathnameInArchive.string().length();\n\n\t\t// choose method and the corresponding codec\n\t\tZipMethod method;\n\t\tPICodec codec;\n\t\tif(m_noDeflate || IsFileTypeIncompressible(pathnameInArchive))\n\t\t{\n\t\t\tmethod = ZIP_METHOD_NONE;\n\t\t\tcodec = CreateCodec_ZLibNone();\n\t\t}\n\t\telse\n\t\t{\n\t\t\tmethod = ZIP_METHOD_DEFLATE;\n\t\t\tcodec = CreateCompressor_ZLibDeflate();\n\t\t}\n\n\t\t// allocate memory\n\t\tconst size_t csizeMax = codec->MaxOutputSize(size_t(usize));\n\t\tUniqueRange buf(std::move(io::Allocate(sizeof(LFH) + pathnameLength + csizeMax)));\n\n\t\t// read and compress file contents\n\t\tsize_t csize; u32 checksum;\n\t\t{\n\t\t\tu8* cdata = (u8*)buf.get() + sizeof(LFH) + pathnameLength;\n\t\t\tStream stream(codec);\n\t\t\tstream.SetOutputBuffer(cdata, csizeMax);\n\t\t\tStreamFeeder streamFeeder(stream);\n\t\t\tif(file)\n\t\t\t{\n\t\t\t\tio::Operation op(*file.get(), 0, usize);\n\t\t\t\tRETURN_STATUS_IF_ERR(io::Run(op, io::Parameters(), streamFeeder));\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tRETURN_STATUS_IF_ERR(streamFeeder(data, usize));\n\t\t\t}\n\t\t\tRETURN_STATUS_IF_ERR(stream.Finish());\n\t\t\tcsize = stream.OutSize();\n\t\t\tchecksum = stream.Checksum();\n\t\t}\n\n\t\t// build LFH\n\t\t{\n\t\t\tLFH* lfh = (LFH*)buf.get();\n\t\t\tlfh->Init(fileInfo, (off_t)csize, method, checksum, pathnameInArchive);\n\t\t}\n\n\t\t// append a CDFH to the central directory (in memory)\n\t\tconst off_t ofs = m_fileSize;\n\t\tconst size_t prev_pos = m_cdfhPool.da.pos;\t// (required to determine padding size)\n\t\tconst size_t cdfhSize = sizeof(CDFH) + pathnameLength;\n\t\tCDFH* cdfh = (CDFH*)pool_alloc(&m_cdfhPool, cdfhSize);\n\t\tif(!cdfh)\n\t\t\tWARN_RETURN(ERR::NO_MEM);\n\t\tconst size_t slack = m_cdfhPool.da.pos - prev_pos - cdfhSize;\n\t\tcdfh->Init(fileInfo, ofs, (off_t)csize, method, checksum, pathnameInArchive, slack);\n\t\tm_numEntries++;\n\n\t\t// write LFH, pathname and cdata to file\n\t\tconst size_t packageSize = sizeof(LFH) + pathnameLength + csize;\n\t\tif(write(m_file->Descriptor(), buf.get(), packageSize) < 0)\n\t\t\tWARN_RETURN(ERR::IO);\n\t\tm_fileSize += (off_t)packageSize;\n\n\t\treturn INFO::OK;\n\t}\n\nprivate:\n\tstatic bool IsFileTypeIncompressible(const OsPath& pathname)\n\t{\n\t\tconst OsPath extension = pathname.Extension();\n\n\t\t// file extensions that we don't want to compress\n\t\tstatic const wchar_t* incompressibleExtensions[] =\n\t\t{\n\t\t\tL\".zip\", L\".rar\",\n\t\t\tL\".jpg\", L\".jpeg\", L\".png\", \n\t\t\tL\".ogg\", L\".mp3\"\n\t\t};\n\n\t\tfor(size_t i = 0; i < ARRAY_SIZE(incompressibleExtensions); i++)\n\t\t{\n\t\t\tif(extension == incompressibleExtensions[i])\n\t\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tPFile m_file;\n\toff_t m_fileSize;\n\n\tPool m_cdfhPool;\n\tsize_t m_numEntries;\n\n\tbool m_noDeflate;\n};\n\nPIArchiveWriter CreateArchiveWriter_Zip(const OsPath& archivePathname, bool noDeflate)\n{\n\ttry\n\t{\n\t\treturn PIArchiveWriter(new ArchiveWriter_Zip(archivePathname, noDeflate));\n\t}\n\tcatch(Status)\n\t{\n\t\treturn PIArchiveWriter();\n\t}\n}\n"
  },
  {
    "path": "fpsgame/gui/file/archive/archive_zip.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * archive backend for Zip files.\n */\n\n#ifndef INCLUDED_ARCHIVE_ZIP\n#define INCLUDED_ARCHIVE_ZIP\n\n#include \"lib/file/archive/archive.h\"\n\n/**\n * @return 0 if opening the archive failed (e.g. because an external program is holding on to it)\n **/\nLIB_API PIArchiveReader CreateArchiveReader_Zip(const OsPath& archivePathname);\n\n/**\n * @return 0 if opening the archive failed (e.g. because an external program is holding on to it)\n **/\nLIB_API PIArchiveWriter CreateArchiveWriter_Zip(const OsPath& archivePathname, bool noDeflate);\n\n#endif\t// #ifndef INCLUDED_ARCHIVE_ZIP\n"
  },
  {
    "path": "fpsgame/gui/file/archive/codec.cpp",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#include \"precompiled.h\"\n#include \"lib/file/archive/codec.h\"\n\nICodec::~ICodec()\n{\n}\n"
  },
  {
    "path": "fpsgame/gui/file/archive/codec.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * this layer allows for other compression methods/libraries\n * besides ZLib. it also simplifies the interface for user code and\n * does error checking, etc.\n */\n\n#ifndef INCLUDED_CODEC\n#define INCLUDED_CODEC\n\n#define CODEC_COMPUTE_CHECKSUM 1\n\nstruct ICodec\n{\npublic:\n\t/**\n\t * note: the implementation should not check whether any data remains -\n\t * codecs are sometimes destroyed without completing a transfer.\n\t **/\n\tvirtual ~ICodec();\n\n\t/**\n\t * @return an upper bound on the output size for the given amount of input.\n\t * this is used when allocating a single buffer for the whole operation.\n\t **/\n\tvirtual size_t MaxOutputSize(size_t inSize) const = 0;\n\n\t/**\n\t * clear all previous state and prepare for reuse.\n\t *\n\t * this is as if the object were destroyed and re-created, but more\n\t * efficient since it avoids reallocating a considerable amount of\n\t * memory (about 200KB for LZ).\n\t **/\n\tvirtual Status Reset() = 0;\n\n\t/**\n\t * process (i.e. compress or decompress) data.\n\t *\n\t * @param in\n\t * @param inSize\n\t * @param out\n\t * @param outSize Bytes remaining in the output buffer; shall not be zero.\n\t * @param inConsumed,outProduced How many bytes in the input and\n\t *\t\t  output buffers were used. either or both of these can be zero if\n\t *\t\t  the input size is small or there's not enough output space.\n\t **/\n\tvirtual Status Process(const u8* in, size_t inSize, u8* out, size_t outSize, size_t& inConsumed, size_t& outProduced) = 0;\n\n\t/**\n\t * Flush buffers and make sure all output has been produced.\n\t *\n\t * @param checksum Checksum over all input data.\n\t * @param outProduced\n\t * @return error status for the entire operation.\n\t **/\n\tvirtual Status Finish(u32& checksum, size_t& outProduced) = 0;\n\n\t/**\n\t * update a checksum to reflect the contents of a buffer.\n\t *\n\t * @param checksum the initial value (must be 0 on first call)\n\t * @param in\n\t * @param inSize\n\t * @return the new checksum. note: after all data has been seen, this is\n\t * identical to the what Finish would return.\n\t **/\n\tvirtual u32 UpdateChecksum(u32 checksum, const u8* in, size_t inSize) const = 0;\n};\n\ntypedef shared_ptr<ICodec> PICodec;\n\n#endif\t// #ifndef INCLUDED_CODEC\n"
  },
  {
    "path": "fpsgame/gui/file/archive/codec_zlib.cpp",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#include \"precompiled.h\"\n#include \"lib/file/archive/codec_zlib.h\"\n\n#include \"lib/alignment.h\"\n#include \"lib/file/archive/codec.h\"\n#include \"lib/external_libraries/zlib.h\"\n\n#include \"lib/sysdep/cpu.h\"\n\n\nclass Codec_ZLib : public ICodec\n{\npublic:\n\tu32 UpdateChecksum(u32 checksum, const u8* in, size_t inSize) const\n\t{\n#if CODEC_COMPUTE_CHECKSUM\n\t\treturn (u32)crc32(checksum, in, (uInt)inSize);\n#else\n\t\tUNUSED2(checksum);\n\t\tUNUSED2(in);\n\t\tUNUSED2(inSize);\n\t\treturn 0;\n#endif\n\t}\n\nprotected:\n\tu32 InitializeChecksum()\n\t{\n#if CODEC_COMPUTE_CHECKSUM\n\t\treturn crc32(0, 0, 0);\n#else\n\t\treturn 0;\n#endif\n\t}\n};\n\n\n//-----------------------------------------------------------------------------\n\nclass Codec_ZLibNone : public Codec_ZLib\n{\npublic:\n\tCodec_ZLibNone()\n\t{\n\t\tReset();\n\t}\n\n\tvirtual ~Codec_ZLibNone()\n\t{\n\t}\n\n\tvirtual size_t MaxOutputSize(size_t inSize) const\n\t{\n\t\treturn inSize;\n\t}\n\n\tvirtual Status Reset()\n\t{\n\t\tm_checksum = InitializeChecksum();\n\t\treturn INFO::OK;\n\t}\n\n\tvirtual Status Process(const u8* in, size_t inSize, u8* out, size_t outSize, size_t& inConsumed, size_t& outProduced)\n\t{\n\t\tconst size_t transferSize = std::min(inSize, outSize);\n\t\tmemcpy(out, in, transferSize);\n\t\tinConsumed = outProduced = transferSize;\n\t\tm_checksum = UpdateChecksum(m_checksum, out, outProduced);\n\t\treturn INFO::OK;\n\t}\n\n\tvirtual Status Finish(u32& checksum, size_t& outProduced)\n\t{\n\t\toutProduced = 0;\n\t\tchecksum = m_checksum;\n\t\treturn INFO::OK;\n\t}\n\nprivate:\n\tu32 m_checksum;\n};\n\n\n//-----------------------------------------------------------------------------\n\nclass CodecZLibStream : public Codec_ZLib\n{\nprotected:\n\tCodecZLibStream()\n\t{\n\t\tmemset(&m_zs, 0, sizeof(m_zs));\n\t\tm_checksum = InitializeChecksum();\n\t}\n\n\tstatic Status LibError_from_zlib(int zlib_ret)\n\t{\n\t\tswitch(zlib_ret)\n\t\t{\n\t\tcase Z_OK:\n\t\t\treturn INFO::OK;\n\t\tcase Z_STREAM_END:\n\t\t\tWARN_RETURN(ERR::FAIL);\n\t\tcase Z_MEM_ERROR:\n\t\t\tWARN_RETURN(ERR::NO_MEM);\n\t\tcase Z_DATA_ERROR:\n\t\t\tWARN_RETURN(ERR::CORRUPTED);\n\t\tcase Z_STREAM_ERROR:\n\t\t\tWARN_RETURN(ERR::INVALID_PARAM);\n\t\tdefault:\n\t\t\tWARN_RETURN(ERR::FAIL);\n\t\t}\n\t}\n\n\tstatic void WarnIfZLibError(int zlib_ret)\n\t{\n\t\t(void)LibError_from_zlib(zlib_ret);\n\t}\n\n\ttypedef int ZEXPORT (*ZLibFunc)(z_streamp strm, int flush);\n\n\tStatus CallStreamFunc(ZLibFunc func, int flush, const u8* in, const size_t inSize, u8* out, const size_t outSize, size_t& inConsumed, size_t& outProduced)\n\t{\n\t\tm_zs.next_in  = (Byte*)in;\n\t\tm_zs.avail_in = (uInt)inSize;\n\t\tm_zs.next_out  = (Byte*)out;\n\t\tm_zs.avail_out = (uInt)outSize;\n\n\t\tint ret = func(&m_zs, flush);\n\t\t// sanity check: if ZLib reports end of stream, all input data\n\t\t// must have been consumed.\n\t\tif(ret == Z_STREAM_END)\n\t\t{\n\t\t\tENSURE(m_zs.avail_in == 0);\n\t\t\tret = Z_OK;\n\t\t}\n\n\t\tENSURE(inSize >= m_zs.avail_in && outSize >= m_zs.avail_out);\n\t\tinConsumed  = inSize  - m_zs.avail_in;\n\t\toutProduced = outSize - m_zs.avail_out;\n\n\t\treturn LibError_from_zlib(ret);\n\t}\n\n\tmutable z_stream m_zs;\n\n\t// note: z_stream does contain an 'adler' checksum field, but that's\n\t// not updated in streams lacking a gzip header, so we'll have to\n\t// calculate a checksum ourselves.\n\t// adler32 is somewhat weaker than CRC32, but a more important argument\n\t// is that we should use the latter for compatibility with Zip archives.\n\tmutable u32 m_checksum;\n};\n\n\n//-----------------------------------------------------------------------------\n\nclass Compressor_ZLib : public CodecZLibStream\n{\npublic:\n\tCompressor_ZLib()\n\t{\n\t\t// note: with Z_BEST_COMPRESSION, 78% percent of\n\t\t// archive builder CPU time is spent in ZLib, even though\n\t\t// that is interleaved with IO; everything else is negligible.\n\t\t// we prefer faster speed at the cost of 1.5% larger archives.\n\t\tconst int level      = Z_BEST_SPEED;\n\t\tconst int windowBits = -MAX_WBITS;\t// max window size; omit ZLib header\n\t\tconst int memLevel   = 9;\t\t\t\t\t// max speed; total mem ~= 384KiB\n\t\tconst int strategy   = Z_DEFAULT_STRATEGY;\t// normal data - not RLE\n\t\tconst int ret = deflateInit2(&m_zs, level, Z_DEFLATED, windowBits, memLevel, strategy);\n\t\tENSURE(ret == Z_OK);\n\t}\n\n\tvirtual ~Compressor_ZLib()\n\t{\n\t\tconst int ret = deflateEnd(&m_zs);\n\t\tWarnIfZLibError(ret);\n\t}\n\n\tvirtual size_t MaxOutputSize(size_t inSize) const\n\t{\n\t\treturn (size_t)deflateBound(&m_zs, (uLong)inSize);\n\t}\n\n\tvirtual Status Reset()\n\t{\n\t\tm_checksum = InitializeChecksum();\n\t\tconst int ret = deflateReset(&m_zs);\n\t\treturn LibError_from_zlib(ret);\n\t}\n\n\tvirtual Status Process(const u8* in, size_t inSize, u8* out, size_t outSize, size_t& inConsumed, size_t& outProduced)\n\t{\n\t\tm_checksum = UpdateChecksum(m_checksum, in, inSize);\n\t\treturn CodecZLibStream::CallStreamFunc(deflate, 0, in, inSize, out, outSize, inConsumed, outProduced);\n\t}\n\n\tvirtual Status Finish(u32& checksum, size_t& outProduced)\n\t{\n\t\tconst uInt availOut = m_zs.avail_out;\n\n\t\t// notify zlib that no more data is forthcoming and have it flush output.\n\t\t// our output buffer has enough space due to use of deflateBound;\n\t\t// therefore, deflate must return Z_STREAM_END.\n\t\tconst int ret = deflate(&m_zs, Z_FINISH);\n\t\tENSURE(ret == Z_STREAM_END);\n\n\t\toutProduced = size_t(availOut - m_zs.avail_out);\n\n\t\tchecksum = m_checksum;\n\t\treturn INFO::OK;\n\t}\n};\n\n\n//-----------------------------------------------------------------------------\n\nclass Decompressor_ZLib : public CodecZLibStream\n{\npublic:\n\tDecompressor_ZLib()\n\t{\n\t\tconst int windowBits = -MAX_WBITS;\t// max window size; omit ZLib header\n\t\tconst int ret = inflateInit2(&m_zs, windowBits);\n\t\tENSURE(ret == Z_OK);\n\t}\n\n\tvirtual ~Decompressor_ZLib()\n\t{\n\t\tconst int ret = inflateEnd(&m_zs);\n\t\tWarnIfZLibError(ret);\n\t}\n\n\tvirtual size_t MaxOutputSize(size_t inSize) const\n\t{\n\t\t// relying on an upper bound for the output is a really bad idea for\n\t\t// large files. archive formats store the uncompressed file sizes,\n\t\t// so callers should use that when allocating the output buffer.\n\t\tENSURE(inSize < 1*MiB);\n\n\t\treturn inSize*1032;\t// see http://www.zlib.org/zlib_tech.html\n\t}\n\n\tvirtual Status Reset()\n\t{\n\t\tm_checksum = InitializeChecksum();\n\t\tconst int ret = inflateReset(&m_zs);\n\t\treturn LibError_from_zlib(ret);\n\t}\n\n\tvirtual Status Process(const u8* in, size_t inSize, u8* out, size_t outSize, size_t& inConsumed, size_t& outProduced)\n\t{\n\t\tconst Status ret = CodecZLibStream::CallStreamFunc(inflate, Z_SYNC_FLUSH, in, inSize, out, outSize, inConsumed, outProduced);\n\t\tm_checksum = UpdateChecksum(m_checksum, out, outProduced);\n\t\treturn ret;\n\t}\n\n\tvirtual Status Finish(u32& checksum, size_t& outProduced)\n\t{\n\t\t// no action needed - decompression always flushes immediately.\n\t\toutProduced = 0;\n\n\t\tchecksum = m_checksum;\n\t\treturn INFO::OK;\n\t}\n};\n\n\n//-----------------------------------------------------------------------------\n\nPICodec CreateCodec_ZLibNone()\n{\n\treturn PICodec(new Codec_ZLibNone);\n}\n\nPICodec CreateCompressor_ZLibDeflate()\n{\n\treturn PICodec(new Compressor_ZLib);\n}\n\nPICodec CreateDecompressor_ZLibDeflate()\n{\n\treturn PICodec (new Decompressor_ZLib);\n}\n"
  },
  {
    "path": "fpsgame/gui/file/archive/codec_zlib.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#ifndef INCLUDED_CODEC_ZLIB\n#define INCLUDED_CODEC_ZLIB\n\n#include \"lib/file/archive/codec.h\"\n\nextern PICodec CreateCodec_ZLibNone();\nextern PICodec CreateCompressor_ZLibDeflate();\nextern PICodec CreateDecompressor_ZLibDeflate();\n\n#endif // INCLUDED_CODEC_ZLIB\n"
  },
  {
    "path": "fpsgame/gui/file/archive/disabled_tests/test_codec_zlib.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#include \"lib/self_test.h\"\n\n#include \"lib/self_test.h\"\n#include \"lib/res/file/archive/codec_zlib.h\"\n\nclass TestCodecZLib : public CxxTest::TestSuite \n{\npublic:\n\tvoid test_compress_decompress_compare()\n\t{\n\t\tsize_t inConsumed, outProduced;\n\t\tu32 checksum;\n\n\t\t// generate random input udata\n\t\t// (limit values to 0..7 so that the udata will actually be compressible)\n\t\tconst size_t usize = 10000;\n\t\tu8 udata[usize];\n\t\tfor(size_t i = 0; i < usize; i++)\n\t\t\tudata[i] = rand() & 0x07;\n\n\t\t// compress\n\t\tu8* cdata; size_t csize;\n\t\t{\n\t\t\tboost::shared_ptr<ICodec> compressor_zlib = CreateCompressor_ZLib();\n\t\t\tICodec* c = compressor_zlib.get();\n\t\t\tconst size_t csizeMax = c->MaxOutputSize(usize);\n\t\t\tcdata = new u8[csizeMax];\n\t\t\tTS_ASSERT_OK(c->Process(udata, usize, cdata, csizeMax, inConsumed, outProduced));\n\t\t\tTS_ASSERT_EQUALS(inConsumed, usize);\n\t\t\tTS_ASSERT_LESS_THAN_EQUALS(outProduced, csizeMax);\n\t\t\tu8* cdata2;\n\t\t\tTS_ASSERT_OK(c->Finish(cdata2, csize, checksum));\n\t\t\tTS_ASSERT_EQUALS(cdata, cdata2);\n\t\t\tTS_ASSERT_EQUALS(csize, outProduced);\n\t\t}\n\n\t\t// make sure the data changed during compression\n\t\tTS_ASSERT(csize != usize || memcmp(udata, cdata, std::min(usize, csize)) != 0);\n\n\t\t// decompress\n\t\tu8 ddata[usize];\n\t\t{\n\t\t\tboost::shared_ptr<ICodec> decompressor_zlib = CreateDecompressor_ZLib();\n\t\t\tICodec* d = decompressor_zlib.get();\n\t\t\tTS_ASSERT_OK(decompressor_zlib->Process(cdata, csize, ddata, usize, inConsumed, outProduced));\n\t\t\tTS_ASSERT_EQUALS(inConsumed, csize);\t// ZLib always outputs as much data as possible\n\t\t\tTS_ASSERT_EQUALS(outProduced, usize);\t// .. so these figures are correct before Finish()\n\t\t\tu8* ddata2; size_t dsize;\n\t\t\tTS_ASSERT_OK(d->Finish(&ddata2, &dsize, &checksum));\n\t\t\tTS_ASSERT_EQUALS(ddata, ddata2);\n\t\t\tTS_ASSERT_EQUALS(dsize, outProduced);\n\t\t}\n\n\t\t// verify udata survived intact\n\t\tTS_ASSERT_SAME_DATA(udata, ddata, usize);\n\n\t\tdelete[] cdata;\n\t}\n};\n"
  },
  {
    "path": "fpsgame/gui/file/archive/disabled_tests/test_compression.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#include \"lib/self_test.h\"\n\n#include \"lib/self_test.h\"\n#include \"lib/res/file/archive/compression.h\"\n\nclass TestCompression : public CxxTest::TestSuite \n{\npublic:\n\tvoid test_compress_decompress_compare()\n\t{\n\t\t// generate random input data\n\t\t// (limit values to 0..7 so that the data will actually be compressible)\n\t\tconst size_t data_size = 10000;\n\t\tu8 data[data_size];\n\t\tfor(size_t i = 0; i < data_size; i++)\n\t\t\tdata[i] = rand() & 0x07;\n\n\t\tu8* cdata; size_t csize;\n\t\tu8 udata[data_size];\n\n\t\t// compress\n\t\tuintptr_t c = comp_alloc(CT_COMPRESSION, CM_DEFLATE);\n\t\t{\n\t\tTS_ASSERT(c != 0);\n\t\tconst size_t csizeBound = comp_max_output_size(c, data_size);\n\t\tTS_ASSERT_OK(comp_alloc_output(c, csizeBound));\n\t\tconst ssize_t cdata_produced = comp_feed(c, data, data_size);\n\t\tTS_ASSERT(cdata_produced >= 0);\n\t\tu32 checksum;\n\t\tTS_ASSERT_OK(comp_finish(c, &cdata, &csize, &checksum));\n\t\tTS_ASSERT(cdata_produced <= (ssize_t)csize);\t// can't have produced more than total\n\t\t}\n\n\t\t// decompress\n\t\tuintptr_t d = comp_alloc(CT_DECOMPRESSION, CM_DEFLATE);\n\t\t{\n\t\tTS_ASSERT(d != 0);\n\t\tcomp_set_output(d, udata, data_size);\n\t\tconst ssize_t udata_produced = comp_feed(d, cdata, csize);\n\t\tTS_ASSERT(udata_produced >= 0);\n\t\tu8* udata_final; size_t usize_final; u32 checksum;\n\t\tTS_ASSERT_OK(comp_finish(d, &udata_final, &usize_final, &checksum));\n\t\tTS_ASSERT(udata_produced <= (ssize_t)usize_final);\t// can't have produced more than total\n\t\tTS_ASSERT_EQUALS(udata_final, udata);\t// output buffer address is same\n\t\tTS_ASSERT_EQUALS(usize_final, data_size);\t// correct amount of output\n\t\t}\n\n\t\tcomp_free(c);\n\t\tcomp_free(d);\n\n\t\t// verify data survived intact\n\t\tTS_ASSERT_SAME_DATA(data, udata, data_size);\n\t}\n};\n"
  },
  {
    "path": "fpsgame/gui/file/archive/disabled_tests/test_fat_time.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#include \"lib/self_test.h\"\n\n#include <ctime>\n\n#include \"lib/res/file/archive/fat_time.h\"\n\nclass TestFatTime: public CxxTest::TestSuite \n{\npublic:\n\tvoid test_fat_timedate_conversion()\n\t{\n\t\t// note: FAT time stores second/2, which means converting may\n\t\t// end up off by 1 second.\n\n\t\ttime_t t, converted_t;\n\n\t\tt = time(0);\n\t\tconverted_t = time_t_from_FAT(FAT_from_time_t(t));\n\t\tTS_ASSERT_DELTA(t, converted_t, 2);\n\n\t\tt++;\n\t\tconverted_t = time_t_from_FAT(FAT_from_time_t(t));\n\t\tTS_ASSERT_DELTA(t, converted_t, 2);\n\t}\n};\n"
  },
  {
    "path": "fpsgame/gui/file/archive/disabled_tests/test_zip.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#include \"lib/self_test.h\"\n\n#include <time.h>\n\n#include \"lib/res/file/archive/zip.h\"\n\nclass TestZip : public CxxTest::TestSuite \n{\npublic:\n};\n"
  },
  {
    "path": "fpsgame/gui/file/archive/stream.cpp",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#include \"precompiled.h\"\n#include \"lib/file/archive/stream.h\"\n\n#include \"lib/allocators/page_aligned.h\"\n#include \"lib/allocators/shared_ptr.h\"\n#include \"lib/file/archive/codec.h\"\n//#include \"lib/timer.h\"\n\n//TIMER_ADD_CLIENT(tc_stream);\n\n\nOutputBufferManager::OutputBufferManager()\n{\n\tReset();\n}\n\nvoid OutputBufferManager::Reset()\n{\n\tm_buffer = 0;\n\tm_size = 0;\n\tm_capacity = 0;\n}\n\nvoid OutputBufferManager::SetBuffer(u8* buffer, size_t size)\n{\n\tENSURE(IsAllowableBuffer(buffer, size));\n\n\tm_buffer = buffer;\n\tm_size = size;\n}\n\nvoid OutputBufferManager::AllocateBuffer(size_t size)\n{\n\t// notes:\n\t// - this implementation allows reusing previous buffers if they\n\t//   are big enough, which reduces the number of allocations.\n\t// - no further attempts to reduce allocations (e.g. by doubling\n\t//   the current size) are made; this strategy is enough.\n\t// - Pool etc. cannot be used because files may be huge (larger\n\t//   than the address space of 32-bit systems).\n\n\t// no buffer or the previous one wasn't big enough: reallocate\n\tif(!m_mem || m_capacity < size)\n\t{\n\t\tAllocateAligned(m_mem, size);\n\t\tm_capacity = size;\n\t}\n\n\tSetBuffer(m_mem.get(), size);\n}\n\nbool OutputBufferManager::IsAllowableBuffer(u8* buffer, size_t size)\n{\n\t// none yet established\n\tif(m_buffer == 0 && m_size == 0)\n\t\treturn true;\n\n\t// same as last time (happens with temp buffers)\n\tif(m_buffer == buffer && m_size == size)\n\t\treturn true;\n\n\t// located after the last buffer (note: not necessarily after\n\t// the entire buffer; a lack of input can cause the output buffer\n\t// to only partially be used before the next call.)\n\tif((unsigned)(buffer - m_buffer) <= m_size)\n\t\treturn true;\n\n\treturn false;\n}\n\n\n//-----------------------------------------------------------------------------\n\n\nStream::Stream(const PICodec& codec)\n\t: m_codec(codec)\n\t, m_inConsumed(0), m_outProduced(0)\n{\n}\n\n\nvoid Stream::AllocateOutputBuffer(size_t outSizeMax)\n{\n\tm_outputBufferManager.AllocateBuffer(outSizeMax);\n}\n\n\nvoid Stream::SetOutputBuffer(u8* out, size_t outSize)\n{\n\tm_outputBufferManager.SetBuffer(out, outSize);\n}\n\n\nStatus Stream::Feed(const u8* in, size_t inSize)\n{\n\tif(m_outProduced == m_outputBufferManager.Size())\t// output buffer full; must not call Process\n\t\treturn INFO::ALL_COMPLETE;\n\n\tsize_t inConsumed, outProduced;\n\tu8* const out = m_outputBufferManager.Buffer() + m_outProduced;\n\tconst size_t outSize = m_outputBufferManager.Size() - m_outProduced;\n\tRETURN_STATUS_IF_ERR(m_codec->Process(in, inSize, out, outSize, inConsumed, outProduced));\n\n\tm_inConsumed += inConsumed;\n\tm_outProduced += outProduced;\n\treturn INFO::OK;\n}\n\n\nStatus Stream::Finish()\n{\n\tsize_t outProduced;\n\tRETURN_STATUS_IF_ERR(m_codec->Finish(m_checksum, outProduced));\n\tm_outProduced += outProduced;\n\treturn INFO::OK;\n}\n"
  },
  {
    "path": "fpsgame/gui/file/archive/stream.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * output buffer and 'stream' layered on top of a compression codec\n */\n\n#ifndef INCLUDED_STREAM\n#define INCLUDED_STREAM\n\n#include \"lib/file/archive/codec.h\"\n\n// note: this is similar in function to std::vector, but we don't need\n// iterators etc. and would prefer to avoid initializing each byte.\nclass OutputBufferManager\n{\npublic:\n\tOutputBufferManager();\n\n\tvoid Reset();\n\tvoid SetBuffer(u8* buffer, size_t size);\n\n\t/**\n\t * allocate a new output buffer.\n\t *\n\t * @param size [bytes] to allocate.\n\t *\n\t * notes:\n\t * - if a buffer had previously been allocated and is large enough,\n\t *   it is reused (this reduces the number of allocations).\n\t * - this class manages the lifetime of the buffer.\n\t **/\n\tvoid AllocateBuffer(size_t size);\n\n\tu8* Buffer() const\n\t{\n\t\treturn m_buffer;\n\t}\n\n\tsize_t Size() const\n\t{\n\t\treturn m_size;\n\t}\n\nprivate:\n\tbool IsAllowableBuffer(u8* buffer, size_t size);\n\n\tu8* m_buffer;\n\tsize_t m_size;\n\n\tshared_ptr<u8> m_mem;\n\t// size of m_mem. allows reusing previously allocated buffers\n\t// (user-specified buffers can't be reused because we have no control\n\t// over their lifetime)\n\tsize_t m_capacity;\n};\n\n\nclass Stream\n{\npublic:\n\tStream(const PICodec& codec);\n\n\tvoid SetOutputBuffer(u8* out, size_t outSize);\n\n\tvoid AllocateOutputBuffer(size_t outSizeMax);\n\n\t/**\n\t * 'feed' the codec with a data block.\n\t **/\n\tStatus Feed(const u8* in, size_t inSize);\n\n\tStatus Finish();\n\n\tsize_t OutSize() const\n\t{\n\t\treturn m_outProduced;\n\t}\n\n\tu32 Checksum() const\n\t{\n\t\treturn m_checksum;\n\t}\n\nprivate:\n\tPICodec m_codec;\n\tOutputBufferManager m_outputBufferManager;\n\n\tsize_t m_inConsumed;\n\tsize_t m_outProduced;\n\tu32 m_checksum;\n};\n\n// avoids the need for std::bind (not supported on all compilers) and boost::bind (can't be\n// used at work)\nstruct StreamFeeder\n{\n\tNONCOPYABLE(StreamFeeder);\npublic:\n\tStreamFeeder(Stream& stream)\n\t\t: stream(stream)\n\t{\n\t}\n\n\tStatus operator()(const u8* data, size_t size) const\n\t{\n\t\treturn stream.Feed(data, size);\n\t}\n\nprivate:\n\tStream& stream;\n};\n\n#endif\t// #ifndef INCLUDED_STREAM\n"
  },
  {
    "path": "fpsgame/gui/file/common/file_loader.cpp",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#include \"precompiled.h\"\n#include \"lib/file/common/file_loader.h\"\n\n/*virtual*/ IFileLoader::~IFileLoader()\n{\n}\n"
  },
  {
    "path": "fpsgame/gui/file/common/file_loader.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#ifndef INCLUDED_FILE_LOADER\n#define INCLUDED_FILE_LOADER\n\n#include \"lib/os_path.h\"\n\nstruct IFileLoader\n{\n\tvirtual ~IFileLoader();\n\n\tvirtual size_t Precedence() const = 0;\n\tvirtual wchar_t LocationCode() const = 0;\n\tvirtual OsPath Path() const = 0;\n\n\tvirtual Status Load(const OsPath& name, const shared_ptr<u8>& buf, size_t size) const = 0;\n};\n\ntypedef shared_ptr<IFileLoader> PIFileLoader;\n\n#endif\t// #ifndef INCLUDED_FILE_LOADER\n"
  },
  {
    "path": "fpsgame/gui/file/common/file_stats.cpp",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * gathers statistics from all file modules.\n */\n\n#include \"precompiled.h\"\n#include \"lib/file/common/file_stats.h\"\n\n#include <set>\n\n#include \"lib/timer.h\"\n\n#if FILE_STATS_ENABLED\n\n// vfs\nstatic size_t vfs_files;\nstatic double vfs_size_total;\nstatic double vfs_init_elapsed_time;\n\n// file\nstatic size_t unique_names;\nstatic size_t unique_name_len_total;\nstatic size_t open_files_cur, open_files_max;\t// total = opened_files.size()\n\n// file_buf\nstatic size_t extant_bufs_cur, extant_bufs_max, extant_bufs_total;\nstatic double buf_size_total, buf_aligned_size_total;\n\n// file_io\nstatic size_t user_ios;\nstatic double user_io_size_total;\nstatic double io_actual_size_total[FI_MAX_IDX][2];\nstatic double io_elapsed_time[FI_MAX_IDX][2];\nstatic double io_process_time_total;\nstatic size_t io_seeks;\n\n// file_cache\nstatic size_t cache_count[2];\nstatic double cache_size_total[2];\nstatic size_t conflict_misses;\n//static double conflict_miss_size_total;\t// JW: currently not used nor computed\nstatic size_t block_cache_count[2];\n\n// archive builder\nstatic size_t ab_connection_attempts;\t// total number of trace entries\nstatic size_t ab_repeated_connections;\t// how many of these were not unique\n\n\n// convenience functions for measuring elapsed time in an interval.\n// by exposing start/finish calls, we avoid callers from querying\n// timestamps when stats are disabled.\nstatic double start_time;\nstatic void timer_start(double* start_time_storage = &start_time)\n{\n\t// make sure no measurement is currently active\n\t// (since start_time is shared static storage)\n\tENSURE(*start_time_storage == 0.0);\n\t*start_time_storage = timer_Time();\n}\nstatic double timer_reset(double* start_time_storage = &start_time)\n{\n\tdouble elapsed = timer_Time() - *start_time_storage;\n\t*start_time_storage = 0.0;\n\treturn elapsed;\n}\n\n//-----------------------------------------------------------------------------\n\n//\n// vfs\n//\n\nvoid stats_vfs_file_add(size_t file_size)\n{\n\tvfs_files++;\n\tvfs_size_total += file_size;\n}\n\nvoid stats_vfs_file_remove(size_t file_size)\n{\n\tvfs_files--;\n\tvfs_size_total -= file_size;\n}\n\n// stats_vfs_init_* are currently unused\nvoid stats_vfs_init_start()\n{\n\ttimer_start();\n}\n\nvoid stats_vfs_init_finish()\n{\n\tvfs_init_elapsed_time += timer_reset();\n}\n\n\n//\n// file\n//\n\nvoid stats_unique_name(size_t name_len)\n{\n\tunique_names++;\n\tunique_name_len_total += name_len;\n}\n\n\nvoid stats_open()\n{\n\topen_files_cur++;\n\topen_files_max = std::max(open_files_max, open_files_cur);\n\n\t// could also use a set to determine unique files that have been opened\n}\n\nvoid stats_close()\n{\n\tENSURE(open_files_cur > 0);\n\topen_files_cur--;\n}\n\n\n//\n// file_buf\n//\n\nvoid stats_buf_alloc(size_t size, size_t alignedSize)\n{\n\textant_bufs_cur++;\n\textant_bufs_max = std::max(extant_bufs_max, extant_bufs_cur);\n\textant_bufs_total++;\n\n\tbuf_size_total += size;\n\tbuf_aligned_size_total += alignedSize;\n}\n\nvoid stats_buf_free()\n{\n\tENSURE(extant_bufs_cur > 0);\n\textant_bufs_cur--;\n}\n\nvoid stats_buf_ref()\n{\n\textant_bufs_cur++;\n}\n\n\n//\n// file_io\n//\n\nvoid stats_io_user_request(size_t user_size)\n{\n\tuser_ios++;\n\tuser_io_size_total += user_size;\n}\n\nScopedIoMonitor::ScopedIoMonitor()\n{\n\tm_startTime = 0.0;\n\ttimer_start(&m_startTime);\n}\n\nScopedIoMonitor::~ScopedIoMonitor()\n{\n\t// note: we can only bill IOs that have succeeded :S\n\ttimer_reset(&m_startTime);\n}\n\nvoid ScopedIoMonitor::NotifyOfSuccess(FileIOImplentation fi, int opcode, off_t size)\n{\n\tENSURE(fi < FI_MAX_IDX);\n\tENSURE(opcode == LIO_READ || opcode == LIO_WRITE);\n\n\tio_actual_size_total[fi][opcode == LIO_WRITE] += size;\n\tio_elapsed_time[fi][opcode == LIO_WRITE] += timer_reset(&m_startTime);\n}\n\n\nvoid stats_cb_start()\n{\n\ttimer_start();\n}\n\nvoid stats_cb_finish()\n{\n\tio_process_time_total += timer_reset();\n}\n\n\n//\n// file_cache\n//\n\nvoid stats_cache(CacheRet cr, size_t size)\n{\n\tENSURE(cr == CR_HIT || cr == CR_MISS);\n\n#if 0\n\tif(cr == CR_MISS)\n\t{\n\t\tPairIB ret = ever_cached_files.insert(atom_fn);\n\t\tif(!ret.second)\t// was already cached once\n\t\t{\n\t\t\tconflict_miss_size_total += size;\n\t\t\tconflict_misses++;\n\t\t}\n\t}\n#endif\n\n\tcache_count[cr]++;\n\tcache_size_total[cr] += size;\n}\n\nvoid stats_block_cache(CacheRet cr)\n{\n\tENSURE(cr == CR_HIT || cr == CR_MISS);\n\tblock_cache_count[cr]++;\n}\n\n\n//\n// archive builder\n//\n\nvoid stats_ab_connection(bool already_exists)\n{\n\tab_connection_attempts++;\n\tif(already_exists)\n\t\tab_repeated_connections++;\n}\n\n\n//-----------------------------------------------------------------------------\n\ntemplate<typename T> int percent(T num, T divisor)\n{\n\tif(!divisor)\n\t\treturn 0;\n\treturn (int)(100*num / divisor);\n}\n\nvoid file_stats_dump()\n{\n\tif(!debug_filter_allows(\"FILE_STATS|\"))\n\t\treturn;\n\n\tconst double KB = 1e3; const double MB = 1e6; const double ms = 1e-3;\n\n\tdebug_printf(\"--------------------------------------------------------------------------------\\n\");\n\tdebug_printf(\"File statistics:\\n\");\n\n\t// note: we split the reports into several debug_printfs for clarity;\n\t// this is necessary anyway due to fixed-size buffer.\n\n\tdebug_printf(\n\t\tL\"\\nvfs:\\n\"\n\t\tL\"Total files: %lu (%g MB)\\n\"\n\t\tL\"Init/mount time: %g ms\\n\",\n\t\t(unsigned long)vfs_files, vfs_size_total/MB,\n\t\tvfs_init_elapsed_time/ms\n\t);\n\n\tdebug_printf(\n\t\tL\"\\nfile:\\n\"\n\t\tL\"Total names: %lu (%lu KB)\\n\"\n\t\tL\"Max. concurrent: %lu; leaked: %lu.\\n\",\n\t\t(unsigned long)unique_names, (unsigned long)(unique_name_len_total/1000),\n\t\t(unsigned long)open_files_max, (unsigned long)open_files_cur\n\t);\n\n\tdebug_printf(\n\t\tL\"\\nfile_buf:\\n\"\n\t\tL\"Total buffers used: %lu (%g MB)\\n\"\n\t\tL\"Max concurrent: %lu; leaked: %lu\\n\"\n\t\tL\"Internal fragmentation: %d%%\\n\",\n\t\t(unsigned long)extant_bufs_total, buf_size_total/MB,\n\t\t(unsigned long)extant_bufs_max, (unsigned long)extant_bufs_cur,\n\t\tpercent(buf_aligned_size_total-buf_size_total, buf_size_total)\n\t);\n\n\tdebug_printf(\n\t\tL\"\\nfile_io:\\n\"\n\t\tL\"Total user load requests: %lu (%g MB)\\n\"\n\t\tL\"IO thoughput [MB/s; 0=never happened]:\\n\"\n\t\tL\"  lowio: R=%.3g, W=%.3g\\n\"\n\t\tL\"    aio: R=%.3g, W=%.3g\\n\"\n\t\tL\"Average size = %g KB; seeks: %lu; total callback time: %g ms\\n\"\n\t\tL\"Total data actually read from disk = %g MB\\n\",\n\t\t(unsigned long)user_ios, user_io_size_total/MB,\n#define THROUGHPUT(impl, opcode) (io_elapsed_time[impl][opcode == LIO_WRITE] == 0.0)? 0.0 : (io_actual_size_total[impl][opcode == LIO_WRITE] / io_elapsed_time[impl][opcode == LIO_WRITE] / MB)\n\t\tTHROUGHPUT(FI_LOWIO, LIO_READ), THROUGHPUT(FI_LOWIO, LIO_WRITE),\n\t\tTHROUGHPUT(FI_AIO  , LIO_READ), THROUGHPUT(FI_AIO  , LIO_WRITE),\n\t\tuser_io_size_total/user_ios/KB, (unsigned long)io_seeks, io_process_time_total/ms,\n\t\t(io_actual_size_total[FI_LOWIO][0]+io_actual_size_total[FI_AIO][0])/MB\n\t);\n\n\tdebug_printf(\n\t\tL\"\\nfile_cache:\\n\"\n\t\tL\"Hits: %lu (%g MB); misses %lu (%g MB); ratio: %u%%\\n\"\n\t\tL\"Percent of requested bytes satisfied by cache: %u%%; non-compulsory misses: %lu (%u%% of misses)\\n\"\n\t\tL\"Block hits: %lu; misses: %lu; ratio: %u%%\\n\",\n\t\t(unsigned long)cache_count[CR_HIT], cache_size_total[CR_HIT]/MB, (unsigned long)cache_count[CR_MISS], cache_size_total[CR_MISS]/MB, percent(cache_count[CR_HIT], cache_count[CR_HIT]+cache_count[CR_MISS]),\n\t\tpercent(cache_size_total[CR_HIT], cache_size_total[CR_HIT]+cache_size_total[CR_MISS]), (unsigned long)conflict_misses, percent(conflict_misses, cache_count[CR_MISS]),\n\t\t(unsigned long)block_cache_count[CR_HIT], (unsigned long)block_cache_count[CR_MISS], percent(block_cache_count[CR_HIT], block_cache_count[CR_HIT]+block_cache_count[CR_MISS])\n\t);\n\n\tdebug_printf(\n\t\tL\"\\nvfs_optimizer:\\n\"\n\t\tL\"Total trace entries: %lu; repeated connections: %lu; unique files: %lu\\n\",\n\t\t(unsigned long)ab_connection_attempts, (unsigned long)ab_repeated_connections, (unsigned long)(ab_connection_attempts-ab_repeated_connections)\n\t);\n}\n\n#endif\t// FILE_STATS_ENABLED\n"
  },
  {
    "path": "fpsgame/gui/file/common/file_stats.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * gathers statistics from all file modules.\n */\n\n#ifndef INCLUDED_FILE_STATS\n#define INCLUDED_FILE_STATS\n\n#include \"lib/posix/posix_aio.h\"\t// LIO_READ, LIO_WRITE\n\n#define FILE_STATS_ENABLED 0\n\n\nenum FileIOImplentation { FI_LOWIO, FI_AIO, FI_BCACHE, FI_MAX_IDX };\nenum CacheRet { CR_HIT, CR_MISS };\n\n#if FILE_STATS_ENABLED\n\n// vfs\nextern void stats_vfs_file_add(size_t file_size);\nextern void stats_vfs_file_remove(size_t file_size);\nextern void stats_vfs_init_start();\nextern void stats_vfs_init_finish();\n\n// file\n\n// currently not called because string_pool is now in lib/allocators\nextern void stats_unique_name(size_t name_len);\nextern void stats_open();\nextern void stats_close();\n\n// file_buf\nextern void stats_buf_alloc(size_t size, size_t alignedSize);\nextern void stats_buf_free();\nextern void stats_buf_ref();\n\n// file_io\nextern void stats_io_user_request(size_t user_size);\n\n// this is used to measure effective throughput for the two\n// synchronous IO variants.\n// note: improved measurements of the actual aio throughput by instrumenting\n// issue/wait doesn't work because IOManager's decompression may cause us to\n// miss the exact end of IO, thus throwing off measurements.\nclass ScopedIoMonitor\n{\npublic:\n\tScopedIoMonitor();\n\t~ScopedIoMonitor();\n\tvoid NotifyOfSuccess(FileIOImplentation fi, int opcode, off_t size);\n\nprivate:\n\tdouble m_startTime;\n};\n\nextern void stats_cb_start();\nextern void stats_cb_finish();\n\n// file_cache\nextern void stats_cache(CacheRet cr, size_t size);\nextern void stats_block_cache(CacheRet cr);\n\n// archive builder\nextern void stats_ab_connection(bool already_exists);\n\nextern void file_stats_dump();\n\n#else\n\n#define stats_vfs_file_add(file_size)\n#define stats_vfs_file_remove(file_size)\n#define stats_vfs_init_start()\n#define stats_vfs_init_finish()\n#define stats_unique_name(name_len)\n#define stats_open()\n#define stats_close()\n#define stats_buf_alloc(size, alignedSize)\n#define stats_buf_free()\n#define stats_buf_ref()\n#define stats_io_user_request(user_size)\nclass ScopedIoMonitor\n{\npublic:\n\tScopedIoMonitor() {}\n\t~ScopedIoMonitor() {}\n\tvoid NotifyOfSuccess(FileIOImplentation UNUSED(fi), int UNUSED(opcode), off_t UNUSED(size)) {}\n};\n#define stats_cb_start()\n#define stats_cb_finish()\n#define stats_cache(cr, size)\n#define stats_block_cache(cr)\n#define stats_ab_connection(already_exists)\n#define file_stats_dump()\n\n#endif\n\n#endif\t// #ifndef INCLUDED_FILE_STATS\n"
  },
  {
    "path": "fpsgame/gui/file/common/real_directory.cpp",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#include \"precompiled.h\"\n#include \"lib/file/common/real_directory.h\"\n\n#include \"lib/sysdep/filesystem.h\"\n#include \"lib/file/file.h\"\n#include \"lib/file/io/io.h\"\n\n\nRealDirectory::RealDirectory(const OsPath& path, size_t priority, size_t flags)\n\t: m_path(path), m_priority(priority), m_flags(flags)\n{\n}\n\n\n/*virtual*/ size_t RealDirectory::Precedence() const\n{\n\treturn 1u;\n}\n\n\n/*virtual*/ wchar_t RealDirectory::LocationCode() const\n{\n\treturn 'F';\n}\n\n\n/*virtual*/ Status RealDirectory::Load(const OsPath& name, const shared_ptr<u8>& buf, size_t size) const\n{\n\treturn io::Load(m_path / name, buf.get(), size);\n}\n\n\nStatus RealDirectory::Store(const OsPath& name, const shared_ptr<u8>& fileContents, size_t size)\n{\n\treturn io::Store(m_path / name, fileContents.get(), size);\n}\n\n\nvoid RealDirectory::Watch()\n{\n\tif(!m_watch)\n\t\t(void)dir_watch_Add(m_path, m_watch);\n}\n\n\nPRealDirectory CreateRealSubdirectory(const PRealDirectory& realDirectory, const OsPath& subdirectoryName)\n{\n\tconst OsPath path = realDirectory->Path() / subdirectoryName/\"\";\n\treturn PRealDirectory(new RealDirectory(path, realDirectory->Priority(), realDirectory->Flags()));\n}\n"
  },
  {
    "path": "fpsgame/gui/file/common/real_directory.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#ifndef INCLUDED_REAL_DIRECTORY\n#define INCLUDED_REAL_DIRECTORY\n\n#include \"lib/file/common/file_loader.h\"\n#include \"lib/sysdep/dir_watch.h\"\n\nclass RealDirectory : public IFileLoader\n{\n\tNONCOPYABLE(RealDirectory);\npublic:\n\tRealDirectory(const OsPath& path, size_t priority, size_t flags);\n\n\tsize_t Priority() const\n\t{\n\t\treturn m_priority;\n\t}\n\n\tsize_t Flags() const\n\t{\n\t\treturn m_flags;\n\t}\n\n\t// IFileLoader\n\tvirtual size_t Precedence() const;\n\tvirtual wchar_t LocationCode() const;\n\tvirtual OsPath Path() const\n\t{\n\t\treturn m_path;\n\t}\n\tvirtual Status Load(const OsPath& name, const shared_ptr<u8>& buf, size_t size) const;\n\n\tStatus Store(const OsPath& name, const shared_ptr<u8>& fileContents, size_t size);\n\n\tvoid Watch();\n\nprivate:\n\t// note: paths are relative to the root directory, so storing the\n\t// entire path instead of just the portion relative to the mount point\n\t// is not all too wasteful.\n\tconst OsPath m_path;\n\n\tconst size_t m_priority;\n\n\tconst size_t m_flags;\n\n\t// note: watches are needed in each directory because some APIs\n\t// (e.g. FAM) cannot watch entire trees with one call.\n\tPDirWatch m_watch;\n};\n\ntypedef shared_ptr<RealDirectory> PRealDirectory;\n\nextern PRealDirectory CreateRealSubdirectory(const PRealDirectory& realDirectory, const OsPath& subdirectoryName);\n\n#endif\t// #ifndef INCLUDED_REAL_DIRECTORY\n"
  },
  {
    "path": "fpsgame/gui/file/common/tests/test_trace.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#include \"lib/self_test.h\"\n\n#include \"lib/file/common/trace.h\"\n\nclass TestTraceEntry : public CxxTest::TestSuite \n{\npublic:\n\tvoid test_entry()\n\t{\n\t\tstd::wstring buf1;\n\t\tstd::wstring buf2;\n\n\t\tTraceEntry t1(TraceEntry::Load, L\"example.txt\", 1234);\n\t\tTS_ASSERT_EQUALS(t1.Action(), TraceEntry::Load);\n\t\tTS_ASSERT_PATH_EQUALS(t1.Pathname(), OsPath(L\"example.txt\"));\n\t\tTS_ASSERT_EQUALS(t1.Size(), (size_t)1234);\n\n\t\tbuf1 = t1.EncodeAsText();\n\t\t// The part before the ':' is an unpredictable timestamp,\n\t\t// so just test the string after that point\n\t\tTS_ASSERT_WSTR_EQUALS(wcschr(buf1.c_str(), ':'), L\": L \\\"example.txt\\\" 1234\\n\");\n\n\t\tTraceEntry t2(TraceEntry::Store, L\"example two.txt\", 16777216);\n\t\tTS_ASSERT_EQUALS(t2.Action(), TraceEntry::Store);\n\t\tTS_ASSERT_PATH_EQUALS(t2.Pathname(), OsPath(L\"example two.txt\"));\n\t\tTS_ASSERT_EQUALS(t2.Size(), (size_t)16777216);\n\n\t\tbuf2 = t2.EncodeAsText();\n\t\tTS_ASSERT_WSTR_EQUALS(wcschr(buf2.c_str(), ':'), L\": S \\\"example two.txt\\\" 16777216\\n\");\n\n\t\tTraceEntry t3(buf1);\n\t\tTS_ASSERT_EQUALS(t3.Action(), TraceEntry::Load);\n\t\tTS_ASSERT_PATH_EQUALS(t3.Pathname(), OsPath(L\"example.txt\"));\n\t\tTS_ASSERT_EQUALS(t3.Size(), (size_t)1234);\n\n\t\tTraceEntry t4(buf2);\n\t\tTS_ASSERT_EQUALS(t4.Action(), TraceEntry::Store);\n\t\tTS_ASSERT_PATH_EQUALS(t4.Pathname(), OsPath(L\"example two.txt\"));\n\t\tTS_ASSERT_EQUALS(t4.Size(), (size_t)16777216);\n\t}\n\n\tvoid test_maxpath()\n\t{\n\t\tOsPath path1(std::wstring(PATH_MAX, L'x'));\n\t\tstd::wstring buf1 = L\"0: L \\\"\" + path1.string() + L\"\\\" 0\\n\";\n\t\tTraceEntry t1(buf1);\n\t\tTS_ASSERT_PATH_EQUALS(t1.Pathname(), path1);\n\t}\n};\n"
  },
  {
    "path": "fpsgame/gui/file/common/trace.cpp",
    "content": "/* Copyright (c) 2015 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * IO event recording\n */\n\n#include \"precompiled.h\"\n#include \"lib/file/common/trace.h\"\n\n#include <cstdio>\n#include <sstream>\n\n#include \"lib/allocators/pool.h\"\n#include \"lib/timer.h\"\t// timer_Time\n#include \"lib/sysdep/sysdep.h\"\t// sys_OpenFile\n\n\n/*virtual*/ ITrace::~ITrace()\n{\n\n}\n\n\n//-----------------------------------------------------------------------------\n\nTraceEntry::TraceEntry(EAction action, const Path& pathname, size_t size)\n: m_timestamp((float)timer_Time())\n, m_action(action)\n, m_pathname(pathname)\n, m_size(size)\n{\n}\n\n\nTraceEntry::TraceEntry(const std::wstring& text)\n{\n\t// swscanf is far too awkward to get working cross-platform,\n\t// so use iostreams here instead\n\n\twchar_t dummy;\n\twchar_t action;\n\n\tstd::wstringstream stream(text);\n\tstream >> m_timestamp;\n\n\tstream >> dummy;\n\tENSURE(dummy == ':');\n\n\tstream >> action;\n\tENSURE(action == 'L' || action == 'S');\n\tm_action = (EAction)action;\n\n\tstream >> dummy;\n\tENSURE(dummy == '\"');\n\n\tPath::String pathname;\n\tstd::getline(stream, pathname, L'\"');\n\tm_pathname = Path(pathname);\n\n\tstream >> m_size;\n\n\tENSURE(stream.get() == '\\n');\n\t// NOTE: Don't use good() here - it fails due to a bug in older libc++ versions\n\tENSURE(!stream.bad() && !stream.fail());\n\tENSURE(stream.get() == WEOF);\n}\n\n\nstd::wstring TraceEntry::EncodeAsText() const\n{\n\tconst wchar_t action = (wchar_t)m_action;\n\twchar_t buf[1000];\n\tswprintf_s(buf, ARRAY_SIZE(buf), L\"%#010f: %c \\\"%ls\\\" %lu\\n\", m_timestamp, action, m_pathname.string().c_str(), (unsigned long)m_size);\n\treturn buf;\n}\n\n\n//-----------------------------------------------------------------------------\n\nclass Trace_Dummy : public ITrace\n{\npublic:\n\tTrace_Dummy(size_t UNUSED(maxSize))\n\t{\n\n\t}\n\n\tvirtual void NotifyLoad(const Path& UNUSED(pathname), size_t UNUSED(size))\n\t{\n\t}\n\n\tvirtual void NotifyStore(const Path& UNUSED(pathname), size_t UNUSED(size))\n\t{\n\t}\n\n\tvirtual Status Load(const OsPath& UNUSED(pathname))\n\t{\n\t\treturn INFO::OK;\n\t}\n\n\tvirtual Status Store(const OsPath& UNUSED(pathname)) const\n\t{\n\t\treturn INFO::OK;\n\t}\n\n\tvirtual const TraceEntry* Entries() const\n\t{\n\t\treturn 0;\n\t}\n\n\tvirtual size_t NumEntries() const\n\t{\n\t\treturn 0;\n\t}\n};\n\n\n//-----------------------------------------------------------------------------\n\nclass Trace : public ITrace\n{\npublic:\n\tTrace(size_t maxSize)\n\t{\n\t\t(void)pool_create(&m_pool, maxSize, sizeof(TraceEntry));\n\t}\n\n\tvirtual ~Trace()\n\t{\n\t\tfor(size_t i = 0; i < NumEntries(); i++)\n\t\t{\n\t\t\tTraceEntry* entry = (TraceEntry*)(uintptr_t(m_pool.da.base) + i*m_pool.el_size);\n\t\t\tentry->~TraceEntry();\n\t\t}\n\n\t\t(void)pool_destroy(&m_pool);\n\t}\n\n\tvirtual void NotifyLoad(const Path& pathname, size_t size)\n\t{\n\t\tnew(Allocate()) TraceEntry(TraceEntry::Load, pathname, size);\n\t}\n\n\tvirtual void NotifyStore(const Path& pathname, size_t size)\n\t{\n\t\tnew(Allocate()) TraceEntry(TraceEntry::Store, pathname, size);\n\t}\n\n\tvirtual Status Load(const OsPath& pathname)\n\t{\n\t\tpool_free_all(&m_pool);\n\n\t\terrno = 0;\n\t\tFILE* file = sys_OpenFile(pathname, \"rt\");\n\t\tif(!file)\n\t\t\tWARN_RETURN(StatusFromErrno());\n\n\t\tfor(;;)\n\t\t{\n\t\t\twchar_t text[500];\n\t\t\tif(!fgetws(text, ARRAY_SIZE(text)-1, file))\n\t\t\t\tbreak;\n\t\t\tnew(Allocate()) TraceEntry(text);\n\t\t}\n\t\tfclose(file);\n\n\t\treturn INFO::OK;\n\t}\n\n\tvirtual Status Store(const OsPath& pathname) const\n\t{\n\t\terrno = 0;\n\t\tFILE* file = sys_OpenFile(pathname, \"at\");\n\t\tif(!file)\n\t\t\tWARN_RETURN(StatusFromErrno());\n\t\tfor(size_t i = 0; i < NumEntries(); i++)\n\t\t{\n\t\t\tstd::wstring text = Entries()[i].EncodeAsText();\n\t\t\tfputws(text.c_str(), file);\n\t\t}\n\t\t(void)fclose(file);\n\t\treturn INFO::OK;\n\t}\n\n\tvirtual const TraceEntry* Entries() const\n\t{\n\t\treturn (const TraceEntry*)m_pool.da.base;\n\t}\n\n\tvirtual size_t NumEntries() const\n\t{\n\t\treturn m_pool.da.pos / m_pool.el_size;\n\t}\n\nprivate:\n\tvoid* Allocate()\n\t{\n\t\tvoid* p = pool_alloc(&m_pool, 0);\n\t\tENSURE(p);\n\t\treturn p;\n\t}\n\n\tPool m_pool;\n};\n\n\nPITrace CreateDummyTrace(size_t maxSize)\n{\n\treturn PITrace(new Trace_Dummy(maxSize));\n}\n\nPITrace CreateTrace(size_t maxSize)\n{\n\treturn PITrace(new Trace(maxSize));\n}\n"
  },
  {
    "path": "fpsgame/gui/file/common/trace.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * IO event recording\n */\n\n// traces are useful for determining the optimal ordering of archived files\n// and can also serve as a repeatable IO benchmark.\n\n// note: since FileContents are smart pointers, the trace can't easily\n// be notified when they are released (relevant for cache simulation).\n// we have to assume that users process one file at a time -- as they\n// should.\n\n#ifndef INCLUDED_TRACE\n#define INCLUDED_TRACE\n\n#include \"lib/os_path.h\"\n\n// stores information about an IO event.\nclass TraceEntry\n{\npublic:\n\tenum EAction\n\t{\n\t\tLoad = 'L',\n\t\tStore = 'S'\n\t};\n\n\tTraceEntry(EAction action, const Path& pathname, size_t size);\n\tTraceEntry(const std::wstring& text);\n\n\tEAction Action() const\n\t{\n\t\treturn m_action;\n\t}\n\n\tconst Path& Pathname() const\n\t{\n\t\treturn m_pathname;\n\t}\n\n\tsize_t Size() const\n\t{\n\t\treturn m_size;\n\t}\n\n\tstd::wstring EncodeAsText() const;\n\nprivate:\n\t// note: keep an eye on the class size because all instances are kept\n\t// in memory (see ITrace)\n\n\t// time (as returned by timer_Time) after the operation completes.\n\t// rationale: when loading, the VFS doesn't know file size until\n\t// querying the cache or retrieving file information.\n\tfloat m_timestamp;\n\n\tEAction m_action;\n\n\tPath m_pathname;\n\n\t// size of file.\n\t// rationale: other applications using this trace format might not\n\t// have access to the VFS and its file information.\n\tsize_t m_size;\n};\n\n\n// note: to avoid interfering with measurements, this trace container\n// does not cause any IOs (except of course in Load/Store)\nstruct ITrace\n{\n\tvirtual ~ITrace();\n\n\tvirtual void NotifyLoad(const Path& pathname, size_t size) = 0;\n\tvirtual void NotifyStore(const Path& pathname, size_t size) = 0;\n\n\t/**\n\t * store all entries into a file.\n\t *\n\t * @param pathname (native, absolute)\n\t *\n\t * note: the file format is text-based to allow human inspection and\n\t * because storing filename strings in a binary format would be a\n\t * bit awkward.\n\t **/\n\tvirtual Status Store(const OsPath& pathname) const = 0;\n\n\t/**\n\t * load entries from file.\n\t *\n\t * @param pathname (native, absolute)\n\t *\n\t * replaces any existing entries.\n\t **/\n\tvirtual Status Load(const OsPath& pathname) = 0;\n\n\tvirtual const TraceEntry* Entries() const = 0;\n\tvirtual size_t NumEntries() const = 0;\n};\n\ntypedef shared_ptr<ITrace> PITrace;\n\nextern PITrace CreateDummyTrace(size_t maxSize);\nextern PITrace CreateTrace(size_t maxSize);\n\n#endif\t// #ifndef INCLUDED_TRACE\n"
  },
  {
    "path": "fpsgame/gui/file/disabled_tests/test_file_cache.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#include \"lib/self_test.h\"\n\n#include \"lib/res/file/file_cache.h\"\n#include \"lib/rand.h\"\n\nclass TestFileCache : public CxxTest::TestSuite \n{\n\tenum { TEST_ALLOC_TOTAL = 100*1000*1000 };\npublic:\n\tvoid test_cache_allocator()\n\t{\n\t\t// allocated address -> its size\n\t\ttypedef std::map<void*, size_t> AllocMap;\n\t\tAllocMap allocations;\n\n\t\t// put allocator through its paces by allocating several times\n\t\t// its capacity (this ensures memory is reused)\n\t\tsrand(1);\n\t\tsize_t total_size_used = 0;\n\t\twhile(total_size_used < TEST_ALLOC_TOTAL)\n\t\t{\n\t\t\tsize_t size = rand(1, TEST_ALLOC_TOTAL/16);\n\t\t\ttotal_size_used += size;\n\t\t\tvoid* p;\n\t\t\t// until successful alloc:\n\t\t\tfor(;;)\n\t\t\t{\n\t\t\t\tp = file_cache_allocator_alloc(size);\n\t\t\t\tif(p)\n\t\t\t\t\tbreak;\n\t\t\t\t// out of room - remove a previous allocation\n\t\t\t\t// .. choose one at random\n\t\t\t\tsize_t chosen_idx = (size_t)rand(0, (size_t)allocations.size());\n\t\t\t\tAllocMap::iterator it = allocations.begin();\n\t\t\t\tfor(; chosen_idx != 0; chosen_idx--)\n\t\t\t\t\t++it;\n\t\t\t\tfile_cache_allocator_free(it->first, it->second);\n\t\t\t\tallocations.erase(it);\n\t\t\t}\n\n\t\t\t// must not already have been allocated\n\t\t\tTS_ASSERT_EQUALS(allocations.find(p), allocations.end());\n\t\t\tallocations[p] = size;\n\t\t}\n\n\t\t// reset to virginal state\n\t\t// note: even though everything has now been freed, this is\n\t\t// necessary since the freelists may be a bit scattered already.\n\t\tfile_cache_allocator_reset();\n\t}\n};\n"
  },
  {
    "path": "fpsgame/gui/file/disabled_tests/test_path.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#include \"lib/self_test.h\"\n\n#include \"lib/self_test.h\"\n#include \"lib/res/file/path.h\"\n\nclass TestPath : public CxxTest::TestSuite \n{\npublic:\n\tvoid test_conversion()\n\t{\n\t\tchar N_path[PATH_MAX] = {0};\n\t\tTS_ASSERT_OK(file_make_native_path(\"a/b/c\", N_path));\n#if OS_WIN\n\t\tTS_ASSERT_STR_EQUALS(N_path, \"a\\\\b\\\\c\");\n#else\n\t\tTS_ASSERT_STR_EQUALS(N_path, \"a/b/c\");\n#endif\n\n\t\tchar P_path[PATH_MAX] = {0};\n\t\tTS_ASSERT_OK(file_make_portable_path(\"a\\\\b\\\\c\", P_path));\n#if OS_WIN\n\t\tTS_ASSERT_STR_EQUALS(P_path, \"a/b/c\");\n#else\n\t\t// sounds strange, but correct: on non-Windows, \\\\ didn't\n\t\t// get recognized as separators and weren't converted.\n\t\tTS_ASSERT_STR_EQUALS(P_path, \"a\\\\b\\\\c\");\n#endif\n\n\t}\n\n\t// file_make_full_*_path is left untested (hard to do so)\n\n\tvoid test_pool()\n\t{\n\t\t// .. return same address for same string?\n\t\tconst char* atom1 = path_Pool->UniqueCopy(\"a/bc/def\");\n\t\tconst char* atom2 = path_Pool->UniqueCopy(\"a/bc/def\");\n\t\tTS_ASSERT_EQUALS(atom1, atom2);\n\n\t\t// .. early out (already in pool) check works?\n\t\tconst char* atom3 = path_Pool->UniqueCopy(atom1);\n\t\tTS_ASSERT_EQUALS(atom3, atom1);\n\n\t\t// is it reported as in pool?\n\t\tTS_ASSERT(path_Pool()->Contains(atom1));\n\n\t\t// path_Pool()->RandomString\n\t\t// see if the atom added above eventually comes out when a\n\t\t// random one is returned from the pool.\n\t\tint tries_left;\n\t\tfor(tries_left = 1000; tries_left != 0; tries_left--)\n\t\t{\n\t\t\tconst char* random_name = path_Pool->RandomString();\n\t\t\tif(random_name == atom1)\n\t\t\t\tbreak;\n\t\t}\n\t\tTS_ASSERT(tries_left != 0);\n\t}\n};\n"
  },
  {
    "path": "fpsgame/gui/file/file.cpp",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * simple POSIX file wrapper.\n */\n\n#include \"precompiled.h\"\n#include \"lib/file/file.h\"\n\n#include \"lib/file/common/file_stats.h\"\n\nstatic const StatusDefinition fileStatusDefinitions[] = {\n\t{ ERR::FILE_ACCESS, L\"Insufficient access rights to open file\", EACCES },\n\t{ ERR::FILE_NOT_FOUND, L\"No such file or directory\", ENOENT }\n};\nSTATUS_ADD_DEFINITIONS(fileStatusDefinitions);\n\n\nStatus FileOpen(const OsPath& pathname, int oflag)\n{\n\tENSURE((oflag & ~(O_RDONLY|O_WRONLY|O_DIRECT)) == 0);\n\tif(oflag & O_WRONLY)\n\t\toflag |= O_CREAT|O_TRUNC;\n\t// prevent exploits by disallowing writes to our files by other users.\n\t// note that the system-wide installed cache is read-only.\n\tconst mode_t mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH;\t// 0644\n\tconst int fd = wopen(pathname, oflag, mode);\n\tif(fd < 0)\n\t\treturn StatusFromErrno();\t// NOWARN\n\n\tstats_open();\n\treturn (Status)fd;\n}\n\n\nvoid FileClose(int& fd)\n{\n\tif(fd >= 0)\n\t{\n\t\twclose(fd);\n\t\tfd = -1;\n\t}\n}\n"
  },
  {
    "path": "fpsgame/gui/file/file.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * simple POSIX file wrapper.\n */\n\n#ifndef INCLUDED_FILE\n#define INCLUDED_FILE\n\n#include \"lib/os_path.h\"\n#include \"lib/sysdep/filesystem.h\"\t// O_*, S_*\n\nnamespace ERR\n{\n\tconst Status FILE_ACCESS = -110300;\n\tconst Status FILE_NOT_FOUND = -110301;\n}\n\n// @param oflag: either O_RDONLY or O_WRONLY (in which case O_CREAT and\n//   O_TRUNC are added), plus O_DIRECT if aio is desired\n// @return file descriptor or a negative Status\nLIB_API Status FileOpen(const OsPath& pathname, int oflag);\nLIB_API void FileClose(int& fd);\n\nclass File\n{\npublic:\n\tFile()\n\t\t: pathname(), fd(-1)\n\t{\n\t}\n\n\tFile(const OsPath& pathname, int oflag)\n\t{\n\t\tTHROW_STATUS_IF_ERR(Open(pathname, oflag));\n\t}\n\n\t~File()\n\t{\n\t\tClose();\n\t}\n\n\tStatus Open(const OsPath& pathname, int oflag)\n\t{\n\t\tStatus ret = FileOpen(pathname, oflag);\n\t\tRETURN_STATUS_IF_ERR(ret);\n\t\tthis->pathname = pathname;\n\t\tthis->fd = (int)ret;\n\t\tthis->oflag = oflag;\n\t\treturn INFO::OK;\n\t}\n\n\tvoid Close()\n\t{\n\t\tFileClose(fd);\n\t}\n\n\tconst OsPath& Pathname() const\n\t{\n\t\treturn pathname;\n\t}\n\n\tint Descriptor() const\n\t{\n\t\treturn fd;\n\t}\n\n\tint Flags() const\n\t{\n\t\treturn oflag;\n\t}\n\nprivate:\n\tOsPath pathname;\n\tint fd;\n\tint oflag;\n};\n\ntypedef shared_ptr<File> PFile;\n\n#endif\t// #ifndef INCLUDED_FILE\n"
  },
  {
    "path": "fpsgame/gui/file/file_system.cpp",
    "content": "/* Copyright (c) 2016 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * higher-level interface on top of sysdep/filesystem.h\n */\n\n#include \"precompiled.h\"\n#include \"lib/file/file_system.h\"\n\n#include <vector>\n#include <algorithm>\n#include <string>\n\n#include \"lib/sysdep/filesystem.h\"\n\n\nbool DirectoryExists(const OsPath& path)\n{\n\tWDIR* dir = wopendir(path);\n\tif(dir)\n\t{\n\t\twclosedir(dir);\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n\nbool FileExists(const OsPath& pathname)\n{\n\tstruct stat s;\n\tconst bool exists = wstat(pathname, &s) == 0;\n\treturn exists;\n}\n\n\nu64 FileSize(const OsPath& pathname)\n{\n\tstruct stat s;\n\tENSURE(wstat(pathname, &s) == 0);\n\treturn s.st_size;\n}\n\n\nStatus GetFileInfo(const OsPath& pathname, CFileInfo* pPtrInfo)\n{\n\terrno = 0;\n\tstruct stat s;\n\tmemset(&s, 0, sizeof(s));\n\tif(wstat(pathname, &s) != 0)\n\t\tWARN_RETURN(StatusFromErrno());\n\n\t*pPtrInfo = CFileInfo(pathname.Filename(), s.st_size, s.st_mtime);\n\treturn INFO::OK;\n}\n\n\nstruct DirDeleter\n{\n\tvoid operator()(WDIR* osDir) const\n\t{\n\t\tconst int ret = wclosedir(osDir);\n\t\tENSURE(ret == 0);\n\t}\n};\n\nStatus GetDirectoryEntries(const OsPath& path, CFileInfos* files, DirectoryNames* subdirectoryNames)\n{\n\t// open directory\n\terrno = 0;\n\tWDIR* pDir = wopendir(path);\n\tif(!pDir)\n\t\treturn StatusFromErrno();\t// NOWARN\n\tshared_ptr<WDIR> osDir(pDir, DirDeleter());\n\n\tfor(;;)\n\t{\n\t\terrno = 0;\n\t\tstruct wdirent* osEnt = wreaddir(osDir.get());\n\t\tif(!osEnt)\n\t\t{\n\t\t\t// no error, just no more entries to return\n\t\t\tif(!errno)\n\t\t\t\treturn INFO::OK;\n\t\t\tWARN_RETURN(StatusFromErrno());\n\t\t}\n\n\t\tfor(size_t i = 0; osEnt->d_name[i] != '\\0'; i++)\n\t\t\tRETURN_STATUS_IF_ERR(Path::Validate(osEnt->d_name[i]));\n\t\tconst OsPath name(osEnt->d_name);\n\n\t\t// get file information (mode, size, mtime)\n\t\tstruct stat s;\n#if OS_WIN\n\t\t// .. return wdirent directly (much faster than calling stat).\n\t\tRETURN_STATUS_IF_ERR(wreaddir_stat_np(osDir.get(), &s));\n#else\n\t\t// .. call regular stat().\n\t\terrno = 0;\n\t\tconst OsPath pathname = path / name;\n\t\tif(wstat(pathname, &s) != 0)\n\t\t\tWARN_RETURN(StatusFromErrno());\n#endif\n\n\t\tif(files && S_ISREG(s.st_mode))\n\t\t\tfiles->push_back(CFileInfo(name, s.st_size, s.st_mtime));\n\t\telse if(subdirectoryNames && S_ISDIR(s.st_mode) && name != L\".\" && name != L\"..\")\n\t\t\tsubdirectoryNames->push_back(name);\n\t}\n}\n\n\nStatus CreateDirectories(const OsPath& path, mode_t mode, bool breakpoint)\n{\n\tif(path.empty())\n\t\treturn INFO::OK;\n\n\tstruct stat s;\n\tif(wstat(path, &s) == 0)\n\t{\n\t\tif(!S_ISDIR(s.st_mode))\t// encountered a file\n\t\t\tWARN_RETURN(ERR::FAIL);\n\t\treturn INFO::OK;\n\t}\n\n\t// If we were passed a path ending with '/', strip the '/' now so that\n\t// we can consistently use Parent to find parent directory names\n\tif(path.IsDirectory())\n\t\treturn CreateDirectories(path.Parent(), mode, breakpoint);\n\n\tRETURN_STATUS_IF_ERR(CreateDirectories(path.Parent(), mode));\n\n\terrno = 0;\n\tif(wmkdir(path, mode) != 0)\n\t{\n\t\tdebug_printf(\"CreateDirectories: failed to mkdir %s (mode %d)\\n\", path.string8().c_str(), mode);\n\t\tif (breakpoint)\n\t\t\tWARN_RETURN(StatusFromErrno());\n\t\telse\n\t\t\treturn StatusFromErrno();\n\t}\n\n\treturn INFO::OK;\n}\n\n\nStatus DeleteDirectory(const OsPath& path)\n{\n\t// note: we have to recursively empty the directory before it can\n\t// be deleted (required by Windows and POSIX rmdir()).\n\n\tCFileInfos files; DirectoryNames subdirectoryNames;\n\tRETURN_STATUS_IF_ERR(GetDirectoryEntries(path, &files, &subdirectoryNames));\n\n\t// delete files\n\tfor(size_t i = 0; i < files.size(); i++)\n\t{\n\t\tconst OsPath pathname = path / files[i].Name();\n\t\terrno = 0;\n\t\tif(wunlink(pathname) != 0)\n\t\t\tWARN_RETURN(StatusFromErrno());\n\t}\n\n\t// recurse over subdirectoryNames\n\tfor(size_t i = 0; i < subdirectoryNames.size(); i++)\n\t\tRETURN_STATUS_IF_ERR(DeleteDirectory(path / subdirectoryNames[i]));\n\n\terrno = 0;\n\tif(wrmdir(path) != 0)\n\t\tWARN_RETURN(StatusFromErrno());\n\n\treturn INFO::OK;\n}\n"
  },
  {
    "path": "fpsgame/gui/file/file_system.h",
    "content": "/* Copyright (c) 2016 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * higher-level interface on top of sysdep/filesystem.h\n */\n\n#ifndef INCLUDED_FILE_SYSTEM\n#define INCLUDED_FILE_SYSTEM\n\n#include \"lib/os_path.h\"\n#include \"lib/posix/posix_filesystem.h\"\t// mode_t\n\n\nLIB_API bool DirectoryExists(const OsPath& path);\nLIB_API bool FileExists(const OsPath& pathname);\n\nLIB_API u64 FileSize(const OsPath& pathname);\n\n\n// (bundling size and mtime avoids a second expensive call to stat())\nclass CFileInfo\n{\npublic:\n\tCFileInfo()\n\t{\n\t}\n\n\tCFileInfo(const OsPath& name, off_t size, time_t mtime)\n\t\t: name(name), size(size), mtime(mtime)\n\t{\n\t}\n\n\tconst OsPath& Name() const\n\t{\n\t\treturn name;\n\t}\n\n\toff_t Size() const\n\t{\n\t\treturn size;\n\t}\n\n\ttime_t MTime() const\n\t{\n\t\treturn mtime;\n\t}\n\nprivate:\n\tOsPath name;\n\toff_t size;\n\ttime_t mtime;\n};\n\nLIB_API Status GetFileInfo(const OsPath& pathname, CFileInfo* fileInfo);\n\ntypedef std::vector<CFileInfo> CFileInfos;\ntypedef std::vector<OsPath> DirectoryNames;\n\nLIB_API Status GetDirectoryEntries(const OsPath& path, CFileInfos* files, DirectoryNames* subdirectoryNames);\n\n// same as boost::filesystem::create_directories, except that mkdir is invoked with\n// <mode> instead of 0755.\n// If the breakpoint is enabled, debug_break will be called if the directory didn't exist and couldn't be created.\nLIB_API Status CreateDirectories(const OsPath& path, mode_t mode, bool breakpoint = true);\n\nLIB_API Status DeleteDirectory(const OsPath& dirPath);\n\n#endif\t// #ifndef INCLUDED_FILE_SYSTEM\n"
  },
  {
    "path": "fpsgame/gui/file/io/io.cpp",
    "content": "/* Copyright (c) 2011 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#include \"precompiled.h\"\n#include \"lib/file/io/io.h\"\n\n#include \"lib/sysdep/rtl.h\"\n\nstatic const StatusDefinition ioStatusDefinitions[] = {\n\t{ ERR::IO, L\"Error during IO\", EIO }\n};\nSTATUS_ADD_DEFINITIONS(ioStatusDefinitions);\n\nnamespace io {\n\n// this is just a thin wrapper on top of lowio and POSIX aio.\n// note that the Windows aio implementation requires buffers, sizes and\n// offsets to be sector-aligned.\n\nStatus Issue(aiocb& cb, size_t queueDepth)\n{\n#if CONFIG2_FILE_ENABLE_AIO\n\tif(queueDepth > 1)\n\t{\n\t\tconst int ret = (cb.aio_lio_opcode == LIO_WRITE)? aio_write(&cb): aio_read(&cb);\n\t\tif(ret != 0)\n\t\t\tWARN_RETURN(StatusFromErrno());\n\t}\n\telse\n#else\n\tUNUSED2(queueDepth);\n#endif\n\t{\n\t\tENSURE(lseek(cb.aio_fildes, cb.aio_offset, SEEK_SET) == cb.aio_offset);\n\n\t\tvoid* buf = (void*)cb.aio_buf;\t// cast from volatile void*\n\t\tconst ssize_t bytesTransferred = (cb.aio_lio_opcode == LIO_WRITE)? write(cb.aio_fildes, buf, cb.aio_nbytes) : read(cb.aio_fildes, buf, cb.aio_nbytes);\n\t\tif(bytesTransferred < 0)\n\t\t\tWARN_RETURN(StatusFromErrno());\n\n\t\tcb.aio_nbytes = (size_t)bytesTransferred;\n\t}\n\n\treturn INFO::OK;\n}\n\n\nStatus WaitUntilComplete(aiocb& cb, size_t queueDepth)\n{\n#if CONFIG2_FILE_ENABLE_AIO\n\tif(queueDepth > 1)\n\t{\n\t\taiocb* const cbs = &cb;\n\t\ttimespec* const timeout = 0;\t// infinite\nSUSPEND_AGAIN:\n\t\terrno = 0;\n\t\tconst int ret = aio_suspend(&cbs, 1, timeout);\n\t\tif(ret != 0)\n\t\t{\n\t\t\tif(errno == EINTR) // interrupted by signal\n\t\t\t\tgoto SUSPEND_AGAIN;\n\t\t\tWARN_RETURN(StatusFromErrno());\n\t\t}\n\n\t\tconst int err = aio_error(&cb);\n\t\tENSURE(err != EINPROGRESS);\t// else aio_return is undefined\n\t\tssize_t bytesTransferred = aio_return(&cb);\n\t\tif(bytesTransferred == -1)\t// transfer failed\n\t\t{\n\t\t\terrno = err;\n\t\t\tWARN_RETURN(StatusFromErrno());\n\t\t}\n\t\tcb.aio_nbytes = (size_t)bytesTransferred;\n\t}\n#else\n\tUNUSED2(cb);\n\tUNUSED2(queueDepth);\n#endif\n\n\treturn INFO::OK;\n}\n\n}\t// namespace io\n"
  },
  {
    "path": "fpsgame/gui/file/io/io.h",
    "content": "/* Copyright (c) 2011 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * provide asynchronous and synchronous I/O with hooks to allow\n * overlapped processing or progress reporting.\n */\n\n#ifndef INCLUDED_IO\n#define INCLUDED_IO\n\n#include \"lib/config2.h\"\n#include \"lib/alignment.h\"\n#include \"lib/bits.h\"\n#include \"lib/timer.h\"\n#include \"lib/file/file.h\"\n#include \"lib/sysdep/filesystem.h\"\t// wtruncate\n#include \"lib/posix/posix_aio.h\"\t// LIO_READ, LIO_WRITE\n\n#include \"lib/allocators/unique_range.h\"\n\nnamespace ERR\n{\n\tconst Status IO = -110301;\n}\n\nnamespace io {\n\n// @return memory suitable for use as an I/O buffer (address is a\n// multiple of alignment, size is rounded up to a multiple of alignment)\n// @param alignment is automatically increased if smaller than the\n// UniqueRange requirement.\n//\n// use this instead of the file cache for write buffers that are\n// never reused (avoids displacing other items).\nstatic inline UniqueRange Allocate(size_t size, size_t alignment = maxSectorSize)\n{\n\treturn std::move(AllocateAligned(size, alignment));\n}\n\n\n#pragma pack(push, 1)\n\n// required information for any I/O (this is basically the same as aiocb,\n// but also applies to synchronous I/O and has shorter/nicer names.)\nstruct Operation\n{\n\t// @param buf can be 0, in which case temporary block buffers are allocated.\n\t// otherwise, it must be aligned and padded to the I/O alignment, e.g. via\n\t// io::Allocate.\n\tOperation(const File& file, void* buf, off_t size, off_t offset = 0)\n\t\t: fd(file.Descriptor()), opcode((file.Flags() & O_WRONLY)? LIO_WRITE : LIO_READ)\n\t\t, offset(offset), size(size), buf((void*)buf)\n\t{\n\t}\n\n\tvoid Validate() const\n\t{\n\t\tENSURE(fd >= 0);\n\t\tENSURE(opcode == LIO_READ || opcode == LIO_WRITE);\n\n\t\tENSURE(offset >= 0);\n\t\tENSURE(size >= 0);\n\t\t// buf can legitimately be 0 (see above)\n\t}\n\n\tint fd;\n\tint opcode;\n\n\toff_t offset;\n\toff_t size;\n\tvoid* buf;\n};\n\n\n// optional information how an Operation is to be carried out\nstruct Parameters\n{\n\t// default to single blocking I/Os\n\tParameters()\n\t\t: alignment(1)\t// no alignment requirements\n\t\t, blockSize(0)\t// do not split into blocks\n\t\t, queueDepth(1)\t// disable aio\n\t{\n\t}\n\n\t// parameters for asynchronous I/O that maximize throughput on current drives\n\tstruct OverlappedTag {};\n\tParameters(OverlappedTag)\n\t\t: alignment(maxSectorSize), blockSize(128*KiB), queueDepth(32)\n\t{\n\t}\n\n\tParameters(size_t blockSize, size_t queueDepth, off_t alignment = maxSectorSize)\n\t\t: alignment(alignment), blockSize(blockSize), queueDepth(queueDepth)\n\t{\n\t}\n\n\tvoid Validate(const Operation& op) const\n\t{\n\t\tENSURE(is_pow2(alignment));\n\t\tENSURE(alignment > 0);\n\n\t\tif(blockSize != 0)\n\t\t{\n\t\t\tENSURE(is_pow2(blockSize));\n\t\t\tENSURE(pageSize <= blockSize);\t// (don't bother checking an upper bound)\n\t\t}\n\n\t\tENSURE(1 <= queueDepth && queueDepth <= maxQueueDepth);\n\n\t\tENSURE(IsAligned(op.offset, alignment));\n\t\t// op.size doesn't need to be aligned\n\t\tENSURE(IsAligned(op.buf, alignment));\n\t}\n\n\t// (ATTO only allows 10, which improves upon 8)\n\tstatic const size_t maxQueueDepth = 32;\n\n\toff_t alignment;\n\n\tsize_t blockSize;\t// 0 for one big \"block\"\n\n\tsize_t queueDepth;\n};\n\n#define IO_OVERLAPPED io::Parameters(io::Parameters::OverlappedTag())\n\n\nstruct DefaultCompletedHook\n{\n\t/**\n\t * called after a block I/O has completed.\n\t * @return Status (see RETURN_STATUS_FROM_CALLBACK).\n\t *\n\t * allows progress notification and processing data while waiting for\n\t * previous I/Os to complete.\n\t **/\n\tStatus operator()(const u8* UNUSED(block), size_t UNUSED(blockSize)) const\n\t{\n\t\treturn INFO::OK;\n\t}\n};\n\n\nstruct DefaultIssueHook\n{\n\t/**\n\t * called before a block I/O is issued.\n\t * @return Status (see RETURN_STATUS_FROM_CALLBACK).\n\t *\n\t * allows generating the data to write while waiting for\n\t * previous I/Os to complete.\n\t **/\n\tStatus operator()(aiocb& UNUSED(cb)) const\n\t{\n\t\treturn INFO::OK;\n\t}\n};\n\n\n// ring buffer of partially initialized aiocb that can be passed\n// directly to aio_write etc. after setting offset and buffer.\nclass ControlBlockRingBuffer\n{\npublic:\n\tControlBlockRingBuffer(const Operation& op, const Parameters& p)\n\t\t: controlBlocks()\t// zero-initialize\n\t{\n\t\tconst size_t blockSize = p.blockSize? p.blockSize : (size_t)op.size;\n\n\t\tconst bool temporaryBuffersRequested = (op.buf == 0);\n\t\tif(temporaryBuffersRequested)\n\t\t\tbuffers = std::move(io::Allocate(blockSize * p.queueDepth, p.alignment));\n\n\t\tfor(size_t i = 0; i < ARRAY_SIZE(controlBlocks); i++)\n\t\t{\n\t\t\taiocb& cb = operator[](i);\n\t\t\tcb.aio_fildes = op.fd;\n\t\t\tcb.aio_nbytes = blockSize;\n\t\t\tcb.aio_lio_opcode = op.opcode;\n\t\t\tif(temporaryBuffersRequested)\n\t\t\t\tcb.aio_buf = (volatile void*)(uintptr_t(buffers.get()) + i * blockSize);\n\t\t}\n\t}\n\n\tINLINE aiocb& operator[](size_t counter)\n\t{\n\t\treturn controlBlocks[counter % ARRAY_SIZE(controlBlocks)];\n\t}\n\nprivate:\n\tUniqueRange buffers;\n\taiocb controlBlocks[Parameters::maxQueueDepth];\n};\n\n#pragma pack(pop)\n\n\nLIB_API Status Issue(aiocb& cb, size_t queueDepth);\nLIB_API Status WaitUntilComplete(aiocb& cb, size_t queueDepth);\n\n\n//-----------------------------------------------------------------------------\n// Run\n\n#ifndef ENABLE_IO_STATS\n#define ENABLE_IO_STATS 0\n#endif\n\n// (hooks must be passed by const reference to allow passing rvalues.\n// functors with non-const member data can mark them as mutable.)\ntemplate<class CompletedHook, class IssueHook>\nstatic inline Status Run(const Operation& op, const Parameters& p = Parameters(), const CompletedHook& completedHook = CompletedHook(), const IssueHook& issueHook = IssueHook())\n{\n\top.Validate();\n\tp.Validate(op);\n\n\tControlBlockRingBuffer controlBlockRingBuffer(op, p);\n\n#if ENABLE_IO_STATS\n\tconst double t0 = timer_Time();\n\tCOMPILER_FENCE;\n#endif\n\n\tconst off_t numBlocks = p.blockSize? (off_t)DivideRoundUp((u64)op.size, (u64)p.blockSize) : 1;\n\tfor(off_t blocksIssued = 0, blocksCompleted = 0; blocksCompleted < numBlocks; blocksCompleted++)\n\t{\n\t\tfor(; blocksIssued != numBlocks && blocksIssued < blocksCompleted + (off_t)p.queueDepth; blocksIssued++)\n\t\t{\n\t\t\taiocb& cb = controlBlockRingBuffer[blocksIssued];\n\t\t\tcb.aio_offset = op.offset + blocksIssued * p.blockSize;\n\t\t\tif(op.buf)\n\t\t\t\tcb.aio_buf = (volatile void*)(uintptr_t(op.buf) + blocksIssued * p.blockSize);\n\t\t\tif(blocksIssued == numBlocks-1)\n\t\t\t\tcb.aio_nbytes = round_up(size_t(op.size - blocksIssued * p.blockSize), size_t(p.alignment));\n\n\t\t\tRETURN_STATUS_FROM_CALLBACK(issueHook(cb));\n\n\t\t\tRETURN_STATUS_IF_ERR(Issue(cb, p.queueDepth));\n\t\t}\n\n\t\taiocb& cb = controlBlockRingBuffer[blocksCompleted];\n\t\tRETURN_STATUS_IF_ERR(WaitUntilComplete(cb, p.queueDepth));\n\n\t\tRETURN_STATUS_FROM_CALLBACK(completedHook((u8*)cb.aio_buf, cb.aio_nbytes));\n\t}\n\n#if ENABLE_IO_STATS\n\tCOMPILER_FENCE;\n\tconst double t1 = timer_Time();\n\tconst off_t totalSize = p.blockSize? numBlocks*p.blockSize : op.size;\n\tdebug_printf(\"IO: %.2f MB/s (%.2f)\\n\", totalSize/(t1-t0)/1e6, (t1-t0)*1e3);\n#endif\n\n\treturn INFO::OK;\n}\n\n// (overloads allow omitting parameters without requiring a template argument list)\ntemplate<class CompletedHook>\nstatic inline Status Run(const Operation& op, const Parameters& p = Parameters(), const CompletedHook& completedHook = CompletedHook())\n{\n\treturn Run(op, p, completedHook, DefaultIssueHook());\n}\n\nstatic inline Status Run(const Operation& op, const Parameters& p = Parameters())\n{\n\treturn Run(op, p, DefaultCompletedHook(), DefaultIssueHook());\n}\n\n\n//-----------------------------------------------------------------------------\n// Store\n\n// efficient writing requires preallocation; the resulting file is\n// padded to the sector size and needs to be truncated afterwards.\n// this function takes care of both.\ntemplate<class CompletedHook, class IssueHook>\nstatic inline Status Store(const OsPath& pathname, const void* data, size_t size, const Parameters& p = Parameters(), const CompletedHook& completedHook = CompletedHook(), const IssueHook& issueHook = IssueHook())\n{\n\tFile file;\n\tint oflag = O_WRONLY;\n\tif(p.queueDepth != 1)\n\t\toflag |= O_DIRECT;\n\tRETURN_STATUS_IF_ERR(file.Open(pathname, oflag));\n\tio::Operation op(file, (void*)data, size);\n\n#if OS_WIN\n\t(void)waio_Preallocate(op.fd, (off_t)size);\n#endif\n\n\tRETURN_STATUS_IF_ERR(io::Run(op, p, completedHook, issueHook));\n\n\tfile.Close();\t// (required by wtruncate)\n\n\tRETURN_STATUS_IF_ERR(wtruncate(pathname, size));\n\n\treturn INFO::OK;\n}\n\ntemplate<class CompletedHook>\nstatic inline Status Store(const OsPath& pathname, const void* data, size_t size, const Parameters& p = Parameters(), const CompletedHook& completedHook = CompletedHook())\n{\n\treturn Store(pathname, data, size, p, completedHook, DefaultIssueHook());\n}\n\nstatic inline Status Store(const OsPath& pathname, const void* data, size_t size, const Parameters& p = Parameters())\n{\n\treturn Store(pathname, data, size, p, DefaultCompletedHook(), DefaultIssueHook());\n}\n\n\n//-----------------------------------------------------------------------------\n// Load\n\n// convenience function provided for symmetry with Store.\ntemplate<class CompletedHook, class IssueHook>\nstatic inline Status Load(const OsPath& pathname, void* buf, size_t size, const Parameters& p = Parameters(), const CompletedHook& completedHook = CompletedHook(), const IssueHook& issueHook = IssueHook())\n{\n\tFile file;\n\tint oflag = O_RDONLY;\n\tif(p.queueDepth != 1)\n\t\toflag |= O_DIRECT;\n\tRETURN_STATUS_IF_ERR(file.Open(pathname, oflag));\n\tio::Operation op(file, buf, size);\n\treturn io::Run(op, p, completedHook, issueHook);\n}\n\ntemplate<class CompletedHook>\nstatic inline Status Load(const OsPath& pathname, void* buf, size_t size, const Parameters& p = Parameters(), const CompletedHook& completedHook = CompletedHook())\n{\n\treturn Load(pathname, buf, size, p, completedHook, DefaultIssueHook());\n}\n\nstatic inline Status Load(const OsPath& pathname, void* buf, size_t size, const Parameters& p = Parameters())\n{\n\treturn Load(pathname, buf, size, p, DefaultCompletedHook(), DefaultIssueHook());\n}\n\n}\t// namespace io\n\n#endif\t// #ifndef INCLUDED_IO\n"
  },
  {
    "path": "fpsgame/gui/file/io/write_buffer.cpp",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#include \"precompiled.h\"\n#include \"lib/file/io/write_buffer.h\"\n\n#include \"lib/bits.h\"\t// IsAligned\n#include \"lib/sysdep/cpu.h\"\n#include \"lib/allocators/shared_ptr.h\"\n#include \"lib/file/io/io.h\"\n\n\nstatic const size_t BLOCK_SIZE = 512*KiB;\n\n\nWriteBuffer::WriteBuffer()\n\t: m_capacity(pageSize), m_data((u8*)rtl_AllocateAligned(m_capacity, maxSectorSize), AlignedDeleter()), m_size(0)\n{\n}\n\n\nvoid WriteBuffer::EnsureSufficientCapacity(size_t size)\n{\n\tif(m_size + size > m_capacity)\n\t{\n\t\tm_capacity = round_up_to_pow2(m_size + size);\n\t\tshared_ptr<u8> newData;\n\t\tAllocateAligned(newData, m_capacity, maxSectorSize);\n\t\tmemcpy(newData.get(), m_data.get(), m_size);\n\t\tm_data = newData;\n\t}\n}\n\n\nvoid WriteBuffer::Append(const void* data, size_t size)\n{\n\tEnsureSufficientCapacity(size);\n\tmemcpy(m_data.get() + m_size, data, size);\n\tm_size += size;\n}\n\n\nvoid WriteBuffer::Reserve(size_t size)\n{\n\tEnsureSufficientCapacity(size);\n\tmemset(m_data.get() + m_size, 0, size);\n\tm_size += size;\n}\n\n\nvoid WriteBuffer::Overwrite(const void* data, size_t size, size_t offset)\n{\n\tENSURE(offset+size < m_size);\n\tmemcpy(m_data.get()+offset, data, size);\n}\n\n\n//-----------------------------------------------------------------------------\n// UnalignedWriter\n//-----------------------------------------------------------------------------\n\nUnalignedWriter::UnalignedWriter(const PFile& file, off_t ofs)\n\t: m_file(file), m_alignedBuf((u8*)rtl_AllocateAligned(BLOCK_SIZE, maxSectorSize), AlignedDeleter())\n{\n\tm_alignedOfs = round_down(ofs, (off_t)BLOCK_SIZE);\n\tconst size_t misalignment = (size_t)(ofs - m_alignedOfs);\n\tif(misalignment)\n\t{\n\t\tio::Operation op(*m_file.get(), m_alignedBuf.get(), BLOCK_SIZE, m_alignedOfs);\n\t\tTHROW_STATUS_IF_ERR(io::Run(op));\n\t}\n\tm_bytesUsed = misalignment;\n}\n\n\nUnalignedWriter::~UnalignedWriter()\n{\n\tFlush();\n}\n\n\nStatus UnalignedWriter::Append(const u8* data, size_t size) const\n{\n\twhile(size != 0)\n\t{\n\t\t// optimization: write directly from the input buffer, if possible\n\t\tconst size_t alignedSize = (size / BLOCK_SIZE) * BLOCK_SIZE;\n\t\tif(m_bytesUsed == 0 && IsAligned(data, maxSectorSize) && alignedSize != 0)\n\t\t{\n\t\t\tio::Operation op(*m_file.get(), (void*)data, alignedSize, m_alignedOfs);\n\t\t\tRETURN_STATUS_IF_ERR(io::Run(op));\n\t\t\tm_alignedOfs += (off_t)alignedSize;\n\t\t\tdata += alignedSize;\n\t\t\tsize -= alignedSize;\n\t\t}\n\n\t\tconst size_t chunkSize = std::min(size, BLOCK_SIZE-m_bytesUsed);\n\t\tmemcpy(m_alignedBuf.get()+m_bytesUsed, data, chunkSize);\n\t\tm_bytesUsed += chunkSize;\n\t\tdata += chunkSize;\n\t\tsize -= chunkSize;\n\n\t\tif(m_bytesUsed == BLOCK_SIZE)\n\t\t\tRETURN_STATUS_IF_ERR(WriteBlock());\n\t}\n\n\treturn INFO::OK;\n}\n\n\nvoid UnalignedWriter::Flush() const\n{\n\tif(m_bytesUsed)\n\t{\n\t\tmemset(m_alignedBuf.get()+m_bytesUsed, 0, BLOCK_SIZE-m_bytesUsed);\n\t\t(void)WriteBlock();\n\t}\n}\n\n\nStatus UnalignedWriter::WriteBlock() const\n{\n\tio::Operation op(*m_file.get(), m_alignedBuf.get(), BLOCK_SIZE, m_alignedOfs);\n\tRETURN_STATUS_IF_ERR(io::Run(op));\n\tm_alignedOfs += BLOCK_SIZE;\n\tm_bytesUsed = 0;\n\treturn INFO::OK;\n}\n"
  },
  {
    "path": "fpsgame/gui/file/io/write_buffer.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#ifndef INCLUDED_WRITE_BUFFER\n#define INCLUDED_WRITE_BUFFER\n\n#include \"lib/file/file.h\"\n\nclass WriteBuffer\n{\npublic:\n\tWriteBuffer();\n\n\tvoid Append(const void* data, size_t size);\n\tvoid Reserve(size_t size);\n\tvoid Overwrite(const void* data, size_t size, size_t offset);\n\n\tshared_ptr<u8> Data() const\n\t{\n\t\treturn m_data;\n\t}\n\n\tsize_t Size() const\n\t{\n\t\treturn m_size;\n\t}\n\nprivate:\n\tvoid EnsureSufficientCapacity(size_t size);\n\n\tsize_t m_capacity;\t// must come first (init order)\n\n\tshared_ptr<u8> m_data;\n\tsize_t m_size;\n};\n\n\nclass UnalignedWriter\n{\n\tNONCOPYABLE(UnalignedWriter);\npublic:\n\tUnalignedWriter(const PFile& file, off_t ofs);\n\t~UnalignedWriter();\n\n\t/**\n\t * add data to the align buffer, writing it out to disk if full.\n\t **/\n\tStatus Append(const u8* data, size_t size) const;\n\n\t/**\n\t * zero-initialize any remaining space in the align buffer and write\n\t * it to the file. this is called by the destructor.\n\t **/\n\tvoid Flush() const;\n\nprivate:\n\tStatus WriteBlock() const;\n\n\tPFile m_file;\n\tshared_ptr<u8> m_alignedBuf;\n\tmutable off_t m_alignedOfs;\n\tmutable size_t m_bytesUsed;\n};\n\ntypedef shared_ptr<UnalignedWriter> PUnalignedWriter;\n\n#endif\t// #ifndef INCLUDED_WRITE_BUFFER\n"
  },
  {
    "path": "fpsgame/gui/file/vfs/file_cache.cpp",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * cache of file contents (supports zero-copy IO)\n */\n\n#include \"precompiled.h\"\n#include \"lib/file/vfs/file_cache.h\"\n\n#include \"lib/external_libraries/suppress_boost_warnings.h\"\n\n#include \"lib/file/common/file_stats.h\"\n#include \"lib/adts/cache_adt.h\"\n#include \"lib/bits.h\"                   // round_up\n#include \"lib/allocators/allocator_checker.h\"\n#include \"lib/allocators/shared_ptr.h\"\n#include \"lib/allocators/headerless.h\"\n#include \"lib/sysdep/os_cpu.h\"\t// os_cpu_PageSize\n#include \"lib/posix/posix_mman.h\"\t// mprotect\n\n\n//-----------------------------------------------------------------------------\n// allocator\n\n/*\nthe biggest worry of a file cache is external fragmentation. there are two\nbasic ways to combat this:\n1) 'defragment' periodically - move blocks around to increase\nsize of available 'holes'.\n2) prevent fragmentation from occurring at all via\ndeliberate alloc/free policy.\n\nfile contents are returned directly to the user (zero-copy IO), so only\ncurrently unreferenced blocks can be moved. it is believed that this would\nseverely hamper defragmentation; we therefore go with the latter approach.\n\nthe basic insight is: fragmentation occurs when a block is freed whose\nneighbors are not free (thus preventing coalescing). this can be prevented by\nallocating objects of similar lifetimes together. typical workloads\n(uniform access frequency) already show such behavior: the Landlord cache\nmanager evicts files in an LRU manner, which matches the allocation policy.\n\nreferences:\n\"The Memory Fragmentation Problem - Solved?\" (Johnstone and Wilson)\n\"Dynamic Storage Allocation - A Survey and Critical Review\" (Johnstone and Wilson)\n*/\n\n// shared_ptr<u8>s must own a reference to their allocator to ensure it's extant when\n// they are freed. it is stored in the shared_ptr deleter.\nclass Allocator;\ntypedef shared_ptr<Allocator> PAllocator;\n\nclass FileCacheDeleter\n{\npublic:\n\tFileCacheDeleter(size_t size, const PAllocator& allocator)\n\t\t: m_size(size), m_allocator(allocator)\n\t{\n\t}\n\n\t// (this uses Allocator and must come after its definition)\n\tvoid operator()(u8* mem) const;\n\nprivate:\n\tsize_t m_size;\n\tPAllocator m_allocator;\n};\n\n\n// adds statistics and AllocatorChecker to a HeaderlessAllocator\nclass Allocator\n{\npublic:\n\tAllocator(size_t maxSize)\n\t\t: m_allocator(maxSize)\n\t{\n\t}\n\n\tshared_ptr<u8> Allocate(size_t size, const PAllocator& pthis)\n\t{\n\t\tconst size_t alignedSize = Align<maxSectorSize>(size);\n\n\t\tu8* mem = (u8*)m_allocator.Allocate(alignedSize);\n\t\tif(!mem)\n\t\t\treturn DummySharedPtr<u8>(0);\t// (prevent FileCacheDeleter from seeing a null pointer)\n\n#ifndef NDEBUG\n\t\tm_checker.OnAllocate(mem, alignedSize);\n#endif\n\n\t\tstats_buf_alloc(size, alignedSize);\n\t\treturn shared_ptr<u8>(mem, FileCacheDeleter(size, pthis));\n\t}\n\n\tvoid Deallocate(u8* mem, size_t size)\n\t{\n\t\tconst size_t alignedSize = Align<maxSectorSize>(size);\n\n\t\t// (re)allow writes in case the buffer was made read-only. it would\n\t\t// be nice to unmap the buffer, but this is not possible because\n\t\t// HeaderlessAllocator needs to affix boundary tags.\n\t\t(void)mprotect(mem, size, PROT_READ|PROT_WRITE);\n\n#ifndef NDEBUG\n\t\tm_checker.OnDeallocate(mem, alignedSize);\n#endif\n\t\tm_allocator.Deallocate(mem, alignedSize);\n\n\t\tstats_buf_free();\n\t}\n\nprivate:\n\tHeaderlessAllocator m_allocator;\n\n#ifndef NDEBUG\n\tAllocatorChecker m_checker;\n#endif\n};\n\n\nvoid FileCacheDeleter::operator()(u8* mem) const\n{\n\tm_allocator->Deallocate(mem, m_size);\n}\n\n\n//-----------------------------------------------------------------------------\n// FileCache::Impl\n//-----------------------------------------------------------------------------\n\n// since users are strongly encouraged to only load/process one file at a\n// time, there won't be many active references to cache entries. we could\n// take advantage of this with a separate extant list, but the cache's\n// hash map should be fast enough and this way is less work than maintaining\n// (possibly disjunct) cached and extant lists.\n\nclass FileCache::Impl\n{\npublic:\n\tImpl(size_t maxSize)\n\t\t: m_allocator(new Allocator(maxSize))\n\t{\n\t}\n\n\tshared_ptr<u8> Reserve(size_t size)\n\t{\n\t\t// (should never happen because the VFS ensures size != 0.)\n\t\tENSURE(size != 0);\n\n\t\t// (300 iterations have been observed when reserving several MB\n\t\t// of space in a full cache)\n\t\tfor(;;)\n\t\t{\n\t\t\t{\n\t\t\t\tshared_ptr<u8> data = m_allocator->Allocate(size, m_allocator);\n\t\t\t\tif(data)\n\t\t\t\t\treturn data;\n\t\t\t}\n\n\t\t\t// remove least valuable entry from cache (if users are holding\n\t\t\t// references, the contents won't actually be deallocated)\n\t\t\t{\n\t\t\t\tshared_ptr<u8> discardedData; size_t discardedSize;\n\t\t\t\tbool removed = m_cache.remove_least_valuable(&discardedData, &discardedSize);\n\t\t\t\t// the cache is empty, and allocation still failed.\n\t\t\t\t// apparently the cache is full of data that's still\n\t\t\t\t// referenced, so we can't reserve any more space.\n\t\t\t\tif(!removed)\n\t\t\t\t\treturn shared_ptr<u8>();\n\t\t\t}\n\t\t}\n\t}\n\n\tvoid Add(const VfsPath& pathname, const shared_ptr<u8>& data, size_t size, size_t cost)\n\t{\n\t\t// zero-copy cache => all users share the contents => must not\n\t\t// allow changes. this will be reverted when deallocating.\n\t\t(void)mprotect((void*)data.get(), size, PROT_READ);\n\n\t\tm_cache.add(pathname, data, size, cost);\n\t}\n\n\tbool Retrieve(const VfsPath& pathname, shared_ptr<u8>& data, size_t& size)\n\t{\n\t\t// (note: don't call stats_cache because we don't know the file size\n\t\t// in case of a cache miss; doing so is left to the caller.)\n\t\tstats_buf_ref();\n\n\t\treturn m_cache.retrieve(pathname, data, &size);\n\t}\n\n\tvoid Remove(const VfsPath& pathname)\n\t{\n\t\tm_cache.remove(pathname);\n\n\t\t// note: we could check if someone is still holding a reference\n\t\t// to the contents, but that currently doesn't matter.\n\t}\n\nprivate:\n\ttypedef Cache<VfsPath, shared_ptr<u8> > CacheType;\n\tCacheType m_cache;\n\n\tPAllocator m_allocator;\n};\n\n\n//-----------------------------------------------------------------------------\n\nFileCache::FileCache(size_t size)\n\t: impl(new Impl(size))\n{\n}\n\nshared_ptr<u8> FileCache::Reserve(size_t size)\n{\n\treturn impl->Reserve(size);\n}\n\nvoid FileCache::Add(const VfsPath& pathname, const shared_ptr<u8>& data, size_t size, size_t cost)\n{\n\timpl->Add(pathname, data, size, cost);\n}\n\nvoid FileCache::Remove(const VfsPath& pathname)\n{\n\timpl->Remove(pathname);\n}\n\nbool FileCache::Retrieve(const VfsPath& pathname, shared_ptr<u8>& data, size_t& size)\n{\n\treturn impl->Retrieve(pathname, data, size);\n}\n"
  },
  {
    "path": "fpsgame/gui/file/vfs/file_cache.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * cache of file contents (supports zero-copy IO)\n */\n\n#ifndef INCLUDED_FILE_CACHE\n#define INCLUDED_FILE_CACHE\n\n#include \"lib/file/vfs/vfs_path.h\"\n\n/**\n * cache of file contents with support for zero-copy IO.\n * this works by reserving a region of the cache, using it as the IO buffer,\n * and returning the memory directly to users. optional write-protection\n * via MMU ensures that the shared contents aren't inadvertently changed.\n *\n * (unique copies of) VFS pathnames are used as lookup key and owner tag.\n *\n * to ensure efficient operation and prevent fragmentation, only one\n * reference should be active at a time. in other words, read a file,\n * process it, and only then start reading the next file.\n *\n * rationale: this is rather similar to BlockCache; however, the differences\n * (Reserve's size parameter, eviction policies) are enough to warrant\n * separate implementations.\n **/\nclass FileCache\n{\npublic:\n\t/**\n\t * @param size maximum amount [bytes] of memory to use for the cache.\n\t * (managed as a virtual memory region that's committed on-demand)\n\t **/\n\tFileCache(size_t size);\n\n\t/**\n\t * Reserve a chunk of the cache's memory region.\n\t *\n\t * @param size required number of bytes (more may be allocated due to\n\t * alignment and/or internal fragmentation)\n\t * @return memory suitably aligned for IO; never fails.\n\t *\n\t * it is expected that this data will be Add()-ed once its IO completes.\n\t **/\n\tshared_ptr<u8> Reserve(size_t size);\n\n\t/**\n\t * Add a file's contents to the cache.\n\t *\n\t * The cache will be able to satisfy subsequent Retrieve() calls by\n\t * returning this data; if CONFIG2_CACHE_READ_ONLY, the buffer is made\n\t * read-only. If need be and no references are currently attached to it,\n\t * the memory can also be commandeered by Reserve().\n\t *\n\t * @param data\n\t * @param size\n\t * @param pathname key that will be used to Retrieve file contents.\n\t * @param cost is the expected cost of retrieving the file again and\n\t *\t\t  influences how/when it is evicted from the cache.\n\t **/\n\tvoid Add(const VfsPath& pathname, const shared_ptr<u8>& data, size_t size, size_t cost = 1);\n\n\t/**\n\t * Remove a file's contents from the cache (if it exists).\n\t *\n\t * this ensures subsequent reads of the files see the current, presumably\n\t * recently changed, contents of the file.\n\t *\n\t * this would typically be called in response to a notification that a\n\t * file has changed.\n\t **/\n\tvoid Remove(const VfsPath& pathname);\n\n\t/**\n\t * Attempt to retrieve a file's contents from the file cache.\n\t *\n\t * @return whether the contents were successfully retrieved; if so,\n\t * data references the read-only file contents.\n\t **/\n\tbool Retrieve(const VfsPath& pathname, shared_ptr<u8>& data, size_t& size);\n\nprivate:\n\tclass Impl;\n\tshared_ptr<Impl> impl;\n};\n\n#endif\t// #ifndef INCLUDED_FILE_CACHE\n"
  },
  {
    "path": "fpsgame/gui/file/vfs/tests/test_vfs_tree.h",
    "content": "/* Copyright (c) 2013 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#include \"lib/self_test.h\"\n\n#include \"lib/file/vfs/vfs_tree.h\"\n#include \"lib/file/common/file_loader.h\"\n\nclass MockLoader : public IFileLoader\n{\nprivate:\n\tsize_t m_Precedence;\npublic:\n\tMockLoader(size_t precedence) :\n\t\tm_Precedence(precedence)\n\t{\n\t}\n\n\tsize_t Precedence() const { return m_Precedence; }\n\twchar_t LocationCode() const { return L'\\0'; }\n\tOsPath Path() const { return L\"\";}\n\tStatus Load(const OsPath& UNUSED(name), const shared_ptr<u8>& UNUSED(buf), size_t UNUSED(size)) const {return INFO::OK; }\n};\n\nclass TestVfsTree : public CxxTest::TestSuite\n{\npublic:\n\tvoid test_replacement()\n\t{\n\t\tVfsDirectory dir;\n\t\tPIFileLoader loader(new MockLoader(1));\n\n\t\tVfsFile file0(\"a\", 0,  0, 0, loader);\n\t\tVfsFile file1(\"a\", 0, 20, 0, loader);\n\t\tVfsFile file2(\"a\", 0, 10, 1, loader);\n\n\t\t// Modification time\n\t\tTS_ASSERT_EQUALS(dir.AddFile(file0)->MTime(), file0.MTime());\n\t\tTS_ASSERT_EQUALS(dir.AddFile(file1)->MTime(), file1.MTime());\n\t\tTS_ASSERT_EQUALS(dir.AddFile(file0)->MTime(), file1.MTime());\n\n\t\t// Priority\n\t\tTS_ASSERT_EQUALS(dir.AddFile(file2)->MTime(), file2.MTime());\n\t\tTS_ASSERT_EQUALS(dir.AddFile(file1)->MTime(), file2.MTime());\n\t}\n};\n"
  },
  {
    "path": "fpsgame/gui/file/vfs/vfs.cpp",
    "content": "/* Copyright (c) 2014 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#include \"precompiled.h\"\n#include \"lib/file/vfs/vfs.h\"\n\n#include \"lib/allocators/shared_ptr.h\"\n#include \"lib/posix/posix_pthread.h\"\n#include \"lib/file/file_system.h\"\n#include \"lib/file/common/file_stats.h\"\n#include \"lib/file/common/trace.h\"\n#include \"lib/file/archive/archive.h\"\n#include \"lib/file/io/io.h\"\n#include \"lib/file/vfs/vfs_tree.h\"\n#include \"lib/file/vfs/vfs_lookup.h\"\n#include \"lib/file/vfs/vfs_populate.h\"\n#include \"lib/file/vfs/file_cache.h\"\n\nstatic const StatusDefinition vfsStatusDefinitions[] = {\n\t{ ERR::VFS_DIR_NOT_FOUND, L\"VFS directory not found\" },\n\t{ ERR::VFS_FILE_NOT_FOUND, L\"VFS file not found\" },\n\t{ ERR::VFS_ALREADY_MOUNTED, L\"VFS path already mounted\" }\n};\nSTATUS_ADD_DEFINITIONS(vfsStatusDefinitions);\n\nstatic pthread_mutex_t vfs_mutex = PTHREAD_MUTEX_INITIALIZER;\nnamespace {\nstruct ScopedLock\n{\n\tScopedLock() { pthread_mutex_lock(&vfs_mutex); }\n\t~ScopedLock() { pthread_mutex_unlock(&vfs_mutex); }\n};\n} // namespace\n\nclass VFS : public IVFS\n{\npublic:\n\tVFS(size_t cacheSize)\n\t\t: m_cacheSize(cacheSize), m_fileCache(m_cacheSize)\n\t\t, m_trace(CreateDummyTrace(8*MiB))\n\t{\n\t}\n\n\tvirtual Status Mount(const VfsPath& mountPoint, const OsPath& path, size_t flags /* = 0 */, size_t priority /* = 0 */)\n\t{\n\t\tScopedLock s;\n\t\tif(!DirectoryExists(path))\n\t\t{\n\t\t\tif(flags & VFS_MOUNT_MUST_EXIST)\n\t\t\t\treturn ERR::VFS_DIR_NOT_FOUND;\t// NOWARN\n\t\t\telse\n\t\t\t\tRETURN_STATUS_IF_ERR(CreateDirectories(path, 0700));\n\t\t}\n\n\t\tVfsDirectory* directory;\n\t\tWARN_RETURN_STATUS_IF_ERR(vfs_Lookup(mountPoint, &m_rootDirectory, directory, 0, VFS_LOOKUP_ADD|VFS_LOOKUP_SKIP_POPULATE));\n\n\t\tPRealDirectory realDirectory(new RealDirectory(path, priority, flags));\n\t\tRETURN_STATUS_IF_ERR(vfs_Attach(directory, realDirectory));\n\t\treturn INFO::OK;\n\t}\n\n\tvirtual Status GetFileInfo(const VfsPath& pathname, CFileInfo* pfileInfo) const\n\t{\n\t\tScopedLock s;\n\t\tVfsDirectory* directory; VfsFile* file;\n\t\tStatus ret = vfs_Lookup(pathname, &m_rootDirectory, directory, &file);\n\t\tif(!pfileInfo)\t// just indicate if the file exists without raising warnings.\n\t\t\treturn ret;\n\t\tWARN_RETURN_STATUS_IF_ERR(ret);\n\t\t*pfileInfo = CFileInfo(file->Name(), file->Size(), file->MTime());\n\t\treturn INFO::OK;\n\t}\n\n\tvirtual Status GetFilePriority(const VfsPath& pathname, size_t* ppriority) const\n\t{\n\t\tScopedLock s;\n\t\tVfsDirectory* directory; VfsFile* file;\n\t\tRETURN_STATUS_IF_ERR(vfs_Lookup(pathname, &m_rootDirectory, directory, &file));\n\t\t*ppriority = file->Priority();\n\t\treturn INFO::OK;\n\t}\n\n\tvirtual Status GetDirectoryEntries(const VfsPath& path, CFileInfos* fileInfos, DirectoryNames* subdirectoryNames) const\n\t{\n\t\tScopedLock s;\n\t\tVfsDirectory* directory;\n\t\tRETURN_STATUS_IF_ERR(vfs_Lookup(path, &m_rootDirectory, directory, 0));\n\n\t\tif(fileInfos)\n\t\t{\n\t\t\tconst VfsDirectory::VfsFiles& files = directory->Files();\n\t\t\tfileInfos->clear();\n\t\t\tfileInfos->reserve(files.size());\n\t\t\tfor(VfsDirectory::VfsFiles::const_iterator it = files.begin(); it != files.end(); ++it)\n\t\t\t{\n\t\t\t\tconst VfsFile& file = it->second;\n\t\t\t\tfileInfos->push_back(CFileInfo(file.Name(), file.Size(), file.MTime()));\n\t\t\t}\n\t\t}\n\n\t\tif(subdirectoryNames)\n\t\t{\n\t\t\tconst VfsDirectory::VfsSubdirectories& subdirectories = directory->Subdirectories();\n\t\t\tsubdirectoryNames->clear();\n\t\t\tsubdirectoryNames->reserve(subdirectories.size());\n\t\t\tfor(VfsDirectory::VfsSubdirectories::const_iterator it = subdirectories.begin(); it != subdirectories.end(); ++it)\n\t\t\t\tsubdirectoryNames->push_back(it->first);\n\t\t}\n\n\t\treturn INFO::OK;\n\t}\n\n\tvirtual Status CreateFile(const VfsPath& pathname, const shared_ptr<u8>& fileContents, size_t size)\n\t{\n\t\tScopedLock s;\n\t\tVfsDirectory* directory;\n\t\tStatus st;\n\t\tst = vfs_Lookup(pathname, &m_rootDirectory, directory, 0, VFS_LOOKUP_ADD|VFS_LOOKUP_CREATE|VFS_LOOKUP_CREATE_ALWAYS);\n\t\tif (st == ERR::FILE_ACCESS)\n\t\t\treturn ERR::FILE_ACCESS;\n\n\t\tWARN_RETURN_STATUS_IF_ERR(st);\n\n\t\tconst PRealDirectory& realDirectory = directory->AssociatedDirectory();\n\t\tconst OsPath name = pathname.Filename();\n\t\tRETURN_STATUS_IF_ERR(realDirectory->Store(name, fileContents, size));\n\n\t\t// wipe out any cached blocks. this is necessary to cover the (rare) case\n\t\t// of file cache contents predating the file write.\n\t\tm_fileCache.Remove(pathname);\n\n\t\tconst VfsFile file(name, size, time(0), realDirectory->Priority(), realDirectory);\n\t\tdirectory->AddFile(file);\n\n\t\tm_trace->NotifyStore(pathname, size);\n\t\treturn INFO::OK;\n\t}\n\n\tvirtual Status ReplaceFile(const VfsPath& pathname, const shared_ptr<u8>& fileContents, size_t size)\n\t{\n\t\tScopedLock s;\n\t\tVfsDirectory* directory;\n\t\tVfsFile* file;\n\t\tStatus st;\n\t\tst = vfs_Lookup(pathname, &m_rootDirectory, directory, &file, VFS_LOOKUP_ADD|VFS_LOOKUP_CREATE);\n\n\t\t// There is no such file, create it.\n\t\tif (st == ERR::VFS_FILE_NOT_FOUND)\n\t\t{\n\t\t\ts.~ScopedLock();\n\t\t\treturn CreateFile(pathname, fileContents, size);\n\t\t}\n\n\t\tWARN_RETURN_STATUS_IF_ERR(st);\n\n\t\tRealDirectory realDirectory(file->Loader()->Path(), file->Priority(), directory->AssociatedDirectory()->Flags());\n\t\tRETURN_STATUS_IF_ERR(realDirectory.Store(pathname.Filename(), fileContents, size));\n\n\t\t// See comment in CreateFile\n\t\tm_fileCache.Remove(pathname);\n\n\t\tdirectory->AddFile(*file);\n\n\t\tm_trace->NotifyStore(pathname, size);\n\t\treturn INFO::OK;\n\t}\n\n\tvirtual Status LoadFile(const VfsPath& pathname, shared_ptr<u8>& fileContents, size_t& size)\n\t{\n\t\tScopedLock s;\n\t\tconst bool isCacheHit = m_fileCache.Retrieve(pathname, fileContents, size);\n\t\tif(!isCacheHit)\n\t\t{\n\t\t\tVfsDirectory* directory; VfsFile* file;\n\t\t\t// per 2010-05-01 meeting, this shouldn't raise 'scary error\n\t\t\t// dialogs', which might fail to display the culprit pathname\n\t\t\t// instead, callers should log the error, including pathname.\n\t\t\tRETURN_STATUS_IF_ERR(vfs_Lookup(pathname, &m_rootDirectory, directory, &file));\n\n\t\t\tfileContents = DummySharedPtr((u8*)0);\n\t\t\tsize = file->Size();\n\t\t\tif(size != 0)\t// (the file cache can't handle zero-length allocations)\n\t\t\t{\n\t\t\t\tif(size < m_cacheSize/2)\t// (avoid evicting lots of previous data)\n\t\t\t\t\tfileContents = m_fileCache.Reserve(size);\n\t\t\t\tif(fileContents)\n\t\t\t\t{\n\t\t\t\t\tRETURN_STATUS_IF_ERR(file->Loader()->Load(file->Name(), fileContents, file->Size()));\n\t\t\t\t\tm_fileCache.Add(pathname, fileContents, size);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tRETURN_STATUS_IF_ERR(AllocateAligned(fileContents, size, maxSectorSize));\n\t\t\t\t\tRETURN_STATUS_IF_ERR(file->Loader()->Load(file->Name(), fileContents, file->Size()));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tstats_io_user_request(size);\n\t\tstats_cache(isCacheHit? CR_HIT : CR_MISS, size);\n\t\tm_trace->NotifyLoad(pathname, size);\n\n\t\treturn INFO::OK;\n\t}\n\n\tvirtual std::wstring TextRepresentation() const\n\t{\n\t\tScopedLock s;\n\t\tstd::wstring textRepresentation;\n\t\ttextRepresentation.reserve(100*KiB);\n\t\tDirectoryDescriptionR(textRepresentation, m_rootDirectory, 0);\n\t\treturn textRepresentation;\n\t}\n\n\tvirtual Status GetRealPath(const VfsPath& pathname, OsPath& realPathname)\n\t{\n\t\tScopedLock s;\n\t\tVfsDirectory* directory; VfsFile* file;\n\t\tWARN_RETURN_STATUS_IF_ERR(vfs_Lookup(pathname, &m_rootDirectory, directory, &file));\n\t\trealPathname = file->Loader()->Path() / pathname.Filename();\n\t\treturn INFO::OK;\n\t}\n\n\tvirtual Status GetDirectoryRealPath(const VfsPath& pathname, OsPath& realPathname)\n\t{\n\t\tScopedLock s;\n\t\tVfsDirectory* directory;\n\t\tWARN_RETURN_STATUS_IF_ERR(vfs_Lookup(pathname, &m_rootDirectory, directory, NULL));\n\t\trealPathname = directory->AssociatedDirectory()->Path();\n\t\treturn INFO::OK;\n\t}\n\n\tvirtual Status GetVirtualPath(const OsPath& realPathname, VfsPath& pathname)\n\t{\n\t\tScopedLock s;\n\t\tconst OsPath realPath = realPathname.Parent()/\"\";\n\t\tVfsPath path;\n\t\tRETURN_STATUS_IF_ERR(FindRealPathR(realPath, m_rootDirectory, L\"\", path));\n\t\tpathname = path / realPathname.Filename();\n\t\treturn INFO::OK;\n\t}\n\n\tvirtual Status RemoveFile(const VfsPath& pathname)\n\t{\n\t\tScopedLock s;\n\t\tm_fileCache.Remove(pathname);\n\n\t\tVfsDirectory* directory; VfsFile* file;\n\t\tRETURN_STATUS_IF_ERR(vfs_Lookup(pathname, &m_rootDirectory, directory, &file));\n\t\tdirectory->RemoveFile(file->Name());\n\n\t\treturn INFO::OK;\n\t}\n\n\tvirtual Status RepopulateDirectory(const VfsPath& path)\n\t{\n\t\tScopedLock s;\n\n\t\tVfsDirectory* directory;\n\t\tRETURN_STATUS_IF_ERR(vfs_Lookup(path, &m_rootDirectory, directory, 0));\n\t\tdirectory->RequestRepopulate();\n\n\t\treturn INFO::OK;\n\t}\n\n\tvirtual void Clear()\n\t{\n\t\tScopedLock s;\n\t\tm_rootDirectory.Clear();\n\t}\n\nprivate:\n\tStatus FindRealPathR(const OsPath& realPath, const VfsDirectory& directory, const VfsPath& curPath, VfsPath& path)\n\t{\n\t\tPRealDirectory realDirectory = directory.AssociatedDirectory();\n\t\tif(realDirectory && realDirectory->Path() == realPath)\n\t\t{\n\t\t\tpath = curPath;\n\t\t\treturn INFO::OK;\n\t\t}\n\n\t\tconst VfsDirectory::VfsSubdirectories& subdirectories = directory.Subdirectories();\n\t\tfor(VfsDirectory::VfsSubdirectories::const_iterator it = subdirectories.begin(); it != subdirectories.end(); ++it)\n\t\t{\n\t\t\tconst OsPath& subdirectoryName = it->first;\n\t\t\tconst VfsDirectory& subdirectory = it->second;\n\t\t\tStatus ret = FindRealPathR(realPath, subdirectory, curPath / subdirectoryName/\"\", path);\n\t\t\tif(ret == INFO::OK)\n\t\t\t\treturn INFO::OK;\n\t\t}\n\n\t\treturn ERR::PATH_NOT_FOUND;\t// NOWARN\n\t}\n\n\tsize_t m_cacheSize;\n\tFileCache m_fileCache;\n\tPITrace m_trace;\n\tmutable VfsDirectory m_rootDirectory;\n};\n\n//-----------------------------------------------------------------------------\n\nPIVFS CreateVfs(size_t cacheSize)\n{\n\treturn PIVFS(new VFS(cacheSize));\n}\n"
  },
  {
    "path": "fpsgame/gui/file/vfs/vfs.h",
    "content": "/* Copyright (c) 2013 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * Virtual File System API - allows transparent access to files in\n * archives, modding via multiple mount points and hotloading.\n */\n\n#ifndef INCLUDED_VFS\n#define INCLUDED_VFS\n\n#include \"lib/file/file_system.h\"\t// CFileInfo\n#include \"lib/file/vfs/vfs_path.h\"\n\nnamespace ERR\n{\n\tconst Status VFS_DIR_NOT_FOUND   = -110100;\n\tconst Status VFS_FILE_NOT_FOUND  = -110101;\n\tconst Status VFS_ALREADY_MOUNTED = -110102;\n}\n\n// (recursive mounting and mounting archives are no longer optional since they don't hurt)\nenum VfsMountFlags\n{\n\t/**\n\t * all real directories mounted during this operation will be watched\n\t * for changes. this flag is provided to avoid watches in output-only\n\t * directories, e.g. screenshots/ (only causes unnecessary overhead).\n\t **/\n\tVFS_MOUNT_WATCH = 1,\n\n\t/**\n\t * anything mounted from here should be included when building archives.\n\t **/\n\tVFS_MOUNT_ARCHIVABLE = 2,\n\n\t/**\n\t * return ERR::VFS_DIR_NOT_FOUND if the given real path doesn't exist.\n\t * (the default behavior is to create all real directories in the path)\n\t **/\n\tVFS_MOUNT_MUST_EXIST = 4,\n\n\t/**\n\t * keep the files named \"*.DELETED\" visible in the VFS directories.\n\t * the standard behavior of hiding the file with the same name minus the\n\t * \".DELETED\" suffix will still apply.\n\t * (the default behavior is to hide both the suffixed and unsuffixed files)\n\t **/\n\tVFS_MOUNT_KEEP_DELETED = 8,\n\n\t/**\n\t * mark a directory replaceable, so that when writing a file to this path\n\t * new real directories will be created instead of reusing already existing\n\t * ones mounted at a subpath of the VFS path.\n\t * (the default behaviour is to write to the real directory associated\n\t * with the VFS directory that was last mounted to this path (or subpath))\n\t **/\n\tVFS_MOUNT_REPLACEABLE = 16\n};\n\n// (member functions are thread-safe after the instance has been\n// constructed - each acquires a pthread mutex.)\nstruct IVFS\n{\n\tvirtual ~IVFS() {}\n\n\t/**\n\t * mount a directory into the VFS.\n\t *\n\t * @param mountPoint (will be created if it does not already exist)\n\t * @param path real directory path\n\t * @param flags\n\t * @param priority\n\t * @return Status.\n\t *\n\t * if files are encountered that already exist in the VFS (sub)directories,\n\t * the most recent / highest priority/precedence version is preferred.\n\t *\n\t * if files with archive extensions are seen, their contents are added\n\t * as well.\n\t **/\n\tvirtual Status Mount(const VfsPath& mountPoint, const OsPath& path, size_t flags = 0, size_t priority = 0) = 0;\n\n\t/**\n\t * Retrieve information about a file (similar to POSIX stat).\n\t *\n\t * @param pathname\n\t * @param pfileInfo receives information about the file. Passing NULL\n\t *\t\t  suppresses warnings if the file doesn't exist.\n\t * \n\t * @return Status.\n\t **/\n\tvirtual Status GetFileInfo(const VfsPath& pathname, CFileInfo* pfileInfo) const = 0;\n\n\t/**\n\t * Retrieve mount priority for a file.\n\t *\n\t * @param pathname\n\t * @param ppriority receives priority value, if the file can be found.\n\t *\n\t * @return Status.\n\t **/\n\tvirtual Status GetFilePriority(const VfsPath& pathname, size_t* ppriority) const = 0;\n\n\t/**\n\t * Retrieve lists of all files and subdirectories in a directory.\n\t *\n\t * @return Status.\n\t *\n\t * Rationale:\n\t * - this interface avoids having to lock the directory while an\n\t *   iterator is extant.\n\t * - we cannot efficiently provide routines for returning files and\n\t *   subdirectories separately due to the underlying POSIX interface.\n\t **/\n\tvirtual Status GetDirectoryEntries(const VfsPath& path, CFileInfos* fileInfos, DirectoryNames* subdirectoryNames) const = 0;\n\n\t/**\n\t * Create a file with the given contents.\n\t * @param pathname\n\t * @param fileContents\n\t * @param size [bytes] of the contents, will match that of the file.\n\t * @return Status.\n\t *\n\t * rationale: disallowing partial writes simplifies file cache coherency\n\t * (we need only invalidate cached data when closing a newly written file).\n\t **/\n\tvirtual Status CreateFile(const VfsPath& pathname, const shared_ptr<u8>& fileContents, size_t size) = 0;\n\n\t/**\n\t * Replace a file with the given contents.\n\t * \n\t * @see CreateFile\n\t * \n\t * Used to replace a file if it is already present (even if the file is not\n\t * in the attached vfs directory). Calls CreateFile if the file doesn't yet\n\t * exist.\n\t  **/\n\tvirtual Status ReplaceFile(const VfsPath& pathname, const shared_ptr<u8>& fileContents, size_t size) = 0;\n\n\t/**\n\t * Read an entire file into memory.\n\t *\n\t * @param pathname\n\t * @param fileContents receives a smart pointer to the contents.\n\t *\t\t  CAVEAT: this will be taken from the file cache if the VFS was\n\t * \t\t  created with cacheSize != 0 and size < cacheSize. There is no\n\t * \t\t  provision for Copy-on-Write, which means that such buffers\n\t * \t\t  must not be modified (this is enforced via mprotect).\n\t * @param size receives the size [bytes] of the file contents.\n\t * @return Status.\n\t **/\n\tvirtual Status LoadFile(const VfsPath& pathname, shared_ptr<u8>& fileContents, size_t& size) = 0;\n\n\t/**\n\t * @return a string representation of all files and directories.\n\t **/\n\tvirtual std::wstring TextRepresentation() const = 0;\n\n\t/**\n\t * retrieve the real (POSIX) pathname underlying a VFS file.\n\t *\n\t * this is useful for passing paths to external libraries.\n\t **/\n\tvirtual Status GetRealPath(const VfsPath& pathname, OsPath& realPathname) = 0;\n\n\t/**\n\t * retrieve the real (POSIX) pathname underlying a VFS directory.\n\t *\n\t * this is useful for passing paths to external libraries.\n\t **/\n\tvirtual Status GetDirectoryRealPath(const VfsPath& pathname, OsPath& realPathname) = 0;\n\n\t/**\n\t * retrieve the VFS pathname that corresponds to a real file.\n\t *\n\t * this is useful for reacting to file change notifications.\n\t *\n\t * the current implementation requires time proportional to the\n\t * number of directories; this could be accelerated by only checking\n\t * directories below a mount point with a matching real path.\n\t **/\n\tvirtual Status GetVirtualPath(const OsPath& realPathname, VfsPath& pathname) = 0;\n\n\t/**\n\t * remove file from the virtual directory listing and evict its\n\t * data from the cache.\n\t **/\n\tvirtual Status RemoveFile(const VfsPath& pathname) = 0;\n\n\t/**\n\t * request the directory be re-populated when it is next accessed.\n\t * useful for synchronizing with the underlying filesystem after\n\t * files have been created or their metadata changed.\n\t **/\n\tvirtual Status RepopulateDirectory(const VfsPath& path) = 0;\n\n\t/**\n\t * empty the contents of the filesystem.\n\t * this is typically only necessary when changing the set of\n\t * mounted directories, e.g. when switching mods.\n\t * NB: open files are not affected.\n\t **/\n\tvirtual void Clear() = 0;\n};\n\ntypedef shared_ptr<IVFS> PIVFS;\n\n/**\n * create an instance of a Virtual File System.\n *\n * @param cacheSize size [bytes] of memory to reserve for a file cache,\n * or zero to disable it. if small enough to fit, file contents are\n * stored here until no references remain and they are evicted.\n *\n * note: there is no limitation to a single instance, it may make sense\n * to create and destroy VFS instances during each unit test.\n **/\nLIB_API PIVFS CreateVfs(size_t cacheSize);\n\n#endif\t// #ifndef INCLUDED_VFS\n"
  },
  {
    "path": "fpsgame/gui/file/vfs/vfs_lookup.cpp",
    "content": "/* Copyright (c) 2013 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * look up directories/files by traversing path components.\n */\n\n#include \"precompiled.h\"\n#include \"lib/file/vfs/vfs_lookup.h\"\n\n#include \"lib/external_libraries/suppress_boost_warnings.h\"\n\n#include \"lib/sysdep/filesystem.h\"\n#include \"lib/file/file.h\"\n#include \"lib/file/vfs/vfs.h\"\t// error codes\n#include \"lib/file/vfs/vfs_tree.h\"\n#include \"lib/file/vfs/vfs_populate.h\"\n\n#include \"lib/timer.h\"\n\n\nstatic Status CreateDirectory(const OsPath& path)\n{\n\t{\n\t\tconst mode_t mode = S_IRWXU; // 0700 as prescribed by XDG basedir\n\t\tconst int ret = wmkdir(path, mode);\n\t\tif(ret == 0)\t// success\n\t\t\treturn INFO::OK;\n\t}\n\n\t// failed because the directory already exists. this can happen\n\t// when the first vfs_Lookup has addMissingDirectories &&\n\t// !createMissingDirectories, and the directory is subsequently\n\t// created. return 'success' to attach the existing directory..\n\tif(errno == EEXIST)\n\t{\n\t\t// but first ensure it's really a directory (otherwise, a\n\t\t// file is \"in the way\" and needs to be deleted)\n\t\tstruct stat s;\n\t\tconst int ret = wstat(path, &s);\n\t\tENSURE(ret == 0);\t// (wmkdir said it existed)\n\t\tENSURE(S_ISDIR(s.st_mode));\n\t\treturn INFO::OK;\n\t}\n\n\tif (errno == EACCES)\n\t\treturn ERR::FILE_ACCESS;\n\n\t// unexpected failure\n\tdebug_printf(\"wmkdir failed with errno=%d\\n\", errno);\n\tDEBUG_WARN_ERR(ERR::LOGIC);\n\tWARN_RETURN(StatusFromErrno());\n}\n\n\nStatus vfs_Lookup(const VfsPath& pathname, VfsDirectory* startDirectory, VfsDirectory*& directory, VfsFile** pfile, size_t flags)\n{\n\t// extract and validate flags (ensure no unknown bits are set)\n\tconst bool addMissingDirectories    = (flags & VFS_LOOKUP_ADD) != 0;\n\tconst bool createMissingDirectories = (flags & VFS_LOOKUP_CREATE) != 0;\n\tconst bool skipPopulate = (flags & VFS_LOOKUP_SKIP_POPULATE) != 0;\n\tconst bool createAlways = (flags & VFS_LOOKUP_CREATE_ALWAYS) != 0;\n\tENSURE((flags & ~(VFS_LOOKUP_ADD|VFS_LOOKUP_CREATE|VFS_LOOKUP_SKIP_POPULATE|VFS_LOOKUP_CREATE_ALWAYS)) == 0);\n\n\tdirectory = startDirectory;\n\tif(pfile)\n\t\t*pfile = 0;\n\n\tif(!skipPopulate)\n\t\tRETURN_STATUS_IF_ERR(vfs_Populate(directory));\n\n\t// early-out for pathname == \"\" when mounting into VFS root\n\tif(pathname.empty())\t// (prevent iterator error in loop end condition)\n\t{\n\t\tif(pfile)\t// preserve a guarantee that if pfile then we either return an error or set *pfile\n\t\t\treturn ERR::VFS_FILE_NOT_FOUND;\n\t\telse\n\t\t\treturn INFO::OK;\n\t}\n\n\t// for each directory component:\n\tsize_t pos = 0;\t// (needed outside of loop)\n\tfor(;;)\n\t{\n\t\tconst size_t nextSlash = pathname.string().find_first_of('/', pos);\n\t\tif(nextSlash == VfsPath::String::npos)\n\t\t\tbreak;\n\t\tconst VfsPath subdirectoryName = pathname.string().substr(pos, nextSlash-pos);\n\t\tpos = nextSlash+1;\n\n\t\tVfsDirectory* subdirectory = directory->GetSubdirectory(subdirectoryName);\n\t\tif(!subdirectory)\n\t\t{\n\t\t\tif(addMissingDirectories)\n\t\t\t\tsubdirectory = directory->AddSubdirectory(subdirectoryName);\n\t\t\telse\n\t\t\t\treturn ERR::VFS_DIR_NOT_FOUND;\t// NOWARN\n\t\t}\n\n\t\tif(createMissingDirectories && (!subdirectory->AssociatedDirectory()\n\t\t     || (createAlways && (subdirectory->AssociatedDirectory()->Flags() & VFS_MOUNT_REPLACEABLE) != 0)))\n\t\t{\n\t\t\tOsPath currentPath;\n\t\t\tif(directory->AssociatedDirectory())\t// (is NULL when mounting into root)\n\t\t\t\tcurrentPath = directory->AssociatedDirectory()->Path();\n\t\t\tcurrentPath = currentPath / subdirectoryName;\n\n\t\t\tRETURN_STATUS_IF_ERR(CreateDirectory(currentPath));\n\n\t\t\tPRealDirectory realDirectory(new RealDirectory(currentPath, 0, 0));\n\t\t\tRETURN_STATUS_IF_ERR(vfs_Attach(subdirectory, realDirectory));\n\t\t}\n\n\t\tif(!skipPopulate)\n\t\t\tRETURN_STATUS_IF_ERR(vfs_Populate(subdirectory));\n\n\t\tdirectory = subdirectory;\n\t}\n\n\tif(pfile)\n\t{\n\t\tENSURE(!pathname.IsDirectory());\n\t\tconst VfsPath filename = pathname.string().substr(pos);\n\t\t*pfile = directory->GetFile(filename);\n\t\tif(!*pfile)\n\t\t\treturn ERR::VFS_FILE_NOT_FOUND;\t// NOWARN\n\t}\n\n\treturn INFO::OK;\n}\n"
  },
  {
    "path": "fpsgame/gui/file/vfs/vfs_lookup.h",
    "content": "/* Copyright (c) 2013 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * look up directories/files by traversing path components.\n */\n\n#ifndef INCLUDED_VFS_LOOKUP\n#define INCLUDED_VFS_LOOKUP\n\n#include \"lib/file/vfs/vfs_path.h\"\n\nclass VfsFile;\nclass VfsDirectory;\n\n// note: VfsDirectory pointers are non-const because they may be\n// populated during the lookup.\n\nenum VfsLookupFlags\n{\n\t// add (if they do not already exist) subdirectory components\n\t// encountered in the path[name].\n\tVFS_LOOKUP_ADD = 1,\n\n\t// if VFS directories encountered are not already associated\n\t// with a real directory, do so (creating the directories\n\t// if they do not already exist).\n\tVFS_LOOKUP_CREATE = 2,\n\n\t// don't populate the directories encountered. this makes sense\n\t// when adding files from an archive, which would otherwise\n\t// cause nearly every directory to be populated.\n\tVFS_LOOKUP_SKIP_POPULATE = 4,\n\n\t// even create directories if they are already present, this is\n\t// useful to write new files to the directory that was attached\n\t// last, if the directory wasn't mounted with VFS_MOUNT_REPLACEABLE\n\tVFS_LOOKUP_CREATE_ALWAYS = 8\n};\n\n/**\n * Resolve a pathname.\n *\n * @param pathname\n * @param startDirectory VfsStartDirectory.\n * @param directory is set to the last directory component that is encountered.\n * @param pfile File is set to 0 if there is no name component, otherwise the\n *\t\t  corresponding file.\n * @param flags @see VfsLookupFlags.\n * @return Status (INFO::OK if all components in pathname exist).\n *\n * to allow noiseless file-existence queries, this does not raise warnings.\n **/\nextern Status vfs_Lookup(const VfsPath& pathname, VfsDirectory* startDirectory, VfsDirectory*& directory, VfsFile** pfile, size_t flags = 0);\n\n#endif\t// #ifndef INCLUDED_VFS_LOOKUP\n"
  },
  {
    "path": "fpsgame/gui/file/vfs/vfs_path.cpp",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#include \"precompiled.h\"\n#include \"lib/file/vfs/vfs_path.h\"\n"
  },
  {
    "path": "fpsgame/gui/file/vfs/vfs_path.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#ifndef INCLUDED_VFS_PATH\n#define INCLUDED_VFS_PATH\n\n#include \"lib/path.h\"\n\n/**\n * VFS path of the form \"(dir/)*file?\"\n *\n * in other words: the root directory is \"\" and paths are separated by '/'.\n * a trailing slash is allowed for directory names.\n * rationale: it is important to avoid a leading slash because that might be\n * confused with an absolute POSIX path.\n *\n * there is no restriction on path length; when dimensioning character\n * arrays, prefer PATH_MAX.\n **/\n\ntypedef Path VfsPath;\n\ntypedef std::vector<VfsPath> VfsPaths;\n\n#endif\t//\t#ifndef INCLUDED_VFS_PATH\n"
  },
  {
    "path": "fpsgame/gui/file/vfs/vfs_populate.cpp",
    "content": "/* Copyright (c) 2013 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * populate VFS directories with files\n */\n\n#include \"precompiled.h\"\n#include \"lib/file/vfs/vfs_populate.h\"\n\n#include \"lib/file/archive/archive_zip.h\"\n#include \"lib/file/vfs/vfs_tree.h\"\n#include \"lib/file/vfs/vfs_lookup.h\"\n#include \"lib/file/vfs/vfs.h\"\t// error codes\n\n\nstruct CompareFileInfoByName\n{\n\tbool operator()(const CFileInfo& a, const CFileInfo& b)\n\t{\n\t\treturn a.Name() < b.Name();\n\t}\n};\n\n// helper class that allows breaking up the logic into sub-functions without\n// always having to pass directory/realDirectory as parameters.\nclass PopulateHelper\n{\n\tNONCOPYABLE(PopulateHelper);\npublic:\n\tPopulateHelper(VfsDirectory* directory, const PRealDirectory& realDirectory)\n\t\t: m_directory(directory), m_realDirectory(realDirectory)\n\t{\n\t}\n\n\tStatus AddEntries() const\n\t{\n\t\tCFileInfos files; files.reserve(500);\n\t\tDirectoryNames subdirectoryNames; subdirectoryNames.reserve(50);\n\t\tRETURN_STATUS_IF_ERR(GetDirectoryEntries(m_realDirectory->Path(), &files, &subdirectoryNames));\n\n\t\t// to support .DELETED files inside archives safely, we need to load\n\t\t// archives and loose files in a deterministic order in case they add\n\t\t// and then delete the same file (or vice versa, depending on loading\n\t\t// order). GetDirectoryEntries has undefined order so sort its output\n\t\tstd::sort(files.begin(), files.end(), CompareFileInfoByName());\n\t\tstd::sort(subdirectoryNames.begin(), subdirectoryNames.end());\n\n\t\tRETURN_STATUS_IF_ERR(AddFiles(files));\n\t\tAddSubdirectories(subdirectoryNames);\n\n\t\treturn INFO::OK;\n\t}\n\nprivate:\n\tvoid AddFile(const CFileInfo& fileInfo) const\n\t{\n\t\tconst VfsPath name = fileInfo.Name();\n\t\tif(name.Extension() == L\".DELETED\")\n\t\t{\n\t\t\tm_directory->RemoveFile(name.Basename());\n\t\t\tif(!(m_realDirectory->Flags() & VFS_MOUNT_KEEP_DELETED))\n\t\t\t\treturn;\n\t\t}\n\n\t\tconst VfsFile file(name, (size_t)fileInfo.Size(), fileInfo.MTime(), m_realDirectory->Priority(), m_realDirectory);\n\t\tm_directory->AddFile(file);\n\t}\n\n\tstatic void AddArchiveFile(const VfsPath& pathname, const CFileInfo& fileInfo, PIArchiveFile archiveFile, uintptr_t cbData)\n\t{\n\t\tPopulateHelper* this_ = (PopulateHelper*)cbData;\n\n\t\t// (we have to create missing subdirectoryNames because archivers\n\t\t// don't always place directory entries before their files)\n\t\tconst size_t flags = VFS_LOOKUP_ADD|VFS_LOOKUP_SKIP_POPULATE;\n\t\tVfsDirectory* directory;\n\t\tWARN_IF_ERR(vfs_Lookup(pathname, this_->m_directory, directory, 0, flags));\n\n\t\tconst VfsPath name = fileInfo.Name();\n\t\tif(name.Extension() == L\".DELETED\")\n\t\t{\n\t\t\tdirectory->RemoveFile(name.Basename());\n\t\t\tif(!(this_->m_realDirectory->Flags() & VFS_MOUNT_KEEP_DELETED))\n\t\t\t\treturn;\n\t\t}\n\n\t\tconst VfsFile file(name, (size_t)fileInfo.Size(), fileInfo.MTime(), this_->m_realDirectory->Priority(), archiveFile);\n\t\tdirectory->AddFile(file);\n\t}\n\n\tStatus AddFiles(const CFileInfos& files) const\n\t{\n\t\tconst OsPath path(m_realDirectory->Path());\n\n\t\tfor(size_t i = 0; i < files.size(); i++)\n\t\t{\n\t\t\tconst OsPath pathname = path / files[i].Name();\n\t\t\tif(pathname.Extension() == L\".zip\")\n\t\t\t{\n\t\t\t\tPIArchiveReader archiveReader = CreateArchiveReader_Zip(pathname);\n\t\t\t\t// archiveReader == nullptr if file could not be opened (e.g. because\n\t\t\t\t// archive is currently open in another program)\n\t\t\t\tif(archiveReader)\n\t\t\t\t\tRETURN_STATUS_IF_ERR(archiveReader->ReadEntries(AddArchiveFile, (uintptr_t)this));\n\t\t\t}\n\t\t\telse\t// regular (non-archive) file\n\t\t\t\tAddFile(files[i]);\n\t\t}\n\n\t\treturn INFO::OK;\n\t}\n\n\tvoid AddSubdirectories(const DirectoryNames& subdirectoryNames) const\n\t{\n\t\tfor(size_t i = 0; i < subdirectoryNames.size(); i++)\n\t\t{\n\t\t\t// skip version control directories - this avoids cluttering the\n\t\t\t// VFS with hundreds of irrelevant files.\n\t\t\tif(subdirectoryNames[i] == L\".svn\" || subdirectoryNames[i] == L\".git\")\n\t\t\t\tcontinue;\n\n\t\t\tVfsDirectory* subdirectory = m_directory->AddSubdirectory(subdirectoryNames[i]);\n\t\t\tPRealDirectory realDirectory = CreateRealSubdirectory(m_realDirectory, subdirectoryNames[i]);\n\t\t\tvfs_Attach(subdirectory, realDirectory);\n\t\t}\n\t}\n\n\tVfsDirectory* const m_directory;\n\tPRealDirectory m_realDirectory;\n};\n\n\nStatus vfs_Populate(VfsDirectory* directory)\n{\n\tif(!directory->ShouldPopulate())\n\t\treturn INFO::OK;\n\n\tconst PRealDirectory& realDirectory = directory->AssociatedDirectory();\n\n\tif(realDirectory->Flags() & VFS_MOUNT_WATCH)\n\t\trealDirectory->Watch();\n\n\tPopulateHelper helper(directory, realDirectory);\n\tRETURN_STATUS_IF_ERR(helper.AddEntries());\n\n\treturn INFO::OK;\n}\n\n\nStatus vfs_Attach(VfsDirectory* directory, const PRealDirectory& realDirectory)\n{\n\tRETURN_STATUS_IF_ERR(vfs_Populate(directory));\n\tdirectory->SetAssociatedDirectory(realDirectory);\n\treturn INFO::OK;\n}\n"
  },
  {
    "path": "fpsgame/gui/file/vfs/vfs_populate.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * populate VFS directories with files\n */\n\n#ifndef INCLUDED_VFS_POPULATE\n#define INCLUDED_VFS_POPULATE\n\n#include \"lib/file/common/real_directory.h\"\n\nclass VfsDirectory;\n\n/**\n * attach a real directory to a VFS directory.\n *\n * when the VFS directory is accessed, it will first be populated from\n * the real directory. (this delays the impact of mounting a large\n * directory, distributing the cost from startup to the first accesses\n * to each subdirectory.)\n *\n * note: the most recently attached real directory will be used when\n * creating files in the VFS directory.\n **/\nextern Status vfs_Attach(VfsDirectory* directory, const PRealDirectory& realDirectory);\n\n/**\n * populate the directory from the attached real directory.\n *\n * adds each real file and subdirectory entry to the VFS directory.\n * the full contents of any archives in the real directory are also added.\n *\n * has no effect if no directory has been attached since the last populate.\n **/\nextern Status vfs_Populate(VfsDirectory* directory);\n\n#endif\t// #ifndef INCLUDED_VFS_POPULATE\n"
  },
  {
    "path": "fpsgame/gui/file/vfs/vfs_tree.cpp",
    "content": "/* Copyright (c) 2013 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * 'tree' of VFS directories and files\n */\n\n#include \"precompiled.h\"\n#include \"lib/file/vfs/vfs_tree.h\"\n\n#include <cstdio>\n\n#include \"lib/file/common/file_stats.h\"\n#include \"lib/sysdep/cpu.h\"\n\n\n//-----------------------------------------------------------------------------\n\nVfsFile::VfsFile(const VfsPath& name, size_t size, time_t mtime, size_t priority, const PIFileLoader& loader)\n\t: m_name(name), m_size(size), m_mtime(mtime), m_priority(priority), m_loader(loader)\n{\n}\n\n\n\n//-----------------------------------------------------------------------------\n\nVfsDirectory::VfsDirectory()\n\t: m_shouldPopulate(0)\n{\n}\n\n\nstatic bool ShouldReplaceWith(const VfsFile& previousFile, const VfsFile& newFile)\n{\n\t// 1) priority (override mods)\n\tif(newFile.Priority() < previousFile.Priority())\n\t\treturn false;\n\tif(newFile.Priority() > previousFile.Priority())\n\t\treturn true;\n\n\t// 2) timestamp\n\t{\n\t\tconst double howMuchNewer = difftime(newFile.MTime(), previousFile.MTime());\n\t\tconst double threshold = 2.0;\t// FAT timestamp resolution [seconds]\n\t\tif(howMuchNewer > threshold)\t// newer\n\t\t\treturn true;\n\t\tif(howMuchNewer < -threshold)\t// older\n\t\t\treturn false;\n\t\t// else: \"equal\" (tolerating small differences due to FAT's low\n\t\t// mtime resolution)\n\t}\n\n\t// 3) precedence (efficiency of file provider)\n\tif(newFile.Loader()->Precedence() < previousFile.Loader()->Precedence())\n\t\treturn false;\n\n\treturn true;\n}\n\n\nVfsFile* VfsDirectory::AddFile(const VfsFile& file)\n{\n\tstd::pair<VfsPath, VfsFile> value = std::make_pair(file.Name(), file);\n\tstd::pair<VfsFiles::iterator, bool> ret = m_files.insert(value);\n\tif(!ret.second)\t// already existed\n\t{\n\t\tVfsFile& previousFile = ret.first->second;\n\t\tconst VfsFile& newFile = value.second;\n\t\tif(ShouldReplaceWith(previousFile, newFile))\n\t\t\tpreviousFile = newFile;\n\t}\n\telse\n\t{\n\t\tstats_vfs_file_add(file.Size());\n\t}\n\n\treturn &(*ret.first).second;\n}\n\n\n// rationale: passing in a pre-constructed VfsDirectory and copying that into\n// our map would be slower and less convenient for the caller.\nVfsDirectory* VfsDirectory::AddSubdirectory(const VfsPath& name)\n{\n\tstd::pair<VfsPath, VfsDirectory> value = std::make_pair(name.string(), VfsDirectory());\n\tstd::pair<VfsSubdirectories::iterator, bool> ret = m_subdirectories.insert(value);\n\treturn &(*ret.first).second;\n}\n\n\nvoid VfsDirectory::RemoveFile(const VfsPath& name)\n{\n\tm_files.erase(name.string());\n}\n\n\nVfsFile* VfsDirectory::GetFile(const VfsPath& name)\n{\n\tVfsFiles::iterator it = m_files.find(name.string());\n\tif(it == m_files.end())\n\t\treturn 0;\n\treturn &it->second;\n}\n\n\nVfsDirectory* VfsDirectory::GetSubdirectory(const VfsPath& name)\n{\n\tVfsSubdirectories::iterator it = m_subdirectories.find(name.string());\n\tif(it == m_subdirectories.end())\n\t\treturn 0;\n\treturn &it->second;\n}\n\n\nvoid VfsDirectory::SetAssociatedDirectory(const PRealDirectory& realDirectory)\n{\n\tif(!cpu_CAS(&m_shouldPopulate, 0, 1))\n\t\tDEBUG_WARN_ERR(ERR::LOGIC);\t// caller didn't check ShouldPopulate\n\tm_realDirectory = realDirectory;\n}\n\n\nbool VfsDirectory::ShouldPopulate()\n{\n\treturn cpu_CAS(&m_shouldPopulate, 1, 0);\t// test and reset\n}\n\n\nvoid VfsDirectory::RequestRepopulate()\n{\n\tm_shouldPopulate = 1;\n}\n\n\nvoid VfsDirectory::Clear()\n{\n\tm_files.clear();\n\tm_subdirectories.clear();\n\tm_realDirectory.reset();\n\tm_shouldPopulate = 0;\n}\n\n\n//-----------------------------------------------------------------------------\n\nstd::wstring FileDescription(const VfsFile& file)\n{\n\twchar_t timestamp[25];\n\tconst time_t mtime = file.MTime();\n\twcsftime(timestamp, ARRAY_SIZE(timestamp), L\"%a %b %d %H:%M:%S %Y\", localtime(&mtime));\n\n\twchar_t buf[200];\n\tswprintf_s(buf, ARRAY_SIZE(buf), L\"(%c; %6lu; %ls) %ls\", file.Loader()->LocationCode(), (unsigned long)file.Size(), timestamp, file.Name().string().c_str());\n\treturn buf;\n}\n\n\nstd::wstring FileDescriptions(const VfsDirectory& directory, size_t indentLevel)\n{\n\tVfsDirectory::VfsFiles files = directory.Files();\n\n\tstd::wstring descriptions;\n\tdescriptions.reserve(100*files.size());\n\n\tconst std::wstring indentation(4*indentLevel, ' ');\n\tfor(VfsDirectory::VfsFiles::const_iterator it = files.begin(); it != files.end(); ++it)\n\t{\n\t\tconst VfsFile& file = it->second;\n\t\tdescriptions += indentation;\n\t\tdescriptions += FileDescription(file);\n\t\tdescriptions += L\"\\n\";\n\t}\n\n\treturn descriptions;\n}\n\n\nvoid DirectoryDescriptionR(std::wstring& descriptions, const VfsDirectory& directory, size_t indentLevel)\n{\n\tconst std::wstring indentation(4*indentLevel, ' ');\n\n\tconst VfsDirectory::VfsSubdirectories& subdirectories = directory.Subdirectories();\n\tfor(VfsDirectory::VfsSubdirectories::const_iterator it = subdirectories.begin(); it != subdirectories.end(); ++it)\n\t{\n\t\tconst VfsPath& name = it->first;\n\t\tconst VfsDirectory& subdirectory = it->second;\n\t\tdescriptions += indentation;\n\t\tdescriptions += std::wstring(L\"[\") + name.string() + L\"]\\n\";\n\t\tdescriptions += FileDescriptions(subdirectory, indentLevel+1);\n\n\t\tDirectoryDescriptionR(descriptions, subdirectory, indentLevel+1);\n\t}\n}\n"
  },
  {
    "path": "fpsgame/gui/file/vfs/vfs_tree.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * 'tree' of VFS directories and files\n */\n\n#ifndef INCLUDED_VFS_TREE\n#define INCLUDED_VFS_TREE\n\n#include <map>\n\n#include \"lib/file/file_system.h\"\t// CFileInfo\n#include \"lib/file/common/file_loader.h\"\t// PIFileLoader\n#include \"lib/file/common/real_directory.h\"\t// PRealDirectory\n#include \"lib/file/vfs/vfs_path.h\"\n\nclass VfsFile\n{\npublic:\n\tVfsFile(const VfsPath& name, size_t size, time_t mtime, size_t priority, const PIFileLoader& provider);\n\n\tconst VfsPath& Name() const\n\t{\n\t\treturn m_name;\n\t}\n\n\tsize_t Size() const\n\t{\n\t\treturn m_size;\n\t}\n\n\ttime_t MTime() const\n\t{\n\t\treturn m_mtime;\n\t}\n\n\tsize_t Priority() const\n\t{\n\t\treturn m_priority;\n\t}\n\n\tconst PIFileLoader& Loader() const\n\t{\n\t\treturn m_loader;\n\t}\n\nprivate:\n\tVfsPath m_name;\n\tsize_t m_size;\n\ttime_t m_mtime;\n\n\tsize_t m_priority;\n\n\tPIFileLoader m_loader;\n};\n\n\nclass VfsDirectory\n{\npublic:\n\ttypedef std::map<VfsPath, VfsFile> VfsFiles;\n\ttypedef std::map<VfsPath, VfsDirectory> VfsSubdirectories;\n\n\tVfsDirectory();\n\n\t/**\n\t * @return address of existing or newly inserted file.\n\t **/\n\tVfsFile* AddFile(const VfsFile& file);\n\n\t/**\n\t * @return address of existing or newly inserted subdirectory.\n\t **/\n\tVfsDirectory* AddSubdirectory(const VfsPath& name);\n\n\t/**\n\t * remove the given file from the virtual directory (no effect on\n\t * the physical file). no effect if the file does not exist.\n\t **/\n\tvoid RemoveFile(const VfsPath& name);\n\n\t/**\n\t * @return file with the given name.\n\t * (note: non-const to allow changes to the file)\n\t **/\n\tVfsFile* GetFile(const VfsPath& name);\n\n\t/**\n\t * @return subdirectory with the given name.\n\t * (note: non-const to allow changes to the subdirectory)\n\t **/\n\tVfsDirectory* GetSubdirectory(const VfsPath& name);\n\n\t// note: exposing only iterators wouldn't enable callers to reserve space.\n\n\tconst VfsFiles& Files() const\n\t{\n\t\treturn m_files;\n\t}\n\n\tconst VfsSubdirectories& Subdirectories() const\n\t{\n\t\treturn m_subdirectories;\n\t}\n\n\t/**\n\t * side effect: the next ShouldPopulate() will return true.\n\t **/\n\tvoid SetAssociatedDirectory(const PRealDirectory& realDirectory);\n\n\tconst PRealDirectory& AssociatedDirectory() const\n\t{\n\t\treturn m_realDirectory;\n\t}\n\n\t/**\n\t * @return whether this directory should be populated from its\n\t * AssociatedDirectory(). note that calling this is a promise to\n\t * do so if true is returned -- the flag is reset immediately.\n\t **/\n\tbool ShouldPopulate();\n\n\t/**\n\t * ensure the next ShouldPopulate returns true.\n\t **/\n\tvoid RequestRepopulate();\n\n\t/**\n\t * empty file and subdirectory lists (e.g. when rebuilding VFS).\n\t * CAUTION: this invalidates all previously returned pointers.\n\t **/\n\tvoid Clear();\n\nprivate:\n\tVfsFiles m_files;\n\tVfsSubdirectories m_subdirectories;\n\n\tPRealDirectory m_realDirectory;\n\tvolatile intptr_t m_shouldPopulate;\t// (cpu_CAS can't be used on bool)\n};\n\n\n/**\n * @return a string containing file attributes (location, size, timestamp) and name.\n **/\nextern std::wstring FileDescription(const VfsFile& file);\n\n/**\n * @return a string holding each files' description (one per line).\n **/\nextern std::wstring FileDescriptions(const VfsDirectory& directory, size_t indentLevel);\n\n/**\n * append each directory's files' description to the given string.\n **/\nvoid DirectoryDescriptionR(std::wstring& descriptions, const VfsDirectory& directory, size_t indentLevel);\n\n#endif\t// #ifndef INCLUDED_VFS_TREE\n"
  },
  {
    "path": "fpsgame/gui/file/vfs/vfs_util.cpp",
    "content": "/* Copyright (c) 2015 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * helper functions for directory access\n */\n\n#include \"precompiled.h\"\n\n#include \"lib/file/vfs/vfs_util.h\"\n\n#include <cstdio>\n#include <cstring>\n#include <queue>\n\n#include \"lib/regex.h\"\n#include \"lib/sysdep/filesystem.h\"\n\n\nnamespace vfs {\n\nStatus GetPathnames(const PIVFS& fs, const VfsPath& path, const wchar_t* filter, VfsPaths& pathnames)\n{\n\tstd::vector<CFileInfo> files;\n\tRETURN_STATUS_IF_ERR(fs->GetDirectoryEntries(path, &files, 0));\n\n\tpathnames.clear();\n\tpathnames.reserve(files.size());\n\n\tfor(size_t i = 0; i < files.size(); i++)\n\t{\n\t\tif(match_wildcard(files[i].Name().string().c_str(), filter))\n\t\t\tpathnames.push_back(path / files[i].Name());\n\t}\n\n\treturn INFO::OK;\n}\n\n\nStatus ForEachFile(const PIVFS& fs, const VfsPath& startPath, FileCallback cb, uintptr_t cbData, const wchar_t* pattern, size_t flags, DirCallback dircb, uintptr_t dircbData)\n{\n\t// (declare here to avoid reallocations)\n\tCFileInfos files;\n\tDirectoryNames subdirectoryNames;\n\n\t// (a FIFO queue is more efficient than recursion because it uses less\n\t// stack space and avoids seeks due to breadth-first traversal.)\n\tstd::queue<VfsPath> pendingDirectories;\n\tpendingDirectories.push(startPath/\"\");\n\twhile(!pendingDirectories.empty())\n\t{\n\t\tconst VfsPath& path = pendingDirectories.front();\n\n\t\tRETURN_STATUS_IF_ERR(fs->GetDirectoryEntries(path, &files, &subdirectoryNames));\n\n\t\tif(dircb)\n\t\t\tRETURN_STATUS_IF_ERR(dircb(path, dircbData));\n\n\t\tfor(size_t i = 0; i < files.size(); i++)\n\t\t{\n\t\t\tconst CFileInfo fileInfo = files[i];\n\t\t\tif(!match_wildcard(fileInfo.Name().string().c_str(), pattern))\n\t\t\t\tcontinue;\n\n\t\t\tconst VfsPath pathname(path / fileInfo.Name());\t// (CFileInfo only stores the name)\n\t\t\tRETURN_STATUS_IF_ERR(cb(pathname, fileInfo, cbData));\n\t\t}\n\n\t\tif(!(flags & DIR_RECURSIVE))\n\t\t\tbreak;\n\n\t\tfor(size_t i = 0; i < subdirectoryNames.size(); i++)\n\t\t\tpendingDirectories.push(path / subdirectoryNames[i]/\"\");\n\t\tpendingDirectories.pop();\n\t}\n\n\treturn INFO::OK;\n}\n\n\nvoid NextNumberedFilename(const PIVFS& fs, const VfsPath& pathnameFormat, size_t& nextNumber, VfsPath& nextPathname)\n{\n\t// (first call only:) scan directory and set nextNumber according to\n\t// highest matching filename found. this avoids filling \"holes\" in\n\t// the number series due to deleted files, which could be confusing.\n\t// example: add 1st and 2nd; [exit] delete 1st; [restart]\n\t// add 3rd -> without this measure it would get number 1, not 3. \n\tif(nextNumber == 0)\n\t{\n\t\tconst VfsPath nameFormat = pathnameFormat.Filename();\n\t\tconst VfsPath path = pathnameFormat.Parent()/\"\";\n\n\t\tsize_t maxNumber = 0;\n\t\tCFileInfos files;\n\t\tfs->GetDirectoryEntries(path, &files, 0);\n\t\tfor(size_t i = 0; i < files.size(); i++)\n\t\t{\n\t\t\tint number;\n\t\t\tif(swscanf_s(files[i].Name().string().c_str(), nameFormat.string().c_str(), &number) == 1)\n\t\t\t\tmaxNumber = std::max(size_t(number), maxNumber);\n\t\t}\n\n\t\tnextNumber = maxNumber+1;\n\t}\n\n\t// now increment number until that file doesn't yet exist.\n\t// this is fairly slow, but typically only happens once due\n\t// to scan loop above. (we still need to provide for looping since\n\t// someone may have added files in the meantime)\n\t// we don't bother with binary search - this isn't a bottleneck.\n\tdo\n\t{\n\t\twchar_t pathnameBuf[PATH_MAX];\n\t\tswprintf_s(pathnameBuf, ARRAY_SIZE(pathnameBuf), pathnameFormat.string().c_str(), nextNumber++);\n\t\tnextPathname = pathnameBuf;\n\t}\n\twhile(fs->GetFileInfo(nextPathname, 0) == INFO::OK);\n}\n\n}\t// namespace vfs\n"
  },
  {
    "path": "fpsgame/gui/file/vfs/vfs_util.h",
    "content": "/* Copyright (c) 2015 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * helper functions for directory access\n */\n\n#ifndef INCLUDED_VFS_UTIL\n#define INCLUDED_VFS_UTIL\n\n#include \"lib/os_path.h\"\n#include \"lib/file/vfs/vfs.h\"\n\nnamespace vfs {\n\nextern Status GetPathnames(const PIVFS& fs, const VfsPath& path, const wchar_t* filter, VfsPaths& pathnames);\n\n/**\n * called for files in a directory.\n *\n * @param pathname full pathname (since CFileInfo only gives the name).\n * @param fileInfo file information\n * @param cbData user-specified context\n * @return INFO::OK on success; any other value will immediately\n * be returned to the caller (no more calls will be forthcoming).\n *\n * CAVEAT: pathname and fileInfo are only valid until the function\n * returns!\n **/\ntypedef Status (*FileCallback)(const VfsPath& pathname, const CFileInfo& fileInfo, const uintptr_t cbData);\n\n/**\n * called for directories in a directory.\n *\n * @param pathname full pathname\n * @param cbData user-specified context\n * @return INFO::OK on success; any other value will immediately\n * be returned to the caller (no more calls will be forthcoming).\n *\n * CAVEAT: pathname only valid until the function returns!\n **/\ntypedef Status (*DirCallback)(const VfsPath& pathname, const uintptr_t cbData);\n\nenum DirFlags\n{\n\tDIR_RECURSIVE = 1\n};\n\n/**\n * call back for each file in a directory tree, and optionally each directory.\n *\n * @param fs\n * @param path\n * @param cb @ref FileCallback\n * @param cbData\n * @param pattern that file names must match. '*' and '&' wildcards\n *\t\t  are allowed. 0 matches everything.\n * @param flags @ref DirFlags\n * @param dircb @ref DirCallback\n * @param dircbData\n * @return Status\n **/\nextern Status ForEachFile(const PIVFS& fs, const VfsPath& path, FileCallback cb, uintptr_t cbData, const wchar_t* pattern = 0, size_t flags = 0, DirCallback dircb = NULL, uintptr_t dircbData = 0);\n\n\n/**\n * Determine the next available pathname with a given format.\n * This is useful when creating new files without overwriting the previous\n * ones (screenshots are a good example).\n *\n * @param fs\n * @param pathnameFormat Format string for the pathname; must contain one\n *\t\t  format specifier for an integer.\n *\t\t  Example: \"screenshots/screenshot%04d.png\"\n * @param nextNumber in: the first number to try; out: the next number.\n *\t\t  If 0, numbers corresponding to existing files are skipped.\n * @param nextPathname receives the output.\n **/\nextern void NextNumberedFilename(const PIVFS& fs, const VfsPath& pathnameFormat, size_t& nextNumber, VfsPath& nextPathname);\n\n}\t// namespace vfs\n\n#endif\t // #ifndef INCLUDED_VFS_UTIL\n"
  },
  {
    "path": "fpsgame/gui/fnv_hash.cpp",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * Fowler/Noll/Vo string hash\n */\n\n#include \"precompiled.h\"\n\n\n// FNV1-A hash - good for strings.\n// if len = 0 (default), treat buf as a C-string;\n// otherwise, hash <len> bytes of buf.\nu32 fnv_hash(const void* buf, size_t len)\n{\n\tu32 h = 0x811c9dc5u;\n\t// give distinct values for different length 0 buffers.\n\t// value taken from FNV; it has no special significance.\n\n\tconst u8* p = (const u8*)buf;\n\n\t// expected case: string\n\tif(!len)\n\t{\n\t\twhile(*p)\n\t\t{\n\t\t\th ^= *p++;\n\t\t\th *= 0x01000193u;\n\t\t}\n\t}\n\telse\n\t{\n\t\tsize_t bytes_left = len;\n\t\twhile(bytes_left != 0)\n\t\t{\n\t\t\th ^= *p++;\n\t\t\th *= 0x01000193u;\n\n\t\t\tbytes_left--;\n\t\t}\n\t}\n\n\treturn h;\n}\n\n\n// FNV1-A hash - good for strings.\n// if len = 0 (default), treat buf as a C-string;\n// otherwise, hash <len> bytes of buf.\nu64 fnv_hash64(const void* buf, size_t len)\n{\n\tu64 h = 0xCBF29CE484222325ull;\n\t// give distinct values for different length 0 buffers.\n\t// value taken from FNV; it has no special significance.\n\n\tconst u8* p = (const u8*)buf;\n\n\t// expected case: string\n\tif(!len)\n\t{\n\t\twhile(*p)\n\t\t{\n\t\t\th ^= *p++;\n\t\t\th *= 0x100000001B3ull;\n\t\t}\n\t}\n\telse\n\t{\n\t\tsize_t bytes_left = len;\n\t\twhile(bytes_left != 0)\n\t\t{\n\t\t\th ^= *p++;\n\t\t\th *= 0x100000001B3ull;\n\n\t\t\tbytes_left--;\n\t\t}\n\t}\n\n\treturn h;\n}\n\n\n// special version for strings: first converts to lowercase\n// (useful for comparing mixed-case filenames).\n// note: still need <len>, e.g. to support non-0-terminated strings\nu32 fnv_lc_hash(const char* str, size_t len)\n{\n\tu32 h = 0x811c9dc5u;\n\t// give distinct values for different length 0 buffers.\n\t// value taken from FNV; it has no special significance.\n\n\t// expected case: string\n\tif(!len)\n\t{\n\t\twhile(*str)\n\t\t{\n\t\t\th ^= tolower(*str++);\n\t\t\th *= 0x01000193u;\n\t\t}\n\t}\n\telse\n\t{\n\t\tsize_t bytes_left = len;\n\t\twhile(bytes_left != 0)\n\t\t{\n\t\t\th ^= tolower(*str++);\n\t\t\th *= 0x01000193u;\n\n\t\t\tbytes_left--;\n\t\t}\n\t}\n\n\treturn h;\n}\n"
  },
  {
    "path": "fpsgame/gui/fnv_hash.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * Fowler/Noll/Vo string hash\n */\n\n#ifndef INCLUDED_FNV_HASH\n#define INCLUDED_FNV_HASH\n\n/**\n * rationale: this algorithm was chosen because it delivers 'good' results\n * for string data and is relatively simple. other good alternatives exist;\n * see Ozan Yigit's hash roundup.\n **/\n\n/**\n * calculate FNV1-A hash.\n *\n * @param buf input buffer.\n * @param len if 0 (default), treat buf as a C-string; otherwise,\n * indicates how many bytes of buffer to hash.\n * @return hash result. note: results are distinct for buffers containing\n * differing amounts of zero bytes because the hash value is seeded.\n **/\nextern u32 fnv_hash(const void* buf, size_t len = 0);\n/// 64-bit version of fnv_hash.\nextern u64 fnv_hash64(const void* buf, size_t len = 0);\n\n/**\n * special version of fnv_hash for strings: first converts to lowercase\n * (useful for comparing mixed-case filenames)\n **/\nextern u32 fnv_lc_hash(const char* str, size_t len = 0);\n\n#endif\t// #ifndef INCLUDED_FNV_HASH\n"
  },
  {
    "path": "fpsgame/gui/frequency_filter.cpp",
    "content": "/* Copyright (c) 2013 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#include \"precompiled.h\"\n#include \"lib/frequency_filter.h\"\n\nstatic const double errorTolerance = 0.25;\nstatic const double sensitivity = 0.10;\nstatic const double sampleTime = 2.0; // seconds\n\n/**\n * variable-width window for frequency determination\n **/\nclass FrequencyEstimator\n{\n\tNONCOPYABLE(FrequencyEstimator);\npublic:\n\tFrequencyEstimator(double resolution)\n\t\t: m_minDeltaTime(4.0 * resolution)\t// chosen to reduce error but still yield rapid updates.\n\t\t, m_lastTime(0)\t// will be set on first call\n\t\t, m_numEvents(0)\n\t{\n\t\tENSURE(resolution > 0.0);\n\t}\n\n\tbool operator()(double time, double& frequency)\n\t{\n\t\tm_numEvents++;\n\n\t\tif(m_lastTime == 0.0)\n\t\t\tm_lastTime = time;\n\n\t\t// count # events until deltaTime is large enough\n\t\t// (reduces quantization errors if resolution is low)\n\t\tconst double deltaTime = time - m_lastTime;\n\t\tif(deltaTime <= m_minDeltaTime)\n\t\t\treturn false;\n\n\t\tfrequency = m_numEvents / deltaTime;\n\t\tm_numEvents = 0;\n\t\tm_lastTime = time;\n\t\treturn true;\t// success\n\t}\n\nprivate:\n\tconst double m_minDeltaTime;\n\tdouble m_lastTime;\n\tint m_numEvents;\n};\n\n\n/**\n * variable-gain IIR filter\n **/\nclass IirFilter\n{\npublic:\n\tIirFilter(double sensitivity, double initialValue)\n\t\t: m_sensitivity(sensitivity), m_prev(initialValue)\n\t{\n\t}\n\n\t// bias = 0: no change. > 0: increase (n-th root). < 0: decrease (^n)\n\tdouble operator()(double x, int bias)\n\t{\n\t\t// sensitivity to changes ([0,1]).\n\t\tconst double gain = pow(m_sensitivity, ComputeExponent(bias));\n\t\treturn m_prev = x*gain + m_prev*(1.0-gain);\n\t}\n\nprivate:\n\tstatic double ComputeExponent(int bias)\n\t{\n\t\tif(bias > 0)\n\t\t\treturn 1.0 / bias;\t// n-th root\n\t\telse if(bias == 0)\n\t\t\treturn 1.0;\t\t// no change\n\t\telse\n\t\t\treturn -bias;\t// power-of-n\n\t}\n\n\tdouble m_sensitivity;\n\tdouble m_prev;\n};\n\n\n/**\n * regulate IIR gain for rapid but smooth tracking of a function.\n * this is similar in principle to a PID controller but is tuned for\n * the special case of FPS values to simplify stabilizing the filter.\n **/\nclass Controller\n{\npublic:\n\tController(double initialValue)\n\t\t: m_timesOnSameSide(0)\n\t{\n\t\tstd::fill(m_history, m_history+m_historySize, initialValue);\n\t}\n\n\t// bias := exponential change to gain, (-inf, inf)\n\tint ComputeBias(double smoothedValue, double value)\n\t{\n\t\tif(WasOnSameSide(value))\t// (must be checked before updating history)\n\t\t\tm_timesOnSameSide++;\n\t\telse\n\t\t\tm_timesOnSameSide = 0;\n\n\t\t// update history\n\t\tstd::copy(m_history, m_history+m_historySize, m_history+1);\n\t\tm_history[m_historySize-1] = value;\n\n\t\t// dampen jitter\n\t\tif(Change(smoothedValue, value) < 0.04)\n\t\t\treturn -1;\n\n\t\t// dampen spikes/bounces.\n\t\tif(WasSpike())\n\t\t\treturn -2;\n\n\t\t// if the past few samples have been consistently above/below\n\t\t// average, the function is changing and we need to catch up.\n\t\t// (similar to I in a PID)\n\t\tif(m_timesOnSameSide >= 3)\n\t\t\treturn std::min(m_timesOnSameSide, 4);\n\n\t\t// suppress large jumps.\n\t\tif(Change(m_history[m_historySize-1], value) > 0.30)\n\t\t\treturn -4;\t// gain -> 0\n\n\t\treturn 0;\n\t}\n\nprivate:\n\tbool WasOnSameSide(double value) const\n\t{\n\t\tint sum = 0;\n\t\tfor(size_t i = 0; i < m_historySize; i++)\n\t\t{\n\t\t\tconst int vote = (value >= m_history[i])? 1 : -1;\n\t\t\tsum += vote;\n\t\t}\n\t\treturn abs(sum) == (int)m_historySize;\n\t}\n\n\tstatic double Change(double from, double to)\n\t{\n\t\treturn fabs(from - to) / from;\n\t}\n\n\t// /\\ or \\/ in last three history entries\n\tbool WasSpike() const\n\t{\n\t\tcassert(m_historySize >= 3);\n\t\tconst double h2 = m_history[m_historySize-3], h1 = m_history[m_historySize-2], h0 = m_history[m_historySize-1];\n\t\tif(((h2-h1) * (h1-h0)) > 0)\t// no sign change\n\t\t\treturn false;\n\t\tif(Change(h2, h0) > 0.05)\t// overall change from oldest to newest value\n\t\t\treturn false;\n\t\tif(Change(h1, h0) < 0.10)\t// no intervening spike\n\t\t\treturn false;\n\t\treturn true;\n\t}\n\n\tstatic const size_t m_historySize = 3;\n\tdouble m_history[m_historySize];\n\tint m_timesOnSameSide;\n};\n\n\nclass FrequencyFilter : public IFrequencyFilter\n{\n\tNONCOPYABLE(FrequencyFilter);\npublic:\n\tFrequencyFilter(double resolution, double expectedFrequency)\n\t\t: m_frequencyEstimator(resolution), m_controller(expectedFrequency), m_iirFilter(sensitivity, expectedFrequency)\n\t\t, m_stableFrequency((int)expectedFrequency), m_smoothedFrequency(expectedFrequency), m_averagedFrequency(expectedFrequency)\n\t\t, m_numberOfSamples((int)(sampleTime * expectedFrequency) + 1)\n\t{\n\t}\n\n\tvirtual void Update(double time)\n\t{\n\t\tdouble frequency;\n\t\tif(!m_frequencyEstimator(time, frequency))\n\t\t\treturn;\n\n\t\tconst int bias = m_controller.ComputeBias(m_smoothedFrequency, frequency);\n\t\tm_smoothedFrequency = m_iirFilter(frequency, bias);\n\n\t\t// Keep a moving average of the frequency over the last two seconds\n\t\t// If there is a spike of more than 25% (e.g. loading screen => game)\n\t\t// then reset the moving average and reset the number of samples needed\n\t\tconst double difference = fabs(m_smoothedFrequency - m_averagedFrequency);\n\t\tif (difference > errorTolerance * m_averagedFrequency)\n\t\t{\n\t\t\tm_averagedFrequency = m_smoothedFrequency;\n\t\t\tm_numberOfSamples = (int)(m_averagedFrequency * sampleTime) + 1;\n\t\t}\n\t\telse\n\t\t\tm_averagedFrequency = ((double)(m_numberOfSamples - 1) * m_averagedFrequency + m_smoothedFrequency) / (double)m_numberOfSamples;\n\n\t\t// allow the smoothed FPS to free-run until it is no longer near the\n\t\t// previous stable FPS value. round up because values are more often\n\t\t// too low than too high.\n\t\tm_stableFrequency = (int)(m_averagedFrequency + 0.99);\n\t}\n\n\tvirtual double SmoothedFrequency() const\n\t{\n\t\treturn m_smoothedFrequency;\n\t}\n\n\tvirtual int StableFrequency() const\n\t{\n\t\treturn m_stableFrequency;\n\t}\n\nprivate:\n\tFrequencyEstimator m_frequencyEstimator;\n\tController m_controller;\n\tIirFilter m_iirFilter;\n\n\tint m_stableFrequency;\n\tdouble m_smoothedFrequency;\n\tdouble m_averagedFrequency;\n\tint m_numberOfSamples;\n};\n\n\nPIFrequencyFilter CreateFrequencyFilter(double resolution, double expectedFrequency)\n{\n\treturn PIFrequencyFilter(new FrequencyFilter(resolution, expectedFrequency));\n}\n"
  },
  {
    "path": "fpsgame/gui/frequency_filter.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#ifndef INCLUDED_FREQUENCY_FILTER\n#define INCLUDED_FREQUENCY_FILTER\n\n// calculate frequency of events (tuned for 100 Hz)\nstruct IFrequencyFilter\n{\n\tvirtual ~IFrequencyFilter() {}\n\n\tvirtual void Update(double value) = 0;\n\n\t// smoothed but rapidly tracked frequency\n\tvirtual double SmoothedFrequency() const = 0;\n\n\t// stable, non-fluctuating value for user display\n\tvirtual int StableFrequency() const = 0;\n};\n\ntypedef shared_ptr<IFrequencyFilter> PIFrequencyFilter;\n\n// expectedFrequency is a guess that hopefully speeds up convergence\nLIB_API PIFrequencyFilter CreateFrequencyFilter(double resolution, double expectedFrequency);\n\n#endif\t// #ifndef INCLUDED_FREQUENCY_FILTER\n"
  },
  {
    "path": "fpsgame/gui/input.cpp",
    "content": "/* Copyright (c) 2012 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * SDL input redirector; dispatches to multiple handlers.\n */\n\n#include \"precompiled.h\"\n#include \"input.h\"\n\n#include <stdio.h>\n#include <stdlib.h>\n\n#include \"lib/external_libraries/libsdl.h\"\n\nconst size_t MAX_HANDLERS = 9;\nstatic InHandler handler_stack[MAX_HANDLERS];\nstatic size_t handler_stack_top = 0;\n\nstatic std::list<SDL_Event_> priority_events;\n\nvoid in_add_handler(InHandler handler)\n{\n\tENSURE(handler);\n\n\tif(handler_stack_top >= MAX_HANDLERS)\n\t\tWARN_IF_ERR(ERR::LIMIT);\n\n\thandler_stack[handler_stack_top++] = handler;\n}\n\nvoid in_reset_handlers()\n{\n\thandler_stack_top = 0;\n}\n\n// send ev to each handler until one returns IN_HANDLED\nvoid in_dispatch_event(const SDL_Event_* ev)\n{\n\tfor(int i = (int)handler_stack_top-1; i >= 0; i--)\n\t{\n\t\tENSURE(handler_stack[i] && ev);\n\t\tInReaction ret = handler_stack[i](ev);\n\t\t// .. done, return\n\t\tif(ret == IN_HANDLED)\n\t\t\treturn;\n\t\t// .. next handler\n\t\telse if(ret == IN_PASS)\n\t\t\tcontinue;\n\t\t// .. invalid return value\n\t\telse\n\t\t\tDEBUG_WARN_ERR(ERR::LOGIC);\t// invalid handler return value\n\t}\n}\n\nvoid in_push_priority_event(const SDL_Event_* event)\n{\n\tpriority_events.push_back(*event);\n}\n\nint in_poll_event(SDL_Event_* event)\n{\n\tif (!priority_events.empty())\n\t{\n\t\t*event = priority_events.front();\n\t\tpriority_events.pop_front();\n\t\treturn 1;\n\t}\n\n\treturn SDL_PollEvent(&event->ev);\n}\n"
  },
  {
    "path": "fpsgame/gui/input.h",
    "content": "/* Copyright (c) 2012 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * SDL input redirector; dispatches to multiple handlers.\n */\n\n#ifndef INCLUDED_INPUT\n#define INCLUDED_INPUT\n\n\n#include \"lib/external_libraries/libsdl_fwd.h\"\n\n// input handler return values.\nenum InReaction\n{\n\t// (the handlers' return values are checked and these\n\t// 'strange' values might bring errors to light)\n\n\t// pass the event to the next handler in the chain\n\tIN_PASS = 4,\n\n\t// we've handled it; no other handlers will receive this event.\n\tIN_HANDLED = 2\n};\n\ntypedef InReaction (*InHandler)(const SDL_Event_*);\n\n// register an input handler, which will receive all subsequent events first.\n// events are passed to other handlers if handler returns IN_PASS.\nextern void in_add_handler(InHandler handler);\n\n// remove all registered input handlers\nextern void in_reset_handlers();\n\n// send event to each handler (newest first) until one returns true\nextern void in_dispatch_event(const SDL_Event_* event);\n\n// push an event onto the back of a high-priority queue - the new event will\n// be returned by in_poll_event before any standard SDL events\nextern void in_push_priority_event(const SDL_Event_* event);\n\n// reads events that were pushed by in_push_priority_event, or (if there are\n// no high-priority events) reads from the SDL event queue with SDL_PollEvent.\n// returns 1 if an event was read, 0 otherwise.\nextern int in_poll_event(SDL_Event_* event);\n\n#endif\t// #ifndef INCLUDED_INPUT\n"
  },
  {
    "path": "fpsgame/gui/lib.cpp",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * various utility functions.\n */\n\n#include \"precompiled.h\"\n#include \"lib/lib.h\"\n\n#include <stdlib.h>\n#include <string.h>\n#include <ctype.h>\n\n#include \"lib/app_hooks.h\"\n#include \"lib/sysdep/sysdep.h\"\n\n\n//-----------------------------------------------------------------------------\n// type conversion\n\n// these avoid a common mistake in using >> (ANSI requires shift count be\n// less than the bit width of the type).\n\nu32 u64_hi(u64 x)\n{\n\treturn (u32)(x >> 32);\n}\n\nu32 u64_lo(u64 x)\n{\n\treturn (u32)(x & 0xFFFFFFFF);\n}\n\nu16 u32_hi(u32 x)\n{\n\treturn (u16)(x >> 16);\n}\n\nu16 u32_lo(u32 x)\n{\n\treturn (u16)(x & 0xFFFF);\n}\n\n\nu64 u64_from_u32(u32 hi, u32 lo)\n{\n\tu64 x = (u64)hi;\n\tx <<= 32;\n\tx |= lo;\n\treturn x;\n}\n\nu32 u32_from_u16(u16 hi, u16 lo)\n{\n\tu32 x = (u32)hi;\n\tx <<= 16;\n\tx |= lo;\n\treturn x;\n}\n\n\n// input in [0, 1); convert to u8 range\nu8 u8_from_double(double in)\n{\n\tif(!(0.0 <= in && in < 1.0))\n\t{\n\t\tDEBUG_WARN_ERR(ERR::LOGIC);\t// clampf not in [0,1)\n\t\treturn 255;\n\t}\n\n\tint l = (int)(in * 255.0);\n\tENSURE((unsigned)l <= 255u);\n\treturn (u8)l;\n}\n\n// input in [0, 1); convert to u16 range\nu16 u16_from_double(double in)\n{\n\tif(!(0.0 <= in && in < 1.0))\n\t{\n\t\tDEBUG_WARN_ERR(ERR::LOGIC);\t// clampf not in [0,1)\n\t\treturn 65535;\n\t}\n\n\tlong l = (long)(in * 65535.0);\n\tENSURE((unsigned long)l <= 65535u);\n\treturn (u16)l;\n}\n"
  },
  {
    "path": "fpsgame/gui/lib.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * various utility functions.\n */\n\n/**\n\nlow-level aka \"lib\"\n-------------------\n\nthis codebase was grown from modules shared between several projects,\ni.e. my personal library; hence the name \"lib\". it has been expanded to\nfit the needs of 0ad - in particular, resource loading.\n\nowing to the dual-use situation, the 0ad coding conventions are not met;\nalso, major changes are ill-advised because they may break other projects.\n\n\ndesign goals\n------------\n\n- fast and low-overhead, including startup time\n- portable: must run on Win32, Mac OS X and Linux\n- reusable across projects, i.e. no dependency on a\n  central 'manager' that ties modules together.\n\n\nscope\n-----\n\n- POSIX definitions\n- resource management\n- debugging tools (including memory tracker)\n- low-level helper functions, e.g. ADTs, endian conversion and timing\n- platform-dependent system/feature detection\n\n**/\n\n#ifndef INCLUDED_LIB\n#define INCLUDED_LIB\n\n#include <cmath>\t// fabsf\n#include <limits>\t// numeric_limits\n#include <stdexcept>\t// out_of_range\n#include <algorithm>\t// min, max\n\ntemplate<typename T>\nT Clamp(T val, T min, T max)\n{\n\tASSERT(min <= max);\n\treturn std::max(min, std::min(val, max));\n}\n\ntemplate<typename T>\nT DivideRoundUp(T dividend, T divisor)\n{\n\tASSERT(divisor != 0);\n\treturn (dividend + divisor-1) / divisor;\n}\n\n/**\n * are the given floats nearly \"equal\"?\n *\n * @return whether the numbers are within \"epsilon\" of each other.\n *\n * notes:\n * - the epsilon magic number varies with the magnitude of the inputs.\n *   we use a sane default, but don't use this routine for very\n *   large/small comparands.\n * - floating-point numbers don't magically lose precision. addition,\n *   subtraction and multiplication results are precise up to the mantissa's\n *   least-significant bit. only division, sqrt, sin/cos and other\n *   transcendental operations introduce error.\n **/\ninline bool feq(double d1, double d2, double epsilon = 0.00001)\n{\n\treturn fabs(d1 - d2) < epsilon;\n}\n\ninline bool feqf(float f1, float f2, float epsilon = 0.001f)\n{\n\treturn fabsf(f1 - f2) < epsilon;\n}\n\ninline bool IsSimilarMagnitude(double d1, double d2, const double relativeErrorTolerance = 0.05)\n{\n\tconst double relativeError = fabs(d1/d2 - 1.0);\n\tif(relativeError > relativeErrorTolerance)\n\t\treturn false;\n\treturn true;\n}\n\n\n//-----------------------------------------------------------------------------\n// type conversion\n\n// note: these avoid a common mistake in using >> (ANSI requires\n// shift count be less than the bit width of the type).\n\nextern u32 u64_hi(u64 x);\t/// return upper 32-bits\nextern u32 u64_lo(u64 x);\t/// return lower 32-bits\nextern u16 u32_hi(u32 x);\t/// return upper 16-bits\nextern u16 u32_lo(u32 x);\t/// return lower 16-bits\n\nextern u64 u64_from_u32(u32 hi, u32 lo);\t/// assemble u64 from u32\nextern u32 u32_from_u16(u16 hi, u16 lo);\t/// assemble u32 from u16\n\n// safe downcasters: cast from any integral type to u32 or u16; \n// issues warning if larger than would fit in the target type.\n//\n// these are generally useful but included here (instead of e.g. lib.h) for\n// several reasons:\n// - including implementation in lib.h doesn't work because the definition\n//   of ENSURE in turn requires lib.h's STMT.\n// - separate compilation of templates via export isn't supported by\n//   most compilers.\n\ntemplate<typename T> u8 u8_from_larger(T x)\n{\n\tconst u8 max = std::numeric_limits<u8>::max();\n\tif((u64)x > (u64)max)\n\t\tthrow std::out_of_range(\"u8_from_larger\");\n\treturn (u8)(x & max);\n}\n\ntemplate<typename T> u16 u16_from_larger(T x)\n{\n\tconst u16 max = std::numeric_limits<u16>::max();\n\tif((u64)x > (u64)max)\n\t\tthrow std::out_of_range(\"u16_from_larger\");\n\treturn (u16)(x & max);\n}\n\ntemplate<typename T> u32 u32_from_larger(T x)\n{\n\tconst u32 max = std::numeric_limits<u32>::max();\n\tif((u64)x > (u64)max)\n\t\tthrow std::out_of_range(\"u32_from_larger\");\n\treturn (u32)(x & max);\n}\n\n/// convert double to u8; verifies number is in range.\nextern u8 u8_from_double(double in);\n/// convert double to u16; verifies number is in range.\nextern u16 u16_from_double(double in);\n\n#endif\t// #ifndef INCLUDED_LIB\n"
  },
  {
    "path": "fpsgame/gui/lib_api.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#ifndef INCLUDED_LIB_API\n#define INCLUDED_LIB_API\n\n#include \"lib/sysdep/compiler.h\"\n\n// note: EXTERN_C cannot be used because shared_ptr is often returned\n// by value, which requires C++ linkage.\n\n#ifdef LIB_STATIC_LINK\n# define LIB_API\n#else\n# if MSC_VERSION\n#  ifdef LIB_BUILD\n#   define LIB_API __declspec(dllexport)\n#  else\n#   define LIB_API __declspec(dllimport)\n#   ifdef NDEBUG\n#    pragma comment(lib, \"lowlevel.lib\")\n#   else\n#    pragma comment(lib, \"lowlevel_d.lib\")\n#   endif\n#  endif\n# elif GCC_VERSION\n#  ifdef LIB_BUILD\n#   define LIB_API __attribute__ ((visibility(\"default\")))\n#  else\n#   define LIB_API\n#  endif\n# else\n#  error \"Don't know how to define LIB_API for this compiler\"\n# endif\n#endif\n\n#endif\t// #ifndef INCLUDED_LIB_API\n"
  },
  {
    "path": "fpsgame/gui/module_init.cpp",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * helpers for module initialization/shutdown.\n */\n\n#include \"precompiled.h\"\n#include \"lib/module_init.h\"\n\n#include \"lib/sysdep/cpu.h\"\t// cpu_CAS\n\n// not yet initialized, or already shutdown\nstatic const ModuleInitState UNINITIALIZED = 0;\t// value documented in header\n// running user callback - concurrent ModuleInit callers must spin\nstatic const ModuleInitState BUSY = ERR::AGAIN;\t// never returned\n// init succeeded; allow shutdown\nstatic const ModuleInitState INITIALIZED = INFO::SKIPPED;\n\n\nStatus ModuleInit(volatile ModuleInitState* initState, Status (*init)())\n{\n\tfor(;;)\n\t{\n\t\tif(cpu_CAS(initState, UNINITIALIZED, BUSY))\n\t\t{\n\t\t\tStatus ret = init();\n\t\t\t*initState = (ret == INFO::OK)? INITIALIZED : ret;\n\t\t\tCOMPILER_FENCE;\n\t\t\treturn ret;\n\t\t}\n\n\t\tconst ModuleInitState latchedInitState = *initState;\n\t\tif(latchedInitState == UNINITIALIZED || latchedInitState == BUSY)\n\t\t{\n\t\t\tcpu_Pause();\n\t\t\tcontinue;\n\t\t}\n\n\t\tENSURE(latchedInitState == INITIALIZED || latchedInitState < 0);\n\t\treturn (Status)latchedInitState;\n\t}\n}\n\n\nStatus ModuleShutdown(volatile ModuleInitState* initState, void (*shutdown)())\n{\n\tfor(;;)\n\t{\n\t\tif(cpu_CAS(initState, INITIALIZED, BUSY))\n\t\t{\n\t\t\tshutdown();\n\t\t\t*initState = UNINITIALIZED;\n\t\t\tCOMPILER_FENCE;\n\t\t\treturn INFO::OK;\n\t\t}\n\n\t\tconst ModuleInitState latchedInitState = *initState;\n\t\tif(latchedInitState == INITIALIZED || latchedInitState == BUSY)\n\t\t{\n\t\t\tcpu_Pause();\n\t\t\tcontinue;\n\t\t}\n\n\t\tif(latchedInitState == UNINITIALIZED)\n\t\t\treturn INFO::SKIPPED;\n\n\t\tENSURE(latchedInitState < 0);\n\t\treturn (Status)latchedInitState;\n\t}\n}\n"
  },
  {
    "path": "fpsgame/gui/module_init.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * helpers for module initialization/shutdown.\n */\n\n#ifndef INCLUDED_MODULE_INIT\n#define INCLUDED_MODULE_INIT\n\n/**\n * initialization state of a module (class, source file, etc.)\n * must be initialized to zero (e.g. by defining as a static variable).\n * DO NOT change the value!\n **/\ntypedef intptr_t ModuleInitState;\t// intptr_t is required by cpu_CAS\n\n/**\n * calls a user-defined init function if initState is zero.\n *\n * @return INFO::SKIPPED if already initialized, a Status if the\n * previous invocation failed, or the value returned by the callback.\n *\n * postcondition: initState is \"initialized\" if the callback returned\n * INFO::OK, otherwise its Status return value (which prevents\n * shutdown from being called).\n *\n * thread-safe: subsequent callers spin until the callback returns\n * (this prevents using partially-initialized modules)\n *\n * note that callbacks typically reference static data and thus do not\n * require a function argument, but that can later be added if necessary.\n **/\nLIB_API Status ModuleInit(volatile ModuleInitState* initState, Status (*init)());\n\n/**\n * calls a user-defined shutdown function if initState is \"initialized\".\n *\n * @return INFO::OK if shutdown occurred, INFO::SKIPPED if initState was\n * zero (uninitialized), otherwise the Status returned by ModuleInit.\n *\n * postcondition: initState remains set to the Status, or has been\n * reset to zero to allow multiple init/shutdown pairs, e.g. in self-tests.\n *\n * note: there is no provision for reference-counting because that\n * turns out to be problematic (a user might call shutdown immediately\n * after init; if this is the first use of the module, it will\n * be shutdown prematurely, which is at least inefficient and\n * possibly dangerous). instead, shutdown should only be called when\n * cleanup is necessary (e.g. at exit before leak reporting) and\n * it is certain that the module is no longer in use.\n **/\nLIB_API Status ModuleShutdown(volatile ModuleInitState* initState, void (*shutdown)());\n\n#endif\t// #ifndef INCLUDED_MODULE_INIT\n"
  },
  {
    "path": "fpsgame/gui/ogl.cpp",
    "content": "/* Copyright (c) 2013 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * OpenGL helper functions.\n */\n\n#include \"precompiled.h\"\n#include \"lib/ogl.h\"\n\n#include <stdio.h>\n#include <string.h>\n#include <stdarg.h>\n\n#include \"lib/external_libraries/libsdl.h\"\n#include \"lib/debug.h\"\n#include \"lib/sysdep/gfx.h\"\n#include \"lib/res/h_mgr.h\"\n\n#if MSC_VERSION\n#pragma comment(lib, \"opengl32.lib\")\n#endif\n\n\n//----------------------------------------------------------------------------\n// extensions\n//----------------------------------------------------------------------------\n\n// define extension function pointers\nextern \"C\"\n{\n#define FUNC(ret, name, params) ret (GL_CALL_CONV *p##name) params;\n#define FUNC2(ret, nameARB, nameCore, version, params) ret (GL_CALL_CONV *p##nameARB) params;\n#define FUNC3(ret, nameARB, nameCore, version, params) ret (GL_CALL_CONV *p##nameCore) params;\n#include \"lib/external_libraries/glext_funcs.h\"\n#undef FUNC3\n#undef FUNC2\n#undef FUNC\n}\n\n\nstatic const char* exts = NULL;\nstatic bool have_30, have_21, have_20, have_15, have_14, have_13, have_12;\n\n\n// return a C string of unspecified length containing a space-separated\n// list of all extensions the OpenGL implementation advertises.\n// (useful for crash logs).\nconst char* ogl_ExtensionString()\n{\n\tENSURE(exts && \"call ogl_Init before using this function\");\n\treturn exts;\n}\n\n\n// paranoia: newer drivers may forget to advertise an extension\n// indicating support for something that has been folded into the core.\n// we therefore check for all extensions known to be offered by the\n// GL implementation present on the user's system; ogl_HaveExtension will\n// take this into account.\n// the app can therefore just ask for extensions and not worry about this.\nstatic bool isImplementedInCore(const char* ext)\n{\n#define MATCH(known_ext)\\\n\tif(!strcmp(ext, #known_ext))\\\n\t\treturn true;\n\n\tif(have_30)\n\t{\n\t\tMATCH(GL_EXT_gpu_shader4);\n\t\tMATCH(GL_NV_conditional_render);\n\t\tMATCH(GL_ARB_color_buffer_float);\n\t\tMATCH(GL_ARB_depth_buffer_float);\n\t\tMATCH(GL_ARB_texture_float);\n\t\tMATCH(GL_EXT_packed_float);\n\t\tMATCH(GL_EXT_texture_shared_exponent);\n\t\tMATCH(GL_EXT_framebuffer_object);\n\t\tMATCH(GL_NV_half_float);\n\t\tMATCH(GL_ARB_half_float_pixel);\n\t\tMATCH(GL_EXT_framebuffer_multisample);\n\t\tMATCH(GL_EXT_framebuffer_blit);\n\t\tMATCH(GL_EXT_texture_integer);\n\t\tMATCH(GL_EXT_texture_array);\n\t\tMATCH(GL_EXT_packed_depth_stencil);\n\t\tMATCH(GL_EXT_draw_buffers2);\n\t\tMATCH(GL_EXT_texture_compression_rgtc);\n\t\tMATCH(GL_EXT_transform_feedback);\n\t\tMATCH(GL_APPLE_vertex_array_object);\n\t\tMATCH(GL_EXT_framebuffer_sRGB);\n\t}\n\tif(have_21)\n\t{\n\t\tMATCH(GL_ARB_pixel_buffer_object);\n\t\tMATCH(GL_EXT_texture_sRGB);\n\t}\n\tif(have_20)\n\t{\n\t\tMATCH(GL_ARB_shader_objects);\n\t\tMATCH(GL_ARB_vertex_shader);\n\t\tMATCH(GL_ARB_fragment_shader);\n\t\tMATCH(GL_ARB_shading_language_100);\n\t\tMATCH(GL_ARB_draw_buffers);\n\t\tMATCH(GL_ARB_texture_non_power_of_two);\n\t\tMATCH(GL_ARB_point_sprite);\n\t\tMATCH(GL_EXT_blend_equation_separate);\n\t}\n\tif(have_15)\n\t{\n\t\tMATCH(GL_ARB_vertex_buffer_object);\n\t\tMATCH(GL_ARB_occlusion_query);\n\t\tMATCH(GL_EXT_shadow_funcs);\n\t}\n\tif(have_14)\n\t{\n\t\tMATCH(GL_SGIS_generate_mipmap);\n\t\tMATCH(GL_NV_blend_square);\n\t\tMATCH(GL_ARB_depth_texture);\n\t\tMATCH(GL_ARB_shadow);\n\t\tMATCH(GL_EXT_fog_coord);\n\t\tMATCH(GL_EXT_multi_draw_arrays);\n\t\tMATCH(GL_ARB_point_parameters);\n\t\tMATCH(GL_EXT_secondary_color);\n\t\tMATCH(GL_EXT_blend_func_separate);\n\t\tMATCH(GL_EXT_stencil_wrap);\n\t\tMATCH(GL_ARB_texture_env_crossbar);\n\t\tMATCH(GL_EXT_texture_lod_bias);\n\t\tMATCH(GL_ARB_texture_mirrored_repeat);\n\t\tMATCH(GL_ARB_window_pos);\n\n\t\t// These extensions were added to GL 1.2, but as part of the optional\n\t\t// imaging subset; they're only guaranteed as of GL 1.4:\n\t\tMATCH(GL_EXT_blend_color);\n\t\tMATCH(GL_EXT_blend_minmax);\n\t\tMATCH(GL_EXT_blend_subtract);\n\t}\n\tif(have_13)\n\t{\n\t\tMATCH(GL_ARB_texture_compression);\n\t\tMATCH(GL_ARB_texture_cube_map);\n\t\tMATCH(GL_ARB_multisample);\n\t\tMATCH(GL_ARB_multitexture);\n\t\tMATCH(GL_ARB_transpose_matrix);\n\t\tMATCH(GL_ARB_texture_env_add);\n\t\tMATCH(GL_ARB_texture_env_combine);\n\t\tMATCH(GL_ARB_texture_env_dot3);\n\t\tMATCH(GL_ARB_texture_border_clamp);\n\t}\n\tif(have_12)\n\t{\n\t\tMATCH(GL_EXT_texture3D);\n\t\tMATCH(GL_EXT_bgra);\n\t\tMATCH(GL_EXT_packed_pixels);\n\t\tMATCH(GL_EXT_rescale_normal);\n\t\tMATCH(GL_EXT_separate_specular_color);\n\t\tMATCH(GL_SGIS_texture_edge_clamp);\n\t\tMATCH(GL_SGIS_texture_lod);\n\t\tMATCH(GL_EXT_draw_range_elements);\n\t\t// Skip the extensions that only affect the imaging subset\n\t}\n\n#undef MATCH\n\treturn false;\n}\n\n\n// check if the extension <ext> is supported by the OpenGL implementation.\n// takes subsequently added core support for some extensions into account.\nbool ogl_HaveExtension(const char* ext)\n{\n\tENSURE(exts && \"call ogl_Init before using this function\");\n\n\tif(isImplementedInCore(ext))\n\t\treturn true;\n\n\tconst char *p = exts, *end;\n\n\t// make sure ext is valid & doesn't contain spaces\n\tif(!ext || ext[0] == '\\0' || strchr(ext, ' '))\n\t\treturn false;\n\n\tfor(;;)\n\t{\n\t\tp = strstr(p, ext);\n\t\tif(!p)\n\t\t\treturn false; // <ext> string not found - extension not supported\n\t\tend = p + strlen(ext); // end of current substring\n\n\t\t// make sure the substring found is an entire extension string,\n\t\t// i.e. it starts and ends with ' '\n\t\tif((p == exts || p[-1] == ' ') &&\t// valid start AND\n\t\t   (*end == ' ' || *end == '\\0'))\t// valid end\n\t\t\treturn true;\n\t\tp = end;\n\t}\n}\n\n\n// check if the OpenGL implementation is at least at <version>.\n// (format: \"%d.%d\" major minor)\nbool ogl_HaveVersion(const char* desired_version)\n{\n\tint desired_major, desired_minor;\n\tif(sscanf_s(desired_version, \"%d.%d\", &desired_major, &desired_minor) != 2)\n\t{\n\t\tDEBUG_WARN_ERR(ERR::LOGIC);\t// invalid version string\n\t\treturn false;\n\t}\n\n\t// guaranteed to be of the form \"major.minor[.release][ vendor-specific]\"\n\t// or \"OpenGL ES major.minor[.release][ vendor-specific]\".\n\t// we won't distinguish GLES 2.0 from GL 2.0, but that's okay since\n\t// they're close enough.\n\tconst char* version = (const char*)glGetString(GL_VERSION);\n\tint major, minor;\n\tif(!version ||\n\t    (sscanf_s(version, \"%d.%d\", &major, &minor) != 2 &&\n\t\t sscanf_s(version, \"OpenGL ES %d.%d\", &major, &minor) != 2))\n\t{\n\t\tDEBUG_WARN_ERR(ERR::LOGIC);\t// GL_VERSION invalid\n\t\treturn false;\n\t}\n\n\t// note: don't just compare characters - major and minor may be >= 10.\n\treturn (major > desired_major) ||\n\t       (major == desired_major && minor >= desired_minor);\n}\n\n\n// check if all given extension strings (passed as const char* parameters,\n// terminated by a 0 pointer) are supported by the OpenGL implementation,\n// as determined by ogl_HaveExtension.\n// returns 0 if all are present; otherwise, the first extension in the\n// list that's not supported (useful for reporting errors).\n//\n// note: dummy parameter is necessary to access parameter va_list.\n//\n//\n// rationale: this interface is more convenient than individual\n// ogl_HaveExtension calls and allows reporting which extension is missing.\n//\n// one disadvantage is that there is no way to indicate that either one\n// of 2 extensions would be acceptable, e.g. (ARB|EXT)_texture_env_dot3.\n// this is isn't so bad, since they wouldn't be named differently\n// if there weren't non-trivial changes between them. for that reason,\n// we refrain from equivalence checks (which would boil down to\n// string-matching known extensions to their equivalents).\nconst char* ogl_HaveExtensions(int dummy, ...)\n{\n\tconst char* ext;\n\n\tva_list args;\n\tva_start(args, dummy);\n\tfor(;;)\n\t{\n\t\text = va_arg(args, const char*);\n\t\t// end of list reached; all were present => return 0.\n\t\tif(!ext)\n\t\t\tbreak;\n\n\t\t// not found => return name of missing extension.\n\t\tif(!ogl_HaveExtension(ext))\n\t\t\tbreak;\n\t}\n\tva_end(args);\n\n\treturn ext;\n}\n\n\n// to help when running with no hardware acceleration and only OpenGL 1.1\n// (e.g. testing the game in virtual machines), we define dummy versions of\n// some extension functions which our graphics code assumes exist.\n// it will render incorrectly but at least it shouldn't crash.\n\n#if CONFIG2_GLES\n\nstatic void enableDummyFunctions()\n{\n}\n\n#else\n\nstatic void GL_CALL_CONV dummy_glDrawRangeElementsEXT(GLenum mode, GLuint, GLuint, GLsizei count, GLenum type, GLvoid* indices)\n{\n\tglDrawElements(mode, count, type, indices);\n}\n\nstatic void GL_CALL_CONV dummy_glActiveTextureARB(int)\n{\n}\n\nstatic void GL_CALL_CONV dummy_glClientActiveTextureARB(int)\n{\n}\n\nstatic void GL_CALL_CONV dummy_glMultiTexCoord2fARB(int, float s, float t)\n{\n\tglTexCoord2f(s, t);\n}\n\nstatic void GL_CALL_CONV dummy_glMultiTexCoord3fARB(int, float s, float t, float r)\n{\n\tglTexCoord3f(s, t, r);\n}\n\nstatic void enableDummyFunctions()\n{\n\t// fall back to the dummy functions when extensions (or equivalent core support) are missing\n\n\tif(!ogl_HaveExtension(\"GL_EXT_draw_range_elements\"))\n\t{\n\t\tpglDrawRangeElementsEXT = &dummy_glDrawRangeElementsEXT;\n\t}\n\n\tif(!ogl_HaveExtension(\"GL_ARB_multitexture\"))\n\t{\n\t\tpglActiveTextureARB = &dummy_glActiveTextureARB;\n\t\tpglClientActiveTextureARB = &dummy_glClientActiveTextureARB;\n\t\tpglMultiTexCoord2fARB = &dummy_glMultiTexCoord2fARB;\n\t\tpglMultiTexCoord3fARB = &dummy_glMultiTexCoord3fARB;\n\t}\n}\n\n#endif\t// #if CONFIG2_GLES\n\nstatic void importExtensionFunctions()\n{\n\t// It should be safe to load the ARB function pointers even if the\n\t// extension isn't advertised, since we won't actually use them without\n\t// checking for the extension.\n\t// (TODO: this calls ogl_HaveVersion far more times than is necessary -\n\t// we should probably use the have_* variables instead)\n\t// Note: the xorg-x11 implementation of glXGetProcAddress doesn't return NULL\n\t//   if the function is unsupported (i.e. the rare case of a driver not reporting\n\t//   its supported version correctly, see http://trac.wildfiregames.com/ticket/171)\n#define FUNC(ret, name, params) p##name = (ret (GL_CALL_CONV*) params)SDL_GL_GetProcAddress(#name);\n#define FUNC23(pname, ret, nameARB, nameCore, version, params) \\\n\tpname = NULL; \\\n\tif(ogl_HaveVersion(version)) \\\n\t\tpname = (ret (GL_CALL_CONV*) params)SDL_GL_GetProcAddress(#nameCore); \\\n\tif(!pname) /* use the ARB name if the driver lied about what version it supports */ \\\n\t\tpname = (ret (GL_CALL_CONV*) params)SDL_GL_GetProcAddress(#nameARB);\n#define FUNC2(ret, nameARB, nameCore, version, params) FUNC23(p##nameARB, ret, nameARB, nameCore, version, params)\n#define FUNC3(ret, nameARB, nameCore, version, params) FUNC23(p##nameCore, ret, nameARB, nameCore, version, params)\n#include \"lib/external_libraries/glext_funcs.h\"\n#undef FUNC3\n#undef FUNC2\n#undef FUNC23\n#undef FUNC\n\n\tenableDummyFunctions();\n}\n\n\n//----------------------------------------------------------------------------\n\nstatic void dump_gl_error(GLenum err)\n{\n\tdebug_printf(\"OGL| \");\n#define E(e) case e: debug_printf(\"%s\\n\", #e); break;\n\tswitch (err)\n\t{\n\tE(GL_INVALID_ENUM)\n\tE(GL_INVALID_VALUE)\n\tE(GL_INVALID_OPERATION)\n#if !CONFIG2_GLES\n\tE(GL_STACK_OVERFLOW)\n\tE(GL_STACK_UNDERFLOW)\n#endif\n\tE(GL_OUT_OF_MEMORY)\n\tE(GL_INVALID_FRAMEBUFFER_OPERATION)\n\tdefault: debug_printf(\"Unknown GL error: %04x\\n\", err); break;\n\t}\n#undef E\n}\n\nvoid ogl_WarnIfErrorLoc(const char *file, int line)\n{\n\t// glGetError may return multiple errors, so we poll it in a loop.\n\t// the debug_printf should only happen once (if this is set), though.\n\tbool error_enountered = false;\n\tGLenum first_error = 0;\n\n\tfor(;;)\n\t{\n\t\tGLenum err = glGetError();\n\t\tif(err == GL_NO_ERROR)\n\t\t\tbreak;\n\n\t\tif(!error_enountered)\n\t\t\tfirst_error = err;\n\n\t\terror_enountered = true;\n\t\tdump_gl_error(err);\n\t}\n\n\tif(error_enountered)\n\t\tdebug_printf(\"%s:%d: OpenGL error(s) occurred: %04x\\n\", file, line, (unsigned int)first_error);\n}\n\n\n// ignore and reset the specified error (as returned by glGetError).\n// any other errors that have occurred are reported as ogl_WarnIfError would.\n//\n// this is useful for suppressing annoying error messages, e.g.\n// \"invalid enum\" for GL_CLAMP_TO_EDGE even though we've already\n// warned the user that their OpenGL implementation is too old.\nbool ogl_SquelchError(GLenum err_to_ignore)\n{\n\t// glGetError may return multiple errors, so we poll it in a loop.\n\t// the debug_printf should only happen once (if this is set), though.\n\tbool error_enountered = false;\n\tbool error_ignored = false;\n\tGLenum first_error = 0;\n\n\tfor(;;)\n\t{\n\t\tGLenum err = glGetError();\n\t\tif(err == GL_NO_ERROR)\n\t\t\tbreak;\n\n\t\tif(err == err_to_ignore)\n\t\t{\n\t\t\terror_ignored = true;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif(!error_enountered)\n\t\t\tfirst_error = err;\n\n\t\terror_enountered = true;\n\t\tdump_gl_error(err);\n\t}\n\n\tif(error_enountered)\n\t\tdebug_printf(\"OpenGL error(s) occurred: %04x\\n\", (unsigned int)first_error);\n\n\treturn error_ignored;\n}\n\n\n//----------------------------------------------------------------------------\n// feature and limit detect\n//----------------------------------------------------------------------------\n\nGLint ogl_max_tex_size = -1;\t\t\t\t// [pixels]\nGLint ogl_max_tex_units = -1;\t\t\t\t// limit on GL_TEXTUREn\n\n// call after each video mode change, since thereafter extension functions\n// may have changed [address].\nvoid ogl_Init()\n{\n\t// cache extension list and versions for oglHave*.\n\t// note: this is less about performance (since the above are not\n\t// time-critical) than centralizing the 'OpenGL is ready' check.\n\texts = (const char*)glGetString(GL_EXTENSIONS);\n\tENSURE(exts);\t// else: called before OpenGL is ready for use\n\thave_12 = ogl_HaveVersion(\"1.2\");\n\thave_13 = ogl_HaveVersion(\"1.3\");\n\thave_14 = ogl_HaveVersion(\"1.4\");\n\thave_15 = ogl_HaveVersion(\"1.5\");\n\thave_20 = ogl_HaveVersion(\"2.0\");\n\thave_21 = ogl_HaveVersion(\"2.1\");\n\thave_30 = ogl_HaveVersion(\"3.0\");\n\n\timportExtensionFunctions();\n\n\tglGetIntegerv(GL_MAX_TEXTURE_SIZE, &ogl_max_tex_size);\n#if !CONFIG2_GLES\n\tglGetIntegerv(GL_MAX_TEXTURE_UNITS, &ogl_max_tex_units);\n#endif\n}\n"
  },
  {
    "path": "fpsgame/gui/ogl.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * OpenGL helper functions.\n */\n\n#ifndef INCLUDED_OGL\n#define INCLUDED_OGL\n\n#include \"lib/external_libraries/opengl.h\"\n\n\n/**\n * initialization: import extension function pointers and do feature detect.\n * call before using any other function, and after each video mode change.\n * fails if OpenGL not ready for use.\n **/\nextern void ogl_Init();\n\n\n//-----------------------------------------------------------------------------\n// extensions\n\n/**\n * check if an extension is supported by the OpenGL implementation.\n *\n * takes subsequently added core support for some extensions into account\n * (in case drivers forget to advertise extensions).\n *\n * @param ext extension string; exact case.\n * @return bool.\n **/\nextern bool ogl_HaveExtension(const char* ext);\n\n/**\n * make sure the OpenGL implementation version matches or is newer than\n * the given version.\n *\n * @param version version string; format: (\"%d.%d\", major, minor).\n * example: \"1.2\".\n **/\nextern bool ogl_HaveVersion(const char* version);\n\n/**\n * check if a list of extensions are all supported (as determined by\n * ogl_HaveExtension).\n *\n * @param dummy value ignored; varargs requires a placeholder.\n * follow it by a list of const char* extension string parameters,\n * terminated by a 0 pointer.\n * @return 0 if all are present; otherwise, the first extension in the\n * list that's not supported (useful for reporting errors).\n **/\nextern const char* ogl_HaveExtensions(int dummy, ...) SENTINEL_ARG;\n\n/**\n * get a list of all supported extensions.\n *\n * useful for crash logs / system information.\n *\n * @return read-only C string of unspecified length containing all\n * advertised extension names, separated by space.\n **/\nextern const char* ogl_ExtensionString();\n\n// The game wants to use some extension constants that aren't provided by\n// glext.h on some old systems.\n// Manually define all the necessary ones that are missing from\n// GL_GLEXT_VERSION 39 (Mesa 7.0) since that's probably an old enough baseline:\n#ifndef GL_VERSION_3_0\n# define GL_MIN_PROGRAM_TEXEL_OFFSET 0x8904\n# define GL_MAX_PROGRAM_TEXEL_OFFSET 0x8905\n#endif\n#ifndef GL_EXT_transform_feedback\n# define GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS_EXT 0x8C8A\n# define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS_EXT 0x8C8B\n# define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS_EXT 0x8C80\n#endif\n#ifndef GL_ARB_geometry_shader4\n# define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_ARB 0x8C29\n# define GL_MAX_GEOMETRY_VARYING_COMPONENTS_ARB 0x8DDD\n# define GL_MAX_VERTEX_VARYING_COMPONENTS_ARB 0x8DDE\n# define GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_ARB 0x8DDF\n# define GL_MAX_GEOMETRY_OUTPUT_VERTICES_ARB 0x8DE0\n# define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_ARB 0x8DE1\n#endif\n#ifndef GL_ARB_timer_query\n# define GL_TIME_ELAPSED 0x88BF\n# define GL_TIMESTAMP 0x8E28\n#endif\n#ifndef GL_ARB_framebuffer_object\n# define GL_INVALID_FRAMEBUFFER_OPERATION 0x0506\n#endif\n// Also need some more for OS X 10.5:\n#ifndef GL_EXT_texture_array\n# define GL_MAX_ARRAY_TEXTURE_LAYERS_EXT 0x88FF\n#endif\n// Also need some types not in old glext.h:\n#ifndef GL_ARB_sync\n typedef int64_t GLint64;\n typedef uint64_t GLuint64;\n#endif\n\n// declare extension function pointers\n#if OS_WIN\n# define GL_CALL_CONV __stdcall\n#else\n# define GL_CALL_CONV\n#endif\n#define FUNC(ret, name, params) EXTERN_C ret (GL_CALL_CONV *p##name) params;\n#define FUNC2(ret, nameARB, nameCore, version, params) EXTERN_C ret (GL_CALL_CONV *p##nameARB) params;\n#define FUNC3(ret, nameARB, nameCore, version, params) EXTERN_C ret (GL_CALL_CONV *p##nameCore) params;\n#include \"lib/external_libraries/glext_funcs.h\"\n#undef FUNC3\n#undef FUNC2\n#undef FUNC\n// leave GL_CALL_CONV defined for ogl.cpp\n\n\n//-----------------------------------------------------------------------------\n// errors\n\n/**\n * raise a warning (break into the debugger) if an OpenGL error is pending.\n * resets the OpenGL error state afterwards.\n *\n * when an error is reported, insert calls to this in a binary-search scheme\n * to quickly narrow down the actual error location.\n * \n * reports a bogus invalid_operation error if called before OpenGL is\n * initialized, so don't!\n *\n * disabled in release mode for efficiency and to avoid annoying errors.\n **/\nextern void ogl_WarnIfErrorLoc(const char *file, int line);\n#ifdef NDEBUG\n# define ogl_WarnIfError()\n#else\n# define ogl_WarnIfError() ogl_WarnIfErrorLoc(__FILE__, __LINE__)\n#endif\n\n/**\n * ignore and reset the specified OpenGL error.\n *\n * this is useful for suppressing annoying error messages, e.g.\n * \"invalid enum\" for GL_CLAMP_TO_EDGE even though we've already\n * warned the user that their OpenGL implementation is too old.\n *\n * call after the fact, i.e. the error has been raised. if another or\n * different error is pending, those are reported immediately.\n *\n * @param err_to_ignore: one of the glGetError enums.\n * @return true if the requested error was seen and ignored\n **/\nextern bool ogl_SquelchError(GLenum err_to_ignore);\n\n\n//-----------------------------------------------------------------------------\n// implementation limits / feature detect\n\nextern GLint ogl_max_tex_size;\t\t\t\t/// [pixels]\nextern GLint ogl_max_tex_units;\t\t\t\t/// limit on GL_TEXTUREn\n\n#endif\t// #ifndef INCLUDED_OGL\n"
  },
  {
    "path": "fpsgame/gui/os_path.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#ifndef INCLUDED_OS_PATH\n#define INCLUDED_OS_PATH\n\n#include \"lib/path.h\"\n\n// rationale:\n// users are responsible for ensuring the path doesn't contain any forbidden\n// characters (including any code points < 0x00 or >= 0x100 on anything but Windows)\ntypedef Path OsPath;\n\n#if OS_WIN\n\nstatic inline const Path::String& OsString(const OsPath& path)\n{\n\treturn path.string();\n}\n\n#else\n\nstatic inline std::string OsString(const OsPath& path)\n{\n\tconst Path::String& wstring = path.string();\n\tstd::string string(wstring.length(), '\\0');\n\tfor(size_t i = 0; i < wstring.length(); i++)\n\t{\n\t\tENSURE((unsigned)wstring[i] <= (unsigned)UCHAR_MAX);\n\t\tstring[i] = (char)wstring[i];\n\t}\n\treturn string;\n}\n#endif\n\n\n#endif\t// #ifndef INCLUDED_OS_PATH\n"
  },
  {
    "path": "fpsgame/gui/path.cpp",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * helper functions for path strings.\n */\n\n#include \"precompiled.h\"\n#include \"lib/path.h\"\n\n#include <cstring>\n#include <cerrno>\n\nstatic const StatusDefinition pathStatusDefinitions[] = {\n\t{ ERR::PATH_CHARACTER_ILLEGAL, L\"illegal path character\" },\n\t{ ERR::PATH_CHARACTER_UNSAFE, L\"unsafe path character\" },\n\t{ ERR::PATH_NOT_FOUND, L\"path not found\" },\n\t{ ERR::PATH_MIXED_SEPARATORS, L\"path contains both slash and backslash separators\" }\n};\nSTATUS_ADD_DEFINITIONS(pathStatusDefinitions);\n\n\nstatic bool path_is_dir_sep(wchar_t c)\n{\n\tif(c == '/' || c == '\\\\')\n\t\treturn true;\n\treturn false;\n}\n\n// is s2 a subpath of s1, or vice versa?\n// (equal counts as subpath)\nbool path_is_subpath(const wchar_t* s1, const wchar_t* s2)\n{\n\t// make sure s1 is the shorter string\n\tif(wcslen(s1) > wcslen(s2))\n\t\tstd::swap(s1, s2);\n\n\twchar_t c1 = 0, last_c1, c2;\n\tfor(;;)\n\t{\n\t\tlast_c1 = c1;\n\t\tc1 = *s1++, c2 = *s2++;\n\n\t\t// end of s1 reached:\n\t\tif(c1 == '\\0')\n\t\t{\n\t\t\t// s1 matched s2 up until:\n\t\t\tif((c2 == '\\0') ||\t// its end (i.e. they're equal length) OR\n\t\t\t\tpath_is_dir_sep(c2) ||\t\t// start of next component OR\n\t\t\t\tpath_is_dir_sep(last_c1))\t// \", but both have a trailing slash\n\t\t\t\t// => is subpath\n\t\t\t\treturn true;\n\t\t}\n\n\t\t// mismatch => is not subpath\n\t\tif(c1 != c2)\n\t\t\treturn false;\n\t}\n}\n\n\n//-----------------------------------------------------------------------------\n\n// return pointer to the name component within path (i.e. skips over all\n// characters up to the last dir separator, if any).\nconst wchar_t* path_name_only(const wchar_t* path)\n{\n\tconst wchar_t* slash1 = wcsrchr(path, '/');\n\tconst wchar_t* slash2 = wcsrchr(path, '\\\\');\n\t// neither present, it's a filename only\n\tif(!slash1 && !slash2)\n\t\treturn path;\n\n\t// return name, i.e. component after the last slash\n\tconst wchar_t* name = std::max(slash1, slash2)+1;\n\treturn name;\n}\n\n\n/*static*/ Status Path::Validate(String::value_type c)\n{\n\tif(c < 32)\n\t\treturn ERR::PATH_CHARACTER_UNSAFE;\n\n#if !OS_WIN\n\tif(c >= UCHAR_MAX)\n\t\treturn ERR::PATH_CHARACTER_UNSAFE;\n#endif\n\n\tswitch(c)\n\t{\n\tcase '\\\\':\n\tcase '/':\n\tcase ':':\n\tcase '\"':\n\tcase '?':\n\tcase '*':\n\tcase '<':\n\tcase '>':\n\tcase '|':\n\tcase '^':\n\t\treturn ERR::PATH_CHARACTER_ILLEGAL;\n\n\tdefault:\n\t\treturn INFO::OK;\n\t}\n}\n"
  },
  {
    "path": "fpsgame/gui/path.h",
    "content": "/* Copyright (c) 2013 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * Path string class, similar to boost::filesystem::basic_path.\n */\n\n// notes:\n// - this module is independent of lib/file so that it can be used from\n//   other code without pulling in the entire file manager.\n// - there is no restriction on buffer lengths except the underlying OS.\n//   input buffers must not exceed PATH_MAX chars, while outputs\n//   must hold at least that much.\n// - unless otherwise mentioned, all functions are intended to work with\n//   native and VFS paths.\n//   when reading, both '/' and SYS_DIR_SEP are accepted; '/' is written.\n\n#ifndef INCLUDED_PATH\n#define INCLUDED_PATH\n\n#if CONFIG_ENABLE_BOOST\n# include \"boost/functional/hash.hpp\"\n#endif\n\n#include \"lib/utf8.h\"\n\n#include <cstring>\n\nnamespace ERR\n{\n\tconst Status PATH_CHARACTER_ILLEGAL   = -100300;\n\tconst Status PATH_CHARACTER_UNSAFE    = -100301;\n\tconst Status PATH_NOT_FOUND           = -100302;\n\tconst Status PATH_MIXED_SEPARATORS    = -100303;\n}\n\n/**\n * is s2 a subpath of s1, or vice versa? (equal counts as subpath)\n *\n * @param s1, s2 comparand strings\n * @return bool\n **/\nLIB_API bool path_is_subpath(const wchar_t* s1, const wchar_t* s2);\n\n/**\n * Get the path component of a path.\n * Skips over all characters up to the last dir separator, if any.\n *\n * @param path Input path.\n * @return pointer to path component within \\<path\\>.\n **/\nLIB_API const wchar_t* path_name_only(const wchar_t* path);\n\n\n// NB: there is a need for 'generic' paths (e.g. for Trace entry / archive pathnames). \n// converting between specialized variants via c_str would be inefficient, and the\n// Os/VfsPath typedefs are hopefully sufficient to avoid errors.\nclass Path\n{\npublic:\n\ttypedef std::wstring String;\n\n\tPath()\n\t{\n\t\tDetectSeparator();\n\t}\n\n\tPath(const char* p)\n\t\t: path((const unsigned char*)p, (const unsigned char*)p+strlen(p))\n\t\t// interpret bytes as unsigned; makes no difference for ASCII,\n\t\t// and ensures OsPath on Unix will only contain values 0 <= c < 0x100\n\t{\n\t\tDetectSeparator();\n\t}\n\n\tPath(const wchar_t* p)\n\t\t: path(p, p+wcslen(p))\n\t{\n\t\tDetectSeparator();\n\t}\n\n\tPath(const std::string& s)\n\t\t: path((const unsigned char*)s.c_str(), (const unsigned char*)s.c_str()+s.length())\n\t{\n\t\tDetectSeparator();\n\t}\n\n\tPath(const std::wstring& s)\n\t\t: path(s)\n\t{\n\t\tDetectSeparator();\n\t}\n\n\tPath& operator=(const Path& rhs)\n\t{\n\t\tpath = rhs.path;\n\t\tDetectSeparator();\t// (warns if separators differ)\n\t\treturn *this;\n\t}\n\n\tbool empty() const\n\t{\n\t\treturn path.empty();\n\t}\n\n\tconst String& string() const\n\t{\n\t\treturn path;\n\t}\n\n\t/**\n\t * Return a UTF-8 version of the path, in a human-readable but potentially\n\t * lossy form. It is *not* safe to take this string and construct a new\n\t * Path object from it (it may fail for some non-ASCII paths) - it should\n\t * only be used for displaying paths to users.\n\t */\n\tstd::string string8() const\n\t{\n\t\t// TODO: On Unixes, this will only be correct for ASCII or ISO-8859-1\n\t\t// encoded paths; we should probably assume UTF-8 encoding by default\n\t\t// (but take care to handle non-valid-UTF-8 paths safely).\n\n\t\treturn utf8_from_wstring(path);\n\t}\n\n\tbool operator<(const Path& rhs) const\n\t{\n\t\treturn path < rhs.path;\n\t}\n\n\tbool operator==(const Path& rhs) const\n\t{\n\t\treturn path == rhs.path;\n\t}\n\n\tbool operator!=(const Path& rhs) const\n\t{\n\t\treturn !operator==(rhs);\n\t}\n\n\tbool IsDirectory() const\n\t{\n\t\tif(empty())\t// (ensure length()-1 is safe)\n\t\t\treturn true;\t// (the VFS root directory is represented as an empty string)\n\t\treturn path[path.length()-1] == separator;\n\t}\n\n\tPath Parent() const\n\t{\n\t\tconst size_t idxSlash = path.find_last_of(separator);\n\t\tif(idxSlash == String::npos)\n\t\t\treturn L\"\";\n\t\treturn path.substr(0, idxSlash);\n\t}\n\n\tPath Filename() const\n\t{\n\t\tconst size_t idxSlash = path.find_last_of(separator);\n\t\tif(idxSlash == String::npos)\n\t\t\treturn path;\n\t\treturn path.substr(idxSlash+1);\n\t}\n\n\tPath Basename() const\n\t{\n\t\tconst Path filename = Filename();\n\t\tconst size_t idxDot = filename.string().find_last_of('.');\n\t\tif(idxDot == String::npos)\n\t\t\treturn filename;\n\t\treturn filename.string().substr(0, idxDot);\n\t}\n\n\t// (Path return type allows callers to use our operator==)\n\tPath Extension() const\n\t{\n\t\tconst Path filename = Filename();\n\t\tconst size_t idxDot = filename.string().find_last_of('.');\n\t\tif(idxDot == String::npos)\n\t\t\treturn Path();\n\t\treturn filename.string().substr(idxDot);\n\t}\n\n\tPath ChangeExtension(Path extension) const\n\t{\n\t\treturn Parent() / Path(Basename().string() + extension.string());\n\t}\n\n\tPath operator/(Path rhs) const\n\t{\n\t\tPath ret = *this;\n\t\tif(ret.path.empty())\t// (empty paths assume '/')\n\t\t\tret.separator = rhs.separator;\n\t\tif(!ret.IsDirectory())\n\t\t\tret.path += ret.separator;\n\n\t\tif(rhs.path.find((ret.separator == '/')? '\\\\' : '/') != String::npos)\n\t\t{\n\t\t\tPrintToDebugOutput();\n\t\t\trhs.PrintToDebugOutput();\n\t\t\tDEBUG_WARN_ERR(ERR::PATH_MIXED_SEPARATORS);\n\t\t}\n\t\tret.path += rhs.path;\n\t\treturn ret;\n\t}\n\n\t/**\n\t * Return the path before the common part of both paths\n\t * @param other Indicates the start of the path which should be removed\n\t * @note other should be a VfsPath, while this should be an OsPath\n\t */\n\tPath BeforeCommon(Path other) const\n\t{\n\t\tPath ret = *this;\n\t\tif(ret.empty() || other.empty())\n\t\t\treturn L\"\";\n\n\t\t// Convert the separator to allow for string comparison\n\t\tif(other.separator != ret.separator)\n\t\t\treplace(other.path.begin(), other.path.end(), other.separator, ret.separator);\n\n\t\tconst size_t idx = ret.path.rfind(other.path);\n\t\tif(idx == String::npos)\n\t\t\treturn L\"\";\n\n\t\treturn path.substr(0, idx);\n\t}\n\n\tstatic Status Validate(String::value_type c);\n\nprivate:\n\tvoid PrintToDebugOutput() const\n\t{\n\t\tdebug_printf(\"Path %s, separator %c\\n\", string8().c_str(), (char)separator);\n\t}\n\n\tvoid DetectSeparator()\n\t{\n\t\tconst size_t idxBackslash = path.find('\\\\');\n\n\t\tif(path.find('/') != String::npos && idxBackslash != String::npos)\n\t\t{\n\t\t\tPrintToDebugOutput();\n\t\t\tDEBUG_WARN_ERR(ERR::PATH_MIXED_SEPARATORS);\n\t\t}\n\n\t\t// (default to '/' for empty strings)\n\t\tseparator = (idxBackslash == String::npos)? '/' : '\\\\';\n\t}\n\n\tString path;\n\n\t// note: ideally, path strings would only contain '/' or even SYS_DIR_SEP.\n\t// however, Windows-specific code (e.g. the sound driver detection)\n\t// uses these routines with '\\\\' strings. the boost::filesystem approach of\n\t// converting them all to '/' and then back via external_file_string is\n\t// annoying and inefficient. we allow either type of separators,\n\t// appending whichever was first encountered. when modifying the path,\n\t// we ensure the same separator is used.\n\twchar_t separator;\n};\n\nstatic inline std::wostream& operator<<(std::wostream& s, const Path& path)\n{\n\ts << path.string();\n\treturn s;\n}\n\nstatic inline std::wistream& operator>>(std::wistream& s, Path& path)\n{\n\tPath::String string;\n\ts >> string;\n\tpath = Path(string);\n\treturn s;\n}\n\n#if CONFIG_ENABLE_BOOST\n\nnamespace boost {\n\ntemplate<>\nstruct hash<Path> : std::unary_function<Path, std::size_t>\n{\n\tstd::size_t operator()(const Path& path) const\n\t{\n\t\treturn hash_value(path.string());\n\t}\n};\n\n}\n\n#endif\t// #if CONFIG_ENABLE_BOOST\n\n#endif\t// #ifndef INCLUDED_PATH\n"
  },
  {
    "path": "fpsgame/gui/posix/posix.cpp",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#include \"precompiled.h\"\n#include \"lib/posix/posix.h\"\n\n\n#if EMULATE_WCSDUP\nwchar_t* wcsdup(const wchar_t* str)\n{\n\tconst size_t num_chars = wcslen(str);\n\twchar_t* dst = (wchar_t*)malloc((num_chars+1)*sizeof(wchar_t));\t// note: wcsdup is required to use malloc\n\tif(!dst)\n\t\treturn 0;\n\twcscpy_s(dst, num_chars+1, str);\n\treturn dst;\n}\n#endif\n\n#if EMULATE_WCSCASECMP\nint wcscasecmp (const wchar_t* s1, const wchar_t* s2)\n{\n\twint_t a1, a2;\n\n\tif (s1 == s2)\n\t\treturn 0;\n\n\tdo\n\t{\n\t\ta1 = towlower(*s1++);\n\t\ta2 = towlower(*s2++);\n\t\tif (a1 == L'\\0')\n\t\t\tbreak;\n\t}\n\twhile (a1 == a2);\n\n\treturn a1 - a2;\n}\n#endif\n"
  },
  {
    "path": "fpsgame/gui/posix/posix.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * definitions for a subset of POSIX.\n */\n\n/*\n\n[KEEP IN SYNC WITH WIKI]\n\nthis header makes available commonly used POSIX (Portable Operating System\nInterface) definitions, e.g. thread, file I/O and socket APIs.\non Linux and OS X we just include the requisite headers; Win32 doesn't really\nsupport POSIX (*), so we have to implement everything ourselves.\n\nrationale: this is preferable to a wrapper for several reasons:\n- less code (implementation is only needed on Win32)\n- no lock-in (the abstraction may prevent not-designed-for operations\n  that the POSIX interface would have allowed)\n- familiarity (many coders already know POSIX)\n\nif a useful definition is missing, feel free to add it!\n\nimplementation reference is the \"Single Unix Specification v3\"\n(http://www.unix.org/online.html) - it's similar to the POSIX standard\n(superset?) and freely available.\n\n\n* Win32 does have a POSIX subsystem (mandated by a government contract),\nbut it is crippled. only apps with the PE header 'subsystem' field set to\n\"POSIX\" can use the appendant DLL, and then they can't call the regular\nWindows APIs. this is obviously unacceptable - GDI is needed to set up OpenGL.\n\nwe therefore need to emulate POSIX functions using the Win32 API.\nfortunately, many POSIX functions are already implemented in the VC CRT and\nneed only be renamed (e.g. _open, _stat).\n\n*/\n\n#ifndef INCLUDED_POSIX\n#define INCLUDED_POSIX\n\n#include <cmath>\t// see isfinite comment below\n\n#if OS_WIN\n# include \"lib/sysdep/os/win/wposix/wposix.h\"\n#endif\n\n#include \"lib/posix/posix_types.h\"\n\n// disabled to reduce dependencies. include them where needed.\n//#include \"lib/posix/posix_aio.h\"\n//#include \"lib/posix/posix_dlfcn.h\"\n//#include \"lib/posix/posix_filesystem.h\"\n//#include \"lib/posix/posix_mman.h\"\n//#include \"lib/posix/posix_pthread.h\"\n//#include \"lib/posix/posix_time.h\"\n//#include \"lib/posix/posix_utsname.h\"\n\n\n// note: the following need only be #defined (instead of defining a\n// trampoline function) because the redefined functions are already\n// declared by standard headers.\n\n// provide C99 *snprintf functions if compiler doesn't already\n// (MinGW does, VC7.1 doesn't).\n#if MSC_VERSION\n# define snprintf _snprintf\n# define swprintf _snwprintf\n# define vsnprintf _vsnprintf\n# define vswprintf _vsnwprintf\n#endif\n\n// VC doesn't define str[n]casecmp\n#if MSC_VERSION\n#define strcasecmp _stricmp\n#define strncasecmp _strnicmp\n#define wcscasecmp _wcsicmp\n#define wcsncasecmp _wcsnicmp\n#endif\n\n#if OS_MACOSX\n# define EMULATE_WCSDUP 1\n# define EMULATE_WCSCASECMP 1\n#else\n# define EMULATE_WCSDUP 0\n# define EMULATE_WCSCASECMP 0\n#endif\n\n#if EMULATE_WCSDUP\nextern wchar_t* wcsdup(const wchar_t* str);\n#endif\n\n#if EMULATE_WCSCASECMP\nextern int wcscasecmp(const wchar_t* s1, const wchar_t* s2);\n#endif\n\n// Some systems have C99 support but in C++ they provide only std::isfinite\n// and not isfinite. C99 specifies that isfinite is a macro, so we can use\n// #ifndef and define it if it's not there already.\n// We've included <cmath> above to make sure it defines that macro.\n#ifndef isfinite\n# if MSC_VERSION\n#  define isfinite _finite\n#  define isnan _isnan\n# else\n#  define isfinite std::isfinite\n#  define isnan std::isnan\n# endif\n#endif\n\n#endif\t// #ifndef INCLUDED_POSIX\n"
  },
  {
    "path": "fpsgame/gui/rand.cpp",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * pseudorandom number generator\n */\n\n#include \"precompiled.h\"\n#include \"lib/rand.h\"\n\n// avoids several common pitfalls; see discussion at\n// http://www.azillionmonkeys.com/qed/random.html\n\n// rand() is poorly implemented (e.g. in VC7) and only returns < 16 bits;\n// double that amount by concatenating 2 random numbers.\n// this is not to fix poor rand() randomness - the number returned will be\n// folded down to a much smaller interval anyway. instead, a larger XRAND_MAX\n// decreases the probability of having to repeat the loop.\n#if RAND_MAX < 65536\nstatic const size_t XRAND_MAX = (RAND_MAX+1)*(RAND_MAX+1) - 1;\nstatic size_t xrand()\n{\n\treturn rand()*(RAND_MAX+1) + rand();\n}\n// rand() is already ok; no need to do anything.\n#else\nstatic const size_t XRAND_MAX = RAND_MAX;\nstatic size_t xrand()\n{\n\treturn rand();\n}\n#endif\n\nsize_t rand(size_t min_inclusive, size_t max_exclusive)\n{\n\tconst size_t range = (max_exclusive-min_inclusive);\n\t// huge interval or min >= max\n\tif(range == 0 || range > XRAND_MAX)\n\t{\n\t\tWARN_IF_ERR(ERR::INVALID_PARAM);\n\t\treturn 0;\n\t}\n\n\tconst size_t inv_range = XRAND_MAX / range;\n\n\t// generate random number in [0, range)\n\t// idea: avoid skewed distributions when <range> doesn't evenly divide\n\t// XRAND_MAX by simply discarding values in the \"remainder\".\n\t// not expected to run often since XRAND_MAX is large.\n\tsize_t x;\n\tdo\n\t{\n\t\tx = xrand();\n\t}\n\twhile(x >= range * inv_range);\n\tx /= inv_range;\n\n\tx += min_inclusive;\n\tENSURE(x < max_exclusive);\n\treturn x;\n}\n"
  },
  {
    "path": "fpsgame/gui/rand.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * pseudorandom number generator\n */\n\n#ifndef INCLUDED_RAND\n#define INCLUDED_RAND\n\n/**\n * return random integer in [min, max).\n * avoids several common pitfalls; see discussion at\n * http://www.azillionmonkeys.com/qed/random.html\n **/\nLIB_API size_t rand(size_t min_inclusive, size_t max_exclusive);\n\n#endif\t// #ifndef INCLUDED_RAND\n"
  },
  {
    "path": "fpsgame/gui/regex.cpp",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n// minimal regex implementation\n\n#include \"precompiled.h\"\n\n\nint match_wildcard(const wchar_t* s, const wchar_t* w)\n{\n\tif(!w)\n\t\treturn 1;\n\n\t// saved position in both strings, used to expand '*':\n\t// s2 is advanced until match.\n\t// initially 0 - we abort on mismatch before the first '*'.\n\tconst wchar_t* s2 = 0;\n\tconst wchar_t* w2 = 0;\n\n\twhile(*s)\n\t{\n\t\tconst wchar_t wc = *w;\n\t\tif(wc == '*')\n\t\t{\n\t\t\t// wildcard string ended with * => match.\n\t\t\tif(*++w == '\\0')\n\t\t\t\treturn 1;\n\n\t\t\tw2 = w;\n\t\t\ts2 = s+1;\n\t\t}\n\t\t// match one character\n\t\telse if(towupper(wc) == towupper(*s) || wc == '?')\n\t\t{\n\t\t\tw++;\n\t\t\ts++;\n\t\t}\n\t\t// mismatched character\n\t\telse\n\t\t{\n\t\t\t// no '*' found yet => mismatch.\n\t\t\tif(!s2)\n\t\t\t\treturn 0;\n\n\t\t\t// resume at previous position+1\n\t\t\tw = w2;\n\t\t\ts = s2++;\n\t\t}\n\t}\n\n\t// strip trailing * in wildcard string\n\twhile(*w == '*')\n\t\tw++;\n\n\treturn (*w == '\\0');\n}\n"
  },
  {
    "path": "fpsgame/gui/regex.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * minimal regex implementation\n */\n\n#ifndef INCLUDED_REGEX\n#define INCLUDED_REGEX\n\n/**\n * see if string matches pattern.\n *\n * @param s input string\n * @param w pseudo-regex to match against. case-insensitive;\n * may contain '?' and/or '*' wildcards. if NULL, matches everything.\n *\n * @return 1 if they match, otherwise 0.\n *\n * algorithm from http://www.codeproject.com/string/wildcmp.asp.\n **/\nextern int match_wildcard(const wchar_t* s, const wchar_t* w);\n\n#endif\t// #ifndef INCLUDED_REGEX\n"
  },
  {
    "path": "fpsgame/gui/res/handle.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * forward declaration of Handle (reduces dependencies)\n */\n\n#ifndef INCLUDED_HANDLE\n#define INCLUDED_HANDLE\n\n/**\n * `handle' representing a reference to a resource (sound, texture, etc.)\n *\n * 0 is the (silently ignored) invalid handle value; < 0 is an error code.\n *\n * this is 64 bits because we want tags to remain unique. (tags are a\n * counter that disambiguate several subsequent uses of the same\n * resource array slot). 32-bit handles aren't enough because the index\n * field requires at least 12 bits, thus leaving only about 512K possible\n * tag values.\n **/\ntypedef i64 Handle;\n\n#endif\t// #ifndef INCLUDED_HANDLE\n"
  },
  {
    "path": "fpsgame/gui/secure_crt.cpp",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * partial implementation of VC8's secure CRT functions\n */\n\n#include \"precompiled.h\"\n\n#include <stdio.h>\n#include <errno.h>\n#include <stdarg.h>\n\n#include \"lib/secure_crt.h\"\n\n#if OS_ANDROID\n# include <boost/algorithm/string/replace.hpp>\n#endif\n\n// we were included from wsecure_crt.cpp; skip all stuff that\n// must only be done once.\n#ifndef WSECURE_CRT\nstatic const StatusDefinition secureCrtStatusDefinitions[] = {\n\t{ ERR::STRING_NOT_TERMINATED, L\"Invalid string (no 0 terminator found in buffer)\" }\n};\nSTATUS_ADD_DEFINITIONS(secureCrtStatusDefinitions);\n#endif\n\n\n// written against http://std.dkuug.dk/jtc1/sc22/wg14/www/docs/n1031.pdf .\n// optimized for size - e.g. strcpy calls strncpy with n = SIZE_MAX.\n\n// since char and wide versions of these functions are basically the same,\n// this source file implements generic versions and bridges the differences\n// with these macros. wsecure_crt.cpp #defines WSECURE_CRT and\n// includes this file.\n\n// Note: These defines are all #undef:ed at the end of the file - remember to\n// add a corresponding #undef when adding a #define.\n#ifdef WSECURE_CRT\n# define tchar wchar_t\n# define tstring std::wstring\n# define T(string_literal) L ## string_literal\n# define tnlen wcsnlen\n# define tncpy_s wcsncpy_s\n# define tcpy_s wcscpy_s\n# define tncat_s wcsncat_s\n# define tcat_s wcscat_s\n# define tcmp wcscmp\n# define tcpy wcscpy\n# define tvsnprintf vswprintf\t// used by implementation\n# define tvsprintf_s vswprintf_s\n# define tsprintf_s swprintf_s\n#else\n# define tchar char\n# define tstring std::string\n# define T(string_literal) string_literal\n# define tnlen strnlen\n# define tncpy_s strncpy_s\n# define tcpy_s strcpy_s\n# define tncat_s strncat_s\n# define tcat_s strcat_s\n# define tcmp strcmp\n# define tcpy strcpy\n# define tvsnprintf vsnprintf\t// used by implementation\n# define tvsprintf_s vsprintf_s\n# define tsprintf_s sprintf_s\n#endif\t// #ifdef WSECURE_CRT\n\n\n// return <retval> and raise an assertion if <condition> doesn't hold.\n// usable as a statement.\n#define ENFORCE(condition, err_to_warn,\tretval) STMT(\\\n\tif(!(condition))                    \\\n\t{                                   \\\n\t\tDEBUG_WARN_ERR(err_to_warn);    \\\n\t\treturn retval;                  \\\n\t}                                   \\\n)\n\n// raise a debug warning if <len> is the size of a pointer.\n// catches bugs such as: tchar* s = ..; tcpy_s(s, sizeof(s), T(\"..\"));\n// if warnings get annoying, replace with debug_printf. usable as a statement.\n//\n// currently disabled due to high risk of false positives.\n#define WARN_IF_PTR_LEN(len)\\\n/*\n\tENSURE(len != sizeof(char*));\n*/\n\n\n// skip our implementation if already available, but not the\n// self-test and the t* defines (needed for test).\n#if EMULATE_SECURE_CRT\n\n#if !OS_UNIX || OS_MACOSX || OS_OPENBSD\n// return length [in characters] of a string, not including the trailing\n// null character. to protect against access violations, only the\n// first <max_len> characters are examined; if the null character is\n// not encountered by then, <max_len> is returned.\nsize_t tnlen(const tchar* str, size_t max_len)\n{\n\t// note: we can't bail - what would the return value be?\n\tENSURE(str != 0);\n\n\tWARN_IF_PTR_LEN(max_len);\n\n\tsize_t len;\n\tfor(len = 0; len < max_len; len++)\n\t\tif(*str++ == '\\0')\n\t\t\tbreak;\n\n\treturn len;\n}\n#endif // !OS_UNIX\n\n#if OS_ANDROID\nstatic tstring androidFormat(const tchar *fmt)\n{\n\t// FIXME handle %%hs, %%ls, etc\n\ttstring ret(fmt);\n\tboost::algorithm::replace_all(ret, T(\"%ls\"), T(\"%S\"));\n\tboost::algorithm::replace_all(ret, T(\"%hs\"), T(\"%s\"));\n\treturn ret;\n}\n#endif\n\n// copy at most <max_src_chars> (not including trailing null) from\n// <src> into <dst>, which must not overlap.\n// if thereby <max_dst_chars> (including null) would be exceeded,\n// <dst> is set to the empty string and ERANGE returned; otherwise,\n// 0 is returned to indicate success and that <dst> is null-terminated.\n//\n// note: padding with zeroes is not called for by N1031.\nint tncpy_s(tchar* dst, size_t max_dst_chars, const tchar* src, size_t max_src_chars)\n{\n\t// the MS implementation returns EINVAL and allows dst = 0 if\n\t// max_dst_chars = max_src_chars = 0. no mention of this in\n\t// 3.6.2.1.1, so don't emulate that behavior.\n\tENFORCE(dst != 0, ERR::INVALID_POINTER, EINVAL);\n\tENFORCE(max_dst_chars != 0, ERR::INVALID_SIZE, EINVAL);\t// N1031 says ERANGE, MSDN/MSVC says EINVAL\n\t*dst = '\\0';\t// in case src ENFORCE is triggered\n\tENFORCE(src != 0, ERR::INVALID_POINTER, EINVAL);\n\n\tWARN_IF_PTR_LEN(max_dst_chars);\n\tWARN_IF_PTR_LEN(max_src_chars);\n\n\t// copy string until null character encountered or limit reached.\n\t// optimized for size (less comparisons than MS impl) and\n\t// speed (due to well-predicted jumps; we don't bother unrolling).\n\ttchar* p = dst;\n\tsize_t chars_left = std::min(max_dst_chars, max_src_chars);\n\twhile(chars_left != 0)\n\t{\n\t\t// success: reached end of string normally.\n\t\tif((*p++ = *src++) == '\\0')\n\t\t\treturn 0;\n\t\tchars_left--;\n\t}\n\n\t// which limit did we hit?\n\t// .. dst, and last character wasn't null: overflow.\n\tif(max_dst_chars <= max_src_chars)\n\t{\n\t\t*dst = '\\0';\n\t\tENFORCE(0, ERR::INVALID_SIZE, ERANGE);\n\t}\n\t// .. source: success, but still need to null-terminate the destination.\n\t*p = '\\0';\n\treturn 0;\n}\n\n\n// copy <src> (including trailing null) into <dst>, which must not overlap.\n// if thereby <max_dst_chars> (including null) would be exceeded,\n// <dst> is set to the empty string and ERANGE returned; otherwise,\n// 0 is returned to indicate success and that <dst> is null-terminated.\nint tcpy_s(tchar* dst, size_t max_dst_chars, const tchar* src)\n{\n\treturn tncpy_s(dst, max_dst_chars, src, SIZE_MAX);\n}\n\n\n// append <src> to <dst>, which must not overlap.\n// if thereby <max_dst_chars> (including null) would be exceeded,\n// <dst> is set to the empty string and ERANGE returned; otherwise,\n// 0 is returned to indicate success and that <dst> is null-terminated.\nint tncat_s(tchar* dst, size_t max_dst_chars, const tchar* src, size_t max_src_chars)\n{\n\tENFORCE(dst != 0, ERR::INVALID_POINTER, EINVAL);\n\tENFORCE(max_dst_chars != 0, ERR::INVALID_SIZE, EINVAL);\t// N1031 says ERANGE, MSDN/MSVC says EINVAL\n\t// src is checked in tncpy_s\n\n\t// WARN_IF_PTR_LEN not necessary: both max_dst_chars and max_src_chars\n\t// are checked by tnlen / tncpy_s (respectively).\n\n\tconst size_t dst_len = tnlen(dst, max_dst_chars);\n\tif(dst_len == max_dst_chars)\n\t{\n\t\t*dst = '\\0';\n\t\tENFORCE(0, ERR::STRING_NOT_TERMINATED, EINVAL);\t// N1031/MSDN says ERANGE, MSVC says EINVAL\n\t}\n\n\ttchar* const end = dst+dst_len;\n\tconst size_t chars_left = max_dst_chars-dst_len;\n\tint ret = tncpy_s(end, chars_left, src, max_src_chars);\n\t// if tncpy_s overflowed, we need to clear the start of our string\n\t// (not just the appended part). can't do that by default, because\n\t// the beginning of dst is not changed in normal operation.\n\tif(ret != 0)\n\t\t*dst = '\\0';\n\treturn ret;\n}\n\n\n// append <src> to <dst>, which must not overlap.\n// if thereby <max_dst_chars> (including null) would be exceeded,\n// <dst> is set to the empty string and ERANGE returned; otherwise,\n// 0 is returned to indicate success and that <dst> is null-terminated.\n//\n// note: implemented as tncat_s(dst, max_dst_chars, src, SIZE_MAX)\nint tcat_s(tchar* dst, size_t max_dst_chars, const tchar* src)\n{\n\treturn tncat_s(dst, max_dst_chars, src, SIZE_MAX);\n}\n\n\nint tvsprintf_s(tchar* dst, size_t max_dst_chars, const tchar* fmt, va_list ap)\n{\n\tif(!dst || !fmt || max_dst_chars == 0)\n\t{\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\n#if OS_ANDROID\n\t// Workaround for https://code.google.com/p/android/issues/detail?id=109074\n\t// (vswprintf doesn't null-terminate strings)\n\tmemset(dst, 0, max_dst_chars * sizeof(tchar));\n\n\tconst int ret = tvsnprintf(dst, max_dst_chars, androidFormat(fmt).c_str(), ap);\n#else\n\tconst int ret = tvsnprintf(dst, max_dst_chars, fmt, ap);\n#endif\n\tif(ret < 0 || ret >= int(max_dst_chars))\t// not enough space\n\t{\n\t\tdst[0] = '\\0';\n\t\treturn -1;\n\t}\n\treturn ret;\t// negative if error, else # chars written (excluding '\\0')\n}\n\n\nint tsprintf_s(tchar* buf, size_t max_chars, const tchar* fmt, ...)\n{\n\tva_list ap;\n\tva_start(ap, fmt);\n\tint len = tvsprintf_s(buf, max_chars, fmt, ap);\n\tva_end(ap);\n\treturn len;\n}\n\n#endif // #if EMULATE_SECURE_CRT\n\n#undef tchar\n#undef T\n#undef tnlen\n#undef tncpy_s\n#undef tcpy_s\n#undef tncat_s\n#undef tcat_s\n#undef tcmp\n#undef tcpy\n#undef tvsnprintf\n#undef tvsprintf_s\n#undef tsprintf_s\n"
  },
  {
    "path": "fpsgame/gui/secure_crt.h",
    "content": "/* Copyright (c) 2015 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * partial implementation of VC8's secure CRT functions\n */\n\n#ifndef INCLUDED_SECURE_CRT\n#define INCLUDED_SECURE_CRT\n\n#include <stdarg.h>\n\n#include \"lib/status.h\"\n\nnamespace ERR\n{\n\tconst Status STRING_NOT_TERMINATED = -100600;\n}\n\n// if the platform lacks a secure CRT implementation, we'll provide one.\n#if MSC_VERSION\n# define EMULATE_SECURE_CRT 0\n#else\n# define EMULATE_SECURE_CRT 1\n#endif\n\n\n#if EMULATE_SECURE_CRT\n\n// (conflicts with glibc definitions)\n#if !OS_UNIX || OS_MACOSX || OS_OPENBSD\n// return length [in characters] of a string, not including the trailing\n// null character. to protect against access violations, only the\n// first <max_len> characters are examined; if the null character is\n// not encountered by then, <max_len> is returned.\n// strnlen is available on OpenBSD\n#if !OS_OPENBSD\nextern size_t strnlen(const char* str, size_t max_len);\n#endif\nextern size_t wcsnlen(const wchar_t* str, size_t max_len);\n#endif\n\n// copy at most <max_src_chars> (not including trailing null) from\n// <src> into <dst>, which must not overlap.\n// if thereby <max_dst_chars> (including null) would be exceeded,\n// <dst> is set to the empty string and ERANGE returned; otherwise,\n// 0 is returned to indicate success and that <dst> is null-terminated.\n//\n// note: padding with zeroes is not called for by NG1031.\nextern int strncpy_s(char* dst, size_t max_dst_chars, const char* src, size_t max_src_chars);\nextern int wcsncpy_s(wchar_t* dst, size_t max_dst_chars, const wchar_t* src, size_t max_src_chars);\n\n// copy <src> (including trailing null) into <dst>, which must not overlap.\n// if thereby <max_dst_chars> (including null) would be exceeded,\n// <dst> is set to the empty string and ERANGE returned; otherwise,\n// 0 is returned to indicate success and that <dst> is null-terminated.\n//\n// note: implemented as tncpy_s(dst, max_dst_chars, src, SIZE_MAX)\nextern int strcpy_s(char* dst, size_t max_dst_chars, const char* src);\nextern int wcscpy_s(wchar_t* dst, size_t max_dst_chars, const wchar_t* src);\n\n// append at most <max_src_chars> (not including trailing null) from\n// <src> to <dst>, which must not overlap.\n// if thereby <max_dst_chars> (including null) would be exceeded,\n// <dst> is set to the empty string and ERANGE returned; otherwise,\n// 0 is returned to indicate success and that <dst> is null-terminated.\nextern int strncat_s(char* dst, size_t max_dst_chars, const char* src, size_t max_src_chars);\nextern int wcsncat_s(wchar_t* dst, size_t max_dst_chars, const wchar_t* src, size_t max_src_chars);\n\n// append <src> to <dst>, which must not overlap.\n// if thereby <max_dst_chars> (including null) would be exceeded,\n// <dst> is set to the empty string and ERANGE returned; otherwise,\n// 0 is returned to indicate success and that <dst> is null-terminated.\n//\n// note: implemented as tncat_s(dst, max_dst_chars, src, SIZE_MAX)\nextern int strcat_s(char* dst, size_t max_dst_chars, const char* src);\nextern int wcscat_s(wchar_t* dst, size_t max_dst_chars, const wchar_t* src);\n\nextern int vsprintf_s(char* dst, size_t max_dst_chars, const char* fmt, va_list ap) VPRINTF_ARGS(3);\nextern int vswprintf_s(wchar_t* dst, size_t max_dst_chars, const wchar_t* fmt, va_list ap) VWPRINTF_ARGS(3);\n\nextern int sprintf_s(char* buf, size_t max_chars, const char* fmt, ...) PRINTF_ARGS(3);\nextern int swprintf_s(wchar_t* buf, size_t max_chars, const wchar_t* fmt, ...) WPRINTF_ARGS(3);\n\n// we'd like to avoid deprecation warnings caused by scanf. selective\n// 'undeprecation' isn't possible, replacing all stdio declarations with\n// our own deprecation scheme is a lot of work, suppressing all deprecation\n// warnings would cause important other warnings to be missed, and avoiding\n// scanf outright isn't convenient.\n// the remaining alternative is using scanf_s where available and otherwise\n// defining it to scanf. note that scanf_s has a different API:\n// any %s or %c or %[ format specifier's buffer must be followed by a\n// size parameter. callers must either avoid these, or provide two codepaths\n// (use scanf #if EMULATE_SECURE_CRT, otherwise scanf_s).\n#define scanf_s scanf\n#define wscanf_s wscanf\n#define fscanf_s fscanf\n#define fwscanf_s fwscanf\n#define sscanf_s sscanf\n#define swscanf_s swscanf\n\n#endif\t// #if EMULATE_SECURE_CRT\n#endif\t// #ifndef INCLUDED_SECURE_CRT\n"
  },
  {
    "path": "fpsgame/gui/sysdep/os/unix/udbg.cpp",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/* udbg.cpp\n\nThis file contains debug helpers that are common for all unix systems. See\nlinux/ldbg.cpp for the linux-specific stuff (Using BFD and backtrace() for\nsymbol lookups and backtraces)\n*/\n\n#include \"precompiled.h\"\n\n#include <cstdio>\n#include <sys/types.h>\n#include <signal.h>\n\n#include \"lib/timer.h\"\n#include \"lib/sysdep/sysdep.h\"\n#include \"lib/debug.h\"\n#include \"lib/utf8.h\"\n\n\nStatus debug_CaptureContext(void* UNUSED(context))\n{\n\t// (not needed unless/until we support stack traces)\n\treturn INFO::SKIPPED;\n}\n\nvoid debug_break()\n{\n\tkill(getpid(), SIGTRAP);\n}\n\n#define DEBUGGER_WAIT 3\n#define DEBUGGER_CMD \"gdb\"\n#define DEBUGGER_ARG_FORMAT \"--pid=%d\"\n#define DEBUGGER_BREAK_AFTER_WAIT 0\n\n/*\nStart the debugger and tell it to attach to the current process/thread\n(called by display_error)\n*/\nvoid udbg_launch_debugger()\n{\n\tpid_t orgpid=getpid();\n\tpid_t ret=fork();\n\tif (ret == 0)\n\t{\n\t\t// Child Process: exec() gdb (Debugger), set to attach to old fork\n\t\tchar buf[16];\n\t\tsnprintf(buf, 16, DEBUGGER_ARG_FORMAT, orgpid);\n\t\t\n\t\tint ret=execlp(DEBUGGER_CMD, DEBUGGER_CMD, buf, NULL);\n\t\t// In case of success, we should never get here anyway, though...\n\t\tif (ret != 0)\n\t\t{\n\t\t\tperror(\"Debugger launch failed\");\n\t\t}\n\t}\n\telse if (ret > 0)\n\t{\n\t\t// Parent (original) fork:\n\t\tdebug_printf(\"Sleeping until debugger attaches.\\nPlease wait.\\n\");\n\t\tsleep(DEBUGGER_WAIT);\n\t}\n\telse // fork error, ret == -1\n\t{\n\t\tperror(\"Debugger launch: fork failed\");\n\t}\n}\n\n#if OS_ANDROID\n\n#include <android/log.h>\n\nvoid debug_puts(const char* text)\n{\n\t__android_log_print(ANDROID_LOG_WARN, \"pyrogenesis\", \"%s\", text);\n}\n\n#else\n\nvoid debug_puts(const char* text)\n{\n\tprintf(\"%s\", text);\n\tfflush(stdout);\n}\n\n#endif\n\nint debug_IsPointerBogus(const void* UNUSED(p))\n{\n\t// TODO: maybe this should do some checks\n\treturn false;\n}\n"
  },
  {
    "path": "fpsgame/gui/sysdep/os/unix/udbg.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n// sysdep/unix/udbg.h: included from sysdep/debug.h and sysdep/unix/debug.cpp\n\n#ifndef INCLUDED_UDBG\n#define INCLUDED_UDBG\n\nextern void udbg_launch_debugger();\n\n#endif\t// #ifndef INCLUDED_UDBG\n"
  },
  {
    "path": "fpsgame/gui/sysdep/os/unix/ufilesystem.cpp",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * Unix implementation of wchar_t versions of POSIX filesystem functions\n */\n\n#include \"precompiled.h\"\n#include \"lib/sysdep/filesystem.h\"\n\n#include \"lib/path.h\"\n\n#include <cstdio>\n\nstruct WDIR\n{\n\tDIR* d;\n\twchar_t name[PATH_MAX];\n\twdirent ent;\n};\n\n#if OS_ANDROID\n\n// The Crystax NDK seems to do weird things with opendir etc.\n// To avoid that, load the symbols directly from the real libc\n// and use them instead.\n\n#include <dlfcn.h>\n\nstatic void* libc;\nstatic DIR* (*libc_opendir)(const char*);\nstatic dirent* (*libc_readdir)(DIR*);\nstatic int (*libc_closedir)(DIR*);\n\nvoid init_libc()\n{\n\tif (libc)\n\t\treturn;\n\tlibc = dlopen(\"/system/lib/libc.so\", RTLD_LAZY);\n\tENSURE(libc);\n\tlibc_opendir = (DIR*(*)(const char*))dlsym(libc, \"opendir\");\n\tlibc_readdir = (dirent*(*)(DIR*))dlsym(libc, \"readdir\");\n\tlibc_closedir = (int(*)(DIR*))dlsym(libc, \"closedir\");\n\tENSURE(libc_opendir && libc_readdir && libc_closedir);\n}\n\n#define opendir libc_opendir\n#define readdir libc_readdir\n#define closedir libc_closedir\n\n#else\n\nvoid init_libc() { }\n\n#endif\n\nWDIR* wopendir(const OsPath& path)\n{\n\tinit_libc();\n\tDIR* d = opendir(OsString(path).c_str());\n\tif(!d)\n\t\treturn 0;\n\tWDIR* wd = new WDIR;\n\twd->d = d;\n\twd->name[0] = '\\0';\n\twd->ent.d_name = wd->name;\n\treturn wd;\n}\n\nstruct wdirent* wreaddir(WDIR* wd)\n{\n\tdirent* ent = readdir(wd->d);\n\tif(!ent)\n\t\treturn 0;\n\twcscpy_s(wd->name, ARRAY_SIZE(wd->name), OsPath(ent->d_name).string().c_str());\n\treturn &wd->ent;\n}\n\nint wclosedir(WDIR* wd)\n{\n\tint ret = closedir(wd->d);\n\tdelete wd;\n\treturn ret;\n}\n\n\nint wopen(const OsPath& pathname, int oflag)\n{\n\tENSURE(!(oflag & O_CREAT));\n\treturn open(OsString(pathname).c_str(), oflag);\n}\n\nint wopen(const OsPath& pathname, int oflag, mode_t mode)\n{\n\treturn open(OsString(pathname).c_str(), oflag, mode);\n}\n\nint wclose(int fd)\n{\n\treturn close(fd);\n}\n\n\nint wtruncate(const OsPath& pathname, off_t length)\n{\n\treturn truncate(OsString(pathname).c_str(), length);\n}\n\nint wunlink(const OsPath& pathname)\n{\n\treturn unlink(OsString(pathname).c_str());\n}\n\nint wrmdir(const OsPath& path)\n{\n\treturn rmdir(OsString(path).c_str());\n}\n\nint wrename(const OsPath& pathnameOld, const OsPath& pathnameNew)\n{\n\treturn rename(OsString(pathnameOld).c_str(), OsString(pathnameNew).c_str());\n}\n\nOsPath wrealpath(const OsPath& pathname)\n{\n\tchar resolvedBuf[PATH_MAX];\n\tconst char* resolved = realpath(OsString(pathname).c_str(), resolvedBuf);\n\tif(!resolved)\n\t\treturn OsPath();\n\treturn resolved;\n}\n\nint wstat(const OsPath& pathname, struct stat* buf)\n{\n\treturn stat(OsString(pathname).c_str(), buf);\n}\n\nint wmkdir(const OsPath& path, mode_t mode)\n{\n\treturn mkdir(OsString(path).c_str(), mode);\n}\n"
  },
  {
    "path": "fpsgame/gui/sysdep/os/unix/unix.cpp",
    "content": "/* Copyright (c) 2014 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#include \"precompiled.h\"\n\n#include <unistd.h>\n#include <stdio.h>\n#include <wchar.h>\n\n#include \"lib/utf8.h\"\n#include \"lib/sysdep/sysdep.h\"\n#include \"udbg.h\"\n\n#include <boost/algorithm/string/replace.hpp>\n\n#define GNU_SOURCE\n#include <dlfcn.h>\n\n#include <sys/wait.h>\n\n#if OS_MACOSX\n#define URL_OPEN_COMMAND \"open\"\n#else\n#define URL_OPEN_COMMAND \"xdg-open\"\n#endif\n\nbool sys_IsDebuggerPresent()\n{\n\treturn false;\n}\n\nstd::wstring sys_WideFromArgv(const char* argv_i)\n{\n\t// argv is usually UTF-8 on Linux, unsure about OS X..\n\treturn wstring_from_utf8(argv_i);\n}\n\n// these are basic POSIX-compatible backends for the sysdep.h functions.\n// Win32 has better versions which override these.\n\nvoid sys_display_msg(const wchar_t* caption, const wchar_t* msg)\n{\n\tfprintf(stderr, \"%ls: %ls\\n\", caption, msg); // must not use fwprintf, since stderr is byte-oriented\n}\n\n#if OS_MACOSX || OS_ANDROID\nstatic ErrorReactionInternal try_gui_display_error(const wchar_t* text, bool manual_break, bool allow_suppress, bool no_continue)\n{\n\t// TODO: implement this, in a way that doesn't rely on X11\n\t// and doesn't occasionally cause crazy errors like\n\t// \"The process has forked and you cannot use this\n\t// CoreFoundation functionality safely. You MUST exec().\"\n\n\treturn ERI_NOT_IMPLEMENTED;\n}\n#else\nstatic ErrorReactionInternal try_gui_display_error(const wchar_t* text, bool manual_break, bool allow_suppress, bool no_continue)\n{\n\t// We'll run xmessage via fork/exec.\n\t// To avoid bad interaction between fork and pthreads, the child process\n\t// should only call async-signal-safe functions before exec.\n\t// So prepare all the child's data in advance, before forking:\n\n\tStatus err; // ignore UTF-8 errors\n\tstd::string message = utf8_from_wstring(text, &err);\n\n\t// Replace CRLF->LF\n\tboost::algorithm::replace_all(message, \"\\r\\n\", \"\\n\");\n\n\t// TODO: we ought to wrap the text if it's very long,\n\t// since xmessage doesn't do that and it'll get clamped\n\t// to the screen width\n\n\tconst char* cmd = \"/usr/bin/xmessage\";\n\n\tchar buttons[256] = \"\";\n\tconst char* defaultButton = \"Exit\";\n\n\tif(!no_continue)\n\t{\n\t\tstrcat_s(buttons, sizeof(buttons), \"Continue:100,\");\n\t\tdefaultButton = \"Continue\";\n\t}\n\n\tif(allow_suppress)\n\t\tstrcat_s(buttons, sizeof(buttons), \"Suppress:101,\");\n\n\tstrcat_s(buttons, sizeof(buttons), \"Break:102,Debugger:103,Exit:104\");\n\n\t// Since execv wants non-const strings, we strdup them all here\n\t// and will clean them up later (except in the child process where\n\t// memory leaks don't matter)\n\tchar* const argv[] = {\n\t\tstrdup(cmd),\n\t\tstrdup(\"-geometry\"), strdup(\"x500\"), // set height so the box will always be very visible\n\t\tstrdup(\"-title\"), strdup(\"0 A.D. message\"), // TODO: maybe shouldn't hard-code app name\n\t\tstrdup(\"-buttons\"), strdup(buttons),\n\t\tstrdup(\"-default\"), strdup(defaultButton),\n\t\tstrdup(message.c_str()),\n\t\tNULL\n\t};\n\n\tpid_t cpid = fork();\n\tif(cpid == -1)\n\t{\n\t\tfor(char* const* a = argv; *a; ++a)\n\t\t\tfree(*a);\n\t\treturn ERI_NOT_IMPLEMENTED;\n\t}\n\n\tif(cpid == 0)\n\t{\n\t\t// This is the child process\n\n\t\t// Set ASCII charset, to avoid font warnings from xmessage\n\t\tsetenv(\"LC_ALL\", \"C\", 1);\n\n\t\t// NOTE: setenv is not async-signal-safe, so we shouldn't really use\n\t\t// it here (it might want some mutex that was held by another thread\n\t\t// in the parent process and that will never be freed within this\n\t\t// process). But setenv/getenv are not guaranteed reentrant either,\n\t\t// and this error-reporting function might get called from a non-main\n\t\t// thread, so we can't just call setenv before forking as it might\n\t\t// break the other threads. And we can't just clone environ manually\n\t\t// inside the parent thread and use execve, because other threads might\n\t\t// be calling setenv and will break our iteration over environ.\n\t\t// In the absence of a good easy solution, and given that this is only\n\t\t// an error-reporting function and shouldn't get called frequently,\n\t\t// we'll just do setenv after the fork and hope that it fails\n\t\t// extremely rarely.\n\n\t\texecv(cmd, argv);\n\n\t\t// If exec returns, it failed\n\t\t//fprintf(stderr, \"Error running %s: %d\\n\", cmd, errno);\n\t\texit(-1);\n\t}\n\n\t// This is the parent process\n\n\t// Avoid memory leaks\n\tfor(char* const* a = argv; *a; ++a)\n\t\tfree(*a);\n\n\tint status = 0;\n\twaitpid(cpid, &status, 0);\n\n\t// If it didn't exist successfully, fall back to the non-GUI prompt\n\tif(!WIFEXITED(status))\n\t\treturn ERI_NOT_IMPLEMENTED;\n\n\tswitch(WEXITSTATUS(status))\n\t{\n\tcase 103: // Debugger\n\t\tudbg_launch_debugger();\n\t\t//-fallthrough\n\n\tcase 102: // Break\n\t\tif(manual_break)\n\t\t\treturn ERI_BREAK;\n\t\tdebug_break();\n\t\treturn ERI_CONTINUE;\n\n\tcase 100: // Continue\n\t\tif(!no_continue)\n\t\t\treturn ERI_CONTINUE;\n\t\t// continue isn't allowed, so this was invalid input.\n\t\treturn ERI_NOT_IMPLEMENTED;\n\n\tcase 101: // Suppress\n\t\tif(allow_suppress)\n\t\t\treturn ERI_SUPPRESS;\n\t\t// suppress isn't allowed, so this was invalid input.\n\t\treturn ERI_NOT_IMPLEMENTED;\n\n\tcase 104: // Exit\n\t\tabort();\n\t\treturn ERI_EXIT;\t// placebo; never reached\n\n\t}\n\n\t// Unexpected return value - fall back to the non-GUI prompt\n\treturn ERI_NOT_IMPLEMENTED;\n}\n#endif\n\nErrorReactionInternal sys_display_error(const wchar_t* text, size_t flags)\n{\n\tdebug_printf(\"%s\\n\\n\", utf8_from_wstring(text).c_str());\n\n\tconst bool manual_break   = (flags & DE_MANUAL_BREAK  ) != 0;\n\tconst bool allow_suppress = (flags & DE_ALLOW_SUPPRESS) != 0;\n\tconst bool no_continue    = (flags & DE_NO_CONTINUE   ) != 0;\n\n\t// Try the GUI prompt if possible\n\tErrorReactionInternal ret = try_gui_display_error(text, manual_break, allow_suppress, no_continue);\n\tif (ret != ERI_NOT_IMPLEMENTED)\n\t\treturn ret;\n\n#if OS_ANDROID\n\t// Android has no easy way to get user input here,\n\t// so continue or exit automatically\n\tif(no_continue)\n\t\tabort();\n\telse\n\t\treturn ERI_CONTINUE;\n#else\n\t// Otherwise fall back to the terminal-based input\n\n\t// Loop until valid input given:\n\tfor(;;)\n\t{\n\t\tif(!no_continue)\n\t\t\tprintf(\"(C)ontinue, \");\n\t\tif(allow_suppress)\n\t\t\tprintf(\"(S)uppress, \");\n\t\tprintf(\"(B)reak, Launch (D)ebugger, or (E)xit?\\n\");\n\t\t// TODO Should have some kind of timeout here.. in case you're unable to\n\t\t// access the controlling terminal (As might be the case if launched\n\t\t// from an xterm and in full-screen mode)\n\t\tint c = getchar();\n\t\t// note: don't use tolower because it'll choke on EOF\n\t\tswitch(c)\n\t\t{\n\t\tcase EOF:\n\t\tcase 'd': case 'D':\n\t\t\tudbg_launch_debugger();\n\t\t\t//-fallthrough\n\n\t\tcase 'b': case 'B':\n\t\t\tif(manual_break)\n\t\t\t\treturn ERI_BREAK;\n\t\t\tdebug_break();\n\t\t\treturn ERI_CONTINUE;\n\n\t\tcase 'c': case 'C':\n\t\t\tif(!no_continue)\n\t\t\t\treturn ERI_CONTINUE;\n\t\t\t// continue isn't allowed, so this was invalid input. loop again.\n\t\t\tbreak;\n\t\tcase 's': case 'S':\n\t\t\tif(allow_suppress)\n\t\t\t\treturn ERI_SUPPRESS;\n\t\t\t// suppress isn't allowed, so this was invalid input. loop again.\n\t\t\tbreak;\n\n\t\tcase 'e': case 'E':\n\t\t\tabort();\n\t\t\treturn ERI_EXIT;\t// placebo; never reached\n\t\t}\n\t}\n#endif\n}\n\n\nStatus sys_StatusDescription(int err, wchar_t* buf, size_t max_chars)\n{\n\tUNUSED2(err);\n\tUNUSED2(buf);\n\tUNUSED2(max_chars);\n\n\t// don't need to do anything: lib/errors.cpp already queries\n\t// libc's strerror(). if we ever end up needing translation of\n\t// e.g. Qt or X errors, that'd go here.\n\treturn ERR::FAIL;\n}\n\n// note: just use the sector size: Linux aio doesn't really care about\n// the alignment of buffers/lengths/offsets, so we'll just pick a\n// sane value and not bother scanning all drives.\nsize_t sys_max_sector_size()\n{\n\t// users may call us more than once, so cache the results.\n\tstatic size_t cached_sector_size;\n\tif(!cached_sector_size)\n\t\tcached_sector_size = sysconf(_SC_PAGE_SIZE);\n\treturn cached_sector_size;\n}\n\nstd::wstring sys_get_user_name()\n{\n\t// Prefer LOGNAME, fall back on getlogin\n\n\tconst char* logname = getenv(\"LOGNAME\");\n\tif (logname && strcmp(logname, \"\") != 0)\n\t\treturn std::wstring(logname, logname + strlen(logname));\n\t// TODO: maybe we should do locale conversion?\n\n#if OS_ANDROID\n#warning TODO: sys_get_user_name: do something more appropriate and more thread-safe\n\tchar* buf = getlogin();\n\tif (buf)\n\t\treturn std::wstring(buf, buf + strlen(buf));\n#else\n\tchar buf[256];\n\tif (getlogin_r(buf, ARRAY_SIZE(buf)) == 0)\n\t\treturn std::wstring(buf, buf + strlen(buf));\n#endif\n\n\treturn L\"\";\n}\n\nStatus sys_generate_random_bytes(u8* buf, size_t count)\n{\n\tFILE* f = fopen(\"/dev/urandom\", \"rb\");\n\tif (!f)\n\t\tWARN_RETURN(ERR::FAIL);\n\n\twhile (count)\n\t{\n\t\tsize_t numread = fread(buf, 1, count, f);\n\t\tif (numread == 0)\n\t\t\tWARN_RETURN(ERR::FAIL);\n\t\tbuf += numread;\n\t\tcount -= numread;\n\t}\n\n\tfclose(f);\n\n\treturn INFO::OK;\n}\n\nStatus sys_get_proxy_config(const std::wstring& UNUSED(url), std::wstring& UNUSED(proxy))\n{\n\treturn INFO::SKIPPED;\n}\n\nStatus sys_open_url(const std::string& url)\n{\n\tpid_t pid = fork();\n\tif (pid < 0)\n\t{\n\t\tdebug_warn(L\"Fork failed\");\n\t\treturn ERR::FAIL;\n\t}\n\telse if (pid == 0)\n\t{\n\t\t// we are the child\n\n\t\texeclp(URL_OPEN_COMMAND, URL_OPEN_COMMAND, url.c_str(), (const char*)NULL);\n\n\t\tdebug_printf(\"Failed to run '\" URL_OPEN_COMMAND \"' command\\n\");\n\n\t\t// We can't call exit() because that'll try to free resources which were the parent's,\n\t\t// so just abort here\n\t\tabort();\n\t}\n\telse\n\t{\n\t\t// we are the parent\n\n\t\t// TODO: maybe we should wait for the child and make sure it succeeded\n\n\t\treturn INFO::OK;\n\t}\n}\n\nFILE* sys_OpenFile(const OsPath& pathname, const char* mode)\n{\n\treturn fopen(OsString(pathname).c_str(), mode);\n}\n"
  },
  {
    "path": "fpsgame/gui/sysdep/os/unix/unix.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#ifndef sysdep_unix_unix_h__\n#define sysdep_unix_unix_h__\n\n#endif\n"
  },
  {
    "path": "fpsgame/gui/sysdep/os/unix/unix_executable_pathname.cpp",
    "content": "/* Copyright (c) 2014 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#include \"precompiled.h\"\n\n#include \"lib/sysdep/sysdep.h\"\n\n#define GNU_SOURCE\n#include \"mocks/dlfcn.h\"\n#include \"mocks/unistd.h\"\n\n#include <cstdio>\n\nOsPath unix_ExecutablePathname()\n{\n\t// Find the executable's filename\n\tDl_info dl_info;\n\tmemset(&dl_info, 0, sizeof(dl_info));\n\tif (!T::dladdr((void *)sys_ExecutablePathname, &dl_info) || !dl_info.dli_fname)\n\t\treturn OsPath();\n\tconst char* path = dl_info.dli_fname;\n\n\t// If this looks like an absolute path, use realpath to get the normalized\n\t// path (with no '.' or '..')\n\tif (path[0] == '/')\n\t{\n\t\tchar resolved[PATH_MAX];\n\t\tif (!realpath(path, resolved))\n\t\t\treturn OsPath();\n\t\treturn resolved;\n\t}\n\n\t// If this looks like a relative path, resolve against cwd and use realpath\n\tif (strchr(path, '/'))\n\t{\n\t\tchar cwd[PATH_MAX];\n\t\tif (!T::getcwd(cwd, PATH_MAX))\n\t\t\treturn OsPath();\n\n\t\tchar absolute[PATH_MAX];\n\t\tint ret = snprintf(absolute, PATH_MAX, \"%s/%s\", cwd, path);\n\t\tif (ret < 0 || ret >= PATH_MAX)\n\t\t\treturn OsPath(); // path too long, or other error\n\t\tchar resolved[PATH_MAX];\n\t\tif (!realpath(absolute, resolved))\n\t\t\treturn OsPath();\n\t\treturn resolved;\n\t}\n\n\t// If it's not a path at all, i.e. it's just a filename, we'd\n\t// probably have to search through PATH to find it.\n\t// That's complex and should be uncommon, so don't bother.\n\treturn OsPath();\n}\n"
  },
  {
    "path": "fpsgame/gui/sysdep/os/unix/unix_executable_pathname.h",
    "content": "/* Copyright (c) 2014 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#ifndef INCLUDED_UNIX_EXECUTABLE_PATHNAME\n#define INCLUDED_UNIX_EXECUTABLE_PATHNAME\n\nOsPath unix_ExecutablePathname();\n\n#endif  // INCLUDED_UNIX_EXECUTABLE_PATHNAME\n"
  },
  {
    "path": "fpsgame/gui/sysdep/os/unix/unuma.cpp",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#include \"precompiled.h\"\n#include \"lib/sysdep/numa.h\"\n\n#include \"lib/bits.h\"\n#include \"lib/sysdep/os_cpu.h\"\n\nsize_t numa_NumNodes()\n{\n\treturn 1;\n}\n\nsize_t numa_NodeFromProcessor(size_t UNUSED(processor))\n{\n\treturn 0;\n}\n\nuintptr_t numa_ProcessorMaskFromNode(size_t node)\n{\n\tENSURE(node == 0);\n\treturn bit_mask<uintptr_t>(os_cpu_NumProcessors());\n}\n\nsize_t numa_AvailableMemory(size_t node)\n{\n\tENSURE(node == 0);\n\treturn os_cpu_MemoryAvailable();\n}\n\ndouble numa_Factor()\n{\n\treturn 1.0;\n}\n\nbool numa_IsMemoryInterleaved()\n{\n\treturn false;\n}\n"
  },
  {
    "path": "fpsgame/gui/sysdep/os/unix/unuma.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#ifndef INCLUDED_UNUMA\n#define INCLUDED_UNUMA\n\n#endif\t// #ifndef INCLUDED_UNUMA\n"
  },
  {
    "path": "fpsgame/gui/sysdep/os/unix/uvm.cpp",
    "content": "/* Copyright (c) 2011 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#include \"precompiled.h\"\n#include \"lib/sysdep/vm.h\"\n\n#include \"lib/alignment.h\"\n\n// \"anonymous\" effectively means mapping /dev/zero, but is more efficient.\n// MAP_ANONYMOUS is not in SUSv3, but is a very common extension.\n// unfortunately, MacOS X only defines MAP_ANON, which Solaris says is\n// deprecated. workaround there: define MAP_ANONYMOUS in terms of MAP_ANON.\n#ifndef MAP_ANONYMOUS\n# define MAP_ANONYMOUS MAP_ANON\n#endif\n\nstatic const int mmap_flags = MAP_PRIVATE|MAP_ANONYMOUS;\n\nnamespace vm {\n\nvoid* ReserveAddressSpace(size_t size, size_t UNUSED(commitSize), PageType UNUSED(pageType), int UNUSED(prot))\n{\n\terrno = 0;\n\tvoid* p = mmap(0, size, PROT_NONE, mmap_flags|MAP_NORESERVE, -1, 0);\n\tif(p == MAP_FAILED)\n\t\treturn 0;\n\treturn p;\n}\n\nvoid ReleaseAddressSpace(void* p, size_t size)\n{\n\tENSURE(size != 0);\n\n\terrno = 0;\n\tif(munmap(p, size) != 0)\n\t\tDEBUG_WARN_ERR(StatusFromErrno());\n}\n\n\nbool Commit(uintptr_t address, size_t size, PageType UNUSED(pageType), int prot)\n{\n\tif(prot == PROT_NONE)\t// would be understood as a request to decommit\n\t{\n\t\tDEBUG_WARN_ERR(ERR::INVALID_PARAM);\n\t\treturn false;\n\t}\n\n\terrno = 0;\n\tif(mmap((void*)address, size, prot, mmap_flags|MAP_FIXED, -1, 0) == MAP_FAILED)\n\t\treturn false;\n\n\tif(prot != (PROT_READ|PROT_WRITE))\n\t\t(void)Protect(address, size, prot);\n\n\treturn true;\n}\n\nbool Decommit(uintptr_t address, size_t size)\n{\n\terrno = 0;\n\tif(mmap((void*)address, size, PROT_NONE, mmap_flags|MAP_NORESERVE|MAP_FIXED, -1, 0) == MAP_FAILED)\n\t\treturn false;\n\treturn true;\n}\n\n\nbool Protect(uintptr_t address, size_t size, int prot)\n{\n\terrno = 0;\n\tif(mprotect((void*)address, size, prot) != 0)\n\t{\n\t\tDEBUG_WARN_ERR(ERR::FAIL);\n\t\treturn false;\n\t}\n\treturn true;\n}\n\n\nvoid* Allocate(size_t size, PageType pageType, int prot)\n{\n\tvoid* p = ReserveAddressSpace(size);\n\tif(!p)\n\t\treturn 0;\n\n\tif(!Commit(uintptr_t(p), size, pageType, prot))\n\t{\n\t\tReleaseAddressSpace(p, size);\n\t\treturn 0;\n\t}\n\n\treturn p;\n}\n\nvoid Free(void* p, size_t size)\n{\n\t// (only the Windows implementation distinguishes between Free and ReleaseAddressSpace)\n\tvm::ReleaseAddressSpace(p, size);\n}\n\n\nvoid BeginOnDemandCommits()\n{\n\t// not yet implemented, but possible with a signal handler\n}\n\nvoid EndOnDemandCommits()\n{\n\t// not yet implemented, but possible with a signal handler\n}\n\n\nvoid DumpStatistics()\n{\n\t// we haven't collected any statistics\n}\n\n}\t// namespace vm\n"
  },
  {
    "path": "fpsgame/gui/sysdep/os/unix/x/x.cpp",
    "content": "/* Copyright (c) 2013 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n// X Window System-specific code\n\n#include \"precompiled.h\"\n\n#if OS_LINUX || OS_BSD\n# define HAVE_X 1\n#else\n# define HAVE_X 0\n#endif\n\n#if HAVE_X\n\n#include \"lib/debug.h\"\n#include \"lib/utf8.h\"\n#include \"lib/sysdep/gfx.h\"\n#include \"lib/sysdep/cursor.h\"\n\n#include \"ps/VideoMode.h\"\n\n#define Cursor X__Cursor\n\n#include <Xlib.h>\n#include <stdlib.h>\n#include <Xatom.h>\n#include <Xcursor/Xcursor.h>\n\n#include \"SDL.h\"\n#include \"SDL_syswm.h\"\n\n#include <algorithm>\n#undef Cursor\n#undef Status\n\nstatic Display *g_SDL_Display;\nstatic Window g_SDL_Window;\nstatic wchar_t *selection_data=NULL;\nstatic size_t selection_size=0;\n\nnamespace gfx {\n\nStatus GetVideoMode(int* xres, int* yres, int* bpp, int* freq)\n{\n\tDisplay* disp = XOpenDisplay(0);\n\tif(!disp)\n\t\tWARN_RETURN(ERR::FAIL);\n\n\tint screen = XDefaultScreen(disp);\n\t\n\t/* 2004-07-13\n\tNOTE: The XDisplayWidth/Height functions don't actually return the current\n\tdisplay mode - they return the size of the root window. This means that\n\tusers with \"Virtual Desktops\" bigger than what their monitors/graphics\n\tcard can handle will have to set their 0AD screen resolution manually.\n\t\n\tThere's supposed to be an X extension that can give you the actual display\n\tmode, probably including refresh rate info etc, but it's not worth\n\tresearching and implementing that at this stage.\n\t*/\n\t\n\tif(xres)\n\t\t*xres = XDisplayWidth(disp, screen);\n\tif(yres)\n\t\t*yres = XDisplayHeight(disp, screen);\n\tif(bpp)\n\t\t*bpp = XDefaultDepth(disp, screen);\n\tif(freq)\n\t\t*freq = 0;\n\tXCloseDisplay(disp);\n\treturn INFO::OK;\n}\n\n\nStatus GetMonitorSize(int& width_mm, int& height_mm)\n{\n\tDisplay* disp = XOpenDisplay(0);\n\tif(!disp)\n\t\tWARN_RETURN(ERR::FAIL);\n\n\tint screen = XDefaultScreen(disp);\n\t\n\twidth_mm = XDisplayWidthMM(disp, screen);\n\theight_mm = XDisplayHeightMM(disp, screen);\n\t\n\tXCloseDisplay(disp);\n\treturn INFO::OK;\n}\n\n}\t// namespace gfx\n\n\nstatic bool get_wminfo(SDL_SysWMinfo& wminfo)\n{\n\tSDL_VERSION(&wminfo.version);\n\n\tconst int ret = SDL_GetWindowWMInfo(g_VideoMode.GetWindow(), &wminfo);\n\n\tif(ret == 1)\n\t\treturn true;\n\n\tif(ret == -1)\n\t{\n\t\tdebug_printf(\"SDL_GetWMInfo failed\\n\");\n\t\treturn false;\n\t}\n\tif(ret == 0)\n\t{\n\t\tdebug_printf(\"SDL_GetWMInfo is not implemented on this platform\\n\");\n\t\treturn false;\n\t}\n\n\tdebug_printf(\"SDL_GetWMInfo returned an unknown value: %d\\n\", ret);\n\treturn false;\n}\n\n/*\nOh, boy, this is heavy stuff...\n\n<User-End Stuff - Definitions and Conventions>\nhttp://www.freedesktop.org/standards/clipboards-spec/clipboards.txt\n<Technical, API stuff>\nhttp://www.mail-archive.com/xfree86@xfree86.org/msg15594.html\nhttp://michael.toren.net/mirrors/doc/X-copy+paste.txt\nhttp://devdocs.wesnoth.org/clipboard_8cpp-source.html\nhttp://tronche.com/gui/x/xlib/window-information/XGetWindowProperty.html\nhttp://www.jwz.org/doc/x-cut-and-paste.html\n\nThe basic run-down on X Selection Handling:\n* One window owns the \"current selection\" at any one time\n* Accessing the Selection (i.e. \"paste\"), Step-by-step\n\t* Ask the X server for the current selection owner\n\t* Ask the selection owner window to convert the selection into a format\n\twe can understand (XA_STRING - Latin-1 string - for now)\n\t* The converted result is stored as a property of the *selection owner*\n\twindow. It is possible to specify the current application window as the\n\ttarget - but that'd require some X message handling... easier to skip that..\n\t* The final step is to acquire the property value of the selection owner\n\twindow\n\nNotes:\nAn \"Atom\" is a server-side object that represents a string by an index into some\nkind of table or something. Pretty much like a handle that refers to one unique\nstring. Atoms are used here to refer to property names and value formats.\n\nExpansions:\n* Implement UTF-8 format support (should be interresting for international users)\n\n*/\nwchar_t *sys_clipboard_get()\n{\n\tDisplay *disp=XOpenDisplay(NULL);\n\tif(!disp)\n\t\treturn NULL;\n\n\t// We use CLIPBOARD as the default, since the CLIPBOARD semantics are much\n\t// closer to windows semantics.\n\tAtom selSource=XInternAtom(disp, \"CLIPBOARD\", False);\n\n\tWindow selOwner=XGetSelectionOwner(disp, selSource);\n\tif(selOwner == None)\n\t{\n\t\t// However, since many apps don't use CLIPBOARD, but use PRIMARY instead\n\t\t// we use XA_PRIMARY as a fallback clipboard. This is true for xterm,\n\t\t// for example.\n\t\tselSource=XA_PRIMARY;\n\t\tselOwner=XGetSelectionOwner(disp, selSource);\n\t}\n\tif(selOwner != None) {\n\t\tAtom pty=XInternAtom(disp, \"SelectionPropertyTemp\", False);\n\t\tXConvertSelection(disp, selSource, XA_STRING, pty, selOwner, CurrentTime);\n\t\tXFlush(disp);\n\t\t\n\t\tAtom type;\n\t\tint format=0, result=0;\n\t\tunsigned long len=0, bytes_left=0, dummy=0;\n\t\tu8 *data=NULL;\n\t\t\n\t\t// Get the length of the property and some attributes\n\t\t// bytes_left will contain the length of the selection\n\t\tresult = XGetWindowProperty (disp, selOwner, pty,\n\t\t\t0, 0,\t\t\t// offset - len\n\t\t\t0,\t\t\t\t// Delete 0==FALSE\n\t\t\tAnyPropertyType,//flag\n\t\t\t&type,\t\t\t// return type\n\t\t\t&format,\t\t// return format\n\t\t\t&len, &bytes_left,\n\t\t\t&data);\n\t\tif(result != Success)\n\t\t\tdebug_printf(\"clipboard_get: result: %d type:%lu len:%lu format:%d bytes_left:%lu\\n\", \n\t\t\t\tresult, type, len, format, bytes_left);\n\t\tif(result == Success && bytes_left > 0)\n\t\t{\n\t\t\tresult = XGetWindowProperty (disp, selOwner, \n\t\t\t\tpty, 0, bytes_left, 0,\n\t\t\t\tAnyPropertyType, &type, &format,\n\t\t\t\t&len, &dummy, &data);\n\t\t\t\n\t\t\tif(result == Success)\n\t\t\t{\n\t\t\t\tdebug_printf(\"clipboard_get: XGetWindowProperty succeeded, returning data\\n\");\n\t\t\t\tdebug_printf(\"clipboard_get: data was: \\\"%s\\\", type was %lu, XA_STRING atom is %lu\\n\", (char *)data, type, XA_STRING);\n\t\t\t\t\n\t\t\t\tif(type == XA_STRING) //Latin-1: Just copy into low byte of wchar_t\n\t\t\t\t{\n\t\t\t\t\twchar_t *ret=(wchar_t *)malloc((bytes_left+1)*sizeof(wchar_t));\n\t\t\t\t\tstd::copy(data, data+bytes_left, ret);\n\t\t\t\t\tret[bytes_left]=0;\n\t\t\t\t\treturn ret;\n\t\t\t\t}\n\t\t\t\t// TODO: Handle UTF8 strings\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tdebug_printf(\"clipboard_get: XGetWindowProperty failed!\\n\");\n\t\t\t\treturn NULL;\n\t\t\t}\n\t\t}\n\t}\n\t\n\treturn NULL;\n}\n\nStatus sys_clipboard_free(wchar_t *clip_buf)\n{\n\tfree(clip_buf);\n\treturn INFO::OK;\n}\n\n/**\n * An SDL Event filter that intercepts other applications' requests for the\n * X selection buffer.\n *\n * @see x11_clipboard_init\n * @see sys_clipboard_set\n */\nint clipboard_filter(void* UNUSED(userdata), SDL_Event* event)\n{\n\t/* Pass on all non-window manager specific events immediately */\n\t/* And do nothing if we don't actually have a clip-out to send out */\n\tif(event->type != SDL_SYSWMEVENT || !selection_data)\n\t\treturn 1;\n\n\t/* Handle window-manager specific clipboard events */\n\t/* (Note: libsdl must be compiled with X11 support (SDL_VIDEO_DRIVER_X11 in SDL_config.h) -\n\t   else you'll get errors like \"'struct SDL_SysWMmsg' has no member named 'xevent'\") */\n\tXEvent* xevent = &event->syswm.msg->msg.x11.event;\n\tswitch(xevent->type) {\n\t\t/* Copy the selection from our buffer to the requested property, and\n\t\tconvert to the requested target format */\n\t\tcase SelectionRequest: {\n\t\t\tXSelectionRequestEvent *req;\n\t\t\tXEvent sevent;\n\n\t\t\treq = &xevent->xselectionrequest;\n\t\t\tsevent.xselection.type = SelectionNotify;\n\t\t\tsevent.xselection.display = req->display;\n\t\t\tsevent.xselection.selection = req->selection;\n\t\t\tsevent.xselection.target = req->target;\n\t\t\tsevent.xselection.property = None;\n\t\t\tsevent.xselection.requestor = req->requestor;\n\t\t\tsevent.xselection.time = req->time;\n\t\t\t// Simply strip all non-Latin1 characters and replace with '?'\n\t\t\t// We should support XA_UTF8\n\t\t\tif(req->target == XA_STRING)\n\t\t\t{\n\t\t\t\tsize_t size = wcslen(selection_data);\n\t\t\t\tu8* buf = (u8*)alloca(size);\n\t\t\t\t\n\t\t\t\tfor(size_t i = 0; i < size; i++)\n\t\t\t\t{\n\t\t\t\t\tbuf[i] = selection_data[i] < 0x100 ? selection_data[i] : '?';\n\t\t\t\t}\n\t\t\t\n\t\t\t\tXChangeProperty(g_SDL_Display, req->requestor, req->property,\n\t\t\t\t\tsevent.xselection.target, 8, PropModeReplace,\n\t\t\t\t\tbuf, size);\n\t\t\t\tsevent.xselection.property = req->property;\n\t\t\t}\n\t\t\t// TODO Add more target formats\n\t\t\tXSendEvent(g_SDL_Display, req->requestor, False, 0, &sevent);\n\t\t\tXSync(g_SDL_Display, False);\n\t\t}\n\t\tbreak;\n\t}\n\n\treturn 1;\n}\n\n/**\n * Initialization for X clipboard handling, called on-demand by\n * sys_clipboard_set.\n */\nStatus x11_clipboard_init()\n{\n\tSDL_SysWMinfo info;\n\n\tif(get_wminfo(info))\n\t{\n\t\t/* Save the information for later use */\n\t\tif(info.subsystem == SDL_SYSWM_X11)\n\t\t{\n\t\t\tg_SDL_Display = info.info.x11.display;\n\t\t\tg_SDL_Window = info.info.x11.window;\n\n\t\t\t/* Enable the special window hook events */\n\t\t\tSDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);\n\t\t\tSDL_SetEventFilter(clipboard_filter, NULL);\n\n\t\t\treturn INFO::OK;\n\t\t}\n\t\telse\n\t\t{\n\t\t\treturn ERR::FAIL;\n\t\t}\n\t}\n\n\treturn INFO::OK;\n}\n\n/**\n * Set the Selection (i.e. \"copy\")\n *\n * Step-by-step (X11)\n * <ul>\n * <li>Store the selection text in a local buffer\n * <li>Tell the X server that we want to own the selection\n * <li>Listen for Selection events and respond to them as appropriate\n * </ul>\n */\nStatus sys_clipboard_set(const wchar_t *str)\n{\n\tONCE(x11_clipboard_init());\n\n\tdebug_printf(\"sys_clipboard_set: %s\\n\", utf8_from_wstring(str).c_str());\n\n\tif(selection_data)\n\t{\n\t\tfree(selection_data);\n\t\tselection_data = NULL;\n\t}\n\t\n\tselection_size = (wcslen(str)+1)*sizeof(wchar_t);\n\tselection_data = (wchar_t *)malloc(selection_size);\n\twcscpy(selection_data, str);\n\n\t// Like for the clipboard_get code above, we rather use CLIPBOARD than\n\t// PRIMARY - more windows'y behaviour there.\n\tAtom clipboard_atom = XInternAtom(g_SDL_Display, \"CLIPBOARD\", False);\n\tXSetSelectionOwner(g_SDL_Display, clipboard_atom, g_SDL_Window, CurrentTime);\n\tXSetSelectionOwner(g_SDL_Display, XA_PRIMARY, g_SDL_Window, CurrentTime);\n\n\t// SDL2 doesn't have a lockable event thread, so it just uses\n\t// XSync directly instead of lock_func/unlock_func\n\tXSync(g_SDL_Display, False);\n\n\treturn INFO::OK;\n}\n\nstruct sys_cursor_impl\n{\n\tXcursorImage* image;\n\tX__Cursor cursor;\n};\n\nstatic XcursorPixel cursor_pixel_to_x11_format(const XcursorPixel& bgra_pixel)\n{\n\tBOOST_STATIC_ASSERT(sizeof(XcursorPixel) == 4 * sizeof(u8));\n\tXcursorPixel ret;\n\tu8* dst = reinterpret_cast<u8*>(&ret);\n\tconst u8* b = reinterpret_cast<const u8*>(&bgra_pixel);\n\tconst u8 a = b[3];\n\n\tfor(size_t i = 0; i < 3; ++i)\n\t\t*dst++ = (b[i]) * a / 255;\n\t*dst = a;\n\treturn ret;\n}\n\nStatus sys_cursor_create(int w, int h, void* bgra_img, int hx, int hy, sys_cursor* cursor)\n{\n\tdebug_printf(\"sys_cursor_create: using Xcursor to create %d x %d cursor\\n\", w, h);\n\tXcursorImage* image = XcursorImageCreate(w, h);\n\tif(!image)\n\t\tWARN_RETURN(ERR::FAIL);\n\n\tconst XcursorPixel* bgra_img_begin = reinterpret_cast<XcursorPixel*>(bgra_img);\n\tstd::transform(bgra_img_begin, bgra_img_begin + (w*h), image->pixels,\n\t\tcursor_pixel_to_x11_format);\n\timage->xhot = hx;\n\timage->yhot = hy;\n\n\tSDL_SysWMinfo wminfo;\n\tif(!get_wminfo(wminfo))\n\t\tWARN_RETURN(ERR::FAIL);\n\n\tsys_cursor_impl* impl = new sys_cursor_impl;\n\timpl->image = image;\n\timpl->cursor = XcursorImageLoadCursor(wminfo.info.x11.display, image);\n\tif(impl->cursor == None)\n\t\tWARN_RETURN(ERR::FAIL);\n\n\t*cursor = static_cast<sys_cursor>(impl);\n\treturn INFO::OK;\n}\n\n// returns a dummy value representing an empty cursor\nStatus sys_cursor_create_empty(sys_cursor* cursor)\n{\n\tstatic u8 transparent_bgra[] = { 0x0, 0x0, 0x0, 0x0 };\n\n\treturn sys_cursor_create(1, 1, static_cast<void*>(transparent_bgra), 0, 0, cursor);\n}\n\n// replaces the current system cursor with the one indicated. need only be\n// called once per cursor; pass 0 to restore the default.\nStatus sys_cursor_set(sys_cursor cursor)\n{\n\tif(!cursor) // restore default cursor\n\t\tSDL_ShowCursor(SDL_DISABLE);\n\telse\n\t{\n\t\tSDL_SysWMinfo wminfo;\n\t\tif(!get_wminfo(wminfo))\n\t\t\tWARN_RETURN(ERR::FAIL);\n\n\t\tif(wminfo.subsystem != SDL_SYSWM_X11)\n\t\t\tWARN_RETURN(ERR::FAIL);\n\n\t\tSDL_ShowCursor(SDL_ENABLE);\n\n\t\tWindow window;\n\t\tif(wminfo.info.x11.window)\n\t\t\twindow = wminfo.info.x11.window;\n\t\telse\n\t\t\tWARN_RETURN(ERR::FAIL);\n\n\t\tXDefineCursor(wminfo.info.x11.display, window,\n\t\t\tstatic_cast<sys_cursor_impl*>(cursor)->cursor);\n\t\t// SDL2 doesn't have a lockable event thread, so it just uses\n\t\t// XSync directly instead of lock_func/unlock_func\n\t\tXSync(wminfo.info.x11.display, False);\n\t}\n\n\treturn INFO::OK;}\n\n// destroys the indicated cursor and frees its resources. if it is\n// currently the system cursor, the default cursor is restored first.\nStatus sys_cursor_free(sys_cursor cursor)\n{\n\t// bail now to prevent potential confusion below; there's nothing to do.\n\tif(!cursor)\n\t\treturn INFO::OK;\n\n\tsys_cursor_set(0); // restore default cursor\n\tsys_cursor_impl* impl = static_cast<sys_cursor_impl*>(cursor);\n\n\tXcursorImageDestroy(impl->image);\n\n\tSDL_SysWMinfo wminfo;\n\tif(!get_wminfo(wminfo))\n\t\treturn ERR::FAIL;\n\tXFreeCursor(wminfo.info.x11.display, impl->cursor);\n\n\tdelete impl;\n\n\treturn INFO::OK;\n}\n\nStatus sys_cursor_reset()\n{\n\treturn INFO::OK;\n}\n\n\n#endif\t// #if HAVE_X\n"
  },
  {
    "path": "fpsgame/gui/sysdep/os/win/aken/aken.c",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n// note: staticdv cannot yet check C++ code.\n\n#include <ntddk.h>\n#include \"aken.h\"\n#include \"intrinsics.h\"\n\n#define WIN32_NAME L\"\\\\DosDevices\\\\Aken\"\n#define DEVICE_NAME L\"\\\\Device\\\\Aken\"\n\n// placate PREfast\nDRIVER_INITIALIZE DriverEntry;\n__drv_dispatchType(IRP_MJ_CREATE) DRIVER_DISPATCH AkenCreate;\n__drv_dispatchType(IRP_MJ_CLOSE) DRIVER_DISPATCH AkenClose;\n__drv_dispatchType(IRP_MJ_DEVICE_CONTROL) DRIVER_DISPATCH AkenDeviceControl;\nDRIVER_UNLOAD AkenUnload;\n\n// this driver isn't large, but it's still slightly nicer to make its\n// functions pageable and thus not waste precious non-paged pool.\n// #pragma code_seg is more convenient than specifying alloc_text for\n// every other function.\n#pragma alloc_text(INIT, DriverEntry)\t// => discardable\n#pragma code_seg(push, \"PAGE\")\n\n\n//-----------------------------------------------------------------------------\n// memory mapping\n//-----------------------------------------------------------------------------\n\n/*\nthere are three approaches to mapping physical memory:\n(http://www.microsoft.com/whdc/driver/kernel/mem-mgmt.mspx)\n\n- MmMapIoSpace (http://support.microsoft.com/kb/189327/en-us). despite the\n  name, it maps physical pages of any kind by allocating PTEs. very easy to\n  implement, but occupies precious kernel address space. possible bugs:\n    http://www.osronline.com/showThread.cfm?link=96737\n    http://support.microsoft.com/kb/925793/en-us\n\n- ZwMapViewOfSection of PhysicalMemory (http://tinyurl.com/yozmgy).\n  the code is a bit bulky, but the WinXP API prevents mapping pages with\n  conflicting attributes (see below).\n\n- MmMapLockedPagesSpecifyCache or MmGetSystemAddressForMdlSafe\n  (http://www.osronline.com/article.cfm?id=423). note: the latter is a macro\n  that calls the former. this is the 'normal' and fully documented way,\n  but it doesn't appear able to map a fixed physical address.\n  (MmAllocatePagesForMdl understandably doesn't work since some pages we\n  want to map are marked as unavailable for allocation, and I don't see\n  another documented way to fill an MDL with PFNs.)\n\nour choice here is forced by a very insidious issue. if someone else has\nalready mapped a page with different attributes (e.g. cacheable), TLBs\nmay end up corrupted, leading to disaster. the search for a documented\nmeans of accessing the page frame database (to check if mapped anywhere\nand determine the previously set attributes) has not borne fruit, so we\nmust use ZwMapViewOfSection. (if taking this up again, see\nhttp://www.woodmann.com/forum/archive/index.php/t-6516.html )\n\nnote that we guess if the page will have been mapped as cacheable and\neven try the opposite if that turns out to have been incorrect.\n*/\n\nstatic int IsMemoryUncacheable(DWORD64 physicalAddress64)\n{\n\tPAGED_CODE();\n\n\t// original PC memory - contains BIOS\n\tif(physicalAddress64 < 0x100000)\n\t\treturn 1;\n\n\treturn 0;\n}\n\nstatic NTSTATUS AkenMapPhysicalMemory(const DWORD64 physicalAddress64, const DWORD64 numBytes64, DWORD64* virtualAddress64)\n{\n\tNTSTATUS ntStatus;\n\tHANDLE hMemory;\n\tLARGE_INTEGER physicalAddress;\t// convenience\n\tphysicalAddress.QuadPart = physicalAddress64;\n\n\tPAGED_CODE();\n\n\t// get handle to PhysicalMemory object\n\t{\n\t\tOBJECT_ATTRIBUTES objectAttributes;\n\t\tUNICODE_STRING objectName = RTL_CONSTANT_STRING(L\"\\\\Device\\\\PhysicalMemory\");\n\t\tconst ULONG attributes = OBJ_CASE_INSENSITIVE;\n\t\tconst HANDLE rootDirectory = 0;\n\t\tInitializeObjectAttributes(&objectAttributes, &objectName, attributes, rootDirectory, (PSECURITY_DESCRIPTOR)0);\n\t\tntStatus = ZwOpenSection(&hMemory, SECTION_ALL_ACCESS, &objectAttributes);\n\t\tif(!NT_SUCCESS(ntStatus))\n\t\t{\n\t\t\tKdPrint((\"AkenMapPhysicalMemory: ZwOpenSection failed\\n\"));\n\t\t\treturn ntStatus;\n\t\t}\n\t}\n\n\t// add a reference (required to prevent the handle from being deleted)\n\t{\n\t\tPVOID physicalMemorySection = NULL;\n\t\tconst POBJECT_TYPE objectType = 0;\t// allowed since specifying KernelMode\n\t\tntStatus = ObReferenceObjectByHandle(hMemory, SECTION_ALL_ACCESS, objectType, KernelMode, &physicalMemorySection, 0);\n\t\tif(!NT_SUCCESS(ntStatus))\n\t\t{\n\t\t\tKdPrint((\"AkenMapPhysicalMemory: ObReferenceObjectByHandle failed\\n\"));\n\t\t\tgoto close_handle;\n\t\t}\n\t}\n\n\t// note: mapmem.c does HalTranslateBusAddress, but we only care about\n\t// system memory. translating doesn't appear to be necessary, even if\n\t// much existing code uses it (probably due to cargo cult).\n\n\t// map desired memory into user PTEs\n\t{\n\t\tconst HANDLE hProcess = (HANDLE)-1;\n\t\tPVOID virtualBaseAddress = 0;\t// let ZwMapViewOfSection pick\n\t\tconst ULONG zeroBits = 0;\t// # high-order bits in address that must be 0\n\t\tSIZE_T mappedSize = (SIZE_T)numBytes64;\t// will receive the actual page-aligned size\n\t\tLARGE_INTEGER physicalBaseAddress = physicalAddress;\t// will be rounded down to 64KB boundary\n\t\tconst SECTION_INHERIT inheritDisposition = ViewShare;\n\t\tconst ULONG allocationType = 0;\n\t\tULONG protect = PAGE_READWRITE;\n\t\tif(IsMemoryUncacheable(physicalAddress64))\n\t\t\tprotect |= PAGE_NOCACHE;\n\t\tntStatus = ZwMapViewOfSection(hMemory, hProcess, &virtualBaseAddress, zeroBits, mappedSize, &physicalBaseAddress, &mappedSize, inheritDisposition, allocationType, protect);\n\t\tif(!NT_SUCCESS(ntStatus))\n\t\t{\n\t\t\t// try again with the opposite cacheability attribute\n\t\t\tprotect ^= PAGE_NOCACHE;\n\t\t\tntStatus = ZwMapViewOfSection(hMemory, hProcess, &virtualBaseAddress, zeroBits, mappedSize, &physicalBaseAddress, &mappedSize, inheritDisposition, allocationType, protect);\n\t\t\tif(!NT_SUCCESS(ntStatus))\n\t\t\t{\n\t\t\t\tKdPrint((\"AkenMapPhysicalMemory: ZwMapViewOfSection failed\\n\"));\n\t\t\t\tgoto close_handle;\n\t\t\t}\n\t\t}\n\n\t\t// the mapping rounded our physical base address down to the nearest\n\t\t// 64KiB boundary, so adjust the virtual address accordingly.\n\t\t{\n\t\t\tconst DWORD32 numBytesRoundedDown = physicalAddress.LowPart - physicalBaseAddress.LowPart;\n\t\t\tASSERT(numBytesRoundedDown < 0x10000);\n\t\t\t*virtualAddress64 = (DWORD64)virtualBaseAddress + numBytesRoundedDown;\n\t\t}\n\t}\n\n\tntStatus = STATUS_SUCCESS;\n\nclose_handle:\n\t// closing the handle even on success means that callers won't have to\n\t// pass it back when unmapping. why does this work? ZwMapViewOfSection\n\t// apparently adds a reference to hMemory.\n\tZwClose(hMemory);\n\n\treturn ntStatus;\n}\n\n\nstatic NTSTATUS AkenUnmapPhysicalMemory(const DWORD64 virtualAddress)\n{\n\tPAGED_CODE();\n\n\t{\n\t\tconst HANDLE hProcess = (HANDLE)-1;\n\t\tPVOID baseAddress = (PVOID)virtualAddress;\n\t\tNTSTATUS ntStatus = ZwUnmapViewOfSection(hProcess, baseAddress);\n\t\tif(!NT_SUCCESS(ntStatus))\n\t\t{\n\t\t\tKdPrint((\"AkenUnmapPhysicalMemory: ZwUnmapViewOfSection failed\\n\"));\n\t\t\treturn ntStatus;\n\t\t}\n\t}\n\n\treturn STATUS_SUCCESS;\n}\n\n\n//-----------------------------------------------------------------------------\n// helper functions called from DeviceControl\n//-----------------------------------------------------------------------------\n\nstatic NTSTATUS AkenIoctlReadPort(PVOID buf, const ULONG inSize, ULONG* outSize)\n{\n\tDWORD32 value;\n\n\tPAGED_CODE();\n\n\tif(inSize != sizeof(AkenReadPortIn) || *outSize != sizeof(AkenReadPortOut))\n\t\treturn STATUS_BUFFER_TOO_SMALL;\n\n\t{\n\t\tconst AkenReadPortIn* in = (const AkenReadPortIn*)buf;\n\t\tconst USHORT port = in->port;\n\t\tconst UCHAR numBytes = in->numBytes;\n\t\tswitch(numBytes)\n\t\t{\n\t\tcase 1:\n\t\t\tvalue = (DWORD32)READ_PORT_UCHAR((PUCHAR)port);\n\t\t\tbreak;\n\t\tcase 2:\n\t\t\tvalue = (DWORD32)READ_PORT_USHORT((PUSHORT)port);\n\t\t\tbreak;\n\t\tcase 4:\n\t\t\tvalue = (DWORD32)READ_PORT_ULONG((PULONG)port);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\treturn STATUS_INVALID_PARAMETER;\n\t\t}\n\t}\n\n\t{\n\t\tAkenReadPortOut* out = (AkenReadPortOut*)buf;\n\t\tout->value = value;\n\t}\n\treturn STATUS_SUCCESS;\n}\n\nstatic NTSTATUS AkenIoctlWritePort(PVOID buf, const ULONG inSize, ULONG* outSize)\n{\n\tPAGED_CODE();\n\n\tif(inSize != sizeof(AkenWritePortIn) || *outSize != 0)\n\t\treturn STATUS_BUFFER_TOO_SMALL;\n\n\t{\n\t\tconst AkenWritePortIn* in = (const AkenWritePortIn*)buf;\n\t\tconst DWORD32 value  = in->value;\n\t\tconst USHORT port    = in->port;\n\t\tconst UCHAR numBytes = in->numBytes;\n\t\tswitch(numBytes)\n\t\t{\n\t\tcase 1:\n\t\t\tWRITE_PORT_UCHAR((PUCHAR)port, (UCHAR)(value & 0xFF));\n\t\t\tbreak;\n\t\tcase 2:\n\t\t\tWRITE_PORT_USHORT((PUSHORT)port, (USHORT)(value & 0xFFFF));\n\t\t\tbreak;\n\t\tcase 4:\n\t\t\tWRITE_PORT_ULONG((PULONG)port, value);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\treturn STATUS_INVALID_PARAMETER;\n\t\t}\n\t}\n\n\treturn STATUS_SUCCESS;\n}\n\n\nstatic NTSTATUS AkenIoctlMap(PVOID buf, const ULONG inSize, ULONG* outSize)\n{\n\tDWORD64 virtualAddress;\n\tNTSTATUS ntStatus;\n\n\tPAGED_CODE();\n\n\tif(inSize != sizeof(AkenMapIn) || *outSize != sizeof(AkenMapOut))\n\t\treturn STATUS_BUFFER_TOO_SMALL;\n\n\t{\n\t\tconst AkenMapIn* in = (const AkenMapIn*)buf;\n\t\tconst DWORD64 physicalAddress = in->physicalAddress;\n\t\tconst DWORD64 numBytes        = in->numBytes;\n\t\tntStatus = AkenMapPhysicalMemory(physicalAddress, numBytes, &virtualAddress);\n\t}\n\n\t{\n\t\tAkenMapOut* out = (AkenMapOut*)buf;\n\t\tout->virtualAddress = virtualAddress;\n\t}\n\treturn ntStatus;\n}\n\nstatic NTSTATUS AkenIoctlUnmap(PVOID buf, const ULONG inSize, ULONG* outSize)\n{\n\tNTSTATUS ntStatus;\n\n\tPAGED_CODE();\n\n\tif(inSize != sizeof(AkenUnmapIn) || *outSize != 0)\n\t\treturn STATUS_BUFFER_TOO_SMALL;\n\n\t{\n\t\tconst AkenUnmapIn* in = (const AkenUnmapIn*)buf;\n\t\tconst DWORD64 virtualAddress = in->virtualAddress;\n\t\tntStatus = AkenUnmapPhysicalMemory(virtualAddress);\n\t}\n\n\treturn ntStatus;\n}\n\n\nstatic NTSTATUS AkenIoctlReadModelSpecificRegister(PVOID buf, const ULONG inSize, ULONG* outSize)\n{\n\tDWORD64 value;\n\n\tPAGED_CODE();\n\n\tif(inSize != sizeof(AkenReadRegisterIn) || *outSize != sizeof(AkenReadRegisterOut))\n\t\treturn STATUS_BUFFER_TOO_SMALL;\n\n\t{\n\t\tconst AkenReadRegisterIn* in = (const AkenReadRegisterIn*)buf;\n\t\tconst DWORD64 reg = in->reg;\n\t\tvalue = __readmsr((int)reg);\n\t}\n\n\t{\n\t\tAkenReadRegisterOut* out = (AkenReadRegisterOut*)buf;\n\t\tout->value = value;\n\t}\n\n\treturn STATUS_SUCCESS;\n}\n\nstatic NTSTATUS AkenIoctlWriteModelSpecificRegister(PVOID buf, const ULONG inSize, ULONG* outSize)\n{\n\tPAGED_CODE();\n\n\tif(inSize != sizeof(AkenWriteRegisterIn) || *outSize != 0)\n\t\treturn STATUS_BUFFER_TOO_SMALL;\n\n\t{\n\t\tconst AkenWriteRegisterIn* in = (const AkenWriteRegisterIn*)buf;\n\t\tconst DWORD64 reg   = in->reg;\n\t\tconst DWORD64 value = in->value;\n\t\t__writemsr((unsigned long)reg, value);\n\t}\n\n\treturn STATUS_SUCCESS;\n}\n\nstatic NTSTATUS AkenIoctlReadPerformanceMonitoringCounter(PVOID buf, const ULONG inSize, ULONG* outSize)\n{\n\tDWORD64 value;\n\n\tPAGED_CODE();\n\n\tif(inSize != sizeof(AkenReadRegisterIn) || *outSize != sizeof(AkenReadRegisterOut))\n\t\treturn STATUS_BUFFER_TOO_SMALL;\n\n\t{\n\t\tconst AkenReadRegisterIn* in = (const AkenReadRegisterIn*)buf;\n\t\tconst DWORD64 reg = in->reg;\n\t\tvalue = __readpmc((unsigned long)reg);\n\t}\n\n\t{\n\t\tAkenReadRegisterOut* out = (AkenReadRegisterOut*)buf;\n\t\tout->value = value;\n\t}\n\n\treturn STATUS_SUCCESS;\n}\n\n\nstatic NTSTATUS AkenIoctlUnknown(PVOID buf, const ULONG inSize, ULONG* outSize)\n{\n\tPAGED_CODE();\n\n\tKdPrint((\"AkenIoctlUnknown\\n\"));\n\n\t*outSize = 0;\n\treturn STATUS_INVALID_DEVICE_REQUEST;\n}\n\n\ntypedef NTSTATUS (*AkenIoctl)(PVOID buf, ULONG inSize, ULONG* outSize);\n\nstatic AkenIoctl AkenIoctlFromCode(ULONG ioctlCode)\n{\n\tPAGED_CODE();\n\n\tswitch(ioctlCode)\n\t{\n\tcase IOCTL_AKEN_READ_PORT:\n\t\treturn AkenIoctlReadPort;\n\tcase IOCTL_AKEN_WRITE_PORT:\n\t\treturn AkenIoctlWritePort;\n\n\tcase IOCTL_AKEN_MAP:\n\t\treturn AkenIoctlMap;\n\tcase IOCTL_AKEN_UNMAP:\n\t\treturn AkenIoctlUnmap;\n\n\tcase IOCTL_AKEN_READ_MSR:\n\t\treturn AkenIoctlReadModelSpecificRegister;\n\tcase IOCTL_AKEN_WRITE_MSR:\n\t\treturn AkenIoctlWriteModelSpecificRegister;\n\n\tdefault:\n\t\treturn AkenIoctlUnknown;\n\t}\n}\n\n\n//-----------------------------------------------------------------------------\n// entry points\n//-----------------------------------------------------------------------------\n\nstatic NTSTATUS AkenCreate(IN PDEVICE_OBJECT deviceObject, IN PIRP irp)\n{\n\tPAGED_CODE();\n\n\tirp->IoStatus.Status = STATUS_SUCCESS;\n\tirp->IoStatus.Information = 0;\n\tIoCompleteRequest(irp, IO_NO_INCREMENT);\n\treturn STATUS_SUCCESS;\n}\n\n\nstatic NTSTATUS AkenClose(IN PDEVICE_OBJECT deviceObject, IN PIRP irp)\n{\n\tPAGED_CODE();\n\n\t// same as AkenCreate ATM\n\tirp->IoStatus.Status = STATUS_SUCCESS;\n\tirp->IoStatus.Information = 0;\n\tIoCompleteRequest(irp, IO_NO_INCREMENT);\n\treturn STATUS_SUCCESS;\n}\n\n\nstatic NTSTATUS AkenDeviceControl(IN PDEVICE_OBJECT deviceObject, IN PIRP irp)\n{\n\tPAGED_CODE();\n\n\t{\n\t\t// get buffer from IRP. all our IOCTLs are METHOD_BUFFERED, so buf is\n\t\t// allocated by the I/O manager and used for both input and output.\n\t\tPVOID buf = irp->AssociatedIrp.SystemBuffer;\n\t\tPIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(irp);\n\t\tULONG ioctlCode    = irpStack->Parameters.DeviceIoControl.IoControlCode;\n\t\tconst ULONG inSize = irpStack->Parameters.DeviceIoControl.InputBufferLength;\n\t\tULONG outSize      = irpStack->Parameters.DeviceIoControl.OutputBufferLength;\t// modified by AkenIoctl*\n\n\t\tconst AkenIoctl akenIoctl = AkenIoctlFromCode(ioctlCode);\n\t\tconst NTSTATUS ntStatus = akenIoctl(buf, inSize, &outSize);\n\n\t\tirp->IoStatus.Information = outSize;\t// number of bytes to copy from buf to user's buffer\n\t\tirp->IoStatus.Status = ntStatus;\n\t\tIoCompleteRequest(irp, IO_NO_INCREMENT);\n\t\treturn ntStatus;\n\t}\n}\n\n\nstatic VOID AkenUnload(IN PDRIVER_OBJECT driverObject)\n{\n\tPAGED_CODE();\n\n\tKdPrint((\"AkenUnload\\n\"));\n\n\t{\n\t\tUNICODE_STRING win32Name = RTL_CONSTANT_STRING(WIN32_NAME);\n\t\tIoDeleteSymbolicLink(&win32Name);\n\t}\n\n\tif(driverObject->DeviceObject)\n\t\tIoDeleteDevice(driverObject->DeviceObject);\n}\n\n\n#pragma code_seg(pop)\t// make sure we don't countermand the alloc_text\n\nNTSTATUS DriverEntry(IN PDRIVER_OBJECT driverObject, IN PUNICODE_STRING registryPath)\n{\n\tUNICODE_STRING deviceName = RTL_CONSTANT_STRING(DEVICE_NAME);\n\n\t// create device object\n\tPDEVICE_OBJECT deviceObject;\n\t{\n\t\tconst ULONG deviceExtensionSize = 0;\n\t\tconst ULONG deviceCharacteristics = FILE_DEVICE_SECURE_OPEN;\n\t\tconst BOOLEAN exlusive = TRUE;\n\t\tNTSTATUS ntStatus = IoCreateDevice(driverObject, deviceExtensionSize, &deviceName, FILE_DEVICE_AKEN, deviceCharacteristics, exlusive, &deviceObject);\n\t\tif(!NT_SUCCESS(ntStatus))\n\t\t{\n\t\t\tKdPrint((\"DriverEntry: IoCreateDevice failed\\n\"));\n\t\t\treturn ntStatus;\n\t\t}\n\t}\n\n\t// set entry points\n\tdriverObject->MajorFunction[IRP_MJ_CREATE] = AkenCreate;\n\tdriverObject->MajorFunction[IRP_MJ_CLOSE]  = AkenClose;\n\tdriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = AkenDeviceControl;\n\tdriverObject->DriverUnload = AkenUnload;\n\n\t// symlink NT device name to Win32 namespace\n\t{\n\t\tUNICODE_STRING win32Name = RTL_CONSTANT_STRING(WIN32_NAME);\n\t\tNTSTATUS ntStatus = IoCreateSymbolicLink(&win32Name, &deviceName);\n\t\tif(!NT_SUCCESS(ntStatus))\n\t\t{\n\t\t\tKdPrint((\"DriverEntry: IoCreateSymbolicLink failed\\n\"));\n\t\t\tIoDeleteDevice(deviceObject);\n\t\t\treturn ntStatus;\n\t\t}\n\t}\n\n\treturn STATUS_SUCCESS;\n}\n"
  },
  {
    "path": "fpsgame/gui/sysdep/os/win/aken/aken.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * Aken driver interface\n */\n\n// Aken - custodian of the ferryboat to the underworld in Egyptian mythology,\n// and a driver that shuttles between applications and kernel mode resources.\n\n#ifndef INCLUDED_AKEN\n#define INCLUDED_AKEN\n\n#define AKEN_NAME L\"Aken\"\n\n// device type\n#define FILE_DEVICE_AKEN 53498\t// in the \"User Defined\" range.\"\n\n#define AKEN_IOCTL 0x800\t// 0x800..0xFFF are for 'customer' use.\n\n#define IOCTL_AKEN_READ_PORT           CTL_CODE(FILE_DEVICE_AKEN, AKEN_IOCTL+0, METHOD_BUFFERED, FILE_ANY_ACCESS)\n#define IOCTL_AKEN_WRITE_PORT          CTL_CODE(FILE_DEVICE_AKEN, AKEN_IOCTL+1, METHOD_BUFFERED, FILE_ANY_ACCESS)\n#define IOCTL_AKEN_MAP                 CTL_CODE(FILE_DEVICE_AKEN, AKEN_IOCTL+2, METHOD_BUFFERED, FILE_ANY_ACCESS)\n#define IOCTL_AKEN_UNMAP               CTL_CODE(FILE_DEVICE_AKEN, AKEN_IOCTL+3, METHOD_BUFFERED, FILE_ANY_ACCESS)\n#define IOCTL_AKEN_READ_MSR            CTL_CODE(FILE_DEVICE_AKEN, AKEN_IOCTL+4, METHOD_BUFFERED, FILE_ANY_ACCESS)\n#define IOCTL_AKEN_WRITE_MSR           CTL_CODE(FILE_DEVICE_AKEN, AKEN_IOCTL+5, METHOD_BUFFERED, FILE_ANY_ACCESS)\n#define IOCTL_AKEN_READ_PMC            CTL_CODE(FILE_DEVICE_AKEN, AKEN_IOCTL+6, METHOD_BUFFERED, FILE_ANY_ACCESS)\n\n\n// input and output data structures for the IOCTLs\n\n#pragma pack(push, 1)\n\ntypedef struct AkenReadPortIn_\n{\n\tUSHORT port;\n\tUCHAR numBytes;\n}\nAkenReadPortIn;\n\ntypedef struct AkenReadPortOut_\n{\n\tDWORD32 value;\n}\nAkenReadPortOut;\n\ntypedef struct AkenWritePortIn_\n{\n\tDWORD32 value;\n\tUSHORT port;\n\tUCHAR numBytes;\n}\nAkenWritePortIn;\n\ntypedef struct AkenMapIn_\n{\n\t// note: fixed-width types allow the 32 or 64-bit Mahaf wrapper to\n\t// interoperate with the 32 or 64-bit Aken driver.\n\tDWORD64 physicalAddress;\n\tDWORD64 numBytes;\n}\nAkenMapIn;\n\ntypedef struct AkenMapOut_\n{\n\tDWORD64 virtualAddress;\n}\nAkenMapOut;\n\ntypedef struct AkenUnmapIn_\n{\n\tDWORD64 virtualAddress;\n}\nAkenUnmapIn;\n\ntypedef struct AkenReadRegisterIn_\n{\n\tDWORD64 reg;\n}\nAkenReadRegisterIn;\n\ntypedef struct AkenReadRegisterOut_\n{\n\tDWORD64 value;\n}\nAkenReadRegisterOut;\n\ntypedef struct AkenWriteRegisterIn_\n{\n\tDWORD64 reg;\n\tDWORD64 value;\n}\nAkenWriteRegisterIn;\n\n#pragma pack(pop)\n\n#endif\t// #ifndef INCLUDED_AKEN\n"
  },
  {
    "path": "fpsgame/gui/sysdep/os/win/aken/makefile",
    "content": "#\n# DO NOT EDIT THIS FILE!!!  Edit .\\sources. if you want to add a new source\n# file to this component.  This file merely indirects to the real make file\n# that is shared by all the driver components of the Windows NT DDK\n#\n\n!INCLUDE $(NTMAKEENV)\\makefile.def\n"
  },
  {
    "path": "fpsgame/gui/sysdep/os/win/aken/sources",
    "content": "TARGETNAME=aken\nTARGETPATH=.\nTARGETTYPE=DRIVER\n\nSOURCES=aken.c\n"
  },
  {
    "path": "fpsgame/gui/sysdep/os/win/comctl6.manifest",
    "content": "<?xml version='1.0' encoding='UTF-8' standalone='yes'?>\n<assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'>\n  <dependency>\n    <dependentAssembly>\n      <assemblyIdentity type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='X86' publicKeyToken='6595b64144ccf1df' />\n    </dependentAssembly>\n  </dependency>\n</assembly>\n"
  },
  {
    "path": "fpsgame/gui/sysdep/os/win/error_dialog.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n//{{NO_DEPENDENCIES}}\n// Microsoft Visual C++ generated include file.\n// Used by error_dialog.rc\n//\n#define IDD_DIALOG1                     101\n#define IDC_EDIT1                       1001\n#define IDC_COPY                        1002\n#define IDC_CONTINUE                    2000\n#define IDC_SUPPRESS                    2001\n#define IDC_BREAK                       2002\n#define IDC_EXIT                        2003\n\n// Next default values for new objects\n// \n#ifdef APSTUDIO_INVOKED\n#ifndef APSTUDIO_READONLY_SYMBOLS\n#define _APS_NO_MFC                     1\n#define _APS_NEXT_RESOURCE_VALUE        102\n#define _APS_NEXT_COMMAND_VALUE         40001\n#define _APS_NEXT_CONTROL_VALUE         1003\n#define _APS_NEXT_SYMED_VALUE           101\n#endif\n#endif\n"
  },
  {
    "path": "fpsgame/gui/sysdep/os/win/error_dialog.rc",
    "content": "#include \"error_dialog.h\"\n#include <windows.h>\n\n#ifdef _WIN32\nLANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL\n#pragma code_page(1252)\n#endif //_WIN32\n\n\n/////////////////////////////////////////////////////////////////////////////\n//\n// TEXTINCLUDE\n//\n\n#ifdef APSTUDIO_INVOKED\n1 TEXTINCLUDE \nBEGIN\n    \"error_dialog.h\\0\"\nEND\n\n#endif    // APSTUDIO_INVOKED\n\n\n/////////////////////////////////////////////////////////////////////////////\n//\n// Dialog\n//\n\nIDD_DIALOG1 DIALOGEX 0, 0, 328, 260\nSTYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME\nEXSTYLE WS_EX_APPWINDOW\nCAPTION \"Program Error\"\nFONT 8, \"MS Shell Dlg\", 400, 0, 0x1\nBEGIN\n    PUSHBUTTON      \"&Continue\",IDC_CONTINUE,7,239,50,14\n    EDITTEXT        IDC_EDIT1,7,7,314,204,ES_MULTILINE | ES_READONLY | ES_WANTRETURN | WS_VSCROLL\n    PUSHBUTTON      \"Copy\",IDC_COPY,271,212,50,14\n    PUSHBUTTON      \"&Suppress\",IDC_SUPPRESS,59,239,50,14\n    PUSHBUTTON      \"&Break\",IDC_BREAK,111,239,50,14\n    PUSHBUTTON      \"E&xit\",IDC_EXIT,163,239,50,14\nEND\n\n\n/////////////////////////////////////////////////////////////////////////////\n//\n// DESIGNINFO\n//\n\n#ifdef APSTUDIO_INVOKED\nGUIDELINES DESIGNINFO \nBEGIN\n    IDD_DIALOG1, DIALOG\n    BEGIN\n        LEFTMARGIN, 7\n        RIGHTMARGIN, 321\n        TOPMARGIN, 7\n        BOTTOMMARGIN, 253\n    END\nEND\n#endif    // APSTUDIO_INVOKED\n\n/////////////////////////////////////////////////////////////////////////////\n"
  },
  {
    "path": "fpsgame/gui/sysdep/os/win/icon.rc",
    "content": "IDI_ICON1               ICON                    \"..\\\\..\\\\..\\\\..\\\\..\\\\build\\\\resources\\\\ps.ico\"\n"
  },
  {
    "path": "fpsgame/gui/sysdep/os/win/mahaf.cpp",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * user-mode interface to Aken driver\n */\n\n#include \"precompiled.h\"\n#include \"lib/sysdep/os/win/mahaf.h\"\n\n#include \"lib/config2.h\"\n#include \"lib/module_init.h\"\n\n#include \"lib/sysdep/os/win/wutil.h\"\n#include <winioctl.h>\n#include \"lib/sysdep/os/win/aken/aken.h\"\n#include \"lib/sysdep/os/win/wversion.h\"\n\nstatic HANDLE hAken = INVALID_HANDLE_VALUE;\t// handle to Aken driver\n\n\n//-----------------------------------------------------------------------------\n// ioctl wrappers\n//-----------------------------------------------------------------------------\n\nstatic u32 ReadPort(u16 port, u8 numBytes)\n{\n\tAkenReadPortIn in;\n\tin.port = (USHORT)port;\n\tin.numBytes = (UCHAR)numBytes;\n\tAkenReadPortOut out;\n\n\tDWORD bytesReturned;\n\tLPOVERLAPPED ovl = 0;\t// synchronous\n\tconst BOOL ok = DeviceIoControl(hAken, (DWORD)IOCTL_AKEN_READ_PORT, &in, sizeof(in), &out, sizeof(out), &bytesReturned, ovl);\n\tWARN_RETURN_0_IF_FALSE(ok);\n\n\tENSURE(bytesReturned == sizeof(out));\n\treturn out.value;\n}\n\nu8 mahaf_ReadPort8(u16 port)\n{\n\tconst u32 value = ReadPort(port, 1);\n\tENSURE(value <= 0xFF);\n\treturn (u8)(value & 0xFF);\n}\n\nu16 mahaf_ReadPort16(u16 port)\n{\n\tconst u32 value = ReadPort(port, 2);\n\tENSURE(value <= 0xFFFF);\n\treturn (u16)(value & 0xFFFF);\n}\n\nu32 mahaf_ReadPort32(u16 port)\n{\n\tconst u32 value = ReadPort(port, 4);\n\treturn value;\n}\n\n\nstatic void WritePort(u16 port, u32 value, u8 numBytes)\n{\n\tAkenWritePortIn in;\n\tin.value = (DWORD32)value;\n\tin.port  = (USHORT)port;\n\tin.numBytes = (UCHAR)numBytes;\n\n\tDWORD bytesReturned;\t// unused but must be passed to DeviceIoControl\n\tLPOVERLAPPED ovl = 0;\t// synchronous\n\tBOOL ok = DeviceIoControl(hAken, (DWORD)IOCTL_AKEN_WRITE_PORT, &in, sizeof(in), 0, 0u, &bytesReturned, ovl);\n\tWARN_IF_FALSE(ok);\n}\n\nvoid mahaf_WritePort8(u16 port, u8 value)\n{\n\tWritePort(port, (u32)value, 1);\n}\n\nvoid mahaf_WritePort16(u16 port, u16 value)\n{\n\tWritePort(port, (u32)value, 2);\n}\n\nvoid mahaf_WritePort32(u16 port, u32 value)\n{\n\tWritePort(port, value, 4);\n}\n\n\nbool mahaf_IsPhysicalMappingDangerous()\n{\n\t// pre-XP versions don't prevent re-mapping pages with incompatible\n\t// attributes, which may lead to disaster due to TLB corruption.\n\tif(wversion_Number() < WVERSION_XP)\n\t\treturn true;\n\n\treturn false;\n}\n\n\nvolatile void* mahaf_MapPhysicalMemory(uintptr_t physicalAddress, size_t numBytes)\n{\n\tENSURE(!mahaf_IsPhysicalMappingDangerous());\n\n\tAkenMapIn in;\n\tin.physicalAddress = (DWORD64)physicalAddress;\n\tin.numBytes        = (DWORD64)numBytes;\n\tAkenMapOut out;\n\n\tDWORD bytesReturned;\n\tLPOVERLAPPED ovl = 0;\t// synchronous\n\tconst BOOL ok = DeviceIoControl(hAken, (DWORD)IOCTL_AKEN_MAP, &in, sizeof(in), &out, sizeof(out), &bytesReturned, ovl);\n\tWARN_RETURN_0_IF_FALSE(ok);\n\n\tENSURE(bytesReturned == sizeof(out));\n\tvolatile void* virtualAddress = (volatile void*)(uintptr_t)out.virtualAddress;\n\treturn virtualAddress;\n}\n\n\nvoid mahaf_UnmapPhysicalMemory(volatile void* virtualAddress)\n{\n\tENSURE(!mahaf_IsPhysicalMappingDangerous());\n\n\tAkenUnmapIn in;\n\tin.virtualAddress = (DWORD64)virtualAddress;\n\n\tDWORD bytesReturned;\t// unused but must be passed to DeviceIoControl\n\tLPOVERLAPPED ovl = 0;\t// synchronous\n\tBOOL ok = DeviceIoControl(hAken, (DWORD)IOCTL_AKEN_UNMAP, &in, sizeof(in), 0, 0u, &bytesReturned, ovl);\n\tWARN_IF_FALSE(ok);\n}\n\n\nstatic u64 ReadRegister(DWORD ioctl, u64 reg)\n{\n\tAkenReadRegisterIn in;\n\tin.reg = reg;\n\tAkenReadRegisterOut out;\n\n\tDWORD bytesReturned;\n\tLPOVERLAPPED ovl = 0;\t// synchronous\n\tconst BOOL ok = DeviceIoControl(hAken, ioctl, &in, sizeof(in), &out, sizeof(out), &bytesReturned, ovl);\n\tWARN_RETURN_0_IF_FALSE(ok);\n\n\tENSURE(bytesReturned == sizeof(out));\n\treturn out.value;\n}\n\nu64 mahaf_ReadModelSpecificRegister(u64 reg)\n{\n\treturn ReadRegister((DWORD)IOCTL_AKEN_READ_MSR, reg);\n}\n\nu64 mahaf_ReadPerformanceMonitoringCounter(u64 reg)\n{\n\treturn ReadRegister((DWORD)IOCTL_AKEN_READ_PMC, reg);\n}\n\nvoid mahaf_WriteModelSpecificRegister(u64 reg, u64 value)\n{\n\tAkenWriteRegisterIn in;\n\tin.reg = reg;\n\tin.value = value;\n\n\tDWORD bytesReturned;\t// unused but must be passed to DeviceIoControl\n\tLPOVERLAPPED ovl = 0;\t// synchronous\n\tBOOL ok = DeviceIoControl(hAken, (DWORD)IOCTL_AKEN_WRITE_MSR, &in, sizeof(in), 0, 0u, &bytesReturned, ovl);\n\tWARN_IF_FALSE(ok);\n}\n\n\n//-----------------------------------------------------------------------------\n// driver installation\n//-----------------------------------------------------------------------------\n\n// @param access need not include SC_MANAGER_CONNECT (\"implicitly specified\")\nstatic SC_HANDLE OpenServiceControlManager(DWORD access)\n{\n\tLPCWSTR machineName = 0;\t// local\n\tLPCWSTR databaseName = 0;\t// default\n\tSC_HANDLE hSCM = OpenSCManagerW(machineName, databaseName, access);\n\tif(!hSCM)\n\t{\n\t\t// ensure no other problems arose\n\t\tENSURE(GetLastError() == ERROR_ACCESS_DENIED);\n\n\t\t// administrator privileges are required for SC_MANAGER_CREATE_SERVICE.\n\t\t// this is a problem on Vista / Win7, so users will have to use the\n\t\t// separate aken_install.bat\n\n\t\treturn 0;\n\t}\n\n\treturn hSCM;\t// success\n}\n\n\nstatic void UninstallDriver()\n{\n\tSC_HANDLE hSCM = OpenServiceControlManager(SC_MANAGER_ENUMERATE_SERVICE);\n\tif(!hSCM)\n\t\treturn;\n\tSC_HANDLE hService = OpenServiceW(hSCM, AKEN_NAME, SERVICE_STOP|SERVICE_INTERROGATE);\n\tif(!hService)\n\t\treturn;\n\n\t// stop service\n\tSERVICE_STATUS serviceStatus;\n\tif(!ControlService(hService, SERVICE_CONTROL_STOP, &serviceStatus))\n\t{\n\t\t// if the problem wasn't that the service is already stopped,\n\t\t// something actually went wrong.\n\t\tconst DWORD err = GetLastError();\n\t\tENSURE(err == ERROR_SERVICE_NOT_ACTIVE || err == ERROR_SERVICE_CANNOT_ACCEPT_CTRL);\n\t}\n\n\t// delete service\n\tBOOL ok;\n\tok = DeleteService(hService);\n\tWARN_IF_FALSE(ok);\n\tok = CloseServiceHandle(hService);\n\tWARN_IF_FALSE(ok);\n\n\tok = CloseServiceHandle(hSCM);\n\tWARN_IF_FALSE(ok);\n}\n\n\n#if CONFIG2_MAHAF_ATTEMPT_DRIVER_START\n\nstatic void StartDriver(const OsPath& driverPathname)\n{\n\tconst SC_HANDLE hSCM = OpenServiceControlManager(SC_MANAGER_CREATE_SERVICE);\n\tif(!hSCM)\n\t{\n\t\tENSURE(GetLastError() == ERROR_ACCESS_DENIED);\n\t\tSetLastError(0);\n\t\treturn;\n\t}\n\n\tSC_HANDLE hService = OpenServiceW(hSCM, AKEN_NAME, SERVICE_START);\n\n\t// during development, we want to ensure the newest build is used, so\n\t// unload and re-create the service if it's running/installed.\n\t// as of 2008-03-24 no further changes to Aken are pending, so this is\n\t// disabled (thus also avoiding trouble when running multiple instances)\n#if 0\n\tif(hService)\n\t{\n\t\tBOOL ok = CloseServiceHandle(hService);\n\t\tWARN_IF_FALSE(ok);\n\t\thService = 0;\n\t\tUninstallDriver();\n\t}\n#endif\t\n\n\t// create service (note: this just enters the service into SCM's DB;\n\t// no error is raised if the driver binary doesn't exist etc.)\n\tif(!hService)\n\t{\n\t\tLPCWSTR startName = 0;\t// LocalSystem\n\t\t// NB: Windows 7 seems to insist upon backslashes (i.e. external_file_string)\n\t\thService = CreateServiceW(hSCM, AKEN_NAME, AKEN_NAME,\n\t\t\tSERVICE_START, SERVICE_KERNEL_DRIVER, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL,\n\t\t\tOsString(driverPathname).c_str(), 0, 0, 0, startName, 0);\n\t\tENSURE(hService != 0);\n\t}\n\n\t{\n\t\tDWORD numArgs = 0;\n\t\tBOOL ok = StartService(hService, numArgs, 0);\n\t\tif(!ok)\n\t\t{\n\t\t\tswitch(GetLastError())\n\t\t\t{\n\t\t\tcase ERROR_SERVICE_ALREADY_RUNNING:\n\t\t\t\t// ok, no action needed\n\t\t\t\tbreak;\n\t\t\tcase ERROR_ACCESS_DENIED:\n\t\t\t\t// Win7, can't start service; must use aken_install.bat\n\t\t\t\tbreak;\n\t\t\tcase ERROR_INVALID_IMAGE_HASH:\n\t\t\t\t// Win7 x86 rejects our code signing certificate; must enable\n\t\t\t\t// \"test signing\" mode via aken_install.bat\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\t// unexpected problem\n\t\t\t\tDEBUG_WARN_ERR(ERR::LOGIC);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tCloseServiceHandle(hService);\n\tCloseServiceHandle(hSCM);\n}\n\n\nstatic bool Is64BitOs()\n{\n#if OS_WIN64\n\treturn true;\n#else\n\treturn wutil_IsWow64();\n#endif\n}\n\nstatic OsPath DriverPathname()\n{\n\tconst char* const bits = Is64BitOs()? \"64\" : \"\";\n#ifdef NDEBUG\n\tconst char* const debug = \"\";\n#else\n\tconst char* const debug = \"d\";\n#endif\n\tchar filename[PATH_MAX];\n\tsprintf_s(filename, ARRAY_SIZE(filename), \"aken%s%s.sys\", bits, debug);\n\treturn wutil_ExecutablePath() / filename;\n}\n\n#endif // CONFIG2_MAHAF_ATTEMPT_DRIVER_START\n\n\n//-----------------------------------------------------------------------------\n\nstatic Status Init()\n{\n\tWinScopedPreserveLastError s;\n\n\tif(wutil_HasCommandLineArgument(L\"-wNoMahaf\"))\n\t\treturn ERR::NOT_SUPPORTED;\t// NOWARN\n\n#if CONFIG2_MAHAF_ATTEMPT_DRIVER_START\n\t{\n\t\tconst OsPath driverPathname = DriverPathname();\n\t\tStartDriver(driverPathname);\n\t}\n#endif\n\n\t{\n\t\tconst DWORD shareMode = 0;\n\t\thAken = CreateFileW(L\"\\\\\\\\.\\\\Aken\", GENERIC_READ, shareMode, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);\n\t\tif(hAken == INVALID_HANDLE_VALUE)\n\t\t{\n\t\t\t// GetLastError() is ERROR_FILE_NOT_FOUND if the driver isn't running,\n\t\t\t// but this is also reached when a handle has already been opened\n\t\t\t// (e.g. by a second instance of the same program) - in which case we must\n\t\t\t// indicate failure so that clients won't engage in unsynchronized ring 0 operations.\n\t\t\tSetLastError(0);\n\t\t\treturn ERR::INVALID_HANDLE;\t// NOWARN (happens often due to security restrictions)\n\t\t}\n\t}\n\n\treturn INFO::OK;\n}\n\nstatic void Shutdown()\n{\n\tCloseHandle(hAken);\n\thAken = INVALID_HANDLE_VALUE;\n\n\tUninstallDriver();\n}\n\n\nstatic ModuleInitState initState;\n\nStatus mahaf_Init()\n{\n\treturn ModuleInit(&initState, Init);\n}\n\nvoid mahaf_Shutdown()\n{\n\tModuleShutdown(&initState, Shutdown);\n}\n"
  },
  {
    "path": "fpsgame/gui/sysdep/os/win/mahaf.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * user-mode interface to Aken driver\n */\n\n// Mahaf - ferryman in Egyptian mythology that wakes up Aken,\n// and the interface to the Aken driver.\n\n#ifndef INCLUDED_MAHAF\n#define INCLUDED_MAHAF\n\n/**\n * @return whether mapping physical memory is known to be dangerous\n * on this platform.\n *\n * callable before or after mahaf_Init.\n *\n * note: mahaf_MapPhysicalMemory will complain if it\n * is called despite this function having returned true.\n **/\nLIB_API bool mahaf_IsPhysicalMappingDangerous();\n\n\nLIB_API Status mahaf_Init();\nLIB_API void mahaf_Shutdown();\n\nLIB_API u8  mahaf_ReadPort8 (u16 port);\nLIB_API u16 mahaf_ReadPort16(u16 port);\nLIB_API u32 mahaf_ReadPort32(u16 port);\nLIB_API void mahaf_WritePort8 (u16 port, u8  value);\nLIB_API void mahaf_WritePort16(u16 port, u16 value);\nLIB_API void mahaf_WritePort32(u16 port, u32 value);\n\nLIB_API volatile void* mahaf_MapPhysicalMemory(uintptr_t physicalAddress, size_t numBytes);\nLIB_API void mahaf_UnmapPhysicalMemory(volatile void* virtualAddress);\n\nLIB_API u64 mahaf_ReadModelSpecificRegister(u64 reg);\nLIB_API void mahaf_WriteModelSpecificRegister(u64 reg, u64 value);\n\n// must be done in the driver because Windows clears CR4.PCE[8]\nLIB_API u64 mahaf_ReadPerformanceMonitoringCounter(u64 reg);\n\n#endif\t// INCLUDED_MAHAF\n"
  },
  {
    "path": "fpsgame/gui/sysdep/os/win/manifest.cpp",
    "content": "/* Copyright (c) 2015 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#include \"precompiled.h\"\n\n/*\nTo use XP-style themed controls, we need to use the manifest to specify the\ndesired version. (This must be set in the game's .exe in order to affect Atlas.)\n\nFor VC7.1, we use manifest.rc to include a complete manifest file.\nFor VC8.0, which already generates its own manifest, we use the line below\nto add the necessary parts to that generated manifest.\n\nICC 10.1 IPO considers this string to be an input file, hence this\nis currently disabled there.\n*/\n#if MSC_VERSION >= 1400 && !ICC_VERSION && defined(LIB_STATIC_LINK)\n# if ARCH_IA32\n#  pragma comment(linker, \"\\\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='X86' publicKeyToken='6595b64144ccf1df'\\\"\")\n# elif ARCH_AMD64\n#  pragma comment(linker, \"\\\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df'\\\"\")\n# endif\n\n/*\nNOTE: vcbuild.exe (as used by the autobuilder) seems to ignore these linker\ncomments, so we have to duplicate these commands into premake.lua too.\nRemember to keep them in sync with this file.\n(Duplicate entries appear to get omitted from the .manifest file so there\nshould be no harmful effects.)\n*/\n\n#endif\n"
  },
  {
    "path": "fpsgame/gui/sysdep/os/win/manifest.rc",
    "content": "\n// This .rc file is only included in VS2003 projects, since VS2005 defines its own manifest\n\n1 24 \"comctl6.manifest\"\n"
  },
  {
    "path": "fpsgame/gui/sysdep/os/win/tests/test_ia32.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#include \"lib/self_test.h\"\n\n#include \"lib/sysdep/arch/x86_x64/x86_x64.h\"\n\n// note: ia32_i??_from_*, ia32_rint*, ia32_fm??f are all tested within\n// sysdep to avoid test duplication (both the ia32 versions and\n// the portable fallback must behave the same).\n\nclass TestIA32: public CxxTest::TestSuite \n{\npublic:\n\tvoid test_rdtsc()\n\t{\n\t\t// must increase monotonously\n\t\tconst u64 c1 = x86_x64::rdtsc();\n\t\tconst u64 c2 = x86_x64::rdtsc();\n\t\tconst u64 c3 = x86_x64::rdtsc();\n\t\tTS_ASSERT(c1 < c2 && c2 < c3);\n\t}\n\n\tvoid test_ia32_cap()\n\t{\n\t\t// make sure the really common/basic caps end up reported as true\n\t\tTS_ASSERT(x86_x64::Cap(x86_x64::CAP_FPU));\n\t\tTS_ASSERT(x86_x64::Cap(x86_x64::CAP_TSC));\n\t\tTS_ASSERT(x86_x64::Cap(x86_x64::CAP_MMX));\n\t}\n};\n"
  },
  {
    "path": "fpsgame/gui/sysdep/os/win/tests/test_wdbg_sym.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n// note: this is more of an on-demand display of the stack trace than\n// self-test of it.\n// TODO: compare against known-good result?\n// problem: results may differ by compiler (e.g. due to differing STL)\n\n#include \"lib/self_test.h\"\n\n#include <queue>\n#include <deque>\n#include <list>\n#include <map>\n#include <stack>\n\n#include \"lib/bits.h\"\n#include \"lib/sysdep/os/win/win.h\"\t// HWND\n#include \"lib/sysdep/sysdep.h\"\n#include \"lib/sysdep/os/win/wdbg_sym.h\"\n#include \"lib/external_libraries/dbghelp.h\"\n\n\nstatic void* callers[100];\nstatic size_t numCallers;\n\nstatic Status OnFrame(const _tagSTACKFRAME64* frame, uintptr_t UNUSED(cbData))\n{\n\tcallers[numCallers++] = (void*)frame->AddrPC.Offset;\n\treturn INFO::OK;\n}\n\n#pragma optimize(\"\", off)\n#pragma warning(disable:4748)\t// /GS can not protect [..] from local buffer overrun because optimizations are disabled\n\n// (these must be outside of TestWdbgSym so that we can simply\n// search for the function's name as a substring within the ILT\n// decorated name (which omits the :: scope resolution operator,\n// while debug_ResolveSymbol for the function does not)\n__declspec(noinline) static void Func1()\n{\n\tCONTEXT context;\n\t(void)debug_CaptureContext(&context);\n\twdbg_sym_WalkStack(OnFrame, 0, context);\n}\n\n__declspec(noinline) static void Func2()\n{\n\tFunc1();\n}\n\n__declspec(noinline) static void Func3()\n{\n\tFunc2();\n}\n\n\nclass TestWdbgSym : public CxxTest::TestSuite \n{\n\tstatic void m_test_array()\n\t{\n\t\tstruct Small\n\t\t{\n\t\t\tint i1;\n\t\t\tint i2;\n\t\t};\n\n\t\tstruct Large\n\t\t{\n\t\t\tdouble d1;\n\t\t\tdouble d2;\n\t\t\tdouble d3;\n\t\t\tdouble d4;\n\t\t};\n\n\t\tLarge large_array_of_large_structs[8] = { { 0.0,0.0,0.0,0.0 } };\n\t\tLarge small_array_of_large_structs[2] = { { 0.0,0.0,0.0,0.0 } };\n\t\tSmall large_array_of_small_structs[8] = { { 1,2 } };\n\t\tSmall small_array_of_small_structs[2] = { { 1,2 } };\n\n\t\tint ints[] = { 1,2,3,4,5 };\n\t\twchar_t chars[] = { 'w','c','h','a','r','s',0 };\n\t\twchar_t many_wchars[1024]; memset(many_wchars, 'a', sizeof(many_wchars));\n\n\t\tdebug_printf(\"\\n(dumping stack frames may result in access violations...)\\n\");\n\t\tdebug_printf(\"  locals: %.0d %.0c %.0c %.0g %.0g %.0d %.0d\\n\", ints[0], chars[0], many_wchars[0], large_array_of_large_structs[0].d1, small_array_of_large_structs[0].d1, large_array_of_small_structs[0].i1, small_array_of_small_structs[0].i1);\n\n\t\t// note: we don't want any kind of dialog to be raised, because\n\t\t// this test now always runs. therefore, just make sure a decent\n\t\t// amount of text (not just \"(failed)\" error messages) was produced.\n\t\tErrorMessageMem emm = {0};\n\t\tCACHE_ALIGNED(u8) context[DEBUG_CONTEXT_SIZE];\n\t\t(void)debug_CaptureContext(context);\n\t\tconst wchar_t* text = debug_BuildErrorMessage(L\"dummy\", 0,0,0, context, L\"m_test_array\", &emm);\n\t\tTS_ASSERT(wcslen(text) > 500);\n#if 0\n\t\t{\n\t\t\tstd::wofstream s(L\"d:\\\\out.txt\");\n\t\t\ts << text;\n\t\t}\n#endif\n\t\tdebug_FreeErrorMessage(&emm);\n\n\t\tdebug_printf(\"(done dumping stack frames)\\n\");\n\t}\n\n\t// also used by test_stl as an element type\n\tstruct Nested\n\t{\n\t\tint nested_member;\n\t\tstruct Nested* self_ptr;\n\t};\n\n\tstatic void m_test_udt()\n\t{\n\t\tNested nested = { 123 }; nested.self_ptr = &nested;\n\n\t\ttypedef struct\n\t\t{\n\t\t\tu8 s1;\n\t\t\tu8 s2;\n\t\t\tchar s3;\n\t\t}\n\t\tSmall;\n\t\tSmall small__ = { 0x55, 0xaa, -1 }; UNUSED2(small__);\n\n\t\tstruct Large\n\t\t{\n\t\t\tu8 large_member_u8;\n\t\t\tstd::string large_member_string;\n\t\t\tdouble large_member_double;\n\t\t}\n\t\tlarge = { 0xff, \"large struct string\", 123456.0 }; UNUSED2(large);\n\n\n\t\tclass Base\n\t\t{\n\t\t\tint base_int;\n\t\t\tstd::wstring base_wstring;\n\t\tpublic:\n\t\t\tBase()\n\t\t\t\t: base_int(123), base_wstring(L\"base wstring\")\n\t\t\t{\n\t\t\t}\n\t\t};\n\t\tclass Derived : private Base\n\t\t{\n\t\t\tdouble derived_double;\n\t\tpublic:\n\t\t\tDerived()\n\t\t\t\t: derived_double(-1.0)\n\t\t\t{\n\t\t\t}\n\t\t}\n\t\tderived;\n\n\t\tm_test_array();\n\t}\n\n\t// STL containers and their contents\n\tstatic void m_test_stl()\n\t{\n\t\tstd::vector<std::wstring> v_wstring;\n\t\tv_wstring.push_back(L\"ws1\"); v_wstring.push_back(L\"ws2\");\n\n\t\tstd::deque<int> d_int;\n\t\td_int.push_back(1); d_int.push_back(2); d_int.push_back(3);\n\t\tstd::deque<std::string> d_string;\n\t\td_string.push_back(\"a\"); d_string.push_back(\"b\"); d_string.push_back(\"c\");\n\n\t\tstd::list<float> l_float;\n\t\tl_float.push_back(0.1f); l_float.push_back(0.2f); l_float.push_back(0.3f); l_float.push_back(0.4f); \n\n\t\tstd::map<std::string, int> m_string_int;\n\t\tm_string_int.insert(std::make_pair<std::string,int>(\"s5\", 5));\n\t\tm_string_int.insert(std::make_pair<std::string,int>(\"s6\", 6));\n\t\tm_string_int.insert(std::make_pair<std::string,int>(\"s7\", 7));\n\t\tstd::map<int, std::string> m_int_string;\n\t\tm_int_string.insert(std::make_pair<int,std::string>(1, \"s1\"));\n\t\tm_int_string.insert(std::make_pair<int,std::string>(2, \"s2\"));\n\t\tm_int_string.insert(std::make_pair<int,std::string>(3, \"s3\"));\n\t\tstd::map<int, int> m_int_int;\n\t\tm_int_int.insert(std::make_pair<int,int>(1, 1));\n\t\tm_int_int.insert(std::make_pair<int,int>(2, 2));\n\t\tm_int_int.insert(std::make_pair<int,int>(3, 3));\n\n\t\tstd::set<uintptr_t> s_uintptr;\n\t\ts_uintptr.insert(0x123); s_uintptr.insert(0x456);\n\n\t\t// empty\n\t\tstd::deque<u8> d_u8_empty;\n\t\tstd::list<Nested> l_nested_empty;\n\t\tstd::map<double,double> m_double_empty;\n\t\tstd::multimap<int,u8> mm_int_empty;\n\t\tstd::set<size_t> s_uint_empty;\n\t\tstd::multiset<char> ms_char_empty;\n\t\tstd::vector<double> v_double_empty;\n\t\tstd::queue<double> q_double_empty;\n\t\tstd::stack<double> st_double_empty;\n\t\tstd::string str_empty;\n\t\tstd::wstring wstr_empty;\n\n\t\tm_test_udt();\n\n\t\t// uninitialized\n\t\tstd::deque<u8> d_u8_uninit;\n\t\tstd::list<Nested> l_nested_uninit;\n\t\tstd::map<double,double> m_double_uninit;\n\t\tstd::multimap<int,u8> mm_int_uninit;\n\t\tstd::set<size_t> s_uint_uninit;\n\t\tstd::multiset<char> ms_char_uninit;\n\t\tstd::vector<double> v_double_uninit;\n\t\tstd::queue<double> q_double_uninit;\n\t\tstd::stack<double> st_double_uninit;\n\t\tstd::string str_uninit;\n\t\tstd::wstring wstr_uninit;\n\t}\n\n\n\t// also exercises all basic types because we need to display some values\n\t// anyway (to see at a glance whether symbol engine addrs are correct)\n\tstatic void m_test_addrs(int p_int, double p_double, char* p_pchar, uintptr_t p_uintptr)\n\t{\n\t\tsize_t l_uint = 0x1234;\n\t\tbool l_bool = true; UNUSED2(l_bool);\n\t\twchar_t l_wchars[] = L\"wchar string\";\n\t\tenum TestEnum { VAL1=1, VAL2=2 } l_enum = VAL1;\n\t\tu8 l_u8s[] = { 1,2,3,4 };\n\t\tvoid (*l_funcptr)(void) = m_test_stl;\n\n\t\tstatic double s_double = -2.718;\n\t\tstatic char s_chars[] = {'c','h','a','r','s',0};\n\t\tstatic void (*s_funcptr)(int, double, char*, uintptr_t) = m_test_addrs;\n\t\tstatic void* s_ptr = (void*)(uintptr_t)0x87654321;\n\t\tstatic HDC s_hdc = (HDC)0xff0;\n\n#if 0\t// output only needed when debugging\n\t\tdebug_printf(\"\\nTEST_ADDRS\\n\");\n\t\tdebug_printf(\"p_int     addr=%p val=%d\\n\", &p_int, p_int);\n\t\tdebug_printf(\"p_double  addr=%p val=%g\\n\", &p_double, p_double);\n\t\tdebug_printf(\"p_pchar   addr=%p val=%s\\n\", &p_pchar, p_pchar);\n\t\tdebug_printf(\"p_uintptr addr=%p val=%lu\\n\", &p_uintptr, p_uintptr);\n\n\t\tdebug_printf(\"l_uint    addr=%p val=%u\\n\", &l_uint, l_uint);\n\t\tdebug_printf(\"l_wchars  addr=%p val=%s\\n\", &l_wchars, utf8_from_wstring(l_wchars));\n\t\tdebug_printf(\"l_enum    addr=%p val=%d\\n\", &l_enum, l_enum);\n\t\tdebug_printf(\"l_u8s     addr=%p val=%d\\n\", &l_u8s, l_u8s);\n\t\tdebug_printf(\"l_funcptr addr=%p val=%p\\n\", &l_funcptr, l_funcptr);\n#else\n\t\tUNUSED2(p_uintptr);\n\t\tUNUSED2(p_pchar);\n\t\tUNUSED2(p_double);\n\t\tUNUSED2(p_int);\n\t\tUNUSED2(l_funcptr);\n\t\tUNUSED2(l_enum);\n\t\tUNUSED2(l_uint);\n\t\t(void)l_u8s;\n\t\t(void)l_wchars;\n#endif\n\n\t\tm_test_stl();\n\n\t\tint uninit_int; UNUSED2(uninit_int);\n\t\tfloat uninit_float; UNUSED2(uninit_float);\n\t\tdouble uninit_double; UNUSED2(uninit_double);\n\t\tbool uninit_bool; UNUSED2(uninit_bool);\n\t\tHWND uninit_hwnd; UNUSED2(uninit_hwnd);\n\t}\n\n#pragma optimize(\"\", on)\n\npublic:\n\tvoid test_stack_trace()\n\t{\n\t\tm_test_addrs(123, 3.1415926535897932384626, \"pchar string\", 0xf00d);\n\t}\n\n\tvoid test_stack_walk()\n\t{\n\t\tStatus ret;\n\n\t\tFunc3();\n\t\tTS_ASSERT(numCallers >= 3);\n\t\tsize_t foundFunctionBits = 0;\n\t\tvoid* funcAddresses[3] = { (void*)&Func1, (void*)&Func2, (void*)&Func3 };\n\t\tfor(size_t idxCaller = 0; idxCaller < numCallers; idxCaller++)\n\t\t{\n\t\t\twchar_t callerName[DEBUG_SYMBOL_CHARS];\n\t\t\tret = debug_ResolveSymbol(callers[idxCaller], callerName, 0, 0);\n\t\t\tTS_ASSERT_OK(ret);\n\n\t\t\twchar_t funcName[DEBUG_SYMBOL_CHARS];\n\t\t\tfor(size_t idxFunc = 0; idxFunc < ARRAY_SIZE(funcAddresses); idxFunc++)\n\t\t\t{\n\t\t\t\tret = debug_ResolveSymbol(funcAddresses[idxFunc], funcName, 0, 0);\n\t\t\t\tTS_ASSERT_OK(ret);\n\t\t\t\tif(wcsstr(funcName, callerName))\n\t\t\t\t\tfoundFunctionBits |= BIT(idxFunc);\n\t\t\t}\n\t\t}\n\t\tTS_ASSERT(foundFunctionBits == bit_mask<size_t>(ARRAY_SIZE(funcAddresses)));\n\t}\n};\n"
  },
  {
    "path": "fpsgame/gui/sysdep/os/win/wclipboard.cpp",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#include \"precompiled.h\"\n#include \"lib/sysdep/clipboard.h\"\n\n#include \"lib/sysdep/os/win/win.h\"\n#include \"lib/sysdep/os/win/wutil.h\"\n\n\n// caller is responsible for freeing hMem.\nstatic Status SetClipboardText(const wchar_t* text, HGLOBAL& hMem)\n{\n\tconst size_t numChars = wcslen(text);\n\thMem = GlobalAlloc(GMEM_MOVEABLE|GMEM_ZEROINIT, (numChars + 1) * sizeof(wchar_t));\n\tif(!hMem)\n\t\tWARN_RETURN(ERR::NO_MEM);\n\n\twchar_t* lockedText = (wchar_t*)GlobalLock(hMem);\n\tif(!lockedText)\n\t\tWARN_RETURN(ERR::NO_MEM);\n\twcscpy_s(lockedText, numChars+1, text);\n\tGlobalUnlock(hMem);\n\n\tHANDLE hData = SetClipboardData(CF_UNICODETEXT, hMem);\n\tif(!hData)\t// failed\n\t\tWARN_RETURN(ERR::FAIL);\n\n\treturn INFO::OK;\n}\n\n\n// @return INFO::OK iff text has been assigned a pointer (which the\n// caller must free via sys_clipboard_free) to the clipboard text.\nstatic Status GetClipboardText(wchar_t*& text)\n{\n\t// NB: Windows NT/2000+ auto convert CF_UNICODETEXT <-> CF_TEXT.\n\n\tif(!IsClipboardFormatAvailable(CF_UNICODETEXT))\n\t\treturn INFO::CANNOT_HANDLE;\n\n\tHGLOBAL hMem = GetClipboardData(CF_UNICODETEXT);\n\tif(!hMem)\n\t\tWARN_RETURN(ERR::FAIL);\n\n\tconst wchar_t* lockedText = (const wchar_t*)GlobalLock(hMem);\n\tif(!lockedText)\n\t\tWARN_RETURN(ERR::NO_MEM);\n\n\tconst size_t size = GlobalSize(hMem);\n\ttext = (wchar_t*)malloc(size);\n\tif(!text)\n\t\tWARN_RETURN(ERR::NO_MEM);\n\twcscpy_s(text, size/sizeof(wchar_t), lockedText);\n\n\t(void)GlobalUnlock(hMem);\n\n\treturn INFO::OK;\n}\n\n\n// OpenClipboard parameter.\n// NB: using wutil_AppWindow() causes GlobalLock to fail.\nstatic const HWND hWndNewOwner = 0;\t// MSDN: associate with \"current task\"\n\nStatus sys_clipboard_set(const wchar_t* text)\n{\n\tif(!OpenClipboard(hWndNewOwner))\n\t\tWARN_RETURN(ERR::FAIL);\n\n\tWARN_IF_FALSE(EmptyClipboard());\n\n\t// NB: to enable copy/pasting something other than text, add\n\t// message handlers for WM_RENDERFORMAT and WM_RENDERALLFORMATS.\n\tHGLOBAL hMem;\n\tStatus ret = SetClipboardText(text, hMem);\n\n\tWARN_IF_FALSE(CloseClipboard());\t// must happen before GlobalFree\n\n\tENSURE(GlobalFree(hMem) == 0);\t// (0 indicates success)\n\n\treturn ret;\n}\n\n\nwchar_t* sys_clipboard_get()\n{\n\tif(!OpenClipboard(hWndNewOwner))\n\t\treturn 0;\n\n\twchar_t* text;\n\tStatus ret = GetClipboardText(text);\n\n\tWARN_IF_FALSE(CloseClipboard());\n\n\treturn (ret == INFO::OK)? text : 0;\n}\n\n\nStatus sys_clipboard_free(wchar_t* text)\n{\n\tfree(text);\n\treturn INFO::OK;\n}\n"
  },
  {
    "path": "fpsgame/gui/sysdep/os/win/wcpu.cpp",
    "content": "/* Copyright (c) 2014 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * Windows implementation of sysdep/cpu\n */\n\n#include \"precompiled.h\"\n#include \"lib/sysdep/os/win/wcpu.h\"\n#include \"lib/sysdep/os_cpu.h\"\n\n#include \"lib/bits.h\"\n#include \"lib/alignment.h\"\n#include \"lib/module_init.h\"\n#include \"lib/sysdep/os/win/wutil.h\"\n#include \"lib/sysdep/arch/x86_x64/x86_x64.h\"\n\n\nuintptr_t os_cpu_ProcessorMask()\n{\n\tstatic uintptr_t processorMask;\n\n\tif(!processorMask)\n\t{\n\t\tconst HANDLE hProcess = GetCurrentProcess();\n\t\tDWORD_PTR processAffinity, systemAffinity;\n\t\tconst BOOL ok = GetProcessAffinityMask(hProcess, &processAffinity, &systemAffinity);\n\t\tENSURE(ok);\n\t\tENSURE(processAffinity != 0);\n\t\tprocessorMask = processAffinity;\n\t}\n\n\treturn processorMask;\n}\n\n\nsize_t os_cpu_NumProcessors()\n{\n\tstatic size_t numProcessors;\n\n\tif(!numProcessors)\n\t{\n\t\tnumProcessors = PopulationCount(os_cpu_ProcessorMask());\n\n\t\t// sanity check\n\t\tSYSTEM_INFO si;\n\t\tGetSystemInfo(&si);\t// guaranteed to succeed\n\t\tENSURE(numProcessors <= (size_t)si.dwNumberOfProcessors);\n\t\tENSURE(numProcessors >= 1);\n\t}\n\n\treturn numProcessors;\n}\n\n\n//-----------------------------------------------------------------------------\n\nStatus wcpu_ReadFrequencyFromRegistry(u32& freqMhz)\n{\n\tHKEY hKey;\n\tif(RegOpenKeyExW(HKEY_LOCAL_MACHINE, L\"HARDWARE\\\\DESCRIPTION\\\\System\\\\CentralProcessor\\\\0\", 0, KEY_QUERY_VALUE, &hKey) != ERROR_SUCCESS)\n\t\treturn ERR::NOT_SUPPORTED;\n\n\tDWORD size = sizeof(freqMhz);\n\tLONG ret = RegQueryValueExW(hKey, L\"~MHz\", 0, 0, (LPBYTE)&freqMhz, &size);\n\n\tRegCloseKey(hKey);\n\n\tif(ret != ERROR_SUCCESS)\n\t\tWARN_RETURN(ERR::FAIL);\n\n\treturn INFO::OK;\n}\n\n\nsize_t os_cpu_PageSize()\n{\n\tstatic size_t systemPageSize;\n\n\tif(!systemPageSize)\n\t{\n\t\tSYSTEM_INFO si;\n\t\tGetSystemInfo(&si);\t// guaranteed to succeed\n\t\tsystemPageSize = (size_t)si.dwPageSize;\n\t}\n\n\treturn systemPageSize;\n}\n\n\nsize_t os_cpu_LargePageSize()\n{\n\tstatic size_t largePageSize = ~(size_t)0;\t// \"0\" has special significance\n\n\tif(largePageSize == ~(size_t)0)\n\t{\n\t\tWUTIL_FUNC(pGetLargePageMinimum, SIZE_T, (void));\n\t\tWUTIL_IMPORT_KERNEL32(GetLargePageMinimum, pGetLargePageMinimum);\n\t\tif(pGetLargePageMinimum)\n\t\t{\n\t\t\tlargePageSize = pGetLargePageMinimum();\n\t\t\t// Note: checks disabled due to failing on Vista SP2 with old Xeon CPU\n\t\t\t//\tsee http://trac.wildfiregames.com/ticket/2346\n\t\t\t//ENSURE(largePageSize != 0);\t// IA-32 and AMD64 definitely support large pages\n\t\t\t//ENSURE(largePageSize > os_cpu_PageSize());\n\t\t}\n\t\t// no OS support for large pages\n\t\telse\n\t\t\tlargePageSize = 0;\n\t}\n\n\treturn largePageSize;\n}\n\n\nstatic void GetMemoryStatus(MEMORYSTATUSEX& mse)\n{\n\t// note: we no longer bother dynamically importing GlobalMemoryStatusEx -\n\t// it's available on Win2k and above. this function safely handles\n\t// systems with > 4 GB of memory.\n\tmse.dwLength = sizeof(mse);\n\tconst BOOL ok = GlobalMemoryStatusEx(&mse);\n\tWARN_IF_FALSE(ok);\n}\n\nsize_t os_cpu_QueryMemorySize()\n{\n\tMEMORYSTATUSEX mse;\n\tGetMemoryStatus(mse);\n\tDWORDLONG memorySize = mse.ullTotalPhys;\n\n\t// Richter, \"Programming Applications for Windows\": the reported\n\t// value doesn't include non-paged pool reserved during boot;\n\t// it's not considered available to the kernel. (the amount is\n\t// 528 KiB on a 512 MiB WinXP/Win2k machine). we'll round up\n\t// to the nearest megabyte to fix this.\n\tmemorySize = round_up(memorySize, DWORDLONG(1*MiB));\t\t// (Align<> cannot compute DWORDLONG)\n\n\treturn size_t(memorySize / MiB);\n}\n\nsize_t os_cpu_MemoryAvailable()\n{\n\tMEMORYSTATUSEX mse;\n\tGetMemoryStatus(mse);\n\tconst size_t memoryAvailableMiB = size_t(mse.ullAvailPhys / MiB);\n\treturn memoryAvailableMiB;\n}\n\n\n//-----------------------------------------------------------------------------\n\nDWORD_PTR wcpu_AffinityFromProcessorMask(DWORD_PTR processAffinity, uintptr_t processorMask)\n{\n\tDWORD_PTR affinity = 0;\n\n\tsize_t processor = (size_t)-1;\n\tfor(DWORD processorNumber = 0; processorNumber < (DWORD)os_cpu_MaxProcessors; processorNumber++)\n\t{\n\t\tif(IsBitSet(processAffinity, processorNumber))\n\t\t{\n\t\t\t++processor;\t// index among the affinity's set bits\n\n\t\t\tif(IsBitSet(processorMask, processor))\n\t\t\t\taffinity |= DWORD_PTR(1) << processorNumber;\n\t\t}\n\t}\n\n\treturn affinity;\n}\n\nuintptr_t wcpu_ProcessorMaskFromAffinity(DWORD_PTR processAffinity, DWORD_PTR affinity)\n{\n\tuintptr_t processorMask = 0;\n\n\tsize_t processor = (size_t)-1;\n\tfor(DWORD processorNumber = 0; processorNumber < (DWORD)os_cpu_MaxProcessors; processorNumber++)\n\t{\n\t\tif(IsBitSet(processAffinity, processorNumber))\n\t\t{\n\t\t\t++processor;\t// now corresponds to processorNumber\n\n\t\t\tif(IsBitSet(affinity, processorNumber))\n\t\t\t\tprocessorMask |= uintptr_t(1) << processor;\n\t\t}\n\t}\n\n\treturn processorMask;\n}\n\n\n//-----------------------------------------------------------------------------\n\nstatic void VerifyRunningOnCorrectProcessors(DWORD_PTR affinity)\n{\n\tDWORD currentProcessor;\n\n\t// note: NtGetCurrentProcessorNumber and RtlGetCurrentProcessorNumber aren't\n\t// implemented on WinXP SP2.\n\tWUTIL_FUNC(pGetCurrentProcessorNumber, DWORD, (void));\n\tWUTIL_IMPORT_KERNEL32(GetCurrentProcessorNumber, pGetCurrentProcessorNumber);\n\tif(pGetCurrentProcessorNumber)\n\t\tcurrentProcessor = pGetCurrentProcessorNumber();\n\telse\n\t{\n\t\t// note: searching for the current APIC ID or IDT address in a\n\t\t// table won't work because initializing the table also requires\n\t\t// this function. LSL only works on Vista (which already\n\t\t// has GetCurrentProcessorNumber).\n\t\treturn;\n\t}\n\n\tENSURE(IsBitSet(affinity, currentProcessor));\n}\n\n\nuintptr_t os_cpu_SetThreadAffinityMask(uintptr_t processorMask)\n{\n\tconst size_t numProcessors = os_cpu_NumProcessors();\n\t// (avoid undefined result when right shift count >= number of bits)\n\tENSURE(numProcessors == sizeof(processorMask)*CHAR_BIT || (processorMask >> numProcessors) == 0);\n\n\tDWORD_PTR processAffinity, systemAffinity;\n\tconst BOOL ok = GetProcessAffinityMask(GetCurrentProcess(), &processAffinity, &systemAffinity);\n\tWARN_IF_FALSE(ok);\n\n\tconst DWORD_PTR affinity = wcpu_AffinityFromProcessorMask(processAffinity, processorMask);\n\tconst DWORD_PTR previousAffinity = SetThreadAffinityMask(GetCurrentThread(), affinity);\n\tENSURE(previousAffinity != 0);\t// ensure function didn't fail\n\t// (MSDN says SetThreadAffinityMask takes care of rescheduling)\n\tVerifyRunningOnCorrectProcessors(affinity);\n\n\tconst uintptr_t previousProcessorMask = wcpu_ProcessorMaskFromAffinity(processAffinity, previousAffinity);\n\treturn previousProcessorMask;\n}\n\n\nStatus os_cpu_CallByEachCPU(OsCpuCallback cb, uintptr_t cbData)\n{\n\t// abort if we can't run on all system processors\n\tDWORD_PTR processAffinity, systemAffinity;\n\t{\n\t\tconst BOOL ok = GetProcessAffinityMask(GetCurrentProcess(), &processAffinity, &systemAffinity);\n\t\tWARN_IF_FALSE(ok);\n\t\tif(processAffinity != systemAffinity)\n\t\t\treturn ERR::OS_CPU_RESTRICTED_AFFINITY;\t// NOWARN\n\t}\n\n\tconst uintptr_t previousAffinity = os_cpu_SetThreadAffinityMask(os_cpu_ProcessorMask());\n\n\tfor(size_t processor = 0; processor < os_cpu_NumProcessors(); processor++)\n\t{\n\t\tconst uintptr_t processorMask = uintptr_t(1) << processor;\n\t\tos_cpu_SetThreadAffinityMask(processorMask);\n\t\tcb(processor, cbData);\n\t}\n\n\t(void)os_cpu_SetThreadAffinityMask(previousAffinity);\n\n\treturn INFO::OK;\n}\n"
  },
  {
    "path": "fpsgame/gui/sysdep/os/win/wcpu.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * Windows backend of os_cpu\n */\n\n#ifndef INCLUDED_WCPU\n#define INCLUDED_WCPU\n\n#include \"lib/sysdep/os/win/win.h\"\n\nextern Status wcpu_ReadFrequencyFromRegistry(u32& freqMhz);\n\n// \"affinity\" and \"processorNumber\" are what Windows sees.\n// \"processorMask\" and \"processor\" are the idealized representation we expose\n// to users. the latter insulates them from process affinity restrictions by\n// defining IDs as indices of the nonzero bits within the process affinity.\n// these routines are provided for the benefit of wnuma.\n\nextern DWORD_PTR wcpu_AffinityFromProcessorMask(DWORD_PTR processAffinity, uintptr_t processorMask);\nextern uintptr_t wcpu_ProcessorMaskFromAffinity(DWORD_PTR processAffinity, DWORD_PTR affinity);\n\n#endif\t// #ifndef INCLUDED_WCPU\n"
  },
  {
    "path": "fpsgame/gui/sysdep/os/win/wcursor.cpp",
    "content": "/* Copyright (c) 2012 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#include \"precompiled.h\"\n#include \"lib/sysdep/cursor.h\"\n\n#include \"lib/sysdep/gfx.h\"\n#include \"lib/sysdep/os/win/win.h\"\n#include \"lib/sysdep/os/win/wutil.h\"\n\nstatic sys_cursor cursor_from_HICON(HICON hIcon)\n{\n\treturn (sys_cursor)(uintptr_t)hIcon;\n}\n\nstatic sys_cursor cursor_from_HCURSOR(HCURSOR hCursor)\n{\n\treturn (sys_cursor)(uintptr_t)hCursor;\n}\n\nstatic HICON HICON_from_cursor(sys_cursor cursor)\n{\n\treturn (HICON)(uintptr_t)cursor;\n}\n\nstatic HCURSOR HCURSOR_from_cursor(sys_cursor cursor)\n{\n\treturn (HCURSOR)(uintptr_t)cursor;\n}\n\n\nstatic Status sys_cursor_create_common(int w, int h, void* bgra_img, void* mask_img, int hx, int hy, sys_cursor* cursor)\n{\n\t*cursor = 0;\n\n\t// MSDN says selecting this HBITMAP into a DC is slower since we use\n\t// CreateBitmap; bpp/format must be checked against those of the DC.\n\t// this is the simplest way and we don't care about slight performance\n\t// differences because this is typically only called once.\n\tHBITMAP hbmColor = CreateBitmap(w, h, 1, 32, bgra_img);\n\n\t// CreateIconIndirect doesn't access this; we just need to pass\n\t// an empty bitmap.\n\tHBITMAP hbmMask = CreateBitmap(w, h, 1, 1, mask_img);\n\n\t// create the cursor (really an icon; they differ only in\n\t// fIcon and the hotspot definitions).\n\tICONINFO ii;\n\tii.fIcon = FALSE;  // cursor\n\tii.xHotspot = (DWORD)hx;\n\tii.yHotspot = (DWORD)hy;\n\tii.hbmMask  = hbmMask;\n\tii.hbmColor = hbmColor;\n\tHICON hIcon = CreateIconIndirect(&ii);\n\n\t// CreateIconIndirect makes copies, so we no longer need these.\n\tDeleteObject(hbmMask);\n\tDeleteObject(hbmColor);\n\n\tif(!wutil_IsValidHandle(hIcon))\n\t\tWARN_RETURN(ERR::FAIL);\n\n\t*cursor = cursor_from_HICON(hIcon);\n\treturn INFO::OK;\n}\n\nStatus sys_cursor_create(int w, int h, void* bgra_img, int hx, int hy, sys_cursor* cursor)\n{\n\t// alpha-blended cursors do not work on a 16-bit display\n\t// (they get drawn as a black square), so refuse to load the\n\t// cursor in that case\n\tint bpp = 0;\n\tRETURN_STATUS_IF_ERR(gfx::GetVideoMode(NULL, NULL, &bpp, NULL));\n\tif (bpp <= 16)\n\t\treturn ERR::FAIL;\n\n\treturn sys_cursor_create_common(w, h, bgra_img, NULL, hx, hy, cursor);\n}\n\nStatus sys_cursor_create_empty(sys_cursor* cursor)\n{\n\t// the mask gets ignored on 32-bit displays, but is used on 16-bit displays;\n\t// setting it to 0xFF makes the cursor invisible (though I'm not quite\n\t// sure why it's that way round)\n\tu8 bgra_img[] = {0, 0, 0, 0};\n\tu8 mask_img[] = {0xFF};\n\treturn sys_cursor_create_common(1, 1, bgra_img, mask_img, 0, 0, cursor);\n}\n\n\nStatus sys_cursor_set(sys_cursor cursor)\n{\n\t// restore default cursor.\n\tif(!cursor)\n\t\tcursor = cursor_from_HCURSOR(LoadCursor(0, MAKEINTRESOURCE(IDC_ARROW)));\n\n\t(void)SetCursor(HCURSOR_from_cursor(cursor));\n\t// return value (previous cursor) is useless.\n\n\treturn INFO::OK;\n}\n\n\nStatus sys_cursor_free(sys_cursor cursor)\n{\n\t// bail now to prevent potential confusion below; there's nothing to do.\n\tif(!cursor)\n\t\treturn INFO::OK;\n\n\t// if the cursor being freed is active, restore the default arrow\n\t// (just for safety).\n\tif(cursor_from_HCURSOR(GetCursor()) == cursor)\n\t\tWARN_IF_ERR(sys_cursor_set(0));\n\n\tif(!DestroyIcon(HICON_from_cursor(cursor)))\n\t\tWARN_RETURN(StatusFromWin());\n\treturn INFO::OK;\n}\n\nStatus sys_cursor_reset()\n{\n\treturn INFO::OK;\n}\n"
  },
  {
    "path": "fpsgame/gui/sysdep/os/win/wdbg.cpp",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * Win32 debug support code.\n */\n\n#include \"precompiled.h\"\n#include \"lib/debug.h\"\n\n#include \"lib/bits.h\"\n#include \"lib/sysdep/os/win/win.h\"\n#include \"lib/sysdep/os/win/wutil.h\"\n\n\n// return 1 if the pointer appears to be totally bogus, otherwise 0.\n// this check is not authoritative (the pointer may be \"valid\" but incorrect)\n// but can be used to filter out obviously wrong values in a portable manner.\nint debug_IsPointerBogus(const void* p)\n{\n\tif(p < (void*)0x10000)\n\t\treturn true;\n#if ARCH_AMD64\n\tif(p == (const void*)(uintptr_t)0xCCCCCCCCCCCCCCCCull)\n\t\treturn true;\n#elif ARCH_IA32\n\tif(p == (const void*)(uintptr_t)0xCCCCCCCCul)\n\t\treturn true;\n#endif\n\n\t// notes:\n\t// - we don't check alignment because nothing can be assumed about a\n\t//   string pointer and we mustn't reject any actually valid pointers.\n\t// - nor do we bother checking the address against known stack/heap areas\n\t//   because that doesn't cover everything (e.g. DLLs, VirtualAlloc).\n\t// - cannot use IsBadReadPtr because it accesses the mem\n\t//   (false alarm for reserved address space).\n\n\treturn false;\n}\n\n\nbool debug_IsCodePointer(void* p)\n{\n\tuintptr_t addr = (uintptr_t)p;\n\t// totally invalid pointer\n\tif(debug_IsPointerBogus(p))\n\t\treturn false;\n\t// comes before load address\n\tstatic const HMODULE base = GetModuleHandle(0);\n\tif(addr < (uintptr_t)base)\n\t\treturn false;\n\n\treturn true;\n}\n\n\nbool debug_IsStackPointer(void* p)\n{\n\tuintptr_t addr = (uintptr_t)p;\n\t// totally invalid pointer\n\tif(debug_IsPointerBogus(p))\n\t\treturn false;\n\t// not aligned\n\tif(addr % sizeof(void*))\n\t\treturn false;\n\t// out of bounds (note: IA-32 stack grows downwards)\n\tNT_TIB* tib = (NT_TIB*)NtCurrentTeb();\n\tif(!(tib->StackLimit < p && p < tib->StackBase))\n\t\treturn false;\n\n\treturn true;\n}\n\n\nvoid debug_puts(const char* text)\n{\n\tOutputDebugStringW(wstring_from_utf8(text).c_str());\n}\n\n\nvoid wdbg_printf(const wchar_t* fmt, ...)\n{\n\twchar_t buf[1024+1];\t// wvsprintfW will truncate to this size\n\tva_list ap;\n\tva_start(ap, fmt);\n\twvsprintfW(buf, fmt, ap);\t// (return value doesn't indicate whether truncation occurred)\n\tva_end(ap);\n\n\tOutputDebugStringW(buf);\n}\n\n\n// inform the debugger of the current thread's description, which it then\n// displays instead of just the thread handle.\n//\n// see \"Setting a Thread Name (Unmanaged)\": http://msdn2.microsoft.com/en-us/library/xcb2z8hs(vs.71).aspx\nvoid debug_SetThreadName(const char* name)\n{\n\t// we pass information to the debugger via a special exception it\n\t// swallows. if not running under one, bail now to avoid\n\t// \"first chance exception\" warnings.\n\tif(!IsDebuggerPresent())\n\t\treturn;\n\n\t// presented by Jay Bazuzi (from the VC debugger team) at TechEd 1999.\n\tconst struct ThreadNameInfo\n\t{\n\t\tDWORD type;\n\t\tconst char* name;\n\t\tDWORD thread_id;\t// any valid ID or -1 for current thread\n\t\tDWORD flags;\n\t}\n\tinfo = { 0x1000, name, (DWORD)-1, 0 };\n\t__try\n\t{\n\t\tRaiseException(0x406D1388, 0, sizeof(info)/sizeof(DWORD), (ULONG_PTR*)&info);\n\t}\n\t__except(EXCEPTION_EXECUTE_HANDLER)\n\t{\n\t\t// if we get here, the debugger didn't handle the exception.\n\t\t// this happens if profiling with Dependency Walker; ENSURE\n\t\t// must not be called because we may be in critical init.\n\t}\n}\n"
  },
  {
    "path": "fpsgame/gui/sysdep/os/win/wdbg.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * Win32 debug support code.\n */\n\n#ifndef INCLUDED_WDBG\n#define INCLUDED_WDBG\n\n/**\n * same as debug_printf except that some type conversions aren't supported\n * (in particular, no floating point) and output is limited to 1024+1 characters.\n *\n * this function does not allocate memory from the CRT heap, which makes it\n * safe to use from an allocation hook.\n **/\nLIB_API void wdbg_printf(const wchar_t* fmt, ...);\n\n/**\n * similar to ENSURE but safe to use during critical init or\n * while under the heap or dbghelp locks.\n **/\n#define wdbg_assert(expr) STMT(if(!(expr)) debug_break();)\n\n#endif\t// #ifndef INCLUDED_WDBG\n"
  },
  {
    "path": "fpsgame/gui/sysdep/os/win/wdbg_heap.cpp",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#include \"precompiled.h\"\n#include \"lib/sysdep/os/win/wdbg_heap.h\"\n\n#include \"lib/sysdep/os/win/win.h\"\n#include <crtdbg.h>\n#include <excpt.h>\n\n#include \"lib/external_libraries/dbghelp.h\"\n#include \"lib/sysdep/cpu.h\"\t// cpu_AtomicAdd\n#include \"lib/sysdep/os/win/winit.h\"\n#include \"lib/sysdep/os/win/wdbg.h\"       // wdbg_printf\n#include \"lib/sysdep/os/win/wdbg_sym.h\"   // wdbg_sym_WalkStack\n\n\nWINIT_REGISTER_EARLY_INIT2(wdbg_heap_Init);\t// wutil -> wdbg_heap\nWINIT_REGISTER_LATE_SHUTDOWN2(wdbg_heap_Shutdown);\t// last - no leaks are detected after this\n\n\nvoid wdbg_heap_Enable(bool enable)\n{\n#ifdef _DEBUG\t// (avoid \"expression has no effect\" warning in release builds)\n\tint flags = 0;\n\tif(enable)\n\t{\n\t\tflags |= _CRTDBG_ALLOC_MEM_DF;\t// enable checks at deallocation time\n\t\tflags |= _CRTDBG_LEAK_CHECK_DF;\t// report leaks at exit\n#if 0\n\t\tflags |= _CRTDBG_CHECK_ALWAYS_DF;\t// check during every heap operation (too slow to be practical)\n\t\tflags |= _CRTDBG_DELAY_FREE_MEM_DF;\t// memory is never actually freed\n#endif\n\t}\n\t_CrtSetDbgFlag(flags);\n\n\t// Send output to stdout as well as the debug window, so it works during\n\t// the normal build process as well as when debugging the test .exe\n\t_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);\n\t_CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDOUT);\n#else\n\tUNUSED2(enable);\n#endif\n}\n\n\nvoid wdbg_heap_Validate()\n{\n\tint ok = TRUE;\n\t__try\n\t{\n\t\t// NB: this is a no-op if !_CRTDBG_ALLOC_MEM_DF.\n\t\t// we could call _heapchk but that would catch fewer errors.\n\t\tok = _CrtCheckMemory();\n\t}\n\t__except(EXCEPTION_EXECUTE_HANDLER)\n\t{\n\t\tok = FALSE;\n\t}\n\n\twdbg_assert(ok == TRUE);\t// else: heap is corrupt!\n}\n\n\n//-----------------------------------------------------------------------------\n// improved leak detection\n//-----------------------------------------------------------------------------\n\n// (this relies on the debug CRT; not compiling it at all in release builds\n// avoids unreferenced local function warnings)\n// (this has only been tested on IA32 and seems to have trouble with larger\n// pointers and is horribly expensive, so it's disabled for now.)\n#if !defined(NDEBUG) && ARCH_IA32 && 0\n# define ENABLE_LEAK_INSTRUMENTATION 1\n#else\n# define ENABLE_LEAK_INSTRUMENTATION 0\n#endif\n\n#if ENABLE_LEAK_INSTRUMENTATION\n\n// leak detectors often rely on macro redirection to determine the file and\n// line of allocation owners (see _CRTDBG_MAP_ALLOC). unfortunately this\n// breaks code that uses placement new or functions called free() etc.\n//\n// we avoid this problem by using stack traces. this implementation differs\n// from other approaches, e.g. Visual Leak Detector (the safer variant\n// before DLL hooking was used) in that no auxiliary storage is needed.\n// instead, the trace is stashed within the memory block header.\n//\n// to avoid duplication of effort, the CRT's leak detection code is not\n// modified; we only need an allocation and report hook. the latter\n// mixes the improved file/line information into the normal report.\n\n\n//-----------------------------------------------------------------------------\n// memory block header\n\n// the one disadvantage of our approach is that it requires knowledge of\n// the internal memory block header structure. it is hoped that IsValid will\n// uncover any changes. the following definition was adapted from dbgint.h:\nstruct _CrtMemBlockHeader\n{\n\tstruct _CrtMemBlockHeader* next;\n\tstruct _CrtMemBlockHeader* prev;\n\tchar* file;\n\tint line;\n\t// fields reversed on Win64 to ensure size % 16 == 0\n#if OS_WIN64\n\tint blockType;\n\tsize_t userDataSize;\n#else\n\tsize_t userDataSize;\n\tint blockType;\n#endif\n\tlong allocationNumber;\n\tu8 gap[4];\n\n\tbool IsValid() const\n\t{\n\t\t__try\n\t\t{\n\t\t\tif(prev && prev->next != this)\n\t\t\t\treturn false;\n\t\t\tif(next && next->prev != this)\n\t\t\t\treturn false;\n\t\t\tif((unsigned)blockType > 4)\n\t\t\t\treturn false;\n\t\t\tif(userDataSize > 1*GiB)\n\t\t\t\treturn false;\n\t\t\tif(allocationNumber == 0)\n\t\t\t\treturn false;\n\t\t\tfor(int i = 0; i < 4; i++)\n\t\t\t{\n\t\t\t\tif(gap[i] != 0xFD)\n\t\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// this is a false alarm if there is exactly one extant allocation,\n\t\t\t// but also a valuable indication of a block that has been removed\n\t\t\t// from the list (i.e. freed).\n\t\t\tif(prev == next)\n\t\t\t\treturn false;\n\t\t}\n\t\t__except(EXCEPTION_EXECUTE_HANDLER)\n\t\t{\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t}\n};\n\nstatic _CrtMemBlockHeader* HeaderFromData(void* userData)\n{\n\t_CrtMemBlockHeader* const header = ((_CrtMemBlockHeader*)userData)-1;\n\twdbg_assert(header->IsValid());\n\treturn header;\n}\n\n\n/**\n * update our idea of the head of the linked list of heap blocks.\n * called from the allocation hook (see explanation there)\n *\n * @return the current head (most recent allocation).\n * @param operation the current heap operation\n * @param userData allocation address (if reallocating or deallocating)\n * @param hasChanged a convenient indication of whether the return value is\n * different than that of the last call.\n **/\nstatic _CrtMemBlockHeader* GetHeapListHead(int operation, void* userData, bool& hasChanged)\n{\n\tstatic _CrtMemBlockHeader* s_heapListHead;\n\n\t// first call: get the heap block list head\n\t// notes:\n\t// - there is no O(1) accessor for this, so we maintain a copy.\n\t// - must be done here instead of in an initializer to guarantee\n\t//   consistency, since we are now under the _HEAP_LOCK.\n\tif(!s_heapListHead)\n\t{\n\t\t_CrtMemState state = {0};\n\t\t_CrtMemCheckpoint(&state);\t// O(N)\n\t\ts_heapListHead = state.pBlockHeader;\n\t\twdbg_assert(s_heapListHead->IsValid());\n\t}\n\n\t// the last operation was an allocation or expanding reallocation;\n\t// exactly one block has been prepended to the list.\n\tif(s_heapListHead->prev)\n\t{\n\t\ts_heapListHead = s_heapListHead->prev;\t// set to new head of list\n\t\twdbg_assert(s_heapListHead->IsValid());\n\t\twdbg_assert(s_heapListHead->prev == 0);\n\t\thasChanged = true;\n\t}\n\t// the list head remained unchanged, so the last operation was a\n\t// non-expanding reallocation or free.\n\telse\n\t\thasChanged = false;\n\n\t// special case: handle invalidation of the list head\n\t// note: even shrinking reallocations cause deallocation.\n\tif(operation != _HOOK_ALLOC && userData == s_heapListHead+1)\n\t{\n\t\ts_heapListHead = s_heapListHead->next;\n\t\twdbg_assert(s_heapListHead->IsValid());\n\n\t\thasChanged = false;\t// (head is now the same as last time)\n\t}\n\n\treturn s_heapListHead;\n}\n\n\n//-----------------------------------------------------------------------------\n// call stack filter\n\n// we need to make the most out of the limited amount of frames. to that end,\n// only user functions are stored; we skip known library and helper functions.\n// these are determined by recording frames encountered in a backtrace.\n\n/**\n * extents of a module in memory; used to ignore callers that lie within\n * the C runtime library.\n **/\nclass ModuleExtents\n{\npublic:\n\tModuleExtents()\n\t\t: m_address(0), m_length(0)\n\t{\n\t}\n\n\tModuleExtents(const wchar_t* dllName)\n\t{\n\t\tHMODULE hModule = GetModuleHandleW(dllName);\n\t\tPIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS)((u8*)hModule + ((PIMAGE_DOS_HEADER)hModule)->e_lfanew);\n\t\tm_address = (uintptr_t)hModule + ntHeaders->OptionalHeader.BaseOfCode;\n\t\tMEMORY_BASIC_INFORMATION mbi = {0};\n\t\tVirtualQuery((void*)m_address, &mbi, sizeof(mbi));\n\t\tm_length = mbi.RegionSize;\n\t}\n\n\tuintptr_t Address() const\n\t{\n\t\treturn m_address;\n\t}\n\n\tuintptr_t Length() const\n\t{\n\t\treturn m_length;\n\t}\n\n\tbool Contains(uintptr_t address) const\n\t{\n\t\treturn (address - m_address) < m_length;\n\t}\n\nprivate:\n\tuintptr_t m_address;\n\tsize_t m_length;\n};\n\n\n/**\n * set data structure that avoids dynamic allocations because they would\n * cause the allocation hook to be reentered (bad).\n **/\ntemplate<typename T, size_t maxItems>\nclass ArraySet\n{\npublic:\n\tArraySet()\n\t{\n\t\tm_arrayEnd = m_array;\n\t}\n\n\tvoid Add(T t)\n\t{\n\t\tif(m_arrayEnd == m_array+maxItems)\n\t\t{\n\t\t\tRemoveDuplicates();\n\t\t\twdbg_assert(m_arrayEnd < m_array+maxItems);\n\t\t}\n\t\t*m_arrayEnd++ = t;\n\t}\n\n\tbool Find(T t) const\n\t{\n\t\treturn std::find(m_array, const_cast<const T*>(m_arrayEnd), t) != m_arrayEnd;\n\t}\n\n\tvoid RemoveDuplicates()\n\t{\n\t\tstd::sort(m_array, m_arrayEnd);\n\t\tm_arrayEnd = std::unique(m_array, m_arrayEnd);\n\t}\n\nprivate:\n\tT m_array[maxItems];\n\tT* m_arrayEnd;\n};\n\n\nclass CallerFilter\n{\npublic:\n\tCallerFilter()\n\t{\n\t\tAddRuntimeLibraryToIgnoreList();\n\n\t\tm_isRecordingKnownCallers = true;\n\t\tCallHeapFunctions();\n\t\tm_isRecordingKnownCallers = false;\n\t\tm_knownCallers.RemoveDuplicates();\n\t}\n\n\tStatus NotifyOfCaller(uintptr_t pc)\n\t{\n\t\tif(!m_isRecordingKnownCallers)\n\t\t\treturn INFO::SKIPPED;\n\n\t\t// last 'known' function has been reached\n\t\tif(pc == (uintptr_t)&CallerFilter::CallHeapFunctions)\n\t\t\treturn INFO::ALL_COMPLETE;\n\n\t\t// pc is a 'known' function on the allocation hook's back-trace\n\t\t// (e.g. _malloc_dbg and other helper functions)\n\t\tm_knownCallers.Add(pc);\n\t\treturn INFO::OK;\n\t}\n\n\tbool IsKnownCaller(uintptr_t pc) const\n\t{\n\t\tfor(size_t i = 0; i < numModules; i++)\n\t\t{\n\t\t\tif(m_moduleIgnoreList[i].Contains(pc))\n\t\t\t\treturn true;\n\t\t}\n\n\t\treturn m_knownCallers.Find(pc);\n\t}\n\nprivate:\n\tstatic const size_t numModules = 2;\n\n\tvoid AddRuntimeLibraryToIgnoreList()\n\t{\n#if MSC_VERSION && _DLL\t// DLL runtime library\n#ifdef NDEBUG\n\t\tstatic const wchar_t* dllNameFormat = L\"msvc%c%d\" L\".dll\";\n#else\n\t\tstatic const wchar_t* dllNameFormat = L\"msvc%c%d\" L\"d\" L\".dll\";\n#endif\n\t\tconst int dllVersion = (MSC_VERSION-600)/10;\t// VC2005: 1400 => 80\n\t\twdbg_assert(0 < dllVersion && dllVersion <= 999);\n\t\tfor(int i = 0; i < numModules; i++)\n\t\t{\n\t\t\tstatic const char modules[numModules] = { 'r', 'p' };\t// C and C++ runtime libraries\n\t\t\twchar_t dllName[20];\n\t\t\tswprintf_s(dllName, ARRAY_SIZE(dllName), dllNameFormat, modules[i], dllVersion);\n\t\t\tm_moduleIgnoreList[i] = ModuleExtents(dllName);\n\t\t}\n#endif\n\t}\n\n\tstatic void CallHeapFunctions()\n\t{\n\t\t{\n\t\t\tvoid* p1 = malloc(1);\n\t\t\tvoid* p2 = realloc(p1, 111);\n\t\t\tif(p2)\n\t\t\t\tfree(p2);\n\t\t\telse\n\t\t\t\tfree(p1);\n\t\t}\n\t\t{\n\t\t\tu8* p = new u8;\n\t\t\tdelete p;\n\t\t}\n\t\t{\n\t\t\tu8* p = new u8[2];\n\t\t\tdelete[] p;\n\t\t}\n\t}\n\n\tModuleExtents m_moduleIgnoreList[numModules];\n\n\t// note: this mechanism cannot hope to exclude every single STL helper\n\t// function, which is why we need the module ignore list.\n\t// however, it is still useful when compiling against the static CRT.\n\tbool m_isRecordingKnownCallers;\n\tArraySet<uintptr_t, 500> m_knownCallers;\n};\n\n\n//-----------------------------------------------------------------------------\n// stash (part of) a stack trace within _CrtMemBlockHeader\n\n// this avoids the need for a mapping between allocation number and the\n// caller information, which is slow, requires locking and consumes memory.\n//\n// callers := array of addresses inside functions that constitute the\n// stack back-trace.\n\nstatic const size_t numQuantizedPcBits = sizeof(uintptr_t)*CHAR_BIT - 2;\n\nstatic uintptr_t Quantize(uintptr_t pc)\n{\n\t// postcondition: the return value lies within the same function as\n\t// pc but can be stored in fewer bits. this is possible because:\n\t// - linkers typically align functions to at least four bytes\n\t// - pc is a return address and thus preceded by a call instruction and\n\t//   function prolog, which requires at least four bytes.\n\treturn pc/4;\n}\n\nstatic uintptr_t Expand(uintptr_t pc)\n{\n\treturn pc*4;\n}\n\n\nstatic const size_t numEncodedLengthBits = 2;\nstatic const size_t maxCallers = (sizeof(char*)+sizeof(int))*CHAR_BIT / (2+14);\n\nstatic size_t NumBitsForEncodedLength(size_t encodedLength)\n{\n\tstatic const size_t numBitsForEncodedLength[1u << numEncodedLengthBits] =\n\t{\n\t\t8,\t// 1K\n\t\t14,\t// 64K\n\t\t20,\t// 4M\n\t\tnumQuantizedPcBits\t// a full pointer\n\t};\n\treturn numBitsForEncodedLength[encodedLength];\n}\n\nstatic size_t EncodedLength(uintptr_t quantizedOffset)\n{\n\tfor(size_t encodedLength = 0; encodedLength < 1u << numEncodedLengthBits; encodedLength++)\n\t{\n\t\tconst size_t numBits = NumBitsForEncodedLength(encodedLength);\n\t\tconst uintptr_t maxValue = (1u << numBits)-1;\n\t\tif(quantizedOffset <= maxValue)\n\t\t\treturn encodedLength;\n\t}\n\n\twdbg_assert(0);\t// unreachable\n\treturn 0;\n}\n\n\nstatic uintptr_t codeSegmentAddress;\nstatic uintptr_t quantizedCodeSegmentAddress;\nstatic uintptr_t quantizedCodeSegmentLength;\n\nstatic void FindCodeSegment()\n{\n\tconst wchar_t* dllName = 0;\t// current module\n\tModuleExtents extents(dllName);\n\tcodeSegmentAddress = extents.Address();\n\tquantizedCodeSegmentAddress = Quantize(codeSegmentAddress);\n\tquantizedCodeSegmentLength = Quantize(extents.Length());\n}\n\n\nclass BitStream\n{\npublic:\n\tBitStream(u8* storage, size_t storageSize)\n\t\t: m_remainderBits(0), m_numRemainderBits(0)\n\t\t, m_pos(storage), m_bitsLeft((size_t)storageSize*8)\n\t{\n\t}\n\n\tsize_t BitsLeft() const\n\t{\n\t\treturn m_bitsLeft;\n\t}\n\n\tvoid Write(const size_t numOutputBits, uintptr_t outputValue)\n\t{\n\t\twdbg_assert(numOutputBits <= m_bitsLeft);\n\t\twdbg_assert(outputValue < ((uintptr_t)1u << numOutputBits));\n\n\t\tsize_t outputBitsLeft = numOutputBits;\n\t\twhile(outputBitsLeft > 0)\n\t\t{\n\t\t\tconst size_t numBits = std::min(outputBitsLeft, size_t(8));\n\t\t\tm_bitsLeft -= numBits;\n\n\t\t\t// (NB: there is no need to extract exactly numBits because\n\t\t\t// outputValue's MSBs were verified to be zero)\n\t\t\tconst uintptr_t outputByte = outputValue & 0xFF;\n\t\t\toutputValue >>= 8;\n\t\t\toutputBitsLeft -= numBits;\n\n\t\t\tm_remainderBits |= outputByte << m_numRemainderBits;\n\t\t\tm_numRemainderBits += numBits;\n\t\t\tif(m_numRemainderBits >= 8)\n\t\t\t{\n\t\t\t\tconst u8 remainderByte = (m_remainderBits & 0xFF);\n\t\t\t\tm_remainderBits >>= 8;\n\t\t\t\tm_numRemainderBits -= 8;\n\n\t\t\t\t*m_pos++ = remainderByte;\n\t\t\t}\n\t\t}\n\t}\n\n\tvoid Finish()\n\t{\n\t\tconst size_t partialBits = m_numRemainderBits % 8;\n\t\tif(partialBits)\n\t\t{\n\t\t\tm_bitsLeft -= 8-partialBits;\n\t\t\tm_numRemainderBits += 8-partialBits;\n\t\t}\n\t\twhile(m_numRemainderBits)\n\t\t{\n\t\t\tconst u8 remainderByte = (m_remainderBits & 0xFF);\n\t\t\t*m_pos++ = remainderByte;\n\t\t\tm_remainderBits >>= 8;\n\t\t\tm_numRemainderBits -= 8;\n\t\t}\n\n\t\twdbg_assert(m_bitsLeft % 8 == 0);\n\t\twhile(m_bitsLeft)\n\t\t{\n\t\t\t*m_pos++ = 0;\n\t\t\tm_bitsLeft -= 8;\n\t\t}\n\t}\n\n\tuintptr_t Read(const size_t numInputBits)\n\t{\n\t\twdbg_assert(numInputBits <= m_bitsLeft);\n\n\t\tuintptr_t inputValue = 0;\n\t\tsize_t inputBitsLeft = numInputBits;\n\t\twhile(inputBitsLeft > 0)\n\t\t{\n\t\t\tconst size_t numBits = std::min(inputBitsLeft, size_t(8));\n\t\t\tm_bitsLeft -= numBits;\n\n\t\t\tif(m_numRemainderBits < numBits)\n\t\t\t{\n\t\t\t\tconst size_t inputByte = *m_pos++;\n\t\t\t\tm_remainderBits |= inputByte << m_numRemainderBits;\n\t\t\t\tm_numRemainderBits += 8;\n\t\t\t}\n\n\t\t\tconst uintptr_t remainderByte = (m_remainderBits & ((1u << numBits)-1));\n\t\t\tm_remainderBits >>= numBits;\n\t\t\tm_numRemainderBits -= numBits;\n\t\t\tinputValue |= remainderByte << (numInputBits-inputBitsLeft);\n\n\t\t\tinputBitsLeft -= numBits;\n\t\t}\n\n\t\treturn inputValue;\n\t}\n\nprivate:\n\tuintptr_t m_remainderBits;\n\tsize_t m_numRemainderBits;\n\tu8* m_pos;\n\tsize_t m_bitsLeft;\n};\n\n\nstatic void StashCallers(_CrtMemBlockHeader* header, const uintptr_t* callers, size_t numCallers)\n{\n\t// transform an array of callers into a (sorted and unique) set.\n\tuintptr_t quantizedPcSet[maxCallers];\n\tstd::transform(callers, callers+numCallers, quantizedPcSet, Quantize);\n\tstd::sort(quantizedPcSet, quantizedPcSet+numCallers);\n\tuintptr_t* const end = std::unique(quantizedPcSet, quantizedPcSet+numCallers);\n\tconst size_t quantizedPcSetSize = end-quantizedPcSet;\n\n\t// transform the set into a sequence of quantized offsets.\n\tuintptr_t quantizedOffsets[maxCallers];\n\tif(quantizedPcSet[0] >= quantizedCodeSegmentAddress)\n\t\tquantizedOffsets[0] = quantizedPcSet[0] - quantizedCodeSegmentAddress;\n\telse\n\t{\n\t\tquantizedOffsets[0] = quantizedPcSet[0];\n\n\t\t// make sure RetrieveCallers can differentiate between pointers and code-segment-offsets\n\t\twdbg_assert(quantizedOffsets[0] >= quantizedCodeSegmentLength);\n\t}\n\tfor(size_t i = 1; i < numCallers; i++)\n\t\tquantizedOffsets[i] = quantizedPcSet[i] - quantizedPcSet[i-1];\n\n\t// write quantized offsets to stream\n\tBitStream bitStream((u8*)&header->file, sizeof(header->file)+sizeof(header->line));\n\tfor(size_t i = 0; i < quantizedPcSetSize; i++)\n\t{\n\t\tconst uintptr_t quantizedOffset = quantizedOffsets[i];\n\t\tconst size_t encodedLength = EncodedLength(quantizedOffset);\n\t\tconst size_t numBits = NumBitsForEncodedLength(encodedLength);\n\t\tif(bitStream.BitsLeft() < numEncodedLengthBits+numBits)\n\t\t\tbreak;\n\t\tbitStream.Write(numEncodedLengthBits, encodedLength);\n\t\tbitStream.Write(numBits, quantizedOffset);\n\t}\n\n\tbitStream.Finish();\n}\n\n\nstatic void RetrieveCallers(_CrtMemBlockHeader* header, uintptr_t* callers, size_t& numCallers)\n{\n\t// read quantized offsets from stream\n\tuintptr_t quantizedOffsets[maxCallers];\n\tnumCallers = 0;\n\tBitStream bitStream((u8*)&header->file, sizeof(header->file)+sizeof(header->line));\n\tfor(;;)\n\t{\n\t\tif(bitStream.BitsLeft() < numEncodedLengthBits)\n\t\t\tbreak;\n\t\tconst size_t encodedLength = bitStream.Read(numEncodedLengthBits);\n\t\tconst size_t numBits = NumBitsForEncodedLength(encodedLength);\n\t\tif(bitStream.BitsLeft() < numBits)\n\t\t\tbreak;\n\t\tconst uintptr_t quantizedOffset = bitStream.Read(numBits);\n\t\tif(!quantizedOffset)\n\t\t\tbreak;\n\t\tquantizedOffsets[numCallers++] = quantizedOffset;\n\t}\n\n\tif(!numCallers)\n\t\treturn;\n\n\t// expand offsets into a set of callers\n\tif(quantizedOffsets[0] <= quantizedCodeSegmentLength)\n\t\tcallers[0] = Expand(quantizedOffsets[0] + quantizedCodeSegmentAddress);\n\telse\n\t\tcallers[0] = Expand(quantizedOffsets[0]);\n\tfor(size_t i = 1; i < numCallers; i++)\n\t\tcallers[i] = callers[i-1] + Expand(quantizedOffsets[i]);\n}\n\n\n//-----------------------------------------------------------------------------\n// find out who called an allocation function\n\n/**\n * gather and store a (filtered) list of callers.\n **/\nclass CallStack\n{\npublic:\n\tvoid Gather()\n\t{\n\t\tm_numCallers = 0;\n\t\tCONTEXT context;\n\t\t(void)debug_CaptureContext(&context);\n\t\t(void)wdbg_sym_WalkStack(OnFrame_Trampoline, (uintptr_t)this, context);\n\t\tstd::fill(m_callers+m_numCallers, m_callers+maxCallers, 0);\n\t}\n\n\tconst uintptr_t* Callers() const\n\t{\n\t\treturn m_callers;\n\t}\n\n\tsize_t NumCallers() const\n\t{\n\t\treturn m_numCallers;\n\t}\n\nprivate:\n\tStatus OnFrame(const STACKFRAME64* frame)\n\t{\n\t\tconst uintptr_t pc = frame->AddrPC.Offset;\n\n\t\t// skip invalid frames\n\t\tif(pc == 0)\n\t\t\treturn INFO::OK;\n\n\t\tStatus ret = m_filter.NotifyOfCaller(pc);\n\t\t// (CallerFilter provokes stack traces of heap functions; if that is\n\t\t// what happened, then we must not continue)\n\t\tif(ret != INFO::SKIPPED)\n\t\t\treturn ret;\n\n\t\t// stop the stack walk if frame storage is full\n\t\tif(m_numCallers >= maxCallers)\n\t\t\treturn INFO::ALL_COMPLETE;\n\n\t\tif(!m_filter.IsKnownCaller(pc))\n\t\t\tm_callers[m_numCallers++] = pc;\n\t\treturn INFO::OK;\n\t}\n\n\tstatic Status OnFrame_Trampoline(const STACKFRAME64* frame, uintptr_t cbData)\n\t{\n\t\tCallStack* this_ = (CallStack*)cbData;\n\t\treturn this_->OnFrame(frame);\n\t}\n\n\tCallerFilter m_filter;\n\n\tuintptr_t m_callers[maxCallers];\n\tsize_t m_numCallers;\n};\n\n\n//-----------------------------------------------------------------------------\n// RAII wrapper for installing a CRT allocation hook\n\nclass AllocationHook\n{\npublic:\n\tAllocationHook()\n\t{\n\t\twdbg_assert(s_instance == 0 && s_previousHook == 0);\n\t\ts_instance = this;\n\t\ts_previousHook = _CrtSetAllocHook(Hook);\n\t}\n\n\t~AllocationHook()\n\t{\n\t\t_CRT_ALLOC_HOOK removedHook = _CrtSetAllocHook(s_previousHook);\n\t\twdbg_assert(removedHook == Hook);\t// warn if we removed someone else's hook\n\t\ts_instance = 0;\n\t\ts_previousHook = 0;\n\t}\n\n\t/**\n\t * @param operation either _HOOK_ALLOC, _HOOK_REALLOC or _HOOK_FREE\n\t * @param userData is only valid (nonzero) for realloc and free because\n\t * we are called BEFORE the actual heap operation.\n\t **/\n\tvirtual void OnHeapOperation(int operation, void* userData, size_t size, long allocationNumber) = 0;\n\nprivate:\n\tstatic int __cdecl Hook(int operation, void* userData, size_t size, int blockType, long allocationNumber, const unsigned char* file, int line)\n\t{\n\t\tstatic bool busy = false;\n\t\twdbg_assert(!busy);\n\t\tbusy = true;\n\t\ts_instance->OnHeapOperation(operation, userData, size, allocationNumber);\n\t\tbusy = false;\n\n\t\tif(s_previousHook)\n\t\t\treturn s_previousHook(operation, userData, size, blockType, allocationNumber, file, line);\n\t\treturn 1;\t// continue as if the hook had never been called\n\t}\n\n\t// unfortunately static because we can't pass our `this' pointer through\n\t// the allocation hook.\n\tstatic AllocationHook* s_instance;\n\tstatic _CRT_ALLOC_HOOK s_previousHook;\n};\n\nAllocationHook* AllocationHook::s_instance;\n_CRT_ALLOC_HOOK AllocationHook::s_previousHook;\n\n\n//-----------------------------------------------------------------------------\n// our allocation hook\n\n// ideally we would just stash the callers in the newly created header.\n// unfortunately we are called BEFORE it (and the allocation) are actually\n// created, so we need to keep the information around until the next call to\n// AllocHook; only then can it be stored.\n//\n// unfortunately the CRT does not provide an O(1) means of getting at the\n// most recent block header. instead, we do so once and then keep it\n// up-to-date in the allocation hook. this is safe because we run under\n// the _HEAP_LOCK and ensure the allocation numbers match.\n\nstatic intptr_t s_numAllocations;\n\nintptr_t wdbg_heap_NumberOfAllocations()\n{\n\treturn s_numAllocations;\n}\n\nclass AllocationTracker : public AllocationHook\n{\npublic:\n\tAllocationTracker()\n\t\t: m_pendingAllocationNumber(0)\n\t{\n\t}\n\n\tvirtual void OnHeapOperation(int operation, void* userData, size_t size, long allocationNumber)\n\t{\n\t\tUNUSED2(size);\n\n\t\tif(operation == _HOOK_ALLOC || operation == _HOOK_REALLOC)\n\t\t\tcpu_AtomicAdd(&s_numAllocations, 1);\n\n\t\tbool hasChanged;\n\t\t_CrtMemBlockHeader* head = GetHeapListHead(operation, userData, hasChanged);\n\t\t// if the head changed, the last operation was a (re)allocation and\n\t\t// we now have its header; stash the pending call stack there.\n\t\tif(hasChanged)\n\t\t{\n\t\t\twdbg_assert(head->allocationNumber == m_pendingAllocationNumber);\n\n\t\t\t// note: overwrite existing file/line info (even if valid) to avoid\n\t\t\t// special cases in the report hook.\n\t\t\tStashCallers(head, m_pendingCallStack.Callers(), m_pendingCallStack.NumCallers());\n\t\t}\n\n\t\t// remember the current caller for next time\n\t\tm_pendingCallStack.Gather();\t// NB: called for each operation, as required by the filter recording step\n\t\tm_pendingAllocationNumber = allocationNumber;\n\t}\n\nprivate:\n\tlong m_pendingAllocationNumber;\n\tCallStack m_pendingCallStack;\n};\n\n\n//-----------------------------------------------------------------------------\n\nstatic void PrintCallStack(const uintptr_t* callers, size_t numCallers)\n{\n\tif(!numCallers || callers[0] == 0)\n\t{\n\t\twdbg_printf(L\"\\n  call stack not available.\\n\");\n\t\treturn;\n\t}\n\n\twdbg_printf(L\"\\n  partial, unordered call stack:\\n\");\n\tfor(size_t i = 0; i < numCallers; i++)\n\t{\n\t\twchar_t name[DEBUG_SYMBOL_CHARS] = {'\\0'}; wchar_t file[DEBUG_FILE_CHARS] = {'\\0'}; int line = -1;\n\t\tStatus err = debug_ResolveSymbol((void*)callers[i], name, file, &line);\n\t\twdbg_printf(L\"    \");\n\t\tif(err != INFO::OK)\n\t\t\twdbg_printf(L\"(error %d resolving PC=%p) \", err, callers[i]);\n\t\tif(file[0] != '\\0')\n\t\t\twdbg_printf(L\"%ls(%d) : \", file, line);\n\t\twdbg_printf(L\"%ls\\n\", name);\n\t}\n}\n\nstatic int __cdecl ReportHook(int reportType, wchar_t* message, int* out)\n{\n\tUNUSED2(reportType);\n\n\t// set up return values to reduce the chance of mistakes below\n\t*out = 0;\t// alternatives are failure (-1) and breakIntoDebugger (1)\n\tconst int ret = 0;\t// not \"handled\", continue calling other hooks\n\n\t// note: this hook is transparent in that it never affects the CRT.\n\t// we can't suppress parts of a leak report because that causes the\n\t// rest of it to be skipped.\n\n\tstatic enum\n\t{\n\t\tWaitingForDump,\n\t\tWaitingForBlock,\n\t\tIsBlock\n\t}\n\tstate = WaitingForDump;\n\tswitch(state)\n\t{\n\tcase WaitingForDump:\n\t\tif(!wcscmp(message, L\"Dumping objects ->\\n\"))\n\t\t\tstate = WaitingForBlock;\n\t\treturn ret;\n\n\tcase IsBlock:\n\t\t{\n\t\t\t// common case: \"normal block at 0xPPPPPPPP, N bytes long\".\n\t\t\tconst wchar_t* addressString = wcsstr(message, L\"0x\");\n\t\t\tif(addressString)\n\t\t\t{\n\t\t\t\tconst uintptr_t address = wcstoul(addressString, 0, 0);\n\t\t\t\t_CrtMemBlockHeader* header = HeaderFromData((void*)address);\n\t\t\t\tuintptr_t callers[maxCallers]; size_t numCallers;\n\t\t\t\tRetrieveCallers(header, callers, numCallers);\n\t\t\t\tPrintCallStack(callers, numCallers);\n\n\t\t\t\tstate = WaitingForBlock;\n\t\t\t\treturn ret;\n\t\t\t}\n\t\t\t// else: for reasons unknown, there's apparently no information\n\t\t\t// about the block; fall through to the previous state.\n\t\t}\n\n\tcase WaitingForBlock:\n\t\tif(message[0] == '{')\n\t\t\tstate = IsBlock;\n\t\t// suppress messages containing \"file\" and \"line\" since the normal\n\t\t// interpretation of those header fields is invalid.\n\t\telse if(wcschr(message, '('))\n\t\t\tmessage[0] = '\\0';\n\t\treturn ret;\n\n\tdefault:\n\t\twdbg_assert(0);\t// unreachable\n\t}\n\n\twdbg_assert(0);\t// unreachable\n\treturn 0;\n}\n\n#else\n\nintptr_t wdbg_heap_NumberOfAllocations()\n{\n\treturn 0;\n}\n\n#endif\n\n//-----------------------------------------------------------------------------\n\n#if ENABLE_LEAK_INSTRUMENTATION\nstatic AllocationTracker* s_tracker;\n#endif\n\nstatic Status wdbg_heap_Init()\n{\n#if ENABLE_LEAK_INSTRUMENTATION\n\tFindCodeSegment();\n\n\t// load symbol information now (fails if it happens during shutdown)\n\twchar_t name[DEBUG_SYMBOL_CHARS]; wchar_t file[DEBUG_FILE_CHARS]; int line;\n\t(void)debug_ResolveSymbol(wdbg_heap_Init, name, file, &line);\n\n\tint ret = _CrtSetReportHookW2(_CRT_RPTHOOK_INSTALL, ReportHook);\n\tif(ret == -1)\n\t\tabort();\n\n\ts_tracker = new AllocationTracker;\n#endif\n\n\twdbg_heap_Enable(true);\n\n\treturn INFO::OK;\n}\n\nstatic Status wdbg_heap_Shutdown()\n{\n#if ENABLE_LEAK_INSTRUMENTATION\n\tSAFE_DELETE(s_tracker);\n#endif\n\n\treturn INFO::OK;\n}\n"
  },
  {
    "path": "fpsgame/gui/sysdep/os/win/wdbg_heap.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * improved debug heap using MS CRT\n */\n\n#ifndef INCLUDED_WDBG_HEAP\n#define INCLUDED_WDBG_HEAP\n\n// this module provides a more convenient interface to the MS CRT's\n// debug heap checks. it also hooks into allocations to record the\n// caller/owner information without requiring macros (which break code\n// using placement new or member functions called free).\n\n/**\n * enable or disable manual and automatic heap validity checking.\n * (enabled by default during critical_init.)\n **/\nLIB_API void wdbg_heap_Enable(bool);\n\n/**\n * check heap integrity.\n * errors are reported by the CRT or via debug_DisplayError.\n * no effect if called between wdbg_heap_Enable(false) and the next\n * wdbg_heap_Enable(true).\n **/\nLIB_API void wdbg_heap_Validate();\n\n/**\n * @return the total number of alloc and realloc operations thus far.\n * used by the in-game profiler.\n **/\nLIB_API intptr_t wdbg_heap_NumberOfAllocations();\n\n#endif\t// #ifndef INCLUDED_WDBG_HEAP\n"
  },
  {
    "path": "fpsgame/gui/sysdep/os/win/wdbg_sym.cpp",
    "content": "/* Copyright (c) 2013 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * Win32 stack trace and symbol engine.\n */\n\n#include \"precompiled.h\"\n#include \"lib/sysdep/os/win/wdbg_sym.h\"\n\n#include <cstdlib>\n#include <cstdio>\n#include <set>\n\n#include \"lib/byte_order.h\"\t// movzx_le64\n#include \"lib/module_init.h\"\n#include \"lib/sysdep/cpu.h\"\n#include \"lib/debug_stl.h\"\n#include \"lib/app_hooks.h\"\n#include \"lib/external_libraries/dbghelp.h\"\n#include \"lib/sysdep/os/win/wdbg.h\"\n#include \"lib/sysdep/os/win/wutil.h\"\n#include \"lib/sysdep/os/win/winit.h\"\n\nWINIT_REGISTER_CRITICAL_INIT(wdbg_sym_Init);\n\nstatic WUTIL_FUNC(pRtlCaptureContext, VOID, (PCONTEXT));\n\nstatic Status wdbg_sym_Init()\n{\n\tWUTIL_IMPORT_KERNEL32(RtlCaptureContext, pRtlCaptureContext);\n\treturn INFO::OK;\n}\n\n\n//----------------------------------------------------------------------------\n// dbghelp\n//----------------------------------------------------------------------------\n\n// global for convenience (we only support a single process)\nstatic HANDLE hProcess;\n\n// for StackWalk64; taken from PE header by InitDbghelp.\nstatic WORD machine;\n\nstatic Status InitDbghelp()\n{\n\thProcess = GetCurrentProcess();\n\n\tdbghelp_ImportFunctions();\n\n\t// set options\n\t// notes:\n\t// - can be done before SymInitialize; we do so in case\n\t//   any of the options affect it.\n\t// - do not set directly - that would zero any existing flags.\n\tDWORD opts = pSymGetOptions();\n\t//opts |= SYMOPT_DEBUG;\t// lots of debug spew in output window\n\topts |= SYMOPT_DEFERRED_LOADS;\t// the \"fastest, most efficient way\"\n\topts |= SYMOPT_LOAD_LINES;\n\topts |= SYMOPT_UNDNAME;\n\tpSymSetOptions(opts);\n\n\t// initialize dbghelp.\n\t// .. request symbols from all currently active modules be loaded.\n\tconst BOOL fInvadeProcess = TRUE;\n\t// .. use default *symbol* search path. we don't use this to locate\n\t//    our PDB file because its absolute path is stored inside the EXE.\n\tconst PWSTR UserSearchPath = 0;\n\tWinScopedPreserveLastError s;\t// SymInitializeW\n\tconst BOOL ok = pSymInitializeW(hProcess, UserSearchPath, fInvadeProcess);\n\tWARN_IF_FALSE(ok);\n\n\tHMODULE hModule = GetModuleHandle(0);\n\tIMAGE_NT_HEADERS* const header = pImageNtHeader(hModule);\n\tmachine = header->FileHeader.Machine;\n\n\treturn INFO::OK;\n}\n\n// ensure dbghelp is initialized exactly once.\n// call every time before dbghelp functions are used.\n// (on-demand initialization allows handling exceptions raised before\n// winit.cpp init functions are called)\n//\n// NB: this may take SECONDS if OS symbols are installed and\n// symserv wants to access the internet.\nstatic void sym_init()\n{\n\tstatic ModuleInitState initState;\n\tModuleInit(&initState, InitDbghelp);\n}\n\n\nstatic STACKFRAME64 PopulateStackFrame(CONTEXT& context)\n{\n\tSTACKFRAME64 sf;\n\tmemset(&sf, 0, sizeof(sf));\n\tsf.AddrPC.Mode      = AddrModeFlat;\n\tsf.AddrFrame.Mode   = AddrModeFlat;\n\tsf.AddrStack.Mode   = AddrModeFlat;\n#if ARCH_AMD64\n\tsf.AddrPC.Offset    = context.Rip;\n\tsf.AddrFrame.Offset = context.Rbp;\n\tsf.AddrStack.Offset = context.Rsp;\n#else\n\tsf.AddrPC.Offset    = context.Eip;\n\tsf.AddrFrame.Offset = context.Ebp;\n\tsf.AddrStack.Offset = context.Esp;\n#endif\n\treturn sf;\n}\n\n\nstatic IMAGEHLP_STACK_FRAME PopulateImageStackFrame(const STACKFRAME64& sf)\n{\n\tIMAGEHLP_STACK_FRAME isf;\n\tmemset(&isf, 0, sizeof(isf));\n\t// apparently only PC, FP and SP are necessary, but\n\t// we copy everything to be safe.\n\tisf.InstructionOffset  = sf.AddrPC.Offset;\n\tisf.ReturnOffset       = sf.AddrReturn.Offset;\n\tisf.FrameOffset        = sf.AddrFrame.Offset;\n\tisf.StackOffset        = sf.AddrStack.Offset;\n\tisf.BackingStoreOffset = sf.AddrBStore.Offset;\n\tisf.FuncTableEntry     = (ULONG64)sf.FuncTableEntry;\n\t// (note: array of different types, can't copy directly)\n\tfor(int i = 0; i < 4; i++)\n\t\tisf.Params[i] = sf.Params[i];\n\t// isf.Reserved - already zeroed\n\tisf.Virtual = sf.Virtual;\n\t// isf.Reserved2 - already zeroed\n\treturn isf;\n}\n\n\nstruct SYMBOL_INFO_PACKAGEW2 : public SYMBOL_INFO_PACKAGEW\n{\n\tSYMBOL_INFO_PACKAGEW2()\n\t{\n\t\tsi.SizeOfStruct = sizeof(si);\n\t\tsi.MaxNameLen = MAX_SYM_NAME;\n\t}\n};\n\n#pragma pack(push, 1)\n\n// note: we can't derive from TI_FINDCHILDREN_PARAMS because its members\n// aren't guaranteed to precede ours (although they do in practice).\nstruct TI_FINDCHILDREN_PARAMS2\n{\n\tTI_FINDCHILDREN_PARAMS2(DWORD numChildren)\n\t{\n\t\tp.Start = 0;\n\t\tp.Count = std::min(numChildren, maxChildren);\n\t}\n\n\tstatic const DWORD maxChildren = 300;\n\tTI_FINDCHILDREN_PARAMS p;\n\tDWORD childrenStorage[maxChildren-1];\n};\n\n#pragma pack(pop)\n\n\n// actual implementation; made available so that functions already under\n// the lock don't have to unlock (slow) to avoid recursive locking.\nstatic Status ResolveSymbol_lk(void* ptr_of_interest, wchar_t* sym_name, wchar_t* file, int* line)\n{\n\tsym_init();\n\n\tconst DWORD64 addr = (DWORD64)ptr_of_interest;\n\tsize_t successes = 0;\n\n\tWinScopedPreserveLastError s;\t// SymFromAddrW, SymGetLineFromAddrW64\n\n\t// get symbol name (if requested)\n\tif(sym_name)\n\t{\n\t\tsym_name[0] = '\\0';\n\n\t\tSYMBOL_INFO_PACKAGEW2 sp;\n\t\tSYMBOL_INFOW* sym = &sp.si;\n\t\tif(pSymFromAddrW(hProcess, addr, 0, sym))\n\t\t{\n\t\t\twcscpy_s(sym_name, DEBUG_SYMBOL_CHARS, sym->Name);\n\t\t\tsuccesses++;\n\t\t}\n\t}\n\n\t// get source file and/or line number (if requested)\n\tif(file || line)\n\t{\n\t\tfile[0] = '\\0';\n\t\t*line = 0;\n\n\t\tIMAGEHLP_LINEW64 line_info = { sizeof(IMAGEHLP_LINEW64) };\n\t\tDWORD displacement; // unused but required by pSymGetLineFromAddr64!\n\t\tif(pSymGetLineFromAddrW64(hProcess, addr, &displacement, &line_info))\n\t\t{\n\t\t\tif(file)\n\t\t\t{\n\t\t\t\t// strip full path down to base name only.\n\t\t\t\t// this loses information, but that isn't expected to be a\n\t\t\t\t// problem and is balanced by not having to do this from every\n\t\t\t\t// call site (full path is too long to display nicely).\n\t\t\t\tconst wchar_t* basename = path_name_only(line_info.FileName);\n\t\t\t\twcscpy_s(file, DEBUG_FILE_CHARS, basename);\n\t\t\t\tsuccesses++;\n\t\t\t}\n\n\t\t\tif(line)\n\t\t\t{\n\t\t\t\t*line = line_info.LineNumber;\n\t\t\t\tsuccesses++;\n\t\t\t}\n\t\t}\n\t}\n\n\tif(addr == 0 && GetLastError() == ERROR_MOD_NOT_FOUND)\n\t\tSetLastError(0);\n\tif(GetLastError() == ERROR_INVALID_ADDRESS)\n\t\tSetLastError(0);\n\n\treturn (successes != 0)? INFO::OK : ERR::FAIL;\n}\n\n// file is the base name only, not path (see rationale in wdbg_sym).\n// the PDB implementation is rather slow (~500us).\nStatus debug_ResolveSymbol(void* ptr_of_interest, wchar_t* sym_name, wchar_t* file, int* line)\n{\n\tWinScopedLock lock(WDBG_SYM_CS);\n\treturn ResolveSymbol_lk(ptr_of_interest, sym_name, file, line);\n}\n\n\n//----------------------------------------------------------------------------\n// stack walk\n//----------------------------------------------------------------------------\n\nStatus debug_CaptureContext(void* pcontext)\n{\n\t// there are 4 ways to do so, in order of preference:\n\t// - RtlCaptureContext (only available on WinXP or above)\n\t// - assembly language subroutine (complicates the build system)\n\t// - intentionally raise an SEH exception and capture its context\n\t//   (causes annoying \"first chance exception\" messages and\n\t//   can't co-exist with WinScopedLock's destructor)\n\t// - GetThreadContext while suspended (a bit tricky + slow).\n\t//   note: it used to be common practice to query the current thread\n\t//   context, but WinXP SP2 and above require it be suspended.\n\n\tif(!pRtlCaptureContext)\n\t\treturn ERR::NOT_SUPPORTED;\t// NOWARN\n\n\tCONTEXT* context = (CONTEXT*)pcontext;\n\tcassert(sizeof(CONTEXT) <= DEBUG_CONTEXT_SIZE);\n\tmemset(context, 0, sizeof(CONTEXT));\n\tcontext->ContextFlags = CONTEXT_FULL;\n\tpRtlCaptureContext(context);\n\treturn INFO::OK;\n}\n\n\nstatic Status CallStackWalk(STACKFRAME64& sf, CONTEXT& context)\n{\n\tWinScopedLock lock(WDBG_SYM_CS);\n\n\tSetLastError(0);\t// StackWalk64 doesn't always SetLastError\n\tconst HANDLE hThread = GetCurrentThread();\n\tif(!pStackWalk64(machine, hProcess, hThread, &sf, &context, 0, pSymFunctionTableAccess64, pSymGetModuleBase64, 0))\n\t\treturn ERR::FAIL; // NOWARN (no stack frames left)\n\n\t// (the frame pointer can be zero despite StackWalk64 returning TRUE.)\n\tif(sf.AddrFrame.Offset == 0)\n\t\treturn ERR::FAIL;\t// NOWARN (no stack frames left)\n\n\t// huge WTF in x64 debug builds (dbghelp 6.12.0002.633):\n\t// AddrFrame.Offset doesn't match the correct RBP value.\n\t// StackWalk64 updates the context [http://bit.ly/lo1aqZ] and\n\t// its Rbp is correct, so we'll use that.\n#if ARCH_AMD64\n\tsf.AddrFrame.Offset = context.Rbp;\n#endif\n\n\treturn INFO::OK;\n}\n\n\n// NB: CaptureStackBackTrace may be faster (http://msinilo.pl/blog/?p=40),\n// but wasn't known during development.\nStatus wdbg_sym_WalkStack(StackFrameCallback cb, uintptr_t cbData, CONTEXT& context, const wchar_t* lastFuncToSkip)\n{\n\tsym_init();\n\n\tSTACKFRAME64 sf = PopulateStackFrame(context);\n\t\n\twchar_t func[DEBUG_SYMBOL_CHARS];\n\n\tStatus ret = ERR::SYM_NO_STACK_FRAMES_FOUND;\n\tfor(;;)\t// each stack frame:\n\t{\n\t\tif(CallStackWalk(sf, context) != INFO::OK)\n\t\t\treturn ret;\n\n\t\tif(lastFuncToSkip)\n\t\t{\n\t\t\tvoid* const pc = (void*)(uintptr_t)sf.AddrPC.Offset;\n\t\t\tif(debug_ResolveSymbol(pc, func, 0, 0) == INFO::OK)\n\t\t\t{\n\t\t\t\tif(wcsstr(func, lastFuncToSkip))\t// this was the last one to skip\n\t\t\t\t\tlastFuncToSkip = 0;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\tret = cb(&sf, cbData);\n\t\tRETURN_STATUS_FROM_CALLBACK(ret);\n\t}\n}\n\n\nvoid* debug_GetCaller(void* pcontext, const wchar_t* lastFuncToSkip)\n{\n\tstruct StoreAddress\n\t{\n\t\tstatic Status Func(const STACKFRAME64* sf, uintptr_t cbData)\n\t\t{\n\t\t\tconst uintptr_t funcAddress = sf->AddrPC.Offset;\n\n\t\t\t// store funcAddress in our `output parameter'\n\t\t\tmemcpy((void*)cbData, &funcAddress, sizeof(funcAddress));\n\n\t\t\treturn INFO::OK;\n\t\t}\n\t};\n\tvoid* func;\n\twdbg_assert(pcontext != 0);\n\tStatus ret = wdbg_sym_WalkStack(&StoreAddress::Func, (uintptr_t)&func, *(CONTEXT*)pcontext, lastFuncToSkip);\n\treturn (ret == INFO::OK)? func : 0;\n}\n\n\n//-----------------------------------------------------------------------------\n// helper routines for symbol value dump\n//-----------------------------------------------------------------------------\n\n// infinite recursion has never happened, but we check for it anyway.\nstatic const size_t maxIndirection = 255;\nstatic const size_t maxLevel = 255;\n\nstruct DumpState\n{\n\tsize_t level;\n\tsize_t indirection;\n\tuintptr_t moduleBase;\n\tLPSTACKFRAME64 stackFrame;\n\n\tDumpState(uintptr_t moduleBase, LPSTACKFRAME64 stackFrame)\n\t\t: level(0), indirection(0), moduleBase(moduleBase), stackFrame(stackFrame)\n\t{\n\t}\n};\n\n\n//----------------------------------------------------------------------------\n\nstatic size_t out_chars_left;\nstatic wchar_t* out_pos;\n\n// (only warn once until next out_init to avoid flood of messages.)\nstatic bool out_have_warned_of_overflow;\n\n// some top-level (*) symbols cause tons of output - so much that they may\n// single-handedly overflow the buffer (e.g. pointer to a tree of huge UDTs).\n// we can't have that, so there is a limit in place as to how much a\n// single top-level symbol can output. after that is reached, dumping is\n// aborted for that symbol but continues for the subsequent top-level symbols.\n//\n// this is implemented as follows: dump_sym_cb latches the current output\n// position; each dump_sym (through which all symbols go) checks if the\n// new position exceeds the limit and aborts if so.\n// slight wrinkle: since we don't want each level of UDTs to successively\n// realize the limit has been hit and display the error message, we\n// return ERR::SYM_SINGLE_SYMBOL_LIMIT once and thereafter INFO::SYM_SUPPRESS_OUTPUT.\n//\n// * example: local variables, as opposed to child symbols in a UDT.\nstatic wchar_t* out_latched_pos;\nstatic bool out_have_warned_of_limit;\n\nstatic void out_init(wchar_t* buf, size_t max_chars)\n{\n\tout_pos = buf;\n\tout_chars_left = max_chars;\n\tout_have_warned_of_overflow = false;\n\tout_have_warned_of_limit = false;\n}\n\n\nstatic void out(const wchar_t* fmt, ...)\n{\n\tva_list args;\n\tva_start(args, fmt);\n\t// use vswprintf, not vswprintf_s, because we want to gracefully\n\t// handle buffer overflows\n\tint len = vswprintf(out_pos, out_chars_left, fmt, args);\n\tva_end(args);\n\n\t// success\n\tif(len >= 0)\n\t{\n\t\tout_pos += len;\n\t\t// make sure out_chars_left remains nonnegative\n\t\tif((size_t)len > out_chars_left)\n\t\t{\n\t\t\tDEBUG_WARN_ERR(ERR::LOGIC);\t// apparently wrote more than out_chars_left\n\t\t\tlen = (int)out_chars_left;\n\t\t}\n\t\tout_chars_left -= len;\n\t}\n\t// no more room left\n\telse\n\t{\n\t\t// the buffer really is full yet out_chars_left may not be 0\n\t\t// (since it isn't updated if vswprintf returns -1).\n\t\t// must be set so subsequent calls don't try to squeeze stuff in.\n\t\tout_chars_left = 0;\n\n\t\t// write a warning into the output buffer (once) so it isn't\n\t\t// abruptly cut off (which looks like an error)\n\t\tif(!out_have_warned_of_overflow)\n\t\t{\n\t\t\tout_have_warned_of_overflow = true;\n\n\t\t\t// with the current out_pos / out_chars_left variables, there's\n\t\t\t// no way of knowing where the buffer actually ends. no matter;\n\t\t\t// we'll just put the warning before out_pos and eat into the\n\t\t\t// second newest text.\n\t\t\tconst wchar_t text[] = L\"(no more room in buffer)\";\n\t\t\twcscpy_s(out_pos-ARRAY_SIZE(text), ARRAY_SIZE(text), text);\t// safe\n\t\t}\n\t}\n}\n\n\nstatic void out_erase(size_t num_chars)\n{\n\t// don't do anything if end of buffer was hit (prevents repeatedly\n\t// scribbling over the last few bytes).\n\tif(out_have_warned_of_overflow)\n\t\treturn;\n\n\tout_chars_left += (ssize_t)num_chars;\n\tout_pos -= num_chars;\n\t*out_pos = '\\0';\n\t\t// make sure it's 0-terminated in case there is no further output.\n}\n\n\n// (see above)\nstatic void out_latch_pos()\n{\n\tout_have_warned_of_limit = false;\n\tout_latched_pos = out_pos;\n}\n\n\n// (see above)\nstatic Status out_check_limit()\n{\n\tif(out_have_warned_of_limit)\n\t\treturn INFO::SYM_SUPPRESS_OUTPUT;\n\tif(out_pos - out_latched_pos > 3000)\t// ~30 lines\n\t{\n\t\tout_have_warned_of_limit = true;\n\t\treturn ERR::SYM_SINGLE_SYMBOL_LIMIT;\t// NOWARN\n\t}\n\n\t// no limit hit, proceed normally\n\treturn INFO::OK;\n}\n\n//----------------------------------------------------------------------------\n\n#define INDENT STMT(for(size_t i__ = 0; i__ <= state.level; i__++) out(L\"    \");)\n#define UNINDENT STMT(out_erase((state.level+1)*4);)\n\n\n// does it look like an ASCII string is located at <addr>?\n// set <stride> to 2 to search for WCS-2 strings (of western characters!).\n// called by dump_sequence for its string special-case.\n//\n// algorithm: scan the \"string\" and count # text chars vs. garbage.\nstatic bool is_string(const u8* p, size_t stride)\n{\n\t// note: access violations are caught by dump_sym; output is \"?\".\n\tint score = 0;\n\tfor(;;)\n\t{\n\t\t// current character is:\n\t\tconst int c = *p & 0xff;\t// prevent sign extension\n\t\tp += stride;\n\t\t// .. text\n\t\tif(isalnum(c))\n\t\t\tscore += 5;\n\t\t// .. end of string\n\t\telse if(!c)\n\t\t\tbreak;\n\t\t// .. garbage\n\t\telse if(!isprint(c))\n\t\t\tscore -= 4;\n\n\t\t// got enough information either way => done.\n\t\t// (we don't want to unnecessarily scan huge binary arrays)\n\t\tif(abs(score) >= 10)\n\t\t\tbreak;\n\t}\n\n\treturn (score > 0);\n}\n\n\n\n\n// forward decl; called by dump_sequence and some of dump_sym_*.\nstatic Status dump_sym(DWORD id, const u8* p, DumpState& state);\n\n// from cvconst.h\n//\n// rationale: we don't provide a get_register routine, since only the\n// value of FP is known to dump_frame_cb (via STACKFRAME64).\n// displaying variables stored in registers is out of the question;\n// all we can do is display FP-relative variables.\nenum CV_HREG_e\n{\n\tCV_REG_EBP = 22,\n\tCV_AMD64_RBP = 334\n};\n\n\nstatic void dump_error(Status err)\n{\n\tswitch(err)\n\t{\n\tcase 0:\n\t\t// no error => no output\n\t\tbreak;\n\tcase ERR::SYM_SINGLE_SYMBOL_LIMIT:\n\t\tout(L\"(too much output; skipping to next top-level symbol)\");\n\t\tbreak;\n\tcase ERR::SYM_UNRETRIEVABLE_STATIC:\n\t\tout(L\"(unavailable - located in another module)\");\n\t\tbreak;\n\tcase ERR::SYM_UNRETRIEVABLE:\n\t\tout(L\"(unavailable)\");\n\t\tbreak;\n\tcase ERR::SYM_TYPE_INFO_UNAVAILABLE:\n\t\tout(L\"(unavailable - type info request failed (GLE=%d))\", GetLastError());\n\t\tbreak;\n\tcase ERR::SYM_INTERNAL_ERROR:\n\t\tout(L\"(unavailable - internal error)\\r\\n\");\n\t\tbreak;\n\tcase INFO::SYM_SUPPRESS_OUTPUT:\n\t\t// not an error; do not output anything. handled by caller.\n\t\tbreak;\n\tdefault:\n\t\tout(L\"(unavailable - unspecified error 0x%X (%d))\", err, err);\n\t\tbreak;\n\t}\n}\n\n\n// moved out of dump_sequence.\nstatic Status dump_string(const u8* p, size_t el_size)\n{\n\t// not char or wchar_t string\n\tif(el_size != sizeof(char) && el_size != sizeof(wchar_t))\n\t\treturn INFO::CANNOT_HANDLE;\n\t// not text\n\tif(!is_string(p, el_size))\n\t\treturn INFO::CANNOT_HANDLE;\n\n\twchar_t buf[512];\n\tif(el_size == sizeof(wchar_t))\n\t{\n\t\twcsncpy(buf, (const wchar_t*)p, ARRAY_SIZE(buf)); // can't use wcscpy_s because p might be too long\n\t\twcscpy_s(buf+ARRAY_SIZE(buf)-4, 4, L\"...\"); // ensure null-termination\n\t}\n\t// convert to wchar_t\n\telse\n\t{\n\t\tsize_t i;\n\t\tfor(i = 0; i < ARRAY_SIZE(buf)-1; i++)\n\t\t{\n\t\t\tbuf[i] = (wchar_t)p[i];\n\t\t\tif(buf[i] == '\\0')\n\t\t\t\tbreak;\n\t\t}\n\t\tbuf[i] = '\\0';\n\t}\n\n\tout(L\"\\\"%ls\\\"\", buf);\n\treturn INFO::OK;\n}\n\n\n// moved out of dump_sequence.\nstatic void seq_determine_formatting(size_t el_size, size_t el_count, bool* fits_on_one_line, size_t* num_elements_to_show)\n{\n\tif(el_size == sizeof(char))\n\t{\n\t\t*fits_on_one_line = el_count <= 16;\n\t\t*num_elements_to_show = std::min((size_t)16u, el_count);\n\t}\n\telse if(el_size <= sizeof(int))\n\t{\n\t\t*fits_on_one_line = el_count <= 8;\n\t\t*num_elements_to_show = std::min((size_t)12u, el_count);\n\t}\n\telse\n\t{\n\t\t*fits_on_one_line = false;\n\t\t*num_elements_to_show = std::min((size_t)8u, el_count);\n\t}\n\n\t// make sure empty containers are displayed with [0] {}, otherwise\n\t// the lack of output looks like an error.\n\tif(!el_count)\n\t\t*fits_on_one_line = true;\n}\n\n\nstatic Status dump_sequence(DebugStlIterator el_iterator, void* internal, size_t el_count, DWORD el_type_id, size_t el_size, DumpState& state)\n{\n\tconst u8* el_p = 0;\t// avoid \"uninitialized\" warning\n\n\t// special case: display as a string if the sequence looks to be text.\n\t// do this only if container isn't empty because the otherwise the\n\t// iterator may crash.\n\tif(el_count)\n\t{\n\t\tel_p = el_iterator(internal, el_size);\n\n\t\tStatus ret = dump_string(el_p, el_size);\n\t\tif(ret == INFO::OK)\n\t\t\treturn ret;\n\t}\n\n\t// choose formatting based on element size and count\n\tbool fits_on_one_line;\n\tsize_t num_elements_to_show;\n\tseq_determine_formatting(el_size, el_count, &fits_on_one_line, &num_elements_to_show);\n\n\tout(L\"[%d] \", el_count);\n\tstate.level++;\n\tout(fits_on_one_line? L\"{ \" : L\"\\r\\n\");\n\n\tfor(size_t i = 0; i < num_elements_to_show; i++)\n\t{\n\t\tif(!fits_on_one_line)\n\t\t\tINDENT;\n\n\t\tStatus err = dump_sym(el_type_id, el_p, state);\n\t\tel_p = el_iterator(internal, el_size);\n\n\t\t// there was no output for this child; undo its indentation (if any),\n\t\t// skip everything below and proceed with the next child.\n\t\tif(err == INFO::SYM_SUPPRESS_OUTPUT)\n\t\t{\n\t\t\tif(!fits_on_one_line)\n\t\t\t\tUNINDENT;\n\t\t\tcontinue;\n\t\t}\n\n\t\tdump_error(err);\t// nop if err == INFO::OK\n\t\t// add separator unless this is the last element (can't just\n\t\t// erase below due to additional \"...\").\n\t\tif(i != num_elements_to_show-1)\n\t\t\tout(fits_on_one_line? L\", \" : L\"\\r\\n\");\n\n\t\tif(err == ERR::SYM_SINGLE_SYMBOL_LIMIT)\n\t\t\tbreak;\n\t}\t// for each child\n\n\t// indicate some elements were skipped\n\tif(el_count != num_elements_to_show)\n\t\tout(L\" ...\");\n\n\tstate.level--;\n\tif(fits_on_one_line)\n\t\tout(L\" }\");\n\treturn INFO::OK;\n}\n\n\nstatic const u8* array_iterator(void* internal, size_t el_size)\n{\n\tconst u8*& pos = *(const u8**)internal;\n\tconst u8* cur_pos = pos;\n\tpos += el_size;\n\treturn cur_pos;\n}\n\n\nstatic Status dump_array(const u8* p, size_t el_count, DWORD el_type_id, size_t el_size, DumpState& state)\n{\n\tconst u8* iterator_internal_pos = p;\n\treturn dump_sequence(array_iterator, &iterator_internal_pos,\n\t\tel_count, el_type_id, el_size, state);\n}\n\n\nstatic Status CanHandleDataKind(DWORD dataKind)\n{\n\tswitch(dataKind)\n\t{\n\tcase DataIsMember:\n\t\t// address is already correct (udt_dump_normal retrieved the offset;\n\t\t// we do it that way so we can check it against the total\n\t\t// UDT size for safety) and SymFromIndex would fail\n\t\treturn INFO::SKIPPED;\n\n\tcase DataIsUnknown:\n\t\tWARN_RETURN(ERR::FAIL);\n\n\tcase DataIsStaticMember:\n\t\t// this symbol is defined as static in another module =>\n\t\t// there's nothing we can do.\n\t\treturn ERR::SYM_UNRETRIEVABLE_STATIC;\t// NOWARN\n\n\tcase DataIsLocal:\n\tcase DataIsStaticLocal:\n\tcase DataIsParam:\n\tcase DataIsObjectPtr:\n\tcase DataIsFileStatic:\n\tcase DataIsGlobal:\n\tcase DataIsConstant:\n\t\t// ok, can handle\n\t\treturn INFO::OK;\n\t}\n\n\tWARN_RETURN(ERR::LOGIC);\t// UNREACHABLE\n}\n\nstatic bool IsRelativeToFramePointer(DWORD flags, DWORD reg)\n{\n\tif(flags & SYMFLAG_FRAMEREL)\t// note: this is apparently obsolete\n\t\treturn true;\n\tif((flags & SYMFLAG_REGREL) == 0)\n\t\treturn false;\n\tif(reg == CV_REG_EBP || reg == CV_AMD64_RBP)\n\t\treturn true;\n\treturn false;\n}\n\nstatic bool IsUnretrievable(DWORD flags)\n{\n\t// note: it is unlikely that the crashdump register context\n\t// contains the correct values for this scope, so symbols\n\t// stored in or relative to a general register are unavailable.\n\tif(flags & SYMFLAG_REGISTER)\n\t\treturn true;\n\n\t// note: IsRelativeToFramePointer is called first, so if we still\n\t// see this flag, the base register is not the frame pointer.\n\t// since we most probably don't know its value in the current\n\t// scope (see above), the symbol is inaccessible.\n\tif(flags & SYMFLAG_REGREL)\n\t\treturn true;\n\n\treturn false;\n}\n\nstatic Status DetermineSymbolAddress(DWORD id, const SYMBOL_INFOW* sym, const DumpState& state, const u8** pp)\n{\n\tDWORD dataKind;\n\tif(!pSymGetTypeInfo(hProcess, state.moduleBase, id, TI_GET_DATAKIND, &dataKind))\n\t\tWARN_RETURN(ERR::SYM_TYPE_INFO_UNAVAILABLE);\n\tStatus ret = CanHandleDataKind(dataKind);\n\tRETURN_STATUS_IF_ERR(ret);\n\tif(ret == INFO::SKIPPED)\n\t\treturn INFO::OK;\t// pp is already correct\n\n\t// note: we have not yet observed a non-zero TI_GET_ADDRESSOFFSET or\n\t// TI_GET_ADDRESS, and TI_GET_OFFSET is apparently equal to sym->Address.\n\n\t// get address\n\tuintptr_t addr = (uintptr_t)sym->Address;\n\tif(IsRelativeToFramePointer(sym->Flags, sym->Register))\n\t\taddr += (uintptr_t)state.stackFrame->AddrFrame.Offset;\n\telse if(IsUnretrievable(sym->Flags))\n\t\treturn ERR::SYM_UNRETRIEVABLE;\t// NOWARN\n\n\t*pp = (const u8*)(uintptr_t)addr;\n\n\tdebug_printf(\"SYM| %s at %p  flags=%X dk=%d sym->addr=%I64X fp=%I64x\\n\", utf8_from_wstring(sym->Name).c_str(), *pp, sym->Flags, dataKind, sym->Address, state.stackFrame->AddrFrame.Offset);\n\treturn INFO::OK;\n}\n\n\n//-----------------------------------------------------------------------------\n// dump routines for each dbghelp symbol type\n//-----------------------------------------------------------------------------\n\n// these functions return != 0 if they're not able to produce any\n// reasonable output at all; the caller (dump_sym_data, dump_sequence, etc.)\n// will display the appropriate error message via dump_error.\n// called by dump_sym; lock is held.\n\nstatic Status dump_sym_array(DWORD type_id, const u8* p, DumpState& state)\n{ \n\tULONG64 size64 = 0;\n\tif(!pSymGetTypeInfo(hProcess, state.moduleBase, type_id, TI_GET_LENGTH, &size64))\n\t\tWARN_RETURN(ERR::SYM_TYPE_INFO_UNAVAILABLE);\n\tconst size_t size = (size_t)size64;\n\n\t// get element count and size\n\tDWORD el_type_id = 0;\n\tif(!pSymGetTypeInfo(hProcess, state.moduleBase, type_id, TI_GET_TYPEID, &el_type_id))\n\t\tWARN_RETURN(ERR::SYM_TYPE_INFO_UNAVAILABLE);\n\t// .. workaround: TI_GET_COUNT returns total struct size for\n\t//    arrays-of-struct. therefore, calculate as size / el_size.\n\tULONG64 el_size_;\n\tif(!pSymGetTypeInfo(hProcess, state.moduleBase, el_type_id, TI_GET_LENGTH, &el_size_))\n\t\tWARN_RETURN(ERR::SYM_TYPE_INFO_UNAVAILABLE);\n\tconst size_t el_size = (size_t)el_size_;\n\tENSURE(el_size != 0);\n\tconst size_t num_elements = size/el_size;\n\tENSURE(num_elements != 0);\n \n\treturn dump_array(p, num_elements, el_type_id, el_size, state);\n}\n\n\n//-----------------------------------------------------------------------------\n\n// if the current value is a printable character, display in that form.\n// this isn't only done in btChar because characters are sometimes stored\n// in integers.\nstatic void AppendCharacterIfPrintable(u64 data)\n{\n\tif(data < 0x100)\n\t{\n\t\tint c = (int)data;\n\t\tif(isprint(c))\n\t\t\tout(L\" ('%hc')\", c);\n\t}\n}\n\n\nstatic Status dump_sym_base_type(DWORD type_id, const u8* p, DumpState& state)\n{\n\tDWORD base_type;\n\tif(!pSymGetTypeInfo(hProcess, state.moduleBase, type_id, TI_GET_BASETYPE, &base_type))\n\t\tWARN_RETURN(ERR::SYM_TYPE_INFO_UNAVAILABLE);\n\tULONG64 size64 = 0;\n\tif(!pSymGetTypeInfo(hProcess, state.moduleBase, type_id, TI_GET_LENGTH, &size64))\n\t\tWARN_RETURN(ERR::SYM_TYPE_INFO_UNAVAILABLE);\n\tconst size_t size = (size_t)size64;\n\n\t// single out() call. note: we pass a single u64 for all sizes,\n\t// which will only work on little-endian systems.\n\t// must be declared before goto to avoid W4 warning.\n\tconst wchar_t* fmt = L\"\";\n\n\tu64 data = movzx_le64(p, size);\n\t// if value is 0xCC..CC (uninitialized mem), we display as hex. \n\t// the output would otherwise be garbage; this makes it obvious.\n\t// note: be very careful to correctly handle size=0 (e.g. void*).\n\tfor(size_t i = 0; i < size; i++)\n\t{\n\t\tif(p[i] != 0xCC)\n\t\t\tbreak;\n\t\tif(i == size-1)\n\t\t{\n\t\t\tout(L\"(uninitialized)\");\n\t\t\treturn INFO::OK;\n\t\t}\n\t}\n\n\tswitch(base_type)\n\t{\n\t// floating-point\n\tcase btFloat:\n\t\tif(size == sizeof(float))\n\t\t{\n\t\t\t// NB: the C calling convention calls for float arguments to be\n\t\t\t// converted to double. passing `data' wouldn't work because it's\n\t\t\t// merely a zero-extended 32-bit representation of the float.\n\t\t\tfloat value;\n\t\t\tmemcpy(&value, p, sizeof(value));\n\t\t\tout(L\"%f (0x%08I64X)\", value, data);\n\t\t}\n\t\telse if(size == sizeof(double))\n\t\t\tout(L\"%g (0x%016I64X)\", data, data);\n\t\telse\n\t\t\tDEBUG_WARN_ERR(ERR::LOGIC);\t// invalid float size\n\t\tbreak;\n\n\t// boolean\n\tcase btBool:\n\t\tENSURE(size == sizeof(bool));\n\t\tif(data == 0 || data == 1)\n\t\t\tout(L\"%ls\", data? L\"true \" : L\"false\");\n\t\telse\n\t\t\tout(L\"(bool)0x%02I64X\", data);\n\t\tbreak;\n\n\t// integers (displayed as decimal and hex)\n\t// note: 0x00000000 can get annoying (0 would be nicer),\n\t// but it indicates the variable size and makes for consistently\n\t// formatted structs/arrays. (0x1234 0 0x5678 is ugly)\n\tcase btInt:\n\tcase btLong:\n\tcase btUInt:\n\tcase btULong:\n\t\tif(size == 1)\n\t\t{\n\t\t\t// _TUCHAR\n\t\t\tif(state.indirection)\n\t\t\t{\n\t\t\t\tstate.indirection = 0;\n\t\t\t\treturn dump_array(p, 8, type_id, size, state);\n\t\t\t}\n\t\t\tfmt = L\"%I64d (0x%02I64X)\";\n\t\t}\n\t\telse if(size == 2)\n\t\t\tfmt = L\"%I64d (0x%04I64X)\";\n\t\telse if(size == 4)\n\t\t\tfmt = L\"%I64d (0x%08I64X)\";\n\t\telse if(size == 8)\n\t\t\tfmt = L\"%I64d (0x%016I64X)\";\n\t\telse\n\t\t\tDEBUG_WARN_ERR(ERR::LOGIC);\t// invalid size for integers\n\t\tout(fmt, data, data);\n\t\tbreak;\n\n\t// character\n\tcase btChar:\n\tcase btWChar:\n\t\tENSURE(size == sizeof(char) || size == sizeof(wchar_t));\n\t\t// char*, wchar_t*\n\t\tif(state.indirection)\n\t\t{\n\t\t\tstate.indirection = 0;\n\t\t\treturn dump_array(p, 8, type_id, size, state);\n\t\t}\n\t\tout(L\"%d\", data);\n\t\tAppendCharacterIfPrintable(data);\n\t\tbreak;\n\n\t// note: void* is sometimes indicated as (pointer, btNoType).\n\tcase btVoid:\n\tcase btNoType:\n\t\t// void* - cannot display what it's pointing to (type unknown).\n\t\tif(state.indirection)\n\t\t{\n\t\t\tout_erase(4);\t// \" -> \"\n\t\t\tfmt = L\"\";\n\t\t}\n\t\telse\n\t\t\tDEBUG_WARN_ERR(ERR::LOGIC);\t// non-pointer btVoid or btNoType\n\t\tbreak;\n\n\tdefault:\n\t\tDEBUG_WARN_ERR(ERR::LOGIC);\t// unknown type\n\t\tbreak;\n\n\t// unsupported complex types\n\tcase btBCD:\n\tcase btCurrency:\n\tcase btDate:\n\tcase btVariant:\n\tcase btComplex:\n\tcase btBit:\n\tcase btBSTR:\n\tcase btHresult:\n\t\treturn ERR::SYM_UNSUPPORTED;\t// NOWARN\n\t}\n\n\treturn INFO::OK;\n}\n\n\n//-----------------------------------------------------------------------------\n\nstatic Status dump_sym_base_class(DWORD type_id, const u8* p, DumpState& state)\n{\n\tDWORD base_class_type_id;\n\tif(!pSymGetTypeInfo(hProcess, state.moduleBase, type_id, TI_GET_TYPEID, &base_class_type_id))\n\t\tWARN_RETURN(ERR::SYM_TYPE_INFO_UNAVAILABLE);\n\n\t// this is a virtual base class. we can't display those because it'd\n\t// require reading the VTbl, which is difficult given lack of documentation\n\t// and just not worth it.\n\tDWORD vptr_ofs;\n\tif(pSymGetTypeInfo(hProcess, state.moduleBase, type_id, TI_GET_VIRTUALBASEPOINTEROFFSET, &vptr_ofs))\n\t\treturn ERR::SYM_UNSUPPORTED;\t// NOWARN\n\n\treturn dump_sym(base_class_type_id, p, state);\n}\n\n\n//-----------------------------------------------------------------------------\n\nstatic Status dump_sym_data(DWORD id, const u8* p, DumpState& state)\n{\n\tSYMBOL_INFO_PACKAGEW2 sp;\n\tSYMBOL_INFOW* sym = &sp.si;\n\tif(!pSymFromIndexW(hProcess, state.moduleBase, id, sym))\n\t\tRETURN_STATUS_IF_ERR(ERR::SYM_TYPE_INFO_UNAVAILABLE);\n\n\tout(L\"%ls = \", sym->Name);\n\n\t__try\n\t{\n\t\tRETURN_STATUS_IF_ERR(DetermineSymbolAddress(id, sym, state, &p));\n\t\t// display value recursively\n\t\treturn dump_sym(sym->TypeIndex, p, state);\n\t}\n\t__except(EXCEPTION_EXECUTE_HANDLER)\n\t{\n\t\treturn ERR::SYM_INTERNAL_ERROR;\t// NOWARN\n\t}\n}\n\n\n//-----------------------------------------------------------------------------\n\nstatic Status dump_sym_enum(DWORD type_id, const u8* p, DumpState& state)\n{\n\tULONG64 size64 = 0;\n\tif(!pSymGetTypeInfo(hProcess, state.moduleBase, type_id, TI_GET_LENGTH, &size64))\n\t\tWARN_RETURN(ERR::SYM_TYPE_INFO_UNAVAILABLE);\n\tconst size_t size = (size_t)size64;\n\n\tconst i64 enum_value = movsx_le64(p, size);\n\n\t// get array of child symbols (enumerants).\n\tDWORD numChildren;\n\tif(!pSymGetTypeInfo(hProcess, state.moduleBase, type_id, TI_GET_CHILDRENCOUNT, &numChildren))\n\t\tWARN_RETURN(ERR::SYM_TYPE_INFO_UNAVAILABLE);\n\tTI_FINDCHILDREN_PARAMS2 fcp(numChildren);\n\tif(!pSymGetTypeInfo(hProcess, state.moduleBase, type_id, TI_FINDCHILDREN, &fcp))\n\t\tWARN_RETURN(ERR::SYM_TYPE_INFO_UNAVAILABLE);\n\tnumChildren = fcp.p.Count;\t// was truncated to maxChildren\n\tconst DWORD* children = fcp.p.ChildId;\n\n\t// for each child (enumerant):\n\tfor(size_t i = 0; i < numChildren; i++)\n\t{\n\t\tDWORD child_data_id = children[i];\n\n\t\t// get this enumerant's value. we can't make any assumptions about\n\t\t// the variant's type or size  - no restriction is documented.\n\t\t// rationale: VariantChangeType is much less tedious than doing\n\t\t// it manually and guarantees we cover everything. the OLE DLL is\n\t\t// already pulled in by e.g. OpenGL anyway.\n\t\tVARIANT v;\n\t\tif(!pSymGetTypeInfo(hProcess, state.moduleBase, child_data_id, TI_GET_VALUE, &v))\n\t\t\tWARN_RETURN(ERR::SYM_TYPE_INFO_UNAVAILABLE);\n\t\tif(VariantChangeType(&v, &v, 0, VT_I8) != S_OK)\n\t\t\tcontinue;\n\n\t\t// it's the one we want - output its name.\n\t\tif(enum_value == v.llVal)\n\t\t{\n\t\t\tconst wchar_t* name;\n\t\t\tif(!pSymGetTypeInfo(hProcess, state.moduleBase, child_data_id, TI_GET_SYMNAME, &name))\n\t\t\t\tWARN_RETURN(ERR::SYM_TYPE_INFO_UNAVAILABLE);\n\t\t\tout(L\"%ls\", name);\n\t\t\tLocalFree((HLOCAL)name);\n\t\t\treturn INFO::OK;\n\t\t}\n\t}\n\n\t// we weren't able to retrieve a matching enum value, but can still\n\t// produce reasonable output (the numeric value).\n\t// note: could goto here after a SGTI fails, but we fail instead\n\t// to make sure those errors are noticed.\n\tout(L\"%I64d\", enum_value);\n\treturn INFO::OK;\n}\n\n\n//-----------------------------------------------------------------------------\n\nstatic Status dump_sym_function(DWORD UNUSED(type_id), const u8* UNUSED(p), DumpState& UNUSED(state))\n{\n\treturn INFO::SYM_SUPPRESS_OUTPUT;\n}\n\n\n//-----------------------------------------------------------------------------\n\nstatic Status dump_sym_function_type(DWORD UNUSED(type_id), const u8* p, DumpState& state)\n{\n\t// this symbol gives class parent, return type, and parameter count.\n\t// unfortunately the one thing we care about, its name,\n\t// isn't exposed via TI_GET_SYMNAME, so we resolve it ourselves.\n\n\twchar_t name[DEBUG_SYMBOL_CHARS];\n\tStatus err = ResolveSymbol_lk((void*)p, name, 0, 0);\n\n\tif(state.indirection == 0)\n\t\tout(L\"0x%p \", p);\n\tif(err == INFO::OK)\n\t\tout(L\"(%ls)\", name);\n\treturn INFO::OK;\n}\n\n\n//-----------------------------------------------------------------------------\n\n// do not follow pointers that we have already displayed. this reduces\n// clutter a bit and prevents infinite recursion for cyclical references\n// (e.g. via struct S { S* p; } s; s.p = &s;)\n\n// note: allocating memory dynamically would cause trouble if dumping\n// the stack from within memory-related code (the allocation hook would\n// be reentered, which is not permissible).\n\nstatic const size_t maxVisited = 1000;\nstatic const u8* visited[maxVisited];\nstatic size_t numVisited;\n\nstatic void ptr_reset_visited()\n{\n\tnumVisited = 0;\n}\n\nstatic bool ptr_already_visited(const u8* p)\n{\n\tfor(size_t i = 0; i < numVisited; i++)\n\t{\n\t\tif(visited[i] == p)\n\t\t\treturn true;\n\t}\n\n\tif(numVisited < maxVisited)\n\t{\n\t\tvisited[numVisited] = p;\n\t\tnumVisited++;\n\t}\n\t// capacity exceeded\n\telse\n\t{\n\t\t// warn user - but only once (we can't use the regular\n\t\t// debug_DisplayError and wdbg_assert doesn't have a\n\t\t// suppress mechanism)\n\t\tstatic bool haveComplained;\n\t\tif(!haveComplained)\n\t\t{\n\t\t\tdebug_printf(\"WARNING: ptr_already_visited: capacity exceeded, increase maxVisited\\n\");\n\t\t\tdebug_break();\n\t\t\thaveComplained = true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\nstatic Status dump_sym_pointer(DWORD type_id, const u8* p, DumpState& state)\n{\n\tULONG64 size64 = 0;\n\tif(!pSymGetTypeInfo(hProcess, state.moduleBase, type_id, TI_GET_LENGTH, &size64))\n\t\tWARN_RETURN(ERR::SYM_TYPE_INFO_UNAVAILABLE);\n\tconst size_t size = (size_t)size64;\n\n\t// read+output pointer's value.\n\tp = (const u8*)(uintptr_t)movzx_le64(p, size);\n\tout(L\"0x%p\", p);\n\n\t// bail if it's obvious the pointer is bogus\n\t// (=> can't display what it's pointing to)\n\tif(debug_IsPointerBogus(p))\n\t\treturn INFO::OK;\n\n\t// avoid duplicates and circular references\n\tif(ptr_already_visited(p))\n\t{\n\t\tout(L\" (see above)\");\n\t\treturn INFO::OK;\n\t}\n\n\t// display what the pointer is pointing to.\n\t// if the pointer is invalid (despite \"bogus\" check above),\n\t// dump_data_sym recovers via SEH and prints an error message.\n\t// if the pointed-to value turns out to uninteresting (e.g. void*),\n\t// the responsible dump_sym* will erase \"->\", leaving only address.\n\tout(L\" -> \");\n\tif(!pSymGetTypeInfo(hProcess, state.moduleBase, type_id, TI_GET_TYPEID, &type_id))\n\t\tWARN_RETURN(ERR::SYM_TYPE_INFO_UNAVAILABLE);\n\n\t// prevent infinite recursion just to be safe (shouldn't happen)\n\tif(state.indirection >= maxIndirection)\n\t\tWARN_RETURN(ERR::SYM_NESTING_LIMIT);\n\tstate.indirection++;\n\treturn dump_sym(type_id, p, state);\n}\n\n\n//-----------------------------------------------------------------------------\n\n\nstatic Status dump_sym_typedef(DWORD type_id, const u8* p, DumpState& state)\n{\n\tif(!pSymGetTypeInfo(hProcess, state.moduleBase, type_id, TI_GET_TYPEID, &type_id))\n\t\tWARN_RETURN(ERR::SYM_TYPE_INFO_UNAVAILABLE);\n\treturn dump_sym(type_id, p, state);\n}\n\n\n//-----------------------------------------------------------------------------\n\n\n// determine type and size of the given child in a UDT.\n// useful for UDTs that contain typedefs describing their contents,\n// e.g. value_type in STL containers.\nstatic Status udt_get_child_type(const wchar_t* child_name, ULONG numChildren, const DWORD* children, const DumpState& state, DWORD* el_type_id, size_t* el_size)\n{\n\tconst DWORD lastError = GetLastError();\n\n\t*el_type_id = 0;\n\t*el_size = 0;\n\n\tfor(ULONG i = 0; i < numChildren; i++)\n\t{\n\t\tconst DWORD child_id = children[i];\n\n\t\tSYMBOL_INFO_PACKAGEW2 sp;\n\t\tSYMBOL_INFOW* sym = &sp.si;\n\t\tif(!pSymFromIndexW(hProcess, state.moduleBase, child_id, sym))\n\t\t{\n\t\t\t// this happens for several UDTs; cause is unknown.\n\t\t\tENSURE(GetLastError() == ERROR_NOT_FOUND);\n\t\t\tcontinue;\n\t\t}\n\t\tif(!wcscmp(sym->Name, child_name))\n\t\t{\n\t\t\t*el_type_id = sym->TypeIndex;\n\t\t\t*el_size = (size_t)sym->Size;\n\t\t\treturn INFO::OK;\n\t\t}\n\t}\n\n\tSetLastError(lastError);\n\n\t// (happens if called for containers that are treated as STL but are not)\n\treturn ERR::SYM_CHILD_NOT_FOUND;\t// NOWARN\n}\n\n\nstatic Status udt_dump_std(const wchar_t* type_name, const u8* p, size_t size, DumpState& state, ULONG numChildren, const DWORD* children)\n{\n\tStatus err;\n\n\t// not a C++ standard library object; can't handle it.\n\tif(wcsncmp(type_name, L\"std::\", 5) != 0)\n\t\treturn INFO::CANNOT_HANDLE;\n\n\t// check for C++ objects that should be displayed via udt_dump_normal.\n\t// C++03 containers are special-cased and the rest (apart from those here)\n\t// are ignored, because for the most part they are spew.\n\tif(!wcsncmp(type_name, L\"std::pair\", 9) ||\n\t   !wcsncmp(type_name, L\"std::tr1::\", 10))\n\t\treturn INFO::CANNOT_HANDLE;\n\n\t// display contents of STL containers\n\t// .. get element type\n\tDWORD el_type_id;\n\tsize_t el_size;\n \terr = udt_get_child_type(L\"value_type\", numChildren, children, state, &el_type_id, &el_size);\n\tif(err != INFO::OK)\n\t\tgoto not_valid_container;\n\t// .. get iterator and # elements\n\tsize_t el_count;\n\tDebugStlIterator el_iterator;\n\tu8 it_mem[DEBUG_STL_MAX_ITERATOR_SIZE];\n\terr = debug_stl_get_container_info(type_name, p, size, el_size, &el_count, &el_iterator, it_mem);\n\tif(err != INFO::OK)\n\t\tgoto not_valid_container;\n\treturn dump_sequence(el_iterator, it_mem, el_count, el_type_id, el_size, state);\nnot_valid_container:\n\n\t// build and display detailed \"error\" message.\n\twchar_t buf[100];\n\tconst wchar_t* text;\n\t// .. object named std::* but doesn't include a \"value_type\" child =>\n\t//    it's a non-STL C++ stdlib object. wasn't handled by the\n\t//    special case above, so we just display its simplified type name\n\t//    (the contents are usually spew).\n\tif(err == ERR::SYM_CHILD_NOT_FOUND)\n\t\ttext = L\"\";\n\t// .. not one of the containers we can analyse.\n\telse if(err == ERR::STL_CNT_UNKNOWN)\n\t\ttext = L\"unknown \";\n\telse if(err == ERR::STL_CNT_UNSUPPORTED)\n\t\ttext = L\"unsupported \";\n\t// .. container of a known type but contents are invalid.\n\telse if(err == ERR::STL_CNT_INVALID)\n\t\ttext = L\"uninitialized/invalid \";\n\t// .. some other error encountered\n\telse\n\t{\n\t\twchar_t description[200];\n\t\t(void)StatusDescription(err, description, ARRAY_SIZE(description));\n\t\tswprintf_s(buf, ARRAY_SIZE(buf), L\"error \\\"%ls\\\" while analyzing \", description);\n\t\ttext = buf;\n\t}\n\n\t// (debug_stl modifies its input string in-place; type_name is\n\t// a const string returned by dbghelp)\n\twchar_t type_name_buf[DEBUG_SYMBOL_CHARS];\n\twcscpy_s(type_name_buf, ARRAY_SIZE(type_name_buf), type_name);\n\n\tout(L\"(%ls%ls)\", text, debug_stl_simplify_name(type_name_buf));\n\treturn INFO::OK;\n}\n\n\nstatic bool udt_should_suppress(const wchar_t* type_name)\n{\n\t// specialized HANDLEs are defined as pointers to structs by\n\t// DECLARE_HANDLE. we only want the numerical value (pointer address),\n\t// so prevent these structs from being displayed.\n\t// note: no need to check for indirection; these are only found in\n\t// HANDLEs (which are pointers).\n\t// removed obsolete defs: HEVENT, HFILE, HUMPD\n\tif(type_name[0] != 'H')\n\t\tgoto not_handle;\n#define SUPPRESS_HANDLE(name) if(!wcscmp(type_name, L#name L\"__\")) return true;\n\tSUPPRESS_HANDLE(HACCEL);\n\tSUPPRESS_HANDLE(HBITMAP);\n\tSUPPRESS_HANDLE(HBRUSH);\n\tSUPPRESS_HANDLE(HCOLORSPACE);\n\tSUPPRESS_HANDLE(HCURSOR);\n\tSUPPRESS_HANDLE(HDC);\n\tSUPPRESS_HANDLE(HENHMETAFILE);\n\tSUPPRESS_HANDLE(HFONT);\n\tSUPPRESS_HANDLE(HGDIOBJ);\n\tSUPPRESS_HANDLE(HGLOBAL);\n\tSUPPRESS_HANDLE(HGLRC);\n\tSUPPRESS_HANDLE(HHOOK);\n\tSUPPRESS_HANDLE(HICON);\n\tSUPPRESS_HANDLE(HIMAGELIST);\n\tSUPPRESS_HANDLE(HIMC);\n\tSUPPRESS_HANDLE(HINSTANCE);\n\tSUPPRESS_HANDLE(HKEY);\n\tSUPPRESS_HANDLE(HKL);\n\tSUPPRESS_HANDLE(HKLOCAL);\n\tSUPPRESS_HANDLE(HMENU);\n\tSUPPRESS_HANDLE(HMETAFILE);\n\tSUPPRESS_HANDLE(HMODULE);\n\tSUPPRESS_HANDLE(HMONITOR);\n\tSUPPRESS_HANDLE(HPALETTE);\n\tSUPPRESS_HANDLE(HPEN);\n\tSUPPRESS_HANDLE(HRGN);\n\tSUPPRESS_HANDLE(HRSRC);\n\tSUPPRESS_HANDLE(HSTR);\n\tSUPPRESS_HANDLE(HTASK);\n\tSUPPRESS_HANDLE(HWINEVENTHOOK);\n\tSUPPRESS_HANDLE(HWINSTA);\n\tSUPPRESS_HANDLE(HWND);\nnot_handle:\n\n\treturn false;\n}\n\n\nstatic Status udt_dump_suppressed(const wchar_t* type_name, const u8* UNUSED(p), size_t UNUSED(size), DumpState state, ULONG UNUSED(numChildren), const DWORD* UNUSED(children))\n{\n\tif(!udt_should_suppress(type_name))\n\t\treturn INFO::CANNOT_HANDLE;\n\n\t// the data symbol is pointer-to-UDT. since we won't display its\n\t// contents, leave only the pointer's value.\n\tif(state.indirection)\n\t\tout_erase(4);\t// \" -> \"\n\n\t// indicate something was deliberately left out\n\t// (otherwise, lack of output may be taken for an error)\n\tout(L\" (..)\");\n\n\treturn INFO::OK;\n}\n\n\n// (by now) non-trivial heuristic to determine if a UDT should be\n// displayed on one line or several. split out of udt_dump_normal.\nstatic bool udt_fits_on_one_line(const wchar_t* type_name, size_t child_count, size_t total_size)\n{\n\t// special case: always put CStr* on one line\n\t// (std::*string are displayed directly, but these go through\n\t// udt_dump_normal. we want to avoid the ensuing 3-line output)\n\tif(!wcscmp(type_name, L\"CStr\") || !wcscmp(type_name, L\"CStr8\") || !wcscmp(type_name, L\"CStrW\"))\n\t\treturn true;\n\n\t// try to get actual number of relevant children\n\t// (typedefs etc. are never displayed, but are included in child_count.\n\t// we have to balance that vs. tons of static members, which aren't\n\t// reflected in total_size).\n\t// .. prevent division by 0.\n\tif(child_count == 0)\n\t\tchild_count = 1;\n\t// special-case a few types that would otherwise be classified incorrectly\n\t// (due to having more or less than expected relevant children)\n\tif(!wcsncmp(type_name, L\"std::pair\", 9))\n\t\tchild_count = 2;\n\n\tconst size_t avg_size = total_size / child_count;\n\t\t// (if 0, no worries - child_count will probably be large and\n\t\t// we return false, which is a safe default)\n\n\t// small UDT with a few (small) members: fits on one line.\n\tif(child_count <= 3 && avg_size <= sizeof(int))\n\t\treturn true;\n\n\treturn false;\n}\n\n\nstatic Status udt_dump_normal(const wchar_t* type_name, const u8* p, size_t size, DumpState state, ULONG numChildren, const DWORD* children)\n{\n\t// special case: boost::unordered types are complex and may cause a stack overflow\n\t// see http://trac.wildfiregames.com/ticket/1813\n\t// TODO: at least give some info about them\n\tif(!wcsncmp(type_name, L\"boost::unordered\", 16))\n\t\treturn INFO::CANNOT_HANDLE;\n\n\tconst bool fits_on_one_line = udt_fits_on_one_line(type_name, numChildren, size);\n\n\t// prevent infinite recursion just to be safe (shouldn't happen)\n\tif(state.level >= maxLevel)\n\t\tWARN_RETURN(ERR::SYM_NESTING_LIMIT);\n\tstate.level++;\n\n\tout(fits_on_one_line? L\"{ \" : L\"\\r\\n\");\n\n\tbool displayed_anything = false;\n\tfor(ULONG i = 0; i < numChildren; i++)\n\t{\n\t\tconst DWORD child_id = children[i];\n\n\t\t// get offset. if not available, skip this child\n\t\t// (we only display data here, not e.g. typedefs)\n\t\tDWORD ofs = 0;\n\t\tif(!pSymGetTypeInfo(hProcess, state.moduleBase, child_id, TI_GET_OFFSET, &ofs))\n\t\t\tcontinue;\n\t\tif(ofs >= size)\n\t\t{\n\t\t\tdebug_printf(\"INVALID_UDT %s %d %d\\n\", utf8_from_wstring(type_name).c_str(), ofs, size);\n\t\t}\n\t\t//ENSURE(ofs < size);\n\n\t\tif(!fits_on_one_line)\n\t\t\tINDENT;\n\n\t\tconst u8* el_p = p+ofs;\n\t\tStatus err = dump_sym(child_id, el_p, state);\n\n\t\t// there was no output for this child; undo its indentation (if any),\n\t\t// skip everything below and proceed with the next child.\n\t\tif(err == INFO::SYM_SUPPRESS_OUTPUT)\n\t\t{\n\t\t\tif(!fits_on_one_line)\n\t\t\t\tUNINDENT;\n\t\t\tcontinue;\n\t\t}\n\n\t\tdisplayed_anything = true;\n\t\tdump_error(err);\t// nop if err == INFO::OK\n\t\tout(fits_on_one_line? L\", \" : L\"\\r\\n\");\n\n\t\tif(err == ERR::SYM_SINGLE_SYMBOL_LIMIT)\n\t\t\tbreak;\n\t}\t// for each child\n\n\tstate.level--;\n\n\tif(!displayed_anything)\n\t{\n\t\tout_erase(2);\t// \"{ \" or \"\\r\\n\"\n\t\tout(L\"(%ls)\", type_name);\n\t\treturn INFO::OK;\n\t}\n\n\t// remove trailing comma separator\n\t// note: we can't avoid writing it by checking if i == numChildren-1:\n\t// each child might be the last valid data member.\n\tif(fits_on_one_line)\n\t{\n\t\tout_erase(2);\t// \", \"\n\t\tout(L\" }\");\n\t}\n\n\treturn INFO::OK;\n}\n\n\nstatic Status dump_sym_udt(DWORD type_id, const u8* p, DumpState& state)\n{\n\tULONG64 size64 = 0;\n\tif(!pSymGetTypeInfo(hProcess, state.moduleBase, type_id, TI_GET_LENGTH, &size64))\n\t\tWARN_RETURN(ERR::SYM_TYPE_INFO_UNAVAILABLE);\n\tconst size_t size = (size_t)size64;\n\n\t// get array of child symbols (members/functions/base classes).\n\tDWORD numChildren;\n\tif(!pSymGetTypeInfo(hProcess, state.moduleBase, type_id, TI_GET_CHILDRENCOUNT, &numChildren))\n\t\tWARN_RETURN(ERR::SYM_TYPE_INFO_UNAVAILABLE);\n\tTI_FINDCHILDREN_PARAMS2 fcp(numChildren);\n\tif(!pSymGetTypeInfo(hProcess, state.moduleBase, type_id, TI_FINDCHILDREN, &fcp))\n\t\tWARN_RETURN(ERR::SYM_TYPE_INFO_UNAVAILABLE);\n\tnumChildren = fcp.p.Count;\t// was truncated to maxChildren\n\tconst DWORD* children = fcp.p.ChildId;\n\n\tconst wchar_t* type_name;\n\tif(!pSymGetTypeInfo(hProcess, state.moduleBase, type_id, TI_GET_SYMNAME, &type_name))\n\t\tWARN_RETURN(ERR::SYM_TYPE_INFO_UNAVAILABLE);\n\n\tStatus ret;\n\t// note: order is important (e.g. STL special-case must come before\n\t// suppressing UDTs, which tosses out most other C++ stdlib classes)\n\n\tret = udt_dump_std       (type_name, p, size, state, numChildren, children);\n\tif(ret != INFO::CANNOT_HANDLE)\n\t\tgoto done;\n\n\tret = udt_dump_suppressed(type_name, p, size, state, numChildren, children);\n\tif(ret != INFO::CANNOT_HANDLE)\n\t\tgoto done;\n\n\tret = udt_dump_normal    (type_name, p, size, state, numChildren, children);\n\tif(ret != INFO::CANNOT_HANDLE)\n\t\tgoto done;\n\ndone:\n\tLocalFree((HLOCAL)type_name);\n\treturn ret;\n}\n\n\n//-----------------------------------------------------------------------------\n\n\nstatic Status dump_sym_vtable(DWORD UNUSED(type_id), const u8* UNUSED(p), DumpState& UNUSED(state))\n{\n\t// unsupported (vtable internals are undocumented; too much work).\n\treturn INFO::SYM_SUPPRESS_OUTPUT;\n}\n\n\n//-----------------------------------------------------------------------------\n\n\nstatic Status dump_sym_unknown(DWORD type_id, const u8* UNUSED(p), DumpState& state)\n{\n\t// redundant (already done in dump_sym), but this is rare.\n\tDWORD type_tag;\n\tif(!pSymGetTypeInfo(hProcess, state.moduleBase, type_id, TI_GET_SYMTAG, &type_tag))\n\t\tWARN_RETURN(ERR::SYM_TYPE_INFO_UNAVAILABLE);\n\n\tdebug_printf(\"SYM: unknown tag: %d\\n\", type_tag);\n\tout(L\"(unknown symbol type)\");\n\treturn INFO::OK;\n}\n\n\n//-----------------------------------------------------------------------------\n\ntypedef Status (*DumpFunc)(DWORD typeId, const u8* p, DumpState& state);\n\nstatic DumpFunc DumpFuncFromTypeTag(DWORD typeTag)\n{\n\tswitch(typeTag)\n\t{\n\tcase SymTagArrayType:\n\t\treturn dump_sym_array;\n\tcase SymTagBaseType:\n\t\treturn dump_sym_base_type;\n\tcase SymTagBaseClass:\n\t\treturn dump_sym_base_class;\n\tcase SymTagData:\n\t\treturn dump_sym_data;\n\tcase SymTagEnum:\n\t\treturn dump_sym_enum;\n\tcase SymTagFunction:\n\t\treturn dump_sym_function;\n\tcase SymTagFunctionType:\n\t\treturn dump_sym_function_type;\n\tcase SymTagPointerType:\n\t\treturn dump_sym_pointer;\n\tcase SymTagTypedef:\n\t\treturn dump_sym_typedef;\n\tcase SymTagUDT:\n\t\treturn dump_sym_udt;\n\tcase SymTagVTable:\n\t\treturn dump_sym_vtable;\n\tdefault:\n\t\treturn dump_sym_unknown;\n\t}\n}\n\n\n// write name and value of the symbol <type_id> to the output buffer.\n// delegates to dump_sym_* depending on the symbol's tag.\nstatic Status dump_sym(DWORD type_id, const u8* p, DumpState& state)\n{\n\tRETURN_STATUS_IF_ERR(out_check_limit());\n\n\tDWORD typeTag;\n\tif(!pSymGetTypeInfo(hProcess, state.moduleBase, type_id, TI_GET_SYMTAG, &typeTag))\n\t\tWARN_RETURN(ERR::SYM_TYPE_INFO_UNAVAILABLE);\n\tconst DumpFunc dumpFunc = DumpFuncFromTypeTag(typeTag);\n\treturn dumpFunc(type_id, p, state);\n}\n\n\n//-----------------------------------------------------------------------------\n// stack trace\n//-----------------------------------------------------------------------------\n\nstatic bool ShouldSkipSymbol(const wchar_t* name)\n{\n\tif(!wcscmp(name, L\"suppress__\"))\n\t\treturn true;\n\tif(!wcscmp(name, L\"__profile\"))\n\t\treturn true;\n\treturn false;\n}\n\n// output the symbol's name and value via dump_sym*.\n// called from dump_frame_cb for each local symbol; lock is held.\nstatic BOOL CALLBACK dump_sym_cb(SYMBOL_INFOW* sym, ULONG UNUSED(size), PVOID userContext)\n{\n\tif(ShouldSkipSymbol(sym->Name))\n\t\treturn TRUE;\t// continue\n\n\tout_latch_pos();\t// see decl\n\tconst u8* p = (const u8*)(uintptr_t)sym->Address;\n\tDumpState state((uintptr_t)sym->ModBase, (LPSTACKFRAME64)userContext);\n\n\tINDENT;\n\tStatus err = dump_sym(sym->Index, p, state);\n\tdump_error(err);\n\tif(err == INFO::SYM_SUPPRESS_OUTPUT)\n\t\tUNINDENT;\n\telse\n\t\tout(L\"\\r\\n\");\n\n\treturn TRUE;\t// continue\n}\n\n// called by wdbg_sym_WalkStack for each stack frame\nstatic Status dump_frame_cb(const STACKFRAME64* sf, uintptr_t UNUSED(userContext))\n{\n\tvoid* func = (void*)(uintptr_t)sf->AddrPC.Offset;\n\n\twchar_t func_name[DEBUG_SYMBOL_CHARS]; wchar_t file[DEBUG_FILE_CHARS]; int line;\n\tStatus ret = ResolveSymbol_lk(func, func_name, file, &line);\n\tif(ret == INFO::OK)\n\t{\n\t\t// don't trace back further than the app's entry point\n\t\t// (no one wants to see this frame). checking for the\n\t\t// function name isn't future-proof, but not stopping is no big deal.\n\t\t// an alternative would be to check if module=kernel32, but\n\t\t// that would cut off callbacks as well.\n\t\t// note: the stdcall mangled name includes parameter size, which is\n\t\t// different in 64-bit, so only check the first characters.\n\t\tif(!wcsncmp(func_name, L\"_BaseProcessStart\", 17) ||\n\t\t   !wcscmp(func_name, L\"BaseThreadInitThunk\"))\n\t\t\treturn INFO::OK;\n\n\t\t// skip any mainCRTStartup frames\n\t\tif(!wcscmp(func_name, L\"__tmainCRTStartup\"))\n\t\t\treturn INFO::OK;\n\t\tif(!wcscmp(func_name, L\"mainCRTStartup\"))\n\t\t\treturn INFO::OK;\n\n\t\tout(L\"%ls (%ls:%d)\\r\\n\", func_name, file, line);\n\t}\n\telse\n\t\tout(L\"%p\\r\\n\", func);\n\n\tWinScopedPreserveLastError s;\t// SymSetContext\n\n\t// only enumerate symbols for this stack frame\n\t// (i.e. its locals and parameters)\n\t// problem: debug info is scope-aware, so we won't see any variables\n\t// declared in sub-blocks. we'd have to pass an address in that block,\n\t// which isn't worth the trouble.\n\tIMAGEHLP_STACK_FRAME isf = PopulateImageStackFrame(*sf);\n\tconst PIMAGEHLP_CONTEXT ic = 0;\t// ignored\n\t// NB: this sometimes fails for reasons unknown in a static\n\t// member function, possibly because the return address is in kernel32\n\t(void)pSymSetContext(hProcess, &isf, ic);\n\n\tconst ULONG64 base = 0; const wchar_t* const mask = 0;\t// use scope set by pSymSetContext\n\tpSymEnumSymbolsW(hProcess, base, mask, dump_sym_cb, (PVOID)sf);\n\n\tif(GetLastError() == ERROR_NOT_SUPPORTED)\t// no debug info present?\n\t\tSetLastError(0);\n\n\tout(L\"\\r\\n\");\n\treturn INFO::OK;\n}\n\n\nStatus debug_DumpStack(wchar_t* buf, size_t maxChars, void* pcontext, const wchar_t* lastFuncToSkip)\n{\n\tstatic intptr_t busy;\n\tif(!cpu_CAS(&busy, 0, 1))\n\t\treturn ERR::REENTERED;\t// NOWARN\n\n\tout_init(buf, maxChars);\n\tptr_reset_visited();\n\n\twdbg_assert(pcontext != 0);\n\tStatus ret = wdbg_sym_WalkStack(dump_frame_cb, 0, *(CONTEXT*)pcontext, lastFuncToSkip);\n\n\tCOMPILER_FENCE;\n\tbusy = 0;\n\n\treturn ret;\n}\n\n\n//-----------------------------------------------------------------------------\n\n// write out a \"minidump\" containing register and stack state; this enables\n// examining the crash in a debugger. called by wdbg_exception_filter.\n// heavily modified from http://www.codeproject.com/debug/XCrashReportPt3.asp\n// lock must be held.\nvoid wdbg_sym_WriteMinidump(EXCEPTION_POINTERS* exception_pointers)\n{\n\tsym_init();\n\n\tWinScopedLock lock(WDBG_SYM_CS);\n\n\tOsPath path = ah_get_log_dir()/\"crashlog.dmp\";\n\tHANDLE hFile = CreateFileW(OsString(path).c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, 0, CREATE_ALWAYS, 0, 0);\n\tif(hFile == INVALID_HANDLE_VALUE)\n\t{\n\t\tDEBUG_DISPLAY_ERROR(L\"wdbg_sym_WriteMinidump: unable to create crashlog.dmp.\");\n\t\treturn;\n\t}\n\n\tMINIDUMP_EXCEPTION_INFORMATION mei;\n\tmei.ThreadId = GetCurrentThreadId();\n\tmei.ExceptionPointers = exception_pointers;\n\tmei.ClientPointers = FALSE;\n\t// exception_pointers is not in our address space.\n\n\t// note: we don't store other crashlog info within the dump file\n\t// (UserStreamParam), since we will need to generate a plain text file on\n\t// non-Windows platforms. users will just have to send us both files.\n\n\tHANDLE hProcess = GetCurrentProcess();\n\tDWORD pid = GetCurrentProcessId();\n\tif(!pMiniDumpWriteDump || !pMiniDumpWriteDump(hProcess, pid, hFile, MiniDumpNormal, &mei, 0, 0))\n\t\tDEBUG_DISPLAY_ERROR(L\"wdbg_sym_WriteMinidump: unable to generate minidump.\");\n\n\tCloseHandle(hFile);\n}\n"
  },
  {
    "path": "fpsgame/gui/sysdep/os/win/wdbg_sym.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * Win32 stack trace and symbol engine.\n */\n\n#ifndef INCLUDED_WDBG_SYM\n#define INCLUDED_WDBG_SYM\n\n#include \"lib/sysdep/os/win/win.h\"\t// CONTEXT, EXCEPTION_POINTERS\n\nstruct _tagSTACKFRAME64;\n\n/**\n * called for each stack frame found by wdbg_sym_WalkStack.\n *\n * @param frame the dbghelp stack frame (we can't just pass the\n *   instruction-pointer because dump_frame_cb needs the frame pointer to\n *   locate frame-relative variables)\n * @param cbData the user-specified value that was passed to wdbg_sym_WalkStack\n * @return Status (see RETURN_STATUS_FROM_CALLBACK).\n **/\ntypedef Status (*StackFrameCallback)(const _tagSTACKFRAME64* frame, uintptr_t cbData);\n\n/**\n * Iterate over a call stack, invoking a callback for each frame encountered.\n *\n * @param cb\n * @param cbData\n * @param context Processor context from which to start (taken from\n *   an exception record or debug_CaptureContext).\n * @param lastFuncToSkip\n *\n * @note It is safe to use ENSURE/debug_warn/WARN_RETURN_STATUS_IF_ERR even during a\n *   stack trace (which is triggered by ENSURE et al. in app code) because\n *   nested stack traces are ignored and only the error is displayed.\n **/\nLIB_API Status wdbg_sym_WalkStack(StackFrameCallback cb, uintptr_t cbData, CONTEXT& context, const wchar_t* lastFuncToSkip = 0);\n\nLIB_API void wdbg_sym_WriteMinidump(EXCEPTION_POINTERS* ep);\n\n#endif\t// #ifndef INCLUDED_WDBG_SYM\n"
  },
  {
    "path": "fpsgame/gui/sysdep/os/win/wdir_watch.cpp",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * Win32 directory change notification\n */\n\n#include \"precompiled.h\"\n#include \"lib/sysdep/dir_watch.h\"\n\n#include \"lib/allocators/shared_ptr.h\"\n#include \"lib/path.h\"\t// path_is_subpath\n#include \"lib/sysdep/os/win/win.h\"\n#include \"lib/sysdep/os/win/winit.h\"\n#include \"lib/sysdep/os/win/wutil.h\"\n#include \"lib/sysdep/os/win/wiocp.h\"\n\n\nWINIT_REGISTER_MAIN_INIT(wdir_watch_Init);\nWINIT_REGISTER_MAIN_SHUTDOWN(wdir_watch_Shutdown);\n\n\n//-----------------------------------------------------------------------------\n// DirHandle\n\nclass DirHandle\n{\npublic:\n\tDirHandle(const OsPath& path)\n\t{\n\t\tWinScopedPreserveLastError s;\t// CreateFile\n\t\tconst DWORD share = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;\n\t\tconst DWORD flags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED;\n\t\tm_hDir = CreateFileW(OsString(path).c_str(), FILE_LIST_DIRECTORY, share, 0, OPEN_EXISTING, flags, 0);\n\t}\n\n\t~DirHandle()\n\t{\n\t\t// contrary to MSDN, the canceled IOs do not issue a completion notification.\n\t\t// (receiving packets after (unsuccessful) cancellation would be dangerous)\n\t\tBOOL ok = CancelIo(m_hDir);\n\t\tWARN_IF_FALSE(ok);\n\n\t\tCloseHandle(m_hDir);\n\t\tm_hDir = INVALID_HANDLE_VALUE;\n\t}\n\n\t// == INVALID_HANDLE_VALUE if path doesn't exist\n\toperator HANDLE() const\n\t{\n\t\treturn m_hDir;\n\t}\n\nprivate:\n\tHANDLE m_hDir;\n};\n\n\n//-----------------------------------------------------------------------------\n// DirWatchRequest\n\nclass DirWatchRequest\n{\n\tNONCOPYABLE(DirWatchRequest);\npublic:\n\tDirWatchRequest(const OsPath& path)\n\t\t: m_path(path), m_dirHandle(path), m_data(new u8[dataSize])\n\t{\n\t\tm_ovl = (OVERLAPPED*)calloc(1, sizeof(OVERLAPPED));\t// rationale for dynamic alloc: see decl\n\t\tENSURE(m_ovl);\n\n\t\t// (hEvent is needed for the wait after CancelIo below)\n\t\tconst BOOL manualReset = TRUE;\n\t\tconst BOOL initialState = FALSE;\n\t\tm_ovl->hEvent = CreateEvent(0, manualReset, initialState, 0);\n\t}\n\n\t~DirWatchRequest()\n\t{\n\t\t// we need to free m_data here, so the pending IO had better\n\t\t// not write to that memory in future. therefore:\n\t\tWARN_IF_FALSE(CancelIo(m_dirHandle));\n\t\t// however, this is not synchronized with the DPC (?) that apparently\n\t\t// delivers the data - m_data is filled anyway.\n\t\t// we need to ensure that either the IO has happened or that it\n\t\t// was successfully canceled before freeing m_data and m_ovl, so wait:\n\t\t{\n\t\t\tWinScopedPreserveLastError s;\n\t\t\t// (GetOverlappedResult without a valid hEvent hangs on Vista;\n\t\t\t// we'll abort after a timeout to be safe.)\n\t\t\tconst DWORD ret = WaitForSingleObject(m_ovl->hEvent, 1000);\n\t\t\tWARN_IF_FALSE(CloseHandle(m_ovl->hEvent));\n\t\t\tif(ret == WAIT_OBJECT_0 || GetLastError() == ERROR_OPERATION_ABORTED)\n\t\t\t{\n\t\t\t\tSetLastError(0);\n\t\t\t\tdelete[] m_data;\n\t\t\t\tfree(m_ovl);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// (this could conceivably happen if a kernel debugger\n\t\t\t\t// hangs the system during the wait duration.)\n\t\t\t\tdebug_printf(\"WARNING: IO may still be pending; to avoid memory corruption, we won't free the buffer.\\n\");\n\t\t\t\tDEBUG_WARN_ERR(ERR::TIMED_OUT);\n\t\t\t\t// intentionally leak m_data and m_ovl!\n\t\t\t}\n\t\t}\n\t}\n\n\tconst OsPath& Path() const\n\t{\n\t\treturn m_path;\n\t}\n\n\tvoid AttachTo(HANDLE& hIOCP) const\n\t{\n\t\tAttachToCompletionPort(m_dirHandle, hIOCP, (uintptr_t)this);\n\t}\n\n\t// (called again after each notification, so it mustn't AttachToCompletionPort)\n\tStatus Issue()\n\t{\n\t\tif(m_dirHandle == INVALID_HANDLE_VALUE)\n\t\t\tWARN_RETURN(ERR::PATH_NOT_FOUND);\n\n\t\tconst BOOL watchSubtree = TRUE;\t// (see IntrusiveLink comments)\n\t\tconst DWORD filter = FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME |\n\t\t\tFILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE |\n\t\t\tFILE_NOTIFY_CHANGE_CREATION;\n\t\t// not set: FILE_NOTIFY_CHANGE_ATTRIBUTES, FILE_NOTIFY_CHANGE_LAST_ACCESS, FILE_NOTIFY_CHANGE_SECURITY\n\t\tDWORD undefined = 0;\t// (non-NULL pointer avoids BoundsChecker warning)\n\t\tm_ovl->Internal = 0;\n\t\tWARN_IF_FALSE(ReadDirectoryChangesW(m_dirHandle, m_data, dataSize, watchSubtree, filter, &undefined, m_ovl, 0));\n\t\treturn INFO::OK;\n\t}\n\n\t/**\n\t * (call when completion port indicates data is available)\n\t **/\n\tvoid RetrieveNotifications(DirWatchNotifications& notifications) const\n\t{\n\t\tconst FILE_NOTIFY_INFORMATION* fni = (const FILE_NOTIFY_INFORMATION*)m_data;\n\t\tfor(;;)\n\t\t{\n\t\t\t// convert (non-zero-terminated) BSTR to Path::String\n\t\t\tcassert(sizeof(wchar_t) == sizeof(WCHAR));\n\t\t\tconst size_t length = fni->FileNameLength / sizeof(WCHAR);\n\t\t\tPath::String name(fni->FileName, length);\n\n\t\t\t// (NB: name is actually a relative path since we watch entire subtrees)\n\t\t\tconst OsPath pathname = m_path / name;\n\t\t\tconst DirWatchNotification::EType type = TypeFromAction(fni->Action);\n\t\t\tnotifications.push_back(DirWatchNotification(pathname, type));\n\n\t\t\tif(!fni->NextEntryOffset)\t// this was the last entry.\n\t\t\t\tbreak;\n\t\t\tfni = (const FILE_NOTIFY_INFORMATION*)(uintptr_t(fni) + fni->NextEntryOffset);\n\t\t}\n\t}\n\nprivate:\n\tstatic DirWatchNotification::EType TypeFromAction(const DWORD action)\n\t{\n\t\tswitch(action)\n\t\t{\n\t\tcase FILE_ACTION_ADDED:\n\t\tcase FILE_ACTION_RENAMED_NEW_NAME:\n\t\t\treturn DirWatchNotification::Created;\n\n\t\tcase FILE_ACTION_REMOVED:\n\t\tcase FILE_ACTION_RENAMED_OLD_NAME:\n\t\t\treturn DirWatchNotification::Deleted;\n\n\t\tcase FILE_ACTION_MODIFIED:\n\t\t\treturn DirWatchNotification::Changed;\n\n\t\tdefault:\n\t\t\tDEBUG_WARN_ERR(ERR::LOGIC);\n\t\t\treturn DirWatchNotification::Changed;\n\t\t}\n\t}\n\n\tOsPath m_path;\n\tDirHandle m_dirHandle;\n\n\t// rationale:\n\t// - if too small, notifications may be lost! (the CSD-poll application\n\t//   may be confronted with hundreds of new files in a short time frame)\n\t// - requests larger than 64 KiB fail on SMB due to packet restrictions.\n\tstatic const size_t dataSize = 64*KiB;\n\n\t// rationale:\n\t// - each instance needs their own buffer. (we can't share a central\n\t//   copy because the watches are independent and may be triggered\n\t//   'simultaneously' before the next poll.)\n\t// - lifetime must be managed manually (see dtor)\n\tu8* m_data;\n\n\t// rationale:\n\t// - ReadDirectoryChangesW's asynchronous mode is triggered by passing\n\t//   a valid OVERLAPPED parameter; notification proceeds via\n\t//   completion ports, but we still need hEvent - see above.\n\t// - this must remain valid while the IO is pending. if the wait\n\t//   were to fail, we must not free this memory, either.\n\tOVERLAPPED* m_ovl;\n};\n\ntypedef shared_ptr<DirWatchRequest> PDirWatchRequest;\n\n\n//-----------------------------------------------------------------------------\n// IntrusiveLink\n\n// using watches of entire subtrees to satisfy single-directory requests\n// requires a list of existing watches. an intrusive, doubly-linked list\n// is convenient because removal must occur within the DirWatch destructor.\n// since boost::intrusive doesn't automatically remove objects from their\n// containers when they are destroyed, we implement a simple circular list\n// via sentinel. note that DirWatchManager iterates over DirWatch, not their\n// embedded links. we map from link to the parent object via offsetof\n// (slightly less complex than storing back pointers to the parents, and\n// avoids 'this-pointer used during initialization list' warnings).\n\nclass IntrusiveLink\n{\npublic:\n\tIntrusiveLink()\n\t{\n\t\tm_prev = m_next = this;\t// sentinel\n\t}\n\n\tIntrusiveLink(IntrusiveLink* sentinel)\n\t{\n\t\t// insert after sentinel\n\t\tm_prev = sentinel;\n\t\tm_next = sentinel->m_next;\n\t\tm_next->m_prev = this;\n\t\tsentinel->m_next = this;\n\t}\n\n\t~IntrusiveLink()\n\t{\n\t\t// remove from list\n\t\tm_prev->m_next = m_next;\n\t\tm_next->m_prev = m_prev;\n\t}\n\n\tIntrusiveLink* Next() const\n\t{\n\t\treturn m_next;\n\t}\n\nprivate:\n\tIntrusiveLink* m_prev;\n\tIntrusiveLink* m_next;\n};\n\n\n//-----------------------------------------------------------------------------\n// DirWatch\n\nstruct DirWatch\n{\n\tDirWatch(IntrusiveLink* sentinel, const PDirWatchRequest& request)\n\t\t: link(sentinel), request(request)\n\t{\n\t}\n\n\tIntrusiveLink link;\n\tPDirWatchRequest request;\n};\n\n\n//-----------------------------------------------------------------------------\n// DirWatchManager\n\nclass DirWatchManager\n{\npublic:\n\tDirWatchManager()\n\t\t: hIOCP(0)\t// Win32 requires 0-init; created in the first call to AttachTo\n\t{\n\t}\n\n\t~DirWatchManager()\n\t{\n\t\tCloseHandle(hIOCP);\n\t}\n\n\tStatus Add(const OsPath& path, PDirWatch& dirWatch)\n\t{\n\t\tENSURE(path.IsDirectory());\n\n\t\t// check if this is a subdirectory of a tree that's already being\n\t\t// watched (this is much faster than issuing a new watch; it also\n\t\t// prevents accidentally watching the same directory twice).\n\t\tfor(IntrusiveLink* link = m_sentinel.Next(); link != &m_sentinel; link = link->Next())\n\t\t{\n\t\t\tDirWatch* const existingDirWatch = (DirWatch*)(uintptr_t(link) - offsetof(DirWatch, link));\n\t\t\tif(path_is_subpath(OsString(path).c_str(), OsString(existingDirWatch->request->Path()).c_str()))\n\t\t\t{\n\t\t\t\tdirWatch.reset(new DirWatch(&m_sentinel, existingDirWatch->request));\n\t\t\t\treturn INFO::OK;\n\t\t\t}\n\t\t}\n\n\t\tPDirWatchRequest request(new DirWatchRequest(path));\n\t\trequest->AttachTo(hIOCP);\n\t\tRETURN_STATUS_IF_ERR(request->Issue());\n\t\tdirWatch.reset(new DirWatch(&m_sentinel, request));\n\t\treturn INFO::OK;\n\t}\n\n\tStatus Poll(DirWatchNotifications& notifications)\n\t{\nPOLL_AGAIN:\n\t\tDWORD bytesTransferred; ULONG_PTR key; OVERLAPPED* ovl;\n\t\tconst Status ret = PollCompletionPort(hIOCP, 0, bytesTransferred, key, ovl);\n\t\tif(ret == ERR::ABORTED)\t// watch was canceled\n\t\t\tgoto POLL_AGAIN;\n\t\tRETURN_STATUS_IF_ERR(ret);\n\n\t\tDirWatchRequest* request = (DirWatchRequest*)key;\n\t\trequest->RetrieveNotifications(notifications);\n\t\tRETURN_STATUS_IF_ERR(request->Issue());\t// re-issue\n\t\treturn INFO::OK;\n\t}\n\nprivate:\n\tIntrusiveLink m_sentinel;\n\tHANDLE hIOCP;\n};\n\nstatic DirWatchManager* s_dirWatchManager;\n\n\n//-----------------------------------------------------------------------------\n\nStatus dir_watch_Add(const OsPath& path, PDirWatch& dirWatch)\n{\n\tWinScopedLock lock(WDIR_WATCH_CS);\n\treturn s_dirWatchManager->Add(path, dirWatch);\n}\n\nStatus dir_watch_Poll(DirWatchNotifications& notifications)\n{\n\tWinScopedLock lock(WDIR_WATCH_CS);\n\treturn s_dirWatchManager->Poll(notifications);\n}\n\n\n//-----------------------------------------------------------------------------\n\nstatic Status wdir_watch_Init()\n{\n\ts_dirWatchManager = new DirWatchManager;\n\treturn INFO::OK;\n}\n\nstatic Status wdir_watch_Shutdown()\n{\n\tSAFE_DELETE(s_dirWatchManager);\n\treturn INFO::OK;\n}\n"
  },
  {
    "path": "fpsgame/gui/sysdep/os/win/wdll_delay_load.cpp",
    "content": "/* Copyright (c) 2013 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * DLL delay loading and notification\n */\n\n#include \"precompiled.h\"\n#include \"lib/sysdep/os/win/wdll_delay_load.h\"\n\n#include \"lib/sysdep/cpu.h\"\n#include \"lib/sysdep/os/win/win.h\"\n#include \"lib/sysdep/os/win/winit.h\"\n\nWINIT_REGISTER_LATE_SHUTDOWN2(wdll_Shutdown);\t// last - DLLs are unloaded here\n\n//-----------------------------------------------------------------------------\n// delay loading (modified from VC7 DelayHlp.cpp and DelayImp.h)\n\n#if MSC_VERSION && MSC_VERSION >= 1700\n// FACILITY_VISUALCPP is already defined in winerror.h in VC2012\n# undef FACILITY_VISUALCPP\n#endif\n#define FACILITY_VISUALCPP  ((LONG)0x6d)\n#define VcppException(sev,status)  ((sev) | (FACILITY_VISUALCPP<<16) | status)\n\ntypedef IMAGE_THUNK_DATA *          PImgThunkData;\ntypedef const IMAGE_THUNK_DATA *    PCImgThunkData;\ntypedef DWORD                       RVA;\n\ntypedef struct ImgDelayDescr {\n\tDWORD           grAttrs;        // attributes\n\tRVA             rvaDLLName;     // RVA to dll name\n\tRVA             rvaHmod;        // RVA of module handle\n\tRVA             rvaIAT;         // RVA of the IAT\n\tRVA             rvaINT;         // RVA of the INT\n\tRVA             rvaBoundIAT;    // RVA of the optional bound IAT\n\tRVA             rvaUnloadIAT;   // RVA of optional copy of original IAT\n\tDWORD           dwTimeStamp;    // 0 if not bound,\n\t// O.W. date/time stamp of DLL bound to (Old BIND)\n} ImgDelayDescr, * PImgDelayDescr;\n\ntypedef const ImgDelayDescr *   PCImgDelayDescr;\n\nenum DLAttr {                   // Delay Load Attributes\n\tdlattrRva = 0x1                // RVAs are used instead of pointers\n\t// Having this set indicates a VC7.0\n\t// and above delay load descriptor.\n};\n\nenum {\n\tdliStartProcessing,             // used to bypass or note helper only\n\tdliNoteStartProcessing = dliStartProcessing,\n\n\tdliNotePreLoadLibrary,          // called just before LoadLibrary, can\n\t//  override w/ new HMODULE return val\n\tdliNotePreGetProcAddress,       // called just before GetProcAddress, can\n\t//  override w/ new FARPROC return value\n\tdliFailLoadLib,                 // failed to load library, fix it by\n\t//  returning a valid HMODULE\n\tdliFailGetProc,                 // failed to get proc address, fix it by\n\t//  returning a valid FARPROC\n\tdliNoteEndProcessing           // called after all processing is done, no\n\t//  no bypass possible at this point except\n\t//  by longjmp()/throw()/RaiseException.\n};\n\n\ntypedef struct DelayLoadProc {\n\tBOOL                fImportByName;\n\tunion {\n\t\tLPCSTR          szProcName;\n\t\tDWORD           dwOrdinal;\n\t};\n} DelayLoadProc;\n\n\ntypedef struct DelayLoadInfo {\n\tDWORD               cb;         // size of structure\n\tPCImgDelayDescr     pidd;       // raw form of data (everything is there)\n\tFARPROC *           ppfn;       // points to address of function to load\n\tLPCSTR              szDll;      // name of dll\n\tDelayLoadProc       dlp;        // name or ordinal of procedure\n\tHMODULE             hmodCur;    // the hInstance of the library we have loaded\n\tFARPROC             pfnCur;     // the actual function that will be called\n\tDWORD               dwLastError;// error received (if an error notification)\n} DelayLoadInfo, * PDelayLoadInfo;\n\n\ntypedef FARPROC (WINAPI *PfnDliHook)(unsigned dliNotify, PDelayLoadInfo  pdli);\n\n\n//-----------------------------------------------------------------------------\n// load notification\n\nstatic WdllLoadNotify* notify_list;\n\nvoid wdll_add_notify(WdllLoadNotify* notify)\n{\n\tnotify->next = notify_list;\n\tnotify_list = notify;\n}\n\nstatic FARPROC WINAPI notify_hook(unsigned dliNotify, PDelayLoadInfo pdli)\n{\n\tif(dliNotify != dliNoteEndProcessing)\n\t\treturn 0;\n\n\tfor(WdllLoadNotify* n = notify_list; n; n = n->next)\n\t\tif(strncasecmp(pdli->szDll, n->dll_name, strlen(n->dll_name)) == 0)\n\t\t\tn->func();\n\n\treturn 0;\n}\n\n\n//-----------------------------------------------------------------------------\n// hook\n\n// The \"notify hook\" gets called for every call to the\n// delay load helper.  This allows a user to hook every call and\n// skip the delay load helper entirely.\n//\n// dliNotify == {\n//  dliStartProcessing |\n//  dliNotePreLoadLibrary  |\n//  dliNotePreGetProc |\n//  dliNoteEndProcessing}\n//  on this call.\n//\nEXTERN_C PfnDliHook __pfnDliNotifyHook2 = notify_hook;\n\n// This is the failure hook, dliNotify = {dliFailLoadLib|dliFailGetProc}\nEXTERN_C PfnDliHook __pfnDliFailureHook2 = 0;\n\n\n\n\n\n\n\n\n#if !ICC_VERSION\n#pragma intrinsic(strlen,memcmp,memcpy)\n#endif\n\n// utility function for calculating the index of the current import\n// for all the tables (INT, BIAT, UIAT, and IAT).\ninline unsigned\nIndexFromPImgThunkData(PCImgThunkData pitdCur, PCImgThunkData pitdBase) {\n    return (unsigned) (pitdCur - pitdBase);\n    }\n\n// C++ template utility function for converting RVAs to pointers\n//\nextern \"C\" const IMAGE_DOS_HEADER __ImageBase;\n\ntemplate <class X>\nX PFromRva(RVA rva) {\n    return X(PBYTE(&__ImageBase) + rva);\n    }\n\n// structure definitions for the list of unload records\ntypedef struct UnloadInfo * PUnloadInfo;\ntypedef struct UnloadInfo {\n    PUnloadInfo     puiNext;\n    PCImgDelayDescr pidd;\n    } UnloadInfo;\n\n// utility function for calculating the count of imports given the base\n// of the IAT.  NB: this only works on a valid IAT!\ninline unsigned\nCountOfImports(PCImgThunkData pitdBase) {\n    unsigned        cRet = 0;\n    PCImgThunkData  pitd = pitdBase;\n    while (pitd->u1.Function) {\n        pitd++;\n        cRet++;\n        }\n    return cRet;\n    }\n\nextern \"C\" PUnloadInfo __puiHead = 0;\n\nstruct ULI : public UnloadInfo\n{\n\tULI(PCImgDelayDescr pidd_)\n\t{\n\t\tpidd = pidd_;\n\t\tLink();\n\t}\n\n\t~ULI() { Unlink();\t}\n\n\tvoid* operator new(size_t cb) {\treturn ::LocalAlloc(LPTR, cb); }\n\tvoid operator delete(void* pv) { ::LocalFree(pv); }\n\n\tvoid Unlink()\n\t{\n\t\tPUnloadInfo* ppui = &__puiHead;\n\n\t\twhile (*ppui && *ppui != this)\n\t\t\tppui = &((*ppui)->puiNext);\n\t\tif (*ppui == this)\n\t\t\t*ppui = puiNext;\n\t}\n\n\tvoid Link()\n\t{\n\t\tpuiNext = __puiHead;\n\t\t__puiHead = this;\n\t}\n};\n\n\n// For our own internal use, we convert to the old\n// format for convenience.\n//\nstruct InternalImgDelayDescr {\n    DWORD           grAttrs;        // attributes\n    LPCSTR          szName;         // pointer to dll name\n    HMODULE *       phmod;          // address of module handle\n    PImgThunkData   pIAT;           // address of the IAT\n    PCImgThunkData  pINT;           // address of the INT\n    PCImgThunkData  pBoundIAT;      // address of the optional bound IAT\n    PCImgThunkData  pUnloadIAT;     // address of optional copy of original IAT\n    DWORD           dwTimeStamp;    // 0 if not bound,\n                                    // O.W. date/time stamp of DLL bound to (Old BIND)\n    };\n\ntypedef InternalImgDelayDescr *         PIIDD;\ntypedef const InternalImgDelayDescr *   PCIIDD;\n\nstatic inline PIMAGE_NT_HEADERS WINAPI\nPinhFromImageBase(HMODULE hmod) {\n    return PIMAGE_NT_HEADERS(PBYTE(hmod) + PIMAGE_DOS_HEADER(hmod)->e_lfanew);\n    }\n\nstatic inline void WINAPI\nOverlayIAT(PImgThunkData pitdDst, PCImgThunkData pitdSrc) {\n    memcpy(pitdDst, pitdSrc, CountOfImports(pitdDst) * sizeof IMAGE_THUNK_DATA);\n    }\n\nstatic inline DWORD WINAPI\nTimeStampOfImage(PIMAGE_NT_HEADERS pinh) {\n    return pinh->FileHeader.TimeDateStamp;\n    }\n\nstatic inline bool WINAPI\nFLoadedAtPreferredAddress(PIMAGE_NT_HEADERS pinh, HMODULE hmod) {\n    return UINT_PTR(hmod) == pinh->OptionalHeader.ImageBase;\n    }\n\n\nextern \"C\" FARPROC WINAPI __delayLoadHelper2(PCImgDelayDescr pidd, FARPROC* ppfnIATEntry)\n{\n    // Set up some data we use for the hook procs but also useful for\n    // our own use\n    //\n    InternalImgDelayDescr   idd = {\n        pidd->grAttrs,\n        PFromRva<LPCSTR>(pidd->rvaDLLName),\n        PFromRva<HMODULE*>(pidd->rvaHmod),\n        PFromRva<PImgThunkData>(pidd->rvaIAT),\n        PFromRva<PCImgThunkData>(pidd->rvaINT),\n        PFromRva<PCImgThunkData>(pidd->rvaBoundIAT),\n        PFromRva<PCImgThunkData>(pidd->rvaUnloadIAT),\n        pidd->dwTimeStamp\n        };\n\n    DelayLoadInfo   dli = {\n        sizeof(DelayLoadInfo), pidd, ppfnIATEntry, idd.szName,\n        { 0 }, 0, 0, 0\n\t};\n\n    if (!(idd.grAttrs & dlattrRva))\n\t{\n        PDelayLoadInfo  rgpdli[1] = { &dli };\n        RaiseException(VcppException(ERROR_SEVERITY_ERROR, ERROR_INVALID_PARAMETER), 0, 1, PULONG_PTR(rgpdli));\n        return 0;\n    }\n\n    HMODULE hmod = *idd.phmod;\n\n    // Calculate the index for the IAT entry in the import address table\n    // N.B. The INT entries are ordered the same as the IAT entries so\n    // the calculation can be done on the IAT side.\n    //\n    const unsigned  iIAT = IndexFromPImgThunkData(PCImgThunkData(ppfnIATEntry), idd.pIAT);\n    const unsigned  iINT = iIAT;\n\n    PCImgThunkData  pitd = &(idd.pINT[iINT]);\n\n    dli.dlp.fImportByName = !IMAGE_SNAP_BY_ORDINAL(pitd->u1.Ordinal);\n\n    if (dli.dlp.fImportByName)\n        dli.dlp.szProcName = LPCSTR(PFromRva<PIMAGE_IMPORT_BY_NAME>(RVA(UINT_PTR(pitd->u1.AddressOfData)))->Name);\n    else\n        dli.dlp.dwOrdinal = DWORD(IMAGE_ORDINAL(pitd->u1.Ordinal));\n\n    // Call the initial hook.  If it exists and returns a function pointer,\n    // abort the rest of the processing and just return it for the call.\n    //\n    FARPROC pfnRet = NULL;\n\n    if (__pfnDliNotifyHook2) {\n        pfnRet = ((*__pfnDliNotifyHook2)(dliStartProcessing, &dli));\n\n        if (pfnRet != NULL)\n            goto HookBypass;\n    }\n\n    // Check to see if we need to try to load the library.\n    //\n    if (hmod == 0) {\n        if (__pfnDliNotifyHook2) {\n            hmod = HMODULE(((*__pfnDliNotifyHook2)(dliNotePreLoadLibrary, &dli)));\n            }\n        if (hmod == 0) {\n            hmod = ::LoadLibraryA(dli.szDll);\n            }\n        if (hmod == 0) {\n            dli.dwLastError = ::GetLastError();\n            if (__pfnDliFailureHook2) {\n                // when the hook is called on LoadLibrary failure, it will\n                // return 0 for failure and an hmod for the lib if it fixed\n                // the problem.\n                //\n                hmod = HMODULE((*__pfnDliFailureHook2)(dliFailLoadLib, &dli));\n                }\n\n            if (hmod == 0) {\n                PDelayLoadInfo  rgpdli[1] = { &dli };\n\n                RaiseException(VcppException(ERROR_SEVERITY_ERROR, ERROR_MOD_NOT_FOUND),\n                    0, 1, PULONG_PTR(rgpdli));\n\n                // If we get to here, we blindly assume that the handler of the exception\n                // has magically fixed everything up and left the function pointer in \n                // dli.pfnCur.\n                //\n                return dli.pfnCur;\n                }\n            }\n\n        // Store the library handle.  If it is already there, we infer\n        // that another thread got there first, and we need to do a\n        // FreeLibrary() to reduce the refcount\n        //\n        HMODULE hmodT = HMODULE(InterlockedExchangePointer((PVOID *) idd.phmod, PVOID(hmod)));\n        if (hmodT != hmod) {\n            // add lib to unload list if we have unload data\n            if (pidd->rvaUnloadIAT) {\n                new ULI(pidd);\n                }\n            }\n        else {\n            ::FreeLibrary(hmod);\n            }\n        \n        }\n\n    // Go for the procedure now.\n    //\n    dli.hmodCur = hmod;\n    if (__pfnDliNotifyHook2) {\n        pfnRet = (*__pfnDliNotifyHook2)(dliNotePreGetProcAddress, &dli);\n        }\n    if (pfnRet == 0) {\n        if (pidd->rvaBoundIAT && pidd->dwTimeStamp) {\n            // bound imports exist...check the timestamp from the target image\n            //\n            PIMAGE_NT_HEADERS   pinh(PinhFromImageBase(hmod));\n\n            if (pinh->Signature == IMAGE_NT_SIGNATURE &&\n                TimeStampOfImage(pinh) == idd.dwTimeStamp &&\n                FLoadedAtPreferredAddress(pinh, hmod)) {\n\n                // Everything is good to go, if we have a decent address\n                // in the bound IAT!\n                //\n                pfnRet = FARPROC(UINT_PTR(idd.pBoundIAT[iIAT].u1.Function));\n                if (pfnRet != 0) {\n                    goto SetEntryHookBypass;\n                    }\n                }\n            }\n\n        pfnRet = ::GetProcAddress(hmod, dli.dlp.szProcName);\n        }\n\n    if (pfnRet == 0) {\n        dli.dwLastError = ::GetLastError();\n        if (__pfnDliFailureHook2) {\n            // when the hook is called on GetProcAddress failure, it will\n            // return 0 on failure and a valid proc address on success\n            //\n            pfnRet = (*__pfnDliFailureHook2)(dliFailGetProc, &dli);\n            }\n        if (pfnRet == 0) {\n            PDelayLoadInfo  rgpdli[1] = { &dli };\n\n            RaiseException(VcppException(ERROR_SEVERITY_ERROR, ERROR_PROC_NOT_FOUND),\n                0, 1, PULONG_PTR(rgpdli));\n\n            // If we get to here, we blindly assume that the handler of the exception\n            // has magically fixed everything up and left the function pointer in \n            // dli.pfnCur.\n            //\n            pfnRet = dli.pfnCur;\n            }\n        }\n\nSetEntryHookBypass:\n    *ppfnIATEntry = pfnRet;\n\nHookBypass:\n    if (__pfnDliNotifyHook2) {\n        dli.dwLastError = 0;\n        dli.hmodCur = hmod;\n        dli.pfnCur = pfnRet;\n        (*__pfnDliNotifyHook2)(dliNoteEndProcessing, &dli);\n        }\n    return pfnRet;\n    }\n\n\nstatic void UnloadAllDlls()\n{\n\tPUnloadInfo pui;\n\n\t// free all DLLs (avoid BoundsChecker warning)\n\twhile((pui = __puiHead) != 0)\n\t\tif(pui->pidd->rvaUnloadIAT)\n\t\t{\n\t\t\tPCImgDelayDescr pidd = pui->pidd;\n\t\t\tHMODULE* phmod = PFromRva<HMODULE*>(pidd->rvaHmod);\n\t\t\tHMODULE hmod = *phmod;\n\n\t\t\tOverlayIAT(\n\t\t\tPFromRva<PImgThunkData>(pidd->rvaIAT),\n\t\t\tPFromRva<PCImgThunkData>(pidd->rvaUnloadIAT)\n\t\t\t);\n\t\t\t::FreeLibrary(hmod);\n\t\t\t*phmod = NULL;\n\n\t\t\tdelete (ULI*)pui; // changes __puiHead!\n\t\t}\n}\n\n//-----------------------------------------------------------------------------\n\nstatic Status wdll_Shutdown()\n{\n\tUnloadAllDlls();\n\treturn INFO::OK;\n}\n"
  },
  {
    "path": "fpsgame/gui/sysdep/os/win/wdll_delay_load.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * DLL delay loading and notification\n */\n\n#ifndef INCLUDED_WDLL_DELAY_LOAD\n#define INCLUDED_WDLL_DELAY_LOAD\n\n// must be POD because it is used before static ctors run.\nstruct WdllLoadNotify\n{\n\tconst char* dll_name;\n\tStatus (*func)();\n\tWdllLoadNotify* next;\n};\n\nextern void wdll_add_notify(WdllLoadNotify*);\n\n// request that func be called if and when dll_name is ever delay-loaded.\n// must be invoked at function scope.\n#define WDLL_ADD_NOTIFY(dll_name, func)\\\nSTMT(\\\n\tstatic WdllLoadNotify UID__ = { dll_name, func };\\\n\twdll_add_notify(&UID__);\\\n)\n\n#endif\t// #ifndef INCLUDED_WDLL_DELAY_LOAD\n"
  },
  {
    "path": "fpsgame/gui/sysdep/os/win/wdll_main.h",
    "content": "/* Copyright (c) 2015 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n// for each project that builds a shared-library, include this \"header\" in\n// one source file that is on the linker command line.\n// (avoids the ICC remark \"Main entry point was not seen\")\n\n#if OS_WIN\n#include \"lib/sysdep/os/win/win.h\"\n\nBOOL WINAPI DllMain(HINSTANCE hInstance, DWORD UNUSED(reason), LPVOID UNUSED(reserved))\n{\n\t// avoid unnecessary DLL_THREAD_ATTACH/DETACH calls\n\t// (ignore failure - happens if the DLL uses TLS)\n\t(void)DisableThreadLibraryCalls(hInstance);\n\treturn TRUE;\t// success (ignored unless reason == DLL_PROCESS_ATTACH)\n}\n\n#endif\n"
  },
  {
    "path": "fpsgame/gui/sysdep/os/win/wdll_ver.cpp",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * return DLL version information.\n */\n\n#include \"precompiled.h\"\n#include \"lib/sysdep/os/win/wdll_ver.h\"\n\n#include <stdio.h>\n#include <stdlib.h>\n\n#include \"lib/sysdep/os/win/win.h\"\n#include \"lib/sysdep/os/win/wutil.h\"\n\n#include \"lib/allocators/shared_ptr.h\"\n\n#if MSC_VERSION\n#pragma comment(lib, \"version.lib\")\t\t// DLL version\n#endif\n\n\n//-----------------------------------------------------------------------------\n\nstatic Status ReadVersionString(const OsPath& modulePathname, wchar_t* out_ver, size_t out_ver_len)\n{\n\tWinScopedPreserveLastError s;\t// GetFileVersion*, Ver*\n\n\t// determine size of and allocate memory for version information.\n\tDWORD unused;\n\tconst DWORD ver_size = GetFileVersionInfoSizeW(OsString(modulePathname).c_str(), &unused);\t// [bytes]\n\tif(!ver_size)\n\t{\n\t\t// check if the failure is due to not finding modulePathname\n\t\t// (necessary since GetFileVersionInfoSize doesn't SetLastError)\n\t\tHMODULE hModule = LoadLibraryExW(OsString(modulePathname).c_str(), 0, LOAD_LIBRARY_AS_DATAFILE);\n\t\tif(!hModule)\n\t\t\treturn ERR::FAIL;\t// NOWARN (file not found - due to FS redirection?)\n\t\tFreeLibrary(hModule);\n\t\treturn ERR::NOT_SUPPORTED;\t// NOWARN (module apparently lacks version information)\n\t}\n\n\tshared_ptr<u8> mem = Allocate(ver_size);\n\tif(!GetFileVersionInfoW(OsString(modulePathname).c_str(), 0, ver_size, mem.get()))\n\t\tWARN_RETURN(ERR::_3);\n\n\tu16* lang;\t// -> 16 bit language ID, 16 bit codepage\n\tUINT lang_len;\n\tconst BOOL ok = VerQueryValueW(mem.get(), L\"\\\\VarFileInfo\\\\Translation\", (void**)&lang, &lang_len);\n\tif(!ok || !lang || lang_len != 4)\n\t\tWARN_RETURN(ERR::_4);\n\n\twchar_t subblock[64];\n\tswprintf_s(subblock, ARRAY_SIZE(subblock), L\"\\\\StringFileInfo\\\\%04X%04X\\\\FileVersion\", lang[0], lang[1]);\n\tconst wchar_t* in_ver;\n\tUINT in_ver_len;\n\tif(!VerQueryValueW(mem.get(), subblock, (void**)&in_ver, &in_ver_len))\n\t\tWARN_RETURN(ERR::_5);\n\n\twcscpy_s(out_ver, out_ver_len, in_ver);\n\treturn INFO::OK;\n}\n\n\nvoid wdll_ver_Append(const OsPath& pathname, VersionList& list)\n{\n\tif(pathname.empty())\n\t\treturn;\t// avoid error in ReadVersionString\n\n\t// pathname may not have an extension (e.g. driver names from the\n\t// registry). note that always appending \".dll\" would be incorrect\n\t// since some have \".sys\" extension.\n\tOsPath modulePathname(pathname);\n\tif(modulePathname.Extension() == \"\")\n\t\tmodulePathname = modulePathname.ChangeExtension(L\".dll\");\n\tconst OsPath moduleName(modulePathname.Filename());\n\n\t// read file version. try this with and without FS redirection since\n\t// pathname might assume both.\n\twchar_t versionString[500];\t// enclosed in () below\n\tif(ReadVersionString(modulePathname, versionString, ARRAY_SIZE(versionString)) != INFO::OK)\n\t{\n\t\tWinScopedDisableWow64Redirection s;\n\t\t// still failed; avoid polluting list with DLLs that don't exist\n\t\t// (requiring callers to check for their existence beforehand would be\n\t\t// onerous and unreliable)\n\t\tif(ReadVersionString(modulePathname, versionString, ARRAY_SIZE(versionString)) != INFO::OK)\n\t\t\treturn;\n\t}\n\n\tif(!list.empty())\n\t\tlist += L\", \";\n\t\n\tlist += moduleName.Filename().string();\n\tlist += L\" (\";\n\tlist += versionString;\n\tlist += L\")\";\n}\n"
  },
  {
    "path": "fpsgame/gui/sysdep/os/win/wdll_ver.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * return DLL version information\n */\n\n#ifndef INCLUDED_WDLL_VER\n#define INCLUDED_WDLL_VER\n\n#include \"lib/os_path.h\"\n\ntypedef std::wstring VersionList;\n\n/**\n * Read DLL version information and append it to a string.\n *\n * @param pathname of DLL (preferably the complete path, so that we don't\n *\t\t  inadvertently load another one on the library search path.)\n *\t\t  If no extension is given, .dll will be appended.\n *\n * The text output includes the module name.\n * On failure, the version is given as \"unknown\".\n **/\nextern void wdll_ver_Append(const OsPath& pathname, VersionList& list);\n\n#endif\t// #ifndef INCLUDED_WDLL_VER\n"
  },
  {
    "path": "fpsgame/gui/sysdep/smbios.h",
    "content": "/* Copyright (c) 2011 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * provide access to System Management BIOS information\n */\n\n#ifndef INCLUDED_SMBIOS\n#define INCLUDED_SMBIOS\n\nnamespace SMBIOS {\n\n// to introduce another enumeration:\n// 1) add its name and underlying type here\n// 2) define a <name>_ENUMERATORS macro specifying its enumerators\n//    (prefer lower case to avoid conflicts with macros)\n#define ENUMERATIONS\\\n\tENUMERATION(State, u8)\\\n\tENUMERATION(ECC, u8)\\\n\tENUMERATION(BiosFlags, u32)\\\n\tENUMERATION(BiosFlags1, u8)\\\n\tENUMERATION(BiosFlags2, u8)\\\n\tENUMERATION(SystemWakeUpType, u8)\\\n\tENUMERATION(BaseboardFlags, u8)\\\n\tENUMERATION(BaseboardType, u8)\\\n\tENUMERATION(ChassisType, u8)\\\n\tENUMERATION(ChassisSecurityStatus, u8)\\\n\tENUMERATION(ProcessorType, u8)\\\n\tENUMERATION(ProcessorStatus, u8)\\\n\tENUMERATION(ProcessorUpgrade, u8)\\\n\tENUMERATION(ProcessorFlags, u16)\\\n\tENUMERATION(CacheMode, u8)\\\n\tENUMERATION(CacheLocation, u8)\\\n\tENUMERATION(CacheConfigurationFlags, u16)\\\n\tENUMERATION(CacheFlags, u16)\\\n\tENUMERATION(CacheType, u8)\\\n\tENUMERATION(CacheAssociativity, u8)\\\n\tENUMERATION(PortConnectorType, u8)\\\n\tENUMERATION(PortType, u8)\\\n\tENUMERATION(SystemSlotType, u8)\\\n\tENUMERATION(SystemSlotBusWidth, u8)\\\n\tENUMERATION(SystemSlotUsage, u8)\\\n\tENUMERATION(SystemSlotLength, u8)\\\n\tENUMERATION(SystemSlotFlags1, u8)\\\n\tENUMERATION(SystemSlotFlags2, u8)\\\n\tENUMERATION(OnBoardDeviceType, u8)\\\n\tENUMERATION(MemoryArrayLocation, u8)\\\n\tENUMERATION(MemoryArrayUse, u8)\\\n\tENUMERATION(MemoryDeviceFormFactor, u8)\\\n\tENUMERATION(MemoryDeviceType, u8)\\\n\tENUMERATION(MemoryDeviceTypeFlags, u16)\\\n\tENUMERATION(PortableBatteryChemistry, u8)\\\n\tENUMERATION(VoltageProbeLocation, u8)\\\n\tENUMERATION(CoolingDeviceType, u8)\\\n\tENUMERATION(TemperatureProbeLocation, u8)\\\n\tENUMERATION(SystemBootStatus, u8)\\\n\tENUMERATION(ManagementDeviceType, u8)\\\n\tENUMERATION(ManagementDeviceAddressType, u8)\\\n\tENUMERATION(SystemPowerSupplyCharacteristics, u16)\\\n\tENUMERATION(SystemPowerSupplyType, u8)\\\n\tENUMERATION(SystemPowerSupplyInputSwitching, u8)\n\n// to introduce another structure:\n// 1) add its name and ID here\n// 2) define a <name>_FIELDS macro specifying its fields\n// 3) (optional) add a specialization of Fixup\n#define STRUCTURES\\\n\tSTRUCTURE(Bios, 0)\\\n\tSTRUCTURE(System, 1)\\\n\tSTRUCTURE(Baseboard, 2)\\\n\tSTRUCTURE(Chassis, 3)\\\n\tSTRUCTURE(Processor, 4)\\\n\t/* MemoryController (5) and MemoryModule (6) are obsolete */\\\n\tSTRUCTURE(Cache, 7)\\\n\tSTRUCTURE(PortConnector, 8)\\\n\tSTRUCTURE(SystemSlot, 9)\\\n\tSTRUCTURE(OnBoardDevices, 10)\\\n\t/* OemStrings (11), SystemConfiguration (12), BiosLanguage (13), GroupAssociations (14), SystemEventLog (15) are optional */\\\n\tSTRUCTURE(MemoryArray, 16)\\\n\tSTRUCTURE(MemoryDevice, 17)\\\n\t/* MemoryError32 (18) is optional */\\\n\tSTRUCTURE(MemoryArrayMappedAddress, 19)\\\n\tSTRUCTURE(MemoryDeviceMappedAddress, 20)\\\n\t/* PointingDevice (21) is optional */\\\n\tSTRUCTURE(PortableBattery, 22)\\\n\t/* SystemReset (23), HardwareSecurity (24), SystemPowerControls (25) are optional */\\\n\tSTRUCTURE(VoltageProbe, 26)\\\n\tSTRUCTURE(CoolingDevice, 27)\\\n\tSTRUCTURE(TemperatureProbe, 28)\\\n\t/* ElectricalCurrentProbe (29), OutOfBandRemoteAccess (30), BootIntegrityServices (31) are optional */\\\n\tSTRUCTURE(SystemBoot, 32)\\\n\tSTRUCTURE(ManagementDevice, 34)\\\n\tSTRUCTURE(ManagementDeviceComponent, 35)\\\n\tSTRUCTURE(ManagementDeviceThreshold, 36)\\\n\tSTRUCTURE(SystemPowerSupply, 39)\\\n\tSTRUCTURE(OnboardDevices2, 41)\\\n\t/* MemoryError64 (33), MemoryChannel (37), IpmiDevice (38) are optional */\n\t/* Additional (40), ManagementControllerHostInterface (42) are optional */\n\n\n//-----------------------------------------------------------------------------\n// declarations required for the fields\n\n// indicates a field (:= member of a structure) is:\nenum FieldFlags\n{\n\t// computed via other fields (i.e. should not be copied from the SMBIOS data).\n\tF_DERIVED  = 1,\n\n\t// not intended for display / use by applications (usually because\n\t// it is superseded by another - possibly derived - field).\n\tF_INTERNAL = 2,\n\n\t// a number that should be displayed in hexadecimal form.\n\tF_HEX      = 4\n};\n\n\n// (wrapper classes allow special handling of certain fields via\n// template specialization and function overloads)\n\n// size [bytes] - displayed with auto-range\ntemplate<typename T>\nstruct Size\n{\n\tSize(): value(0) {}\n\tSize(T value): value(value) {}\n\tT value;\n\toperator T() const { return value; }\n};\n\n// SMBIOS structure handle - only displayed if meaningful\nstruct Handle\n{\n\tHandle(): value(0) {}\n\tHandle(u16 value): value(value) {}\n\tu16 value;\n};\n\n\n#define State_ENUMERATORS\\\n\tENUM(other, 1)\\\n\tENUM(unknown, 2)\\\n\tENUM(ok, 3)\\\n\tENUM(noncritical, 4)\\\n\tENUM(critical, 5)\\\n\tENUM(nonrecoverable, 6)\n\n#define ECC_ENUMERATORS\\\n\tENUM(other, 1)\\\n\tENUM(unknown, 2)\\\n\tENUM(none, 3)\\\n\tENUM(parity, 4)\\\n\tENUM(single_bit, 5)\\\n\tENUM(multiple_bit, 6)\\\n\tENUM(crc, 7)\n\n\n#pragma pack(push, 1)\n\n\n//-----------------------------------------------------------------------------\n// Bios\n\n#define BiosFlags_ENUMERATORS\\\n\tENUM(isa, 0x10)\\\n\tENUM(mca, 0x20)\\\n\tENUM(eisa, 0x40)\\\n\tENUM(pci, 0x80)\\\n\tENUM(pcmcia, 0x100)\\\n\tENUM(plug_and_play, 0x200)\\\n\tENUM(apm, 0x400)\\\n\tENUM(upgradable, 0x800)\\\n\tENUM(shadowing, 0x1000)\\\n\tENUM(vl_vesa, 0x2000)\\\n\tENUM(escd, 0x4000)\\\n\tENUM(boot_cd, 0x8000)\\\n\tENUM(selectable_boot, 0x10000)\\\n\tENUM(socketed_rom, 0x20000)\\\n\tENUM(boot_pcmcia, 0x40000)\\\n\tENUM(edd, 0x80000)\\\n\tENUM(int13a, 0x100000)\\\n\tENUM(int13b, 0x200000)\\\n\tENUM(int13c, 0x400000)\\\n\tENUM(int13d, 0x800000)\\\n\tENUM(int13e, 0x1000000)\\\n\tENUM(int13f, 0x2000000)\\\n\tENUM(int5, 0x4000000)\\\n\tENUM(int9, 0x8000000)\\\n\tENUM(int14, 0x10000000)\\\n\tENUM(int17, 0x20000000)\\\n\tENUM(int10, 0x40000000)\\\n\tENUM(pc_98, 0x80000000)\n\n#define BiosFlags1_ENUMERATORS\\\n\tENUM(acpi, 0x01)\\\n\tENUM(usb_legacy, 0x02)\\\n\tENUM(agp, 0x04)\\\n\tENUM(boot_i2o, 0x08)\\\n\tENUM(boot_ls_120, 0x10)\\\n\tENUM(boot_zip_drive, 0x20)\\\n\tENUM(boot_1394, 0x40)\\\n\tENUM(smart_battery, 0x80)\n\n#define BiosFlags2_ENUMERATORS\\\n\tENUM(bios_boot, 0x01)\\\n\tENUM(function_key_boot, 0x02)\\\n\tENUM(targeted_content_distribution, 0x04)\\\n\tENUM(uefi, 0x08)\\\n\tENUM(virtual_machine, 0x10)\n\n#define Bios_FIELDS\\\n\tFIELD(0, const char*, vendor, \"\")\\\n\tFIELD(0, const char*, version, \"\")\\\n\tFIELD(F_HEX, u16, startSegment, \"\")\\\n\tFIELD(0, const char*, releaseDate, \"\")\\\n\tFIELD(F_INTERNAL, u8, encodedSize, \"\")\\\n\tFIELD(0, BiosFlags, flags, \"\")\\\n\tFIELD(F_HEX, u32, vendorFlags, \"\")\\\n\tFIELD(0, BiosFlags1, flags1, \"\")\\\n\tFIELD(0, BiosFlags2, flags2, \"\")\\\n\tFIELD(F_DERIVED, Size<size_t>, size, \"\")\n\n\n//-----------------------------------------------------------------------------\n// System\n\n#define SystemWakeUpType_ENUMERATORS\\\n\tENUM(other, 1)\\\n\tENUM(unknown, 2)\\\n\tENUM(apm_timer, 3)\\\n\tENUM(modem_ring, 4)\\\n\tENUM(lan_remote, 5)\\\n\tENUM(power_switch, 6)\\\n\tENUM(pci_pme, 7)\\\n\tENUM(ac_power_restored, 8)\n\n#define System_FIELDS\\\n\tFIELD(0, const char*, manufacturer, \"\")\\\n\tFIELD(0, const char*, productName, \"\")\\\n\tFIELD(0, const char*, version, \"\")\\\n\tFIELD(0, const char*, serialNumber, \"\")\\\n\tFIELD(F_HEX, u64, uuid0, \"\")\\\n\tFIELD(F_HEX, u64, uuid1, \"\")\\\n\tFIELD(0, SystemWakeUpType, wakeUpType, \"\")\\\n\tFIELD(0, const char*, skuNumber, \"\")\\\n\tFIELD(0, const char*, family, \"\")\n\n\n//-----------------------------------------------------------------------------\n// Baseboard\n\n#define BaseboardFlags_ENUMERATORS\\\n\tENUM(motherboard, 0x01)\\\n\tENUM(requires_add_in, 0x02)\\\n\tENUM(removeable, 0x04)\\\n\tENUM(replaceable, 0x08)\\\n\tENUM(hot_swappable, 0x10)\n\n#define BaseboardType_ENUMERATORS\\\n\tENUM(other, 1)\\\n\tENUM(unknown, 2)\\\n\tENUM(blade, 3)\\\n\tENUM(connectivity_switch, 4)\\\n\tENUM(system_management, 5)\\\n\tENUM(processor, 6)\\\n\tENUM(io, 7)\\\n\tENUM(memory, 8)\\\n\tENUM(daughter, 9)\\\n\tENUM(motherboard, 10)\\\n\tENUM(processor_memory, 11)\\\n\tENUM(processor_io, 12)\\\n\tENUM(interconnect, 13)\n\n#define Baseboard_FIELDS\\\n\tFIELD(0, const char*, manufacturer, \"\")\\\n\tFIELD(0, const char*, product, \"\")\\\n\tFIELD(0, const char*, version, \"\")\\\n\tFIELD(0, const char*, serialNumber, \"\")\\\n\tFIELD(0, const char*, assetTag, \"\")\\\n\tFIELD(0, BaseboardFlags, flags, \"\")\\\n\tFIELD(0, const char*, location, \"\")\\\n\tFIELD(0, Handle, hChassis, \"\")\\\n\tFIELD(0, BaseboardType, type, \"\")\\\n\t/* omit subsequent fields because we can't handle the variable-length contained objects */\n\n\n//-----------------------------------------------------------------------------\n// Chassis\n\n#define ChassisType_ENUMERATORS\\\n\tENUM(other, 1)\\\n\tENUM(unknown, 2)\\\n\tENUM(desktop, 3)\\\n\tENUM(low_profile_desktop, 4)\\\n\tENUM(pizza_box, 5)\\\n\tENUM(mini_tower, 6)\\\n\tENUM(tower, 7)\\\n\tENUM(portable, 8)\\\n\tENUM(laptop, 9)\\\n\tENUM(notebook, 10)\\\n\tENUM(handheld, 11)\\\n\tENUM(docking_station, 12)\\\n\tENUM(all_in_one, 13)\\\n\tENUM(subnotebook, 14)\\\n\tENUM(space_saving, 15)\\\n\tENUM(lunchbox, 16)\\\n\tENUM(main_server, 17)\\\n\tENUM(expansion, 18)\\\n\tENUM(sub, 19)\\\n\tENUM(bus_expansion, 20)\\\n\tENUM(peripheral, 21)\\\n\tENUM(raid, 22)\\\n\tENUM(rack_mount, 23)\\\n\tENUM(sealed_case, 24)\\\n\tENUM(multi_system, 25)\\\n\tENUM(compact_pci, 26)\\\n\tENUM(advanced_tca, 27)\\\n\tENUM(blade, 28)\\\n\tENUM(blade_enclosure, 29)\n\n#define ChassisSecurityStatus_ENUMERATORS\\\n\tENUM(other, 1)\\\n\tENUM(unknown, 2)\\\n\tENUM(none, 3)\\\n\tENUM(external_interface_locked, 4)\\\n\tENUM(external_interface_enabled, 5)\n\n#define Chassis_FIELDS\\\n\tFIELD(0, const char*, manufacturer, \"\")\\\n\tFIELD(0, ChassisType, type, \"\")\\\n\tFIELD(0, const char*, version, \"\")\\\n\tFIELD(0, const char*, serialNumber, \"\")\\\n\tFIELD(0, const char*, assetTag, \"\")\\\n\tFIELD(0, State, state, \"\")\\\n\tFIELD(0, State, powerState, \"\")\\\n\tFIELD(0, State, thermalState, \"\")\\\n\tFIELD(0, ChassisSecurityStatus, securityStatus, \"\")\\\n\tFIELD(0, u32, oemDefined, \"\")\\\n\tFIELD(0, u8, height, \"U\")\\\n\tFIELD(0, u8, numPowerCords, \"\")\\\n\t/* omit subsequent fields because we can't handle the variable-length contained objects */\n\n\n//-----------------------------------------------------------------------------\n// Processor\n\n#define ProcessorType_ENUMERATORS\\\n\tENUM(other, 1)\\\n\tENUM(unknown, 2)\\\n\tENUM(CPU, 3)\\\n\tENUM(FPU, 4)\\\n\tENUM(DSP, 5)\\\n\tENUM(GPU, 6)\n\n#define ProcessorStatus_ENUMERATORS\\\n\tENUM(unknown, 0)\\\n\tENUM(other, 7)\\\n\tENUM(enabled, 1)\\\n\tENUM(user_disabled, 2)\\\n\tENUM(post_disabled, 3)\\\n\tENUM(idle, 4)\n\n#define ProcessorUpgrade_ENUMERATORS\\\n\tENUM(other, 1)\\\n\tENUM(unknown, 2)\\\n\tENUM(daughter, 3)\\\n\tENUM(zif, 4)\\\n\tENUM(piggyback, 5)\\\n\tENUM(none, 6)\\\n\tENUM(lif, 7)\\\n\tENUM(slot_1, 8)\\\n\tENUM(slot_2, 9)\\\n\tENUM(socket_370, 10)\\\n\tENUM(slot_a, 11)\\\n\tENUM(slot_m, 12)\\\n\tENUM(socket_423, 13)\\\n\tENUM(socket_a, 14)\\\n\tENUM(socket_478, 15)\\\n\tENUM(socket_754, 16)\\\n\tENUM(socket_940, 17)\\\n\tENUM(socket_939, 18)\\\n\tENUM(socket_604, 19)\\\n\tENUM(socket_771, 20)\\\n\tENUM(socket_775, 21)\\\n\tENUM(socket_s1, 22)\\\n\tENUM(socket_am2, 23)\\\n\tENUM(socket_1207, 24)\\\n\tENUM(socket_1366, 25)\\\n\tENUM(socket_g34, 26)\\\n\tENUM(socket_am3, 27)\\\n\tENUM(socket_c32, 28)\\\n\tENUM(socket_1156, 29)\\\n\tENUM(socket_1567, 30)\\\n\tENUM(socket_988a, 31)\\\n\tENUM(socket_1288, 32)\\\n\tENUM(socket_988b, 33)\\\n\tENUM(socket_1023, 34)\\\n\tENUM(socket_1224, 35)\\\n\tENUM(socket_1155, 36)\\\n\tENUM(socket_1356, 37)\\\n\tENUM(socket_2011, 38)\\\n\tENUM(socket_fs1, 39)\\\n\tENUM(socket_fs2, 40)\\\n\tENUM(socket_fm1, 41)\\\n\tENUM(socket_fm2, 42)\n\n#define ProcessorFlags_ENUMERATORS\\\n\tENUM(unknown, 0x2)\\\n\tENUM(x64, 0x4)\\\n\tENUM(multi_core, 0x8)/* indicates cores are present, but they might be disabled*/\\\n\tENUM(ht, 0x10)\\\n\tENUM(execute_protection, 0x20)\\\n\tENUM(enhanced_virtualization, 0x40)\\\n\tENUM(power_control, 0x80)\n\n#define Processor_FIELDS\\\n\tFIELD(0, const char*, socket, \"\")\\\n\tFIELD(0, ProcessorType, type, \"\")\\\n\tFIELD(0, u8, family, \"\") /* we don't bother providing enumerators for > 200 families */\\\n\tFIELD(0, const char*, manufacturer, \"\")\\\n\tFIELD(F_HEX, u64, id, \"\")\\\n\tFIELD(0, const char*, version, \"\")\\\n\tFIELD(0, u8, voltage, \" dV\")\\\n\tFIELD(0, u16, externalClockFrequency, \" MHz\")\\\n\tFIELD(0, u16, maxFrequency, \" MHz\")\\\n\tFIELD(0, u16, bootFrequency, \" MHz\")\\\n\tFIELD(0, ProcessorStatus, status, \"\")\\\n\tFIELD(0, ProcessorUpgrade, upgrade, \"\")\\\n\tFIELD(0, Handle, hL1, \"\")\\\n\tFIELD(0, Handle, hL2, \"\")\\\n\tFIELD(0, Handle, hL3, \"\")\\\n\tFIELD(0, const char*, serialNumber, \"\")\\\n\tFIELD(0, const char*, assetTag, \"\")\\\n\tFIELD(0, const char*, partNumber, \"\")\\\n\tFIELD(0, u8, coresPerPackage, \"\")\\\n\tFIELD(0, u8, enabledCores, \"\")\\\n\tFIELD(0, u8, logicalPerPackage, \"\")\\\n\tFIELD(0, ProcessorFlags, flags, \"\")\\\n\tFIELD(0, u16, family2, \"\")\\\n\tFIELD(F_DERIVED, bool, populated, \"\")\n\n\n//-----------------------------------------------------------------------------\n// Cache\n\n#define CacheMode_ENUMERATORS\\\n\tENUM(write_through, 0)\\\n\tENUM(write_back, 1)\\\n\tENUM(varies, 2)\\\n\tENUM(unknown, 3)\n\n#define CacheLocation_ENUMERATORS\\\n\tENUM(internal, 0)\\\n\tENUM(external, 1)\\\n\tENUM(reserved, 2)\\\n\tENUM(unknown, 3)\n\n#define CacheConfigurationFlags_ENUMERATORS\\\n\tENUM(socketed, 0x08)\\\n\tENUM(enabled, 0x80)\n\n#define CacheFlags_ENUMERATORS\\\n\tENUM(other, 0x01)\\\n\tENUM(unknown, 0x02)\\\n\tENUM(non_burst, 0x04)\\\n\tENUM(burst, 0x08)\\\n\tENUM(pipeline_burst, 0x10)\\\n\tENUM(synchronous, 0x20)\\\n\tENUM(asynchronous, 0x40)\n\n#define CacheType_ENUMERATORS\\\n\tENUM(other, 1)\\\n\tENUM(unknown, 2)\\\n\tENUM(instruction, 3)\\\n\tENUM(data, 4)\\\n\tENUM(unified, 5)\n\n#define CacheAssociativity_ENUMERATORS\\\n\tENUM(other, 1)\\\n\tENUM(unknown, 2)\\\n\tENUM(direct_mapped, 3)\\\n\tENUM(A2, 4)\\\n\tENUM(A4, 5)\\\n\tENUM(full, 6)\\\n\tENUM(A8, 7)\\\n\tENUM(A16, 8)\\\n\tENUM(A12, 9)\\\n\tENUM(A24, 10)\\\n\tENUM(A32, 11)\\\n\tENUM(A48, 12)\\\n\tENUM(A64, 13)\\\n\tENUM(A20, 14)\n\n#define Cache_FIELDS\\\n\tFIELD(0, const char*, designation, \"\")\\\n\tFIELD(0, CacheConfigurationFlags, configuration, \"\")\\\n\tFIELD(F_INTERNAL, u16, maxSize16, \"\")\\\n\tFIELD(F_INTERNAL, u16, installedSize16, \"\")\\\n\tFIELD(0, CacheFlags, supportedFlags, \"\")\\\n\tFIELD(0, CacheFlags, currentFlags, \"\")\\\n\tFIELD(0, u8, speed, \" ns\")\\\n\tFIELD(0, ECC, ecc, \"\")\\\n\tFIELD(0, CacheType, type, \"\")\\\n\tFIELD(0, CacheAssociativity, associativity, \"\")\\\n\tFIELD(F_DERIVED, size_t, level, \"\") /* 1..8 */\\\n\tFIELD(F_DERIVED, CacheLocation, location, \"\")\\\n\tFIELD(F_DERIVED, CacheMode, mode, \"\")\\\n\tFIELD(F_DERIVED, Size<u64>, maxSize, \"\")\\\n\tFIELD(F_DERIVED, Size<u64>, installedSize, \"\")\n\n\n//-----------------------------------------------------------------------------\n// PortConnector\n\n#define PortConnectorType_ENUMERATORS\\\n\tENUM(other, 255)\\\n\tENUM(none, 0)\\\n\tENUM(centronics, 1)\\\n\tENUM(mini_centronics, 2)\\\n\tENUM(proprietary, 3)\\\n\tENUM(db25_male, 4)\\\n\tENUM(db25_pin_female, 5)\\\n\tENUM(db15_pin_male, 6)\\\n\tENUM(db15_pin_female, 7)\\\n\tENUM(db9_pin_male, 8)\\\n\tENUM(db9_pin_female, 9)\\\n\tENUM(rj11, 10)\\\n\tENUM(rj45, 11)\\\n\tENUM(mini_scsi, 12)\\\n\tENUM(mini_din, 13)\\\n\tENUM(micro_din, 14)\\\n\tENUM(ps2, 15)\\\n\tENUM(infrared, 16)\\\n\tENUM(hp_hil, 17)\\\n\tENUM(access_bus_usb, 18)\\\n\tENUM(pc_ssa_scsi, 19)\\\n\tENUM(din8_male, 20)\\\n\tENUM(din8_female, 21)\\\n\tENUM(on_board_ide, 22)\\\n\tENUM(on_board_floppy, 23)\\\n\tENUM(dual_inline_9, 24)\\\n\tENUM(dual_inline_25, 25)\\\n\tENUM(dual_inline_50, 26)\\\n\tENUM(dual_inline_68, 27)\\\n\tENUM(on_board_sound_input_from_cd, 28)\\\n\tENUM(mini_centronics_14, 29)\\\n\tENUM(mini_centronics_26, 30)\\\n\tENUM(headphones, 31)\\\n\tENUM(bnc, 32)\\\n\tENUM(pc_firewire, 33)\\\n\tENUM(sas_sata, 34)\\\n\tENUM(pc_98, 160)\\\n\tENUM(pc_98_hireso, 161)\\\n\tENUM(pc_h98, 162)\\\n\tENUM(pc_98_note, 163)\\\n\tENUM(pc_98_full, 164)\n\n#define PortType_ENUMERATORS\\\n\tENUM(other, 255)\\\n\tENUM(none, 0)\\\n\tENUM(parallel_xt_at, 1)\\\n\tENUM(parallel_ps2, 2)\\\n\tENUM(parallel_ecp, 3)\\\n\tENUM(parallel_epp, 4)\\\n\tENUM(parallel_ecepp, 5)\\\n\tENUM(serial_xt_at, 6)\\\n\tENUM(serial_16450, 7)\\\n\tENUM(serial_16550, 8)\\\n\tENUM(serial_16550a, 9)\\\n\tENUM(scsi, 10)\\\n\tENUM(midi, 11)\\\n\tENUM(joystick, 12)\\\n\tENUM(keyboard, 13)\\\n\tENUM(mouse, 14)\\\n\tENUM(ssa_scsi, 15)\\\n\tENUM(usb, 16)\\\n\tENUM(firewire, 17)\\\n\tENUM(pcmcia_i, 18)\\\n\tENUM(pcmcia_ii, 19)\\\n\tENUM(pcmcia_iii, 20)\\\n\tENUM(cardbus, 21)\\\n\tENUM(access_bus, 22)\\\n\tENUM(scsi_ii, 23)\\\n\tENUM(scsi_wide, 24)\\\n\tENUM(pc_98, 25)\\\n\tENUM(pc_98_hireso, 26)\\\n\tENUM(pc_h98, 27)\\\n\tENUM(video, 28)\\\n\tENUM(audio, 29)\\\n\tENUM(modem, 30)\\\n\tENUM(network, 31)\\\n\tENUM(sata, 32)\\\n\tENUM(sas, 33)\\\n\tENUM(_8251_compatible, 160)\\\n\tENUM(_8251_fifo_compatible, 161)\n\n#define PortConnector_FIELDS\\\n\tFIELD(0, const char*, internalDesignator, \"\")\\\n\tFIELD(0, PortConnectorType, internalConnectorType, \"\")\\\n\tFIELD(0, const char*, externalDesignator, \"\")\\\n\tFIELD(0, PortConnectorType, externalConnectorType, \"\")\\\n\tFIELD(0, PortType, portType, \"\")\n\n\n//-----------------------------------------------------------------------------\n// SystemSlot\n\n#define SystemSlotType_ENUMERATORS\\\n\tENUM(other, 1)\\\n\tENUM(unknown, 2)\\\n\tENUM(isa, 3)\\\n\tENUM(mca, 4)\\\n\tENUM(eisa, 5)\\\n\tENUM(pci, 6)\\\n\tENUM(pcmcia, 7)\\\n\tENUM(vesa, 8)\\\n\tENUM(proprietary, 9)\\\n\tENUM(processor, 10)\\\n\tENUM(memory_card, 11)\\\n\tENUM(io_riser, 12)\\\n\tENUM(nubus, 13)\\\n\tENUM(pci_66, 14)\\\n\tENUM(agp, 15)\\\n\tENUM(agp_2x, 16)\\\n\tENUM(agp_4x, 17)\\\n\tENUM(pcix, 18)\\\n\tENUM(agp_8x, 19)\\\n\tENUM(pc_98_c20, 160)\\\n\tENUM(pc_98_c24, 161)\\\n\tENUM(pc_98_e, 162)\\\n\tENUM(pc_98_local_bus, 163)\\\n\tENUM(pc_98_card, 164)\\\n\tENUM(pcie, 165)\\\n\tENUM(pcie_x1, 166)\\\n\tENUM(pcie_x2, 167)\\\n\tENUM(pcie_x4, 168)\\\n\tENUM(pcie_x8, 169)\\\n\tENUM(pcie_x16, 170)\\\n\tENUM(pcie2, 171)\\\n\tENUM(pcie2_x1, 172)\\\n\tENUM(pcie2_x2, 173)\\\n\tENUM(pcie2_x4, 174)\\\n\tENUM(pcie2_x8, 175)\\\n\tENUM(pcie2_x16, 176)\\\n\tENUM(pcie3, 177)\\\n\tENUM(pcie3_x1, 178)\\\n\tENUM(pcie3_x2, 179)\\\n\tENUM(pcie3_x4, 180)\\\n\tENUM(pcie3_x8, 181)\\\n\tENUM(pcie3_x16, 182)\n\n#define SystemSlotBusWidth_ENUMERATORS\\\n\tENUM(other, 1)\\\n\tENUM(unknown, 2)\\\n\tENUM(_8, 3)\\\n\tENUM(_16, 4)\\\n\tENUM(_32, 5)\\\n\tENUM(_64, 6)\\\n\tENUM(_128, 7)\\\n\tENUM(x1, 8)\\\n\tENUM(x2, 9)\\\n\tENUM(x4, 10)\\\n\tENUM(x8, 11)\\\n\tENUM(x12, 12)\\\n\tENUM(x16, 13)\\\n\tENUM(x32, 14)\n\n#define SystemSlotUsage_ENUMERATORS\\\n\tENUM(other, 1)\\\n\tENUM(unknown, 2)\\\n\tENUM(available, 3)\\\n\tENUM(in_use, 4)\n\n#define SystemSlotLength_ENUMERATORS\\\n\tENUM(other, 1)\\\n\tENUM(unknown, 2)\\\n\tENUM(_short, 3)\\\n\tENUM(_long, 4)\n\n#define SystemSlotFlags1_ENUMERATORS\\\n\tENUM(unknown, 0x1)\\\n\tENUM(v5, 0x2)\\\n\tENUM(v3_3, 0x4)\\\n\tENUM(shared, 0x8)\\\n\tENUM(pc_card_16, 0x10)\\\n\tENUM(pc_cardbus, 0x20)\\\n\tENUM(pc_zoom_video, 0x40)\\\n\tENUM(pc_modem_ring_resume, 0x80)\n\n#define SystemSlotFlags2_ENUMERATORS\\\n\tENUM(pme, 0x1)\\\n\tENUM(hot_plug, 0x2)\\\n\tENUM(smbus, 0x4)\\\n\n#define SystemSlot_FIELDS\\\n\tFIELD(0, const char*, designation, \"\")\\\n\tFIELD(0, SystemSlotType, type, \"\")\\\n\tFIELD(0, SystemSlotBusWidth, busWidth, \"\")\\\n\tFIELD(0, SystemSlotUsage, usage, \"\")\\\n\tFIELD(0, SystemSlotLength, length, \"\")\\\n\tFIELD(0, u16, id, \"\")\\\n\tFIELD(0, SystemSlotFlags1, flags1, \"\")\\\n\tFIELD(0, SystemSlotFlags2, flags2, \"\")\\\n\tFIELD(0, u16, segmentGroupNumber, \"\")\\\n\tFIELD(0, u8, busNumber, \"\")\\\n\tFIELD(F_INTERNAL, u8, functionAndDeviceNumber, \"\")\\\n\tFIELD(F_DERIVED, u8, deviceNumber, \"\")\\\n\tFIELD(F_DERIVED, u8, functionNumber, \"\")\n\n\n//-----------------------------------------------------------------------------\n// OnBoardDevices\n\n#define OnBoardDeviceType_ENUMERATORS\\\n\tENUM(other, 1)\\\n\tENUM(unknown, 2)\\\n\tENUM(video, 3)\\\n\tENUM(scsi_controller, 4)\\\n\tENUM(ethernet, 5)\\\n\tENUM(token_ring, 6)\\\n\tENUM(sound, 7)\\\n\tENUM(pata_controller, 8)\\\n\tENUM(sata_controller, 9)\\\n\tENUM(sas_controller, 10)\n\n#define OnBoardDevices_FIELDS\\\n\tFIELD(0, OnBoardDeviceType, type, \"\")\\\n\tFIELD(0, const char*, description, \"\")\\\n\tFIELD(F_DERIVED, bool, enabled, \"\")\\\n\t/* NB: this structure could contain any number of type/description pairs, but Dell BIOS only provides 1 */\n\n\n//-----------------------------------------------------------------------------\n// MemoryArray\n\n#define MemoryArrayLocation_ENUMERATORS\\\n\tENUM(other, 1)\\\n\tENUM(unknown, 2)\\\n\tENUM(motherboard, 3)\\\n\tENUM(isa_addon, 4)\\\n\tENUM(eisa_addon, 5)\\\n\tENUM(pci_addon, 6)\\\n\tENUM(mca_addon, 7)\\\n\tENUM(pcmcia_addon, 8)\\\n\tENUM(proprietary_addon, 9)\\\n\tENUM(nubus, 10)\\\n\tENUM(pc_98_c20, 160)\\\n\tENUM(pc_98_c24, 161)\\\n\tENUM(pc_98_e, 162)\\\n\tENUM(pc_98_local_bus, 163)\n\n#define MemoryArrayUse_ENUMERATORS\\\n\tENUM(other, 1)\\\n\tENUM(unknown, 2)\\\n\tENUM(system, 3)\\\n\tENUM(video, 4)\\\n\tENUM(flash, 5)\\\n\tENUM(nvram, 6)\\\n\tENUM(cache, 7)\n\n#define MemoryArray_FIELDS\\\n\tFIELD(0, MemoryArrayLocation, location, \"\")\\\n\tFIELD(0, MemoryArrayUse, use, \"\")\\\n\tFIELD(0, ECC, ecc, \"\")\\\n\tFIELD(F_INTERNAL, u32, maxCapacity32, \"\")\\\n\tFIELD(0, Handle, hError, \"\")\\\n\tFIELD(0, u16, numDevices, \"\")\\\n\tFIELD(0, Size<u64>, maxCapacity, \"\")\n\n\n//-----------------------------------------------------------------------------\n// MemoryDevice\n\n#define MemoryDeviceFormFactor_ENUMERATORS\\\n\tENUM(other, 1)\\\n\tENUM(unknown, 2)\\\n\tENUM(SIMM, 3)\\\n\tENUM(SIP, 4)\\\n\tENUM(chip, 5)\\\n\tENUM(DIP, 6)\\\n\tENUM(ZIP, 7)\\\n\tENUM(proprietary_card, 8)\\\n\tENUM(DIMM, 9)\\\n\tENUM(TSOP, 10)\\\n\tENUM(row_of_chips, 11)\\\n\tENUM(RIMM, 12)\\\n\tENUM(SODIMM, 13)\\\n\tENUM(SRIMM, 14)\\\n\tENUM(FBDIMM, 15)\n\n#define MemoryDeviceType_ENUMERATORS\\\n\tENUM(other, 1)\\\n\tENUM(unknown, 2)\\\n\tENUM(DRAM, 3)\\\n\tENUM(EDRAM, 4)\\\n\tENUM(VRAM, 5)\\\n\tENUM(SRAM, 6)\\\n\tENUM(RAM, 7)\\\n\tENUM(ROM, 8)\\\n\tENUM(FLASH, 9)\\\n\tENUM(EEPROM, 10)\\\n\tENUM(FEPROM, 11)\\\n\tENUM(EPROM, 12)\\\n\tENUM(CRRAM, 13)\\\n\tENUM(_3DRAM, 14)\\\n\tENUM(SDRAM, 15)\\\n\tENUM(SGRAM, 16)\\\n\tENUM(RDRAM, 17)\\\n\tENUM(DDR, 18)\\\n\tENUM(DDR2, 19)\\\n\tENUM(DDR2_FBDIMM, 20)\\\n\tENUM(DDR3, 24)\\\n\tENUM(FBD2, 25)\n\n#define MemoryDeviceTypeFlags_ENUMERATORS\\\n\tENUM(other, 0x0002)\\\n\tENUM(unknown, 0x0004)\\\n\tENUM(fast_paged, 0x0008)\\\n\tENUM(static_column, 0x0010)\\\n\tENUM(pseudo_static, 0x0020)\\\n\tENUM(rambus, 0x0040)\\\n\tENUM(synchronous, 0x0080)\\\n\tENUM(cmos, 0x0100)\\\n\tENUM(edo, 0x0200)\\\n\tENUM(window_dram, 0x0400)\\\n\tENUM(cache_dram, 0x0800)\\\n\tENUM(non_volatile, 0x1000)\\\n\tENUM(buffered, 0x2000)\\\n\tENUM(unbuffered, 0x4000)\n\n#define MemoryDevice_FIELDS\\\n\tFIELD(0, Handle, hMemoryArray, \"\")\\\n\tFIELD(0, Handle, hError, \"\")\\\n\tFIELD(0, u16, totalWidth, \" bits\")\\\n\tFIELD(0, u16, dataWidth, \" bits\")\\\n\tFIELD(F_INTERNAL, u16, size16, \"\")\\\n\tFIELD(0, MemoryDeviceFormFactor, formFactor, \"\")\\\n\tFIELD(0, u8, deviceSet, \"\")\\\n\tFIELD(0, const char*, locator, \"\")\\\n\tFIELD(0, const char*, bank, \"\")\\\n\tFIELD(0, MemoryDeviceType, type, \"\")\\\n\tFIELD(0, MemoryDeviceTypeFlags, typeFlags, \"\")\\\n\tFIELD(0, u16, speed, \" MHz\")\\\n\tFIELD(0, const char*, manufacturer, \"\")\\\n\tFIELD(0, const char*, serialNumber, \"\")\\\n\tFIELD(0, const char*, assetTag, \"\")\\\n\tFIELD(0, const char*, partNumber, \"\")\\\n\tFIELD(F_INTERNAL, u8, attributes, \"\")\\\n\tFIELD(F_INTERNAL, u32, size32, \"\")\\\n\tFIELD(0, u16, configuredSpeed, \" MHz\")\\\n\tFIELD(F_DERIVED, Size<u64>, size, \"\")\\\n\tFIELD(F_DERIVED, u8, rank, \"\")\\\n\n\n//-----------------------------------------------------------------------------\n// MemoryArrayMappedAddress\n\n#define MemoryArrayMappedAddress_FIELDS\\\n\tFIELD(F_INTERNAL, u32, startAddress32, \"\")\\\n\tFIELD(F_INTERNAL, u32, endAddress32, \"\")\\\n\tFIELD(0, Handle, hMemoryArray, \"\")\\\n\tFIELD(0, u8, partitionWidth, \"\")\\\n\tFIELD(F_HEX, u64, startAddress, \"\")\\\n\tFIELD(F_HEX, u64, endAddress, \"\")\n\n\n//-----------------------------------------------------------------------------\n// MemoryDeviceMappedAddress\n\n#define MemoryDeviceMappedAddress_FIELDS\\\n\tFIELD(F_INTERNAL, u32, startAddress32, \"\")\\\n\tFIELD(F_INTERNAL, u32, endAddress32, \"\")\\\n\tFIELD(0, Handle, hMemoryDevice, \"\")\\\n\tFIELD(0, Handle, hMemoryArrayMappedAddress, \"\")\\\n\tFIELD(0, u8, partitionRowPosition, \"\")\\\n\tFIELD(0, u8, interleavePosition, \"\")\\\n\tFIELD(0, u8, interleavedDataDepth, \"\")\\\n\tFIELD(F_HEX, u64, startAddress, \"\")\\\n\tFIELD(F_HEX, u64, endAddress, \"\")\n\n\n//----------------------------------------------------------------------------\n// PortableBattery\n\n#define PortableBatteryChemistry_ENUMERATORS\\\n\tENUM(other, 1)\\\n\tENUM(unknown, 2)\\\n\tENUM(lead_acid, 3)\\\n\tENUM(nickel_cadmium, 4)\\\n\tENUM(nickel_metal_hydride, 5)\\\n\tENUM(lithium_ion, 6)\\\n\tENUM(zinc_air, 7)\\\n\tENUM(lithium_polymer, 8)\n\n#define PortableBattery_FIELDS\\\n\tFIELD(0, const char*, location, \"\")\\\n\tFIELD(0, const char*, manufacturer, \"\")\\\n\tFIELD(0, const char*, date, \"\")\\\n\tFIELD(0, const char*, serialNumber, \"\")\\\n\tFIELD(0, const char*, deviceName, \"\")\\\n\tFIELD(0, PortableBatteryChemistry, chemistry, \"\")\\\n\tFIELD(0, u16, capacity, \" mWh\")\\\n\tFIELD(0, u16, voltage, \" mV\")\\\n\tFIELD(0, const char*, sbdsVersion, \"\")\\\n\tFIELD(0, u8, maxError, \"%\")\\\n\tFIELD(0, u16, sbdsSerialNumber, \"\")\\\n\tFIELD(0, u16, sbdsDate, \"\")\\\n\tFIELD(0, const char*, sbdsChemistry, \"\")\\\n\tFIELD(0, u8, capacityMultiplier, \"\")\\\n\tFIELD(0, u32, oemSpecific, \"\")\n\n\n//----------------------------------------------------------------------------\n// VoltageProbe\n\n#define VoltageProbeLocation_ENUMERATORS\\\n\tENUM(other, 1)\\\n\tENUM(unknown, 2)\\\n\tENUM(processor, 3)\\\n\tENUM(disk, 4)\\\n\tENUM(peripheral_bay, 5)\\\n\tENUM(system_management_module, 6)\\\n\tENUM(motherboard, 7)\\\n\tENUM(memory_module, 8)\\\n\tENUM(processor_module, 9)\\\n\tENUM(power_unit, 10)\\\n\tENUM(add_in_card, 11)\n\n#define VoltageProbe_FIELDS\\\n\tFIELD(0, const char*, description, \"\")\\\n\tFIELD(F_INTERNAL, u8, locationAndStatus, \"\")\\\n\tFIELD(0, i16, maxValue, \" mV\")\\\n\tFIELD(0, i16, minValue, \" mV\")\\\n\tFIELD(0, i16, resolution, \" x 0.1 mV\")\\\n\tFIELD(0, i16, tolerance, \" mV\")\\\n\tFIELD(0, i16, accuracy, \" x 0.01%\")\\\n\tFIELD(0, u32, oemDefined, \"\")\\\n\tFIELD(0, i16, nominalValue, \" mv\")\\\n\tFIELD(F_DERIVED, VoltageProbeLocation, location, \"\")\\\n\tFIELD(F_DERIVED, State, status, \"\")\n\n\n//----------------------------------------------------------------------------\n// CoolingDevice\n\n#define CoolingDeviceType_ENUMERATORS\\\n\tENUM(other, 1)\\\n\tENUM(unknown, 2)\\\n\tENUM(fan, 3)\\\n\tENUM(centrifugal_blower, 4)\\\n\tENUM(chip_fan, 5)\\\n\tENUM(cabinet_fan, 6)\\\n\tENUM(power_supply_fan, 7)\\\n\tENUM(heat_pipe, 8)\\\n\tENUM(integrated_refrigeration, 9)\\\n\tENUM(active_cooling, 16)\\\n\tENUM(passive_cooling, 17)\n\n#define CoolingDevice_FIELDS\\\n\tFIELD(0, Handle, hTemperatureProbe, \"\")\\\n\tFIELD(F_INTERNAL, u8, typeAndStatus, \"\")\\\n\tFIELD(0, u8, group, \"\")\\\n\tFIELD(0, u32, oemDefined, \"\")\\\n\tFIELD(0, u16, nominalSpeed, \" rpm\")\\\n\tFIELD(0, const char*, description, \"\")\\\n\tFIELD(F_DERIVED, CoolingDeviceType, type, \"\")\\\n\tFIELD(F_DERIVED, State, status, \"\")\n\n\n//----------------------------------------------------------------------------\n// TemperatureProbe\n\n#define TemperatureProbeLocation_ENUMERATORS\\\n\tENUM(other, 1)\\\n\tENUM(unknown, 2)\\\n\tENUM(processor, 3)\\\n\tENUM(disk, 4)\\\n\tENUM(peripheral_bay, 5)\\\n\tENUM(system_management_module, 6)\\\n\tENUM(motherboard, 7)\\\n\tENUM(memory_module, 8)\\\n\tENUM(processor_module, 9)\\\n\tENUM(power_unit, 10)\\\n\tENUM(add_in_card, 11)\\\n\tENUM(front_panel_board, 12)\\\n\tENUM(back_panel_board, 13)\\\n\tENUM(power_system_board, 14)\\\n\tENUM(drive_backplane, 15)\n\n#define TemperatureProbe_FIELDS\\\n\tFIELD(0, const char*, description, \"\")\\\n\tFIELD(F_INTERNAL, u8, locationAndStatus, \"\")\\\n\tFIELD(0, i16, maxValue, \" dDegC\")\\\n\tFIELD(0, i16, minValue, \" dDegC\")\\\n\tFIELD(0, i16, resolution, \" mDegC\")\\\n\tFIELD(0, i16, tolerance, \" dDegC\")\\\n\tFIELD(0, i16, accuracy, \" x 0.01%\")\\\n\tFIELD(0, u32, oemDefined, \"\")\\\n\tFIELD(0, i16, nominalValue, \" dDegC\")\\\n\tFIELD(F_DERIVED, TemperatureProbeLocation, location, \"\")\\\n\tFIELD(F_DERIVED, State, status, \"\")\n\n\n//----------------------------------------------------------------------------\n// SystemBoot\n\n#define SystemBootStatus_ENUMERATORS\\\n\tENUM(no_error, 0)\\\n\tENUM(no_bootable_media, 1)\\\n\tENUM(os_load_failed, 2)\\\n\tENUM(hardware_failure_firmware, 3)\\\n\tENUM(hardware_failure_os, 4)\\\n\tENUM(user_requested_boot, 5)\\\n\tENUM(security_violation, 6)\\\n\tENUM(previously_requested_image, 7)\\\n\tENUM(watchdog_expired, 8)\n\n#define SystemBoot_FIELDS\\\n\tFIELD(F_INTERNAL, u32, reserved32, \"\")\\\n\tFIELD(F_INTERNAL, u16, reserved16, \"\")\\\n\tFIELD(0, SystemBootStatus, status, \"\")\\\n\n\n//----------------------------------------------------------------------------\n// ManagementDevice\n\n#define ManagementDeviceType_ENUMERATORS\\\n\tENUM(other, 1)\\\n\tENUM(unknown, 2)\\\n\tENUM(LM75, 3)\\\n\tENUM(LM78, 4)\\\n\tENUM(LM79, 5)\\\n\tENUM(LM80, 6)\\\n\tENUM(LM81, 7)\\\n\tENUM(ADM9240, 8)\\\n\tENUM(DS1780, 9)\\\n\tENUM(M1617, 0xA)\\\n\tENUM(GL518SM, 0xB)\\\n\tENUM(W83781D, 0xC)\\\n\tENUM(HT82H791, 0xD)\\\n\n#define ManagementDeviceAddressType_ENUMERATORS\\\n\tENUM(other, 1)\\\n\tENUM(unknown, 2)\\\n\tENUM(port, 3)\\\n\tENUM(memory, 4)\\\n\tENUM(smbus, 5)\n\n#define ManagementDevice_FIELDS\\\n\tFIELD(0, const char*, description, \"\")\\\n\tFIELD(0, ManagementDeviceType, type, \"\")\\\n\tFIELD(0, u32, address, \"\")\\\n\tFIELD(0, ManagementDeviceAddressType, addressType, \"\")\n\n\n//----------------------------------------------------------------------------\n// ManagementDeviceComponent\n\n#define ManagementDeviceComponent_FIELDS\\\n\tFIELD(0, const char*, description, \"\")\\\n\tFIELD(0, Handle, hDevice, \"\")\\\n\tFIELD(0, Handle, hComponent, \"\")\\\n\tFIELD(0, Handle, hThreshold, \"\")\n\n\n//----------------------------------------------------------------------------\n// ManagementDeviceThreshold\n\n#define ManagementDeviceThreshold_FIELDS\\\n\tFIELD(0, i16, nonCriticalLo, \"\")\\\n\tFIELD(0, i16, nonCriticalHi, \"\")\\\n\tFIELD(0, i16, criticalLo, \"\")\\\n\tFIELD(0, i16, criticalHi, \"\")\\\n\tFIELD(0, i16, nonrecoverableLo, \"\")\\\n\tFIELD(0, i16, nonrecoverableHi, \"\")\n\n\n//----------------------------------------------------------------------------\n// SystemPowerSupply\n\n#define SystemPowerSupplyCharacteristics_ENUMERATORS\\\n\tENUM(hot_replaceable, 1)\\\n\tENUM(present, 2)\\\n\tENUM(unplugged, 4)\n\n#define SystemPowerSupplyType_ENUMERATORS\\\n\tENUM(other, 1)\\\n\tENUM(unknown, 2)\\\n\tENUM(linear, 3)\\\n\tENUM(switching, 4)\\\n\tENUM(battery, 5)\\\n\tENUM(ups, 6)\\\n\tENUM(converter, 7)\\\n\tENUM(regulator, 8)\n\n#define SystemPowerSupplyInputSwitching_ENUMERATORS\\\n\tENUM(other, 1)\\\n\tENUM(unknown, 2)\\\n\tENUM(manual, 3)\\\n\tENUM(auto_switch, 4)\\\n\tENUM(wide_range, 5)\\\n\tENUM(none, 6)\n\n#define SystemPowerSupply_FIELDS\\\n\tFIELD(0, u8, group, \"\")\\\n\tFIELD(0, const char*, location, \"\")\\\n\tFIELD(0, const char*, deviceName, \"\")\\\n\tFIELD(0, const char*, manufacturer, \"\")\\\n\tFIELD(0, const char*, serialNumber, \"\")\\\n\tFIELD(0, const char*, assetTag, \"\")\\\n\tFIELD(0, const char*, partNumber, \"\")\\\n\tFIELD(0, const char*, revisionLevel, \"\")\\\n\tFIELD(0, i16, maxPower, \" mW\")\\\n\tFIELD(0, SystemPowerSupplyCharacteristics, characteristics, \"\")\\\n\tFIELD(0, Handle, hVoltageProbe, \"\")\\\n\tFIELD(0, Handle, hCoolingDevice, \"\")\\\n\tFIELD(0, Handle, hCurrentProbe, \"\")\\\n\tFIELD(F_DERIVED, SystemPowerSupplyType, type, \"\")\\\n\tFIELD(F_DERIVED, State, status, \"\")\\\n\tFIELD(F_DERIVED, SystemPowerSupplyInputSwitching, inputSwitching, \"\")\\\n\n\n//----------------------------------------------------------------------------\n// OnboardDevices2\n\n#define OnboardDevices2_FIELDS\\\n\tFIELD(0, const char*, referenceDesignation, \"\")\\\n\tFIELD(0, OnBoardDeviceType, type, \"\")\\\n\tFIELD(0, u8, instance, \"\")\\\n\tFIELD(0, u16, groupNumber, \"\")\\\n\tFIELD(0, u8, busNumber, \"\")\\\n\tFIELD(F_INTERNAL, u8, functionAndDeviceNumber, \"\")\\\n\tFIELD(F_DERIVED, bool, enabled, \"\")\\\n\tFIELD(F_DERIVED, u8, deviceNumber, \"\")\\\n\tFIELD(F_DERIVED, u8, functionNumber, \"\")\\\n\n\n//-----------------------------------------------------------------------------\n\nstruct Header\n{\n\tu8 id;\n\tu8 length;\n\tHandle handle;\n};\n\n// define each enumeration:\n#define ENUM(enumerator, value) enumerator = value,\n// (a struct wrapper allows reusing common enumerator names such as `other'.)\n#define ENUMERATION(name, type)\\\n\tstruct name\\\n\t{\\\n\t\t/* (determines how much data to read from SMBIOS) */\\\n\t\ttypedef type T;\\\n\t\tname(): value((Enum)0) {}\\\n\t\tname(size_t num): value((Enum)num) {}\\\n\t\t/* (the existence of this member type indicates the field is an enum) */\\\n\t\tenum Enum { name##_ENUMERATORS sentinel } value;\\\n\t\t/* (allows generic Field comparison against numeric_limits) */\\\n\t\toperator size_t() const { return value; }\\\n\t};\nENUMERATIONS\n#undef ENUMERATION\n#undef ENUM\n\n// declare each structure\n#define FIELD(flags, type, name, units) type name;\n#define STRUCTURE(name, id)\\\n\tstruct name\\\n\t{\\\n\t\t/* (allows searching for a structure with a given handle) */\\\n\t\tHeader header;\\\n\t\t/* (defines a linked list of instances of this structure type) */\\\n\t\tname* next;\\\n\t\tname##_FIELDS\\\n\t};\nSTRUCTURES\n#undef STRUCTURE\n#undef FIELD\n\n// declare a struct holding pointers (freed at exit) to each structure\nstruct Structures\n{\n#define STRUCTURE(name, id) name* name##_;\n\tSTRUCTURES\n#undef STRUCTURE\n};\n\n#pragma pack(pop)\n\n\n//-----------------------------------------------------------------------------\n\n/**\n * @return a pointer to a static Structures (i.e. always valid), with its\n * member pointers non-zero iff SMBIOS information is available and includes\n * the corresponding structure.\n *\n * thread-safe; return value should be cached (if possible) to avoid an\n * atomic comparison.\n **/\nLIB_API const Structures* GetStructures();\n\n/**\n * @return a string describing all structures (omitting fields with\n * meaningless or dummy values).\n **/\nLIB_API std::string StringizeStructures(const Structures*);\n\n}\t// namespace SMBIOS\n\n#endif\t// #ifndef INCLUDED_SMBIOS\n"
  },
  {
    "path": "fpsgame/gui/sysdep/snd.cpp",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * sound card detection.\n */\n\n#include \"precompiled.h\"\n#include \"lib/sysdep/snd.h\"\n\n#if OS_WIN\n# include \"lib/sysdep/os/win/wsnd.h\"\n#endif\n\n\nwchar_t snd_card[SND_CARD_LEN];\nwchar_t snd_drv_ver[SND_DRV_VER_LEN];\n\nvoid snd_detect()\n{\n\t// note: OpenAL alGetString is worthless: it only returns\n\t// OpenAL API version and renderer (e.g. \"Software\").\n\n#if OS_WIN\n\twin_get_snd_info();\n#else\n\t// At least reset the values for unhandled platforms.\n\tENSURE(SND_CARD_LEN >= 8 && SND_DRV_VER_LEN >= 8);\t// protect strcpy\n\twcscpy_s(snd_card, ARRAY_SIZE(snd_card), L\"Unknown\");\n\twcscpy_s(snd_drv_ver, ARRAY_SIZE(snd_drv_ver), L\"Unknown\");\n#endif\n}\n"
  },
  {
    "path": "fpsgame/gui/sysdep/snd.h",
    "content": "/* Copyright (c) 2015 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * sound card detection.\n */\n\n#ifndef INCLUDED_SND\n#define INCLUDED_SND\n\nconst size_t SND_CARD_LEN = 512;\n/**\n * description of sound card.\n **/\nextern wchar_t snd_card[SND_CARD_LEN];\n\nconst size_t SND_DRV_VER_LEN = 512;\n/**\n * sound driver identification and version.\n **/\nextern wchar_t snd_drv_ver[SND_DRV_VER_LEN];\n\n/**\n * detect sound card and set the above information.\n **/\nextern void snd_detect();\n\n#endif\t// #ifndef INCLUDED_SND\n"
  },
  {
    "path": "fpsgame/gui/sysdep/stl.h",
    "content": "/* Copyright (c) 2013 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * fixes for various STL implementations\n */\n\n#ifndef INCLUDED_STL\n#define INCLUDED_STL\n\n#include \"lib/config.h\"\n#include \"compiler.h\"\n#include <cstdlib> // indirectly pull in bits/c++config.h on Linux, so __GLIBCXX__ is defined\n\n// detect STL version\n// .. Dinkumware\n#if MSC_VERSION\n# include <yvals.h>\t// defines _CPPLIB_VER\n#endif\n#if defined(_CPPLIB_VER)\n# define STL_DINKUMWARE _CPPLIB_VER\n#else\n# define STL_DINKUMWARE 0\n#endif\n// .. GCC\n#if defined(__GLIBCPP__)\n# define STL_GCC __GLIBCPP__\n#elif defined(__GLIBCXX__)\n# define STL_GCC __GLIBCXX__\n#else\n# define STL_GCC 0\n#endif\n// .. ICC\n#if defined(__INTEL_CXXLIB_ICC)\n# define STL_ICC __INTEL_CXXLIB_ICC\n#else\n# define STL_ICC 0\n#endif\n\n\n// disable (slow!) iterator checks in release builds (unless someone already defined this)\n#if STL_DINKUMWARE && defined(NDEBUG) && !defined(_SECURE_SCL)\n# define _SECURE_SCL 0\n#endif\n\n\n// pass \"disable exceptions\" setting on to the STL\n#if CONFIG_DISABLE_EXCEPTIONS\n# if STL_DINKUMWARE\n#  define _HAS_EXCEPTIONS 0\n# else\n#  define STL_NO_EXCEPTIONS\n# endif\n#endif\n\n\n// OS X - fix some stream template instantiations that break 10.5 compatibility on newer SDKs\n#if OS_MACOSX\n# include \"os/osx/osx_stl_fixes.h\"\n#endif\n\n#endif\t// #ifndef INCLUDED_STL\n"
  },
  {
    "path": "fpsgame/gui/sysdep/sysdep.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * various system-specific function implementations\n */\n\n#ifndef INCLUDED_SYSDEP\n#define INCLUDED_SYSDEP\n\n#include \"lib/debug.h\"\t// ErrorReactionInternal\n#include \"lib/os_path.h\"\n\n\n//\n// output\n//\n\n/**\n * display a message.\n *\n * @param caption title message\n * @param msg message contents\n *\n * implemented as a MessageBox on Win32 and printf on Unix.\n * called from debug_DisplayMessage.\n **/\nextern void sys_display_msg(const wchar_t* caption, const wchar_t* msg);\n\n/**\n * show the error dialog.\n *\n * @param text to display (practically unlimited length)\n * @param flags: see DebugDisplayErrorFlags.\n * @return ErrorReactionInternal (except ERI_EXIT, which is acted on immediately)\n *\n * called from debug_DisplayError unless overridden by means of\n * ah_display_error.\n **/\nextern ErrorReactionInternal sys_display_error(const wchar_t* text, size_t flags);\n\n\n//\n// misc\n//\n\n/**\n * @return whether a debugger is attached to the process\n * (if so, it is safe to use debug_break; otherwise, that would\n * raise an exception)\n **/\nLIB_API bool sys_IsDebuggerPresent();\n\n/**\n * @return a wide string conversion of the platform's encoding of main's argv.\n *\n * (NB: wseh.cpp defines a wmain that converts argv to UTF-8 and calls main(),\n * but only if LIB_STATIC_LINK)\n **/\nLIB_API std::wstring sys_WideFromArgv(const char* argv_i);\n\n/**\n * describe the current OS error state.\n * \n * @param err: if not 0, use that as the error code to translate; otherwise,\n * uses GetLastError or similar.\n * @param buf output buffer\n * @param max_chars\n *\n * rationale: it is expected to be rare that OS return/error codes are\n * actually seen by user code, but we leave the possibility open.\n **/\nextern Status sys_StatusDescription(int err, wchar_t* buf, size_t max_chars);\n\n/**\n * determine filename of the module to whom an address belongs.\n *\n * @param addr\n * @param pathname Full path to module (unchanged unless INFO::OK is returned).\n * @return Status\n *\n * note: this is useful for handling exceptions in other modules.\n **/\nStatus sys_get_module_filename(void* addr, OsPath& pathname);\n\n/**\n * @return full pathname of the current executable.\n *\n * this is useful for determining installation directory, e.g. for VFS.\n **/\nLIB_API OsPath sys_ExecutablePathname();\n\n/**\n * Get the current user's login name.\n *\n * @return login name, or empty string on error\n */\nextern std::wstring sys_get_user_name();\n\n/**\n * Have the user choose a directory via OS dialog.\n *\n * @param path Path's input value determines the starting directory for\n *\t\t  faster browsing. if INFO::OK is returned, it receives\n *\t\t  chosen directory path.\n **/\nextern Status sys_pick_directory(OsPath& path);\n\n/**\n * Open the user's default web browser to the given URL.\n **/\nextern Status sys_open_url(const std::string& url);\n\n/**\n * return the largest sector size [bytes] of any storage medium\n * (HD, optical, etc.) in the system.\n * \n * this may be a bit slow to determine (iterates over all drives),\n * but caches the result so subsequent calls are free.\n * (caveat: device changes won't be noticed during this program run)\n * \n * sector size is relevant because Windows aio requires all IO\n * buffers, offsets and lengths to be a multiple of it. this requirement\n * is also carried over into the vfs / file.cpp interfaces for efficiency\n * (avoids the need for copying to/from align buffers).\n * \n * waio uses the sector size to (in some cases) align IOs if\n * they aren't already, but it's also needed by user code when\n * aligning their buffers to meet the requirements.\n * \n * the largest size is used so that we can read from any drive. while this\n * is a bit wasteful (more padding) and requires iterating over all drives,\n * it is the only safe way: this may be called before we know which\n * drives will be needed, and hardlinks may confuse things.\n **/\nextern size_t sys_max_sector_size();\n\n/**\n * generate high-quality random bytes.\n *\n * this should only be used with small numbers of bytes, to avoid\n * hogging the system's entropy.\n **/\nLIB_API Status sys_generate_random_bytes(u8* buf, size_t count);\n\n/**\n * get the proxy address for accessing the given HTTP URL.\n *\n * this may be very slow (tens of seconds).\n *\n * @return INFO::OK on success; INFO::SKIPPED if no proxy found.\n **/\nLIB_API Status sys_get_proxy_config(const std::wstring& url, std::wstring& proxy);\n\n/**\n * open a file like with fopen (but taking an OsPath argument).\n */\nLIB_API FILE* sys_OpenFile(const OsPath& pathname, const char* mode);\n\n/**\n * directory separation character\n **/\n#if OS_WIN\n# define SYS_DIR_SEP '\\\\'\n#else\n# define SYS_DIR_SEP '/'\n#endif\n\n#endif\t// #ifndef INCLUDED_SYSDEP\n"
  },
  {
    "path": "fpsgame/gui/sysdep/tests/test_rtl.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#include \"lib/self_test.h\"\n\n#include \"lib/sysdep/rtl.h\"\n\nclass Test_rtl : public CxxTest::TestSuite\n{\n\tvoid _test_AllocateAligned_helper(size_t size, size_t align)\n\t{\n\t\tvoid* p = rtl_AllocateAligned(size, align);\n\t\tTS_ASSERT(p != NULL);\n\t\tTS_ASSERT_EQUALS((intptr_t)p % align, 0u);\n\t\tmemset(p, 0x42, size);\n\t\trtl_FreeAligned(p);\n\t}\npublic:\n\tvoid test_AllocateAligned()\n\t{\n\t\tfor (size_t s = 0; s < 64; ++s)\n\t\t{\n\t\t\t_test_AllocateAligned_helper(s, 8);\n\t\t\t_test_AllocateAligned_helper(s, 16);\n\t\t\t_test_AllocateAligned_helper(s, 64);\n\t\t\t_test_AllocateAligned_helper(s, 1024);\n\t\t\t_test_AllocateAligned_helper(s, 65536);\n\t\t}\n\t}\n\n\tvoid test_FreeAligned_null()\n\t{\n\t\trtl_FreeAligned(NULL);\n\t}\n};\n"
  },
  {
    "path": "fpsgame/gui/sysdep/tests/test_sysdep.h",
    "content": "/* Copyright (c) 2014 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#include \"lib/self_test.h\"\n\n#include \"lib/lib.h\"\n#include \"lib/secure_crt.h\"\n#include \"lib/sysdep/cpu.h\"\n#include \"lib/sysdep/filesystem.h\"\n#include \"lib/sysdep/sysdep.h\"\n\n#if OS_BSD || OS_LINUX\n# include \"lib/sysdep/os/unix/unix_executable_pathname.h\"\n# include \"mocks/dlfcn.h\"\n# include \"mocks/unistd.h\"\n#endif\n\nclass TestSysdep : public CxxTest::TestSuite \n{\npublic:\n\tvoid test_random()\n\t{\n\t\tu64 a = 0, b = 0;\n\t\tTS_ASSERT_OK(sys_generate_random_bytes((u8*)&a, sizeof(a)));\n\t\tTS_ASSERT_OK(sys_generate_random_bytes((u8*)&b, sizeof(b)));\n\t\tTS_ASSERT_DIFFERS(a, b);\n\t}\n\n\tvoid test_sys_ExecutablePathname()\n\t{\n\t\tOsPath path = sys_ExecutablePathname();\n\n\t\t// Try it first with the real executable (i.e. the\n\t\t// one that's running this test code)\n\t\t// Check it's absolute\n\t\tTSM_ASSERT(L\"Path: \"+path.string(), path_is_absolute(path.string().c_str()));\n\t\t// Check the file exists\n\t\tstruct stat s;\n\t\tTSM_ASSERT_EQUALS(L\"Path: \"+path.string(), wstat(path, &s), 0);\n\t}\n\n\tvoid test_unix_ExecutablePathname()\n\t{\n#if !(OS_BSD || OS_LINUX)\n\t}\n#else\n\t\t// Since the implementation uses realpath, the tested files need to\n\t\t// really exist. So set up a directory tree for testing:\n\n\t\tconst char* tmpdir = getenv(\"TMPDIR\");\n\t\tif (! tmpdir) tmpdir = P_tmpdir;\n\n\t\tchar root[PATH_MAX];\n\t\tsprintf_s(root, ARRAY_SIZE(root), \"%s/pyrogenesis-test-sysdep-XXXXXX\", tmpdir);\n\t\tTS_ASSERT(mkdtemp(root));\n\n\t\tchar rootres[PATH_MAX];\n\t\tTS_ASSERT(realpath(root, rootres));\n\n\t\tstd::string rootstr(rootres);\n\t\tOsPath rootstrw(rootstr);\n\n\t\tconst char* dirs[] = {\n\t\t\t\"/example\",\n\t\t\t\"/example/a\",\n\t\t\t\"/example/a/b\",\n\t\t\t\"/example/a/b/c\",\n\t\t\t\"/example/a/b/d\",\n\t\t\t\"/example/a/e\",\n\t\t\t\"/example/a/f\"\n\t\t};\n\t\tconst char* files[] = {\n\t\t\t\"/example/executable\",\n\t\t\t\"/example/a/f/executable\",\n\t\t};\n\t\tfor (size_t i = 0; i < ARRAY_SIZE(dirs); ++i)\n\t\t{\n\t\t\tstd::string name = rootstr + dirs[i];\n\t\t\tTS_ASSERT_EQUALS(mkdir(name.c_str(), 0700), 0);\n\t\t}\n\t\tfor (size_t i = 0; i < ARRAY_SIZE(files); ++i)\n\t\t{\n\t\t\tstd::string name = rootstr + files[i];\n\t\t\tFILE* f = fopen(name.c_str(), \"w\");\n\t\t\tTS_ASSERT(f);\n\t\t\tfclose(f);\n\t\t}\n\n\t\t// Try with absolute paths\n\t\t{\n\t\t\tMock_dladdr d(rootstr+\"/example/executable\");\n\t\t\tTS_ASSERT_PATH_EQUALS(unix_ExecutablePathname(), rootstrw/L\"example/executable\");\n\t\t}\n\t\t{\n\t\t\tMock_dladdr d(rootstr+\"/example/./a/b/../e/../../executable\");\n\t\t\tTS_ASSERT_PATH_EQUALS(unix_ExecutablePathname(), rootstrw/L\"example/executable\");\n\t\t}\n\n\t\t// Try with relative paths\n\t\t{\n\t\t\tMock_dladdr d(\"./executable\");\n\t\t\tMock_getcwd m(rootstr+\"/example\");\n\t\t\tTS_ASSERT_PATH_EQUALS(unix_ExecutablePathname(), rootstrw/L\"example/executable\");\n\t\t}\n\t\t{\n\t\t\tMock_dladdr d(\"./executable\");\n\t\t\tMock_getcwd m(rootstr+\"/example/\");\n\t\t\tTS_ASSERT_PATH_EQUALS(unix_ExecutablePathname(), rootstrw/L\"example/executable\");\n\t\t}\n\t\t{\n\t\t\tMock_dladdr d(\"../d/../../f/executable\");\n\t\t\tMock_getcwd m(rootstr+\"/example/a/b/c\");\n\t\t\tTS_ASSERT_PATH_EQUALS(unix_ExecutablePathname(), rootstrw/L\"example/a/f/executable\");\n\t\t}\n\n\t\t// Try with pathless names\n\t\t{\n\t\t\tMock_dladdr d(\"executable\");\n\t\t\tTS_ASSERT_PATH_EQUALS(unix_ExecutablePathname(), OsPath());\n\t\t}\n\n\t\t// Clean up the temporary files\n\t\tfor (size_t i = 0; i < ARRAY_SIZE(files); ++i)\n\t\t{\n\t\t\tstd::string name = rootstr + files[i];\n\t\t\tTS_ASSERT_EQUALS(unlink(name.c_str()), 0);\n\t\t}\n\t\tfor (ssize_t i = ARRAY_SIZE(dirs)-1; i >= 0; --i) // reverse order\n\t\t{\n\t\t\tstd::string name(root);\n\t\t\tname += dirs[i];\n\t\t\tTS_ASSERT_EQUALS(rmdir(name.c_str()), 0);\n\t\t}\n\t\tTS_ASSERT_EQUALS(rmdir(root), 0);\n\t}\n\n\t// Mock classes for test_unix_ExecutablePathname\n\tclass Mock_dladdr : public T::Base_dladdr\n\t{\n\tpublic:\n\t\tMock_dladdr(const std::string& fname) : fname_(fname) { }\n\t\tint dladdr(void *UNUSED(addr), Dl_info *info) {\n\t\t\tinfo->dli_fname = fname_.c_str();\n\t\t\treturn 1;\n\t\t}\n\tprivate:\n\t\tstd::string fname_;\n\t};\n\n\tclass Mock_getcwd : public T::Base_getcwd\n\t{\n\tpublic:\n\t\tMock_getcwd(const std::string& buf) : buf_(buf) { }\n\t\tchar* getcwd(char* buf, size_t size) {\n\t\t\tstrncpy_s(buf, size, buf_.c_str(), buf_.length());\n\t\t\treturn buf;\n\t\t}\n\tprivate:\n\t\tstd::string buf_;\n\t};\n#endif // !(OS_BSD || OS_LINUX)\n\nprivate:\n\tbool path_is_absolute(const wchar_t* path)\n\t{\n\t\t// UNIX-style absolute paths\n\t\tif (path[0] == '/')\n\t\t\treturn true;\n\n\t\t// Windows UNC absolute paths\n\t\tif (path[0] == '\\\\' && path[1] == '\\\\')\n\t\t\treturn true;\n\n\t\t// Windows drive-letter absolute paths\n\t\tif (iswalpha(path[0]) && path[1] == ':' && (path[2] == '/' || path[2] == '\\\\'))\n\t\t\treturn true;\n\n\t\treturn false;\n\t}\n\n};\n"
  },
  {
    "path": "fpsgame/gui/sysdep/vm.h",
    "content": "/* Copyright (c) 2011 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * virtual memory interface. supercedes POSIX mmap; provides support for\n * large pages, autocommit, and specifying protection flags during allocation.\n */\n\n#ifndef INCLUDED_SYSDEP_VM\n#define INCLUDED_SYSDEP_VM\n\n#include \"lib/posix/posix_mman.h\"\t// PROT_*\n\nnamespace vm {\n\n// committing large pages (2 MiB) instead of regular 4 KiB pages can\n// increase TLB coverage and reduce misses for sequential access patterns.\n// however, small page TLBs have more entries, making them better suited\n// to random accesses. it may also take a long time to find/free up\n// contiguous regions of physical memory for large pages. applications\n// can express their preference or go along with the default,\n// which depends on several factors such as allocation size.\nenum PageType\n{\n\tkLarge,     // use large if available\n\tkSmall,     // always use small\n\tkDefault\t// heuristic\n};\n\n/**\n * reserve address space and set the parameters for any later\n * on-demand commits.\n *\n * @param size desired number of bytes. any additional space\n *   in the last page is also accessible.\n * @param commitSize [bytes] how much to commit each time.\n *   larger values reduce the number of page faults at the cost of\n *   additional internal fragmentation. must be a multiple of\n *   largePageSize unless pageType == kSmall.\n * @param pageType chooses between large/small pages for commits.\n * @param prot memory protection flags for newly committed pages.\n * @return base address (aligned to the respective page size) or\n *   0 if address space/descriptor storage is exhausted\n *   (an error dialog will also be raised).\n *   must be freed via ReleaseAddressSpace.\n**/\nLIB_API void* ReserveAddressSpace(size_t size, size_t commitSize = largePageSize, PageType pageType = kDefault, int prot = PROT_READ|PROT_WRITE);\n\n/**\n * release address space and decommit any memory.\n *\n * @param p a pointer previously returned by ReserveAddressSpace.\n * @param size is required by the POSIX implementation and\n *   ignored on Windows. it also ensures compatibility with UniqueRange.\n **/\nLIB_API void ReleaseAddressSpace(void* p, size_t size = 0);\n\n\n/**\n * map physical memory to previously reserved address space.\n *\n * @param address, size need not be aligned, but this function commits\n *   any pages intersecting that interval.\n * @param pageType, prot - see ReserveAddressSpace.\n * @return whether memory was successfully committed.\n *\n * note: committing only maps virtual pages and does not actually allocate\n * page frames. Windows XP uses a first-touch heuristic - the page will\n * be taken from the node whose processor caused the fault.\n * therefore, worker threads should be the first to write to their memory.\n *\n * (this is surprisingly slow in XP, possibly due to PFN lock contention)\n **/\nLIB_API bool Commit(uintptr_t address, size_t size, PageType pageType = kDefault, int prot = PROT_READ|PROT_WRITE);\n\n/**\n * unmap physical memory.\n *\n * @return whether the operation succeeded.\n **/\nLIB_API bool Decommit(uintptr_t address, size_t size);\n\n\n/**\n * set the memory protection flags for all pages that intersect\n * the given interval.\n * the pages must currently be committed.\n *\n * @param prot memory protection flags: PROT_NONE or a combination of\n *   PROT_READ, PROT_WRITE, PROT_EXEC.\n **/\nLIB_API bool Protect(uintptr_t address, size_t size, int prot);\n\n\n/**\n * reserve address space and commit memory.\n *\n * @param size [bytes] to allocate.\n * @param pageType, prot - see ReserveAddressSpace.\n * @return zero-initialized memory aligned to the respective\n *   page size.\n **/\nLIB_API void* Allocate(size_t size, PageType pageType = kDefault, int prot = PROT_READ|PROT_WRITE);\n\n/**\n * decommit memory and release address space.\n *\n * @param p a pointer previously returned by Allocate.\n * @param size is required by the POSIX implementation and\n *   ignored on Windows. it also ensures compatibility with UniqueRange.\n *\n * (this differs from ReleaseAddressSpace, which must account for\n * extra padding/alignment to largePageSize.)\n **/\nLIB_API void Free(void* p, size_t size = 0);\n\n\n/**\n * install a handler that attempts to commit memory whenever a\n * read/write page fault is encountered. thread-safe.\n **/\nLIB_API void BeginOnDemandCommits();\n\n/**\n * decrements the reference count begun by BeginOnDemandCommit and\n * removes the page fault handler when it reaches 0. thread-safe.\n **/\nLIB_API void EndOnDemandCommits();\n\n\nLIB_API void DumpStatistics();\n\n}\t// namespace vm\n\n#endif\t// #ifndef INCLUDED_SYSDEP_VM\n"
  },
  {
    "path": "fpsgame/gui/tests/test_adts.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#include \"lib/self_test.h\"\n\n#include \"lib/adts/ring_buf.h\"\n#include \"lib/rand.h\"\n\nclass TestRingbuf : public CxxTest::TestSuite \n{\n\tstatic const size_t N = 49;\t// RingBuf capacity\n\tstatic const int S = 100;\t// number of test items\npublic:\n\tvoid test_insert_remove()\n\t{\n\t\tRingBuf<int, N> buf;\n\t\tfor(int i = 1; i < S; i++)\n\t\t{\n\t\t\tbuf.push_back(i);\n\t\t\tTS_ASSERT_EQUALS(buf.front(), i);\n\t\t\tbuf.pop_front();\n\t\t}\n\t\tTS_ASSERT(buf.size() == 0 && buf.empty());\n\t}\n\n\tvoid test_fill_overwrite_old()\n\t{\n\t\tRingBuf<int, N> buf;\n\t\tfor(int i = 1; i < S; i++)\n\t\t\tbuf.push_back(i);\n\t\tTS_ASSERT_EQUALS(buf.size(), N);\n\t\tint first = buf.front();\n\t\tTS_ASSERT_EQUALS(first, (int)(S-1 -N +1));\n\t\tfor(size_t i = 0; i < N; i++)\n\t\t{\n\t\t\tTS_ASSERT_EQUALS(buf.front(), first);\n\t\t\tfirst++;\n\t\t\tbuf.pop_front();\n\t\t}\n\t\tTS_ASSERT(buf.size() == 0 && buf.empty());\n\t}\n\n\tvoid test_randomized_insert_remove()\n\t{\n\t\tsrand(1);\n\t\tRingBuf<int, N> buf;\n\t\tstd::deque<int> deq;\n\t\tfor(size_t rep = 0; rep < 1000; rep++)\n\t\t{\n\t\t\tsize_t rnd_op = rand(0, 10);\n\t\t\t// 70% - insert\n\t\t\tif(rnd_op >= 3)\n\t\t\t{\n\t\t\t\tint item = rand();\n\t\t\t\tbuf.push_back(item);\n\n\t\t\t\tdeq.push_back(item);\n\t\t\t\tint excess_items = (int)deq.size() - N;\n\t\t\t\tif(excess_items > 0)\n\t\t\t\t{\n\t\t\t\t\tfor(int i = 0; i < excess_items; i++)\n\t\t\t\t\t{\n\t\t\t\t\t\tdeq.pop_front();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// 30% - pop front (only if not empty)\n\t\t\telse if(!deq.empty())\n\t\t\t{\n\t\t\t\tbuf.pop_front();\n\t\t\t\tdeq.pop_front();\n\t\t\t}\n\n\t\t\tTS_ASSERT_EQUALS(buf.size(), deq.size());\n\t\t\tRingBuf<int, N>::iterator begin = buf.begin(), end = buf.end();\n\t\t\tTS_ASSERT(std::equal(begin, end, deq.begin()));\n\t\t}\n\t}\n};\n"
  },
  {
    "path": "fpsgame/gui/tests/test_base32.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#include \"lib/self_test.h\"\n\n#include \"lib/base32.h\"\n\nclass TestBase32 : public CxxTest::TestSuite \n{\npublic:\n\tvoid test_base32()\n\t{\n\t\t// compare against previous output (generated via this base32() call)\n\t\tconst u8 in[] = { 0x12, 0x57, 0x85, 0xA2, 0xF9, 0x41, 0xCD, 0x57, 0xF3 };\n\t\tu8 out[20] = {0};\n\t\tbase32(ARRAY_SIZE(in), in, out);\n\t\tconst u8 correct_out[] = \"CJLYLIXZIHGVP4Y\";\n\t\tTS_ASSERT_SAME_DATA(out, correct_out, ARRAY_SIZE(correct_out));\n\t}\n\n\tvoid test_base32_lengths()\n\t{\n#define TEST(in, expected) \\\n\t\t{ \\\n\t\t\tu8 out[20] = {0}; \\\n\t\t\tbase32(ARRAY_SIZE(in), in, out); \\\n\t\t\tconst u8 correct_out[] = expected; \\\n\t\t\tTS_ASSERT_SAME_DATA(out, correct_out, ARRAY_SIZE(correct_out)); \\\n\t\t}\n\t\tconst u8 in1[] = { 0xFF };\n\t\tconst u8 in2[] = { 0xFF, 0xFF };\n\t\tconst u8 in3[] = { 0xFF, 0xFF, 0xFF };\n\t\tconst u8 in4[] = { 0xFF, 0xFF, 0xFF, 0xFF };\n\t\tconst u8 in5[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };\n\t\tconst u8 in6[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };\n\t\tTEST(in1, \"74\");\n\t\tTEST(in2, \"777Q\");\n\t\tTEST(in3, \"77776\");\n\t\tTEST(in4, \"777777Y\");\n\t\tTEST(in5, \"77777777\");\n\t\tTEST(in6, \"7777777774\");\n\t}\n};\n"
  },
  {
    "path": "fpsgame/gui/tests/test_bits.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#include \"lib/self_test.h\"\n\n#include \"lib/bits.h\"\n\n//#define EQUALS(actual, expected) ENSURE((actual) == (expected))\n#define EQUALS TS_ASSERT_EQUALS\n\nclass TestBits : public CxxTest::TestSuite \n{\npublic:\n\tvoid test_Bit()\n\t{\n\t\tEQUALS(Bit<unsigned>(0), 1u);\n\t\tEQUALS(Bit<unsigned>(8), 0x100u);\n\t\tEQUALS(Bit<u32>(31), u32(0x80000000ul));\n\t\tEQUALS(Bit<u64>(1), u64(2));\n\t\tEQUALS(Bit<u64>(32), u64(0x100000000ull));\n\t\tEQUALS(Bit<u64>(63), u64(0x8000000000000000ull));\n\t}\n\n\tvoid test_IsBitSet()\n\t{\n\t\tEQUALS(IsBitSet(0u, 1), false);\n\t\tEQUALS(IsBitSet(1u, 1), false);\n\t\tEQUALS(IsBitSet(2u, 1), true);\n\t\tEQUALS(IsBitSet<u32>(0xFFFFFFFFul, 0), true);\n\t\tEQUALS(IsBitSet<u32>(0xFFFFFFFFul, 31), true);\n\t\tEQUALS(IsBitSet<u64>(0xFFFFFFFFFFFFFFFFull, 0), true);\n\t\tEQUALS(IsBitSet<u64>(0xFFFFFFFFFFFFFFFFull, 31), true);\n\t\tEQUALS(IsBitSet<u64>(0xFFFFFFFFFFFFFFFFull, 32), true);\n\t\tEQUALS(IsBitSet<u64>(0xFFFFFFFFFFFFFFFFull, 63), true);\n\t}\n\n\tvoid test_bit_mask()\n\t{\n\t\tEQUALS(bit_mask<u16>(0), 0);\n\t\tEQUALS(bit_mask<u16>(2), 0x3);\n\t\tEQUALS(bit_mask<u16>(16), 0xFFFF);\n\t\tEQUALS(bit_mask<u32>(0), 0u);\n\t\tEQUALS(bit_mask<u32>(2), 0x3u);\n\t\tEQUALS(bit_mask<u32>(32), 0xFFFFFFFFul);\n\t\tEQUALS(bit_mask<u64>(0), 0u);\n\t\tEQUALS(bit_mask<u64>(2), 0x3u);\n\t\tEQUALS(bit_mask<u64>(32), 0xFFFFFFFFull);\n\t\tEQUALS(bit_mask<u64>(64), 0xFFFFFFFFFFFFFFFFull);\n\t}\n\n\tvoid test_bits()\n\t{\n\t\tEQUALS(bits<u16>(0xFFFF, 0, 15), 0xFFFF);\n\t\tEQUALS(bits<u16>(0xFFFF, 0, 7), 0xFF);\n\t\tEQUALS(bits<u16>(0xFFFF, 8, 15), 0xFF);\n\t\tEQUALS(bits<u16>(0xFFFF, 14, 15), 0x3);\n\t\tEQUALS(bits<u16>(0xAA55, 4, 11), 0xA5);\n\t\tEQUALS(bits<u16>(0xAA55, 14, 15), 0x2);\n\t\tEQUALS(bits<u32>(0ul, 0, 31), 0ul);\n\t\tEQUALS(bits<u32>(0xFFFFFFFFul, 0, 31), 0xFFFFFFFFul);\n\t\tEQUALS(bits<u64>(0ull, 0, 63), 0ull);\n\t\tEQUALS(bits<u64>(0xFFFFFFFFull, 0, 31), 0xFFFFFFFFull);\n\t\tEQUALS(bits<u64>(0x0000FFFFFFFF0000ull, 16, 47), 0xFFFFFFFFull);\n\t\tEQUALS(bits<u64>(0xFFFFFFFFFFFFFFFFull, 0, 63), 0xFFFFFFFFFFFFFFFFull);\n\t\tEQUALS(bits<u64>(0xA5A5A5A5A5A5A5A5ull, 32, 63), 0xA5A5A5A5ull);\n\t}\n\n\tvoid test_PopulationCount()\n\t{\n\t\tEQUALS(PopulationCount<u8>(0), 0u);\n\t\tEQUALS(PopulationCount<u8>(4), 1u);\n\t\tEQUALS(PopulationCount<u8>(0x28), 2u);\n\t\tEQUALS(PopulationCount<u8>(0xFF), 8u);\n\t\tEQUALS(PopulationCount<u32>(0x0ul), 0u);\n\t\tEQUALS(PopulationCount<u32>(0x8ul), 1u);\n\t\tEQUALS(PopulationCount<u32>(0xFFFFul), 16u);\n\t\tEQUALS(PopulationCount<u32>(0xFFFFFFFFul), 32u);\n\t\tEQUALS(PopulationCount<u64>(0x0ull), 0u);\n\t\tEQUALS(PopulationCount<u64>(0x10ull), 1u);\n\t\tEQUALS(PopulationCount<u64>(0xFFFFull), 16u);\n\t\tEQUALS(PopulationCount<u64>(0xFFFFFFFFull), 32u);\n\t\tEQUALS(PopulationCount<u64>(0xFFFFFFFFFFFFFFFEull), 63u);\n\t\tEQUALS(PopulationCount<u64>(0xFFFFFFFFFFFFFFFFull), 64u);\n\t}\n\n\tvoid test_is_pow2()\n\t{\n\t\tEQUALS(is_pow2(0u), false);\n\t\tEQUALS(is_pow2(~0u), false);\n\t\tEQUALS(is_pow2(0x80000001), false);\n\t\tEQUALS(is_pow2(1), true);\n\t\tEQUALS(is_pow2(1u << 31), true);\n\t}\n\n\tvoid test_ceil_log2()\n\t{\n\t\tEQUALS(ceil_log2(3u), 2u);\n\t\tEQUALS(ceil_log2(0xffffffffu), 32u);\n\t\tEQUALS(ceil_log2(1u), 0u);\n\t\tEQUALS(ceil_log2(256u), 8u);\n\t\tEQUALS(ceil_log2(0x80000000u), 31u);\n\t}\n\n\tvoid test_floor_log2()\n\t{\n\t\tEQUALS(floor_log2(1.f), 0);\n\t\tEQUALS(floor_log2(3.f), 1);\n\t\tEQUALS(floor_log2(256.f), 8);\n\t}\n\n\tvoid test_round_up_to_pow2()\n\t{\n\t\tEQUALS(round_up_to_pow2(0u), 1u);\n\t\tEQUALS(round_up_to_pow2(1u), 1u);\n\t\tEQUALS(round_up_to_pow2(127u), 128u);\n\t\tEQUALS(round_up_to_pow2(128u), 128u);\n\t\tEQUALS(round_up_to_pow2(129u), 256u);\n\t}\n\n\tvoid test_round_down_to_pow2()\n\t{\n\t\tEQUALS(round_down_to_pow2(1u), 1u);\n\t\tEQUALS(round_down_to_pow2(127u), 64u);\n\t\tEQUALS(round_down_to_pow2(128u), 128u);\n\t\tEQUALS(round_down_to_pow2(129u), 128u);\n\t}\n\n\tvoid test_round_up()\n\t{\n\t\tEQUALS(round_up( 0u, 16u), 0u);\n\t\tEQUALS(round_up( 4u, 16u), 16u);\n\t\tEQUALS(round_up(15u, 16u), 16u);\n\t\tEQUALS(round_up(20u, 32u), 32u);\n\t\tEQUALS(round_up(29u, 32u), 32u);\n\t\tEQUALS(round_up(0x1000u, 0x1000u), 0x1000u);\n\t\tEQUALS(round_up(0x1001u, 0x1000u), 0x2000u);\n\t\tEQUALS(round_up(0x1900u, 0x1000u), 0x2000u);\n\t}\n\n\tvoid test_round_down()\n\t{\n\t\tEQUALS(round_down( 0u, 16u), 0u);\n\t\tEQUALS(round_down( 4u, 16u), 0u);\n\t\tEQUALS(round_down(15u, 16u), 0u);\n\t\tEQUALS(round_down(20u, 16u), 16u);\n\t\tEQUALS(round_down(29u, 16u), 16u);\n\t\tEQUALS(round_down(0x1900u, 0x1000u), 0x1000u);\n\t\tEQUALS(round_down(0x2001u, 0x2000u), 0x2000u);\n\t}\n};\n"
  },
  {
    "path": "fpsgame/gui/tests/test_byte_order.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#include \"lib/self_test.h\"\n\n#include \"lib/byte_order.h\"\n\nclass TestByteOrder : public CxxTest::TestSuite \n{\npublic:\n\tvoid test_conversion()\n\t{\n\t\tconst u32 x = 0x01234567u;\n\t\tu8 LS_byte;\n\t\tmemcpy(&LS_byte, &x, 1);\n\t\t// little endian\n\t\tif(LS_byte == 0x67)\n\t\t{\n\t\t\tTS_ASSERT_EQUALS(to_le16(0x0123u), 0x0123u);\n\t\t\tTS_ASSERT_EQUALS(to_le32(0x01234567u), 0x01234567u);\n\t\t\tTS_ASSERT_EQUALS(to_le64(0x0123456789ABCDEFull), 0x0123456789ABCDEFull);\n\n\t\t\tTS_ASSERT_EQUALS(to_be16(0x0123u), 0x2301u);\n\t\t\tTS_ASSERT_EQUALS(to_be32(0x01234567u), 0x67452301u);\n\t\t\tTS_ASSERT_EQUALS(to_be64(0x0123456789ABCDEFull), 0xEFCDAB8967452301ull);\n\t\t}\n\t\t// big endian\n\t\telse if(LS_byte == 0x01)\n\t\t{\n\t\t\tTS_ASSERT_EQUALS(to_le16(0x0123u), 0x2301u);\n\t\t\tTS_ASSERT_EQUALS(to_le32(0x01234567u), 0x67452301u);\n\t\t\tTS_ASSERT_EQUALS(to_le64(0x0123456789ABCDEFull), 0xEFCDAB8967452301ull);\n\n\t\t\tTS_ASSERT_EQUALS(to_be16(0x0123u), 0x0123u);\n\t\t\tTS_ASSERT_EQUALS(to_be32(0x01234567u), 0x01234567u);\n\t\t\tTS_ASSERT_EQUALS(to_be64(0x0123456789ABCDEFull), 0x0123456789ABCDEFull);\n\t\t}\n\t\telse\n\t\t\tTS_FAIL(\"endian determination failed\");\n\n\t\t// note: no need to test read_?e* / write_?e* - they are\n\t\t// trivial wrappers on top of to_?e*.\n\t}\n\n\tvoid test_movzx()\n\t{\n\t\tconst u8 d1[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 };\n\t\tconst u8 d2[] = { 0x43, 0x12, 0x23, 0xA4 };\n\n\t\tTS_ASSERT_EQUALS(movzx_le64(d1, 1), 0x01ull);\n\t\tTS_ASSERT_EQUALS(movzx_le64(d1, 2), 0x0201ull);\n\t\tTS_ASSERT_EQUALS(movzx_le64(d1, 8), 0x0807060504030201ull);\n\t\tTS_ASSERT_EQUALS(movzx_le64(d2, 4), 0xA4231243ull);\n\t\tTS_ASSERT_EQUALS(movzx_le64(d2+3, 1), 0xA4ull);\n\n\t\tTS_ASSERT_EQUALS(movzx_be64(d1, 1), 0x01ull);\n\t\tTS_ASSERT_EQUALS(movzx_be64(d1, 2), 0x0102ull);\n\t\tTS_ASSERT_EQUALS(movzx_be64(d1, 8), 0x0102030405060708ull);\n\t\tTS_ASSERT_EQUALS(movzx_be64(d2, 4), 0x431223A4ull);\n\t\tTS_ASSERT_EQUALS(movzx_be64(d2+3, 1), 0xA4ull);\n\t}\n\n\tvoid test_movsx()\n\t{\n\t\tconst u8 d1[] = { 0x09, 0xFE };\n\t\tconst u8 d2[] = { 0xD9, 0x2C, 0xDD, 0x8F };\n\t\tconst u8 d3[] = { 0x92, 0x26, 0x88, 0xF1, 0x35, 0xAC, 0x01, 0x83 };\n\n\t\tTS_ASSERT_EQUALS(movsx_le64(d1, 1), (i64)0x09ull);\n\t\tTS_ASSERT_EQUALS(movsx_le64(d1, 2), (i64)0xFFFFFFFFFFFFFE09ull);\n\t\tTS_ASSERT_EQUALS(movsx_le64(d2, 4), (i64)0xFFFFFFFF8FDD2CD9ull);\n\t\tTS_ASSERT_EQUALS(movsx_le64(d3, 8), (i64)0x8301AC35F1882692ull);\n\n\t\tTS_ASSERT_EQUALS(movsx_be64(d1, 1), (i64)0x09ull);\n\t\tTS_ASSERT_EQUALS(movsx_be64(d1, 2), (i64)0x00000000000009FEull);\n\t\tTS_ASSERT_EQUALS(movsx_be64(d2, 4), (i64)0xFFFFFFFFD92CDD8Full);\n\t\tTS_ASSERT_EQUALS(movsx_be64(d3, 8), (i64)0x922688F135AC0183ull);\n\t}\n};\n"
  },
  {
    "path": "fpsgame/gui/tests/test_cache_adt.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#include \"lib/self_test.h\"\n\n#include \"lib/adts/cache_adt.h\"\n#include \"lib/rand.h\"\n\nclass TestCache: public CxxTest::TestSuite \n{\npublic:\n\tvoid test_cache_perf()\n\t{\n\t\tCache<int, int, Landlord_Naive> c1;\n\t\tCache<int, int, Landlord_Naive, Divider_Recip> c1r;\n\t\tCache<int, int, Landlord_Cached> c2;\n\t\tCache<int, int, Landlord_Cached, Divider_Recip> c2r;\n\t\tCache<int, int, Landlord_Lazy> c3;\n\t\tCache<int, int, Landlord_Lazy, Divider_Recip> c3r;\n\n#if defined(ENABLE_CACHE_POLICY_BENCHMARK) || 0\n\t\t// set max priority, to reduce interference while measuring.\n\t\tint old_policy; static sched_param old_param;\t// (static => 0-init)\n\t\tpthread_getschedparam(pthread_self(), &old_policy, &old_param);\n\t\tstatic sched_param max_param;\n\t\tmax_param.sched_priority = sched_get_priority_max(SCHED_FIFO);\n\t\tpthread_setschedparam(pthread_self(), SCHED_FIFO, &max_param);\n\n#define MEASURE(c, desc)\\\n\t{\\\n\t\tsrand(1);\\\n\t\tint cnt = 1;\\\n\t\tTIMER_BEGIN(desc);\\\n\t\tfor(int i = 0; i < 30000; i++)\\\n\t\t{\\\n\t\t\t/* 70% add (random objects) */\\\n\t\t\tbool add = rand(1,10) < 7;\\\n\t\t\tif(add)\\\n\t\t\t{\\\n\t\t\t\tint key = cnt++;\\\n\t\t\t\tint val = cnt++;\\\n\t\t\t\tsize_t size = (size_t)rand(1,100);\\\n\t\t\t\tsize_t cost = (size_t)rand(1,100);\\\n\t\t\t\tc.add(key, val, size, cost);\\\n\t\t\t}\\\n\t\t\telse\\\n\t\t\t{\\\n\t\t\t\tsize_t size;\\\n\t\t\t\tint value;\\\n\t\t\t\tc.remove_least_valuable(&value, &size);\\\n\t\t\t}\\\n\t\t}\\\n\t\tTIMER_END(desc);\\\n\t}\n\t\tMEASURE(c1, \"naive\")\n\t\tMEASURE(c1r, \"naiverecip\")\n\t\tMEASURE(c2, \"cached\")\n\t\tMEASURE(c2r, \"cachedrecip\")\n\t\tMEASURE(c3, \"lazy\")\n\t\tMEASURE(c3r, \"lazyrecip\")\n\n\t\t// restore previous policy and priority.\n\t\tpthread_setschedparam(pthread_self(), old_policy, &old_param);\n\t\texit(1134);\n#endif\n\t}\n\n\t// ensures all 3 variants of Landlord<> behave the same\n\t// [PT: disabled because it's far too slow]\n\tvoid DISABLED_test_cache_policies()\n\t{\n\t\tCache<int, int, Landlord_Naive > c1;\n\t\tCache<int, int, Landlord_Cached> c2;\n\t\tCache<int, int, Landlord_Lazy  > c3;\n\n\t\tsrand(1);\n\t\tint cnt = 1;\n\t\tfor(int i = 0; i < 1000; i++)\n\t\t{\n\t\t\t// 70% add (random objects)\n\t\t\tbool add = rand(1,10) < 7;\n\t\t\tif(add)\n\t\t\t{\n\t\t\t\tint key = cnt++;\n\t\t\t\tint val = cnt++;\n\t\t\t\tsize_t size = (size_t)rand(1,100);\n\t\t\t\tsize_t cost = (size_t)rand(1,100);\n\t\t\t\tc1.add(key, val, size, cost);\n\t\t\t\tc2.add(key, val, size, cost);\n\t\t\t\tc3.add(key, val, size, cost);\n\t\t\t}\n\t\t\t// 30% delete - make sure \"least valuable\" was same for all\n\t\t\telse\n\t\t\t{\n\t\t\t\tsize_t size1, size2, size3;\n\t\t\t\tint value1, value2, value3;\n\t\t\t\tbool removed1, removed2, removed3;\n\t\t\t\tremoved1 = c1.remove_least_valuable(&value1, &size1);\n\t\t\t\tremoved2 = c2.remove_least_valuable(&value2, &size2);\n\t\t\t\tremoved3 = c3.remove_least_valuable(&value3, &size3);\n\t\t\t\tTS_ASSERT_EQUALS(removed1, removed2);\n\t\t\t\tTS_ASSERT_EQUALS(removed2, removed3);\n\t\t\t\tif (removed1)\n\t\t\t\t{\n\t\t\t\t\tTS_ASSERT_EQUALS(size1, size2);\n\t\t\t\t\tTS_ASSERT_EQUALS(value1, value2);\n\t\t\t\t\tTS_ASSERT_EQUALS(size2, size3);\n\t\t\t\t\tTS_ASSERT_EQUALS(value2, value3);\n\t\t\t\t}\n\t\t\t}\t// else\n\t\t}\t// for i\n\t}\n};\n"
  },
  {
    "path": "fpsgame/gui/tests/test_fnv_hash.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#include \"lib/self_test.h\"\n\n#include \"lib/fnv_hash.h\"\n\nclass TestFnvHash : public CxxTest::TestSuite \n{\npublic:\n\tvoid test_fnv_hash()\n\t{\n\t\tTS_ASSERT_EQUALS(fnv_hash(\"\"), 0x811C9DC5u);\t\t// verify initial value\n\t\tconst u32 h1 = fnv_hash(\"abcdef\");\n\t\tTS_ASSERT_EQUALS(h1, 0xFF478A2A);\t\t\t\t\t// verify value for simple string\n\t\tTS_ASSERT_EQUALS(fnv_hash   (\"abcdef\", 6), h1);\t// same result if hashing buffer\n\t\tTS_ASSERT_EQUALS(fnv_lc_hash(\"ABcDeF\", 6), h1);\t// same result if case differs\n\n\t\tTS_ASSERT_EQUALS(fnv_hash64(\"\"), 0xCBF29CE484222325ull);\t// verify initial value\n\t\tconst u64 h2 = fnv_hash64(\"abcdef\");\n\t\tTS_ASSERT_EQUALS(h2, 0xD80BDA3FBE244A0Aull);\t\t// verify value for simple string\n\t\tTS_ASSERT_EQUALS(fnv_hash64(\"abcdef\", 6), h2);\t// same result if hashing buffer\n\t}\n};\n"
  },
  {
    "path": "fpsgame/gui/tests/test_lib.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#include \"lib/self_test.h\"\n\n#include \"lib/lib.h\"\n\nclass TestLib : public CxxTest::TestSuite \n{\npublic:\n\tvoid test_hi_lo()\n\t{\n\t\tTS_ASSERT_EQUALS(u64_hi(0x0123456789ABCDEFull), 0x01234567u);\n\t\tTS_ASSERT_EQUALS(u64_hi(0x0000000100000002ull), 0x00000001u);\n\n\t\tTS_ASSERT_EQUALS(u64_lo(0x0123456789ABCDEFull), 0x89ABCDEFu);\n\t\tTS_ASSERT_EQUALS(u64_lo(0x0000000100000002ull), 0x00000002u);\n\n\t\tTS_ASSERT_EQUALS(u32_hi(0x01234567u), 0x0123u);\n\t\tTS_ASSERT_EQUALS(u32_hi(0x00000001u), 0x0000u);\n\n\t\tTS_ASSERT_EQUALS(u32_lo(0x01234567u), 0x4567u);\n\t\tTS_ASSERT_EQUALS(u32_lo(0x00000001u), 0x0001u);\n\n\t\tTS_ASSERT_EQUALS(u64_from_u32(0xFFFFFFFFu, 0x80000008u), 0xFFFFFFFF80000008ull);\n\t\tTS_ASSERT_EQUALS(u32_from_u16(0x8000u, 0xFFFFu), 0x8000FFFFu);\n\t}\n\n\t// fp_to_u?? already validate the result.\n};\n"
  },
  {
    "path": "fpsgame/gui/tests/test_path.h",
    "content": "/* Copyright (c) 2012 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#include \"lib/self_test.h\"\n\n#include \"lib/path.h\"\n#include \"lib/os_path.h\"\n\nclass TestPath : public CxxTest::TestSuite \n{\npublic:\n\tvoid test_ctor()\n\t{\n\t\tconst char* s1 = \"a/b/c\";\n\t\tconst char* s2 = \"a/b/\\xEF\\xBF\\xBF\";\n\t\tconst wchar_t* w1 = L\"a/b/c\";\n\t\tconst wchar_t w2[] = { 'a', '/', 'b', '/', 0xEF, 0xBF, 0xBF, 0 };\n\t\tconst wchar_t w3[] = { 'a', '/', 'b', '/', 0xFFFF, 0 };\n\n\t\t// Empty strings\n\t\tPath p0a;\n\t\tPath p0b = Path(std::string());\n\t\tPath p0c = Path(std::wstring());\n\t\tTS_ASSERT(p0a.empty());\n\t\tTS_ASSERT_WSTR_EQUALS(p0a.string(), p0b.string());\n\t\tTS_ASSERT_WSTR_EQUALS(p0a.string(), p0c.string());\n\n\t\t// Construct from various types\n\t\tPath ps1a = Path(s1);\n\t\tPath ps2a = Path(s2);\n\t\tPath ps1b = Path(std::string(s1));\n\t\tPath ps2b = Path(std::string(s2));\n\t\tPath pw1a = Path(w1);\n\t\tPath pw2a = Path(w2);\n\t\tPath pw3a = Path(w3);\n\t\tPath pw1b = Path(std::wstring(w1));\n\t\tPath pw2b = Path(std::wstring(w2));\n\t\tPath pw3b = Path(std::wstring(w3));\n\n\t\tTS_ASSERT_WSTR_EQUALS(ps1a.string(), w1);\n\t\tTS_ASSERT_WSTR_EQUALS(ps1b.string(), w1);\n\t\tTS_ASSERT_WSTR_EQUALS(pw1a.string(), w1);\n\t\tTS_ASSERT_WSTR_EQUALS(pw1b.string(), w1);\n\n\t\tTS_ASSERT_WSTR_EQUALS(ps2a.string(), w2);\n\t\tTS_ASSERT_WSTR_EQUALS(ps2b.string(), w2);\n\t\tTS_ASSERT_WSTR_EQUALS(pw2a.string(), w2);\n\t\tTS_ASSERT_WSTR_EQUALS(pw2b.string(), w2);\n\n\t\tTS_ASSERT_WSTR_EQUALS(pw3a.string(), w3);\n\t\tTS_ASSERT_WSTR_EQUALS(pw3b.string(), w3);\n\n#if OS_WIN\n\t\tTS_ASSERT_WSTR_EQUALS(OsString(pw2a), w2);\n\t\tTS_ASSERT_WSTR_EQUALS(OsString(pw3a), w3);\n#else\n\t\tTS_ASSERT_STR_EQUALS(OsString(pw2a), s2);\n\t\t// OsString(pw3a) causes an intentional assertion failure, but we can't test that\n#endif\n\t}\n};\n"
  },
  {
    "path": "fpsgame/gui/tests/test_path_util.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#include \"lib/lib.h\"\n#include \"lib/self_test.h\"\n\n#include \"lib/path.h\"\n\n// Macros, not functions, to get proper line number reports when tests fail\n\n#define TEST_NAME_ONLY(path, correct_result) \\\n{ \\\n\tconst wchar_t* result = path_name_only(path); \\\n\tTS_ASSERT_WSTR_EQUALS(result, correct_result); \\\n}\n\nclass TestPathUtil : public CxxTest::TestSuite \n{\npublic:\n\n\tvoid test_subpath()\n\t{\n\t\t// obvious true\n\t\tTS_ASSERT(path_is_subpath(L\"abc/def/\", L\"abc/def/\") == true);\t// same\n\t\tTS_ASSERT(path_is_subpath(L\"abc/def/\", L\"abc/\") == true);\t// 2 is subpath\n\t\tTS_ASSERT(path_is_subpath(L\"abc/\", L\"abc/def/\") == true);\t// 1 is subpath\n\n\t\t// nonobvious true\n\t\tTS_ASSERT(path_is_subpath(L\"\", L\"\") == true);\n\t\tTS_ASSERT(path_is_subpath(L\"abc/def/\", L\"abc/def\") == true);\t// no '/' !\n\n\t\t// obvious false\n\t\tTS_ASSERT(path_is_subpath(L\"abc\", L\"def\") == false);\t// different, no path\n\n\t\t// nonobvious false\n\t\tTS_ASSERT(path_is_subpath(L\"abc\", L\"\") == false);\t// empty comparand\n\t\t// .. different but followed by common subdir\n\t\tTS_ASSERT(path_is_subpath(L\"abc/def/\", L\"ghi/def/\") == false);\n\t\tTS_ASSERT(path_is_subpath(L\"abc/def/\", L\"abc/ghi\") == false);\n\t}\n\n\t// TODO: can't test path validate yet without suppress-error-dialog\n\n\tvoid test_name_only()\n\t{\n\t\t// path with filename\n\t\tTEST_NAME_ONLY(L\"abc/def\", L\"def\");\n\t\t// nonportable path with filename\n\t\tTEST_NAME_ONLY(L\"abc\\\\def\\\\ghi\", L\"ghi\");\n\t\t// mixed path with filename\n\t\tTEST_NAME_ONLY(L\"abc/def\\\\ghi\", L\"ghi\");\n\t\t// mixed path with filename (2)\n\t\tTEST_NAME_ONLY(L\"abc\\\\def/ghi\", L\"ghi\");\n\t\t// filename only\n\t\tTEST_NAME_ONLY(L\"abc\", L\"abc\");\n\t\t// empty\n\t\tTEST_NAME_ONLY(L\"\", L\"\");\n\t}\n};\n"
  },
  {
    "path": "fpsgame/gui/tests/test_rand.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#include \"lib/self_test.h\"\n\n#include \"lib/rand.h\"\n\nclass TestRand : public CxxTest::TestSuite \n{\npublic:\n\t// complain if huge interval or min > max\n\tvoid TestParam()\n\t{\n\t\tdebug_SkipErrors(ERR::INVALID_PARAM);\n\t\tTS_ASSERT_EQUALS(rand(1, 0), size_t(0));\n\t\tTS_ASSERT_EQUALS(rand(2, ~0u), size_t(0));\n\t\tconst size_t numSkipped = debug_StopSkippingErrors();\n\t\tTS_ASSERT_EQUALS(numSkipped, (size_t)2);\n\t}\n\n\t// returned number must be in [min, max)\n\tvoid TestReturnedRange()\n\t{\n\t\tfor(int i = 0; i < 100; i++)\n\t\t{\n\t\t\tsize_t min = rand(), max = min+rand();\n\t\t\tsize_t x = rand(min, max);\n\t\t\tTS_ASSERT(min <= x && x < max);\n\t\t}\n\t}\n\n\t// make sure both possible values are hit\n\tvoid TestTwoValues()\n\t{\n\t\tsize_t ones = 0, twos = 0;\n\t\tfor(int i = 0; i < 100; i++)\n\t\t{\n\t\t\tsize_t x = rand(1, 3);\n\t\t\t// paranoia: don't use array (x might not be 1 or 2 - checked below)\n\t\t\tif(x == 1) ones++;\n\t\t\tif(x == 2) twos++;\n\t\t}\n\t\tTS_ASSERT_EQUALS(ones+twos, size_t(100));\n\t\tTS_ASSERT(ones > 10 && twos > 10);\n\t}\n};\n"
  },
  {
    "path": "fpsgame/gui/tests/test_regex.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#include \"lib/self_test.h\"\n\n#include \"lib/regex.h\"\n\nclass TestRegex : public CxxTest::TestSuite \n{\npublic:\n\tvoid test_regex()\n\t{\n\t\tTS_ASSERT_EQUALS(match_wildcard(L\"\", L\"\"), 1);\n\t\tTS_ASSERT_EQUALS(match_wildcard(L\"a\", 0), 1);\t// NULL matches everything\n\n\t\tTS_ASSERT_EQUALS(match_wildcard(L\"abc\", L\"abc\")     , 1);\t// direct match\n\t\tTS_ASSERT_EQUALS(match_wildcard(L\"abc\", L\"???\")     , 1);\t// only ?\n\t\tTS_ASSERT_EQUALS(match_wildcard(L\"abc\", L\"*\"  )     , 1);\t// only *\n\n\t\tTS_ASSERT_EQUALS(match_wildcard(L\"ab\" , L\"a?\" )     , 1);\t// trailing ?\n\t\tTS_ASSERT_EQUALS(match_wildcard(L\"abc\", L\"a?c\")     , 1);\t// middle ?\n\t\tTS_ASSERT_EQUALS(match_wildcard(L\"abc\", L\"?bc\")     , 1);\t// leading ?\n\n\t\tTS_ASSERT_EQUALS(match_wildcard(L\"abc\", L\"a*\" )     , 1);\t// trailing *\n\t\tTS_ASSERT_EQUALS(match_wildcard(L\"abcdef\", L\"ab*ef\"), 1);\t// middle *\n\t\tTS_ASSERT_EQUALS(match_wildcard(L\"abcdef\", L\"*f\"   ), 1);\t// leading *\n\n\t\tTS_ASSERT_EQUALS(match_wildcard(L\"abcdef\", L\"a?cd*\"), 1);\t// ? and *\n\t\tTS_ASSERT_EQUALS(match_wildcard(L\"abcdef\", L\"a*d?f\"), 1);\t// * and ?\n\t\tTS_ASSERT_EQUALS(match_wildcard(L\"abcdef\", L\"a*d*\" ), 1);\t// multiple *\n\t}\n};\n"
  },
  {
    "path": "fpsgame/gui/tests/test_secure_crt.h",
    "content": "/* Copyright (c) 2016 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#include \"lib/self_test.h\"\n\n#include \"lib/secure_crt.h\"\n\n// note: we only test the char version. this avoids having to\n// expose secure_crt.cpp's tchar / tcpy etc. macros in the header and/or\n// writing a copy of this test for the unicode version.\n// secure_crt.cpp's unicode functions are the same anyway\n// (they're implemented via the abovementioned tcpy macro redirection).\n\n\n#if OS_WIN\n// helper class to disable CRT error dialogs\nclass SuppressErrors\n{\npublic:\n\tSuppressErrors()\n\t{\n\t\t// Redirect the assertion output to somewhere where it shouldn't be noticed\n\t\told_mode = _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG);\n\t\t// Replace the invalid parameter handler with one that ignores everything\n\t\told_handler = _set_invalid_parameter_handler(&invalid_parameter_handler);\n\t}\n\t~SuppressErrors()\n\t{\n\t\t_CrtSetReportMode(_CRT_ASSERT, old_mode);\n\t\t_set_invalid_parameter_handler(old_handler);\n\t}\n\nprivate:\n\tint old_mode;\n\t_invalid_parameter_handler old_handler;\n\n\tstatic void invalid_parameter_handler(const wchar_t* UNUSED(expression), const wchar_t* UNUSED(function),\n\t\tconst wchar_t* UNUSED(file), unsigned int UNUSED(line), uintptr_t UNUSED(pReserved))\n\t{\n\t\treturn;\n\t}\n};\n#else\nclass SuppressErrors\n{\npublic:\n\tSuppressErrors()\n\t{\n\t}\n};\n#endif\n\n\nclass TestString_s : public CxxTest::TestSuite \n{\n\t// note: avoid 4-byte strings - they would trigger WARN::IF_PTR_LEN.\n\n\tconst char* const s0;\n\tconst char* const s1;\n\tconst char* const s5;\n\tconst char* const s10;\n\tconst wchar_t* const ws10;\n\n\tchar d1[1];\n\tchar d2[2];\n\tchar d3[3];\n\tchar d5[5];\n\tchar d6[6];\n\tchar d10[10];\n\twchar_t wd10[10];\n\tchar d11[11];\n\n\tchar no_null[7];\n\n\n\tstatic void TEST_LEN(const char* string, size_t limit,\n\t\tsize_t expected_len)\n\t{\n\t\tTS_ASSERT_EQUALS(int(strnlen((string), int(limit))), int(expected_len));\n\t}\n\n\tstatic void TEST_CPY(char* dst, size_t dst_max, const char* src,\n\t\tint expected_ret, const char* expected_dst)\n\t{\n\t\tint ret = strcpy_s(dst, dst_max, src);\n\t\tTS_ASSERT_EQUALS(ret, expected_ret);\n\t\tif(dst != 0)\n\t\t\tTS_ASSERT(!strcmp(dst, expected_dst));\n\t}\n\n\tstatic void TEST_CPY2(char* dst, size_t max_dst_chars, const char* src,\n\t\tint expected_ret, const char* expected_dst)\n\t{\n\t\tint ret = strcpy_s((dst), max_dst_chars, (src));\n\t\tTS_ASSERT_EQUALS(ret, expected_ret);\n\t\tif(dst != 0)\n\t\t\tTS_ASSERT(!strcmp(dst, expected_dst));\n\t}\n\n\tstatic void TEST_NCPY(char* dst, size_t max_dst_chars, const char* src, size_t max_src_chars,\n\t\tint expected_ret, const char* expected_dst)\n\t{\n\t\tint ret = strncpy_s(dst, max_dst_chars, src, max_src_chars);\n\t\tTS_ASSERT_EQUALS(ret, expected_ret);\n\t\tif(dst != 0)\n\t\t\tTS_ASSERT(!strcmp(dst, expected_dst));\n\t}\n\n\tstatic void TEST_CAT(char* dst, size_t max_dst_chars, const char* src,\n\t\tint expected_ret, const char* expected_dst)\n\t{\n\t\tint ret = strcat_s(dst, max_dst_chars, src);\n\t\tTS_ASSERT_EQUALS(ret, expected_ret);\n\t\tif(dst != 0)\n\t\t\tTS_ASSERT(!strcmp(dst, expected_dst));\n\t}\n\n\tstatic void TEST_CAT2(char* dst, size_t max_dst_chars, const char* src,\n\t\tconst char* dst_val, int expected_ret, const char* expected_dst)\n\t{\n\t\tstrcpy(dst, dst_val);\n\t\tint ret = strcat_s(dst, max_dst_chars, src);\n\t\tTS_ASSERT_EQUALS(ret, expected_ret);\n\t\tif(dst != 0)\n\t\t\tTS_ASSERT(!strcmp(dst, expected_dst));\n\t}\n\n\tstatic void TEST_NCAT(char* dst, size_t max_dst_chars, const char* src, size_t max_src_chars,\n\t\tconst char* dst_val, int expected_ret, const char* expected_dst)\n\t{\n\t\tstrcpy(dst, dst_val);\n\t\tint ret = strncat_s(dst, max_dst_chars, src, (max_src_chars));\n\t\tTS_ASSERT_EQUALS(ret, expected_ret);\n\t\tif(dst != 0)\n\t\t\tTS_ASSERT(!strcmp(dst, expected_dst));\n\t}\n\npublic:\n\tTestString_s()\n\t\t: s0(\"\"), s1(\"a\"), s5(\"abcde\"), s10(\"abcdefghij\"), ws10(L\"abcdefghij\")\n\t{\n\t\tconst char no_null_tmp[] = { 'n','o','_','n','u','l','l'};\n\t\tmemcpy(no_null, no_null_tmp, sizeof(no_null));\n\t}\n\n\t// contains all tests that verify correct behavior for bogus input.\n\t// our implementation suppresses error dialogs while the self-test is active,\n\t// but others (e.g. the functions shipped with VC8) need the code in\n\t// SuppressErrors to disable the error dialogs.\n\tvoid test_param_validation()\n\t{\n\t\tSuppressErrors suppress;\n\n#if EMULATE_SECURE_CRT\n# define SKIP_ERRORS(err) debug_SkipErrors(err)\n# define STOP_SKIPPING_ERRORS(expectedCount) TS_ASSERT_EQUALS(debug_StopSkippingErrors(), (size_t)expectedCount)\n#else\n# define SKIP_ERRORS(err) (void)0\n# define STOP_SKIPPING_ERRORS(expectedCount) (void)0\n#endif\n\n\t\tSKIP_ERRORS(ERR::INVALID_POINTER);\n\t\tTEST_CPY(0 ,0,0 , EINVAL,\"\");\t// all invalid\n\t\tTEST_CPY(0 ,0,s1, EINVAL,\"\");\t// dst = 0, max = 0\n\t\tTEST_CPY(0 ,1,s1, EINVAL,\"\");\t// dst = 0, max > 0\n\t\tTEST_CPY(d1,1,0 , EINVAL,\"\");\t// src = 0\n\t\tSTOP_SKIPPING_ERRORS(4);\n\n\t\tSKIP_ERRORS(ERR::INVALID_SIZE);\n\t\tTEST_CPY(d1,0,s1, EINVAL,\"\");\t// max_dst_chars = 0\n\t\tTEST_CPY2(d1,1, s1, ERANGE,\"\");\n\t\tTEST_CPY2(d1,1, s5, ERANGE,\"\");\n\t\tTEST_CPY2(d5,5, s5, ERANGE,\"\");\n\t\tSTOP_SKIPPING_ERRORS(4);\n\n\t\tSKIP_ERRORS(ERR::INVALID_SIZE);\n\t\tTEST_NCPY(d1,1 ,s1,1, ERANGE,\"\");\n\t\tTEST_NCPY(d1,1 ,s5,1, ERANGE,\"\");\n\t\tTEST_NCPY(d5,5 ,s5,5, ERANGE,\"\");\n\t\tSTOP_SKIPPING_ERRORS(3);\n\n\t\tSKIP_ERRORS(ERR::INVALID_POINTER);\n\t\tTEST_CAT(0 ,0,0 , EINVAL,\"\");\t// all invalid\n\t\tTEST_CAT(0 ,0,s1, EINVAL,\"\");\t// dst = 0, max = 0\n\t\tTEST_CAT(0 ,1,s1, EINVAL,\"\");\t// dst = 0, max > 0\n\t\tTEST_CAT(d1,1,0 , EINVAL,\"\");\t// src = 0\n\t\tSTOP_SKIPPING_ERRORS(4);\n\t\tSKIP_ERRORS(ERR::INVALID_SIZE);\n\t\tTEST_CAT(d1,0,s1, EINVAL,\"\");\t// max_dst_chars = 0\n\t\tSTOP_SKIPPING_ERRORS(1);\n\n\t\tSKIP_ERRORS(ERR::STRING_NOT_TERMINATED);\n\t\tTEST_CAT(no_null,5,s1, EINVAL,\"\");\t// dst not terminated\n\t\tSTOP_SKIPPING_ERRORS(1);\n\n\t\tSKIP_ERRORS(ERR::INVALID_SIZE);\n\t\tTEST_CAT2(d1,1, s1, \"\",ERANGE,\"\");\n\t\tTEST_CAT2(d1,1, s5, \"\",ERANGE,\"\");\n\t\tTEST_CAT2(d10,10, s10, \"\",ERANGE,\"\");\t\t// empty, total overflow\n\t\tTEST_CAT2(d10,10, s5, \"12345\",ERANGE,\"\");\t// not empty, overflow\n\t\tTEST_CAT2(d10,10, s10, \"12345\",ERANGE,\"\");\t// not empty, total overflow\n\t\tSTOP_SKIPPING_ERRORS(5);\n\n\t\tSKIP_ERRORS(ERR::INVALID_SIZE);\n\t\tTEST_NCAT(d1,1, s1,1, \"\",ERANGE,\"\");\n\t\tTEST_NCAT(d1,1, s5,5, \"\",ERANGE,\"\");\n\t\tTEST_NCAT(d10,10, s10,10, \"\",ERANGE,\"\");\t\t// empty, total overflow\n\t\tTEST_NCAT(d10,10, s5,5, \"12345\",ERANGE,\"\");\t\t// not empty, overflow\n\t\tTEST_NCAT(d10,10, s10,10, \"12345\",ERANGE,\"\");\t// not empty, total overflow\n\t\tSTOP_SKIPPING_ERRORS(5);\n\n#undef SKIP_ERRORS\n#undef STOP_SKIPPING_ERRORS\n\t}\n\n\n\tvoid test_length()\n\t{\n\t\tTEST_LEN(s0, 0 , 0 );\n\t\tTEST_LEN(s0, 1 , 0 );\n\t\tTEST_LEN(s0, 50, 0 );\n\t\tTEST_LEN(s1, 0 , 0 );\n\t\tTEST_LEN(s1, 1 , 1 );\n\t\tTEST_LEN(s1, 50, 1 );\n\t\tTEST_LEN(s5, 0 , 0 );\n\t\tTEST_LEN(s5, 1 , 1 );\n\t\tTEST_LEN(s5, 50, 5 );\n\t\tTEST_LEN(s10,9 , 9 );\n\t\tTEST_LEN(s10,10, 10);\n\t\tTEST_LEN(s10,11, 10);\n\t}\n\n\n\tvoid test_copy()\n\t{\n\t\tTEST_CPY2(d2,2 ,s1, 0,\"a\");\n\t\tTEST_CPY2(d6,6 ,s5, 0,\"abcde\");\n\t\tTEST_CPY2(d11,11, s5, 0,\"abcde\");\n\n\t\tTEST_NCPY(d2,2 ,s1,1, 0,\"a\");\n\t\tTEST_NCPY(d6,6 ,s5,5, 0,\"abcde\");\n\t\tTEST_NCPY(d11,11, s5,5, 0,\"abcde\");\n\n\t\tstrcpy(d5, \"----\");\n\t\tTEST_NCPY(d5,5, s5,0 , 0,\"\");\t// specified behavior! see 3.6.2.1.1 #4\n\t\tTEST_NCPY(d5,5, s5,1 , 0,\"a\");\n\t\tTEST_NCPY(d6,6, s5,5 , 0,\"abcde\");\n\t\tTEST_NCPY(d6,6, s5,10, 0,\"abcde\");\n\t}\n\n\n\tvoid test_concatenate()\n\t{\n\t\tTEST_CAT2(d3,3, s1, \"1\",0,\"1a\");\n\t\tTEST_CAT2(d5,5, s1, \"1\",0,\"1a\");\n\t\tTEST_CAT2(d6,6, s5, \"\",0,\"abcde\");\n\t\tTEST_CAT2(d10,10, s5, \"\",0,\"abcde\");\n\t\tTEST_CAT2(d10,10, s5, \"1234\",0,\"1234abcde\");\n\n\t\tTEST_NCAT(d3,3, s1,1, \"1\",0,\"1a\");\n\t\tTEST_NCAT(d5,5, s1,1, \"1\",0,\"1a\");\n\t\tTEST_NCAT(d6,6, s5,5, \"\",0,\"abcde\");\n\t\tTEST_NCAT(d10,10, s5,5, \"\",0,\"abcde\");\n\t\tTEST_NCAT(d10,10, s5,5, \"1234\",0,\"1234abcde\");\n\n\t\tTEST_NCAT(d5,5, s5,0, \"----\",0,\"----\");\n\t\tTEST_NCAT(d5,5, s5,1, \"\",0,\"a\");\n\t\tTEST_NCAT(d5,5, s5,4, \"\",0,\"abcd\");\n\t\tTEST_NCAT(d5,5, s5,2, \"12\",0,\"12ab\");\n\t\tTEST_NCAT(d6,6, s5,10, \"\",0,\"abcde\");\n\t}\n\n\n\tstatic void TEST_PRINTF(char* dst, size_t max_dst_chars, const char* dst_val,\n\t\tint expected_ret, const char* expected_dst, const char* fmt, ...)\n\t{\n\t\tif (dst)\n\t\t\tstrcpy(dst, dst_val);\n\t\tva_list ap;\n\t\tva_start(ap, fmt);\n\t\tint ret = vsprintf_s(dst, max_dst_chars, fmt, ap);\n\t\tva_end(ap);\n\t\tTS_ASSERT_EQUALS(ret, expected_ret);\n\t\tif (dst)\n\t\t\tTS_ASSERT_STR_EQUALS(dst, expected_dst);\n\t}\n\n\tstatic void TEST_WPRINTF(wchar_t* dst, size_t max_dst_chars, const wchar_t* dst_val,\n\t\tint expected_ret, const wchar_t* expected_dst, const wchar_t* fmt, ...)\n\t{\n\t\tif (dst)\n\t\t\twcscpy(dst, dst_val);\n\t\tva_list ap;\n\t\tva_start(ap, fmt);\n\t\tint ret = vswprintf_s(dst, max_dst_chars, fmt, ap);\n\t\tva_end(ap);\n\t\tTS_ASSERT_EQUALS(ret, expected_ret);\n\t\tif (dst)\n\t\t\tTS_ASSERT_WSTR_EQUALS(dst, expected_dst);\n\t}\n\n\tvoid test_printf_overflow()\n\t{\n\t\tTEST_PRINTF(d10,10, s10, 4, \"1234\", \"%d\", 1234);\n\t\tTEST_PRINTF(d10,5, s10, 4, \"1234\", \"%d\", 1234);\n\n\t\tSuppressErrors suppress;\n\t\tTEST_PRINTF(d10,4, s10, -1, \"\", \"%d\", 1234);\n\t\tTEST_PRINTF(d10,3, s10, -1, \"\", \"%d\", 1234);\n\t\tTEST_PRINTF(d10,0, s10, -1, \"abcdefghij\", \"%d\", 1234);\n\t\tTEST_PRINTF(NULL,0, NULL, -1, \"\", \"%d\", 1234);\n\t\tTEST_PRINTF(d10,10, s10, -1, \"abcdefghij\", NULL);\n\t}\n\n\tvoid test_wprintf_overflow()\n\t{\n\t\tTEST_WPRINTF(wd10,10, ws10, 4, L\"1234\", L\"%d\", 1234);\n\t\tTEST_WPRINTF(wd10,5, ws10, 4, L\"1234\", L\"%d\", 1234);\n\n\t\tSuppressErrors suppress;\n\t\tTEST_WPRINTF(wd10,4, ws10, -1, L\"\", L\"%d\", 1234);\n\t\tTEST_WPRINTF(wd10,3, ws10, -1, L\"\", L\"%d\", 1234);\n\t\tTEST_WPRINTF(wd10,0, ws10, -1, L\"abcdefghij\", L\"%d\", 1234);\n\t\tTEST_WPRINTF(NULL,0, NULL, -1, L\"\", L\"%d\", 1234);\n\t\tTEST_WPRINTF(wd10,10, ws10, -1, L\"abcdefghij\", NULL);\n\t}\n\n\tvoid test_printf_strings()\n\t{\n\t\tTEST_PRINTF(d10,10, s10, 3, \"123\", \"%s\", \"123\");\n\t\tTEST_PRINTF(d10,10, s10, 3, \"123\", \"%hs\", \"123\");\n\t\tTEST_PRINTF(d10,10, s10, 3, \"123\", \"%ls\", L\"123\");\n\t}\n\n\tvoid test_wprintf_strings()\n\t{\n\t\tTEST_WPRINTF(wd10,10, ws10, 3, L\"123\", L\"%hs\", \"123\");\n\t\tTEST_WPRINTF(wd10,10, ws10, 3, L\"123\", L\"%ls\", L\"123\");\n\t}\n};\n"
  },
  {
    "path": "fpsgame/gui/tests/test_wchar.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#include \"lib/self_test.h\"\n#include \"lib/utf8.h\"\n\n// (copied from CStr test)\n\nclass Test_wchar : public CxxTest::TestSuite \n{\npublic:\n\tvoid test_utf8_utf16_conversion()\n\t{\n\t\tconst wchar_t chr_utf16[] = {\n\t\t\t0x12,\n\t\t\t0xff,\n\t\t\t0x1234,\n\t\t\t0x3456,\n\t\t\t0x5678,\n\t\t\t0x7890,\n\t\t\t0x9abc,\n\t\t\t0xbcde,\n\t\t\t0xfffd\n\t\t};\n\t\tconst unsigned char chr_utf8[] = {\n\t\t\t0x12,\n\t\t\t0xc3, 0xbf,\n\t\t\t0xe1, 0x88, 0xb4,\n\t\t\t0xe3, 0x91, 0x96,\n\t\t\t0xe5, 0x99, 0xb8,\n\t\t\t0xe7, 0xa2, 0x90,\n\t\t\t0xe9, 0xaa, 0xbc,\n\t\t\t0xeb, 0xb3, 0x9e,\n\t\t\t0xef, 0xbf, 0xbd\n\t\t};\n\t\tconst std::wstring str_utf16(chr_utf16, ARRAY_SIZE(chr_utf16));\n\n\t\tconst std::string str_utf8 = utf8_from_wstring(str_utf16);\n\t\tTS_ASSERT_EQUALS(str_utf8.length(), ARRAY_SIZE(chr_utf8));\n\t\tTS_ASSERT_SAME_DATA(str_utf8.data(), chr_utf8, ARRAY_SIZE(chr_utf8)*sizeof(char));\n\n\t\tconst std::wstring str_utf16b = wstring_from_utf8(str_utf8);\n\t\tTS_ASSERT_WSTR_EQUALS(str_utf16b, str_utf16);\n\t}\n\n\tvoid test_invalid_utf8()\n\t{\n\t\tstruct { const char* utf8; const wchar_t* utf16; } tests[] = {\n\t\t\t{ \"a\\xef\", L\"a\\xfffd\" },\n\t\t\t{ \"b\\xef\\xbf\", L\"b\\xfffd\\xfffd\" },\n\t\t\t{ \"c\\xef\\xbf\\x01\", L\"c\\xfffd\\xfffd\\x0001\" },\n\t\t\t{ \"d\\xffX\\x80Y\\x80\" , L\"d\\xfffdX\\xfffdY\\xfffd\" }\n\t\t};\n\t\tfor (size_t i = 0; i < ARRAY_SIZE(tests); ++i)\n\t\t{\n\t\t\tconst std::string str_utf8(tests[i].utf8);\n\t\t\tconst std::wstring str_utf16(tests[i].utf16);\n\n\t\t\tStatus err;\n\t\t\tconst std::wstring str_utf8to16 = wstring_from_utf8(str_utf8, &err);\n\t\t\tTS_ASSERT_EQUALS(err, ERR::UTF8_INVALID_UTF8);\n\t\t\tTS_ASSERT_EQUALS(str_utf16.length(), str_utf8to16.length());\n\t\t\tTS_ASSERT_SAME_DATA(str_utf8to16.data(), str_utf16.data(), str_utf16.length()*sizeof(wchar_t));\n\t\t}\n\t}\n};\n"
  },
  {
    "path": "fpsgame/gui/tex/tex.cpp",
    "content": "/* Copyright (c) 2015 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * support routines for 2d texture access/writing.\n */\n\n#include \"precompiled.h\"\n#include \"tex.h\"\n\n#include <math.h>\n#include <stdlib.h>\n#include <algorithm>\n\n#include \"lib/timer.h\"\n#include \"lib/bits.h\"\n#include \"lib/allocators/shared_ptr.h\"\n#include \"lib/sysdep/cpu.h\"\n\n#include \"tex_codec.h\"\n\n\nstatic const StatusDefinition texStatusDefinitions[] = {\n\t{ ERR::TEX_FMT_INVALID, L\"Invalid/unsupported texture format\" },\n\t{ ERR::TEX_INVALID_COLOR_TYPE, L\"Invalid color type\" },\n\t{ ERR::TEX_NOT_8BIT_PRECISION, L\"Not 8-bit channel precision\" },\n\t{ ERR::TEX_INVALID_LAYOUT, L\"Unsupported texel layout, e.g. right-to-left\" },\n\t{ ERR::TEX_COMPRESSED, L\"Unsupported texture compression\" },\n\t{ WARN::TEX_INVALID_DATA, L\"Warning: invalid texel data encountered\" },\n\t{ ERR::TEX_INVALID_SIZE, L\"Texture size is incorrect\" },\n\t{ INFO::TEX_CODEC_CANNOT_HANDLE, L\"Texture codec cannot handle the given format\" }\n};\nSTATUS_ADD_DEFINITIONS(texStatusDefinitions);\n\n\n//-----------------------------------------------------------------------------\n// validation\n//-----------------------------------------------------------------------------\n\n// be careful not to use other tex_* APIs here because they call us.\nStatus Tex::validate() const\n{\n\tif(m_Flags & TEX_UNDEFINED_FLAGS)\n\t\tWARN_RETURN(ERR::_1);\n\n\t// pixel data (only check validity if the image is still in memory;\n\t// ogl_tex frees the data after uploading to GL)\n\tif(m_Data)\n\t{\n\t\t// file size smaller than header+pixels.\n\t\t// possible causes: texture file header is invalid,\n\t\t// or file wasn't loaded completely.\n\t\tif(m_DataSize < m_Ofs + m_Width*m_Height*m_Bpp/8)\n\t\t\tWARN_RETURN(ERR::_2);\n\t}\n\n\t// bits per pixel\n\t// (we don't bother checking all values; a sanity check is enough)\n\tif(m_Bpp % 4 || m_Bpp > 32)\n\t\tWARN_RETURN(ERR::_3);\n\n\t// flags\n\t// .. DXT value\n\tconst size_t dxt = m_Flags & TEX_DXT;\n\tif(dxt != 0 && dxt != 1 && dxt != DXT1A && dxt != 3 && dxt != 5)\n\t\tWARN_RETURN(ERR::_4);\n\t// .. orientation\n\tconst size_t orientation = m_Flags & TEX_ORIENTATION;\n\tif(orientation == (TEX_BOTTOM_UP|TEX_TOP_DOWN))\n\t\tWARN_RETURN(ERR::_5);\n\n\treturn INFO::OK;\n}\n\n#define CHECK_TEX(t) RETURN_STATUS_IF_ERR((t->validate()))\n\n\n// check if the given texture format is acceptable: 8bpp grey,\n// 24bpp color or 32bpp color+alpha (BGR / upside down are permitted).\n// basically, this is the \"plain\" format understood by all codecs and\n// tex_codec_plain_transform.\nStatus tex_validate_plain_format(size_t bpp, size_t flags)\n{\n\tconst bool alpha   = (flags & TEX_ALPHA  ) != 0;\n\tconst bool grey    = (flags & TEX_GREY   ) != 0;\n\tconst bool dxt     = (flags & TEX_DXT    ) != 0;\n\tconst bool mipmaps = (flags & TEX_MIPMAPS) != 0;\n\n\tif(dxt || mipmaps)\n\t\tWARN_RETURN(ERR::TEX_FMT_INVALID);\n\n\t// grey must be 8bpp without alpha, or it's invalid.\n\tif(grey)\n\t{\n\t\tif(bpp == 8 && !alpha)\n\t\t\treturn INFO::OK;\n\t\tWARN_RETURN(ERR::TEX_FMT_INVALID);\n\t}\n\n\tif(bpp == 24 && !alpha)\n\t\treturn INFO::OK;\n\tif(bpp == 32 && alpha)\n\t\treturn INFO::OK;\n\n\tWARN_RETURN(ERR::TEX_FMT_INVALID);\n}\n\n\n//-----------------------------------------------------------------------------\n// mipmaps\n//-----------------------------------------------------------------------------\n\nvoid tex_util_foreach_mipmap(size_t w, size_t h, size_t bpp, const u8* pixels, int levels_to_skip, size_t data_padding, MipmapCB cb, void* RESTRICT cbData)\n{\n\tENSURE(levels_to_skip >= 0 || levels_to_skip == TEX_BASE_LEVEL_ONLY);\n\n\tsize_t level_w = w, level_h = h;\n\tconst u8* level_data = pixels;\n\n\t// we iterate through the loop (necessary to skip over image data),\n\t// but do not actually call back until the requisite number of\n\t// levels have been skipped (i.e. level == 0).\n\tint level = (levels_to_skip == TEX_BASE_LEVEL_ONLY)? 0 : -levels_to_skip;\n\n\t// until at level 1x1:\n\tfor(;;)\n\t{\n\t\t// used to skip past this mip level in <data>\n\t\tconst size_t level_dataSize = (size_t)(round_up(level_w, data_padding) * round_up(level_h, data_padding) * bpp/8);\n\n\t\tif(level >= 0)\n\t\t\tcb((size_t)level, level_w, level_h, level_data, level_dataSize, cbData);\n\n\t\tlevel_data += level_dataSize;\n\n\t\t// 1x1 reached - done\n\t\tif(level_w == 1 && level_h == 1)\n\t\t\tbreak;\n\t\tlevel_w /= 2;\n\t\tlevel_h /= 2;\n\t\t// if the texture is non-square, one of the dimensions will become\n\t\t// 0 before the other. to satisfy OpenGL's expectations, change it\n\t\t// back to 1.\n\t\tif(level_w == 0) level_w = 1;\n\t\tif(level_h == 0) level_h = 1;\n\t\tlevel++;\n\n\t\t// special case: no mipmaps, we were only supposed to call for\n\t\t// the base level\n\t\tif(levels_to_skip == TEX_BASE_LEVEL_ONLY)\n\t\t\tbreak;\n\t}\n}\n\n\nstruct CreateLevelData\n{\n\tsize_t num_components;\n\n\tsize_t prev_level_w;\n\tsize_t prev_level_h;\n\tconst u8* prev_level_data;\n\tsize_t prev_level_dataSize;\n};\n\n// uses 2x2 box filter\nstatic void create_level(size_t level, size_t level_w, size_t level_h, const u8* RESTRICT level_data, size_t level_dataSize, void* RESTRICT cbData)\n{\n\tCreateLevelData* cld = (CreateLevelData*)cbData;\n\tconst size_t src_w = cld->prev_level_w;\n\tconst size_t src_h = cld->prev_level_h;\n\tconst u8* src = cld->prev_level_data;\n\tu8* dst = (u8*)level_data;\n\n\t// base level - must be copied over from source buffer\n\tif(level == 0)\n\t{\n\t\tENSURE(level_dataSize == cld->prev_level_dataSize);\n\t\tmemcpy(dst, src, level_dataSize);\n\t}\n\telse\n\t{\n\t\tconst size_t num_components = cld->num_components;\n\t\tconst size_t dx = num_components, dy = dx*src_w;\n\n\t\t// special case: image is too small for 2x2 filter\n\t\tif(cld->prev_level_w == 1 || cld->prev_level_h == 1)\n\t\t{\n\t\t\t// image is either a horizontal or vertical line.\n\t\t\t// their memory layout is the same (packed pixels), so no special\n\t\t\t// handling is needed; just pick max dimension.\n\t\t\tfor(size_t y = 0; y < std::max(src_w, src_h); y += 2)\n\t\t\t{\n\t\t\t\tfor(size_t i = 0; i < num_components; i++)\n\t\t\t\t{\n\t\t\t\t\t*dst++ = (src[0]+src[dx]+1)/2;\n\t\t\t\t\tsrc += 1;\n\t\t\t\t}\n\n\t\t\t\tsrc += dx;\t// skip to next pixel (since box is 2x2)\n\t\t\t}\n\t\t}\n\t\t// normal\n\t\telse\n\t\t{\n\t\t\tfor(size_t y = 0; y < src_h; y += 2)\n\t\t\t{\n\t\t\t\tfor(size_t x = 0; x < src_w; x += 2)\n\t\t\t\t{\n\t\t\t\t\tfor(size_t i = 0; i < num_components; i++)\n\t\t\t\t\t{\n\t\t\t\t\t\t*dst++ = (src[0]+src[dx]+src[dy]+src[dx+dy]+2)/4;\n\t\t\t\t\t\tsrc += 1;\n\t\t\t\t\t}\n\n\t\t\t\t\tsrc += dx;\t// skip to next pixel (since box is 2x2)\n\t\t\t\t}\n\n\t\t\t\tsrc += dy;\t// skip to next row (since box is 2x2)\n\t\t\t}\n\t\t}\n\n\t\tENSURE(dst == level_data + level_dataSize);\n\t\tENSURE(src == cld->prev_level_data + cld->prev_level_dataSize);\n\t}\n\n\tcld->prev_level_data = level_data;\n\tcld->prev_level_dataSize = level_dataSize;\n\tcld->prev_level_w = level_w;\n\tcld->prev_level_h = level_h;\n}\n\n\nstatic Status add_mipmaps(Tex* t, size_t w, size_t h, size_t bpp, void* newData, size_t dataSize)\n{\n\t// this code assumes the image is of POT dimension; we don't\n\t// go to the trouble of implementing image scaling because\n\t// the only place this is used (ogl_tex_upload) requires POT anyway.\n\tif(!is_pow2(w) || !is_pow2(h))\n\t\tWARN_RETURN(ERR::TEX_INVALID_SIZE);\n\tt->m_Flags |= TEX_MIPMAPS;\t// must come before tex_img_size!\n\tconst size_t mipmap_size = t->img_size();\n\tshared_ptr<u8> mipmapData;\n\tAllocateAligned(mipmapData, mipmap_size);\n\tCreateLevelData cld = { bpp/8, w, h, (const u8*)newData, dataSize };\n\ttex_util_foreach_mipmap(w, h, bpp, mipmapData.get(), 0, 1, create_level, &cld);\n\tt->m_Data = mipmapData;\n\tt->m_DataSize = mipmap_size;\n\tt->m_Ofs = 0;\n\n\treturn INFO::OK;\n}\n\n\n//-----------------------------------------------------------------------------\n// pixel format conversion (transformation)\n//-----------------------------------------------------------------------------\n\nTIMER_ADD_CLIENT(tc_plain_transform);\n\n// handles BGR and row flipping in \"plain\" format (see below).\n//\n// called by codecs after they get their format-specific transforms out of\n// the way. note that this approach requires several passes over the image,\n// but is much easier to maintain than providing all<->all conversion paths.\n//\n// somewhat optimized (loops are hoisted, cache associativity accounted for)\nstatic Status plain_transform(Tex* t, size_t transforms)\n{\nTIMER_ACCRUE(tc_plain_transform);\n\n\t// (this is also called directly instead of through ogl_tex, so\n\t// we need to validate)\n\tCHECK_TEX(t);\n\n\t// extract texture info\n\tconst size_t w = t->m_Width, h = t->m_Height, bpp = t->m_Bpp;\n\tconst size_t flags = t->m_Flags;\n\tu8* const srcStorage = t->get_data();\n\n\t// sanity checks (not errors, we just can't handle these cases)\n\t// .. unknown transform\n\tif(transforms & ~(TEX_BGR|TEX_ORIENTATION|TEX_MIPMAPS|TEX_ALPHA))\n\t\treturn INFO::TEX_CODEC_CANNOT_HANDLE;\n\t// .. data is not in \"plain\" format\n\tRETURN_STATUS_IF_ERR(tex_validate_plain_format(bpp, flags));\n\t// .. nothing to do\n\tif(!transforms)\n\t\treturn INFO::OK;\n\n\tconst size_t srcSize = t->img_size();\n\tsize_t dstSize = srcSize;\n\n\tif(transforms & TEX_ALPHA)\n\t{\n\t\t// add alpha channel\n\t\tif(bpp == 24)\n\t\t{\n\t\t\tdstSize = (srcSize / 3) * 4;\n\t\t\tt->m_Bpp = 32;\n\t\t}\n\t\t// remove alpha channel\n\t\telse if(bpp == 32)\n\t\t{\n\t\t\treturn INFO::TEX_CODEC_CANNOT_HANDLE;\n\t\t}\n\t\t// can't have alpha with grayscale\n\t\telse\n\t\t{\n\t\t\treturn INFO::TEX_CODEC_CANNOT_HANDLE;\n\t\t}\n\t}\n\n\t// allocate copy of the image data.\n\t// rationale: L1 cache is typically A2 => swapping in-place with a\n\t// line buffer leads to thrashing. we'll assume the whole texture*2\n\t// fits in cache, allocate a copy, and transfer directly from there.\n\t//\n\t// this is necessary even when not flipping because the initial data\n\t// is read-only.\n\tshared_ptr<u8> dstStorage;\n\tAllocateAligned(dstStorage, dstSize);\n\n\t// setup row source/destination pointers (simplifies outer loop)\n\tu8* dst = (u8*)dstStorage.get();\n\tconst u8* src;\n\tconst size_t pitch = w * bpp/8;\t// source bpp (not necessarily dest bpp)\n\t// .. avoid y*pitch multiply in row loop; instead, add row_ofs.\n\tssize_t row_ofs = (ssize_t)pitch;\n\n\t// flipping rows (0,1,2 -> 2,1,0)\n\tif(transforms & TEX_ORIENTATION)\n\t{\n\t\tsrc = (const u8*)srcStorage+srcSize-pitch;\t// last row\n\t\trow_ofs = -(ssize_t)pitch;\n\t}\n\t// adding/removing alpha channel (can't convert in-place)\n\telse if(transforms & TEX_ALPHA)\n\t{\n\t\tsrc = (const u8*)srcStorage;\n\t}\n\t// do other transforms in-place\n\telse\n\t{\n\t\tsrc = (const u8*)dstStorage.get();\n\t\tmemcpy(dstStorage.get(), srcStorage, srcSize);\n\t}\n\n\t// no conversion necessary\n\tif(!(transforms & (TEX_BGR | TEX_ALPHA)))\n\t{\n\t\tif(src != dst)\t// avoid overlapping memcpy if not flipping rows\n\t\t{\n\t\t\tfor(size_t y = 0; y < h; y++)\n\t\t\t{\n\t\t\t\tmemcpy(dst, src, pitch);\n\t\t\t\tdst += pitch;\n\t\t\t\tsrc += row_ofs;\n\t\t\t}\n\t\t}\n\t}\n\t// RGB -> BGRA, BGR -> RGBA\n\telse if(bpp == 24 && (transforms & TEX_ALPHA) && (transforms & TEX_BGR))\n\t{\n\t\tfor(size_t y = 0; y < h; y++)\n\t\t{\n\t\t\tfor(size_t x = 0; x < w; x++)\n\t\t\t{\n\t\t\t\t// need temporaries in case src == dst (i.e. not flipping)\n\t\t\t\tconst u8 b = src[0], g = src[1], r = src[2];\n\t\t\t\tdst[0] = r; dst[1] = g; dst[2] = b; dst[3] = 0xFF;\n\t\t\t\tdst += 4;\n\t\t\t\tsrc += 3;\n\t\t\t}\n\t\t\tsrc += row_ofs - pitch;\t// flip? previous row : stay\n\t\t}\n\t}\n\t// RGB -> RGBA, BGR -> BGRA\n\telse if(bpp == 24 && (transforms & TEX_ALPHA) && !(transforms & TEX_BGR))\n\t{\n\t\tfor(size_t y = 0; y < h; y++)\n\t\t{\n\t\t\tfor(size_t x = 0; x < w; x++)\n\t\t\t{\n\t\t\t\t// need temporaries in case src == dst (i.e. not flipping)\n\t\t\t\tconst u8 r = src[0], g = src[1], b = src[2];\n\t\t\t\tdst[0] = r; dst[1] = g; dst[2] = b; dst[3] = 0xFF;\n\t\t\t\tdst += 4;\n\t\t\t\tsrc += 3;\n\t\t\t}\n\t\t\tsrc += row_ofs - pitch;\t// flip? previous row : stay\n\t\t}\n\t}\n\t// RGB <-> BGR\n\telse if(bpp == 24 && !(transforms & TEX_ALPHA))\n\t{\n\t\tfor(size_t y = 0; y < h; y++)\n\t\t{\n\t\t\tfor(size_t x = 0; x < w; x++)\n\t\t\t{\n\t\t\t\t// need temporaries in case src == dst (i.e. not flipping)\n\t\t\t\tconst u8 b = src[0], g = src[1], r = src[2];\n\t\t\t\tdst[0] = r; dst[1] = g; dst[2] = b;\n\t\t\t\tdst += 3;\n\t\t\t\tsrc += 3;\n\t\t\t}\n\t\t\tsrc += row_ofs - pitch;\t// flip? previous row : stay\n\t\t}\n\t}\n\t// RGBA <-> BGRA\n\telse if(bpp == 32 && !(transforms & TEX_ALPHA))\n\t{\n\t\tfor(size_t y = 0; y < h; y++)\n\t\t{\n\t\t\tfor(size_t x = 0; x < w; x++)\n\t\t\t{\n\t\t\t\t// need temporaries in case src == dst (i.e. not flipping)\n\t\t\t\tconst u8 b = src[0], g = src[1], r = src[2], a = src[3];\n\t\t\t\tdst[0] = r; dst[1] = g; dst[2] = b; dst[3] = a;\n\t\t\t\tdst += 4;\n\t\t\t\tsrc += 4;\n\t\t\t}\n\t\t\tsrc += row_ofs - pitch;\t// flip? previous row : stay\n\t\t}\n\t}\n\telse\n\t{\n\t\tdebug_warn(L\"unsupported transform\");\n\t\treturn INFO::TEX_CODEC_CANNOT_HANDLE;\n\t}\n\n\tt->m_Data = dstStorage;\n\tt->m_DataSize = dstSize;\n\tt->m_Ofs = 0;\n\n\tif(!(t->m_Flags & TEX_MIPMAPS) && transforms & TEX_MIPMAPS)\n\t\tRETURN_STATUS_IF_ERR(add_mipmaps(t, w, h, bpp, dstStorage.get(), dstSize));\n\n\tCHECK_TEX(t);\n\treturn INFO::OK;\n}\n\n\nTIMER_ADD_CLIENT(tc_transform);\n\n// change the pixel format by flipping the state of all TEX_* flags\n// that are set in transforms.\nStatus Tex::transform(size_t transforms)\n{\n\tTIMER_ACCRUE(tc_transform);\n\tCHECK_TEX(this);\n\n\tconst size_t target_flags = m_Flags ^ transforms;\n\tsize_t remaining_transforms;\n\tfor(;;)\n\t{\n\t\tremaining_transforms = target_flags ^ m_Flags;\n\t\t// we're finished (all required transforms have been done)\n\t\tif(remaining_transforms == 0)\n\t\t\treturn INFO::OK;\n\n\t\tStatus ret = tex_codec_transform(this, remaining_transforms);\n\t\tif(ret != INFO::OK)\n\t\t\tbreak;\n\t}\n\n\t// last chance\n\tRETURN_STATUS_IF_ERR(plain_transform(this, remaining_transforms));\n\treturn INFO::OK;\n}\n\n\n// change the pixel format to the new format specified by <new_flags>.\n// (note: this is equivalent to transform(t, t->flags^new_flags).\nStatus Tex::transform_to(size_t new_flags)\n{\n\t// transform takes care of validating\n\tconst size_t transforms = m_Flags ^ new_flags;\n\treturn transform(transforms);\n}\n\n\n//-----------------------------------------------------------------------------\n// image orientation\n//-----------------------------------------------------------------------------\n\n// see \"Default Orientation\" in docs.\n\nstatic int global_orientation = TEX_TOP_DOWN;\n\n// set the orientation (either TEX_BOTTOM_UP or TEX_TOP_DOWN) to which\n// all loaded images will automatically be converted\n// (excepting file formats that don't specify their orientation, i.e. DDS).\nvoid tex_set_global_orientation(int o)\n{\n\tENSURE(o == TEX_TOP_DOWN || o == TEX_BOTTOM_UP);\n\tglobal_orientation = o;\n}\n\n\nstatic void flip_to_global_orientation(Tex* t)\n{\n\t// (can't use normal CHECK_TEX due to void return)\n\tWARN_IF_ERR(t->validate());\n\n\tsize_t orientation = t->m_Flags & TEX_ORIENTATION;\n\t// if codec knows which way around the image is (i.e. not DDS):\n\tif(orientation)\n\t{\n\t\t// flip image if necessary\n\t\tsize_t transforms = orientation ^ global_orientation;\n\t\tWARN_IF_ERR(plain_transform(t, transforms));\n\t}\n\n\t// indicate image is at global orientation. this is still done even\n\t// if the codec doesn't know: the default orientation should be chosen\n\t// to make that work correctly (see \"Default Orientation\" in docs).\n\tt->m_Flags = (t->m_Flags & ~TEX_ORIENTATION) | global_orientation;\n\n\t// (can't use normal CHECK_TEX due to void return)\n\tWARN_IF_ERR(t->validate());\n}\n\n\n// indicate if the orientation specified by <src_flags> matches\n// dst_orientation (if the latter is 0, then the global_orientation).\n// (we ask for src_flags instead of src_orientation so callers don't\n// have to mask off TEX_ORIENTATION)\nbool tex_orientations_match(size_t src_flags, size_t dst_orientation)\n{\n\tconst size_t src_orientation = src_flags & TEX_ORIENTATION;\n\tif(dst_orientation == 0)\n\t\tdst_orientation = global_orientation;\n\treturn (src_orientation == dst_orientation);\n}\n\n\n//-----------------------------------------------------------------------------\n// misc. API\n//-----------------------------------------------------------------------------\n\n// indicate if <filename>'s extension is that of a texture format\n// supported by Tex::load. case-insensitive.\n//\n// rationale: Tex::load complains if the given file is of an\n// unsupported type. this API allows users to preempt that warning\n// (by checking the filename themselves), and also provides for e.g.\n// enumerating only images in a file picker.\n// an alternative might be a flag to suppress warning about invalid files,\n// but this is open to misuse.\nbool tex_is_known_extension(const VfsPath& pathname)\n{\n\tconst ITexCodec* dummy;\n\t// found codec for it => known extension\n\tconst OsPath extension = pathname.Extension();\n\tif(tex_codec_for_filename(extension, &dummy) == INFO::OK)\n\t\treturn true;\n\n\treturn false;\n}\n\n\n// store the given image data into a Tex object; this will be as if\n// it had been loaded via Tex::load.\n//\n// rationale: support for in-memory images is necessary for\n//   emulation of glCompressedTexImage2D and useful overall.\n//   however, we don't want to  provide an alternate interface for each API;\n//   these would have to be changed whenever fields are added to Tex.\n//   instead, provide one entry point for specifying images.\n//\n// we need only add bookkeeping information and \"wrap\" it in\n// our Tex struct, hence the name.\nStatus Tex::wrap(size_t w, size_t h, size_t bpp, size_t flags, const shared_ptr<u8>& data, size_t ofs)\n{\n\tm_Width    = w;\n\tm_Height   = h;\n\tm_Bpp      = bpp;\n\tm_Flags    = flags;\n\tm_Data     = data;\n\tm_DataSize = ofs + w*h*bpp/8;\n\tm_Ofs      = ofs;\n\n\tCHECK_TEX(this);\n\treturn INFO::OK;\n}\n\n\n// free all resources associated with the image and make further\n// use of it impossible.\nvoid Tex::free()\n{\n\t// do not validate - this is called from Tex::load if loading\n\t// failed, so not all fields may be valid.\n\n\tm_Data.reset();\n\n\t// do not zero out the fields! that could lead to trouble since\n\t// ogl_tex_upload followed by ogl_tex_free is legit, but would\n\t// cause OglTex_validate to fail (since its Tex.w is == 0).\n}\n\n\n//-----------------------------------------------------------------------------\n// getters\n//-----------------------------------------------------------------------------\n\n// returns a pointer to the image data (pixels), taking into account any\n// header(s) that may come before it.\nu8* Tex::get_data()\n{\n\t// (can't use normal CHECK_TEX due to u8* return value)\n\tWARN_IF_ERR(validate());\n\n\tu8* p = m_Data.get();\n\tif(!p)\n\t\treturn 0;\n\treturn p + m_Ofs;\n}\n\n// returns color of 1x1 mipmap level\nu32 Tex::get_average_color() const\n{\n\t// require mipmaps\n\tif(!(m_Flags & TEX_MIPMAPS))\n\t\treturn 0;\n\n\t// find the total size of image data\n\tsize_t size = img_size();\n\n\t// compute the size of the last (1x1) mipmap level\n\tconst size_t data_padding = (m_Flags & TEX_DXT)? 4 : 1;\n\tsize_t last_level_size = (size_t)(data_padding * data_padding * m_Bpp/8);\n\n\t// construct a new texture based on the current one,\n\t// but only include the last mipmap level\n\t// do this so that we can use the general conversion methods for the pixel data\n\tTex basetex = *this;\n\tuint8_t *data = new uint8_t[last_level_size];\n\tmemcpy(data, m_Data.get() + m_Ofs + size - last_level_size, last_level_size);\n\tshared_ptr<uint8_t> sdata(data, ArrayDeleter());\n\tbasetex.wrap(1, 1, m_Bpp, m_Flags, sdata, 0);\n\n\t// convert to BGRA\n\tWARN_IF_ERR(basetex.transform_to(TEX_BGR | TEX_ALPHA));\n\n\t// extract components into u32\n\tENSURE(basetex.m_DataSize >= basetex.m_Ofs+4);\n\tu8 b = basetex.m_Data.get()[basetex.m_Ofs];\n\tu8 g = basetex.m_Data.get()[basetex.m_Ofs+1];\n\tu8 r = basetex.m_Data.get()[basetex.m_Ofs+2];\n\tu8 a = basetex.m_Data.get()[basetex.m_Ofs+3];\n\treturn b + (g << 8) + (r << 16) + (a << 24);\n}\n\n\nstatic void add_level_size(size_t UNUSED(level), size_t UNUSED(level_w), size_t UNUSED(level_h), const u8* RESTRICT UNUSED(level_data), size_t level_dataSize, void* RESTRICT cbData)\n{\n\tsize_t* ptotal_size = (size_t*)cbData;\n\t*ptotal_size += level_dataSize;\n}\n\n// return total byte size of the image pixels. (including mipmaps!)\n// this is preferable to calculating manually because it's\n// less error-prone (e.g. confusing bits_per_pixel with bytes).\nsize_t Tex::img_size() const\n{\n\t// (can't use normal CHECK_TEX due to size_t return value)\n\tWARN_IF_ERR(validate());\n\n\tconst int levels_to_skip = (m_Flags & TEX_MIPMAPS)? 0 : TEX_BASE_LEVEL_ONLY;\n\tconst size_t data_padding = (m_Flags & TEX_DXT)? 4 : 1;\n\tsize_t out_size = 0;\n\ttex_util_foreach_mipmap(m_Width, m_Height, m_Bpp, 0, levels_to_skip, data_padding, add_level_size, &out_size);\n\treturn out_size;\n}\n\n\n// return the minimum header size (i.e. offset to pixel data) of the\n// file format indicated by <fn>'s extension (that is all it need contain:\n// e.g. \".bmp\"). returns 0 on error (i.e. no codec found).\n// this can be used to optimize calls to tex_write: when allocating the\n// buffer that will hold the image, allocate this much extra and\n// pass the pointer as base+hdr_size. this allows writing the header\n// directly into the output buffer and makes for zero-copy IO.\nsize_t tex_hdr_size(const VfsPath& filename)\n{\n\tconst ITexCodec* c;\n\t\n\tconst OsPath extension = filename.Extension();\n\tWARN_RETURN_STATUS_IF_ERR(tex_codec_for_filename(extension, &c));\n\treturn c->hdr_size(0);\n}\n\n\n//-----------------------------------------------------------------------------\n// read/write from memory and disk\n//-----------------------------------------------------------------------------\n\nStatus Tex::decode(const shared_ptr<u8>& Data, size_t DataSize)\n{\n\tconst ITexCodec* c;\n\tRETURN_STATUS_IF_ERR(tex_codec_for_header(Data.get(), DataSize, &c));\n\n\t// make sure the entire header is available\n\tconst size_t min_hdr_size = c->hdr_size(0);\n\tif(DataSize < min_hdr_size)\n\t\tWARN_RETURN(ERR::TEX_INCOMPLETE_HEADER);\n\tconst size_t hdr_size = c->hdr_size(Data.get());\n\tif(DataSize < hdr_size)\n\t\tWARN_RETURN(ERR::TEX_INCOMPLETE_HEADER);\n\n\tm_Data = Data;\n\tm_DataSize = DataSize;\n\tm_Ofs = hdr_size;\n\n\tRETURN_STATUS_IF_ERR(c->decode((rpU8)Data.get(), DataSize, this));\n\n\t// sanity checks\n\tif(!m_Width || !m_Height || m_Bpp > 32)\n\t\tWARN_RETURN(ERR::TEX_FMT_INVALID);\n\tif(m_DataSize < m_Ofs + img_size())\n\t\tWARN_RETURN(ERR::TEX_INVALID_SIZE);\n\n\tflip_to_global_orientation(this);\n\n\tCHECK_TEX(this);\n\n\treturn INFO::OK;\n}\n\n\nStatus Tex::encode(const OsPath& extension, DynArray* da)\n{\n\tCHECK_TEX(this);\n\tWARN_RETURN_STATUS_IF_ERR(tex_validate_plain_format(m_Bpp, m_Flags));\n\n\t// we could be clever here and avoid the extra alloc if our current\n\t// memory block ensued from the same kind of texture file. this is\n\t// most likely the case if in_img == get_data() + c->hdr_size(0).\n\t// this would make for zero-copy IO.\n\n\tconst size_t max_out_size = img_size()*4 + 256*KiB;\n\tRETURN_STATUS_IF_ERR(da_alloc(da, max_out_size));\n\n\tconst ITexCodec* c;\n\tWARN_RETURN_STATUS_IF_ERR(tex_codec_for_filename(extension, &c));\n\n\t// encode into <da>\n\tStatus err = c->encode(this, da);\n\tif(err < 0)\n\t{\n\t\t(void)da_free(da);\n\t\tWARN_RETURN(err);\n\t}\n\n\treturn INFO::OK;\n}\n"
  },
  {
    "path": "fpsgame/gui/tex/tex.h",
    "content": "/* Copyright (c) 2014 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * read/write 2d texture files; allows conversion between pixel formats\n * and automatic orientation correction.\n */\n\n/**\n\nIntroduction\n------------\n\nThis module allows reading/writing 2d images in various file formats and\nencapsulates them in Tex objects.\nIt supports converting between pixel formats; this is to an extent done\nautomatically when reading/writing. Provision is also made for flipping\nall images to a default orientation.\n\n\nFormat Conversion\n-----------------\n\nImage file formats have major differences in their native pixel format:\nsome store in BGR order, or have rows arranged bottom-up.\nWe must balance runtime cost/complexity and convenience for the\napplication (not dumping the entire problem on its lap).\nThat means rejecting really obscure formats (e.g. right-to-left pixels),\nbut converting everything else to uncompressed RGB \"plain\" format\nexcept where noted in enum TexFlags (1).\n\nNote: conversion is implemented as a pipeline: e.g. \"DDS decompress +\nvertical flip\" would be done by decompressing to RGB (DDS codec) and then\nflipping (generic transform). This is in contrast to all<->all\nconversion paths: that would be much more complex, if more efficient.\n\nSince any kind of preprocessing at runtime is undesirable (the absolute\npriority is minimizing load time), prefer file formats that are\nclose to the final pixel format.\n\n1) one of the exceptions is S3TC compressed textures. glCompressedTexImage2D\n   requires these be passed in their original format; decompressing would be\n   counterproductive. In this and similar cases, TexFlags indicates such\n   deviations from the plain format.\n\n\nDefault Orientation\n-------------------\n\nAfter loading, all images (except DDS, because its orientation is\nindeterminate) are automatically converted to the global row\norientation: top-down or bottom-up, as specified by\ntex_set_global_orientation. If that isn't called, the default is top-down\nto match Photoshop's DDS output (since this is meant to be the\nno-preprocessing-required optimized format).\nReasons to change it might be to speed up loading bottom-up\nBMP or TGA images, or to match OpenGL's convention for convenience;\nhowever, be aware of the abovementioned issues with DDS.\n\nRationale: it is not expected that this will happen at the renderer layer\n(a 'flip all texcoords' flag is too much trouble), so the\napplication would have to do the same anyway. By taking care of it here,\nwe unburden the app and save time, since some codecs (e.g. PNG) can\nflip for free when loading.\n\n\nCodecs / IO Implementation\n--------------------------\n\nTo ease adding support for new formats, they are organized as codecs.\nThe interface aims to minimize code duplication, so it's organized\nfollowing the principle of \"Template Method\" - this module both\ncalls into codecs, and provides helper functions that they use.\n\nIO is done via VFS, but the codecs are decoupled from this and\nwork with memory buffers. Access to them is endian-safe.\n\nWhen \"writing\", the image is put into an expandable memory region.\nThis supports external libraries like libpng that do not know the\noutput size beforehand, but avoids the need for a buffer between\nlibrary and IO layer. Read and write are zero-copy.\n\n**/\n\n#ifndef INCLUDED_TEX\n#define INCLUDED_TEX\n\n#include \"lib/res/handle.h\"\n#include \"lib/os_path.h\"\n#include \"lib/file/vfs/vfs_path.h\"\n#include \"lib/allocators/dynarray.h\"\n\n\nnamespace ERR\n{\n\tconst Status TEX_UNKNOWN_FORMAT      = -120100;\n\tconst Status TEX_INCOMPLETE_HEADER   = -120101;\n\tconst Status TEX_FMT_INVALID         = -120102;\n\tconst Status TEX_INVALID_COLOR_TYPE  = -120103;\n\tconst Status TEX_NOT_8BIT_PRECISION  = -120104;\n\tconst Status TEX_INVALID_LAYOUT      = -120105;\n\tconst Status TEX_COMPRESSED          = -120106;\n\tconst Status TEX_INVALID_SIZE        = -120107;\n}\n\nnamespace WARN\n{\n\tconst Status TEX_INVALID_DATA        = +120108;\n}\n\nnamespace INFO\n{\n\tconst Status TEX_CODEC_CANNOT_HANDLE = +120109;\n}\n\n\n/**\n * flags describing the pixel format. these are to be interpreted as\n * deviations from \"plain\" format, i.e. uncompressed RGB.\n **/\nenum TexFlags\n{\n\t/**\n\t * flags & TEX_DXT is a field indicating compression.\n\t * if 0, the texture is uncompressed;\n\t * otherwise, it holds the S3TC type: 1,3,5 or DXT1A.\n\t * not converted by default - glCompressedTexImage2D receives\n\t * the compressed data.\n\t **/\n\tTEX_DXT = 0x7,\t // mask\n\n\t/**\n\t * we need a special value for DXT1a to avoid having to consider\n\t * flags & TEX_ALPHA to determine S3TC type.\n\t * the value is arbitrary; do not rely on it!\n\t **/\n\tDXT1A = 7,\n\n\t/**\n\t * indicates B and R pixel components are exchanged. depending on\n\t * flags & TEX_ALPHA or bpp, this means either BGR or BGRA.\n\t * not converted by default - it's an acceptable format for OpenGL.\n\t **/\n\tTEX_BGR = 0x08,\n\n\t/**\n\t * indicates the image contains an alpha channel. this is set for\n\t * your convenience - there are many formats containing alpha and\n\t * divining this information from them is hard.\n\t * (conversion is not applicable here)\n\t **/\n\tTEX_ALPHA = 0x10,\n\n\t/**\n\t * indicates the image is 8bpp greyscale. this is required to\n\t * differentiate between alpha-only and intensity formats.\n\t * not converted by default - it's an acceptable format for OpenGL.\n\t **/\n\tTEX_GREY = 0x20,\n\n\t/**\n\t * flags & TEX_ORIENTATION is a field indicating orientation,\n\t * i.e. in what order the pixel rows are stored.\n\t *\n\t * tex_load always sets this to the global orientation\n\t * (and flips the image accordingly to match).\n\t * texture codecs may in intermediate steps during loading set this\n\t * to 0 if they don't know which way around they are (e.g. DDS),\n\t * or to whatever their file contains.\n\t **/\n\tTEX_BOTTOM_UP = 0x40,\n\tTEX_TOP_DOWN  = 0x80,\n\tTEX_ORIENTATION = TEX_BOTTOM_UP|TEX_TOP_DOWN,\t /// mask\n\n\t/**\n\t * indicates the image data includes mipmaps. they are stored from lowest\n\t * to highest (1x1), one after the other.\n\t * (conversion is not applicable here)\n\t **/\n\tTEX_MIPMAPS = 0x100,\n\n\tTEX_UNDEFINED_FLAGS = ~0x1FF\n};\n\n/**\n * stores all data describing an image.\n * we try to minimize size, since this is stored in OglTex resources\n * (which are big and pushing the h_mgr limit).\n **/\nstruct Tex\n{\n\t/**\n\t * file buffer or image data. note: during the course of transforms\n\t * (which may occur when being loaded), this may be replaced with\n\t * a new buffer (e.g. if decompressing file contents).\n\t **/\n\tshared_ptr<u8> m_Data;\n\n\tsize_t m_DataSize;\n\n\t/**\n\t * offset to image data in file. this is required since\n\t * tex_get_data needs to return the pixels, but data\n\t * returns the actual file buffer. zero-copy load and\n\t * write-back to file is also made possible.\n\t **/\n\tsize_t m_Ofs;\n\n\tsize_t m_Width;\n\tsize_t m_Height;\n\tsize_t m_Bpp;\n\n\t/// see TexFlags and \"Format Conversion\" in docs.\n\tsize_t m_Flags;\n\n\t~Tex()\n\t{\n\t\tfree();\n\t}\n\n\t/**\n\t * Is the texture object valid and self-consistent?\n\t * \n\t * @return Status\n\t **/\n\tStatus validate() const;\n\n\t/**\n\t * free all resources associated with the image and make further\n\t * use of it impossible.\n\t *\n\t * @return Status\n\t **/\n\tvoid free();\n\n\t/**\n\t * decode an in-memory texture file into texture object.\n\t *\n\t * FYI, currently BMP, TGA, JPG, JP2, PNG, DDS are supported - but don't\n\t * rely on this (not all codecs may be included).\n\t *\n\t * @param data Input data.\n\t * @param data_size Its size [bytes].\n\t * @return Status.\n\t **/\n\tStatus decode(const shared_ptr<u8>& data, size_t data_size);\n\n\t/**\n\t * encode a texture into a memory buffer in the desired file format.\n\t *\n\t * @param extension (including '.').\n\t * @param da Output memory array. Allocated here; caller must free it\n\t *\t\t  when no longer needed. Invalid unless function succeeds.\n\t * @return Status\n\t **/\n\tStatus encode(const OsPath& extension, DynArray* da);\n\t\n\t/**\n\t * store the given image data into a Tex object; this will be as if\n\t * it had been loaded via tex_load.\n\t *\n\t * rationale: support for in-memory images is necessary for\n\t *   emulation of glCompressedTexImage2D and useful overall.\n\t *   however, we don't want to provide an alternate interface for each API;\n\t *   these would have to be changed whenever fields are added to Tex.\n\t *   instead, provide one entry point for specifying images.\n\t * note: since we do not know how \\<img\\> was allocated, the caller must free\n\t *   it themselves (after calling tex_free, which is required regardless of\n\t *   alloc type).\n\t *\n\t * we need only add bookkeeping information and \"wrap\" it in\n\t * our Tex struct, hence the name.\n\t *\n\t * @param w,h Pixel dimensions.\n\t * @param bpp Bits per pixel.\n\t * @param flags TexFlags.\n\t * @param data Img texture data. note: size is calculated from other params.\n\t * @param ofs\n\t * @return Status\n\t **/\n\tStatus wrap(size_t w, size_t h, size_t bpp, size_t flags, const shared_ptr<u8>& data, size_t ofs);\n\t\n\t//\n\t// modify image\n\t//\n\n\t/**\n\t * Change the pixel format.\n\t *\n\t * @param transforms TexFlags that are to be flipped.\n\t * @return Status\n\t **/\n\tStatus transform(size_t transforms);\n\n\t/**\n\t * Change the pixel format (2nd version)\n\t * (note: this is equivalent to Tex::transform(t, t-\\>flags^new_flags).\n\t *\n\t * @param new_flags desired new value of TexFlags.\n\t * @return Status\n\t **/\n\tStatus transform_to(size_t new_flags);\n\n\t//\n\t// return image information\n\t//\n\n\t/**\n\t * return a pointer to the image data (pixels), taking into account any\n\t * header(s) that may come before it.\n\t *\n\t * @return pointer to data returned by mem_get_ptr (holds reference)!\n\t **/\n\tu8* get_data();\n\n\t/**\n\t * return the ARGB value of the 1x1 mipmap level of the texture.\n\t *\n\t * @return ARGB value (or 0 if texture does not have mipmaps)\n\t **/\n\tu32 get_average_color() const;\n\n\t/**\n\t * return total byte size of the image pixels. (including mipmaps!)\n\t * rationale: this is preferable to calculating manually because it's\n\t * less error-prone (e.g. confusing bits_per_pixel with bytes).\n\t *\n\t * @return size [bytes]\n\t **/\n\tsize_t img_size() const;\n\n};\n\n\n/**\n * Set the orientation to which all loaded images will\n * automatically be converted (excepting file formats that don't specify\n * their orientation, i.e. DDS). See \"Default Orientation\" in docs.\n * @param orientation Either TEX_BOTTOM_UP or TEX_TOP_DOWN.\n **/\nextern void tex_set_global_orientation(int orientation);\n\n\n/**\n * special value for levels_to_skip: the callback will only be called\n * for the base mipmap level (i.e. 100%)\n **/\nconst int TEX_BASE_LEVEL_ONLY = -1;\n\n/**\n * callback function for each mipmap level.\n *\n * @param level number; 0 for base level (i.e. 100%), or the first one\n * in case some were skipped.\n * @param level_w, level_h pixel dimensions (powers of 2, never 0)\n * @param level_data the level's texels\n * @param level_data_size [bytes]\n * @param cbData passed through from tex_util_foreach_mipmap.\n **/\ntypedef void (*MipmapCB)(size_t level, size_t level_w, size_t level_h, const u8* RESTRICT level_data, size_t level_data_size, void* RESTRICT cbData);\n\n/**\n * for a series of mipmaps stored from base to highest, call back for\n * each level.\n *\n * @param w,h Pixel dimensions.\n * @param bpp Bits per pixel.\n * @param data Series of mipmaps.\n * @param levels_to_skip Number of levels (counting from base) to skip, or\n *\t\t  TEX_BASE_LEVEL_ONLY to only call back for the base image.\n *\t\t  Rationale: this avoids needing to special case for images with or\n *\t\t  without mipmaps.\n * @param data_padding Minimum pixel dimensions of mipmap levels.\n *\t\t  This is used in S3TC images, where each level is actually stored in\n *\t\t  4x4 blocks. usually 1 to indicate levels are consecutive.\n * @param cb MipmapCB to call.\n * @param cbData Extra data to pass to cb.\n **/\nextern void tex_util_foreach_mipmap(size_t w, size_t h, size_t bpp, const u8* data, int levels_to_skip, size_t data_padding, MipmapCB cb, void* RESTRICT cbData);\n\n\n//\n// image writing\n//\n\n/**\n * Is the file's extension that of a texture format supported by tex_load?\n *\n * Rationale: tex_load complains if the given file is of an\n * unsupported type. this API allows users to preempt that warning\n * (by checking the filename themselves), and also provides for e.g.\n * enumerating only images in a file picker.\n * an alternative might be a flag to suppress warning about invalid files,\n * but this is open to misuse.\n *\n * @param pathname Only the extension (starting with '.') is used. case-insensitive.\n * @return bool\n **/\nextern bool tex_is_known_extension(const VfsPath& pathname);\n\n/**\n * return the minimum header size (i.e. offset to pixel data) of the\n * file format corresponding to the filename.\n *\n * rationale: this can be used to optimize calls to tex_write: when\n * allocating the buffer that will hold the image, allocate this much\n * extra and pass the pointer as base+hdr_size. this allows writing the\n * header directly into the output buffer and makes for zero-copy IO.\n *\n * @param filename Filename; only the extension (that after '.') is used.\n *\t\t  case-insensitive.\n * @return size [bytes] or 0 on error (i.e. no codec found).\n **/\nextern size_t tex_hdr_size(const VfsPath& filename);\n\n#endif\t // INCLUDED_TEX\n"
  },
  {
    "path": "fpsgame/gui/tex/tex_bmp.cpp",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * Windows BMP codec\n */\n\n#include \"precompiled.h\"\n\n#include \"lib/byte_order.h\"\n#include \"tex_codec.h\"\n\n#pragma pack(push, 1)\n\nstruct BmpHeader\n{ \n\t// BITMAPFILEHEADER\n\tu16 bfType;\t\t\t// \"BM\"\n\tu32 bfSize;\t\t\t// of file\n\tu16 bfReserved1;\n\tu16 bfReserved2;\n\tu32 bfOffBits;\t\t// offset to image data\n\n\t// BITMAPINFOHEADER\n\tu32 biSize;\n\ti32 biWidth;\n\ti32 biHeight;\n\tu16 biPlanes;\n\tu16 biBitCount;\n\tu32 biCompression;\n\tu32 biSizeImage;\n\t// the following are unused and zeroed when writing:\n\ti32 biXPelsPerMeter;\n\ti32 biYPelsPerMeter;\n\tu32 biClrUsed;\n\tu32 biClrImportant;\n};\n\n#pragma pack(pop)\n\n#define BI_RGB 0\t\t// biCompression\n\n\nStatus TexCodecBmp::transform(Tex* UNUSED(t), size_t UNUSED(transforms)) const\n{\n\treturn INFO::TEX_CODEC_CANNOT_HANDLE;\n}\n\n\nbool TexCodecBmp::is_hdr(const u8* file) const\n{\n\t// check header signature (bfType == \"BM\"?).\n\t// we compare single bytes to be endian-safe.\n\treturn (file[0] == 'B' && file[1] == 'M');\n}\n\n\nbool TexCodecBmp::is_ext(const OsPath& extension) const\n{\n\treturn extension == L\".bmp\";\n}\n\n\nsize_t TexCodecBmp::hdr_size(const u8* file) const\n{\n\tconst size_t hdr_size = sizeof(BmpHeader);\n\tif(file)\n\t{\n\t\tBmpHeader* hdr = (BmpHeader*)file;\n\t\tconst u32 ofs = read_le32(&hdr->bfOffBits);\n\t\tENSURE(ofs >= hdr_size && \"bmp_hdr_size invalid\");\n\t\treturn ofs;\n\t}\n\treturn hdr_size;\n}\n\n\n// requirements: uncompressed, direct color, bottom up\nStatus TexCodecBmp::decode(rpU8 data, size_t UNUSED(size), Tex* RESTRICT t) const\n{\n\tconst BmpHeader* hdr = (const BmpHeader*)data;\n\tconst long w       = (long)read_le32(&hdr->biWidth);\n\tconst long h_      = (long)read_le32(&hdr->biHeight);\n\tconst u16 bpp      = read_le16(&hdr->biBitCount);\n\tconst u32 compress = read_le32(&hdr->biCompression);\n\n\tconst long h = abs(h_);\n\n\tsize_t flags = 0;\n\tflags |= (h_ < 0)? TEX_TOP_DOWN : TEX_BOTTOM_UP;\n\tif(bpp > 16)\n\t\tflags |= TEX_BGR;\n\tif(bpp == 32)\n\t\tflags |= TEX_ALPHA;\n\n\t// sanity checks\n\tif(compress != BI_RGB)\n\t\tWARN_RETURN(ERR::TEX_COMPRESSED);\n\n\tt->m_Width  = w;\n\tt->m_Height = h;\n\tt->m_Bpp    = bpp;\n\tt->m_Flags  = flags;\n\treturn INFO::OK;\n}\n\n\nStatus TexCodecBmp::encode(Tex* RESTRICT t, DynArray* RESTRICT da) const\n{\n\tconst size_t hdr_size = sizeof(BmpHeader);\t// needed for BITMAPFILEHEADER\n\tconst size_t img_size = t->img_size();\n\tconst size_t file_size = hdr_size + img_size;\n\tconst i32 h = (t->m_Flags & TEX_TOP_DOWN)? -(i32)t->m_Height : (i32)t->m_Height;\n\n\tsize_t transforms = t->m_Flags;\n\ttransforms &= ~TEX_ORIENTATION;\t// no flip needed - we can set top-down bit.\n\ttransforms ^= TEX_BGR;\t\t\t// BMP is native BGR.\n\n\tconst BmpHeader hdr =\n\t{\n\t\t// BITMAPFILEHEADER\n\t\t0x4D42,\t\t\t\t// bfType = 'B','M'\n\t\t(u32)file_size,\t\t// bfSize\n\t\t0, 0,\t\t\t\t// bfReserved1,2\n\t\thdr_size,\t\t\t// bfOffBits\n\n\t\t// BITMAPINFOHEADER\n\t\t40,\t\t\t\t\t// biSize = sizeof(BITMAPINFOHEADER)\n\t\t(i32)t->m_Width,\n\t\th,\n\t\t1,\t\t\t\t\t// biPlanes\n\t\t(u16)t->m_Bpp,\n\t\tBI_RGB,\t\t\t\t// biCompression\n\t\t(u32)img_size,\t\t// biSizeImage\n\t\t0, 0, 0, 0\t\t\t// unused (bi?PelsPerMeter, biClr*)\n\t};\n\treturn tex_codec_write(t, transforms, &hdr, hdr_size, da);\n}\n"
  },
  {
    "path": "fpsgame/gui/tex/tex_codec.cpp",
    "content": "/* Copyright (c) 2014 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * support routines for texture codecs\n */\n\n#include \"precompiled.h\"\n#include \"tex_codec.h\"\n\n#include <string.h>\n#include <stdlib.h>\n\n#include \"lib/allocators/shared_ptr.h\" // ArrayDeleter\n#include \"tex.h\"\n\n// Statically allocate all of the codecs...\nTexCodecDds DdsCodec;\nTexCodecPng PngCodec;\nTexCodecTga TgaCodec;\nTexCodecBmp BmpCodec;\n// Codecs will be searched in this order\nstatic const ITexCodec *codecs[] = {(ITexCodec *)&DdsCodec, (ITexCodec *)&PngCodec,\n\t(ITexCodec *)&TgaCodec, (ITexCodec *)&BmpCodec};\nstatic const int codecs_len = sizeof(codecs) / sizeof(ITexCodec*);\n\n// find codec that recognizes the desired output file extension,\n// or return ERR::TEX_UNKNOWN_FORMAT if unknown.\n// note: does not raise a warning because it is used by\n// tex_is_known_extension.\nStatus tex_codec_for_filename(const OsPath& extension, const ITexCodec** c)\n{\n\tfor(int i = 0; i < codecs_len; ++i)\n\t{\n\t\t// we found it\n\t\tif(codecs[i]->is_ext(extension)) {\n\t\t\t*c = codecs[i];\n\t\t\treturn INFO::OK;\n\t\t}\n\t}\n\n\treturn ERR::TEX_UNKNOWN_FORMAT;\t// NOWARN\n}\n\n\n// find codec that recognizes the header's magic field\nStatus tex_codec_for_header(const u8* file, size_t file_size, const ITexCodec** c)\n{\n\t// we guarantee at least 4 bytes for is_hdr to look at\n\tif(file_size < 4)\n\t\tWARN_RETURN(ERR::TEX_INCOMPLETE_HEADER);\n\t\n\tfor(int i = 0; i < codecs_len; ++i)\n\t{\n\t\t// we found it\n\t\tif(codecs[i]->is_hdr(file)) {\n\t\t\t*c = codecs[i];\n\t\t\treturn INFO::OK;\n\t\t}\n\t}\n\n\tWARN_RETURN(ERR::TEX_UNKNOWN_FORMAT);\n}\n\nStatus tex_codec_transform(Tex* t, size_t transforms)\n{\n\tStatus ret = INFO::TEX_CODEC_CANNOT_HANDLE;\n\n\t// find codec that understands the data, and transform\n\tfor(int i = 0; i < codecs_len; ++i)\n\t{\n\t\tStatus err = codecs[i]->transform(t, transforms);\n\t\t// success\n\t\tif(err == INFO::OK)\n\t\t\treturn INFO::OK;\n\t\t// something went wrong\n\t\telse if(err != INFO::TEX_CODEC_CANNOT_HANDLE)\n\t\t{\n\t\t\tret = err;\n\t\t\tDEBUG_WARN_ERR(ERR::LOGIC);\t// codec indicates error\n\t\t}\n\t}\n\n\treturn ret;\n}\n\n\n//-----------------------------------------------------------------------------\n// helper functions used by codecs\n//-----------------------------------------------------------------------------\n\n// allocate an array of row pointers that point into the given texture data.\n// <file_orientation> indicates whether the file format is top-down or\n// bottom-up; the row array is inverted if necessary to match global\n// orienatation. (this is more efficient than \"transforming\" later)\n//\n// used by PNG and JPG codecs.\n//\n// note: we don't allocate the data param ourselves because this function is\n// needed for encoding, too (where data is already present).\nstd::vector<RowPtr> tex_codec_alloc_rows(const u8* data, size_t h, size_t pitch, size_t src_flags, size_t dst_orientation)\n{\n\tconst bool flip = !tex_orientations_match(src_flags, dst_orientation);\n\n\tstd::vector<RowPtr> rows(h);\n\n\t// determine start position and direction\n\tRowPtr pos        = flip? data+pitch*(h-1) : data;\n\tconst ssize_t add = flip? -(ssize_t)pitch : (ssize_t)pitch;\n\tconst RowPtr end  = flip? data-pitch : data+pitch*h;\n\n\tfor(size_t i = 0; i < h; i++)\n\t{\n\t\trows[i] = pos;\n\t\tpos += add;\n\t}\n\n\tENSURE(pos == end);\n\treturn rows;\n}\n\n\nStatus tex_codec_write(Tex* t, size_t transforms, const void* hdr, size_t hdr_size, DynArray* da)\n{\n\tRETURN_STATUS_IF_ERR(t->transform(transforms));\n\n\tvoid* img_data = t->get_data(); const size_t img_size = t->img_size();\n\tRETURN_STATUS_IF_ERR(da_append(da, hdr, hdr_size));\n\tRETURN_STATUS_IF_ERR(da_append(da, img_data, img_size));\n\treturn INFO::OK;\n}\n"
  },
  {
    "path": "fpsgame/gui/tex/tex_codec.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * support routines and interface for texture codecs.\n */\n\n#ifndef INCLUDED_TEX_CODEC\n#define INCLUDED_TEX_CODEC\n\n#include \"tex.h\"\n#include \"tex_internal.h\"\t// for codec's convenience\n\n/**\n * virtual method table for TexCodecs.\n * rationale: this works in C and also allows storing name and next in vtbl.\n * 'template method'-style interface to increase code reuse and\n * simplify writing new codecs.\n **/\nclass ITexCodec\n{\npublic:\n\t/**\n\t * decode the file into a Tex structure.\n\t *\n\t * @param data input data array (non-const, because the texture\n\t * may have to be flipped in-place - see \"texture orientation\").\n\t * @param size [bytes] of data, always >= 4\n\t *   (this is usually enough to compare the header's \"magic\" field,\n\t *    and no legitimate file will be smaller)\n\t * @param t output texture object\n\t * @return Status\n\t **/\n\tvirtual Status decode(u8* data, size_t size, Tex* RESTRICT t) const = 0;\n\n\t/**\n\t * encode the texture data into the codec's file format (in memory).\n\t *\n\t * @param t input texture object. note: non-const because encoding may\n\t * require a Tex::transform.\n\t * @param da output data array, allocated by codec.\n\t * rationale: some codecs cannot calculate the output size beforehand\n\t * (e.g. PNG output via libpng), so the output memory cannot be allocated\n\t * by the caller.\n\t * @return Status\n\t **/\n\tvirtual Status encode(Tex* RESTRICT t, DynArray* RESTRICT da) const = 0;\n\n\t/**\n\t * transform the texture's pixel format.\n\t *\n\t * @param t texture object\n\t * @param transforms: OR-ed combination of TEX_* flags that are to\n\t * be changed. note: the codec needs only handle situations specific\n\t * to its format; generic pixel format transforms are handled by\n\t * the caller.\n\t **/\n\tvirtual Status transform(Tex* t, size_t transforms) const = 0;\n\n\t/**\n\t * indicate if the data appears to be an instance of this codec's header,\n\t * i.e. can this codec decode it?\n\t *\n\t * @param file input data; only guaranteed to be 4 bytes!\n\t * (this should be enough to examine the header's 'magic' field)\n\t * @return bool\n\t **/\n\tvirtual bool is_hdr(const u8* file) const = 0;\n\n\t/**\n\t * is the extension that of a file format supported by this codec?\n\t *\n\t * rationale: cannot just return the extension string and have\n\t * caller compare it (-> smaller code) because a codec's file format\n\t * may have several valid extensions (e.g. jpg and jpeg).\n\t *\n\t * @param extension (including '.')\n\t * @return bool\n\t **/\n\tvirtual bool is_ext(const OsPath& extension) const = 0;\n\n\t/**\n\t * return size of the file header supported by this codec.\n\t *\n\t * @param file the specific header to return length of (taking its\n\t * variable-length fields into account). if NULL, return minimum\n\t * guaranteed header size, i.e. the header without any\n\t * variable-length fields.\n\t * @return size [bytes]\n\t **/\n\tvirtual size_t hdr_size(const u8* file) const = 0;\n\n\t/**\n\t * name of codec for debug purposes. typically set via TEX_CODEC_REGISTER.\n\t **/\n\tvirtual const wchar_t* get_name() const = 0;\n\n\tvirtual ~ITexCodec() {}\n};\n\nclass TexCodecPng:ITexCodec {\npublic:\n\tvirtual Status decode(u8* data, size_t size, Tex* RESTRICT t) const;\n\tvirtual Status encode(Tex* RESTRICT t, DynArray* RESTRICT da) const;\n\tvirtual Status transform(Tex* t, size_t transforms) const;\n\tvirtual bool is_hdr(const u8* file) const;\n\tvirtual bool is_ext(const OsPath& extension) const;\n\tvirtual size_t hdr_size(const u8* file) const;\n\tvirtual const wchar_t* get_name() const {\n\t\tstatic const wchar_t *name = L\"png\";\n\t\treturn name;\n\t};\n};\n\nclass TexCodecDds:ITexCodec {\npublic:\n\tvirtual Status decode(u8* data, size_t size, Tex* RESTRICT t) const;\n\tvirtual Status encode(Tex* RESTRICT t, DynArray* RESTRICT da) const;\n\tvirtual Status transform(Tex* t, size_t transforms) const;\n\tvirtual bool is_hdr(const u8* file) const;\n\tvirtual bool is_ext(const OsPath& extension) const;\n\tvirtual size_t hdr_size(const u8* file) const;\n\tvirtual const wchar_t* get_name() const {\n\t\tstatic const wchar_t *name = L\"dds\";\n\t\treturn name;\n\t};\n};\n\nclass TexCodecTga:ITexCodec {\npublic:\n\tvirtual Status decode(u8* data, size_t size, Tex* RESTRICT t) const;\n\tvirtual Status encode(Tex* RESTRICT t, DynArray* RESTRICT da) const;\n\tvirtual Status transform(Tex* t, size_t transforms) const;\n\tvirtual bool is_hdr(const u8* file) const;\n\tvirtual bool is_ext(const OsPath& extension) const;\n\tvirtual size_t hdr_size(const u8* file) const;\n\tvirtual const wchar_t* get_name() const {\n\t\tstatic const wchar_t *name = L\"tga\";\n\t\treturn name;\n\t};\n};\n\nclass TexCodecBmp:ITexCodec {\npublic:\n\tvirtual Status decode(u8* data, size_t size, Tex* RESTRICT t) const;\n\tvirtual Status encode(Tex* RESTRICT t, DynArray* RESTRICT da) const;\n\tvirtual Status transform(Tex* t, size_t transforms) const;\n\tvirtual bool is_hdr(const u8* file) const;\n\tvirtual bool is_ext(const OsPath& extension) const;\n\tvirtual size_t hdr_size(const u8* file) const;\n\tvirtual const wchar_t* get_name() const {\n\t\tstatic const wchar_t *name = L\"bmp\";\n\t\treturn name;\n\t};\n};\n\n/**\n * Find codec that recognizes the desired output file extension.\n *\n * @param extension\n * @param c (out) vtbl of responsible codec\n * @return Status; ERR::RES_UNKNOWN_FORMAT (without warning, because this is\n * called by tex_is_known_extension) if no codec indicates they can\n * handle the given extension.\n **/\nextern Status tex_codec_for_filename(const OsPath& extension, const ITexCodec** c);\n\n/**\n * find codec that recognizes the header's magic field.\n *\n * @param data typically contents of file, but need only include the\n * (first 4 bytes of) header.\n * @param data_size [bytes]\n * @param c (out) vtbl of responsible codec\n * @return Status; ERR::RES_UNKNOWN_FORMAT if no codec indicates they can\n * handle the given format (header).\n **/\nextern Status tex_codec_for_header(const u8* data, size_t data_size, const ITexCodec** c);\n\n/**\n * transform the texture's pixel format.\n * tries each codec's transform method once, or until one indicates success.\n *\n * @param t texture object\n * @param transforms: OR-ed combination of TEX_* flags that are to\n * be changed.\n * @return Status\n **/\nextern Status tex_codec_transform(Tex* t, size_t transforms);\n\n/**\n * allocate an array of row pointers that point into the given texture data.\n * for texture decoders that support output via row pointers (e.g. PNG),\n * this allows flipping the image vertically (useful when matching bottom-up\n * textures to a global orientation) directly, which is much more\n * efficient than transforming later via copying all pixels.\n *\n * @param data the texture data into which row pointers will point.\n * note: we don't allocate it here because this function is\n * needed for encoding, too (where data is already present).\n * @param h height [pixels] of texture.\n * @param pitch size [bytes] of one texture row, i.e. width*bytes_per_pixel.\n * @param src_flags TexFlags of source texture. used to extract its\n * orientation.\n * @param dst_orientation desired orientation of the output data.\n * can be one of TEX_BOTTOM_UP, TEX_TOP_DOWN, or 0 for the\n * \"global orientation\".\n * depending on src and dst, the row array is flipped if necessary.\n **/\ntypedef const u8* RowPtr;\nextern std::vector<RowPtr> tex_codec_alloc_rows(const u8* data, size_t h, size_t pitch, size_t src_flags, size_t dst_orientation);\n\n/**\n * apply transforms and then copy header and image into output buffer.\n *\n * @param t input texture object\n * @param transforms transformations to be applied to pixel format\n * @param hdr header data\n * @param hdr_size [bytes]\n * @param da output data array (will be expanded as necessary)\n * @return Status\n **/\nextern Status tex_codec_write(Tex* t, size_t transforms, const void* hdr, size_t hdr_size, DynArray* da);\n\n#endif\t // #ifndef INCLUDED_TEX_CODEC\n"
  },
  {
    "path": "fpsgame/gui/tex/tex_dds.cpp",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * DDS (DirectDraw Surface) codec.\n */\n\n#include \"precompiled.h\"\n\n#include \"lib/byte_order.h\"\n#include \"lib/bits.h\"\n#include \"lib/timer.h\"\n#include \"lib/allocators/shared_ptr.h\"\n#include \"tex_codec.h\"\n\n\n// NOTE: the convention is bottom-up for DDS, but there's no way to tell.\n\n\n//-----------------------------------------------------------------------------\n// S3TC decompression\n//-----------------------------------------------------------------------------\n\n// note: this code may not be terribly efficient. it's only used to\n// emulate hardware S3TC support - if that isn't available, performance\n// will suffer anyway due to increased video memory usage.\n\n\n// for efficiency, we precalculate as much as possible about a block\n// and store it here.\nclass S3tcBlock\n{\npublic:\n\tS3tcBlock(size_t dxt, const u8* RESTRICT block)\n\t\t: dxt(dxt)\n\t{\n\t\t// (careful, 'dxt != 1' doesn't work - there's also DXT1a)\n\t\tconst u8* a_block = block;\n\t\tconst u8* c_block = (dxt == 3 || dxt == 5)? block+8 : block;\n\n\t\tPrecalculateAlpha(dxt, a_block);\n\t\tPrecalculateColor(dxt, c_block);\n\t}\n\n\tvoid WritePixel(size_t pixel_idx, u8* RESTRICT out) const\n\t{\n\t\tENSURE(pixel_idx < 16);\n\n\t\t// pixel index -> color selector (2 bit) -> color\n\t\tconst size_t c_selector = access_bit_tbl(c_selectors, pixel_idx, 2);\n\t\tfor(int i = 0; i < 3; i++)\n\t\t\tout[i] = (u8)c[c_selector][i];\n\n\t\t// if no alpha, done\n\t\tif(dxt == 1)\n\t\t\treturn;\n\n\t\tsize_t a;\n\t\tif(dxt == 3)\n\t\t{\n\t\t\t// table of 4-bit alpha entries\n\t\t\ta = access_bit_tbl(a_bits, pixel_idx, 4);\n\t\t\ta |= a << 4; // expand to 8 bits (replicate high into low!)\n\t\t}\n\t\telse if(dxt == 5)\n\t\t{\n\t\t\t// pixel index -> alpha selector (3 bit) -> alpha\n\t\t\tconst size_t a_selector = access_bit_tbl(a_bits, pixel_idx, 3);\n\t\t\ta = dxt5_a_tbl[a_selector];\n\t\t}\n\t\t// (dxt == DXT1A)\n\t\telse\n\t\t\ta = c[c_selector][A];\n\t\tout[A] = (u8)(a & 0xFF);\n\t}\n\nprivate:\n\t// pixel colors are stored as size_t[4]. size_t rather than u8 protects from\n\t// overflow during calculations, and padding to an even size is a bit\n\t// more efficient (even though we don't need the alpha component).\n\tenum RGBA {\tR, G, B, A };\n\n\tstatic inline void mix_2_3(size_t dst[4], size_t c0[4], size_t c1[4])\n\t{\n\t\tfor(int i = 0; i < 3; i++) dst[i] = (c0[i]*2 + c1[i] + 1)/3;\n\t}\n\n\tstatic inline void mix_avg(size_t dst[4], size_t c0[4], size_t c1[4])\n\t{\n\t\tfor(int i = 0; i < 3; i++) dst[i] = (c0[i]+c1[i])/2;\n\t}\n\n\ttemplate<typename T>\n\tstatic inline size_t access_bit_tbl(T tbl, size_t idx, size_t bit_width)\n\t{\n\t\tsize_t val = (tbl >> (idx*bit_width)) & bit_mask<T>(bit_width);\n\t\treturn val;\n\t}\n\n\t// extract a range of bits and expand to 8 bits (by replicating\n\t// MS bits - see http://www.mindcontrol.org/~hplus/graphics/expand-bits.html ;\n\t// this is also the algorithm used by graphics cards when decompressing S3TC).\n\t// used to convert 565 to 32bpp RGB.\n\tstatic inline size_t unpack_to_8(u16 c, size_t bits_below, size_t num_bits)\n\t{\n\t\tconst size_t num_filler_bits = 8-num_bits;\n\t\tconst size_t field = (size_t)bits(c, bits_below, bits_below+num_bits-1);\n\t\tconst size_t filler = field >> (num_bits-num_filler_bits);\n\t\treturn (field << num_filler_bits) | filler;\n\t}\n\n\tvoid PrecalculateAlpha(size_t dxt, const u8* RESTRICT a_block)\n\t{\n\t\t// read block contents\n\t\tconst u8 a0 = a_block[0], a1 = a_block[1];\n\t\ta_bits = read_le64(a_block);\t// see below\n\n\t\tif(dxt == 5)\n\t\t{\n\t\t\t// skip a0,a1 bytes (data is little endian)\n\t\t\ta_bits >>= 16;\n\n\t\t\tconst bool is_dxt5_special_combination = (a0 <= a1);\n\t\t\tu8* a = dxt5_a_tbl;\t// shorthand\n\t\t\tif(is_dxt5_special_combination)\n\t\t\t{\n\t\t\t\ta[0] = a0;\n\t\t\t\ta[1] = a1;\n\t\t\t\ta[2] = (4*a0 + 1*a1 + 2)/5;\n\t\t\t\ta[3] = (3*a0 + 2*a1 + 2)/5;\n\t\t\t\ta[4] = (2*a0 + 3*a1 + 2)/5;\n\t\t\t\ta[5] = (1*a0 + 4*a1 + 2)/5;\n\t\t\t\ta[6] = 0;\n\t\t\t\ta[7] = 255;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\ta[0] = a0;\n\t\t\t\ta[1] = a1;\n\t\t\t\ta[2] = (6*a0 + 1*a1 + 3)/7;\n\t\t\t\ta[3] = (5*a0 + 2*a1 + 3)/7;\n\t\t\t\ta[4] = (4*a0 + 3*a1 + 3)/7;\n\t\t\t\ta[5] = (3*a0 + 4*a1 + 3)/7;\n\t\t\t\ta[6] = (2*a0 + 5*a1 + 3)/7;\n\t\t\t\ta[7] = (1*a0 + 6*a1 + 3)/7;\n\t\t\t}\n\t\t}\n\t}\n\n\n\tvoid PrecalculateColor(size_t dxt, const u8* RESTRICT c_block)\n\t{\n\t\t// read block contents\n\t\t// .. S3TC reference colors (565 format). the color table is generated\n\t\t//    from some combination of these, depending on their ordering.\n\t\tu16 rc[2];\n\t\tfor(int i = 0; i < 2; i++)\n\t\t\trc[i] = read_le16(c_block + 2*i);\n\t\t// .. table of 2-bit color selectors\n\t\tc_selectors = read_le32(c_block+4);\n\n\t\tconst bool is_dxt1_special_combination = (dxt == 1 || dxt == DXT1A) && rc[0] <= rc[1];\n\n\t\t// c0 and c1 are the values of rc[], converted to 32bpp\n\t\tfor(int i = 0; i < 2; i++)\n\t\t{\n\t\t\tc[i][R] = unpack_to_8(rc[i], 11, 5);\n\t\t\tc[i][G] = unpack_to_8(rc[i],  5, 6);\n\t\t\tc[i][B] = unpack_to_8(rc[i],  0, 5);\n\t\t}\n\n\t\t// c2 and c3 are combinations of c0 and c1:\n\t\tif(is_dxt1_special_combination)\n\t\t{\n\t\t\tmix_avg(c[2], c[0], c[1]);\t\t\t// c2 = (c0+c1)/2\n\t\t\tfor(int i = 0; i < 3; i++) c[3][i] = 0;\t// c3 = black\n\t\t\tc[3][A] = (dxt == DXT1A)? 0 : 255;\t\t// (transparent iff DXT1a)\n\t\t}\n\t\telse\n\t\t{\n\t\t\tmix_2_3(c[2], c[0], c[1]);\t\t\t// c2 = 2/3*c0 + 1/3*c1\n\t\t\tmix_2_3(c[3], c[1], c[0]);\t\t\t// c3 = 1/3*c0 + 2/3*c1\n\t\t}\n\t}\n\n\t// the 4 color choices for each pixel (RGBA)\n\tsize_t c[4][4];\t// c[i][RGBA_component]\n\n\t// (DXT5 only) the 8 alpha choices\n\tu8 dxt5_a_tbl[8];\n\n\t// alpha block; interpretation depends on dxt.\n\tu64 a_bits;\n\n\t// table of 2-bit color selectors\n\tu32 c_selectors;\n\n\tsize_t dxt;\n};\n\n\nstruct S3tcDecompressInfo\n{\n\tsize_t dxt;\n\tsize_t s3tc_block_size;\n\tsize_t out_Bpp;\n\tu8* out;\n};\n\nstatic void s3tc_decompress_level(size_t UNUSED(level), size_t level_w, size_t level_h,\n\tconst u8* RESTRICT level_data, size_t level_data_size, void* RESTRICT cbData)\n{\n\tS3tcDecompressInfo* di = (S3tcDecompressInfo*)cbData;\n\tconst size_t dxt             = di->dxt;\n\tconst size_t s3tc_block_size = di->s3tc_block_size;\n\n\t// note: 1x1 images are legitimate (e.g. in mipmaps). they report their\n\t// width as such for glTexImage, but the S3TC data is padded to\n\t// 4x4 pixel block boundaries.\n\tconst size_t blocks_w = DivideRoundUp(level_w, size_t(4));\n\tconst size_t blocks_h = DivideRoundUp(level_h, size_t(4));\n\tconst u8* s3tc_data = level_data;\n\tENSURE(level_data_size % s3tc_block_size == 0);\n\n\tfor(size_t block_y = 0; block_y < blocks_h; block_y++)\n\t{\n\t\tfor(size_t block_x = 0; block_x < blocks_w; block_x++)\n\t\t{\n\t\t\tS3tcBlock block(dxt, s3tc_data);\n\t\t\ts3tc_data += s3tc_block_size;\n\n\t\t\tsize_t pixel_idx = 0;\n\t\t\tfor(int y = 0; y < 4; y++)\n\t\t\t{\n\t\t\t\t// this is ugly, but advancing after x, y and block_y loops\n\t\t\t\t// is no better.\n\t\t\t\tu8* out = (u8*)di->out + ((block_y*4+y)*blocks_w*4 + block_x*4) * di->out_Bpp;\n\t\t\t\tfor(int x = 0; x < 4; x++)\n\t\t\t\t{\n\t\t\t\t\tblock.WritePixel(pixel_idx, out);\n\t\t\t\t\tout += di->out_Bpp;\n\t\t\t\t\tpixel_idx++;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tENSURE(s3tc_data == level_data + level_data_size);\n\tdi->out += blocks_w*blocks_h * 16 * di->out_Bpp;\n}\n\n\n// decompress the given image (which is known to be stored as DXTn)\n// effectively in-place. updates Tex fields.\nstatic Status s3tc_decompress(Tex* t)\n{\n\t// alloc new image memory\n\t// notes:\n\t// - dxt == 1 is the only non-alpha case.\n\t// - adding or stripping alpha channels during transform is not\n\t//   our job; we merely output the same pixel format as given\n\t//   (tex.cpp's plain transform could cover it, if ever needed).\n\tconst size_t dxt = t->m_Flags & TEX_DXT;\n\tconst size_t out_bpp = (dxt != 1)? 32 : 24;\n\tconst size_t out_size = t->img_size() * out_bpp / t->m_Bpp;\n\tshared_ptr<u8> decompressedData;\n\tAllocateAligned(decompressedData, out_size, pageSize);\n\n\tconst size_t s3tc_block_size = (dxt == 3 || dxt == 5)? 16 : 8;\n\tS3tcDecompressInfo di = { dxt, s3tc_block_size, out_bpp/8, decompressedData.get() };\n\tconst u8* s3tc_data = t->get_data();\n\tconst int levels_to_skip = (t->m_Flags & TEX_MIPMAPS)? 0 : TEX_BASE_LEVEL_ONLY;\n\ttex_util_foreach_mipmap(t->m_Width, t->m_Height, t->m_Bpp, s3tc_data, levels_to_skip, 4, s3tc_decompress_level, &di);\n\tt->m_Data = decompressedData;\n\tt->m_DataSize = out_size;\n\tt->m_Ofs = 0;\n\tt->m_Bpp = out_bpp;\n\tt->m_Flags &= ~TEX_DXT;\n\treturn INFO::OK;\n}\n\n\n//-----------------------------------------------------------------------------\n// DDS file format\n//-----------------------------------------------------------------------------\n\n// bit values and structure definitions taken from \n// http://msdn.microsoft.com/en-us/library/ee417785(VS.85).aspx\n\n#pragma pack(push, 1)\n\n// DDS_PIXELFORMAT.dwFlags\n// we've seen some DXT3 files that don't have this set (which is nonsense;\n// any image lacking alpha should be stored as DXT1). it's authoritative\n// if fourcc is DXT1 (there's no other way to tell DXT1 and DXT1a apart)\n// and ignored otherwise.\n#define DDPF_ALPHAPIXELS 0x00000001\n#define DDPF_FOURCC      0x00000004\n#define DDPF_RGB         0x00000040\n\nstruct DDS_PIXELFORMAT\n{\n\tu32 dwSize;                       // size of structure (32)\n\tu32 dwFlags;                      // indicates which fields are valid\n\tu32 dwFourCC;                     // (DDPF_FOURCC) FOURCC code, \"DXTn\"\n\tu32 dwRGBBitCount;                // (DDPF_RGB) bits per pixel\n\tu32 dwRBitMask;\n\tu32 dwGBitMask;\n\tu32 dwBBitMask;\n\tu32 dwABitMask;                   // (DDPF_ALPHAPIXELS)\n};\n\n\n// DDS_HEADER.dwFlags (none are optional)\n#define DDSD_CAPS        0x00000001\n#define DDSD_HEIGHT      0x00000002\n#define DDSD_WIDTH       0x00000004\n#define DDSD_PITCH       0x00000008 // used when texture is uncompressed\n#define DDSD_PIXELFORMAT 0x00001000\n#define DDSD_MIPMAPCOUNT 0x00020000\n#define DDSD_LINEARSIZE  0x00080000 // used when texture is compressed\n#define DDSD_DEPTH       0x00800000\n\n// DDS_HEADER.dwCaps\n#define DDSCAPS_MIPMAP   0x00400000 // optional\n#define DDSCAPS_TEXTURE\t 0x00001000 // required\n\nstruct DDS_HEADER\n{\n\t// (preceded by the FOURCC \"DDS \")\n\tu32 dwSize;                    // size of structure (124)\n\tu32 dwFlags;                   // indicates which fields are valid\n\tu32 dwHeight;                  // (DDSD_HEIGHT) height of main image (pixels)\n\tu32 dwWidth;                   // (DDSD_WIDTH ) width  of main image (pixels)\n\tu32 dwPitchOrLinearSize;       // (DDSD_LINEARSIZE) size [bytes] of top level\n\t                               // (DDSD_PITCH) bytes per row (%4 = 0)\n\tu32 dwDepth;                   // (DDSD_DEPTH) vol. textures: vol. depth\n\tu32 dwMipMapCount;             // (DDSD_MIPMAPCOUNT) total # levels\n\tu32 dwReserved1[11];           // reserved\n\tDDS_PIXELFORMAT ddpf;          // (DDSD_PIXELFORMAT) surface description\n\tu32 dwCaps;                    // (DDSD_CAPS) misc. surface flags\n\tu32 dwCaps2;\n\tu32 dwCaps3;\n\tu32 dwCaps4;\n\tu32 dwReserved2;               // reserved\n};\n\n#pragma pack(pop)\n\n\nstatic bool is_valid_dxt(size_t dxt)\n{\n\tswitch(dxt)\n\t{\n\tcase 0:\n\tcase 1:\n\tcase DXT1A:\n\tcase 3:\n\tcase 5:\n\t\treturn true;\n\tdefault:\n\t\treturn false;\n\t}\n}\n\n\n// extract all information from DDS pixel format and store in bpp, flags.\n// pf points to the DDS file's header; all fields must be endian-converted\n// before use.\n// output parameters invalid on failure.\nstatic Status decode_pf(const DDS_PIXELFORMAT* pf, size_t& bpp, size_t& flags)\n{\n\tbpp = 0;\n\tflags = 0;\n\n\t// check struct size\n\tif(read_le32(&pf->dwSize) != sizeof(DDS_PIXELFORMAT))\n\t\tWARN_RETURN(ERR::TEX_INVALID_SIZE);\n\n\t// determine type\n\tconst size_t pf_flags = (size_t)read_le32(&pf->dwFlags);\n\t// .. uncompressed RGB/RGBA\n\tif(pf_flags & DDPF_RGB)\n\t{\n\t\tconst size_t pf_bpp    = (size_t)read_le32(&pf->dwRGBBitCount);\n\t\tconst size_t pf_r_mask = (size_t)read_le32(&pf->dwRBitMask);\n\t\tconst size_t pf_g_mask = (size_t)read_le32(&pf->dwGBitMask);\n\t\tconst size_t pf_b_mask = (size_t)read_le32(&pf->dwBBitMask);\n\t\tconst size_t pf_a_mask = (size_t)read_le32(&pf->dwABitMask);\n\n\t\t// (checked below; must be set in case below warning is to be\n\t\t// skipped)\n\t\tbpp = pf_bpp;\n\n\t\tif(pf_flags & DDPF_ALPHAPIXELS)\n\t\t{\n\t\t\t// something weird other than RGBA or BGRA\n\t\t\tif(pf_a_mask != 0xFF000000)\n\t\t\t\tWARN_RETURN(ERR::TEX_FMT_INVALID);\n\t\t\tflags |= TEX_ALPHA;\n\t\t}\n\n\t\t// make sure component ordering is 0xBBGGRR = RGB (see below)\n\t\tif(pf_r_mask != 0xFF || pf_g_mask != 0xFF00 || pf_b_mask != 0xFF0000)\n\t\t{\n\t\t\t// DDS_PIXELFORMAT in theory supports any ordering of R,G,B,A.\n\t\t\t// we need to upload to OpenGL, which can only receive BGR(A) or\n\t\t\t// RGB(A). the former still requires conversion (done by driver),\n\t\t\t// so it's slower. since the very purpose of supporting uncompressed\n\t\t\t// DDS is storing images in a format that requires no processing,\n\t\t\t// we do not allow any weird orderings that require runtime work.\n\t\t\t// instead, the artists must export with the correct settings.\n\t\t\tWARN_RETURN(ERR::TEX_FMT_INVALID);\n\t\t}\n\n\t\tRETURN_STATUS_IF_ERR(tex_validate_plain_format(bpp, (int)flags));\n\t}\n\t// .. uncompressed 8bpp greyscale\n\telse if(pf_flags & DDPF_ALPHAPIXELS)\n\t{\n\t\tconst size_t pf_bpp    = (size_t)read_le32(&pf->dwRGBBitCount);\n\t\tconst size_t pf_a_mask = (size_t)read_le32(&pf->dwABitMask);\n\n\t\tbpp = pf_bpp;\n\n\t\tif(pf_bpp != 8)\n\t\t\tWARN_RETURN(ERR::TEX_FMT_INVALID);\n\n\t\tif(pf_a_mask != 0xFF)\n\t\t\tWARN_RETURN(ERR::TEX_FMT_INVALID);\n\t\tflags |= TEX_GREY;\n\n\t\tRETURN_STATUS_IF_ERR(tex_validate_plain_format(bpp, (int)flags));\n\t}\n\t// .. compressed\n\telse if(pf_flags & DDPF_FOURCC)\n\t{\n\t\t// set effective bpp and store DXT format in flags & TEX_DXT.\n\t\t// no endian conversion necessary - FOURCC() takes care of that.\n\t\tswitch(pf->dwFourCC)\n\t\t{\n\t\tcase FOURCC('D','X','T','1'):\n\t\t\tbpp = 4;\n\t\t\tif(pf_flags & DDPF_ALPHAPIXELS)\n\t\t\t\tflags |= DXT1A | TEX_ALPHA;\n\t\t\telse\n\t\t\t\tflags |= 1;\n\t\t\tbreak;\n\t\tcase FOURCC('D','X','T','3'):\n\t\t\tbpp = 8;\n\t\t\tflags |= 3;\n\t\t\tflags |= TEX_ALPHA;\t// see DDPF_ALPHAPIXELS decl\n\t\t\tbreak;\n\t\tcase FOURCC('D','X','T','5'):\n\t\t\tbpp = 8;\n\t\t\tflags |= 5;\n\t\t\tflags |= TEX_ALPHA;\t// see DDPF_ALPHAPIXELS decl\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tWARN_RETURN(ERR::TEX_FMT_INVALID);\n\t\t}\n\t}\n\t// .. neither uncompressed nor compressed - invalid\n\telse\n\t\tWARN_RETURN(ERR::TEX_FMT_INVALID);\n\n\treturn INFO::OK;\n}\n\n\n// extract all information from DDS header and store in w, h, bpp, flags.\n// sd points to the DDS file's header; all fields must be endian-converted\n// before use.\n// output parameters invalid on failure.\nstatic Status decode_sd(const DDS_HEADER* sd, size_t& w, size_t& h, size_t& bpp, size_t& flags)\n{\n\t// check header size\n\tif(read_le32(&sd->dwSize) != sizeof(*sd))\n\t\tWARN_RETURN(ERR::CORRUPTED);\n\n\t// flags (indicate which fields are valid)\n\tconst size_t sd_flags = (size_t)read_le32(&sd->dwFlags);\n\t// .. not all required fields are present\n\t// note: we can't guess dimensions - the image may not be square.\n\tconst size_t sd_req_flags = DDSD_CAPS|DDSD_HEIGHT|DDSD_WIDTH|DDSD_PIXELFORMAT;\n\tif((sd_flags & sd_req_flags) != sd_req_flags)\n\t\tWARN_RETURN(ERR::TEX_INCOMPLETE_HEADER);\n\n\t// image dimensions\n\th = (size_t)read_le32(&sd->dwHeight);\n\tw = (size_t)read_le32(&sd->dwWidth);\n\n\t// pixel format\n\tRETURN_STATUS_IF_ERR(decode_pf(&sd->ddpf, bpp, flags));\n\n\t// if the image is not aligned with the S3TC block size, it is stored\n\t// with extra pixels on the bottom left to fill up the space, so we need\n\t// to account for those when calculating how big it should be\n\tsize_t stored_h, stored_w;\n\tif(flags & TEX_DXT)\n\t{\n\t\tstored_h = Align<4>(h);\n\t\tstored_w = Align<4>(w);\n\t}\n\telse\n\t{\n\t\tstored_h = h;\n\t\tstored_w = w;\n\t}\n\n\t// verify pitch or linear size, if given\n\tconst size_t pitch = stored_w*bpp/8;\n\tconst size_t sd_pitch_or_size = (size_t)read_le32(&sd->dwPitchOrLinearSize);\n\tif(sd_flags & DDSD_PITCH)\n\t{\n\t\tif(sd_pitch_or_size != Align<4>(pitch))\n\t\t\tDEBUG_WARN_ERR(ERR::CORRUPTED);\n\t}\n\tif(sd_flags & DDSD_LINEARSIZE)\n\t{\n\t\t// some DDS tools mistakenly store the total size of all levels,\n\t\t// so allow values close to that as well\n\t\tconst ssize_t totalSize = ssize_t(pitch*stored_h*1.333333f);\n\t\tif(sd_pitch_or_size != pitch*stored_h && abs(ssize_t(sd_pitch_or_size)-totalSize) > 64)\n\t\t\tDEBUG_WARN_ERR(ERR::CORRUPTED);\n\t}\n\t// note: both flags set would be invalid; no need to check for that,\n\t// though, since one of the above tests would fail.\n\n\t// mipmaps\n\tif(sd_flags & DDSD_MIPMAPCOUNT)\n\t{\n\t\tconst size_t mipmap_count = (size_t)read_le32(&sd->dwMipMapCount);\n\t\tif(mipmap_count)\n\t\t{\n\t\t\t// mipmap chain is incomplete\n\t\t\t// note: DDS includes the base level in its count, hence +1.\n\t\t\tif(mipmap_count != ceil_log2(std::max(w,h))+1)\n\t\t\t\tWARN_RETURN(ERR::TEX_FMT_INVALID);\n\t\t\tflags |= TEX_MIPMAPS;\n\t\t}\n\t}\n\n\t// check for volume textures\n\tif(sd_flags & DDSD_DEPTH)\n\t{\n\t\tconst size_t depth = (size_t)read_le32(&sd->dwDepth);\n\t\tif(depth)\n\t\t\tWARN_RETURN(ERR::NOT_SUPPORTED);\n\t}\n\n\t// check caps\n\t// .. this is supposed to be set, but don't bail if not (pointless)\n\tENSURE(sd->dwCaps & DDSCAPS_TEXTURE);\n\t// .. sanity check: warn if mipmap flag not set (don't bail if not\n\t// because we've already made the decision).\n\tconst bool mipmap_cap = (sd->dwCaps & DDSCAPS_MIPMAP) != 0;\n\tconst bool mipmap_flag = (flags & TEX_MIPMAPS) != 0;\n\tENSURE(mipmap_cap == mipmap_flag);\n\t// note: we do not check for cubemaps and volume textures (not supported)\n\t// because the file may still have useful data we can read.\n\n\treturn INFO::OK;\n}\n\n\n//-----------------------------------------------------------------------------\n\nbool TexCodecDds::is_hdr(const u8* file) const\n{\n\treturn *(u32*)file == FOURCC('D','D','S',' ');\n}\n\n\nbool TexCodecDds::is_ext(const OsPath& extension) const\n{\n\treturn extension == L\".dds\";\n}\n\n\nsize_t TexCodecDds::hdr_size(const u8* UNUSED(file)) const\n{\n\treturn 4+sizeof(DDS_HEADER);\n}\n\n\nStatus TexCodecDds::decode(rpU8 data, size_t UNUSED(size), Tex* RESTRICT t) const\n{\n\tconst DDS_HEADER* sd = (const DDS_HEADER*)(data+4);\n\tRETURN_STATUS_IF_ERR(decode_sd(sd, t->m_Width, t->m_Height, t->m_Bpp, t->m_Flags));\n\treturn INFO::OK;\n}\n\n\nStatus TexCodecDds::encode(Tex* RESTRICT UNUSED(t), DynArray* RESTRICT UNUSED(da)) const\n{\n\t// note: do not return ERR::NOT_SUPPORTED et al. because that would\n\t// break tex_write (which assumes either this, 0 or errors are returned).\n\treturn INFO::TEX_CODEC_CANNOT_HANDLE;\n}\n\n\nTIMER_ADD_CLIENT(tc_dds_transform);\n\nStatus TexCodecDds::transform(Tex* t, size_t transforms) const\n{\n\tTIMER_ACCRUE(tc_dds_transform);\n\n\tsize_t mipmaps = t->m_Flags & TEX_MIPMAPS;\n\tsize_t dxt = t->m_Flags & TEX_DXT;\n\tENSURE(is_valid_dxt(dxt));\n\n\tconst size_t transform_mipmaps = transforms & TEX_MIPMAPS;\n\tconst size_t transform_dxt = transforms & TEX_DXT;\n\t// requesting removal of mipmaps\n\tif(mipmaps && transform_mipmaps)\n\t{\n\t\t// we don't need to actually change anything except the flag - the\n\t\t// mipmap levels will just be treated as trailing junk\n\t\tt->m_Flags &= ~TEX_MIPMAPS;\n\t\treturn INFO::OK;\n\t}\n\t// requesting decompression\n\tif(dxt && transform_dxt)\n\t{\n\t\tRETURN_STATUS_IF_ERR(s3tc_decompress(t));\n\t\treturn INFO::OK;\n\t}\n\t// both are DXT (unsupported; there are no flags we can change while\n\t// compressed) or requesting compression (not implemented) or\n\t// both not DXT (nothing we can do) - bail.\n\treturn INFO::TEX_CODEC_CANNOT_HANDLE;\n}\n"
  },
  {
    "path": "fpsgame/gui/tex/tex_internal.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * private texture loader helper functions\n */\n\n#ifndef INCLUDED_TEX_INTERNAL\n#define INCLUDED_TEX_INTERNAL\n\n#include \"lib/pointer_typedefs.h\"\n#include \"lib/allocators/dynarray.h\"\n#include \"lib/file/io/io.h\"\t// io::Allocate\n\n/**\n * check if the given texture format is acceptable: 8bpp grey,\n * 24bpp color or 32bpp color+alpha (BGR / upside down are permitted).\n * basically, this is the \"plain\" format understood by all codecs and\n * tex_codec_plain_transform.\n * @param bpp bits per pixel\n * @param flags TexFlags\n * @return Status\n **/\nextern Status tex_validate_plain_format(size_t bpp, size_t flags);\n\n\n/**\n * indicate if the two vertical orientations match.\n *\n * used by tex_codec.\n * \n * @param src_flags TexFlags, used to extract the orientation.\n * we ask for this instead of src_orientation so callers don't have to\n * mask off TEX_ORIENTATION.\n * @param dst_orientation orientation to compare against.\n * can be one of TEX_BOTTOM_UP, TEX_TOP_DOWN, or 0 for the\n * \"global orientation\".\n * @return bool\n **/\nextern bool tex_orientations_match(size_t src_flags, size_t dst_orientation);\n\n#endif\t// #ifndef INCLUDED_TEX_INTERNAL\n"
  },
  {
    "path": "fpsgame/gui/tex/tex_png.cpp",
    "content": "/* Copyright (c) 2015 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * PNG codec using libpng.\n */\n\n#include \"precompiled.h\"\n\n#include \"lib/external_libraries/png.h\"\n\n#include \"lib/byte_order.h\"\n#include \"tex_codec.h\"\n#include \"lib/allocators/shared_ptr.h\"\n#include \"lib/timer.h\"\n\n#if MSC_VERSION\n\n// squelch \"dtor / setjmp interaction\" warnings.\n// all attempts to resolve the underlying problem failed; apparently\n// the warning is generated if setjmp is used at all in C++ mode.\n// (png_*_impl have no code that would trigger ctors/dtors, nor are any\n// called in their prolog/epilog code).\n# pragma warning(disable: 4611)\n\n#endif\t// MSC_VERSION\n\n\n//-----------------------------------------------------------------------------\n// \n//-----------------------------------------------------------------------------\n\nclass MemoryStream\n{\npublic:\n\tMemoryStream(rpU8 data, size_t size)\n\t\t: data(data), size(size), pos(0)\n\t{\n\t}\n\n\tsize_t RemainingSize() const\n\t{\n\t\tASSERT(pos <= size);\n\t\treturn size-pos;\n\t}\n\n\tvoid CopyTo(rpU8 dst, size_t dstSize)\n\t{\n\t\tmemcpy(dst, data+pos, dstSize);\n\t\tpos += dstSize;\n\t}\n\nprivate:\n\trpU8 data;\n\tsize_t size;\n\tsize_t pos;\n};\n\n\n// pass data from PNG file in memory to libpng\nstatic void io_read(png_struct* png_ptr, rpU8 data, png_size_t size)\n{\n\tMemoryStream* stream = (MemoryStream*)png_get_io_ptr(png_ptr);\n\tif(stream->RemainingSize() < size)\n\t{\n\t\tpng_error(png_ptr, \"PNG: not enough input\");\n\t\treturn;\n\t}\n\n\tstream->CopyTo(data, size);\n}\n\n\n// write libpng output to PNG file\nstatic void io_write(png_struct* png_ptr, u8* data, png_size_t length)\n{\n\tDynArray* da = (DynArray*)png_get_io_ptr(png_ptr);\n\tif(da_append(da, data, length) != 0)\n\t\tpng_error(png_ptr, \"io_write failed\");\n}\n\n\nstatic void io_flush(png_structp UNUSED(png_ptr))\n{\n}\n\n\n\n//-----------------------------------------------------------------------------\n\nStatus TexCodecPng::transform(Tex* UNUSED(t), size_t UNUSED(transforms)) const\n{\n\treturn INFO::TEX_CODEC_CANNOT_HANDLE;\n}\n\n\n// note: it's not worth combining png_encode and png_decode, due to\n// libpng read/write interface differences (grr).\n\n// split out of png_decode to simplify resource cleanup and avoid\n// \"dtor / setjmp interaction\" warning.\nstatic Status png_decode_impl(MemoryStream* stream, png_structp png_ptr, png_infop info_ptr, Tex* t)\n{\n\tpng_set_read_fn(png_ptr, stream, io_read);\n\n\t// read header and determine format\n\tpng_read_info(png_ptr, info_ptr);\n\tpng_uint_32 w, h;\n\tint bit_depth, color_type, interlace_type;\n\tpng_get_IHDR(png_ptr, info_ptr, &w, &h, &bit_depth, &color_type, &interlace_type, 0, 0);\n\t\n\t// (The following is based on GdkPixbuf's PNG image loader)\n\n\t// Convert the following images to 8-bit RGB/RGBA:\n\t// * indexed colors\n\t// * grayscale with alpha\n\t// * transparency header\n\t// * bit depth of 16 or less than 8\n\t// * interlaced\n\tif (color_type == PNG_COLOR_TYPE_PALETTE && bit_depth <= 8)\n\t\tpng_set_expand(png_ptr);\n\telse if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))\n\t\tpng_set_expand(png_ptr);\n\telse if (bit_depth < 8)\n\t\tpng_set_expand(png_ptr);\n\n\tif (bit_depth == 16)\n\t\tpng_set_strip_16(png_ptr);\n\tif (color_type == PNG_COLOR_TYPE_GRAY_ALPHA)\n\t\tpng_set_gray_to_rgb(png_ptr);\n\tif (interlace_type != PNG_INTERLACE_NONE)\n\t\tpng_set_interlace_handling(png_ptr);\n\n\t// Update info after transformations\n\tpng_read_update_info(png_ptr, info_ptr);\n\t\n\tpng_get_IHDR(png_ptr, info_ptr, &w, &h, &bit_depth, &color_type, &interlace_type, 0, 0);\n\n\t// make sure format is acceptable:\n\t// * non-zero dimensions\n\t// * 8-bit depth\n\t// * RGB, RGBA, or grayscale\n\t// * 1, 3 or 4 channels\n\tif (w == 0 || h == 0)\n\t\tWARN_RETURN(ERR::TEX_INVALID_SIZE);\n\tif (bit_depth != 8)\n\t\tWARN_RETURN(ERR::TEX_NOT_8BIT_PRECISION);\n\tif (!(color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_RGB_ALPHA || color_type == PNG_COLOR_TYPE_GRAY))\n\t\tWARN_RETURN(ERR::TEX_INVALID_COLOR_TYPE);\n\t\n\tconst int channels = png_get_channels(png_ptr, info_ptr);\n\tif (!(channels == 3 || channels == 4 || channels == 1))\n\t\tWARN_RETURN(ERR::TEX_FMT_INVALID);\n\n\tconst size_t pitch = png_get_rowbytes(png_ptr, info_ptr);\n\tconst u32 bpp = (u32)(pitch / w * 8);\n\n\tsize_t flags = 0;\n\tif (color_type == PNG_COLOR_TYPE_RGB_ALPHA)\n\t\tflags |= TEX_ALPHA;\n\tif (color_type == PNG_COLOR_TYPE_GRAY)\n\t\tflags |= TEX_GREY;\n\n\tconst size_t img_size = pitch * h;\n\tshared_ptr<u8> data;\n\tAllocateAligned(data, img_size, pageSize);\n\n\tstd::vector<RowPtr> rows = tex_codec_alloc_rows(data.get(), h, pitch, TEX_TOP_DOWN, 0);\n\tpng_read_image(png_ptr, (png_bytepp)&rows[0]);\n\tpng_read_end(png_ptr, info_ptr);\n\n\t// success; make sure all data was consumed.\n\tENSURE(stream->RemainingSize() == 0);\n\n\t// store image info and validate\n\treturn t->wrap(w,h,bpp,flags,data,0);\n}\n\n\n// split out of png_encode to simplify resource cleanup and avoid\n// \"dtor / setjmp interaction\" warning.\nstatic Status png_encode_impl(Tex* t, png_structp png_ptr, png_infop info_ptr, DynArray* da)\n{\n\tconst png_uint_32 w = (png_uint_32)t->m_Width, h = (png_uint_32)t->m_Height;\n\tconst size_t pitch = w * t->m_Bpp / 8;\n\n\tint color_type;\n\tswitch(t->m_Flags & (TEX_GREY|TEX_ALPHA))\n\t{\n\tcase TEX_GREY|TEX_ALPHA:\n\t\tcolor_type = PNG_COLOR_TYPE_GRAY_ALPHA;\n\t\tbreak;\n\tcase TEX_GREY:\n\t\tcolor_type = PNG_COLOR_TYPE_GRAY;\n\t\tbreak;\n\tcase TEX_ALPHA:\n\t\tcolor_type = PNG_COLOR_TYPE_RGB_ALPHA;\n\t\tbreak;\n\tdefault:\n\t\tcolor_type = PNG_COLOR_TYPE_RGB;\n\t\tbreak;\n\t}\n\n\tpng_set_write_fn(png_ptr, da, io_write, io_flush);\n\tpng_set_IHDR(png_ptr, info_ptr, w, h, 8, color_type,\n\t\tPNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);\n\n\tu8* data = t->get_data();\n\tstd::vector<RowPtr> rows = tex_codec_alloc_rows(data, h, pitch, t->m_Flags, TEX_TOP_DOWN);\n\n\t// PNG is native RGB.\n\tconst int png_transforms = (t->m_Flags & TEX_BGR)? PNG_TRANSFORM_BGR : PNG_TRANSFORM_IDENTITY;\n\n\tpng_set_rows(png_ptr, info_ptr, (png_bytepp)&rows[0]);\n\tpng_write_png(png_ptr, info_ptr, png_transforms, 0);\n\n\treturn INFO::OK;\n}\n\n\n\nbool TexCodecPng::is_hdr(const u8* file) const\n{\n\t// don't use png_sig_cmp, so we don't pull in libpng for\n\t// this check alone (it might not actually be used).\n\treturn *(u32*)file == FOURCC('\\x89','P','N','G');\n}\n\n\nbool TexCodecPng::is_ext(const OsPath& extension) const\n{\n\treturn extension == L\".png\";\n}\n\n\nsize_t TexCodecPng::hdr_size(const u8* UNUSED(file)) const\n{\n\treturn 0;\t// libpng returns decoded image data; no header\n}\n\nstatic void user_warning_fn(png_structp UNUSED(png_ptr), png_const_charp warning_msg)\n{\n\t// Suppress this warning because it's useless and occurs on a large number of files\n\t// see http://trac.wildfiregames.com/ticket/2184\n\tif (strcmp(warning_msg, \"iCCP: known incorrect sRGB profile\") == 0)\n\t\treturn;\n\tdebug_printf(\"libpng warning: %s\\n\", warning_msg);\n}\n\nTIMER_ADD_CLIENT(tc_png_decode);\n\n// limitation: palette images aren't supported\nStatus TexCodecPng::decode(rpU8 data, size_t size, Tex* RESTRICT t) const\n{\nTIMER_ACCRUE(tc_png_decode);\n\n\tpng_infop info_ptr = 0;\n\n\t// allocate PNG structures; use default stderr and longjmp error handler, use custom\n\t// warning handler to filter out useless messages\n\tpng_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, user_warning_fn);\n\tif(!png_ptr)\n\t\tWARN_RETURN(ERR::FAIL);\n\tinfo_ptr = png_create_info_struct(png_ptr);\n\tif(!info_ptr)\n\t{\n\t\tpng_destroy_read_struct(&png_ptr, &info_ptr, 0);\n\t\tWARN_RETURN(ERR::NO_MEM);\n\t}\n\t// setup error handling\n\tif(setjmp(png_jmpbuf(png_ptr)))\n\t{\n\t\t// libpng longjmps here after an error\n\t\tpng_destroy_read_struct(&png_ptr, &info_ptr, 0);\n\t\tWARN_RETURN(ERR::FAIL);\n\t}\n\n\tMemoryStream stream(data, size);\n\tStatus ret = png_decode_impl(&stream, png_ptr, info_ptr, t);\n\n\tpng_destroy_read_struct(&png_ptr, &info_ptr, 0);\n\t\n\treturn ret;\n}\n\n\n// limitation: palette images aren't supported\nStatus TexCodecPng::encode(Tex* RESTRICT t, DynArray* RESTRICT da) const\n{\n\tStatus ret = ERR::FAIL;\n\tpng_infop info_ptr = 0;\n\n\t// allocate PNG structures; use default stderr and longjmp error handlers\n\tpng_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);\n\tif(!png_ptr)\n\t\tWARN_RETURN(ERR::FAIL);\n\tinfo_ptr = png_create_info_struct(png_ptr);\n\tif(!info_ptr)\n\t\tgoto fail;\n\n\t// setup error handling\n\tif(setjmp(png_jmpbuf(png_ptr)))\n\t{\n\t\t// libpng longjmps here after an error\n\t\tgoto fail;\n\t}\n\n\tret = png_encode_impl(t, png_ptr, info_ptr, da);\n\n\t// shared cleanup\nfail:\n\tpng_destroy_write_struct(&png_ptr, &info_ptr);\n\treturn ret;\n}\n"
  },
  {
    "path": "fpsgame/gui/tex/tex_tga.cpp",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * TGA codec.\n */\n\n#include \"precompiled.h\"\n\n#include \"lib/byte_order.h\"\n#include \"tex_codec.h\"\n#include \"lib/bits.h\"\n\n#pragma pack(push, 1)\n\nenum TgaImgType\n{\n\tTGA_TRUE_COLOR = 2,\t// uncompressed 24 or 32 bit direct RGB\n\tTGA_GREY        = 3\t\t// uncompressed 8 bit direct greyscale\n};\n\nenum TgaImgDesc\n{\n\tTGA_RIGHT_TO_LEFT = BIT(4),\n\tTGA_TOP_DOWN      = BIT(5),\n};\n\ntypedef struct\n{\n\tu8 img_id_len;\t\t\t// 0 - no image identifier present\n\tu8 color_map_type;\t\t// 0 - no color map present\n\tu8 img_type;\t\t\t// see TgaImgType\n\tu8 color_map[5];\t\t// unused\n\n\tu16 x_origin;\t\t\t// unused\n\tu16 y_origin;\t\t\t// unused\n\n\tu16 w;\n\tu16 h;\n\tu8 bpp;\t\t\t\t\t// bits per pixel\n\n\tu8 img_desc;\n}\nTgaHeader;\n\n// TGA file: header [img id] [color map] image data\n\n#pragma pack(pop)\n\n\nStatus TexCodecTga::transform(Tex* UNUSED(t), size_t UNUSED(transforms)) const\n{\n\treturn INFO::TEX_CODEC_CANNOT_HANDLE;\n}\n\n\nbool TexCodecTga::is_hdr(const u8* file) const\n{\n\tTgaHeader* hdr = (TgaHeader*)file;\n\n\t// the first TGA header doesn't have a magic field;\n\t// we can only check if the first 4 bytes are valid\n\t// .. not direct color\n\tif(hdr->color_map_type != 0)\n\t\treturn false;\n\t// .. wrong color type (not uncompressed greyscale or RGB)\n\tif(hdr->img_type != TGA_TRUE_COLOR && hdr->img_type != TGA_GREY)\n\t\treturn false;\n\n\t// note: we can't check img_id_len or color_map[0] - they are\n\t// undefined and may assume any value.\n\n\treturn true;\n}\n\n\nbool TexCodecTga::is_ext(const OsPath& extension) const\n{\n\treturn extension == L\".tga\";\n}\n\n\nsize_t TexCodecTga::hdr_size(const u8* file) const\n{\n\tsize_t hdr_size = sizeof(TgaHeader);\n\tif(file)\n\t{\n\t\tTgaHeader* hdr = (TgaHeader*)file;\n\t\thdr_size += hdr->img_id_len;\n\t}\n\treturn hdr_size;\n}\n\n\n// requirements: uncompressed, direct color, bottom up\nStatus TexCodecTga::decode(rpU8 data, size_t UNUSED(size), Tex* RESTRICT t) const\n{\n\tconst TgaHeader* hdr = (const TgaHeader*)data;\n\tconst u8 type  = hdr->img_type;\n\tconst size_t w   = read_le16(&hdr->w);\n\tconst size_t h   = read_le16(&hdr->h);\n\tconst size_t bpp = hdr->bpp;\n\tconst u8 desc  = hdr->img_desc;\n\n\tsize_t flags = 0;\n\tflags |= (desc & TGA_TOP_DOWN)? TEX_TOP_DOWN : TEX_BOTTOM_UP;\n\tif(desc & 0x0F)\t// alpha bits\n\t\tflags |= TEX_ALPHA;\n\tif(bpp == 8)\n\t\tflags |= TEX_GREY;\n\tif(type == TGA_TRUE_COLOR)\n\t\tflags |= TEX_BGR;\n\n\t// sanity checks\n\t// .. storing right-to-left is just stupid;\n\t//    we're not going to bother converting it.\n\tif(desc & TGA_RIGHT_TO_LEFT)\n\t\tWARN_RETURN(ERR::TEX_INVALID_LAYOUT);\n\n\tt->m_Width  = w;\n\tt->m_Height = h;\n\tt->m_Bpp    = bpp;\n\tt->m_Flags  = flags;\n\treturn INFO::OK;\n}\n\n\nStatus TexCodecTga::encode(Tex* RESTRICT t, DynArray* RESTRICT da) const\n{\n\tu8 img_desc = 0;\n\tif(t->m_Flags & TEX_TOP_DOWN)\n\t\timg_desc |= TGA_TOP_DOWN;\n\tif(t->m_Bpp == 32)\n\t\timg_desc |= 8;\t// size of alpha channel\n\tTgaImgType img_type = (t->m_Flags & TEX_GREY)? TGA_GREY : TGA_TRUE_COLOR;\n\n\tsize_t transforms = t->m_Flags;\n\ttransforms &= ~TEX_ORIENTATION;\t// no flip needed - we can set top-down bit.\n\ttransforms ^= TEX_BGR;\t\t\t// TGA is native BGR.\n\n\tconst TgaHeader hdr =\n\t{\n\t\t0,\t\t\t\t// no image identifier present\n\t\t0,\t\t\t\t// no color map present\n\t\t(u8)img_type,\n\t\t{0,0,0,0,0},\t// unused (color map)\n\t\t0, 0,\t\t\t// unused (origin)\n\t\t(u16)t->m_Width,\n\t\t(u16)t->m_Height,\n\t\t(u8)t->m_Bpp,\n\t\timg_desc\n\t};\n\tconst size_t hdr_size = sizeof(hdr);\n\treturn tex_codec_write(t, transforms, &hdr, hdr_size, da);\n}\n\n"
  },
  {
    "path": "fpsgame/gui/timer.cpp",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * platform-independent high resolution timer\n */\n\n#include \"precompiled.h\"\n#include \"lib/timer.h\"\n\n#include <sstream>\t// std::stringstream\n#include <numeric>\n#include <cmath>\n#include <cfloat>\n#include <cstdarg>\n\n#include \"lib/module_init.h\"\n#include \"lib/posix/posix_pthread.h\"\n#include \"lib/posix/posix_time.h\"\n# include \"lib/sysdep/cpu.h\"\n#if OS_WIN\n# include \"lib/sysdep/os/win/whrt/whrt.h\"\n#endif\n#if OS_UNIX\n# include <unistd.h>\n#endif\n\n#if OS_UNIX || OS_WIN\n# define HAVE_GETTIMEOFDAY 1\n#else\n# define HAVE_GETTIMEOFDAY 0\n#endif\n\n#if (defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0) || OS_WIN\n# define HAVE_CLOCK_GETTIME 1\n#else\n# define HAVE_CLOCK_GETTIME 0\n#endif\n\n// rationale for wrapping gettimeofday and clock_gettime, instead of just\n// emulating them where not available: allows returning higher-resolution\n// timer values than their us / ns interface, via double [seconds].\n// they're also not guaranteed to be monotonic.\n\n#if HAVE_CLOCK_GETTIME\nstatic struct timespec start;\n#elif HAVE_GETTIMEOFDAY\nstatic struct timeval start;\n#endif\n\n\n//-----------------------------------------------------------------------------\n// timer API\n\nvoid timer_LatchStartTime()\n{\n#if OS_WIN\n\t// whrt_Time starts at zero, nothing needs to be done.\n#elif HAVE_CLOCK_GETTIME\n\t(void)clock_gettime(CLOCK_REALTIME, &start);\n#elif HAVE_GETTIMEOFDAY\n\tgettimeofday(&start, 0);\n#endif\n}\n\nstatic pthread_mutex_t ensure_monotonic_mutex = PTHREAD_MUTEX_INITIALIZER;\n// NB: does not guarantee strict monotonicity - callers must avoid\n// dividing by the difference of two equal times.\nstatic void EnsureMonotonic(double& newTime)\n{\n\tpthread_mutex_lock(&ensure_monotonic_mutex);\n\tstatic double maxTime;\n\tmaxTime = std::max(maxTime, newTime);\n\tnewTime = maxTime;\n\tpthread_mutex_unlock(&ensure_monotonic_mutex);\n}\n\n\ndouble timer_Time()\n{\n\tdouble t;\n\n#if OS_WIN\n\tt = whrt_Time();\n#elif HAVE_CLOCK_GETTIME\n\tENSURE(start.tv_sec || start.tv_nsec);\t// must have called timer_LatchStartTime first\n\tstruct timespec cur;\n\t(void)clock_gettime(CLOCK_REALTIME, &cur);\n\tt = (cur.tv_sec - start.tv_sec) + (cur.tv_nsec - start.tv_nsec)*1e-9;\n#elif HAVE_GETTIMEOFDAY\n\tENSURE(start.tv_sec || start.tv_usec);\t// must have called timer_LatchStartTime first\n\tstruct timeval cur;\n\tgettimeofday(&cur, 0);\n\tt = (cur.tv_sec - start.tv_sec) + (cur.tv_usec - start.tv_usec)*1e-6;\n#else\n# error \"timer_Time: add timer implementation for this platform!\"\n#endif\n\n\tEnsureMonotonic(t);\n\treturn t;\n}\n\n\n// cached because the default implementation may take several milliseconds\nstatic double resolution;\n\nstatic Status InitResolution()\n{\n#if OS_WIN\n\tresolution = whrt_Resolution();\n#elif HAVE_CLOCK_GETTIME\n\tstruct timespec ts;\n\tif(clock_getres(CLOCK_REALTIME, &ts) == 0)\n\t\tresolution = ts.tv_nsec * 1e-9;\n#else\n\tconst double t0 = timer_Time();\n\tdouble t1, t2;\n\tdo t1 = timer_Time(); while(t1 == t0);\n\tdo t2 = timer_Time(); while(t2 == t1);\n\tresolution = t2-t1;\n#endif\n\n\treturn INFO::OK;\n}\n\ndouble timer_Resolution()\n{\n\tstatic ModuleInitState initState;\n\tModuleInit(&initState, InitResolution);\n\treturn resolution;\n}\n\n\n//-----------------------------------------------------------------------------\n// client API\n\n// intrusive linked-list of all clients. a fixed-size limit would be\n// acceptable (since timers are added manually), but the list is easy\n// to implement and only has the drawback of exposing TimerClient to users.\n//\n// do not use std::list et al. for this! we must be callable at any time,\n// especially before NLSO ctors run or before heap init.\nstatic size_t numClients;\nstatic TimerClient* clients;\n\n\nTimerClient* timer_AddClient(TimerClient* tc, const wchar_t* description)\n{\n\ttc->sum.SetToZero();\n\n\ttc->description = description;\n\n\t// insert at front of list\n\ttc->next = clients;\n\tclients = tc;\n\tnumClients++;\n\n\treturn tc;\n}\n\n\nvoid timer_DisplayClientTotals()\n{\n\tdebug_printf(\"TIMER TOTALS (%lu clients)\\n\", (unsigned long)numClients);\n\tdebug_printf(\"-----------------------------------------------------\\n\");\n\n\twhile(clients)\n\t{\n\t\t// (make sure list and count are consistent)\n\t\tENSURE(numClients != 0);\n\t\tTimerClient* tc = clients;\n\t\tclients = tc->next;\n\t\tnumClients--;\n\n\t\tconst std::string duration = tc->sum.ToString();\n\t\tdebug_printf(\"  %s: %s (%lux)\\n\", utf8_from_wstring(tc->description).c_str(), duration.c_str(), (unsigned long)tc->num_calls);\n\t}\n\n\tdebug_printf(\"-----------------------------------------------------\\n\");\n}\n\n\n//-----------------------------------------------------------------------------\n\nstd::string StringForSeconds(double seconds)\n{\n\tdouble scale = 1e6;\n\tconst char* unit = \" us\";\n\tif(seconds > 1.0)\n\t\tscale = 1, unit = \" s\";\n\telse if(seconds > 1e-3)\n\t\tscale = 1e3, unit = \" ms\";\n\n\tstd::stringstream ss;\n\tss << seconds*scale;\n\tss << unit;\n\treturn ss.str();\n}\n\n\nstd::string StringForCycles(Cycles cycles)\n{\n\tdouble scale = 1.0;\n\tconst char* unit = \" c\";\n\tif(cycles > 10000000000LL)\t// 10 Gc\n\t\tscale = 1e-9, unit = \" Gc\";\n\telse if(cycles > 10000000)\t// 10 Mc\n\t\tscale = 1e-6, unit = \" Mc\";\n\telse if(cycles > 10000)\t// 10 kc\n\t\tscale = 1e-3, unit = \" kc\";\n\n\tstd::stringstream ss;\n\tss << cycles*scale;\n\tss << unit;\n\treturn ss.str();\n}\n"
  },
  {
    "path": "fpsgame/gui/timer.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * platform-independent high resolution timer\n */\n\n#ifndef INCLUDED_TIMER\n#define INCLUDED_TIMER\n\n#include \"lib/config2.h\"\t// CONFIG2_TIMER_ALLOW_RDTSC\n#include \"lib/sysdep/cpu.h\"\t// cpu_AtomicAdd\n#if ARCH_X86_X64 && CONFIG2_TIMER_ALLOW_RDTSC\n# include \"lib/sysdep/os_cpu.h\"\t// os_cpu_ClockFrequency\n# include \"lib/sysdep/arch/x86_x64/x86_x64.h\"\t// x86_x64::rdtsc\n#endif\n\n#include \"lib/utf8.h\"\n\n/**\n * timer_Time will subsequently return values relative to the current time.\n **/\nLIB_API void timer_LatchStartTime();\n\n/**\n * @return high resolution (> 1 us) timestamp [s].\n **/\nLIB_API double timer_Time();\n\n/**\n * @return resolution [s] of the timer.\n **/\nLIB_API double timer_Resolution();\n\n\n// (allow using XADD (faster than CMPXCHG) in 64-bit builds without casting)\n#if ARCH_AMD64\ntypedef intptr_t Cycles;\n#else\ntypedef i64 Cycles;\n#endif\n\n/**\n * internal helper functions for returning an easily readable\n * string (i.e. re-scaled to appropriate units)\n **/\nLIB_API std::string StringForSeconds(double seconds);\nLIB_API std::string StringForCycles(Cycles cycles);\n\n\n//-----------------------------------------------------------------------------\n// scope timing\n\n/// used by TIMER\nclass ScopeTimer\n{\n\tNONCOPYABLE(ScopeTimer);\npublic:\n\tScopeTimer(const wchar_t* description)\n\t\t: m_t0(timer_Time()), m_description(description)\n\t{\n\t}\n\n\t~ScopeTimer()\n\t{\n\t\tconst double t1 = timer_Time();\n\t\tconst std::string elapsedTimeString = StringForSeconds(t1-m_t0);\n\t\tdebug_printf(\"TIMER| %s: %s\\n\", utf8_from_wstring(m_description).c_str(), elapsedTimeString.c_str());\n\t}\n\nprivate:\n\tdouble m_t0;\n\tconst wchar_t* m_description;\n};\n\n/**\n * Measures the time taken to execute code up until end of the current scope; \n * displays it via debug_printf. Can safely be nested.\n * Useful for measuring time spent in a function or basic block.\n * <description> must remain valid over the lifetime of this object;\n * a string literal is safest.\n * \n * Example usage:\n * \tvoid func()\n * \t{\n * \t\tTIMER(L\"description\");\n * \t\t// code to be measured\n * \t}\n **/\n#define TIMER(description) ScopeTimer UID__(description)\n\n/**\n * Measures the time taken to execute code between BEGIN and END markers;\n * displays it via debug_printf. Can safely be nested.\n * Useful for measuring several pieces of code within the same function/block.\n * <description> must remain valid over the lifetime of this object;\n * a string literal is safest.\n * \n * Caveats:\n * - this wraps the code to be measured in a basic block, so any\n *   variables defined there are invisible to surrounding code.\n * - the description passed to END isn't inspected; you are responsible for\n *   ensuring correct nesting!\n * \n * Example usage:\n * \tvoid func2()\n * \t{\n * \t\t// uninteresting code\n * \t\tTIMER_BEGIN(L\"description2\");\n * \t\t// code to be measured\n * \t\tTIMER_END(L\"description2\");\n * \t\t// uninteresting code\n * \t}\n **/\n#define TIMER_BEGIN(description) { ScopeTimer UID__(description)\n#define TIMER_END(description) }\n\n\n//-----------------------------------------------------------------------------\n// cumulative timer API\n\n// this supplements in-game profiling by providing low-overhead,\n// high resolution time accounting of specific areas.\n\n// since TIMER_ACCRUE et al. are called so often, we try to keep\n// overhead to an absolute minimum. storing raw tick counts (e.g. CPU cycles\n// returned by x86_x64::rdtsc) instead of absolute time has two benefits:\n// - no need to convert from raw->time on every call\n//   (instead, it's only done once when displaying the totals)\n// - possibly less overhead to querying the time itself\n//   (timer_Time may be using slower time sources with ~3us overhead)\n//\n// however, the cycle count is not necessarily a measure of wall-clock time\n// (see http://www.gamedev.net/reference/programming/features/timing).\n// therefore, on systems with SpeedStep active, measurements of I/O or other\n// non-CPU bound activity may be skewed. this is ok because the timer is\n// only used for profiling; just be aware of the issue.\n// if this is a problem, disable CONFIG2_TIMER_ALLOW_RDTSC.\n// \n// note that overflow isn't an issue either way (63 bit cycle counts\n// at 10 GHz cover intervals of 29 years).\n\n#if ARCH_X86_X64 && CONFIG2_TIMER_ALLOW_RDTSC\n\nclass TimerUnit\n{\npublic:\n\tvoid SetToZero()\n\t{\n\t\tm_cycles = 0;\n\t}\n\n\tvoid SetFromTimer()\n\t{\n\t\tm_cycles = x86_x64::rdtsc();\n\t}\n\n\tvoid AddDifference(TimerUnit t0, TimerUnit t1)\n\t{\n\t\tm_cycles += t1.m_cycles - t0.m_cycles;\n\t}\n\n\tvoid AddDifferenceAtomic(TimerUnit t0, TimerUnit t1)\n\t{\n\t\tconst Cycles delta = t1.m_cycles - t0.m_cycles;\n#if ARCH_AMD64\n\t\tcpu_AtomicAdd(&m_cycles, delta);\n#elif ARCH_IA32\nretry:\n\t\tif(!cpu_CAS64(&m_cycles, m_cycles, m_cycles+delta))\n\t\t\tgoto retry;\n#else\n# error \"port\"\n#endif\n\t}\n\n\tvoid Subtract(TimerUnit t)\n\t{\n\t\tm_cycles -= t.m_cycles;\n\t}\n\n\tstd::string ToString() const\n\t{\n\t\tENSURE(m_cycles >= 0);\n\t\treturn StringForCycles(m_cycles);\n\t}\n\n\tdouble ToSeconds() const\n\t{\n\t\treturn (double)m_cycles / os_cpu_ClockFrequency();\n\t}\n\nprivate:\n\tCycles m_cycles;\n};\n\n#else\n\nclass TimerUnit\n{\npublic:\n\tvoid SetToZero()\n\t{\n\t\tm_seconds = 0.0;\n\t}\n\n\tvoid SetFromTimer()\n\t{\n\t\tm_seconds = timer_Time();\n\t}\n\n\tvoid AddDifference(TimerUnit t0, TimerUnit t1)\n\t{\n\t\tm_seconds += t1.m_seconds - t0.m_seconds;\n\t}\n\n\tvoid AddDifferenceAtomic(TimerUnit t0, TimerUnit t1)\n\t{\nretry:\n\t\ti64 oldRepresentation;\n\t\tmemcpy(&oldRepresentation, &m_seconds, sizeof(oldRepresentation));\n\n\t\tconst double seconds = m_seconds + t1.m_seconds - t0.m_seconds;\n\t\ti64 newRepresentation;\n\t\tmemcpy(&newRepresentation, &seconds, sizeof(newRepresentation));\n\n\t\tif(!cpu_CAS64((volatile i64*)&m_seconds, oldRepresentation, newRepresentation))\n\t\t\tgoto retry;\n\t}\n\n\tvoid Subtract(TimerUnit t)\n\t{\n\t\tm_seconds -= t.m_seconds;\n\t}\n\n\tstd::string ToString() const\n\t{\n\t\tENSURE(m_seconds >= 0.0);\n\t\treturn StringForSeconds(m_seconds);\n\t}\n\n\tdouble ToSeconds() const\n\t{\n\t\treturn m_seconds;\n\t}\n\nprivate:\n\tdouble m_seconds;\n};\n\n#endif\n\n// opaque - do not access its fields!\n// note: must be defined here because clients instantiate them;\n// fields cannot be made private due to POD requirement.\nstruct TimerClient\n{\n\tTimerUnit sum;\t// total bill\n\n\t// only store a pointer for efficiency.\n\tconst wchar_t* description;\n\n\tTimerClient* next;\n\n\t// how often the timer was billed (helps measure relative\n\t// performance of something that is done indeterminately often).\n\tintptr_t num_calls;\n};\n\n/**\n * make the given TimerClient (usually instantiated as static data)\n * ready for use. returns its address for TIMER_ADD_CLIENT's convenience.\n * this client's total (which is increased by a BillingPolicy) will be\n * displayed by timer_DisplayClientTotals.\n * notes:\n * - may be called at any time;\n * - always succeeds (there's no fixed limit);\n * - free() is not needed nor possible.\n * - description must remain valid until exit; a string literal is safest.\n **/\nLIB_API TimerClient* timer_AddClient(TimerClient* tc, const wchar_t* description);\n\n/**\n * \"allocate\" a new TimerClient that will keep track of the total time\n * billed to it, along with a description string. These are displayed when\n * timer_DisplayClientTotals is called.\n * Invoke this at file or function scope; a (static) TimerClient pointer of\n * name \\<id\\> will be defined, which should be passed to TIMER_ACCRUE.\n **/\n#define TIMER_ADD_CLIENT(id)\\\n\tstatic TimerClient UID__;\\\n\tstatic TimerClient* id = timer_AddClient(&UID__, WIDEN(#id))\n\n/**\n * bill the difference between t0 and t1 to the client's total.\n **/\nstruct BillingPolicy_Default\n{\n\tvoid operator()(TimerClient* tc, TimerUnit t0, TimerUnit t1) const\n\t{\n\t\ttc->sum.AddDifference(t0, t1);\n\t\ttc->num_calls++;\n\t}\n};\n\n/**\n * thread-safe (not used by default due to its higher overhead)\n * note: we can't just use thread-local variables to avoid\n * synchronization overhead because we don't have control over all\n * threads (for accumulating their separate timer copies).\n **/\nstruct BillingPolicy_Atomic\n{\n\tvoid operator()(TimerClient* tc, TimerUnit t0, TimerUnit t1) const\n\t{\n\t\ttc->sum.AddDifferenceAtomic(t0, t1);\n\t\tcpu_AtomicAdd(&tc->num_calls, +1);\n\t}\n};\n\n/**\n * display all clients' totals; does not reset them.\n * typically called at exit.\n **/\nLIB_API void timer_DisplayClientTotals();\n\n\n/// used by TIMER_ACCRUE\ntemplate<class BillingPolicy = BillingPolicy_Default>\nclass ScopeTimerAccrue\n{\n\tNONCOPYABLE(ScopeTimerAccrue);\npublic:\n\tScopeTimerAccrue(TimerClient* tc)\n\t\t: m_tc(tc)\n\t{\n\t\tm_t0.SetFromTimer();\n\t}\n\n\t~ScopeTimerAccrue()\n\t{\n\t\tTimerUnit t1;\n\t\tt1.SetFromTimer();\n\t\tBillingPolicy()(m_tc, m_t0, t1);\n\t}\n\nprivate:\n\tTimerUnit m_t0;\n\tTimerClient* m_tc;\n};\n\n/**\n * Measure the time taken to execute code up until end of the current scope; \n * bill it to the given TimerClient object. Can safely be nested.\n * Useful for measuring total time spent in a function or basic block over the\n * entire program.\n * `client' is an identifier registered via TIMER_ADD_CLIENT.\n * \n * Example usage:\n * \tTIMER_ADD_CLIENT(client);\n *\n * \tvoid func()\n * \t{\n * \t\tTIMER_ACCRUE(client);\n * \t\t// code to be measured\n * \t}\n * \n * \t[later or at exit]\n * \ttimer_DisplayClientTotals();\n **/\n#define TIMER_ACCRUE(client) ScopeTimerAccrue<> UID__(client)\n#define TIMER_ACCRUE_ATOMIC(client) ScopeTimerAccrue<BillingPolicy_Atomic> UID__(client)\n\n#endif\t// #ifndef INCLUDED_TIMER\n"
  },
  {
    "path": "fpsgame/gui/types.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * convenient type aliases (shorter than stdint.h's uintN_t)\n */\n\n#ifndef INCLUDED_TYPES\n#define INCLUDED_TYPES\n\n#include \"lib/posix/posix_types.h\"\n\ntypedef int8_t i8;\ntypedef int16_t i16;\ntypedef int32_t i32;\ntypedef int64_t i64;\n\ntypedef uint8_t u8;\ntypedef uint16_t u16;\ntypedef uint32_t u32;\ntypedef uint64_t u64;\n\ntypedef unsigned int uint;\n\n#endif // #ifndef INCLUDED_TYPES\n"
  },
  {
    "path": "fpsgame/gui/utf8.cpp",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#include \"precompiled.h\"\n#include \"lib/utf8.h\"\n\nstatic const StatusDefinition utf8StatusDefinitions[] = {\n\t{ ERR::UTF8_SURROGATE, L\"UTF-16 surrogate pairs aren't supported\" },\n\t{ ERR::UTF8_OUTSIDE_BMP, L\"Code point outside BMP (> 0x10000)\" },\n\t{ ERR::UTF8_NONCHARACTER, L\"Noncharacter (e.g. WEOF)\" },\n\t{ ERR::UTF8_INVALID_UTF8, L\"Invalid UTF-8 sequence\" }\n};\nSTATUS_ADD_DEFINITIONS(utf8StatusDefinitions);\n\n\n// adapted from http://unicode.org/Public/PROGRAMS/CVTUTF/ConvertUTF.c\n// which bears the following notice:\n/*\n* Copyright 2001-2004 Unicode, Inc.\n* \n* Disclaimer\n* \n* This source code is provided as is by Unicode, Inc. No claims are\n* made as to fitness for any particular purpose. No warranties of any\n* kind are expressed or implied. The recipient agrees to determine\n* applicability of information provided. If this file has been\n* purchased on magnetic or optical media from Unicode, Inc., the\n* sole remedy for any claim will be exchange of defective media\n* within 90 days of receipt.\n* \n* Limitations on Rights to Redistribute This Code\n* \n* Unicode, Inc. hereby grants the right to freely use the information\n* supplied in this file in the creation of products supporting the\n* Unicode Standard, and to make copies of this file in any form\n* for internal or external distribution as long as this notice\n* remains attached.\n*/\n\n// design rationale:\n// - to cope with wchar_t differences between VC (UTF-16) and\n//   GCC (UCS-4), we only allow codepoints in the BMP.\n//   encoded UTF-8 sequences are therefore no longer than 3 bytes.\n// - surrogates are disabled because variable-length strings\n//   violate the purpose of using wchar_t instead of UTF-8.\n// - replacing disallowed characters instead of aborting outright\n//   avoids overly inconveniencing users and eases debugging.\n\n// this implementation survives http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt\n\n// (must be unsigned to avoid sign extension)\ntypedef u8 UTF8;\ntypedef u32 UTF32;\n\n\n// called from ReplaceIfInvalid and UTF8Codec::Decode\nstatic UTF32 RaiseError(Status err, Status* perr)\n{\n\tif(perr)\t// caller wants return code, not warning dialog\n\t{\n\t\tif(*perr == INFO::OK)\t// only return the first error (see header)\n\t\t\t*perr = err;\n\t}\n\telse\n\t\tDEBUG_WARN_ERR(err);\n\n\treturn 0xFFFDul;\t// replacement character\n}\n\n\nstatic UTF32 ReplaceIfInvalid(UTF32 u, Status* err)\n{\n\t// disallow surrogates\n\tif(0xD800ul <= u && u <= 0xDFFFul)\n\t\treturn RaiseError(ERR::UTF8_SURROGATE, err);\n\t// outside BMP (UTF-16 representation would require surrogates)\n\tif(u > 0xFFFFul)\n\t\treturn RaiseError(ERR::UTF8_OUTSIDE_BMP, err);\n\t// noncharacter (note: WEOF (0xFFFF) causes VC's swprintf to fail)\n\tif(u == 0xFFFEul || u == 0xFFFFul || (0xFDD0ul <= u && u <= 0xFDEFul))\n\t\treturn RaiseError(ERR::UTF8_NONCHARACTER, err);\n\treturn u;\n}\n\n\nclass UTF8Codec\n{\npublic:\n\tstatic void Encode(UTF32 u, UTF8*& dstPos)\n\t{\n\t\tswitch (Size(u))\n\t\t{\n\t\tcase 1:\n\t\t\t*dstPos++ = UTF8(u);\n\t\t\tbreak;\n\t\tcase 2:\n\t\t\t*dstPos++ = UTF8((u >> 6) | 0xC0);\n\t\t\t*dstPos++ = UTF8((u | 0x80u) & 0xBFu);\n\t\t\tbreak;\n\t\tcase 3:\n\t\t\t*dstPos++ = UTF8((u >> 12) | 0xE0);\n\t\t\t*dstPos++ = UTF8(((u >> 6) | 0x80u) & 0xBFu);\n\t\t\t*dstPos++ = UTF8((u | 0x80u) & 0xBFu);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t// @return decoded scalar, or replacementCharacter on error\n\tstatic UTF32 Decode(const UTF8*& srcPos, const UTF8* const srcEnd, Status* err)\n\t{\n\t\tconst size_t size = SizeFromFirstByte(*srcPos);\n\t\tif(!IsValid(srcPos, size, srcEnd))\n\t\t{\n\t\t\tsrcPos += 1;\t// only skip the offending byte (increases chances of resynchronization)\n\t\t\treturn RaiseError(ERR::UTF8_INVALID_UTF8, err);\n\t\t}\n\n\t\tUTF32 u = 0;\n\t\tfor(size_t i = 0; i < size-1; i++)\n\t\t{\n\t\t\tu += UTF32(*srcPos++);\n\t\t\tu <<= 6;\n\t\t}\n\t\tu += UTF32(*srcPos++);\n\n\t\tstatic const UTF32 offsets[1+4] = { 0, 0x00000000ul, 0x00003080ul, 0x000E2080ul, 0x03C82080UL };\n\t\tu -= offsets[size];\n\t\treturn u;\n\t}\n\nprivate:\n\tstatic inline size_t Size(UTF32 u)\n\t{\n\t\tif(u < 0x80)\n\t\t\treturn 1;\n\t\tif(u < 0x800)\n\t\t\treturn 2;\n\t\t// ReplaceIfInvalid ensures > 3 byte encodings are never used.\n\t\treturn 3;\n\t}\n\n\tstatic inline size_t SizeFromFirstByte(UTF8 firstByte)\n\t{\n\t\tif(firstByte < 0xC0)\n\t\t\treturn 1;\n\t\tif(firstByte < 0xE0)\n\t\t\treturn 2;\n\t\tif(firstByte < 0xF0)\n\t\t\treturn 3;\n\t\t// IsValid rejects firstByte values that would cause > 4 byte encodings.\n\t\treturn 4;\n\t}\n\n\t// c.f. Unicode 3.1 Table 3-7\n\t// @param size obtained via SizeFromFirstByte (our caller also uses it)\n\tstatic bool IsValid(const UTF8* const src, size_t size, const UTF8* const srcEnd)\n\t{\n\t\tif(src+size > srcEnd)\t// not enough data\n\t\t\treturn false;\n\n\t\tif(src[0] < 0x80)\n\t\t\treturn true;\n\t\tif(!(0xC2 <= src[0] && src[0] <= 0xF4))\n\t\t\treturn false;\n\n\t\t// special cases (stricter than the loop)\n\t\tif(src[0] == 0xE0 && src[1] < 0xA0)\n\t\t\treturn false;\n\t\tif(src[0] == 0xED && src[1] > 0x9F)\n\t\t\treturn false;\n\t\tif(src[0] == 0xF0 && src[1] < 0x90)\n\t\t\treturn false;\n\t\tif(src[0] == 0xF4 && src[1] > 0x8F)\n\t\t\treturn false;\n\n\t\tfor(size_t i = 1; i < size; i++)\n\t\t{\n\t\t\tif(!(0x80 <= src[i] && src[i] <= 0xBF))\n\t\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t}\n};\n\n\n//-----------------------------------------------------------------------------\n\nstd::string utf8_from_wstring(const std::wstring& src, Status* err)\n{\n\tif(err)\n\t\t*err = INFO::OK;\n\n\tstd::string dst(src.size()*3+1, ' ');\t// see UTF8Codec::Size; +1 ensures &dst[0] is valid\n\tUTF8* dstPos = (UTF8*)&dst[0];\n\tfor(size_t i = 0; i < src.size(); i++)\n\t{\n\t\tconst UTF32 u = ReplaceIfInvalid(UTF32(src[i]), err);\n\t\tUTF8Codec::Encode(u, dstPos);\n\t}\n\tdst.resize(dstPos - (UTF8*)&dst[0]);\n\treturn dst;\n}\n\n\nstd::wstring wstring_from_utf8(const std::string& src, Status* err)\n{\n\tif(err)\n\t\t*err = INFO::OK;\n\n\tstd::wstring dst;\n\tdst.reserve(src.size());\n\tconst UTF8* srcPos = (const UTF8*)src.data();\n\tconst UTF8* const srcEnd = srcPos + src.size();\n\twhile(srcPos < srcEnd)\n\t{\n\t\tconst UTF32 u = UTF8Codec::Decode(srcPos, srcEnd, err);\n\t\tdst.push_back((wchar_t)ReplaceIfInvalid(u, err));\n\t}\n\treturn dst;\n}\n"
  },
  {
    "path": "fpsgame/gui/utf8.h",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#ifndef INCLUDED_UTF8\n#define INCLUDED_UTF8\n\n// note: error codes are returned via optional output parameter.\nnamespace ERR\n{\n\tconst Status UTF8_SURROGATE     = -100700;\n\tconst Status UTF8_OUTSIDE_BMP   = -100701;\n\tconst Status UTF8_NONCHARACTER  = -100702;\n\tconst Status UTF8_INVALID_UTF8  = -100703;\n}\n\n/**\n * convert UTF-8 to a wide string (UTF-16 or UCS-4, depending on the\n * platform's wchar_t).\n *\n * @param s input string (UTF-8)\n * @param err if nonzero, this receives the first error encountered\n * (the rest may be subsequent faults) or INFO::OK if all went well.\n * otherwise, the function raises a warning dialog for every\n * error/warning.\n **/\nLIB_API std::wstring wstring_from_utf8(const std::string& s, Status* err = 0);\n\n/**\n * opposite of wstring_from_utf8\n **/\nLIB_API std::string utf8_from_wstring(const std::wstring& s, Status* err = 0);\n\n#endif\t// #ifndef INCLUDED_UTF8\n"
  },
  {
    "path": "fpsgame/gui/wsecure_crt.cpp",
    "content": "/* Copyright (c) 2010 Wildfire Games\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n * \n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n// unicode version of secure_crt\n\n#include \"precompiled.h\"\n\n#define WSECURE_CRT\n#include \"secure_crt.cpp\"\n#undef WSECURE_CRT\n"
  },
  {
    "path": "fpsgame/precompiled.cpp",
    "content": "/* Copyright (C) 2009 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"precompiled.h\"\n"
  },
  {
    "path": "fpsgame/precompiled.h",
    "content": "/* Copyright (C) 2009 Wildfire Games.\n * This file is part of 0 A.D.\n *\n * 0 A.D. is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * 0 A.D. is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef INCLUDED_COLLADA_PRECOMPILED\n#define INCLUDED_COLLADA_PRECOMPILED\n\n#define COLLADA_DLL\n#include \"DLL.h\"\n\nextern void Log(int severity, const char* fmt, ...);\n\n#ifdef _WIN32\n# define WIN32\n# define WIN32_LEAN_AND_MEAN\n# pragma warning(disable: 4996)\n#endif\n\n#include <climits>\n\n#include \"FCollada.h\"\n#include \"FCDocument/FCDAsset.h\"\n#include \"FCDocument/FCDocument.h\"\n#include \"FCDocument/FCDocumentTools.h\"\n#include \"FCDocument/FCDAnimated.h\"\n#include \"FCDocument/FCDAnimationCurve.h\"\n#include \"FCDocument/FCDController.h\"\n#include \"FCDocument/FCDControllerInstance.h\"\n#include \"FCDocument/FCDExtra.h\"\n#include \"FCDocument/FCDGeometry.h\"\n#include \"FCDocument/FCDGeometryMesh.h\"\n#include \"FCDocument/FCDGeometryPolygons.h\"\n#include \"FCDocument/FCDGeometryPolygonsTools.h\"\n#include \"FCDocument/FCDGeometrySource.h\"\n#include \"FCDocument/FCDSceneNode.h\"\n#include \"FCDocument/FCDSkinController.h\"\n#include \"FUtils/FUDaeSyntax.h\"\n#include \"FUtils/FUFileManager.h\"\n#include \"FUtils/FUXmlParser.h\"\n\n#include <cassert>\n#include <cstdarg>\n#include <string>\n\n// FCollada pollutes the global namespace by defining these\n// to std::{min,max}, so undo its macros\n#undef min\n#undef max\n\n#endif // INCLUDED_COLLADA_PRECOMPILED\n"
  },
  {
    "path": "fpsgame/stdafx.cpp",
    "content": "// stdafx.cpp : ֻ׼ļԴļ\n// fpsgame.pch ΪԤͷ\n// stdafx.obj ԤϢ\n\n#include \"stdafx.h\"\n\n// TODO:  STDAFX.H κĸͷļ\n//ڴļ\n"
  },
  {
    "path": "fpsgame/stdafx.h",
    "content": "// stdafx.h : ׼ϵͳļİļ\n// Ǿʹõĵ\n// ضĿİļ\n//\n\n#pragma once\n\n#include \"targetver.h\"\n\n#define WIN32_LEAN_AND_MEAN             //  Windows ͷųʹõ\n// Windows ͷļ: \n#include <windows.h>\n\n// C ʱͷļ\n#include <stdlib.h>\n#include <malloc.h>\n#include <memory.h>\n#include <tchar.h>\n\n\n// TODO: ڴ˴óҪͷļ\n"
  },
  {
    "path": "fpsgame/targetver.h",
    "content": "#pragma once\n\n//  SDKDDKVer.h õ߰汾 Windows ƽ̨\n\n// ҪΪǰ Windows ƽ̨Ӧó WinSDKVer.h\n//  _WIN32_WINNT ΪҪֵ֧ƽ̨Ȼٰ SDKDDKVer.h\n\n#include <SDKDDKVer.h>\n"
  },
  {
    "path": "fpsgame/test_Color.h",
    "content": "/* Copyright (C) 2009 Wildfire Games.\n* This file is part of 0 A.D.\n*\n* 0 A.D. is free software: you can redistribute it and/or modify\n* it under the terms of the GNU General Public License as published by\n* the Free Software Foundation, either version 2 of the License, or\n* (at your option) any later version.\n*\n* 0 A.D. is distributed in the hope that it will be useful,\n* but WITHOUT ANY WARRANTY; without even the implied warranty of\n* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n* GNU General Public License for more details.\n*\n* You should have received a copy of the GNU General Public License\n* along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#include \"lib/self_test.h\"\n\n#include \"graphics/Color.h\"\n\nclass TestColor : public CxxTest::TestSuite\n{\npublic:\n\tvoid setUp()\n\t{\n\t\tColorActivateFastImpl();\n\t}\n\n\tvoid test_Color4ub()\n\t{\n\t\tCheckColor(0, 0, 0, 0x000000);\n\t\tCheckColor(1, 0, 0, 0x0000ff);\n\t\tCheckColor(0, 1, 0, 0x00ff00);\n\t\tCheckColor(0, 0, 1, 0xff0000);\n\t\tCheckColor(1, 1, 1, 0xffffff);\n\t}\n\nprivate:\n\tvoid CheckColor(int r, int g, int b, u32 expected)\n\t{\n\t\tSColor4ub colorStruct = ConvertRGBColorTo4ub(RGBColor(r, g, b));\n\t\tu32 actual;\n\t\tmemcpy(&actual, &colorStruct, sizeof(u32));\n\t\texpected |= 0xff000000;\t// ConvertRGBColorTo4ub sets alpha to opaque \n\t\tTS_ASSERT_EQUALS(expected, actual);\n\t}\n};\n"
  },
  {
    "path": "fpsgame/tests.py",
    "content": "from ctypes import *\nimport sys\nimport os\nimport xml.etree.ElementTree as ET\n\nbinaries = '../../../binaries'\n\n# Work out the platform-dependent library filename\ndll_filename = {\n\t'posix': './libCollada_dbg.so',\n\t'nt': 'Collada_dbg.dll',\n}[os.name]\n\n# The DLL may need other DLLs which are in its directory, so set the path to that\n# (Don't care about clobbering the old PATH - it doesn't have anything important)\nos.environ['PATH'] = '%s/system/' % binaries"
  },
  {
    "path": "fpsgame.sln",
    "content": "﻿\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio 15\nVisualStudioVersion = 15.0.26430.14\nMinimumVisualStudioVersion = 10.0.40219.1\nProject(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"fpsgame\", \"fpsgame\\fpsgame.vcxproj\", \"{057B5724-2848-4AB5-91E2-D9F26C9E9BB6}\"\nEndProject\nProject(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"Test\", \"Test\\Test.vcxproj\", \"{C944C3E9-7AD8-4117-B664-DBE932214E45}\"\nEndProject\nGlobal\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n\t\tDebug|x64 = Debug|x64\n\t\tDebug|x86 = Debug|x86\n\t\tRelease|x64 = Release|x64\n\t\tRelease|x86 = Release|x86\n\tEndGlobalSection\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n\t\t{057B5724-2848-4AB5-91E2-D9F26C9E9BB6}.Debug|x64.ActiveCfg = Debug|x64\n\t\t{057B5724-2848-4AB5-91E2-D9F26C9E9BB6}.Debug|x64.Build.0 = Debug|x64\n\t\t{057B5724-2848-4AB5-91E2-D9F26C9E9BB6}.Debug|x86.ActiveCfg = Debug|Win32\n\t\t{057B5724-2848-4AB5-91E2-D9F26C9E9BB6}.Debug|x86.Build.0 = Debug|Win32\n\t\t{057B5724-2848-4AB5-91E2-D9F26C9E9BB6}.Release|x64.ActiveCfg = Release|x64\n\t\t{057B5724-2848-4AB5-91E2-D9F26C9E9BB6}.Release|x64.Build.0 = Release|x64\n\t\t{057B5724-2848-4AB5-91E2-D9F26C9E9BB6}.Release|x86.ActiveCfg = Release|Win32\n\t\t{057B5724-2848-4AB5-91E2-D9F26C9E9BB6}.Release|x86.Build.0 = Release|Win32\n\t\t{C944C3E9-7AD8-4117-B664-DBE932214E45}.Debug|x64.ActiveCfg = Debug|x64\n\t\t{C944C3E9-7AD8-4117-B664-DBE932214E45}.Debug|x64.Build.0 = Debug|x64\n\t\t{C944C3E9-7AD8-4117-B664-DBE932214E45}.Debug|x86.ActiveCfg = Debug|Win32\n\t\t{C944C3E9-7AD8-4117-B664-DBE932214E45}.Debug|x86.Build.0 = Debug|Win32\n\t\t{C944C3E9-7AD8-4117-B664-DBE932214E45}.Release|x64.ActiveCfg = Release|x64\n\t\t{C944C3E9-7AD8-4117-B664-DBE932214E45}.Release|x64.Build.0 = Release|x64\n\t\t{C944C3E9-7AD8-4117-B664-DBE932214E45}.Release|x86.ActiveCfg = Release|Win32\n\t\t{C944C3E9-7AD8-4117-B664-DBE932214E45}.Release|x86.Build.0 = Release|Win32\n\tEndGlobalSection\n\tGlobalSection(SolutionProperties) = preSolution\n\t\tHideSolutionNode = FALSE\n\tEndGlobalSection\nEndGlobal\n"
  },
  {
    "path": "fpsgame.vcxproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project DefaultTargets=\"Build\" ToolsVersion=\"15.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <ItemGroup Label=\"ProjectConfigurations\">\n    <ProjectConfiguration Include=\"Debug|Win32\">\n      <Configuration>Debug</Configuration>\n      <Platform>Win32</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release|Win32\">\n      <Configuration>Release</Configuration>\n      <Platform>Win32</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Debug|x64\">\n      <Configuration>Debug</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release|x64\">\n      <Configuration>Release</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n  </ItemGroup>\n  <PropertyGroup Label=\"Globals\">\n    <VCProjectVersion>15.0</VCProjectVersion>\n    <ProjectGuid>{057B5724-2848-4AB5-91E2-D9F26C9E9BB6}</ProjectGuid>\n    <Keyword>Win32Proj</Keyword>\n    <RootNamespace>fpsgame</RootNamespace>\n    <WindowsTargetPlatformVersion>10.0.15063.0</WindowsTargetPlatformVersion>\n  </PropertyGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>true</UseDebugLibraries>\n    <PlatformToolset>v141</PlatformToolset>\n    <CharacterSet>Unicode</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v141</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>Unicode</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>true</UseDebugLibraries>\n    <PlatformToolset>v141</PlatformToolset>\n    <CharacterSet>Unicode</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v141</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>Unicode</CharacterSet>\n  </PropertyGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\n  <ImportGroup Label=\"ExtensionSettings\">\n  </ImportGroup>\n  <ImportGroup Label=\"Shared\">\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <PropertyGroup Label=\"UserMacros\" />\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\n    <LinkIncremental>true</LinkIncremental>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <LinkIncremental>true</LinkIncremental>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\n    <LinkIncremental>false</LinkIncremental>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <LinkIncremental>false</LinkIncremental>\n  </PropertyGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\n    <ClCompile>\n      <PrecompiledHeader>Use</PrecompiledHeader>\n      <WarningLevel>Level3</WarningLevel>\n      <Optimization>Disabled</Optimization>\n      <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <ClCompile>\n      <PrecompiledHeader>Use</PrecompiledHeader>\n      <WarningLevel>Level3</WarningLevel>\n      <Optimization>Disabled</Optimization>\n      <PreprocessorDefinitions>_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <PrecompiledHeader>Use</PrecompiledHeader>\n      <Optimization>MaxSpeed</Optimization>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <PrecompiledHeader>Use</PrecompiledHeader>\n      <Optimization>MaxSpeed</Optimization>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <PreprocessorDefinitions>NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemGroup>\n    <Text Include=\"ReadMe.txt\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ClInclude Include=\"fpsgame.h\" />\n    <ClInclude Include=\"Resource.h\" />\n    <ClInclude Include=\"stdafx.h\" />\n    <ClInclude Include=\"targetver.h\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ClCompile Include=\"fpsgame.cpp\" />\n    <ClCompile Include=\"stdafx.cpp\">\n      <PrecompiledHeader Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">Create</PrecompiledHeader>\n      <PrecompiledHeader Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">Create</PrecompiledHeader>\n      <PrecompiledHeader Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">Create</PrecompiledHeader>\n      <PrecompiledHeader Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">Create</PrecompiledHeader>\n    </ClCompile>\n  </ItemGroup>\n  <ItemGroup>\n    <ResourceCompile Include=\"fpsgame.rc\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Image Include=\"fpsgame.ico\" />\n    <Image Include=\"small.ico\" />\n  </ItemGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\n  <ImportGroup Label=\"ExtensionTargets\">\n  </ImportGroup>\n</Project>"
  },
  {
    "path": "sys/SysControl.cpp",
    "content": "#include \"SysControl.h\"\n#include \"ControlsApp.h\"\n#include \"ControlsXPad360.h\"\n#include \"Engine.h\"\n#include \"App.h\"\n#include \"Engine.h\"\n#include \"App.h\"\n#include \"Input.h\"\n#include \"ObjectGui.h\"\n#include \"WidgetLabel.h\"\n\n\n\n#ifdef _WIN32\n#pragma execution_character_set(\"utf-8\")\n#endif\n\nusing namespace MathLib;\n\nclass CSysControlLocal : public CSysControl\n{\npublic:\n\tCSysControlLocal();\n\tvirtual ~CSysControlLocal();\n\n\tvirtual void Init();\n\tvirtual void Update(float ifps);\n\tvirtual void Shutdown();\n\n\tvirtual int GetState(int state);\n\tvirtual int ClearState(int state);\n\n\tvirtual float GetMouseDX();\n\tvirtual float GetMouseDY();\n\n\tvirtual void SetMouseGrab(int g);\n\tvirtual int GetMouseGrab();\n\n\n\tvirtual void SetControlMode(ControlMode mode);\n\tvirtual ControlMode GetControlMode() const;\n\nprivate:\n\tvoid Update_Mouse(float ifps);\n\tvoid Update_Keyboard(float ifps);\n\tvoid Update_XPad360(float ifps);\n\n\tCControlsXPad360    *m_pControlsXPad360;\n\tControlMode         m_nControlMode;\t\n\n\tint                 m_nOldMouseX;\n\tint                 m_nOldMouseY;\n\n\tCObjectGui          *m_pTest3DUI;\n\tCWidgetLabel        *m_pTestMessageLabel;\n\n\n\n\n};\n\n\n\nCSysControlLocal::CSysControlLocal()\n{\n\n}\nCSysControlLocal::~CSysControlLocal()\n{\n\n}\n\nvoid CSysControlLocal::Init()\n{\n\tm_pControlsApp = new CControlsApp;\n\tm_pControlsXPad360 = new CControlsXPad360(0);\n\n\n\tg_Engine.pApp->SetMouseGrab(0);\n\tg_Engine.pApp->SetMouseShow(0);\n\n\tSetControlMode(CONTROL_KEYBORAD_MOUSE);\n\n\tm_pTest3DUI = new CObjectGui(2.0f, 1.0f, \"data/core/gui/\");\n\tm_pTest3DUI->SetMouseShow(0);\n\tm_pTest3DUI->SetBackground(1);\n\tm_pTest3DUI->SetBackgroundColor(vec4(1.0f, 0.0f, 0.0f, 1.0f));\n\n\tm_pTest3DUI->SetScreenSize(800, 400);\n\tm_pTest3DUI->SetControlDistance(1000.0f);\n\tm_pTest3DUI->CreateMaterial(\"gui_base\");\n\n\n\tm_pTest3DUI->SetWorldTransform(Translate(0.0f, 0.0f, 2.0f) * MakeRotationFromZY(vec3(0.0f, -1.0f, 0.0f), vec3(0.0f, 0.0f, 1.0f)));\n\n\tm_pTestMessageLabel = new CWidgetLabel(m_pTest3DUI->GetGui());\n\tm_pTest3DUI->GetGui()->AddChild(m_pTestMessageLabel, CGui::ALIGN_CENTER);\n\tm_pTestMessageLabel->SetFontColor(vec4(0.0f, 0.0f, 0.0f, 1.0f));\n\n\tm_pTestMessageLabel = new CWidgetLabel(m_pTest3DUI->GetGui());\n\tm_pTest3DUI->GetGui()->AddChild(m_pTestMessageLabel, CGui::ALIGN_CENTER);\n\tm_pTestMessageLabel->SetFontColor(vec4(0.0f, 0.0f, 0.0f, 1.0f));\n\n\tm_pTestMessageLabel->SetFontSize(80);\n\tm_pTestMessageLabel->SetFontOutline(1);\n\tm_pTestMessageLabel->SetText(\"两个黄鹂鸣翠柳\\n一行白鹭上青天\\n窗含西岭千秋雪\\n门泊东吴万里船\");\n\n\n\tvoid CSysControlLocal::Update(float ifps)\n\t{\n\t\tUpdate_Mouse(ifps);\n\t\tUpdate_Keyboard(ifps);\n\t\tUpdate_XPad360(ifps);\n\t}\n\n\n\tm_nOldMouseX = 0;\n\tm_nOldMouseY = 0;\n}\nvoid CSysControlLocal::Shutdown()\n{\n\tg_Engine.pApp->SetMouseGrab(0);\n\tg_Engine.pApp->SetMouseShow(0);\n\tdelete m_pControlsApp;\n\tm_pControlsApp = NULL;\n\tdelete m_pControlsXPad360;\n\tm_pControlsXPad360 = NULL;\n\tdelete m_pTestMessageLabel;\n\tm_pTestMessageLabel = NULL;\n\tdelete m_pTest3DUI;\n\tm_pTest3DUI = NULL;\n\n}\n\nint CSysControlLocal::GetState(int state)\n{\n\n\treturn m_pControlsApp->GetState(state);\n}\n\nint CSysControlLocal::ClearState(int state)\n{\n\treturn m_pControlsApp->ClearState(state);\n}\n\nfloat CSysControlLocal::GetMouseDX()\n{\n\treturn m_pControlsApp->GetMouseDX();\n}\n\nfloat CSysControlLocal::GetMouseDY()\n{\n\treturn m_pControlsApp->GetMouseDY();\n}\nvoid CSysControlLocal::SetMouseGrab(int g)\n{\n\tg_Engine.pApp->SetMouseGrab(g);\n\tg_Engine.pGui->SetMouseShow(!g);\n}\nint CSysControlLocal::GetMouseGrab()\n{\n\treturn g_Engine.pApp->GetMouseGrab();\n}\n\nvoid CSysControlLocal::SetControlMode(ControlMode mode)\n{\n\tm_nControlMode = mode;\n}\n\nCSysControl::ControlMode CSysControlLocal::GetControlMode() const\n{\n\treturn m_nControlMode;\n}\n\nCSysControl::ControlMode CSysControlLocal::GetControlMode() const\n{\n\treturn m_nControlMode;\n}\nvoid CSysControlLocal::Update_Mouse(float ifps)\n{\n\tfloat dx = (g_Engine.pApp->GetMouseX() - m_nOldMouseX) * g_Engine.pControls->GetMouseSensitivity() * 0.1f;//0.1f这个数值越大，鼠标移动越快\n\tfloat dy = (g_Engine.pApp->GetMouseY() - m_nOldMouseY) * g_Engine.pControls->GetMouseSensitivity() * 0.1f;//0.1这个数值越小，鼠标移动越慢\n\n\tm_pControlsApp->SetMouseDX(dx);\n\tm_pControlsApp->SetMouseDY(dy);\n\n\tif (g_Engine.pApp->GetMouseGrab() && g_Engine.pApp->GetActive())\n\t{\n\t\tg_Engine.pApp->SetMouse(g_Engine.pApp->GetWidth() / 2, g_Engine.pApp->GetHeight() / 2);\n\t}\n\n\tm_nOldMouseX = g_Engine.pApp->GetMouseX();\n\tm_nOldMouseY = g_Engine.pApp->GetMouseY();\n}\n\nvoid CSysControlLocal::Update_Keyboard(float ifps)\n{\n\n\n\tif (g_Engine.pInput->IsKeyDown('w'))\n\t\tm_pControlsApp->SetState(CControls::STATE_FORWARD, 1);\n\tif (g_Engine.pInput->IsKeyDown('s'))\n\t\tm_pControlsApp->SetState(CControls::STATE_BACKWARD, 1);\n\tif (g_Engine.pInput->IsKeyDown('a'))\n\t\tm_pControlsApp->SetState(CControls::STATE_MOVE_LEFT, 1);\n\tif (g_Engine.pInput->IsKeyDown('d'))\n\t\tm_pControlsApp->SetState(CControls::STATE_MOVE_RIGHT, 1);\n\n\tif (g_Engine.pInput->IsKeyUp('w'))\n\t\tm_pControlsApp->SetState(CControls::STATE_FORWARD, 0);\n\telse if (g_Engine.pInput->IsKeyUp('s'))\n\t\tm_pControlsApp->SetState(CControls::STATE_BACKWARD, 0);\n\n\tif (g_Engine.pInput->IsKeyUp('a'))\n\t\tm_pControlsApp->SetState(CControls::STATE_MOVE_LEFT, 0);\n\telse if (g_Engine.pInput->IsKeyUp('d'))\n\t\tm_pControlsApp->SetState(CControls::STATE_MOVE_RIGHT, 0);\n\tif (g_Engine.pInput->IsKeyDown(' '))\n\t\tm_pControlsApp->SetState(CControls::STATE_JUMP, 1);\n\telse\n\t\tm_pControlsApp->SetState(CControls::STATE_JUMP, 0);\n}\nvoid CSysControlLocal::Update_XPad360(float ifps)\n{\n\tm_pControlsXPad360->UpdateEvents();\n\tif (m_pControlsXPad360->IsAvailable())\n\t{\n\t\tCUtilStr strMessage;\n\t\tstrMessage = CUtilStr(\"测试3D UI\\n\"),\n\t\tstrMessage += CUtilStr(m_pControlsXPad360->GetName()) + \"\\n\";\n\n\n\t\tstrMessage += CUtilStr::Format(\"\\n手柄测试\\n\");\n\t\tstrMessage += CUtilStr::Format(\"LeftX:  %5.2f\\n\", m_pControlsXPad360->GetLeftX());\n\t\tstrMessage += CUtilStr::Format(\"LeftY:  %5.2f\\n\", m_pControlsXPad360->GetLeftY());\n\t\tstrMessage += CUtilStr::Format(\"RightX: %5.2f\\n\", m_pControlsXPad360->GetRightX());\n\t\tstrMessage += CUtilStr::Format(\"RightY: %5.2f\\n\", m_pControlsXPad360->GetRightY());\n\t\tstrMessage += \"\\nTriggers:\\n\";\n\t\tstrMessage += CUtilStr::Format(\"Left:   %5.2f\\n\", m_pControlsXPad360->GetLeftTrigger());\n\t\tstrMessage += CUtilStr::Format(\"Right:  %5.2f\\n\", m_pControlsXPad360->GetRightTrigger());\n\n\t\tstrMessage += CUtilStr::Format(\"\\nButtons:\\n\");\n\t\tfor (int i = 0; i < CControlsXPad360::NUM_BUTTONS; ++i)\n\t\t{\n\t\t\tstrMessage += CUtilStr::Format(\"%d \", m_pControlsXPad360->GetButton(i));\n\t\t}\n\t\tm_pTestMessageLabel->SetText(strMessage.Get());\n\n\t\tconst float fPadThreshold = 0.5f;\n\t\tif (m_pControlsXPad360->GetLeftX() > fPadThreshold)\n\t\t\tm_pControlsApp->SetState(CControls::STATE_MOVE_RIGHT, 1);\n\t\telse if (m_pControlsXPad360->GetLeftX() < -fPadThreshold)\n\t\t\tm_pControlsApp->SetState(CControls::STATE_MOVE_LEFT, 1);\n\t\telse\n\t\t{\n\t\t\tm_pControlsApp->SetState(CControls::STATE_MOVE_LEFT, 0);\n\t\t\tm_pControlsApp->SetState(CControls::STATE_MOVE_RIGHT, 0);\n\t\t}\n\t\tif (m_pControlsXPad360->GetLeftY() > fPadThreshold)\n\t\t\tm_pControlsApp->SetState(CControls::STATE_FORWARD, 1);\n\t\telse if (m_pControlsXPad360->GetLeftY() < -fPadThreshold)\n\t\t\tm_pControlsApp->SetState(CControls::STATE_BACKWARD, 1);\n\t\telse\n\t\t{\n\t\t\tm_pControlsApp->SetState(CControls::STATE_FORWARD, 0);\n\t\t\tm_pControlsApp->SetState(CControls::STATE_BACKWARD, 0);\n\t\t}\n\t\tif (m_pControlsXPad360->GetButton(CControlsXPad360::BUTTON_SHOULDER_LEFT) || m_pControlsXPad360->GetButton(CControlsXPad360::BUTTON_SHOULDER_RIGHT))\n\t\t\tm_pControlsApp->SetState(CControls::STATE_JUMP, 1);\n\t\telse\n\t\t\tm_pControlsApp->SetState(CControls::STATE_JUMP, 0);\n\t\tif (m_pControlsXPad360->GetRightTrigger() > fPadThreshold || m_pControlsXPad360->GetLeftTrigger() > fPadThreshold)\n\t\t{\n\t\t\tm_pControlsApp->SetState(CControls::STATE_FIRE, 1);\n\t\t\t//震动\n\t\t\tm_pControlsXPad360->SetLeftMotor(0.3f);\n\t\t\tm_pControlsXPad360->SetRightMotor(0.3f);\n\t\t}\n\t\telse if (m_pControlsXPad360->GetRightTrigger() < fPadThreshold && m_pControlsXPad360->GetLeftTrigger() < fPadThreshold)\n\t\t{\n\t\t\tm_pControlsApp->SetState(CControls::STATE_FIRE, 0);\n\n\t\t\tm_pControlsXPad360->SetLeftMotor(0.0f);\n\t\t\tm_pControlsXPad360->SetRightMotor(0.0f);\n\t\t}\n\t}\n}\n\nCSysControlLocal local;\nCSysControl *g_pSysControl = &local;"
  },
  {
    "path": "sys/SysControl.h",
    "content": "#ifndef __SYS_CONTROL_H__\n#define __SYS_CONTROL_H__\n\nclass CSysControl\t//\n{\npublic:\n\t// ״̬\n\tenum {\t\t\t\t\t\n\t\tSTATE_FORWARD = 0,\t//ǰ\n\t\tSTATE_BACKWARD,\t\t//\n\t\tSTATE_MOVE_LEFT,\t//ƶ\n\t\tSTATE_MOVE_RIGHT,\t//ƶ\n\t\tSTATE_TURN_UP,\t\t//ת\n\t\tSTATE_TURN_DOWN,\t//ת\n\t\tSTATE_TURN_LEFT,\t//ת\n\t\tSTATE_TURN_RIGHT,\t//ת\n\t\tSTATE_CROUCH,\t\t//\n\t\tSTATE_JUMP,\t\t\t//\n\t\tSTATE_RUN,\t\t\t//\n\t\tSTATE_USE,\t\t\t\n\t\tSTATE_FIRE,\t\t\t//\n\t\tSTATE_SAVE,\t\t\t//\n\t\tSTATE_RESTORE,\t\t//ָ\n\t\tSTATE_SCREENSHOT,\t//Ļ\n\t\tSTATE_AUX_0,\t\t//豸\n\t\tSTATE_AUX_1,\n\t\tSTATE_AUX_2,\n\t\tSTATE_AUX_3,\n\t\tSTATE_AUX_4,\n\t\tSTATE_AUX_5,\n\t\tSTATE_AUX_6,\n\t\tSTATE_AUX_7,\n\t\tSTATE_AUX_8,\n\t\tSTATE_AUX_9,\n\t\tSTATE_AUX_A,\n\t\tSTATE_AUX_B,\n\t\tSTATE_AUX_C,\n\t\tSTATE_AUX_D,\n\t\tSTATE_AUX_E,\n\t\tSTATE_AUX_F,\n\t\tNUM_STATES,\t\t\t\n\t};\n\tenum ControlMode\t\t//ģʽֻмpadַʽ\n\t{\n\t\tCONTROL_KEYBORAD_MOUSE,\n\t\tCONTROL_XPAD360,\n\t};\n\tCSysControl() {};\n\tvirtual ~CSysControl() {};\n\n\tvirtual void Init() = 0;\t//ʼ\n\tvirtual void Update(float ifps) = 0;\t\n\tvirtual void Shutdown() = 0;\t//ر\n\tvirtual int GetState(int state) = 0;\t//ȡ״̬\n\tvirtual int ClearState(int state) = 0;\t\t//״̬\n\tvirtual float GetMouseDX() = 0;\t//ȡX\n\tvirtual float GetMouseDY() = 0;\t//ȡY\n\n\tvirtual void SetMouseGrab(int g) = 0;\n\tvirtual int GetMouseGrab() = 0;\n\n\tvirtual void SetControlMode(ControlMode mode) = 0;\t\t//ÿģʽ\n\tvirtual ControlMode GetControlMode() const = 0;\t\t\t\t//ȡģʽ\nprivate:\n\t//ȫǹк\n};\n//externڱߺǰԱʾߺĶڱļУʾ˱ͺʱģѰ䶨\nextern CSysControl *g_pSysControl;\t//ܶģжá\n#endif"
  }
]