// Based on SphereWorld4.cpp
#include <GLTools.h>
#include <GLShaderManager.h>
#include <GLFrustum.h>
#include <GLBatch.h>
#include <GLFrame.h>
#include <GLMatrixStack.h>
#include <GLGeometryTransform.h>
#include <StopWatch.h>
#include <math.h>
#include <math3d.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <fstream>
#include <sstream>
#include <iostream>
#include "md2model.h"
using namespace std;
//#ifdef __APPLE__ // Don't need on Windows
//#include <glut/glut.h>
//#else
#define FREEGLUT_STATIC
#include <GL/glut.h>
//#endif
// ** GLOBALS **
GLShaderManager shaderManager; // Shader Manager
GLMatrixStack modelViewMatrix; // Modelview Matrix
GLMatrixStack projectionMatrix; // Projection Matrix
GLFrustum viewFrustum; // View Frustum
GLGeometryTransform transformPipeline; // Geometry Transform Pipeline
GLBatch floorBatch;
GLBatch backWallBatch;
GLBatch frontWallBatch;
GLBatch leftWallBatch;
GLBatch rightWallBatch;
GLFrame cameraFrame;
GLFrame modelFrame;
md2model model1;
GLuint currentAnim = MD2_STAND;
// Skybox objects
GLuint textures[10];
GLBatch boxPosX;
GLBatch boxNegX;
GLBatch boxPosY;
GLBatch boxNegY;
GLBatch boxPosZ;
GLBatch boxNegZ;
// ** FUNCTIONS **
//////////////////////////////////////////////////////////////////
// TEXTURE LOADER
GLuint loadTexture(char *fname, GLuint *texID)
{
// generate texture ID
glGenTextures(1, texID);
// texture dimensions and data buffer
GLint width, height, components;
GLenum format;
GLbyte *pBytes;
// load file - using GLTools library
pBytes = gltReadTGABits(fname, &width, &height, &components, &format);
if (pBytes == NULL)
{
cerr << "Something went wrong loading texture " << fname << endl;
}
// bind texture and set parameters
glBindTexture(GL_TEXTURE_2D, *texID);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // GL_REPEAT
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexImage2D(GL_TEXTURE_2D,0,components,width, height, 0,
format, GL_UNSIGNED_BYTE, pBytes);
//glGenerateMipmap(GL_TEXTURE_2D);
// texture loaded, free the temporary buffer
free(pBytes);
return *texID; // return value of texure ID - redundant
}
//////////////////////////////////////////////////////////////////
// This function does any needed initialization on the rendering
// context.
void SetupRC()
{
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
// Initialze Shader Manager
shaderManager.InitializeStockShaders();
glShadeModel (GL_SMOOTH);
glEnable(GL_DEPTH_TEST);
glEnable (GL_TEXTURE_2D);
//////////////////////////////////////////////////////////////////
// LOAD TEXTURES
textures[0] = loadTexture("pos_x.tga", &textures[0]);
textures[1] = loadTexture("neg_x.tga", &textures[1]);
textures[2] = loadTexture("pos_y.tga", &textures[2]);
textures[3] = loadTexture("neg_y.tga", &textures[3]);
textures[4] = loadTexture("pos_z.tga", &textures[4]);
textures[5] = loadTexture("neg_z.tga", &textures[5]);
textures[6] = loadTexture("grass.tga", &textures[6]);
textures[7] = loadTexture("brick.tga", &textures[7]);
textures[8] = loadTexture("skeleton.tga", &textures[8]);
if (!model1.ReadMD2Model ("skeleton.md2"))
exit (EXIT_FAILURE);
// END
//////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////
// SKYBOX
boxPosX.Begin(GL_TRIANGLE_FAN,4,1);
boxPosX.MultiTexCoord2f(0,0.0f,0.0f);
boxPosX.Vertex3f(10.0f,-10.0f,-10.0f);
boxPosX.MultiTexCoord2f(0,1.0f,0.0f);
boxPosX.Vertex3f(10.0f,-10.0f,10.0f);
boxPosX.MultiTexCoord2f(0,1.0f,1.0f);
boxPosX.Vertex3f(10.0f,10.0f,10.0f);
boxPosX.MultiTexCoord2f(0,0.0f,1.0f);
boxPosX.Vertex3f(10.0f,10.0f,-10.0f);
boxPosX.End();
boxNegX.Begin(GL_TRIANGLE_FAN,4,1);
boxNegX.MultiTexCoord2f(0,0.0f,0.0f);
boxNegX.Vertex3f(-10.0f,-10.0f,10.0f);
boxNegX.MultiTexCoord2f(0,1.0f,0.0f);
boxNegX.Vertex3f(-10.0f,-10.0f,-10.0f);
boxNegX.MultiTexCoord2f(0,1.0f,1.0f);
boxNegX.Vertex3f(-10.0f,10.0f,-10.0f);
boxNegX.MultiTexCoord2f(0,0.0f,1.0f);
boxNegX.Vertex3f(-10.0f,10.0f,10.0f);
boxNegX.End();
boxPosY.Begin(GL_TRIANGLE_FAN,4,1);
boxPosY.MultiTexCoord2f(0,0.0f,0.0f);
boxPosY.Vertex3f(-10.0f,10.0f,-10.0f);
boxPosY.MultiTexCoord2f(0,1.0f,0.0f);
boxPosY.Vertex3f(10.0f,10.0f,-10.0f);
boxPosY.MultiTexCoord2f(0,1.0f,1.0f);
boxPosY.Vertex3f(10.0f,10.0f,10.0f);
boxPosY.MultiTexCoord2f(0,0.0f,1.0f);
boxPosY.Vertex3f(-10.0f,10.0f,10.0f);
boxPosY.End();
boxNegY.Begin(GL_TRIANGLE_FAN,4,1);
boxNegY.MultiTexCoord2f(0,0.0f,0.0f);
boxNegY.Vertex3f(-10.0f,-10.0f,10.0f);
boxNegY.MultiTexCoord2f(0,1.0f,0.0f);
boxNegY.Vertex3f(10.0f,-10.0f,10.0f);
boxNegY.MultiTexCoord2f(0,1.0f,1.0f);
boxNegY.Vertex3f(10.0f,-10.0f,-10.0f);
boxNegY.MultiTexCoord2f(0,0.0f,1.0f);
boxNegY.Vertex3f(-10.0f,-10.0f,-10.0f);
boxNegY.End();
boxPosZ.Begin(GL_TRIANGLE_FAN,4,1);
boxPosZ.MultiTexCoord2f(0,0.0f,0.0f);
boxPosZ.Vertex3f(10.0f,-10.0f,10.0f);
boxPosZ.MultiTexCoord2f(0,1.0f,0.0f);
boxPosZ.Vertex3f(-10.0f,-10.0f,10.0f);
boxPosZ.MultiTexCoord2f(0,1.0f,1.0f);
boxPosZ.Vertex3f(-10.0f,10.0f,10.0f);
boxPosZ.MultiTexCoord2f(0,0.0f,1.0f);
boxPosZ.Vertex3f(10.0f,10.0f,10.0f);
boxPosZ.End();
boxNegZ.Begin(GL_TRIANGLE_FAN,4,1);
boxNegZ.MultiTexCoord2f(0,0.0f,0.0f);
boxNegZ.Vertex3f(-10.0f,-10.0f,-10.0f);
boxNegZ.MultiTexCoord2f(0,1.0f,0.0f);
boxNegZ.Vertex3f(10.0f,-10.0f,-10.0f);
boxNegZ.MultiTexCoord2f(0,1.0f,1.0f);
boxNegZ.Vertex3f(10.0f,10.0f,-10.0f);
boxNegZ.MultiTexCoord2f(0,0.0f,1.0f);
boxNegZ.Vertex3f(-10.0f,10.0f,-10.0f);
boxNegZ.End();
// END
//////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////
// FLOOR
GLfloat texSize = 10.0f;
floorBatch.Begin(GL_TRIANGLE_FAN, 4, 1);
floorBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
floorBatch.Vertex3f(-20.0f, -0.41f, 20.0f);
floorBatch.MultiTexCoord2f(0, texSize, 0.0f);
floorBatch.Vertex3f(20.0f, -0.41f, 20.0f);
floorBatch.MultiTexCoord2f(0, texSize, texSize);
floorBatch.Vertex3f(20.0f, -0.41f, -20.0f);
floorBatch.MultiTexCoord2f(0, 0.0f, texSize);
floorBatch.Vertex3f(-20.0f, -0.41f, -20.0f);
floorBatch.End();
// END
//////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////
// WALLS
GLfloat wallTexSizeH = 0.5f; // Height of texture
GLfloat wallTexSizeW = 20.0f; // Width of texture
// BACK
backWallBatch.Begin(GL_TRIANGLE_FAN, 4, 1);
backWallBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
backWallBatch.Vertex3f(-20.0f, -0.41f, 20.0f);
backWallBatch.MultiTexCoord2f(0, wallTexSizeW, 0.0f);
backWallBatch.Vertex3f(20.0f, -0.41f, 20.0f);
backWallBatch.MultiTexCoord2f(0, wallTexSizeW, wallTexSizeH);
backWallBatch.Vertex3f(20.0f, 0.41f, 20.0f);
backWallBatch.MultiTexCoord2f(0, 0.0f, wallTexSizeH);
backWallBatch.Vertex3f(-20.0f, 0.41f, 20.0f);
backWallBatch.End();
// FRONT
frontWallBatch.Begin(GL_TRIANGLE_FAN, 4, 1);
frontWallBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
frontWallBatch.Vertex3f(-20.0f, -0.41f, -20.0f);
frontWallBatch.MultiTexCoord2f(0, wallTexSizeW, 0.0f);
frontWallBatch.Vertex3f(20.0f, -0.41f, -20.0f);
frontWallBatch.MultiTexCoord2f(0, wallTexSizeW, wallTexSizeH);
frontWallBatch.Vertex3f(20.0f, 0.41f, -20.0f);
frontWallBatch.MultiTexCoord2f(0, 0.0f, wallTexSizeH);
frontWallBatch.Vertex3f(-20.0f, 0.41f, -20.0f);
frontWallBatch.End();
// LEFT
leftWallBatch.Begin(GL_TRIANGLE_FAN, 4, 1);
leftWallBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
leftWallBatch.Vertex3f(-20.0f, -0.41f, 20.0f);
leftWallBatch.MultiTexCoord2f(0, wallTexSizeW, 0.0f);
leftWallBatch.Vertex3f(-20.0f, -0.41f, -20.0f);
leftWallBatch.MultiTexCoord2f(0, wallTexSizeW, wallTexSizeH);
leftWallBatch.Vertex3f(-20.0f, 0.41f, -20.0f);
leftWallBatch.MultiTexCoord2f(0, 0.0f, wallTexSizeH);
leftWallBatch.Vertex3f(-20.0f, 0.41f, 20.0f);
leftWallBatch.End();
// RIGHT
rightWallBatch.Begin(GL_TRIANGLE_FAN, 4, 1);
rightWallBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
rightWallBatch.Vertex3f(20.0f, -0.41f, -20.0f);
rightWallBatch.MultiTexCoord2f(0, wallTexSizeW, 0.0f);
rightWallBatch.Vertex3f(20.0f, -0.41f, 20.0f);
rightWallBatch.MultiTexCoord2f(0, wallTexSizeW, wallTexSizeH);
rightWallBatch.Vertex3f(20.0f, 0.41f, 20.0f);
rightWallBatch.MultiTexCoord2f(0, 0.0f, wallTexSizeH);
rightWallBatch.Vertex3f(20.0f, 0.41f, -20.0f);
rightWallBatch.End();
// END
//////////////////////////////////////////////////////////////////
}
//////////////////////////////////////////////////////////////////
// Clean up model data after exiting
void Cleanup()
{
model1.FreeModel();
}
//////////////////////////////////////////////////////////////////
// Screen changes size or is initialized
void ChangeSize(int nWidth, int nHeight)
{
glViewport(0, 0, nWidth, nHeight);
// Create the projection matrix, and load it on the projection matrix stack
viewFrustum.SetPerspective(35.0f, float(nWidth)/float(nHeight), 1.0f, 100.0f);
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
// Set the transformation pipeline to use the two matrix stacks
transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
}
//////////////////////////////////////////////////////////////////
// Called to draw scene
void RenderScene(void)
{
//
static float interp = 0.0;
static double current_time = 0;
static double last_time = 0;
float dt;
GLfloat vColour[] = { 1.0f, 1.0f, 1.0f, 1.0f};
GLfloat vLightPos[] = {0.0f, 2.0f, -1.0f};
//
// Color values
static GLfloat vTexColour[] = { 1.0f, 1.0f, 1.0f, 1.0f}; // white colour for accurate walls
static GLfloat vCylColour[] = { 0.2f, 1.0f, 1.0f, 1.0f}; // Cylinder colour
// Clear the color and depth buffers
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//
last_time = current_time;
current_time = (double)glutGet (GLUT_ELAPSED_TIME) / 100.0;
/* Animate model from frames 0 to num_frames-1 */
interp += (current_time - last_time);
dt = (current_time - last_time);
//
// Save the current modelview matrix (the identity matrix)
modelViewMatrix.PushMatrix();
M3DMatrix44f mCamera;
cameraFrame.GetCameraMatrix(mCamera);
modelViewMatrix.PushMatrix(mCamera);
//cameraFrame.MoveUp(0.0f);
// Transform the light position into eye coordinates
//M3DVector4f vLightPos = { 0.0f, 10.0f, 5.0f, 1.0f };
M3DVector4f vLightEyePos;
m3dTransformVector4(vLightEyePos, vLightPos, mCamera);
//////////////////////////////////////////////////////////////////
// SKYBOX
modelViewMatrix.PushMatrix();
// Set up the MVP matrix for drawing the skybox
// Get the current camera facing, but ignore its position
cameraFrame.GetCameraMatrix(mCamera,true);
modelViewMatrix.LoadMatrix(mCamera);
// Now set shader - this step is where the MVP matrix
// is set for any rendering
shaderManager.UseStockShader(GLT_SHADER_SHADED,
transformPipeline.GetModelViewProjectionMatrix());
// Disable Depth Testing then draw, so skybox renders in the distance
glDisable(GL_DEPTH_TEST);
glBindTexture(GL_TEXTURE_2D,textures[0]);
shaderManager.UseStockShader(GLT_SHADER_TEXTURE_REPLACE, transformPipeline.GetModelViewProjectionMatrix(), 0);
boxPosX.Draw();
glBindTexture(GL_TEXTURE_2D,textures[1]);
shaderManager.UseStockShader(GLT_SHADER_TEXTURE_REPLACE, transformPipeline.GetModelViewProjectionMatrix(), 0);
boxNegX.Draw();
glBindTexture(GL_TEXTURE_2D,textures[2]);
shaderManager.UseStockShader(GLT_SHADER_TEXTURE_REPLACE, transformPipeline.GetModelViewProjectionMatrix(), 0);
boxPosY.Draw();
glBindTexture(GL_TEXTURE_2D,textures[3]);
shaderManager.UseStockShader(GLT_SHADER_TEXTURE_REPLACE, transformPipeline.GetModelViewProjectionMatrix(), 0);
boxNegY.Draw();
glBindTexture(GL_TEXTURE_2D,textures[4]);
shaderManager.UseStockShader(GLT_SHADER_TEXTURE_REPLACE, transformPipeline.GetModelViewProjectionMatrix(), 0);
boxPosZ.Draw();
glBindTexture(GL_TEXTURE_2D,textures[5]);
shaderManager.UseStockShader(GLT_SHADER_TEXTURE_REPLACE, transformPipeline.GetModelViewProjectionMatrix(), 0);
boxNegZ.Draw();
// Re-enable Depth Testing, so other objects load correctly in environment
glEnable(GL_DEPTH_TEST);
modelViewMatrix.PopMatrix();
// END
//////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////
// FLOOR
modelViewMatrix.PushMatrix();
glBindTexture(GL_TEXTURE_2D, textures[6]);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
shaderManager.UseStockShader(GLT_SHADER_TEXTURE_REPLACE, transformPipeline.GetModelViewProjectionMatrix(), 0);
floorBatch.Draw();
modelViewMatrix.PopMatrix();
//END
//////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////
// WALLS
// BACK
modelViewMatrix.PushMatrix();
glBindTexture(GL_TEXTURE_2D, textures[7]);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
shaderManager.UseStockShader(GLT_SHADER_TEXTURE_MODULATE, transformPipeline.GetModelViewProjectionMatrix(), vTexColour, 0);
backWallBatch.Draw();
modelViewMatrix.PopMatrix();
// FRONT
modelViewMatrix.PushMatrix();
glBindTexture(GL_TEXTURE_2D, textures[7]);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
shaderManager.UseStockShader(GLT_SHADER_TEXTURE_MODULATE, transformPipeline.GetModelViewProjectionMatrix(), vTexColour, 0);
frontWallBatch.Draw();
modelViewMatrix.PopMatrix();
// LEFT
modelViewMatrix.PushMatrix();
glBindTexture(GL_TEXTURE_2D, textures[7]);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
shaderManager.UseStockShader(GLT_SHADER_TEXTURE_MODULATE, transformPipeline.GetModelViewProjectionMatrix(), vTexColour, 0);
leftWallBatch.Draw();
modelViewMatrix.PopMatrix();
// RIGHT
modelViewMatrix.PushMatrix();
glBindTexture(GL_TEXTURE_2D, textures[7]);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
shaderManager.UseStockShader(GLT_SHADER_TEXTURE_MODULATE, transformPipeline.GetModelViewProjectionMatrix(), vTexColour, 0);
rightWallBatch.Draw();
modelViewMatrix.PopMatrix();
//END
//////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////
// MODEL
modelViewMatrix.PushMatrix();
modelViewMatrix.MultMatrix(modelFrame);
modelFrame.SetOrigin(0.0f, 0.0f, -1.0f);
modelViewMatrix.Rotate(-90,1.0f,0.0f,0.0f); // turn upright
modelViewMatrix.Rotate(-90,0.0f,0.0f,1.0f); // turn to face
modelViewMatrix.Scale(0.005f,0.005f,0.005f);
shaderManager.UseStockShader(GLT_SHADER_TEXTURE_POINT_LIGHT_DIFF,
modelViewMatrix.GetMatrix(), projectionMatrix.GetMatrix(),
vLightPos, vColour, 0);
glBindTexture(GL_TEXTURE_2D,textures[8]);
model1.Animate (currentAnim, dt);
model1.RenderFrameItp();
modelViewMatrix.PopMatrix();
// END
//////////////////////////////////////////////////////////////////
// Restore the previous modleview matrix (the identity matrix)
modelViewMatrix.PopMatrix();
modelViewMatrix.PopMatrix();
//cameraFrame.SetOrigin(0.0f, 0.0f, 1.0f);
if (interp >= 1.0f)
interp = 0.0f;
// Do the buffer Swap
glutSwapBuffers();
// Tell GLUT to do it again
glutPostRedisplay();
}
void Keyboard(unsigned char key, int x, int y)
{
/* Escape */
//if (key == 27)
//exit (0);
//if (currentAnim >19)
//currentAnim = 0;
float linear = 0.1f;
float angular = float(m3dDegToRad(5.0f));
float CAMx, CAMy, CAMz;
CAMx = cameraFrame.GetOriginX();
CAMy = cameraFrame.GetOriginY();
CAMz = cameraFrame.GetOriginZ();
switch(key)
{
case('w'):
{
cameraFrame.MoveForward(linear);
modelFrame.MoveForward(linear);
currentAnim = MD2_RUN;
break;
}
case('s'):
{
cameraFrame.MoveForward(-linear);
modelFrame.MoveForward(-linear);
currentAnim = MD2_RUN;
break;
}
case('a'):
{
cameraFrame.RotateWorld(angular, 0.0f, 1.0f, 0.0f);
modelFrame.RotateLocalY(angular);
break;
}
case('d'):
{
cameraFrame.RotateWorld(-angular, 0.0f, 1.0f, 0.0f);
modelFrame.RotateLocalY(-angular);
break;
}
default:
currentAnim = MD2_STAND;
}
}
void KeyboardUp (unsigned char key, int x, int y)
{
currentAnim = MD2_STAND;
}
int main(int argc, char* argv[])
{
//gltSetWorkingDirectory(argv[0]); // Don't need on Windows.
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize(1280,720);
glutCreateWindow("Equilibrium Alpha");
glutKeyboardFunc(Keyboard);
glutKeyboardUpFunc(KeyboardUp);
glutReshapeFunc(ChangeSize);
glutDisplayFunc(RenderScene);
GLenum err = glewInit();
if (GLEW_OK != err)
{
fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
return 1;
}
atexit(Cleanup);
SetupRC();
glutMainLoop();
return 0;
}
Ly8gQmFzZWQgb24gU3BoZXJlV29ybGQ0LmNwcAoKI2luY2x1ZGUgPEdMVG9vbHMuaD4KI2luY2x1ZGUgPEdMU2hhZGVyTWFuYWdlci5oPgojaW5jbHVkZSA8R0xGcnVzdHVtLmg+CiNpbmNsdWRlIDxHTEJhdGNoLmg+CiNpbmNsdWRlIDxHTEZyYW1lLmg+CiNpbmNsdWRlIDxHTE1hdHJpeFN0YWNrLmg+CiNpbmNsdWRlIDxHTEdlb21ldHJ5VHJhbnNmb3JtLmg+CiNpbmNsdWRlIDxTdG9wV2F0Y2guaD4KCiNpbmNsdWRlIDxtYXRoLmg+CiNpbmNsdWRlIDxtYXRoM2QuaD4KI2luY2x1ZGUgPHN0ZGlvLmg+CiNpbmNsdWRlIDxzdGRsaWIuaD4KI2luY2x1ZGUgPHN0cmluZy5oPgojaW5jbHVkZSA8aW9zdHJlYW0+CiNpbmNsdWRlIDxmc3RyZWFtPgojaW5jbHVkZSA8c3N0cmVhbT4KCiNpbmNsdWRlIDxpb3N0cmVhbT4KI2luY2x1ZGUgIm1kMm1vZGVsLmgiCgp1c2luZyBuYW1lc3BhY2Ugc3RkOwoKLy8jaWZkZWYgX19BUFBMRV9fICAvLyBEb24ndCBuZWVkIG9uIFdpbmRvd3MKLy8jaW5jbHVkZSA8Z2x1dC9nbHV0Lmg+Ci8vI2Vsc2UKI2RlZmluZSBGUkVFR0xVVF9TVEFUSUMKI2luY2x1ZGUgPEdML2dsdXQuaD4KLy8jZW5kaWYKCgovLyAqKiBHTE9CQUxTICoqCgpHTFNoYWRlck1hbmFnZXIJCXNoYWRlck1hbmFnZXI7CQkJLy8gU2hhZGVyIE1hbmFnZXIKR0xNYXRyaXhTdGFjawkJbW9kZWxWaWV3TWF0cml4OwkJLy8gTW9kZWx2aWV3IE1hdHJpeApHTE1hdHJpeFN0YWNrCQlwcm9qZWN0aW9uTWF0cml4OwkJLy8gUHJvamVjdGlvbiBNYXRyaXgKR0xGcnVzdHVtCQkJdmlld0ZydXN0dW07CQkJLy8gVmlldyBGcnVzdHVtCkdMR2VvbWV0cnlUcmFuc2Zvcm0JdHJhbnNmb3JtUGlwZWxpbmU7CQkvLyBHZW9tZXRyeSBUcmFuc2Zvcm0gUGlwZWxpbmUKCkdMQmF0Y2gJCQkJZmxvb3JCYXRjaDsKR0xCYXRjaAkJCQliYWNrV2FsbEJhdGNoOwpHTEJhdGNoCQkJCWZyb250V2FsbEJhdGNoOwpHTEJhdGNoCQkJCWxlZnRXYWxsQmF0Y2g7CkdMQmF0Y2gJCQkJcmlnaHRXYWxsQmF0Y2g7CkdMRnJhbWUgICAgICAgICAgICAgY2FtZXJhRnJhbWU7CkdMRnJhbWUJCQkJbW9kZWxGcmFtZTsKbWQybW9kZWwJCQltb2RlbDE7CkdMdWludAkJCQljdXJyZW50QW5pbSA9IE1EMl9TVEFORDsKCi8vIFNreWJveCBvYmplY3RzCkdMdWludCB0ZXh0dXJlc1sxMF07CkdMQmF0Y2gJCWJveFBvc1g7CkdMQmF0Y2gJCWJveE5lZ1g7CkdMQmF0Y2gJCWJveFBvc1k7CkdMQmF0Y2gJCWJveE5lZ1k7CkdMQmF0Y2gJCWJveFBvc1o7CkdMQmF0Y2gJCWJveE5lZ1o7CgoKLy8gKiogRlVOQ1RJT05TICoqCgovLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8KLy8gVEVYVFVSRSBMT0FERVIKR0x1aW50IGxvYWRUZXh0dXJlKGNoYXIgKmZuYW1lLCBHTHVpbnQgKnRleElEKQp7CgkvLyBnZW5lcmF0ZSB0ZXh0dXJlIElECglnbEdlblRleHR1cmVzKDEsIHRleElEKTsKCS8vIHRleHR1cmUgZGltZW5zaW9ucyBhbmQgZGF0YSBidWZmZXIKCUdMaW50IHdpZHRoLCBoZWlnaHQsIGNvbXBvbmVudHM7CglHTGVudW0gZm9ybWF0OwoJR0xieXRlICpwQnl0ZXM7CgoJLy8gbG9hZCBmaWxlIC0gdXNpbmcgR0xUb29scyBsaWJyYXJ5CiAgICBwQnl0ZXMgPSBnbHRSZWFkVEdBQml0cyhmbmFtZSwgJndpZHRoLCAmaGVpZ2h0LCAmY29tcG9uZW50cywgJmZvcm1hdCk7CglpZiAocEJ5dGVzID09IE5VTEwpCgl7CgkJY2VyciA8PCAiU29tZXRoaW5nIHdlbnQgd3JvbmcgbG9hZGluZyB0ZXh0dXJlICIgPDwgZm5hbWUgPDwgZW5kbDsKCX0KCgkvLyBiaW5kIHRleHR1cmUgYW5kIHNldCBwYXJhbWV0ZXJzCglnbEJpbmRUZXh0dXJlKEdMX1RFWFRVUkVfMkQsICp0ZXhJRCk7CglnbFRleFBhcmFtZXRlcmkoR0xfVEVYVFVSRV8yRCwgR0xfVEVYVFVSRV9NSU5fRklMVEVSLCBHTF9MSU5FQVIpOwoJZ2xUZXhQYXJhbWV0ZXJpKEdMX1RFWFRVUkVfMkQsIEdMX1RFWFRVUkVfTUFHX0ZJTFRFUiwgR0xfTElORUFSKTsKCWdsVGV4UGFyYW1ldGVyaShHTF9URVhUVVJFXzJELCBHTF9URVhUVVJFX1dSQVBfUywgR0xfUkVQRUFUKTsgIC8vIEdMX1JFUEVBVAoJZ2xUZXhQYXJhbWV0ZXJpKEdMX1RFWFRVUkVfMkQsIEdMX1RFWFRVUkVfV1JBUF9ULCBHTF9SRVBFQVQpOwoJZ2xUZXhJbWFnZTJEKEdMX1RFWFRVUkVfMkQsMCxjb21wb25lbnRzLHdpZHRoLCBoZWlnaHQsIDAsCgkJZm9ybWF0LCBHTF9VTlNJR05FRF9CWVRFLCBwQnl0ZXMpOwoJLy9nbEdlbmVyYXRlTWlwbWFwKEdMX1RFWFRVUkVfMkQpOwoKCS8vIHRleHR1cmUgbG9hZGVkLCBmcmVlIHRoZSB0ZW1wb3JhcnkgYnVmZmVyCglmcmVlKHBCeXRlcyk7CgoJcmV0dXJuICp0ZXhJRDsJLy8gcmV0dXJuIHZhbHVlIG9mIHRleHVyZSBJRCAtIHJlZHVuZGFudAoKfSAgICAgICAKCi8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLwovLyBUaGlzIGZ1bmN0aW9uIGRvZXMgYW55IG5lZWRlZCBpbml0aWFsaXphdGlvbiBvbiB0aGUgcmVuZGVyaW5nCi8vIGNvbnRleHQuIAp2b2lkIFNldHVwUkMoKQp7CglnbENsZWFyQ29sb3IoMC4wZiwgMC4wZiwgMC4wZiwgMS4wZik7CgkJCgkvLyBJbml0aWFsemUgU2hhZGVyIE1hbmFnZXIKCXNoYWRlck1hbmFnZXIuSW5pdGlhbGl6ZVN0b2NrU2hhZGVycygpOwoJZ2xTaGFkZU1vZGVsIChHTF9TTU9PVEgpOwoJZ2xFbmFibGUoR0xfREVQVEhfVEVTVCk7CglnbEVuYWJsZSAoR0xfVEVYVFVSRV8yRCk7CgoJLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vCgkvLyBMT0FEIFRFWFRVUkVTCgl0ZXh0dXJlc1swXSA9IGxvYWRUZXh0dXJlKCJwb3NfeC50Z2EiLCAmdGV4dHVyZXNbMF0pOwoJdGV4dHVyZXNbMV0gPSBsb2FkVGV4dHVyZSgibmVnX3gudGdhIiwgJnRleHR1cmVzWzFdKTsKCXRleHR1cmVzWzJdID0gbG9hZFRleHR1cmUoInBvc195LnRnYSIsICZ0ZXh0dXJlc1syXSk7Cgl0ZXh0dXJlc1szXSA9IGxvYWRUZXh0dXJlKCJuZWdfeS50Z2EiLCAmdGV4dHVyZXNbM10pOwoJdGV4dHVyZXNbNF0gPSBsb2FkVGV4dHVyZSgicG9zX3oudGdhIiwgJnRleHR1cmVzWzRdKTsKCXRleHR1cmVzWzVdID0gbG9hZFRleHR1cmUoIm5lZ196LnRnYSIsICZ0ZXh0dXJlc1s1XSk7Cgl0ZXh0dXJlc1s2XSA9IGxvYWRUZXh0dXJlKCJncmFzcy50Z2EiLCAmdGV4dHVyZXNbNl0pOwoJdGV4dHVyZXNbN10gPSBsb2FkVGV4dHVyZSgiYnJpY2sudGdhIiwgJnRleHR1cmVzWzddKTsKCXRleHR1cmVzWzhdID0gbG9hZFRleHR1cmUoInNrZWxldG9uLnRnYSIsICZ0ZXh0dXJlc1s4XSk7CglpZiAoIW1vZGVsMS5SZWFkTUQyTW9kZWwgKCJza2VsZXRvbi5tZDIiKSkKCQlleGl0IChFWElUX0ZBSUxVUkUpOwoJLy8gRU5ECgkvLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8KCgkvLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8KCS8vIFNLWUJPWAoJYm94UG9zWC5CZWdpbihHTF9UUklBTkdMRV9GQU4sNCwxKTsKCWJveFBvc1guTXVsdGlUZXhDb29yZDJmKDAsMC4wZiwwLjBmKTsKCWJveFBvc1guVmVydGV4M2YoMTAuMGYsLTEwLjBmLC0xMC4wZik7Cglib3hQb3NYLk11bHRpVGV4Q29vcmQyZigwLDEuMGYsMC4wZik7Cglib3hQb3NYLlZlcnRleDNmKDEwLjBmLC0xMC4wZiwxMC4wZik7Cglib3hQb3NYLk11bHRpVGV4Q29vcmQyZigwLDEuMGYsMS4wZik7Cglib3hQb3NYLlZlcnRleDNmKDEwLjBmLDEwLjBmLDEwLjBmKTsKCWJveFBvc1guTXVsdGlUZXhDb29yZDJmKDAsMC4wZiwxLjBmKTsKCWJveFBvc1guVmVydGV4M2YoMTAuMGYsMTAuMGYsLTEwLjBmKTsKCWJveFBvc1guRW5kKCk7CgoJYm94TmVnWC5CZWdpbihHTF9UUklBTkdMRV9GQU4sNCwxKTsKCWJveE5lZ1guTXVsdGlUZXhDb29yZDJmKDAsMC4wZiwwLjBmKTsKCWJveE5lZ1guVmVydGV4M2YoLTEwLjBmLC0xMC4wZiwxMC4wZik7Cglib3hOZWdYLk11bHRpVGV4Q29vcmQyZigwLDEuMGYsMC4wZik7Cglib3hOZWdYLlZlcnRleDNmKC0xMC4wZiwtMTAuMGYsLTEwLjBmKTsKCWJveE5lZ1guTXVsdGlUZXhDb29yZDJmKDAsMS4wZiwxLjBmKTsKCWJveE5lZ1guVmVydGV4M2YoLTEwLjBmLDEwLjBmLC0xMC4wZik7Cglib3hOZWdYLk11bHRpVGV4Q29vcmQyZigwLDAuMGYsMS4wZik7Cglib3hOZWdYLlZlcnRleDNmKC0xMC4wZiwxMC4wZiwxMC4wZik7Cglib3hOZWdYLkVuZCgpOwoKCWJveFBvc1kuQmVnaW4oR0xfVFJJQU5HTEVfRkFOLDQsMSk7Cglib3hQb3NZLk11bHRpVGV4Q29vcmQyZigwLDAuMGYsMC4wZik7Cglib3hQb3NZLlZlcnRleDNmKC0xMC4wZiwxMC4wZiwtMTAuMGYpOwoJYm94UG9zWS5NdWx0aVRleENvb3JkMmYoMCwxLjBmLDAuMGYpOwoJYm94UG9zWS5WZXJ0ZXgzZigxMC4wZiwxMC4wZiwtMTAuMGYpOwoJYm94UG9zWS5NdWx0aVRleENvb3JkMmYoMCwxLjBmLDEuMGYpOwoJYm94UG9zWS5WZXJ0ZXgzZigxMC4wZiwxMC4wZiwxMC4wZik7Cglib3hQb3NZLk11bHRpVGV4Q29vcmQyZigwLDAuMGYsMS4wZik7Cglib3hQb3NZLlZlcnRleDNmKC0xMC4wZiwxMC4wZiwxMC4wZik7Cglib3hQb3NZLkVuZCgpOwoKCWJveE5lZ1kuQmVnaW4oR0xfVFJJQU5HTEVfRkFOLDQsMSk7Cglib3hOZWdZLk11bHRpVGV4Q29vcmQyZigwLDAuMGYsMC4wZik7Cglib3hOZWdZLlZlcnRleDNmKC0xMC4wZiwtMTAuMGYsMTAuMGYpOwoJYm94TmVnWS5NdWx0aVRleENvb3JkMmYoMCwxLjBmLDAuMGYpOwoJYm94TmVnWS5WZXJ0ZXgzZigxMC4wZiwtMTAuMGYsMTAuMGYpOwoJYm94TmVnWS5NdWx0aVRleENvb3JkMmYoMCwxLjBmLDEuMGYpOwoJYm94TmVnWS5WZXJ0ZXgzZigxMC4wZiwtMTAuMGYsLTEwLjBmKTsKCWJveE5lZ1kuTXVsdGlUZXhDb29yZDJmKDAsMC4wZiwxLjBmKTsKCWJveE5lZ1kuVmVydGV4M2YoLTEwLjBmLC0xMC4wZiwtMTAuMGYpOwoJYm94TmVnWS5FbmQoKTsKCglib3hQb3NaLkJlZ2luKEdMX1RSSUFOR0xFX0ZBTiw0LDEpOwoJYm94UG9zWi5NdWx0aVRleENvb3JkMmYoMCwwLjBmLDAuMGYpOwoJYm94UG9zWi5WZXJ0ZXgzZigxMC4wZiwtMTAuMGYsMTAuMGYpOwoJYm94UG9zWi5NdWx0aVRleENvb3JkMmYoMCwxLjBmLDAuMGYpOwoJYm94UG9zWi5WZXJ0ZXgzZigtMTAuMGYsLTEwLjBmLDEwLjBmKTsKCWJveFBvc1ouTXVsdGlUZXhDb29yZDJmKDAsMS4wZiwxLjBmKTsKCWJveFBvc1ouVmVydGV4M2YoLTEwLjBmLDEwLjBmLDEwLjBmKTsKCWJveFBvc1ouTXVsdGlUZXhDb29yZDJmKDAsMC4wZiwxLjBmKTsKCWJveFBvc1ouVmVydGV4M2YoMTAuMGYsMTAuMGYsMTAuMGYpOwoJYm94UG9zWi5FbmQoKTsKCglib3hOZWdaLkJlZ2luKEdMX1RSSUFOR0xFX0ZBTiw0LDEpOwoJYm94TmVnWi5NdWx0aVRleENvb3JkMmYoMCwwLjBmLDAuMGYpOwoJYm94TmVnWi5WZXJ0ZXgzZigtMTAuMGYsLTEwLjBmLC0xMC4wZik7Cglib3hOZWdaLk11bHRpVGV4Q29vcmQyZigwLDEuMGYsMC4wZik7Cglib3hOZWdaLlZlcnRleDNmKDEwLjBmLC0xMC4wZiwtMTAuMGYpOwoJYm94TmVnWi5NdWx0aVRleENvb3JkMmYoMCwxLjBmLDEuMGYpOwoJYm94TmVnWi5WZXJ0ZXgzZigxMC4wZiwxMC4wZiwtMTAuMGYpOwoJYm94TmVnWi5NdWx0aVRleENvb3JkMmYoMCwwLjBmLDEuMGYpOwoJYm94TmVnWi5WZXJ0ZXgzZigtMTAuMGYsMTAuMGYsLTEwLjBmKTsKCWJveE5lZ1ouRW5kKCk7CgkvLyBFTkQKCS8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLwoKCS8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLwoJLy8gRkxPT1IKCUdMZmxvYXQgdGV4U2l6ZSA9IDEwLjBmOwoJZmxvb3JCYXRjaC5CZWdpbihHTF9UUklBTkdMRV9GQU4sIDQsIDEpOwoJZmxvb3JCYXRjaC5NdWx0aVRleENvb3JkMmYoMCwgMC4wZiwgMC4wZik7CglmbG9vckJhdGNoLlZlcnRleDNmKC0yMC4wZiwgLTAuNDFmLCAyMC4wZik7CgkKCWZsb29yQmF0Y2guTXVsdGlUZXhDb29yZDJmKDAsIHRleFNpemUsIDAuMGYpOwogICAgZmxvb3JCYXRjaC5WZXJ0ZXgzZigyMC4wZiwgLTAuNDFmLCAyMC4wZik7CgkKCWZsb29yQmF0Y2guTXVsdGlUZXhDb29yZDJmKDAsIHRleFNpemUsIHRleFNpemUpOwoJZmxvb3JCYXRjaC5WZXJ0ZXgzZigyMC4wZiwgLTAuNDFmLCAtMjAuMGYpOwoJCglmbG9vckJhdGNoLk11bHRpVGV4Q29vcmQyZigwLCAwLjBmLCB0ZXhTaXplKTsKCWZsb29yQmF0Y2guVmVydGV4M2YoLTIwLjBmLCAtMC40MWYsIC0yMC4wZik7CglmbG9vckJhdGNoLkVuZCgpOwoJLy8gRU5ECgkvLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8KCgkvLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8KCS8vIFdBTExTCgoJR0xmbG9hdCB3YWxsVGV4U2l6ZUggPSAwLjVmOyAgLy8gSGVpZ2h0IG9mIHRleHR1cmUKCUdMZmxvYXQgd2FsbFRleFNpemVXID0gMjAuMGY7ICAvLyBXaWR0aCBvZiB0ZXh0dXJlCgoJLy8gQkFDSwoJYmFja1dhbGxCYXRjaC5CZWdpbihHTF9UUklBTkdMRV9GQU4sIDQsIDEpOwoJYmFja1dhbGxCYXRjaC5NdWx0aVRleENvb3JkMmYoMCwgMC4wZiwgMC4wZik7CgliYWNrV2FsbEJhdGNoLlZlcnRleDNmKC0yMC4wZiwgLTAuNDFmLCAyMC4wZik7CgkKCWJhY2tXYWxsQmF0Y2guTXVsdGlUZXhDb29yZDJmKDAsIHdhbGxUZXhTaXplVywgMC4wZik7CiAgICBiYWNrV2FsbEJhdGNoLlZlcnRleDNmKDIwLjBmLCAtMC40MWYsIDIwLjBmKTsKCQoJYmFja1dhbGxCYXRjaC5NdWx0aVRleENvb3JkMmYoMCwgd2FsbFRleFNpemVXLCB3YWxsVGV4U2l6ZUgpOwoJYmFja1dhbGxCYXRjaC5WZXJ0ZXgzZigyMC4wZiwgMC40MWYsIDIwLjBmKTsKCQoJYmFja1dhbGxCYXRjaC5NdWx0aVRleENvb3JkMmYoMCwgMC4wZiwgd2FsbFRleFNpemVIKTsKCWJhY2tXYWxsQmF0Y2guVmVydGV4M2YoLTIwLjBmLCAwLjQxZiwgMjAuMGYpOwoJYmFja1dhbGxCYXRjaC5FbmQoKTsKCgkvLyBGUk9OVAoJZnJvbnRXYWxsQmF0Y2guQmVnaW4oR0xfVFJJQU5HTEVfRkFOLCA0LCAxKTsKCWZyb250V2FsbEJhdGNoLk11bHRpVGV4Q29vcmQyZigwLCAwLjBmLCAwLjBmKTsKCWZyb250V2FsbEJhdGNoLlZlcnRleDNmKC0yMC4wZiwgLTAuNDFmLCAtMjAuMGYpOwoJCglmcm9udFdhbGxCYXRjaC5NdWx0aVRleENvb3JkMmYoMCwgd2FsbFRleFNpemVXLCAwLjBmKTsKICAgIGZyb250V2FsbEJhdGNoLlZlcnRleDNmKDIwLjBmLCAtMC40MWYsIC0yMC4wZik7CgkKCWZyb250V2FsbEJhdGNoLk11bHRpVGV4Q29vcmQyZigwLCB3YWxsVGV4U2l6ZVcsIHdhbGxUZXhTaXplSCk7Cglmcm9udFdhbGxCYXRjaC5WZXJ0ZXgzZigyMC4wZiwgMC40MWYsIC0yMC4wZik7CgkKCWZyb250V2FsbEJhdGNoLk11bHRpVGV4Q29vcmQyZigwLCAwLjBmLCB3YWxsVGV4U2l6ZUgpOwoJZnJvbnRXYWxsQmF0Y2guVmVydGV4M2YoLTIwLjBmLCAwLjQxZiwgLTIwLjBmKTsKCWZyb250V2FsbEJhdGNoLkVuZCgpOwoKCS8vIExFRlQKCWxlZnRXYWxsQmF0Y2guQmVnaW4oR0xfVFJJQU5HTEVfRkFOLCA0LCAxKTsKCWxlZnRXYWxsQmF0Y2guTXVsdGlUZXhDb29yZDJmKDAsIDAuMGYsIDAuMGYpOwoJbGVmdFdhbGxCYXRjaC5WZXJ0ZXgzZigtMjAuMGYsIC0wLjQxZiwgMjAuMGYpOwoJCglsZWZ0V2FsbEJhdGNoLk11bHRpVGV4Q29vcmQyZigwLCB3YWxsVGV4U2l6ZVcsIDAuMGYpOwogICAgbGVmdFdhbGxCYXRjaC5WZXJ0ZXgzZigtMjAuMGYsIC0wLjQxZiwgLTIwLjBmKTsKCQoJbGVmdFdhbGxCYXRjaC5NdWx0aVRleENvb3JkMmYoMCwgd2FsbFRleFNpemVXLCB3YWxsVGV4U2l6ZUgpOwoJbGVmdFdhbGxCYXRjaC5WZXJ0ZXgzZigtMjAuMGYsIDAuNDFmLCAtMjAuMGYpOwoJCglsZWZ0V2FsbEJhdGNoLk11bHRpVGV4Q29vcmQyZigwLCAwLjBmLCB3YWxsVGV4U2l6ZUgpOwoJbGVmdFdhbGxCYXRjaC5WZXJ0ZXgzZigtMjAuMGYsIDAuNDFmLCAyMC4wZik7CglsZWZ0V2FsbEJhdGNoLkVuZCgpOwoKCS8vIFJJR0hUCglyaWdodFdhbGxCYXRjaC5CZWdpbihHTF9UUklBTkdMRV9GQU4sIDQsIDEpOwoJcmlnaHRXYWxsQmF0Y2guTXVsdGlUZXhDb29yZDJmKDAsIDAuMGYsIDAuMGYpOwoJcmlnaHRXYWxsQmF0Y2guVmVydGV4M2YoMjAuMGYsIC0wLjQxZiwgLTIwLjBmKTsKCQoJcmlnaHRXYWxsQmF0Y2guTXVsdGlUZXhDb29yZDJmKDAsIHdhbGxUZXhTaXplVywgMC4wZik7CiAgICByaWdodFdhbGxCYXRjaC5WZXJ0ZXgzZigyMC4wZiwgLTAuNDFmLCAyMC4wZik7CgkKCXJpZ2h0V2FsbEJhdGNoLk11bHRpVGV4Q29vcmQyZigwLCB3YWxsVGV4U2l6ZVcsIHdhbGxUZXhTaXplSCk7CglyaWdodFdhbGxCYXRjaC5WZXJ0ZXgzZigyMC4wZiwgMC40MWYsIDIwLjBmKTsKCQoJcmlnaHRXYWxsQmF0Y2guTXVsdGlUZXhDb29yZDJmKDAsIDAuMGYsIHdhbGxUZXhTaXplSCk7CglyaWdodFdhbGxCYXRjaC5WZXJ0ZXgzZigyMC4wZiwgMC40MWYsIC0yMC4wZik7CglyaWdodFdhbGxCYXRjaC5FbmQoKTsKCS8vIEVORAoJLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vCn0KCi8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLwovLyBDbGVhbiB1cCBtb2RlbCBkYXRhIGFmdGVyIGV4aXRpbmcKdm9pZCBDbGVhbnVwKCkKewogIG1vZGVsMS5GcmVlTW9kZWwoKTsKfQoKCi8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLwovLyBTY3JlZW4gY2hhbmdlcyBzaXplIG9yIGlzIGluaXRpYWxpemVkCnZvaWQgQ2hhbmdlU2l6ZShpbnQgbldpZHRoLCBpbnQgbkhlaWdodCkKewoJZ2xWaWV3cG9ydCgwLCAwLCBuV2lkdGgsIG5IZWlnaHQpOwoJCiAgICAvLyBDcmVhdGUgdGhlIHByb2plY3Rpb24gbWF0cml4LCBhbmQgbG9hZCBpdCBvbiB0aGUgcHJvamVjdGlvbiBtYXRyaXggc3RhY2sKCXZpZXdGcnVzdHVtLlNldFBlcnNwZWN0aXZlKDM1LjBmLCBmbG9hdChuV2lkdGgpL2Zsb2F0KG5IZWlnaHQpLCAxLjBmLCAxMDAuMGYpOwoJcHJvamVjdGlvbk1hdHJpeC5Mb2FkTWF0cml4KHZpZXdGcnVzdHVtLkdldFByb2plY3Rpb25NYXRyaXgoKSk7CiAgICAKICAgIC8vIFNldCB0aGUgdHJhbnNmb3JtYXRpb24gcGlwZWxpbmUgdG8gdXNlIHRoZSB0d28gbWF0cml4IHN0YWNrcyAKCXRyYW5zZm9ybVBpcGVsaW5lLlNldE1hdHJpeFN0YWNrcyhtb2RlbFZpZXdNYXRyaXgsIHByb2plY3Rpb25NYXRyaXgpOwp9CgovLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8KLy8gQ2FsbGVkIHRvIGRyYXcgc2NlbmUKdm9pZCBSZW5kZXJTY2VuZSh2b2lkKQp7CiAgICAvLwoJc3RhdGljIGZsb2F0IGludGVycCA9IDAuMDsKCXN0YXRpYyBkb3VibGUgY3VycmVudF90aW1lID0gMDsKCXN0YXRpYyBkb3VibGUgbGFzdF90aW1lID0gMDsKCWZsb2F0IGR0OwoJR0xmbG9hdCB2Q29sb3VyW10gPSB7IDEuMGYsIDEuMGYsIDEuMGYsIDEuMGZ9OwoJR0xmbG9hdCB2TGlnaHRQb3NbXSA9IHswLjBmLCAyLjBmLCAtMS4wZn07CgkvLwoJCgkvLyBDb2xvciB2YWx1ZXMKCXN0YXRpYyBHTGZsb2F0IHZUZXhDb2xvdXJbXSA9IHsgMS4wZiwgMS4wZiwgMS4wZiwgMS4wZn07ICAvLyB3aGl0ZSBjb2xvdXIgZm9yIGFjY3VyYXRlIHdhbGxzCglzdGF0aWMgR0xmbG9hdCB2Q3lsQ29sb3VyW10gPSB7IDAuMmYsIDEuMGYsIDEuMGYsIDEuMGZ9OyAgLy8gQ3lsaW5kZXIgY29sb3VyCgkKCS8vIENsZWFyIHRoZSBjb2xvciBhbmQgZGVwdGggYnVmZmVycwoJZ2xDbGVhcihHTF9DT0xPUl9CVUZGRVJfQklUIHwgR0xfREVQVEhfQlVGRkVSX0JJVCk7CgoJLy8KCWxhc3RfdGltZSA9IGN1cnJlbnRfdGltZTsKCWN1cnJlbnRfdGltZSA9IChkb3VibGUpZ2x1dEdldCAoR0xVVF9FTEFQU0VEX1RJTUUpIC8gMTAwLjA7CgoJLyogQW5pbWF0ZSBtb2RlbCBmcm9tIGZyYW1lcyAwIHRvIG51bV9mcmFtZXMtMSAqLwoJaW50ZXJwICs9IChjdXJyZW50X3RpbWUgLSBsYXN0X3RpbWUpOwoJZHQgPSAoY3VycmVudF90aW1lIC0gbGFzdF90aW1lKTsKCS8vCgogICAgLy8gU2F2ZSB0aGUgY3VycmVudCBtb2RlbHZpZXcgbWF0cml4ICh0aGUgaWRlbnRpdHkgbWF0cml4KQoJbW9kZWxWaWV3TWF0cml4LlB1c2hNYXRyaXgoKTsKICAgIAogICAgTTNETWF0cml4NDRmIG1DYW1lcmE7CiAgICBjYW1lcmFGcmFtZS5HZXRDYW1lcmFNYXRyaXgobUNhbWVyYSk7CiAgICBtb2RlbFZpZXdNYXRyaXguUHVzaE1hdHJpeChtQ2FtZXJhKTsKCS8vY2FtZXJhRnJhbWUuTW92ZVVwKDAuMGYpOwoKCS8vIFRyYW5zZm9ybSB0aGUgbGlnaHQgcG9zaXRpb24gaW50byBleWUgY29vcmRpbmF0ZXMKICAgIC8vTTNEVmVjdG9yNGYgdkxpZ2h0UG9zID0geyAwLjBmLCAxMC4wZiwgNS4wZiwgMS4wZiB9OwogICAgTTNEVmVjdG9yNGYgdkxpZ2h0RXllUG9zOwogICAgbTNkVHJhbnNmb3JtVmVjdG9yNCh2TGlnaHRFeWVQb3MsIHZMaWdodFBvcywgbUNhbWVyYSk7CgoJLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vCgkvLyBTS1lCT1gKCgltb2RlbFZpZXdNYXRyaXguUHVzaE1hdHJpeCgpOwoKCS8vIFNldCB1cCB0aGUgTVZQIG1hdHJpeCBmb3IgZHJhd2luZyB0aGUgc2t5Ym94CgkvLyBHZXQgdGhlIGN1cnJlbnQgY2FtZXJhIGZhY2luZywgYnV0IGlnbm9yZSBpdHMgcG9zaXRpb24KCWNhbWVyYUZyYW1lLkdldENhbWVyYU1hdHJpeChtQ2FtZXJhLHRydWUpOwoJbW9kZWxWaWV3TWF0cml4LkxvYWRNYXRyaXgobUNhbWVyYSk7CgoJLy8gTm93IHNldCBzaGFkZXIgLSB0aGlzIHN0ZXAgaXMgd2hlcmUgdGhlIE1WUCBtYXRyaXgKCS8vIGlzIHNldCBmb3IgYW55IHJlbmRlcmluZwoJc2hhZGVyTWFuYWdlci5Vc2VTdG9ja1NoYWRlcihHTFRfU0hBREVSX1NIQURFRCwKCQl0cmFuc2Zvcm1QaXBlbGluZS5HZXRNb2RlbFZpZXdQcm9qZWN0aW9uTWF0cml4KCkpOwoKCS8vIERpc2FibGUgRGVwdGggVGVzdGluZyB0aGVuIGRyYXcsIHNvIHNreWJveCByZW5kZXJzIGluIHRoZSBkaXN0YW5jZQoJZ2xEaXNhYmxlKEdMX0RFUFRIX1RFU1QpOwoKCWdsQmluZFRleHR1cmUoR0xfVEVYVFVSRV8yRCx0ZXh0dXJlc1swXSk7CglzaGFkZXJNYW5hZ2VyLlVzZVN0b2NrU2hhZGVyKEdMVF9TSEFERVJfVEVYVFVSRV9SRVBMQUNFLCB0cmFuc2Zvcm1QaXBlbGluZS5HZXRNb2RlbFZpZXdQcm9qZWN0aW9uTWF0cml4KCksIDApOwoJYm94UG9zWC5EcmF3KCk7CgoJZ2xCaW5kVGV4dHVyZShHTF9URVhUVVJFXzJELHRleHR1cmVzWzFdKTsKCXNoYWRlck1hbmFnZXIuVXNlU3RvY2tTaGFkZXIoR0xUX1NIQURFUl9URVhUVVJFX1JFUExBQ0UsIHRyYW5zZm9ybVBpcGVsaW5lLkdldE1vZGVsVmlld1Byb2plY3Rpb25NYXRyaXgoKSwgMCk7Cglib3hOZWdYLkRyYXcoKTsKCglnbEJpbmRUZXh0dXJlKEdMX1RFWFRVUkVfMkQsdGV4dHVyZXNbMl0pOwoJc2hhZGVyTWFuYWdlci5Vc2VTdG9ja1NoYWRlcihHTFRfU0hBREVSX1RFWFRVUkVfUkVQTEFDRSwgdHJhbnNmb3JtUGlwZWxpbmUuR2V0TW9kZWxWaWV3UHJvamVjdGlvbk1hdHJpeCgpLCAwKTsKCWJveFBvc1kuRHJhdygpOwoKCWdsQmluZFRleHR1cmUoR0xfVEVYVFVSRV8yRCx0ZXh0dXJlc1szXSk7CglzaGFkZXJNYW5hZ2VyLlVzZVN0b2NrU2hhZGVyKEdMVF9TSEFERVJfVEVYVFVSRV9SRVBMQUNFLCB0cmFuc2Zvcm1QaXBlbGluZS5HZXRNb2RlbFZpZXdQcm9qZWN0aW9uTWF0cml4KCksIDApOwoJYm94TmVnWS5EcmF3KCk7CgoJZ2xCaW5kVGV4dHVyZShHTF9URVhUVVJFXzJELHRleHR1cmVzWzRdKTsKCXNoYWRlck1hbmFnZXIuVXNlU3RvY2tTaGFkZXIoR0xUX1NIQURFUl9URVhUVVJFX1JFUExBQ0UsIHRyYW5zZm9ybVBpcGVsaW5lLkdldE1vZGVsVmlld1Byb2plY3Rpb25NYXRyaXgoKSwgMCk7Cglib3hQb3NaLkRyYXcoKTsKCglnbEJpbmRUZXh0dXJlKEdMX1RFWFRVUkVfMkQsdGV4dHVyZXNbNV0pOwoJc2hhZGVyTWFuYWdlci5Vc2VTdG9ja1NoYWRlcihHTFRfU0hBREVSX1RFWFRVUkVfUkVQTEFDRSwgdHJhbnNmb3JtUGlwZWxpbmUuR2V0TW9kZWxWaWV3UHJvamVjdGlvbk1hdHJpeCgpLCAwKTsKCWJveE5lZ1ouRHJhdygpOwoKCS8vIFJlLWVuYWJsZSBEZXB0aCBUZXN0aW5nLCBzbyBvdGhlciBvYmplY3RzIGxvYWQgY29ycmVjdGx5IGluIGVudmlyb25tZW50CglnbEVuYWJsZShHTF9ERVBUSF9URVNUKTsKCgltb2RlbFZpZXdNYXRyaXguUG9wTWF0cml4KCk7CgoJLy8gRU5ECgkvLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8KCgkvLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8KCS8vIEZMT09SCgoJbW9kZWxWaWV3TWF0cml4LlB1c2hNYXRyaXgoKTsKCglnbEJpbmRUZXh0dXJlKEdMX1RFWFRVUkVfMkQsIHRleHR1cmVzWzZdKTsKCWdsQmxlbmRGdW5jKEdMX1NSQ19BTFBIQSwgR0xfT05FX01JTlVTX1NSQ19BTFBIQSk7CglzaGFkZXJNYW5hZ2VyLlVzZVN0b2NrU2hhZGVyKEdMVF9TSEFERVJfVEVYVFVSRV9SRVBMQUNFLCB0cmFuc2Zvcm1QaXBlbGluZS5HZXRNb2RlbFZpZXdQcm9qZWN0aW9uTWF0cml4KCksIDApOwoJZmxvb3JCYXRjaC5EcmF3KCk7CgoJbW9kZWxWaWV3TWF0cml4LlBvcE1hdHJpeCgpOwoKCS8vRU5ECgkvLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8KCgkvLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8KCS8vIFdBTExTCgoJLy8gQkFDSwoKCW1vZGVsVmlld01hdHJpeC5QdXNoTWF0cml4KCk7CgoJZ2xCaW5kVGV4dHVyZShHTF9URVhUVVJFXzJELCB0ZXh0dXJlc1s3XSk7CglnbEJsZW5kRnVuYyhHTF9TUkNfQUxQSEEsIEdMX09ORV9NSU5VU19TUkNfQUxQSEEpOwoJc2hhZGVyTWFuYWdlci5Vc2VTdG9ja1NoYWRlcihHTFRfU0hBREVSX1RFWFRVUkVfTU9EVUxBVEUsIHRyYW5zZm9ybVBpcGVsaW5lLkdldE1vZGVsVmlld1Byb2plY3Rpb25NYXRyaXgoKSwgdlRleENvbG91ciwgMCk7CgliYWNrV2FsbEJhdGNoLkRyYXcoKTsKCgltb2RlbFZpZXdNYXRyaXguUG9wTWF0cml4KCk7CgoJLy8gRlJPTlQKCgltb2RlbFZpZXdNYXRyaXguUHVzaE1hdHJpeCgpOwoKCWdsQmluZFRleHR1cmUoR0xfVEVYVFVSRV8yRCwgdGV4dHVyZXNbN10pOwoJZ2xCbGVuZEZ1bmMoR0xfU1JDX0FMUEhBLCBHTF9PTkVfTUlOVVNfU1JDX0FMUEhBKTsKCXNoYWRlck1hbmFnZXIuVXNlU3RvY2tTaGFkZXIoR0xUX1NIQURFUl9URVhUVVJFX01PRFVMQVRFLCB0cmFuc2Zvcm1QaXBlbGluZS5HZXRNb2RlbFZpZXdQcm9qZWN0aW9uTWF0cml4KCksIHZUZXhDb2xvdXIsIDApOwoJZnJvbnRXYWxsQmF0Y2guRHJhdygpOwoKCW1vZGVsVmlld01hdHJpeC5Qb3BNYXRyaXgoKTsKCgkvLyBMRUZUCgoJbW9kZWxWaWV3TWF0cml4LlB1c2hNYXRyaXgoKTsKCglnbEJpbmRUZXh0dXJlKEdMX1RFWFRVUkVfMkQsIHRleHR1cmVzWzddKTsKCWdsQmxlbmRGdW5jKEdMX1NSQ19BTFBIQSwgR0xfT05FX01JTlVTX1NSQ19BTFBIQSk7CglzaGFkZXJNYW5hZ2VyLlVzZVN0b2NrU2hhZGVyKEdMVF9TSEFERVJfVEVYVFVSRV9NT0RVTEFURSwgdHJhbnNmb3JtUGlwZWxpbmUuR2V0TW9kZWxWaWV3UHJvamVjdGlvbk1hdHJpeCgpLCB2VGV4Q29sb3VyLCAwKTsKCWxlZnRXYWxsQmF0Y2guRHJhdygpOwoKCW1vZGVsVmlld01hdHJpeC5Qb3BNYXRyaXgoKTsKCgkvLyBSSUdIVAoKCW1vZGVsVmlld01hdHJpeC5QdXNoTWF0cml4KCk7CgoJZ2xCaW5kVGV4dHVyZShHTF9URVhUVVJFXzJELCB0ZXh0dXJlc1s3XSk7CglnbEJsZW5kRnVuYyhHTF9TUkNfQUxQSEEsIEdMX09ORV9NSU5VU19TUkNfQUxQSEEpOwoJc2hhZGVyTWFuYWdlci5Vc2VTdG9ja1NoYWRlcihHTFRfU0hBREVSX1RFWFRVUkVfTU9EVUxBVEUsIHRyYW5zZm9ybVBpcGVsaW5lLkdldE1vZGVsVmlld1Byb2plY3Rpb25NYXRyaXgoKSwgdlRleENvbG91ciwgMCk7CglyaWdodFdhbGxCYXRjaC5EcmF3KCk7CgoJbW9kZWxWaWV3TWF0cml4LlBvcE1hdHJpeCgpOwoKCS8vRU5ECgkvLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8KCgkvLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8KCS8vIE1PREVMCgltb2RlbFZpZXdNYXRyaXguUHVzaE1hdHJpeCgpOwoJbW9kZWxWaWV3TWF0cml4Lk11bHRNYXRyaXgobW9kZWxGcmFtZSk7Cgltb2RlbEZyYW1lLlNldE9yaWdpbigwLjBmLCAwLjBmLCAtMS4wZik7Cgltb2RlbFZpZXdNYXRyaXguUm90YXRlKC05MCwxLjBmLDAuMGYsMC4wZik7ICAgLy8gdHVybiB1cHJpZ2h0Cgltb2RlbFZpZXdNYXRyaXguUm90YXRlKC05MCwwLjBmLDAuMGYsMS4wZik7ICAgLy8gdHVybiB0byBmYWNlCgltb2RlbFZpZXdNYXRyaXguU2NhbGUoMC4wMDVmLDAuMDA1ZiwwLjAwNWYpOwoJc2hhZGVyTWFuYWdlci5Vc2VTdG9ja1NoYWRlcihHTFRfU0hBREVSX1RFWFRVUkVfUE9JTlRfTElHSFRfRElGRiwgCgkJbW9kZWxWaWV3TWF0cml4LkdldE1hdHJpeCgpLCBwcm9qZWN0aW9uTWF0cml4LkdldE1hdHJpeCgpLAoJCXZMaWdodFBvcywgdkNvbG91ciwgMCk7CglnbEJpbmRUZXh0dXJlKEdMX1RFWFRVUkVfMkQsdGV4dHVyZXNbOF0pOwoJbW9kZWwxLkFuaW1hdGUgKGN1cnJlbnRBbmltLCBkdCk7Cgltb2RlbDEuUmVuZGVyRnJhbWVJdHAoKTsKCW1vZGVsVmlld01hdHJpeC5Qb3BNYXRyaXgoKTsKCS8vIEVORAoJLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vCgoJLy8gUmVzdG9yZSB0aGUgcHJldmlvdXMgbW9kbGV2aWV3IG1hdHJpeCAodGhlIGlkZW50aXR5IG1hdHJpeCkKCW1vZGVsVmlld01hdHJpeC5Qb3BNYXRyaXgoKTsKICAgIG1vZGVsVmlld01hdHJpeC5Qb3BNYXRyaXgoKTsKCgkvL2NhbWVyYUZyYW1lLlNldE9yaWdpbigwLjBmLCAwLjBmLCAxLjBmKTsKCglpZiAoaW50ZXJwID49IDEuMGYpCgkJaW50ZXJwID0gMC4wZjsKCiAgICAvLyBEbyB0aGUgYnVmZmVyIFN3YXAKICAgIGdsdXRTd2FwQnVmZmVycygpOwogICAgICAgIAogICAgLy8gVGVsbCBHTFVUIHRvIGRvIGl0IGFnYWluCiAgICBnbHV0UG9zdFJlZGlzcGxheSgpOwp9Cgp2b2lkIEtleWJvYXJkKHVuc2lnbmVkIGNoYXIga2V5LCBpbnQgeCwgaW50IHkpCnsKICAvKiBFc2NhcGUgKi8KICAvL2lmIChrZXkgPT0gMjcpCiAgICAvL2V4aXQgKDApOwogIC8vaWYgKGN1cnJlbnRBbmltID4xOSkKCSAgLy9jdXJyZW50QW5pbSA9IDA7CgogIGZsb2F0IGxpbmVhciA9IDAuMWY7CglmbG9hdCBhbmd1bGFyID0gZmxvYXQobTNkRGVnVG9SYWQoNS4wZikpOwoKCWZsb2F0IENBTXgsIENBTXksIENBTXo7CglDQU14ID0gY2FtZXJhRnJhbWUuR2V0T3JpZ2luWCgpOwoJQ0FNeSA9IGNhbWVyYUZyYW1lLkdldE9yaWdpblkoKTsKCUNBTXogPSBjYW1lcmFGcmFtZS5HZXRPcmlnaW5aKCk7CgoJc3dpdGNoKGtleSkKCXsKCWNhc2UoJ3cnKToKCQl7CgkJCWNhbWVyYUZyYW1lLk1vdmVGb3J3YXJkKGxpbmVhcik7CgkJCW1vZGVsRnJhbWUuTW92ZUZvcndhcmQobGluZWFyKTsKCQkJY3VycmVudEFuaW0gPSBNRDJfUlVOOwoJCQlicmVhazsKCQl9CgljYXNlKCdzJyk6CgkJewoJCQljYW1lcmFGcmFtZS5Nb3ZlRm9yd2FyZCgtbGluZWFyKTsKCQkJbW9kZWxGcmFtZS5Nb3ZlRm9yd2FyZCgtbGluZWFyKTsKCQkJY3VycmVudEFuaW0gPSBNRDJfUlVOOwoJCQlicmVhazsKCQl9CgkKCWNhc2UoJ2EnKToKCQl7CgkJCWNhbWVyYUZyYW1lLlJvdGF0ZVdvcmxkKGFuZ3VsYXIsIDAuMGYsIDEuMGYsIDAuMGYpOwoJCQltb2RlbEZyYW1lLlJvdGF0ZUxvY2FsWShhbmd1bGFyKTsKCQkJYnJlYWs7CgkJfQoJCgljYXNlKCdkJyk6CgkJewoJCQljYW1lcmFGcmFtZS5Sb3RhdGVXb3JsZCgtYW5ndWxhciwgMC4wZiwgMS4wZiwgMC4wZik7CgkJCW1vZGVsRnJhbWUuUm90YXRlTG9jYWxZKC1hbmd1bGFyKTsKCQkJYnJlYWs7CgkJfQoJZGVmYXVsdDoKCQljdXJyZW50QW5pbSA9IE1EMl9TVEFORDsKCX0KfQoKdm9pZCBLZXlib2FyZFVwICh1bnNpZ25lZCBjaGFyIGtleSwgaW50IHgsIGludCB5KQp7CgljdXJyZW50QW5pbSA9IE1EMl9TVEFORDsKfQoKaW50IG1haW4oaW50IGFyZ2MsIGNoYXIqIGFyZ3ZbXSkKewoJLy9nbHRTZXRXb3JraW5nRGlyZWN0b3J5KGFyZ3ZbMF0pOyAgLy8gRG9uJ3QgbmVlZCBvbiBXaW5kb3dzLgoJCQogICAgZ2x1dEluaXQoJmFyZ2MsIGFyZ3YpOwogICAgZ2x1dEluaXREaXNwbGF5TW9kZShHTFVUX0RPVUJMRSB8IEdMVVRfUkdCIHwgR0xVVF9ERVBUSCk7CiAgICBnbHV0SW5pdFdpbmRvd1NpemUoMTI4MCw3MjApOwogIAogICAgZ2x1dENyZWF0ZVdpbmRvdygiRXF1aWxpYnJpdW0gQWxwaGEiKTsKIAogICAgZ2x1dEtleWJvYXJkRnVuYyhLZXlib2FyZCk7CglnbHV0S2V5Ym9hcmRVcEZ1bmMoS2V5Ym9hcmRVcCk7CiAgICBnbHV0UmVzaGFwZUZ1bmMoQ2hhbmdlU2l6ZSk7CiAgICBnbHV0RGlzcGxheUZ1bmMoUmVuZGVyU2NlbmUpOwogICAgCiAgICBHTGVudW0gZXJyID0gZ2xld0luaXQoKTsKICAgIGlmIChHTEVXX09LICE9IGVycikgCgl7CgkJZnByaW50ZihzdGRlcnIsICJHTEVXIEVycm9yOiAlc1xuIiwgZ2xld0dldEVycm9yU3RyaW5nKGVycikpOwoJCXJldHVybiAxOwoJfQogICAgICAgIAoJYXRleGl0KENsZWFudXApOwogICAgU2V0dXBSQygpOwogICAgZ2x1dE1haW5Mb29wKCk7ICAgIAogICAgcmV0dXJuIDA7Cn0=