From 67c0742ac36f432316ee2efc97f43f291c918cf4 Mon Sep 17 00:00:00 2001 From: PeytonPlayz595 <106421860+PeytonPlayz595@users.noreply.github.com> Date: Tue, 7 May 2024 23:55:27 -0400 Subject: [PATCH] Add OpenGL emulator --- .../glemu/FixedFunctionShader.java | 408 +++ src/net/PeytonPlayz585/glemu/GLObjectMap.java | 55 + .../glemu/GameOverlayFramebuffer.java | 72 + src/net/PeytonPlayz585/glemu/ModeBuffer.java | 58 + .../PeytonPlayz585/glemu/StreamBuffer.java | 130 + .../PeytonPlayz585/glemu/vector/Matrix.java | 130 + .../PeytonPlayz585/glemu/vector/Matrix2f.java | 418 +++ .../PeytonPlayz585/glemu/vector/Matrix3f.java | 529 ++++ .../PeytonPlayz585/glemu/vector/Matrix4f.java | 923 ++++++ .../glemu/vector/Quaternion.java | 506 +++ .../glemu/vector/ReadableVector.java | 57 + .../glemu/vector/ReadableVector2f.java | 47 + .../glemu/vector/ReadableVector3f.java | 42 + .../glemu/vector/ReadableVector4f.java | 44 + .../PeytonPlayz585/glemu/vector/Vector.java | 110 + .../PeytonPlayz585/glemu/vector/Vector2f.java | 319 ++ .../PeytonPlayz585/glemu/vector/Vector3f.java | 376 +++ .../PeytonPlayz585/glemu/vector/Vector4f.java | 377 +++ .../glemu/vector/WritableVector2f.java | 64 + .../glemu/vector/WritableVector3f.java | 58 + .../glemu/vector/WritableVector4f.java | 59 + src/net/PeytonPlayz585/input/Keyboard.java | 15 + src/net/PeytonPlayz585/input/Mouse.java | 55 + .../opengl/EaglerAdapterGL30.java | 1245 ++++++++ src/net/PeytonPlayz585/opengl/GL11.java | 9 + src/net/PeytonPlayz585/opengl/GL12.java | 5 + .../opengl/RealOpenGLEnums.java | 2417 +++++++++++++++ .../storage/LocalStorageManager.java | 80 + src/net/PeytonPlayz585/util/glu/GLU.java | 7 + src/net/PeytonPlayz595/nbt/NBTBase.java | 130 + src/net/PeytonPlayz595/nbt/NBTTagByte.java | 31 + .../PeytonPlayz595/nbt/NBTTagByteArray.java | 34 + .../PeytonPlayz595/nbt/NBTTagCompound.java | 194 ++ src/net/PeytonPlayz595/nbt/NBTTagDouble.java | 31 + src/net/PeytonPlayz595/nbt/NBTTagEnd.java | 23 + src/net/PeytonPlayz595/nbt/NBTTagFloat.java | 31 + src/net/PeytonPlayz595/nbt/NBTTagInt.java | 31 + src/net/PeytonPlayz595/nbt/NBTTagList.java | 63 + src/net/PeytonPlayz595/nbt/NBTTagLong.java | 31 + src/net/PeytonPlayz595/nbt/NBTTagShort.java | 31 + src/net/PeytonPlayz595/nbt/NBTTagString.java | 36 + .../lax1dude/eaglercraft/AssetRepository.java | 55 + src/net/lax1dude/eaglercraft/Base64.java | 857 ++++++ src/net/lax1dude/eaglercraft/BaseNCodec.java | 694 +++++ src/net/lax1dude/eaglercraft/Client.java | 132 + src/net/lax1dude/eaglercraft/EaglerImage.java | 59 + .../lax1dude/eaglercraft/GLAllocation.java | 60 + .../lax1dude/eaglercraft/GeneralDigest.java | 108 + src/net/lax1dude/eaglercraft/JSONArray.java | 1716 +++++++++++ .../lax1dude/eaglercraft/JSONException.java | 69 + src/net/lax1dude/eaglercraft/JSONObject.java | 2732 +++++++++++++++++ src/net/lax1dude/eaglercraft/JSONPointer.java | 295 ++ .../eaglercraft/JSONPointerException.java | 45 + .../eaglercraft/JSONPropertyIgnore.java | 43 + .../eaglercraft/JSONPropertyName.java | 47 + src/net/lax1dude/eaglercraft/JSONString.java | 43 + src/net/lax1dude/eaglercraft/JSONTokener.java | 545 ++++ src/net/lax1dude/eaglercraft/JSONWriter.java | 414 +++ src/net/lax1dude/eaglercraft/SHA1Digest.java | 213 ++ .../adapter/EaglerAdapterImpl2.java | 2067 +++++++++++++ .../adapter/FileChooserResult.java | 26 + .../adapter/teavm/BufferConverter.java | 34 + .../adapter/teavm/IDBObjectStorePatched.java | 79 + .../adapter/teavm/IndexedDBFilesystem.java | 414 +++ .../adapter/teavm/WebGL2RenderingContext.java | 42 + .../eaglercraft/adapter/teavm/WebGLQuery.java | 6 + .../adapter/teavm/WebGLVertexArray.java | 6 + 67 files changed, 20082 insertions(+) create mode 100644 src/net/PeytonPlayz585/glemu/FixedFunctionShader.java create mode 100644 src/net/PeytonPlayz585/glemu/GLObjectMap.java create mode 100644 src/net/PeytonPlayz585/glemu/GameOverlayFramebuffer.java create mode 100644 src/net/PeytonPlayz585/glemu/ModeBuffer.java create mode 100644 src/net/PeytonPlayz585/glemu/StreamBuffer.java create mode 100644 src/net/PeytonPlayz585/glemu/vector/Matrix.java create mode 100644 src/net/PeytonPlayz585/glemu/vector/Matrix2f.java create mode 100644 src/net/PeytonPlayz585/glemu/vector/Matrix3f.java create mode 100644 src/net/PeytonPlayz585/glemu/vector/Matrix4f.java create mode 100644 src/net/PeytonPlayz585/glemu/vector/Quaternion.java create mode 100644 src/net/PeytonPlayz585/glemu/vector/ReadableVector.java create mode 100644 src/net/PeytonPlayz585/glemu/vector/ReadableVector2f.java create mode 100644 src/net/PeytonPlayz585/glemu/vector/ReadableVector3f.java create mode 100644 src/net/PeytonPlayz585/glemu/vector/ReadableVector4f.java create mode 100644 src/net/PeytonPlayz585/glemu/vector/Vector.java create mode 100644 src/net/PeytonPlayz585/glemu/vector/Vector2f.java create mode 100644 src/net/PeytonPlayz585/glemu/vector/Vector3f.java create mode 100644 src/net/PeytonPlayz585/glemu/vector/Vector4f.java create mode 100644 src/net/PeytonPlayz585/glemu/vector/WritableVector2f.java create mode 100644 src/net/PeytonPlayz585/glemu/vector/WritableVector3f.java create mode 100644 src/net/PeytonPlayz585/glemu/vector/WritableVector4f.java create mode 100644 src/net/PeytonPlayz585/input/Keyboard.java create mode 100644 src/net/PeytonPlayz585/input/Mouse.java create mode 100644 src/net/PeytonPlayz585/opengl/EaglerAdapterGL30.java create mode 100644 src/net/PeytonPlayz585/opengl/GL11.java create mode 100644 src/net/PeytonPlayz585/opengl/GL12.java create mode 100644 src/net/PeytonPlayz585/opengl/RealOpenGLEnums.java create mode 100644 src/net/PeytonPlayz585/storage/LocalStorageManager.java create mode 100644 src/net/PeytonPlayz585/util/glu/GLU.java create mode 100644 src/net/PeytonPlayz595/nbt/NBTBase.java create mode 100644 src/net/PeytonPlayz595/nbt/NBTTagByte.java create mode 100644 src/net/PeytonPlayz595/nbt/NBTTagByteArray.java create mode 100644 src/net/PeytonPlayz595/nbt/NBTTagCompound.java create mode 100644 src/net/PeytonPlayz595/nbt/NBTTagDouble.java create mode 100644 src/net/PeytonPlayz595/nbt/NBTTagEnd.java create mode 100644 src/net/PeytonPlayz595/nbt/NBTTagFloat.java create mode 100644 src/net/PeytonPlayz595/nbt/NBTTagInt.java create mode 100644 src/net/PeytonPlayz595/nbt/NBTTagList.java create mode 100644 src/net/PeytonPlayz595/nbt/NBTTagLong.java create mode 100644 src/net/PeytonPlayz595/nbt/NBTTagShort.java create mode 100644 src/net/PeytonPlayz595/nbt/NBTTagString.java create mode 100644 src/net/lax1dude/eaglercraft/AssetRepository.java create mode 100644 src/net/lax1dude/eaglercraft/Base64.java create mode 100644 src/net/lax1dude/eaglercraft/BaseNCodec.java create mode 100644 src/net/lax1dude/eaglercraft/Client.java create mode 100644 src/net/lax1dude/eaglercraft/EaglerImage.java create mode 100644 src/net/lax1dude/eaglercraft/GLAllocation.java create mode 100644 src/net/lax1dude/eaglercraft/GeneralDigest.java create mode 100644 src/net/lax1dude/eaglercraft/JSONArray.java create mode 100644 src/net/lax1dude/eaglercraft/JSONException.java create mode 100644 src/net/lax1dude/eaglercraft/JSONObject.java create mode 100644 src/net/lax1dude/eaglercraft/JSONPointer.java create mode 100644 src/net/lax1dude/eaglercraft/JSONPointerException.java create mode 100644 src/net/lax1dude/eaglercraft/JSONPropertyIgnore.java create mode 100644 src/net/lax1dude/eaglercraft/JSONPropertyName.java create mode 100644 src/net/lax1dude/eaglercraft/JSONString.java create mode 100644 src/net/lax1dude/eaglercraft/JSONTokener.java create mode 100644 src/net/lax1dude/eaglercraft/JSONWriter.java create mode 100644 src/net/lax1dude/eaglercraft/SHA1Digest.java create mode 100644 src/net/lax1dude/eaglercraft/adapter/EaglerAdapterImpl2.java create mode 100644 src/net/lax1dude/eaglercraft/adapter/FileChooserResult.java create mode 100644 src/net/lax1dude/eaglercraft/adapter/teavm/BufferConverter.java create mode 100644 src/net/lax1dude/eaglercraft/adapter/teavm/IDBObjectStorePatched.java create mode 100644 src/net/lax1dude/eaglercraft/adapter/teavm/IndexedDBFilesystem.java create mode 100644 src/net/lax1dude/eaglercraft/adapter/teavm/WebGL2RenderingContext.java create mode 100644 src/net/lax1dude/eaglercraft/adapter/teavm/WebGLQuery.java create mode 100644 src/net/lax1dude/eaglercraft/adapter/teavm/WebGLVertexArray.java diff --git a/src/net/PeytonPlayz585/glemu/FixedFunctionShader.java b/src/net/PeytonPlayz585/glemu/FixedFunctionShader.java new file mode 100644 index 0000000..ae39a12 --- /dev/null +++ b/src/net/PeytonPlayz585/glemu/FixedFunctionShader.java @@ -0,0 +1,408 @@ +package net.PeytonPlayz585.glemu; + +import static net.PeytonPlayz585.opengl.GL11.*; + +import java.util.ArrayList; +import java.util.List; + +import net.PeytonPlayz585.glemu.vector.*; + +public class FixedFunctionShader { + + private static final FixedFunctionShader[] instances = new FixedFunctionShader[128]; + private static final List instanceList = new ArrayList(); + + public static void refreshCoreGL() { + for (int i = 0; i < instances.length; ++i) { + if (instances[i] != null) { + _wglDeleteProgram(instances[i].globject); + instances[i] = null; + } + } + instanceList.clear(); + shaderSource = null; + } + + public static final int COLOR = 1; + public static final int NORMAL = 2; + public static final int TEXTURE0 = 4; + public static final int LIGHTING = 8; + public static final int FOG = 16; + public static final int ALPHATEST = 32; + public static final int UNIT0 = 64; + + public static FixedFunctionShader instance(int i) { + FixedFunctionShader s = instances[i]; + if (s == null) { + boolean CC_a_color = false; + boolean CC_a_normal = false; + boolean CC_a_texture0 = false; + boolean CC_lighting = false; + boolean CC_fog = false; + boolean CC_alphatest = false; + boolean CC_unit0 = false; + if ((i & COLOR) == COLOR) { + CC_a_color = true; + } + if ((i & NORMAL) == NORMAL) { + CC_a_normal = true; + } + if ((i & TEXTURE0) == TEXTURE0) { + CC_a_texture0 = true; + } + if ((i & LIGHTING) == LIGHTING) { + CC_lighting = true; + } + if ((i & FOG) == FOG) { + CC_fog = true; + } + if ((i & ALPHATEST) == ALPHATEST) { + CC_alphatest = true; + } + if ((i & UNIT0) == UNIT0) { + CC_unit0 = true; + } + s = new FixedFunctionShader(i, CC_a_color, CC_a_normal, CC_a_texture0, CC_lighting, CC_fog, CC_alphatest, CC_unit0); + instances[i] = s; + instanceList.add(s); + } + return s; + } + + private static String shaderSource = null; + + private final boolean enable_color; + private final boolean enable_normal; + private final boolean enable_texture0; + private final boolean enable_lighting; + private final boolean enable_fog; + private final boolean enable_alphatest; + private final boolean enable_unit0; + private final ProgramGL globject; + + private UniformGL u_matrix_m = null; + private UniformGL u_matrix_p = null; + private UniformGL u_matrix_t = null; + + private UniformGL u_fogColor = null; + private UniformGL u_fogMode = null; + private UniformGL u_fogStart = null; + private UniformGL u_fogEnd = null; + private UniformGL u_fogDensity = null; + private UniformGL u_fogPremultiply = null; + + private UniformGL u_colorUniform = null; + private UniformGL u_normalUniform = null; + + private UniformGL u_alphaTestF = null; + + private UniformGL u_texCoordV0 = null; + + private UniformGL u_light0Pos = null; + private UniformGL u_light1Pos = null; + + private final int a_position; + private final int a_texture0; + private final int a_color; + private final int a_normal; + + private final int attributeIndexesToEnable; + + public final StreamBuffer streamBuffer; + public boolean bufferIsInitialized = false; + + private FixedFunctionShader(int j, boolean CC_a_color, boolean CC_a_normal, boolean CC_a_texture0, + boolean CC_lighting, boolean CC_fog, boolean CC_alphatest, boolean CC_unit0) { + enable_color = CC_a_color; + enable_normal = CC_a_normal; + enable_texture0 = CC_a_texture0; + enable_lighting = CC_lighting; + enable_fog = CC_fog; + enable_alphatest = CC_alphatest; + enable_unit0 = CC_unit0; + + if (shaderSource == null) { + shaderSource = fileContents("/glsl/core.glsl"); + } + + String source = ""; + if (enable_color) + source += "\n#define CC_a_color\n"; + if (enable_normal) + source += "#define CC_a_normal\n"; + if (enable_texture0) + source += "#define CC_a_texture0\n"; + if (enable_lighting) + source += "#define CC_lighting\n"; + if (enable_fog) + source += "#define CC_fog\n"; + if (enable_alphatest) + source += "#define CC_alphatest\n"; + if (enable_unit0) + source += "#define CC_unit0\n"; + source += shaderSource; + + ShaderGL v = _wglCreateShader(_wGL_VERTEX_SHADER); + _wglShaderSource(v, _wgetShaderHeader() + "\n#define CC_VERT\n" + source); + _wglCompileShader(v); + + if (!_wglGetShaderCompiled(v)) { + System.err.println(("\n\n" + _wglGetShaderInfoLog(v)).replace("\n", "\n[/glsl/core.glsl][CC_VERT] ")); + throw new RuntimeException("broken shader file"); + } + + ShaderGL f = _wglCreateShader(_wGL_FRAGMENT_SHADER); + _wglShaderSource(f, _wgetShaderHeader() + "\n#define CC_FRAG\n" + source); + _wglCompileShader(f); + + if (!_wglGetShaderCompiled(f)) { + System.err.println(("\n\n" + _wglGetShaderInfoLog(f)).replace("\n", "\n[/glsl/core.glsl][CC_FRAG] ")); + throw new RuntimeException("broken shader file"); + } + + globject = _wglCreateProgram(); + _wglAttachShader(globject, v); + _wglAttachShader(globject, f); + + int i = 0; + a_position = i++; + _wglBindAttributeLocation(globject, a_position, "a_position"); + + if (enable_texture0) { + a_texture0 = i++; + _wglBindAttributeLocation(globject, a_texture0, "a_texture0"); + } else { + a_texture0 = -1; + } + if (enable_color) { + a_color = i++; + _wglBindAttributeLocation(globject, a_color, "a_color"); + } else { + a_color = -1; + } + if (enable_normal) { + a_normal = i++; + _wglBindAttributeLocation(globject, a_normal, "a_normal"); + } else { + a_normal = -1; + } + + attributeIndexesToEnable = i; + + _wglLinkProgram(globject); + + _wglDetachShader(globject, v); + _wglDetachShader(globject, f); + _wglDeleteShader(v); + _wglDeleteShader(f); + + if (!_wglGetProgramLinked(globject)) { + System.err.println(("\n\n" + _wglGetProgramInfoLog(globject)).replace("\n", "\n[LINKER] ")); + throw new RuntimeException("broken shader file"); + } + + _wglUseProgram(globject); + + u_matrix_m = _wglGetUniformLocation(globject, "matrix_m"); + u_matrix_p = _wglGetUniformLocation(globject, "matrix_p"); + u_matrix_t = _wglGetUniformLocation(globject, "matrix_t"); + + u_colorUniform = _wglGetUniformLocation(globject, "colorUniform"); + + if (enable_lighting) { + u_normalUniform = _wglGetUniformLocation(globject, "normalUniform"); + u_light0Pos = _wglGetUniformLocation(globject, "light0Pos"); + u_light1Pos = _wglGetUniformLocation(globject, "light1Pos"); + } + + if (enable_fog) { + u_fogColor = _wglGetUniformLocation(globject, "fogColor"); + u_fogMode = _wglGetUniformLocation(globject, "fogMode"); + u_fogStart = _wglGetUniformLocation(globject, "fogStart"); + u_fogEnd = _wglGetUniformLocation(globject, "fogEnd"); + u_fogDensity = _wglGetUniformLocation(globject, "fogDensity"); + u_fogPremultiply = _wglGetUniformLocation(globject, "fogPremultiply"); + } + + if (enable_alphatest) { + u_alphaTestF = _wglGetUniformLocation(globject, "alphaTestF"); + } + + _wglUniform1i(_wglGetUniformLocation(globject, "tex0"), 0); + u_texCoordV0 = _wglGetUniformLocation(globject, "texCoordV0"); + + streamBuffer = new StreamBuffer(0x8000, 3, 8, (vertexArray, vertexBuffer) -> { + _wglBindVertexArray0(vertexArray); + _wglBindBuffer(_wGL_ARRAY_BUFFER, vertexBuffer); + setupArrayForProgram(); + }); + + } + + public void setupArrayForProgram() { + _wglEnableVertexAttribArray(a_position); + _wglVertexAttribPointer(a_position, 3, _wGL_FLOAT, false, 28, 0); + if (enable_texture0) { + _wglEnableVertexAttribArray(a_texture0); + _wglVertexAttribPointer(a_texture0, 2, _wGL_FLOAT, false, 28, 12); + } + if (enable_color) { + _wglEnableVertexAttribArray(a_color); + _wglVertexAttribPointer(a_color, 4, _wGL_UNSIGNED_BYTE, true, 28, 20); + } + if (enable_normal) { + _wglEnableVertexAttribArray(a_normal); + _wglVertexAttribPointer(a_normal, 4, _wGL_UNSIGNED_BYTE, true, 28, 24); + } + } + + public void useProgram() { + _wglUseProgram(globject); + } + + public void unuseProgram() { + + } + + public static void optimize() { + FixedFunctionShader pp; + for(int i = 0, l = instanceList.size(); i < l; ++i) { + instanceList.get(i).streamBuffer.optimize(); + } + } + + private float[] modelBuffer = new float[16]; + private float[] projectionBuffer = new float[16]; + private float[] textureBuffer = new float[16]; + + private Matrix4f modelMatrix = (Matrix4f) new Matrix4f().setZero(); + private Matrix4f projectionMatrix = (Matrix4f) new Matrix4f().setZero(); + private Matrix4f textureMatrix = (Matrix4f) new Matrix4f().setZero(); + private Vector4f light0Pos = new Vector4f(); + private Vector4f light1Pos = new Vector4f(); + + public void setModelMatrix(Matrix4f mat) { + if (!mat.equals(modelMatrix)) { + modelMatrix.load(mat).store(modelBuffer); + _wglUniformMat4fv(u_matrix_m, modelBuffer); + } + } + + public void setProjectionMatrix(Matrix4f mat) { + if (!mat.equals(projectionMatrix)) { + projectionMatrix.load(mat).store(projectionBuffer); + _wglUniformMat4fv(u_matrix_p, projectionBuffer); + } + } + + public void setTextureMatrix(Matrix4f mat) { + if (!mat.equals(textureMatrix)) { + textureMatrix.load(mat).store(textureBuffer); + _wglUniformMat4fv(u_matrix_t, textureBuffer); + } + } + + public void setLightPositions(Vector4f pos0, Vector4f pos1) { + if (!pos0.equals(light0Pos) || !pos1.equals(light1Pos)) { + light0Pos.set(pos0); + light1Pos.set(pos1); + _wglUniform3f(u_light0Pos, light0Pos.x, light0Pos.y, light0Pos.z); + _wglUniform3f(u_light1Pos, light1Pos.x, light1Pos.y, light1Pos.z); + } + } + + private int fogMode = 0; + + public void setFogMode(int mode) { + if (fogMode != mode) { + fogMode = mode; + _wglUniform1i(u_fogMode, mode % 2); + _wglUniform1f(u_fogPremultiply, mode / 2); + } + } + + private float fogColorR = 0.0f; + private float fogColorG = 0.0f; + private float fogColorB = 0.0f; + private float fogColorA = 0.0f; + + public void setFogColor(float r, float g, float b, float a) { + if (fogColorR != r || fogColorG != g || fogColorB != b || fogColorA != a) { + fogColorR = r; + fogColorG = g; + fogColorB = b; + fogColorA = a; + _wglUniform4f(u_fogColor, fogColorR, fogColorG, fogColorB, fogColorA); + } + } + + private float fogStart = 0.0f; + private float fogEnd = 0.0f; + + public void setFogStartEnd(float s, float e) { + if (fogStart != s || fogEnd != e) { + fogStart = s; + fogEnd = e; + _wglUniform1f(u_fogStart, fogStart); + _wglUniform1f(u_fogEnd, fogEnd); + } + } + + private float fogDensity = 0.0f; + + public void setFogDensity(float d) { + if (fogDensity != d) { + fogDensity = d; + _wglUniform1f(u_fogDensity, fogDensity); + } + } + + private float alphaTestValue = 0.0f; + + public void setAlphaTest(float limit) { + if (alphaTestValue != limit) { + alphaTestValue = limit; + _wglUniform1f(u_alphaTestF, alphaTestValue); + } + } + + private float tex0x = 0.0f; + private float tex0y = 0.0f; + + public void setTex0Coords(float x, float y) { + if (tex0x != x || tex0y != y) { + tex0x = x; + tex0y = y; + _wglUniform2f(u_texCoordV0, tex0x, tex0y); + } + } + + private float colorUniformR = 0.0f; + private float colorUniformG = 0.0f; + private float colorUniformB = 0.0f; + private float colorUniformA = 0.0f; + + public void setColor(float r, float g, float b, float a) { + if (colorUniformR != r || colorUniformG != g || colorUniformB != b || colorUniformA != a) { + colorUniformR = r; + colorUniformG = g; + colorUniformB = b; + colorUniformA = a; + _wglUniform4f(u_colorUniform, colorUniformR, colorUniformG, colorUniformB, colorUniformA); + } + } + + private float normalUniformX = 0.0f; + private float normalUniformY = 0.0f; + private float normalUniformZ = 0.0f; + + public void setNormal(float x, float y, float z) { + if (normalUniformX != x || normalUniformY != y || normalUniformZ != z) { + normalUniformX = x; + normalUniformY = y; + normalUniformZ = z; + _wglUniform3f(u_normalUniform, normalUniformX, normalUniformY, normalUniformZ); + } + } + +} \ No newline at end of file diff --git a/src/net/PeytonPlayz585/glemu/GLObjectMap.java b/src/net/PeytonPlayz585/glemu/GLObjectMap.java new file mode 100644 index 0000000..409d52a --- /dev/null +++ b/src/net/PeytonPlayz585/glemu/GLObjectMap.java @@ -0,0 +1,55 @@ +package net.PeytonPlayz585.glemu; + +public class GLObjectMap { + private Object[] values; + private int size; + private int insertIndex; + public int allocatedObjects; + + public GLObjectMap(int initialSize) { + this.values = new Object[initialSize]; + this.size = initialSize; + this.insertIndex = 0; + this.allocatedObjects = 0; + } + + public int register(T obj) { + int start = insertIndex; + do { + ++insertIndex; + if (insertIndex >= size) { + insertIndex = 0; + } + if (insertIndex == start) { + resize(); + return register(obj); + } + } while (values[insertIndex] != null); + values[insertIndex] = obj; + ++allocatedObjects; + return insertIndex; + } + + public T free(int obj) { + if (obj >= size || obj < 0) + return null; + Object ret = values[obj]; + values[obj] = null; + --allocatedObjects; + return (T) ret; + } + + public T get(int obj) { + if (obj >= size || obj < 0) + return null; + return (T) values[obj]; + } + + private void resize() { + int oldSize = size; + size += size / 2; + Object[] oldValues = values; + values = new Object[size]; + System.arraycopy(oldValues, 0, values, 0, oldSize); + } +} \ No newline at end of file diff --git a/src/net/PeytonPlayz585/glemu/GameOverlayFramebuffer.java b/src/net/PeytonPlayz585/glemu/GameOverlayFramebuffer.java new file mode 100644 index 0000000..ca53d15 --- /dev/null +++ b/src/net/PeytonPlayz585/glemu/GameOverlayFramebuffer.java @@ -0,0 +1,72 @@ +package net.PeytonPlayz585.glemu; + +import static net.PeytonPlayz585.opengl.GL11.*; + +import java.nio.ByteBuffer; + +public class GameOverlayFramebuffer { + + private long age = -1l; + + private int currentWidth = -1; + private int currentHeight = -1; + + private FramebufferGL framebuffer = null; + private TextureGL framebufferColor = null; + private RenderbufferGL depthBuffer = null; + + public void beginRender(int width, int height) { + if(framebuffer == null) { + framebuffer = _wglCreateFramebuffer(); + depthBuffer = _wglCreateRenderBuffer(); + framebufferColor = _wglGenTextures(); + _wglBindFramebuffer(_wGL_FRAMEBUFFER, framebuffer); + glBindTexture(_wGL_TEXTURE_2D, framebufferColor); + _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + _wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + _wglFramebufferTexture2D(_wGL_COLOR_ATTACHMENT0, framebufferColor, 0); + _wglBindRenderbuffer(depthBuffer); + _wglFramebufferRenderbuffer(_wGL_DEPTH_ATTACHMENT, depthBuffer); + } + + if(currentWidth != width || currentHeight != height) { + currentWidth = width; + currentHeight = height; + glBindTexture(_wGL_TEXTURE_2D, framebufferColor); + _wglTexImage2D(_wGL_TEXTURE_2D, 0, _wGL_RGBA8, width, height, 0, _wGL_RGBA, _wGL_UNSIGNED_BYTE, (ByteBuffer)null); + _wglBindRenderbuffer(depthBuffer); + _wglRenderbufferStorage(0x81A5, width, height); + } + + _wglBindFramebuffer(_wGL_FRAMEBUFFER, framebuffer); + } + + public void endRender() { + _wglBindFramebuffer(_wGL_FRAMEBUFFER, null); + age = System.currentTimeMillis(); + } + + public long getAge() { + return age == -1l ? -1l : (System.currentTimeMillis() - age); + } + + public void bindTexture() { + glBindTexture(_wGL_TEXTURE_2D, framebufferColor); + } + + public void destroy() { + if(framebuffer != null) { + _wglDeleteFramebuffer(framebuffer); + _wglDeleteRenderbuffer(depthBuffer); + _wglDeleteTextures(framebufferColor); + framebuffer = null; + depthBuffer = null; + framebufferColor = null; + age = -1l; + _wglBindFramebuffer(_wGL_FRAMEBUFFER, null); + } + } + +} \ No newline at end of file diff --git a/src/net/PeytonPlayz585/glemu/ModeBuffer.java b/src/net/PeytonPlayz585/glemu/ModeBuffer.java new file mode 100644 index 0000000..f6aaee7 --- /dev/null +++ b/src/net/PeytonPlayz585/glemu/ModeBuffer.java @@ -0,0 +1,58 @@ +package net.PeytonPlayz585.glemu; + +import java.nio.FloatBuffer; + +import net.lax1dude.eaglercraft.GLAllocation; + +/** + * Utility class that emulates immediate mode vertex data submission. + * Can be used to create VBO data. + */ +public final class ModeBuffer { + + private FloatBuffer buffer; + + public ModeBuffer(final int startSize) { + this.buffer = GLAllocation.createDirectFloatBuffer(startSize); + } + + private void checkSize(final int count) { + while ( buffer.remaining() < count ) { + final FloatBuffer newBuffer = GLAllocation.createDirectFloatBuffer(buffer.capacity() << 1); + buffer.flip(); + newBuffer.put(buffer); + buffer = newBuffer; + } + } + + public FloatBuffer getBuffer() { + buffer.flip(); + return buffer; + } + + public void glVertex2f(final float x, final float y) { + checkSize(2); + buffer.put(x).put(y); + } + + public void glVertex3f(final float x, final float y, final float z) { + checkSize(3); + buffer.put(x).put(y).put(z); + } + + public void glVertex4f(final float x, final float y, final float z, final float w) { + checkSize(4); + buffer.put(x).put(y).put(z).put(w); + } + + public void glNormal3f(final float x, final float y, final float z) { + checkSize(3); + buffer.put(x).put(y).put(z); + } + + public void glTexCoord2f(final float s, final float t) { + checkSize(2); + buffer.put(s).put(t); + } + +} \ No newline at end of file diff --git a/src/net/PeytonPlayz585/glemu/StreamBuffer.java b/src/net/PeytonPlayz585/glemu/StreamBuffer.java new file mode 100644 index 0000000..f2858cd --- /dev/null +++ b/src/net/PeytonPlayz585/glemu/StreamBuffer.java @@ -0,0 +1,130 @@ +package net.PeytonPlayz585.glemu; + +import static net.PeytonPlayz585.opengl.GL11.*; + +public class StreamBuffer { + + public final int initialSize; + public final int initialCount; + public final int maxCount; + + protected StreamBufferInstance[] buffers; + + protected int currentBufferId = 0; + protected int overflowCounter = 0; + + protected final IStreamBufferInitializer initializer; + + public static class StreamBufferInstance { + + public BufferArrayGL vertexArray = null; + public BufferGL vertexBuffer = null; + protected int vertexBufferSize = 0; + + public boolean bindQuad16 = false; + public boolean bindQuad32 = false; + + public BufferArrayGL getVertexArray() { + return vertexArray; + } + + public BufferGL getVertexBuffer() { + return vertexBuffer; + } + + } + + public static interface IStreamBufferInitializer { + void initialize(BufferArrayGL vertexArray, BufferGL vertexBuffer); + } + + public StreamBuffer(int initialSize, int initialCount, int maxCount, IStreamBufferInitializer initializer) { + this.buffers = new StreamBufferInstance[initialCount]; + for(int i = 0; i < this.buffers.length; ++i) { + this.buffers[i] = new StreamBufferInstance(); + } + this.initialSize = initialSize; + this.initialCount = initialCount; + this.maxCount = maxCount; + this.initializer = initializer; + } + + public StreamBufferInstance getBuffer(int requiredMemory) { + StreamBufferInstance next = buffers[(currentBufferId++) % buffers.length]; + if(next.vertexBuffer == null) { + next.vertexBuffer = _wglCreateBuffer(); + } + if(next.vertexArray == null) { + next.vertexArray = _wglCreateVertexArray(); + initializer.initialize(next.vertexArray, next.vertexBuffer); + } + if(next.vertexBufferSize < requiredMemory) { + int newSize = (requiredMemory & 0xFFFFF000) + 0x2000; + _wglBindBuffer(_wGL_ARRAY_BUFFER, next.vertexBuffer); + _wglBufferData00(_wGL_ARRAY_BUFFER, newSize, _wGL_STREAM_DRAW); + next.vertexBufferSize = newSize; + } + return next; + } + + public void optimize() { + overflowCounter += currentBufferId - buffers.length; + if(overflowCounter < -25) { + int newCount = buffers.length - 1 + ((overflowCounter + 25) / 5); + if(newCount < initialCount) { + newCount = initialCount; + } + if(newCount < buffers.length) { + StreamBufferInstance[] newArray = new StreamBufferInstance[newCount]; + for(int i = 0; i < buffers.length; ++i) { + if(i < newArray.length) { + newArray[i] = buffers[i]; + }else { + if(buffers[i].vertexArray != null) { + _wglDeleteVertexArray(buffers[i].vertexArray); + } + if(buffers[i].vertexBuffer != null) { + _wglDeleteBuffer(buffers[i].vertexBuffer); + } + } + } + buffers = newArray; + } + overflowCounter = 0; + }else if(overflowCounter > 15) { + int newCount = buffers.length + 1 + ((overflowCounter - 15) / 5); + if(newCount > maxCount) { + newCount = maxCount; + } + if(newCount > buffers.length) { + StreamBufferInstance[] newArray = new StreamBufferInstance[newCount]; + for(int i = 0; i < newArray.length; ++i) { + if(i < buffers.length) { + newArray[i] = buffers[i]; + }else { + newArray[i] = new StreamBufferInstance(); + } + } + buffers = newArray; + } + overflowCounter = 0; + } + currentBufferId = 0; + } + + public void destroy() { + for(int i = 0; i < buffers.length; ++i) { + StreamBufferInstance next = buffers[i]; + if(next.vertexArray != null) { + _wglDeleteVertexArray(next.vertexArray); + } + if(next.vertexBuffer != null) { + _wglDeleteBuffer(next.vertexBuffer); + } + } + buffers = new StreamBufferInstance[initialCount]; + for(int i = 0; i < buffers.length; ++i) { + buffers[i] = new StreamBufferInstance(); + } + } +} \ No newline at end of file diff --git a/src/net/PeytonPlayz585/glemu/vector/Matrix.java b/src/net/PeytonPlayz585/glemu/vector/Matrix.java new file mode 100644 index 0000000..d3e962d --- /dev/null +++ b/src/net/PeytonPlayz585/glemu/vector/Matrix.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.PeytonPlayz585.glemu.vector; + +import java.io.Serializable; +import java.nio.FloatBuffer; + +/** + * + * Base class for matrices. When a matrix is constructed it will be the identity + * matrix unless otherwise stated. + * + * @author cix_foo + * @version $Revision$ $Id$ + */ +public abstract class Matrix implements Serializable { + + /** + * Constructor for Matrix. + */ + protected Matrix() { + super(); + } + + /** + * Set this matrix to be the identity matrix. + * + * @return this + */ + public abstract Matrix setIdentity(); + + /** + * Invert this matrix + * + * @return this + */ + public abstract Matrix invert(); + + /** + * Load from a float buffer. The buffer stores the matrix in column major + * (OpenGL) order. + * + * @param buf A float buffer to read from + * @return this + */ + public abstract Matrix load(FloatBuffer buf); + + /** + * Load from a float buffer. The buffer stores the matrix in row major + * (mathematical) order. + * + * @param buf A float buffer to read from + * @return this + */ + public abstract Matrix loadTranspose(FloatBuffer buf); + + /** + * Negate this matrix + * + * @return this + */ + public abstract Matrix negate(); + + /** + * Store this matrix in a float buffer. The matrix is stored in column major + * (openGL) order. + * + * @param buf The buffer to store this matrix in + * @return this + */ + public abstract Matrix store(FloatBuffer buf); + + /** + * Store this matrix in a float buffer. The matrix is stored in row major + * (maths) order. + * + * @param buf The buffer to store this matrix in + * @return this + */ + public abstract Matrix storeTranspose(FloatBuffer buf); + + /** + * Transpose this matrix + * + * @return this + */ + public abstract Matrix transpose(); + + /** + * Set this matrix to 0. + * + * @return this + */ + public abstract Matrix setZero(); + + /** + * @return the determinant of the matrix + */ + public abstract float determinant(); + +} diff --git a/src/net/PeytonPlayz585/glemu/vector/Matrix2f.java b/src/net/PeytonPlayz585/glemu/vector/Matrix2f.java new file mode 100644 index 0000000..e48997e --- /dev/null +++ b/src/net/PeytonPlayz585/glemu/vector/Matrix2f.java @@ -0,0 +1,418 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.PeytonPlayz585.glemu.vector; + +import java.io.Serializable; +import java.nio.FloatBuffer; + +/** + * + * Holds a 2x2 matrix + * + * @author cix_foo + * @version $Revision$ $Id$ + */ + +public class Matrix2f extends Matrix implements Serializable { + + private static final long serialVersionUID = 1L; + + public float m00, m01, m10, m11; + + /** + * Constructor for Matrix2f. The matrix is initialised to the identity. + */ + public Matrix2f() { + setIdentity(); + } + + /** + * Constructor + */ + public Matrix2f(Matrix2f src) { + load(src); + } + + /** + * Load from another matrix + * + * @param src The source matrix + * @return this + */ + public Matrix2f load(Matrix2f src) { + return load(src, this); + } + + /** + * Copy the source matrix to the destination matrix. + * + * @param src The source matrix + * @param dest The destination matrix, or null if a new one should be created. + * @return The copied matrix + */ + public static Matrix2f load(Matrix2f src, Matrix2f dest) { + if (dest == null) + dest = new Matrix2f(); + + dest.m00 = src.m00; + dest.m01 = src.m01; + dest.m10 = src.m10; + dest.m11 = src.m11; + + return dest; + } + + /** + * Load from a float buffer. The buffer stores the matrix in column major + * (OpenGL) order. + * + * @param buf A float buffer to read from + * @return this + */ + public Matrix load(FloatBuffer buf) { + + m00 = buf.get(); + m01 = buf.get(); + m10 = buf.get(); + m11 = buf.get(); + + return this; + } + + /** + * Load from a float buffer. The buffer stores the matrix in row major + * (mathematical) order. + * + * @param buf A float buffer to read from + * @return this + */ + public Matrix loadTranspose(FloatBuffer buf) { + + m00 = buf.get(); + m10 = buf.get(); + m01 = buf.get(); + m11 = buf.get(); + + return this; + } + + /** + * Store this matrix in a float buffer. The matrix is stored in column major + * (openGL) order. + * + * @param buf The buffer to store this matrix in + */ + public Matrix store(FloatBuffer buf) { + buf.put(m00); + buf.put(m01); + buf.put(m10); + buf.put(m11); + return this; + } + + /** + * Store this matrix in a float buffer. The matrix is stored in row major + * (maths) order. + * + * @param buf The buffer to store this matrix in + */ + public Matrix storeTranspose(FloatBuffer buf) { + buf.put(m00); + buf.put(m10); + buf.put(m01); + buf.put(m11); + return this; + } + + /** + * Add two matrices together and place the result in a third matrix. + * + * @param left The left source matrix + * @param right The right source matrix + * @param dest The destination matrix, or null if a new one is to be created + * @return the destination matrix + */ + public static Matrix2f add(Matrix2f left, Matrix2f right, Matrix2f dest) { + if (dest == null) + dest = new Matrix2f(); + + dest.m00 = left.m00 + right.m00; + dest.m01 = left.m01 + right.m01; + dest.m10 = left.m10 + right.m10; + dest.m11 = left.m11 + right.m11; + + return dest; + } + + /** + * Subtract the right matrix from the left and place the result in a third + * matrix. + * + * @param left The left source matrix + * @param right The right source matrix + * @param dest The destination matrix, or null if a new one is to be created + * @return the destination matrix + */ + public static Matrix2f sub(Matrix2f left, Matrix2f right, Matrix2f dest) { + if (dest == null) + dest = new Matrix2f(); + + dest.m00 = left.m00 - right.m00; + dest.m01 = left.m01 - right.m01; + dest.m10 = left.m10 - right.m10; + dest.m11 = left.m11 - right.m11; + + return dest; + } + + /** + * Multiply the right matrix by the left and place the result in a third matrix. + * + * @param left The left source matrix + * @param right The right source matrix + * @param dest The destination matrix, or null if a new one is to be created + * @return the destination matrix + */ + public static Matrix2f mul(Matrix2f left, Matrix2f right, Matrix2f dest) { + if (dest == null) + dest = new Matrix2f(); + + float m00 = left.m00 * right.m00 + left.m10 * right.m01; + float m01 = left.m01 * right.m00 + left.m11 * right.m01; + float m10 = left.m00 * right.m10 + left.m10 * right.m11; + float m11 = left.m01 * right.m10 + left.m11 * right.m11; + + dest.m00 = m00; + dest.m01 = m01; + dest.m10 = m10; + dest.m11 = m11; + + return dest; + } + + /** + * Transform a Vector by a matrix and return the result in a destination vector. + * + * @param left The left matrix + * @param right The right vector + * @param dest The destination vector, or null if a new one is to be created + * @return the destination vector + */ + public static Vector2f transform(Matrix2f left, Vector2f right, Vector2f dest) { + if (dest == null) + dest = new Vector2f(); + + float x = left.m00 * right.x + left.m10 * right.y; + float y = left.m01 * right.x + left.m11 * right.y; + + dest.x = x; + dest.y = y; + + return dest; + } + + /** + * Transpose this matrix + * + * @return this + */ + public Matrix transpose() { + return transpose(this); + } + + /** + * Transpose this matrix and place the result in another matrix. + * + * @param dest The destination matrix or null if a new matrix is to be created + * @return the transposed matrix + */ + public Matrix2f transpose(Matrix2f dest) { + return transpose(this, dest); + } + + /** + * Transpose the source matrix and place the result in the destination matrix. + * + * @param src The source matrix or null if a new matrix is to be created + * @param dest The destination matrix or null if a new matrix is to be created + * @return the transposed matrix + */ + public static Matrix2f transpose(Matrix2f src, Matrix2f dest) { + if (dest == null) + dest = new Matrix2f(); + + float m01 = src.m10; + float m10 = src.m01; + + dest.m01 = m01; + dest.m10 = m10; + + return dest; + } + + /** + * Invert this matrix + * + * @return this if successful, null otherwise + */ + public Matrix invert() { + return invert(this, this); + } + + /** + * Invert the source matrix and place the result in the destination matrix. + * + * @param src The source matrix to be inverted + * @param dest The destination matrix or null if a new matrix is to be created + * @return The inverted matrix, or null if source can't be reverted. + */ + public static Matrix2f invert(Matrix2f src, Matrix2f dest) { + /* + * inv(A) = 1/det(A) * adj(A); + */ + + float determinant = src.determinant(); + if (determinant != 0) { + if (dest == null) + dest = new Matrix2f(); + float determinant_inv = 1f / determinant; + float t00 = src.m11 * determinant_inv; + float t01 = -src.m01 * determinant_inv; + float t11 = src.m00 * determinant_inv; + float t10 = -src.m10 * determinant_inv; + + dest.m00 = t00; + dest.m01 = t01; + dest.m10 = t10; + dest.m11 = t11; + return dest; + } else + return null; + } + + /** + * Returns a string representation of this matrix + */ + public String toString() { + StringBuilder buf = new StringBuilder(); + buf.append(m00).append(' ').append(m10).append(' ').append('\n'); + buf.append(m01).append(' ').append(m11).append(' ').append('\n'); + return buf.toString(); + } + + /** + * Negate this matrix + * + * @return this + */ + public Matrix negate() { + return negate(this); + } + + /** + * Negate this matrix and stash the result in another matrix. + * + * @param dest The destination matrix, or null if a new matrix is to be created + * @return the negated matrix + */ + public Matrix2f negate(Matrix2f dest) { + return negate(this, dest); + } + + /** + * Negate the source matrix and stash the result in the destination matrix. + * + * @param src The source matrix to be negated + * @param dest The destination matrix, or null if a new matrix is to be created + * @return the negated matrix + */ + public static Matrix2f negate(Matrix2f src, Matrix2f dest) { + if (dest == null) + dest = new Matrix2f(); + + dest.m00 = -src.m00; + dest.m01 = -src.m01; + dest.m10 = -src.m10; + dest.m11 = -src.m11; + + return dest; + } + + /** + * Set this matrix to be the identity matrix. + * + * @return this + */ + public Matrix setIdentity() { + return setIdentity(this); + } + + /** + * Set the source matrix to be the identity matrix. + * + * @param src The matrix to set to the identity. + * @return The source matrix + */ + public static Matrix2f setIdentity(Matrix2f src) { + src.m00 = 1.0f; + src.m01 = 0.0f; + src.m10 = 0.0f; + src.m11 = 1.0f; + return src; + } + + /** + * Set this matrix to 0. + * + * @return this + */ + public Matrix setZero() { + return setZero(this); + } + + public static Matrix2f setZero(Matrix2f src) { + src.m00 = 0.0f; + src.m01 = 0.0f; + src.m10 = 0.0f; + src.m11 = 0.0f; + return src; + } + + /* + * (non-Javadoc) + * + * @see org.lwjgl.vector.Matrix#determinant() + */ + public float determinant() { + return m00 * m11 - m01 * m10; + } +} diff --git a/src/net/PeytonPlayz585/glemu/vector/Matrix3f.java b/src/net/PeytonPlayz585/glemu/vector/Matrix3f.java new file mode 100644 index 0000000..06ccf59 --- /dev/null +++ b/src/net/PeytonPlayz585/glemu/vector/Matrix3f.java @@ -0,0 +1,529 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.PeytonPlayz585.glemu.vector; + +import java.io.Serializable; +import java.nio.FloatBuffer; + +/** + * + * Holds a 3x3 matrix. + * + * @author cix_foo + * @version $Revision$ $Id$ + */ + +public class Matrix3f extends Matrix implements Serializable { + + private static final long serialVersionUID = 1L; + + public float m00, m01, m02, m10, m11, m12, m20, m21, m22; + + /** + * Constructor for Matrix3f. Matrix is initialised to the identity. + */ + public Matrix3f() { + super(); + setIdentity(); + } + + /** + * Load from another matrix + * + * @param src The source matrix + * @return this + */ + public Matrix3f load(Matrix3f src) { + return load(src, this); + } + + /** + * Copy source matrix to destination matrix + * + * @param src The source matrix + * @param dest The destination matrix, or null of a new matrix is to be created + * @return The copied matrix + */ + public static Matrix3f load(Matrix3f src, Matrix3f dest) { + if (dest == null) + dest = new Matrix3f(); + + dest.m00 = src.m00; + dest.m10 = src.m10; + dest.m20 = src.m20; + dest.m01 = src.m01; + dest.m11 = src.m11; + dest.m21 = src.m21; + dest.m02 = src.m02; + dest.m12 = src.m12; + dest.m22 = src.m22; + + return dest; + } + + /** + * Load from a float buffer. The buffer stores the matrix in column major + * (OpenGL) order. + * + * @param buf A float buffer to read from + * @return this + */ + public Matrix load(FloatBuffer buf) { + + m00 = buf.get(); + m01 = buf.get(); + m02 = buf.get(); + m10 = buf.get(); + m11 = buf.get(); + m12 = buf.get(); + m20 = buf.get(); + m21 = buf.get(); + m22 = buf.get(); + + return this; + } + + /** + * Load from a float buffer. The buffer stores the matrix in row major (maths) + * order. + * + * @param buf A float buffer to read from + * @return this + */ + public Matrix loadTranspose(FloatBuffer buf) { + + m00 = buf.get(); + m10 = buf.get(); + m20 = buf.get(); + m01 = buf.get(); + m11 = buf.get(); + m21 = buf.get(); + m02 = buf.get(); + m12 = buf.get(); + m22 = buf.get(); + + return this; + } + + /** + * Store this matrix in a float buffer. The matrix is stored in column major + * (openGL) order. + * + * @param buf The buffer to store this matrix in + */ + public Matrix store(FloatBuffer buf) { + buf.put(m00); + buf.put(m01); + buf.put(m02); + buf.put(m10); + buf.put(m11); + buf.put(m12); + buf.put(m20); + buf.put(m21); + buf.put(m22); + return this; + } + + public Matrix store(float[] buf) { + buf[0] = m00; + buf[1] = m01; + buf[2] = m02; + buf[3] = m10; + buf[4] = m11; + buf[5] = m12; + buf[6] = m20; + buf[7] = m21; + buf[8] = m22; + return this; + } + + /** + * Store this matrix in a float buffer. The matrix is stored in row major + * (maths) order. + * + * @param buf The buffer to store this matrix in + */ + public Matrix storeTranspose(FloatBuffer buf) { + buf.put(m00); + buf.put(m10); + buf.put(m20); + buf.put(m01); + buf.put(m11); + buf.put(m21); + buf.put(m02); + buf.put(m12); + buf.put(m22); + return this; + } + + /** + * Add two matrices together and place the result in a third matrix. + * + * @param left The left source matrix + * @param right The right source matrix + * @param dest The destination matrix, or null if a new one is to be created + * @return the destination matrix + */ + public static Matrix3f add(Matrix3f left, Matrix3f right, Matrix3f dest) { + if (dest == null) + dest = new Matrix3f(); + + dest.m00 = left.m00 + right.m00; + dest.m01 = left.m01 + right.m01; + dest.m02 = left.m02 + right.m02; + dest.m10 = left.m10 + right.m10; + dest.m11 = left.m11 + right.m11; + dest.m12 = left.m12 + right.m12; + dest.m20 = left.m20 + right.m20; + dest.m21 = left.m21 + right.m21; + dest.m22 = left.m22 + right.m22; + + return dest; + } + + /** + * Subtract the right matrix from the left and place the result in a third + * matrix. + * + * @param left The left source matrix + * @param right The right source matrix + * @param dest The destination matrix, or null if a new one is to be created + * @return the destination matrix + */ + public static Matrix3f sub(Matrix3f left, Matrix3f right, Matrix3f dest) { + if (dest == null) + dest = new Matrix3f(); + + dest.m00 = left.m00 - right.m00; + dest.m01 = left.m01 - right.m01; + dest.m02 = left.m02 - right.m02; + dest.m10 = left.m10 - right.m10; + dest.m11 = left.m11 - right.m11; + dest.m12 = left.m12 - right.m12; + dest.m20 = left.m20 - right.m20; + dest.m21 = left.m21 - right.m21; + dest.m22 = left.m22 - right.m22; + + return dest; + } + + /** + * Multiply the right matrix by the left and place the result in a third matrix. + * + * @param left The left source matrix + * @param right The right source matrix + * @param dest The destination matrix, or null if a new one is to be created + * @return the destination matrix + */ + public static Matrix3f mul(Matrix3f left, Matrix3f right, Matrix3f dest) { + if (dest == null) + dest = new Matrix3f(); + + float m00 = left.m00 * right.m00 + left.m10 * right.m01 + left.m20 * right.m02; + float m01 = left.m01 * right.m00 + left.m11 * right.m01 + left.m21 * right.m02; + float m02 = left.m02 * right.m00 + left.m12 * right.m01 + left.m22 * right.m02; + float m10 = left.m00 * right.m10 + left.m10 * right.m11 + left.m20 * right.m12; + float m11 = left.m01 * right.m10 + left.m11 * right.m11 + left.m21 * right.m12; + float m12 = left.m02 * right.m10 + left.m12 * right.m11 + left.m22 * right.m12; + float m20 = left.m00 * right.m20 + left.m10 * right.m21 + left.m20 * right.m22; + float m21 = left.m01 * right.m20 + left.m11 * right.m21 + left.m21 * right.m22; + float m22 = left.m02 * right.m20 + left.m12 * right.m21 + left.m22 * right.m22; + + dest.m00 = m00; + dest.m01 = m01; + dest.m02 = m02; + dest.m10 = m10; + dest.m11 = m11; + dest.m12 = m12; + dest.m20 = m20; + dest.m21 = m21; + dest.m22 = m22; + + return dest; + } + + /** + * Transform a Vector by a matrix and return the result in a destination vector. + * + * @param left The left matrix + * @param right The right vector + * @param dest The destination vector, or null if a new one is to be created + * @return the destination vector + */ + public static Vector3f transform(Matrix3f left, Vector3f right, Vector3f dest) { + if (dest == null) + dest = new Vector3f(); + + float x = left.m00 * right.x + left.m10 * right.y + left.m20 * right.z; + float y = left.m01 * right.x + left.m11 * right.y + left.m21 * right.z; + float z = left.m02 * right.x + left.m12 * right.y + left.m22 * right.z; + + dest.x = x; + dest.y = y; + dest.z = z; + + return dest; + } + + /** + * Transpose this matrix + * + * @return this + */ + public Matrix transpose() { + return transpose(this, this); + } + + /** + * Transpose this matrix and place the result in another matrix + * + * @param dest The destination matrix or null if a new matrix is to be created + * @return the transposed matrix + */ + public Matrix3f transpose(Matrix3f dest) { + return transpose(this, dest); + } + + /** + * Transpose the source matrix and place the result into the destination matrix + * + * @param src The source matrix to be transposed + * @param dest The destination matrix or null if a new matrix is to be created + * @return the transposed matrix + */ + public static Matrix3f transpose(Matrix3f src, Matrix3f dest) { + if (dest == null) + dest = new Matrix3f(); + float m00 = src.m00; + float m01 = src.m10; + float m02 = src.m20; + float m10 = src.m01; + float m11 = src.m11; + float m12 = src.m21; + float m20 = src.m02; + float m21 = src.m12; + float m22 = src.m22; + + dest.m00 = m00; + dest.m01 = m01; + dest.m02 = m02; + dest.m10 = m10; + dest.m11 = m11; + dest.m12 = m12; + dest.m20 = m20; + dest.m21 = m21; + dest.m22 = m22; + return dest; + } + + /** + * @return the determinant of the matrix + */ + public float determinant() { + float f = m00 * (m11 * m22 - m12 * m21) + m01 * (m12 * m20 - m10 * m22) + m02 * (m10 * m21 - m11 * m20); + return f; + } + + /** + * Returns a string representation of this matrix + */ + public String toString() { + StringBuilder buf = new StringBuilder(); + buf.append(m00).append(' ').append(m10).append(' ').append(m20).append(' ').append('\n'); + buf.append(m01).append(' ').append(m11).append(' ').append(m21).append(' ').append('\n'); + buf.append(m02).append(' ').append(m12).append(' ').append(m22).append(' ').append('\n'); + return buf.toString(); + } + + /** + * Invert this matrix + * + * @return this if successful, null otherwise + */ + public Matrix invert() { + return invert(this, this); + } + + /** + * Invert the source matrix and put the result into the destination matrix + * + * @param src The source matrix to be inverted + * @param dest The destination matrix, or null if a new one is to be created + * @return The inverted matrix if successful, null otherwise + */ + public static Matrix3f invert(Matrix3f src, Matrix3f dest) { + float determinant = src.determinant(); + + if (determinant != 0) { + if (dest == null) + dest = new Matrix3f(); + /* + * do it the ordinary way + * + * inv(A) = 1/det(A) * adj(T), where adj(T) = transpose(Conjugate Matrix) + * + * m00 m01 m02 m10 m11 m12 m20 m21 m22 + */ + float determinant_inv = 1f / determinant; + + // get the conjugate matrix + float t00 = src.m11 * src.m22 - src.m12 * src.m21; + float t01 = -src.m10 * src.m22 + src.m12 * src.m20; + float t02 = src.m10 * src.m21 - src.m11 * src.m20; + float t10 = -src.m01 * src.m22 + src.m02 * src.m21; + float t11 = src.m00 * src.m22 - src.m02 * src.m20; + float t12 = -src.m00 * src.m21 + src.m01 * src.m20; + float t20 = src.m01 * src.m12 - src.m02 * src.m11; + float t21 = -src.m00 * src.m12 + src.m02 * src.m10; + float t22 = src.m00 * src.m11 - src.m01 * src.m10; + + dest.m00 = t00 * determinant_inv; + dest.m11 = t11 * determinant_inv; + dest.m22 = t22 * determinant_inv; + dest.m01 = t10 * determinant_inv; + dest.m10 = t01 * determinant_inv; + dest.m20 = t02 * determinant_inv; + dest.m02 = t20 * determinant_inv; + dest.m12 = t21 * determinant_inv; + dest.m21 = t12 * determinant_inv; + return dest; + } else + return null; + } + + /** + * Negate this matrix + * + * @return this + */ + public Matrix negate() { + return negate(this); + } + + /** + * Negate this matrix and place the result in a destination matrix. + * + * @param dest The destination matrix, or null if a new matrix is to be created + * @return the negated matrix + */ + public Matrix3f negate(Matrix3f dest) { + return negate(this, dest); + } + + /** + * Negate the source matrix and place the result in the destination matrix. + * + * @param src The source matrix + * @param dest The destination matrix, or null if a new matrix is to be created + * @return the negated matrix + */ + public static Matrix3f negate(Matrix3f src, Matrix3f dest) { + if (dest == null) + dest = new Matrix3f(); + + dest.m00 = -src.m00; + dest.m01 = -src.m02; + dest.m02 = -src.m01; + dest.m10 = -src.m10; + dest.m11 = -src.m12; + dest.m12 = -src.m11; + dest.m20 = -src.m20; + dest.m21 = -src.m22; + dest.m22 = -src.m21; + return dest; + } + + /** + * Set this matrix to be the identity matrix. + * + * @return this + */ + public Matrix setIdentity() { + return setIdentity(this); + } + + /** + * Set the matrix to be the identity matrix. + * + * @param m The matrix to be set to the identity + * @return m + */ + public static Matrix3f setIdentity(Matrix3f m) { + m.m00 = 1.0f; + m.m01 = 0.0f; + m.m02 = 0.0f; + m.m10 = 0.0f; + m.m11 = 1.0f; + m.m12 = 0.0f; + m.m20 = 0.0f; + m.m21 = 0.0f; + m.m22 = 1.0f; + return m; + } + + /** + * Set this matrix to 0. + * + * @return this + */ + public Matrix setZero() { + return setZero(this); + } + + /** + * Set the matrix matrix to 0. + * + * @param m The matrix to be set to 0 + * @return m + */ + public static Matrix3f setZero(Matrix3f m) { + m.m00 = 0.0f; + m.m01 = 0.0f; + m.m02 = 0.0f; + m.m10 = 0.0f; + m.m11 = 0.0f; + m.m12 = 0.0f; + m.m20 = 0.0f; + m.m21 = 0.0f; + m.m22 = 0.0f; + return m; + } + + public boolean equals(Object m) { + return (m instanceof Matrix3f) && equal(this, (Matrix3f) m); + } + + public static boolean equal(Matrix3f a, Matrix3f b) { + return a.m00 == b.m00 && a.m01 == b.m01 && a.m02 == b.m02 && a.m10 == b.m10 && a.m11 == b.m11 && a.m12 == b.m12 + && a.m20 == b.m20 && a.m21 == b.m21 && a.m22 == b.m22; + } +} diff --git a/src/net/PeytonPlayz585/glemu/vector/Matrix4f.java b/src/net/PeytonPlayz585/glemu/vector/Matrix4f.java new file mode 100644 index 0000000..cddcf62 --- /dev/null +++ b/src/net/PeytonPlayz585/glemu/vector/Matrix4f.java @@ -0,0 +1,923 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.PeytonPlayz585.glemu.vector; + +import java.io.Serializable; +import java.nio.FloatBuffer; + +/** + * Holds a 4x4 float matrix. + * + * @author foo + */ +public class Matrix4f extends Matrix implements Serializable { + private static final long serialVersionUID = 1L; + + public float m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, m30, m31, m32, m33; + + /** + * Construct a new matrix, initialized to the identity. + */ + public Matrix4f() { + super(); + setIdentity(); + } + + public Matrix4f(final Matrix4f src) { + super(); + load(src); + } + + /** + * Returns a string representation of this matrix + */ + public String toString() { + StringBuilder buf = new StringBuilder(); + buf.append(m00).append(' ').append(m10).append(' ').append(m20).append(' ').append(m30).append('\n'); + buf.append(m01).append(' ').append(m11).append(' ').append(m21).append(' ').append(m31).append('\n'); + buf.append(m02).append(' ').append(m12).append(' ').append(m22).append(' ').append(m32).append('\n'); + buf.append(m03).append(' ').append(m13).append(' ').append(m23).append(' ').append(m33).append('\n'); + return buf.toString(); + } + + /** + * Set this matrix to be the identity matrix. + * + * @return this + */ + public Matrix setIdentity() { + return setIdentity(this); + } + + /** + * Set the given matrix to be the identity matrix. + * + * @param m The matrix to set to the identity + * @return m + */ + public static Matrix4f setIdentity(Matrix4f m) { + m.m00 = 1.0f; + m.m01 = 0.0f; + m.m02 = 0.0f; + m.m03 = 0.0f; + m.m10 = 0.0f; + m.m11 = 1.0f; + m.m12 = 0.0f; + m.m13 = 0.0f; + m.m20 = 0.0f; + m.m21 = 0.0f; + m.m22 = 1.0f; + m.m23 = 0.0f; + m.m30 = 0.0f; + m.m31 = 0.0f; + m.m32 = 0.0f; + m.m33 = 1.0f; + + return m; + } + + /** + * Set this matrix to 0. + * + * @return this + */ + public Matrix setZero() { + return setZero(this); + } + + /** + * Set the given matrix to 0. + * + * @param m The matrix to set to 0 + * @return m + */ + public static Matrix4f setZero(Matrix4f m) { + m.m00 = 0.0f; + m.m01 = 0.0f; + m.m02 = 0.0f; + m.m03 = 0.0f; + m.m10 = 0.0f; + m.m11 = 0.0f; + m.m12 = 0.0f; + m.m13 = 0.0f; + m.m20 = 0.0f; + m.m21 = 0.0f; + m.m22 = 0.0f; + m.m23 = 0.0f; + m.m30 = 0.0f; + m.m31 = 0.0f; + m.m32 = 0.0f; + m.m33 = 0.0f; + + return m; + } + + /** + * Load from another matrix4f + * + * @param src The source matrix + * @return this + */ + public Matrix4f load(Matrix4f src) { + return load(src, this); + } + + /** + * Copy the source matrix to the destination matrix + * + * @param src The source matrix + * @param dest The destination matrix, or null of a new one is to be created + * @return The copied matrix + */ + public static Matrix4f load(Matrix4f src, Matrix4f dest) { + if (dest == null) + dest = new Matrix4f(); + dest.m00 = src.m00; + dest.m01 = src.m01; + dest.m02 = src.m02; + dest.m03 = src.m03; + dest.m10 = src.m10; + dest.m11 = src.m11; + dest.m12 = src.m12; + dest.m13 = src.m13; + dest.m20 = src.m20; + dest.m21 = src.m21; + dest.m22 = src.m22; + dest.m23 = src.m23; + dest.m30 = src.m30; + dest.m31 = src.m31; + dest.m32 = src.m32; + dest.m33 = src.m33; + + return dest; + } + + /** + * Load from a float buffer. The buffer stores the matrix in column major + * (OpenGL) order. + * + * @param buf A float buffer to read from + * @return this + */ + public Matrix load(FloatBuffer buf) { + + m00 = buf.get(); + m01 = buf.get(); + m02 = buf.get(); + m03 = buf.get(); + m10 = buf.get(); + m11 = buf.get(); + m12 = buf.get(); + m13 = buf.get(); + m20 = buf.get(); + m21 = buf.get(); + m22 = buf.get(); + m23 = buf.get(); + m30 = buf.get(); + m31 = buf.get(); + m32 = buf.get(); + m33 = buf.get(); + + return this; + } + + /** + * Load from a float buffer. The buffer stores the matrix in row major (maths) + * order. + * + * @param buf A float buffer to read from + * @return this + */ + public Matrix loadTranspose(FloatBuffer buf) { + + m00 = buf.get(); + m10 = buf.get(); + m20 = buf.get(); + m30 = buf.get(); + m01 = buf.get(); + m11 = buf.get(); + m21 = buf.get(); + m31 = buf.get(); + m02 = buf.get(); + m12 = buf.get(); + m22 = buf.get(); + m32 = buf.get(); + m03 = buf.get(); + m13 = buf.get(); + m23 = buf.get(); + m33 = buf.get(); + + return this; + } + + /** + * Store this matrix in a float buffer. The matrix is stored in column major + * (openGL) order. + * + * @param buf The buffer to store this matrix in + */ + public Matrix store(FloatBuffer buf) { + buf.put(m00); + buf.put(m01); + buf.put(m02); + buf.put(m03); + buf.put(m10); + buf.put(m11); + buf.put(m12); + buf.put(m13); + buf.put(m20); + buf.put(m21); + buf.put(m22); + buf.put(m23); + buf.put(m30); + buf.put(m31); + buf.put(m32); + buf.put(m33); + return this; + } + + public Matrix store(float[] buf) { + buf[0] = m00; + buf[1] = m01; + buf[2] = m02; + buf[3] = m03; + buf[4] = m10; + buf[5] = m11; + buf[6] = m12; + buf[7] = m13; + buf[8] = m20; + buf[9] = m21; + buf[10] = m22; + buf[11] = m23; + buf[12] = m30; + buf[13] = m31; + buf[14] = m32; + buf[15] = m33; + return this; + } + + public float[] get() { + float[] buf = new float[15]; + buf[0] = m00; + buf[1] = m01; + buf[2] = m02; + buf[3] = m03; + buf[4] = m10; + buf[5] = m11; + buf[6] = m12; + buf[7] = m13; + buf[8] = m20; + buf[9] = m21; + buf[10] = m22; + buf[11] = m23; + buf[12] = m30; + buf[13] = m31; + buf[14] = m32; + buf[15] = m33; + return buf; + } + + /** + * Store this matrix in a float buffer. The matrix is stored in row major + * (maths) order. + * + * @param buf The buffer to store this matrix in + */ + public Matrix storeTranspose(FloatBuffer buf) { + buf.put(m00); + buf.put(m10); + buf.put(m20); + buf.put(m30); + buf.put(m01); + buf.put(m11); + buf.put(m21); + buf.put(m31); + buf.put(m02); + buf.put(m12); + buf.put(m22); + buf.put(m32); + buf.put(m03); + buf.put(m13); + buf.put(m23); + buf.put(m33); + return this; + } + + /** + * Store the rotation portion of this matrix in a float buffer. The matrix is + * stored in column major (openGL) order. + * + * @param buf The buffer to store this matrix in + */ + public Matrix store3f(FloatBuffer buf) { + buf.put(m00); + buf.put(m01); + buf.put(m02); + buf.put(m10); + buf.put(m11); + buf.put(m12); + buf.put(m20); + buf.put(m21); + buf.put(m22); + return this; + } + + /** + * Add two matrices together and place the result in a third matrix. + * + * @param left The left source matrix + * @param right The right source matrix + * @param dest The destination matrix, or null if a new one is to be created + * @return the destination matrix + */ + public static Matrix4f add(Matrix4f left, Matrix4f right, Matrix4f dest) { + if (dest == null) + dest = new Matrix4f(); + + dest.m00 = left.m00 + right.m00; + dest.m01 = left.m01 + right.m01; + dest.m02 = left.m02 + right.m02; + dest.m03 = left.m03 + right.m03; + dest.m10 = left.m10 + right.m10; + dest.m11 = left.m11 + right.m11; + dest.m12 = left.m12 + right.m12; + dest.m13 = left.m13 + right.m13; + dest.m20 = left.m20 + right.m20; + dest.m21 = left.m21 + right.m21; + dest.m22 = left.m22 + right.m22; + dest.m23 = left.m23 + right.m23; + dest.m30 = left.m30 + right.m30; + dest.m31 = left.m31 + right.m31; + dest.m32 = left.m32 + right.m32; + dest.m33 = left.m33 + right.m33; + + return dest; + } + + /** + * Subtract the right matrix from the left and place the result in a third + * matrix. + * + * @param left The left source matrix + * @param right The right source matrix + * @param dest The destination matrix, or null if a new one is to be created + * @return the destination matrix + */ + public static Matrix4f sub(Matrix4f left, Matrix4f right, Matrix4f dest) { + if (dest == null) + dest = new Matrix4f(); + + dest.m00 = left.m00 - right.m00; + dest.m01 = left.m01 - right.m01; + dest.m02 = left.m02 - right.m02; + dest.m03 = left.m03 - right.m03; + dest.m10 = left.m10 - right.m10; + dest.m11 = left.m11 - right.m11; + dest.m12 = left.m12 - right.m12; + dest.m13 = left.m13 - right.m13; + dest.m20 = left.m20 - right.m20; + dest.m21 = left.m21 - right.m21; + dest.m22 = left.m22 - right.m22; + dest.m23 = left.m23 - right.m23; + dest.m30 = left.m30 - right.m30; + dest.m31 = left.m31 - right.m31; + dest.m32 = left.m32 - right.m32; + dest.m33 = left.m33 - right.m33; + + return dest; + } + + /** + * Multiply the right matrix by the left and place the result in a third matrix. + * + * @param left The left source matrix + * @param right The right source matrix + * @param dest The destination matrix, or null if a new one is to be created + * @return the destination matrix + */ + public static Matrix4f mul(Matrix4f left, Matrix4f right, Matrix4f dest) { + if (dest == null) + dest = new Matrix4f(); + + float m00 = left.m00 * right.m00 + left.m10 * right.m01 + left.m20 * right.m02 + left.m30 * right.m03; + float m01 = left.m01 * right.m00 + left.m11 * right.m01 + left.m21 * right.m02 + left.m31 * right.m03; + float m02 = left.m02 * right.m00 + left.m12 * right.m01 + left.m22 * right.m02 + left.m32 * right.m03; + float m03 = left.m03 * right.m00 + left.m13 * right.m01 + left.m23 * right.m02 + left.m33 * right.m03; + float m10 = left.m00 * right.m10 + left.m10 * right.m11 + left.m20 * right.m12 + left.m30 * right.m13; + float m11 = left.m01 * right.m10 + left.m11 * right.m11 + left.m21 * right.m12 + left.m31 * right.m13; + float m12 = left.m02 * right.m10 + left.m12 * right.m11 + left.m22 * right.m12 + left.m32 * right.m13; + float m13 = left.m03 * right.m10 + left.m13 * right.m11 + left.m23 * right.m12 + left.m33 * right.m13; + float m20 = left.m00 * right.m20 + left.m10 * right.m21 + left.m20 * right.m22 + left.m30 * right.m23; + float m21 = left.m01 * right.m20 + left.m11 * right.m21 + left.m21 * right.m22 + left.m31 * right.m23; + float m22 = left.m02 * right.m20 + left.m12 * right.m21 + left.m22 * right.m22 + left.m32 * right.m23; + float m23 = left.m03 * right.m20 + left.m13 * right.m21 + left.m23 * right.m22 + left.m33 * right.m23; + float m30 = left.m00 * right.m30 + left.m10 * right.m31 + left.m20 * right.m32 + left.m30 * right.m33; + float m31 = left.m01 * right.m30 + left.m11 * right.m31 + left.m21 * right.m32 + left.m31 * right.m33; + float m32 = left.m02 * right.m30 + left.m12 * right.m31 + left.m22 * right.m32 + left.m32 * right.m33; + float m33 = left.m03 * right.m30 + left.m13 * right.m31 + left.m23 * right.m32 + left.m33 * right.m33; + + dest.m00 = m00; + dest.m01 = m01; + dest.m02 = m02; + dest.m03 = m03; + dest.m10 = m10; + dest.m11 = m11; + dest.m12 = m12; + dest.m13 = m13; + dest.m20 = m20; + dest.m21 = m21; + dest.m22 = m22; + dest.m23 = m23; + dest.m30 = m30; + dest.m31 = m31; + dest.m32 = m32; + dest.m33 = m33; + + return dest; + } + + /** + * Transform a Vector by a matrix and return the result in a destination vector. + * + * @param left The left matrix + * @param right The right vector + * @param dest The destination vector, or null if a new one is to be created + * @return the destination vector + */ + public static Vector4f transform(Matrix4f left, Vector4f right, Vector4f dest) { + if (dest == null) + dest = new Vector4f(); + + float x = left.m00 * right.x + left.m10 * right.y + left.m20 * right.z + left.m30 * right.w; + float y = left.m01 * right.x + left.m11 * right.y + left.m21 * right.z + left.m31 * right.w; + float z = left.m02 * right.x + left.m12 * right.y + left.m22 * right.z + left.m32 * right.w; + float w = left.m03 * right.x + left.m13 * right.y + left.m23 * right.z + left.m33 * right.w; + + dest.x = x; + dest.y = y; + dest.z = z; + dest.w = w; + + return dest; + } + + /** + * Transpose this matrix + * + * @return this + */ + public Matrix transpose() { + return transpose(this); + } + + /** + * Translate this matrix + * + * @param vec The vector to translate by + * @return this + */ + public Matrix4f translate(Vector2f vec) { + return translate(vec, this); + } + + /** + * Translate this matrix + * + * @param vec The vector to translate by + * @return this + */ + public Matrix4f translate(Vector3f vec) { + return translate(vec, this); + } + + /** + * Scales this matrix + * + * @param vec The vector to scale by + * @return this + */ + public Matrix4f scale(Vector3f vec) { + return scale(vec, this, this); + } + + /** + * Scales the source matrix and put the result in the destination matrix + * + * @param vec The vector to scale by + * @param src The source matrix + * @param dest The destination matrix, or null if a new matrix is to be created + * @return The scaled matrix + */ + public static Matrix4f scale(Vector3f vec, Matrix4f src, Matrix4f dest) { + if (dest == null) + dest = new Matrix4f(); + dest.m00 = src.m00 * vec.x; + dest.m01 = src.m01 * vec.x; + dest.m02 = src.m02 * vec.x; + dest.m03 = src.m03 * vec.x; + dest.m10 = src.m10 * vec.y; + dest.m11 = src.m11 * vec.y; + dest.m12 = src.m12 * vec.y; + dest.m13 = src.m13 * vec.y; + dest.m20 = src.m20 * vec.z; + dest.m21 = src.m21 * vec.z; + dest.m22 = src.m22 * vec.z; + dest.m23 = src.m23 * vec.z; + return dest; + } + + /** + * Rotates the matrix around the given axis the specified angle + * + * @param angle the angle, in radians. + * @param axis The vector representing the rotation axis. Must be normalized. + * @return this + */ + public Matrix4f rotate(float angle, Vector3f axis) { + return rotate(angle, axis, this); + } + + /** + * Rotates the matrix around the given axis the specified angle + * + * @param angle the angle, in radians. + * @param axis The vector representing the rotation axis. Must be normalized. + * @param dest The matrix to put the result, or null if a new matrix is to be + * created + * @return The rotated matrix + */ + public Matrix4f rotate(float angle, Vector3f axis, Matrix4f dest) { + return rotate(angle, axis, this, dest); + } + + /** + * Rotates the source matrix around the given axis the specified angle and put + * the result in the destination matrix. + * + * @param angle the angle, in radians. + * @param axis The vector representing the rotation axis. Must be normalized. + * @param src The matrix to rotate + * @param dest The matrix to put the result, or null if a new matrix is to be + * created + * @return The rotated matrix + */ + public static Matrix4f rotate(float angle, Vector3f axis, Matrix4f src, Matrix4f dest) { + if (dest == null) + dest = new Matrix4f(); + float c = (float) Math.cos(angle); + float s = (float) Math.sin(angle); + float oneminusc = 1.0f - c; + float xy = axis.x * axis.y; + float yz = axis.y * axis.z; + float xz = axis.x * axis.z; + float xs = axis.x * s; + float ys = axis.y * s; + float zs = axis.z * s; + + float f00 = axis.x * axis.x * oneminusc + c; + float f01 = xy * oneminusc + zs; + float f02 = xz * oneminusc - ys; + // n[3] not used + float f10 = xy * oneminusc - zs; + float f11 = axis.y * axis.y * oneminusc + c; + float f12 = yz * oneminusc + xs; + // n[7] not used + float f20 = xz * oneminusc + ys; + float f21 = yz * oneminusc - xs; + float f22 = axis.z * axis.z * oneminusc + c; + + float t00 = src.m00 * f00 + src.m10 * f01 + src.m20 * f02; + float t01 = src.m01 * f00 + src.m11 * f01 + src.m21 * f02; + float t02 = src.m02 * f00 + src.m12 * f01 + src.m22 * f02; + float t03 = src.m03 * f00 + src.m13 * f01 + src.m23 * f02; + float t10 = src.m00 * f10 + src.m10 * f11 + src.m20 * f12; + float t11 = src.m01 * f10 + src.m11 * f11 + src.m21 * f12; + float t12 = src.m02 * f10 + src.m12 * f11 + src.m22 * f12; + float t13 = src.m03 * f10 + src.m13 * f11 + src.m23 * f12; + dest.m20 = src.m00 * f20 + src.m10 * f21 + src.m20 * f22; + dest.m21 = src.m01 * f20 + src.m11 * f21 + src.m21 * f22; + dest.m22 = src.m02 * f20 + src.m12 * f21 + src.m22 * f22; + dest.m23 = src.m03 * f20 + src.m13 * f21 + src.m23 * f22; + dest.m00 = t00; + dest.m01 = t01; + dest.m02 = t02; + dest.m03 = t03; + dest.m10 = t10; + dest.m11 = t11; + dest.m12 = t12; + dest.m13 = t13; + return dest; + } + + /** + * Translate this matrix and stash the result in another matrix + * + * @param vec The vector to translate by + * @param dest The destination matrix or null if a new matrix is to be created + * @return the translated matrix + */ + public Matrix4f translate(Vector3f vec, Matrix4f dest) { + return translate(vec, this, dest); + } + + /** + * Translate the source matrix and stash the result in the destination matrix + * + * @param vec The vector to translate by + * @param src The source matrix + * @param dest The destination matrix or null if a new matrix is to be created + * @return The translated matrix + */ + public static Matrix4f translate(Vector3f vec, Matrix4f src, Matrix4f dest) { + if (dest == null) + dest = new Matrix4f(); + + dest.m30 += src.m00 * vec.x + src.m10 * vec.y + src.m20 * vec.z; + dest.m31 += src.m01 * vec.x + src.m11 * vec.y + src.m21 * vec.z; + dest.m32 += src.m02 * vec.x + src.m12 * vec.y + src.m22 * vec.z; + dest.m33 += src.m03 * vec.x + src.m13 * vec.y + src.m23 * vec.z; + + return dest; + } + + /** + * Translate this matrix and stash the result in another matrix + * + * @param vec The vector to translate by + * @param dest The destination matrix or null if a new matrix is to be created + * @return the translated matrix + */ + public Matrix4f translate(Vector2f vec, Matrix4f dest) { + return translate(vec, this, dest); + } + + /** + * Translate the source matrix and stash the result in the destination matrix + * + * @param vec The vector to translate by + * @param src The source matrix + * @param dest The destination matrix or null if a new matrix is to be created + * @return The translated matrix + */ + public static Matrix4f translate(Vector2f vec, Matrix4f src, Matrix4f dest) { + if (dest == null) + dest = new Matrix4f(); + + dest.m30 += src.m00 * vec.x + src.m10 * vec.y; + dest.m31 += src.m01 * vec.x + src.m11 * vec.y; + dest.m32 += src.m02 * vec.x + src.m12 * vec.y; + dest.m33 += src.m03 * vec.x + src.m13 * vec.y; + + return dest; + } + + /** + * Transpose this matrix and place the result in another matrix + * + * @param dest The destination matrix or null if a new matrix is to be created + * @return the transposed matrix + */ + public Matrix4f transpose(Matrix4f dest) { + return transpose(this, dest); + } + + /** + * Transpose the source matrix and place the result in the destination matrix + * + * @param src The source matrix + * @param dest The destination matrix or null if a new matrix is to be created + * @return the transposed matrix + */ + public static Matrix4f transpose(Matrix4f src, Matrix4f dest) { + if (dest == null) + dest = new Matrix4f(); + float m00 = src.m00; + float m01 = src.m10; + float m02 = src.m20; + float m03 = src.m30; + float m10 = src.m01; + float m11 = src.m11; + float m12 = src.m21; + float m13 = src.m31; + float m20 = src.m02; + float m21 = src.m12; + float m22 = src.m22; + float m23 = src.m32; + float m30 = src.m03; + float m31 = src.m13; + float m32 = src.m23; + float m33 = src.m33; + + dest.m00 = m00; + dest.m01 = m01; + dest.m02 = m02; + dest.m03 = m03; + dest.m10 = m10; + dest.m11 = m11; + dest.m12 = m12; + dest.m13 = m13; + dest.m20 = m20; + dest.m21 = m21; + dest.m22 = m22; + dest.m23 = m23; + dest.m30 = m30; + dest.m31 = m31; + dest.m32 = m32; + dest.m33 = m33; + + return dest; + } + + /** + * @return the determinant of the matrix + */ + public float determinant() { + float f = m00 * ((m11 * m22 * m33 + m12 * m23 * m31 + m13 * m21 * m32) - m13 * m22 * m31 - m11 * m23 * m32 + - m12 * m21 * m33); + f -= m01 * ((m10 * m22 * m33 + m12 * m23 * m30 + m13 * m20 * m32) - m13 * m22 * m30 - m10 * m23 * m32 + - m12 * m20 * m33); + f += m02 * ((m10 * m21 * m33 + m11 * m23 * m30 + m13 * m20 * m31) - m13 * m21 * m30 - m10 * m23 * m31 + - m11 * m20 * m33); + f -= m03 * ((m10 * m21 * m32 + m11 * m22 * m30 + m12 * m20 * m31) - m12 * m21 * m30 - m10 * m22 * m31 + - m11 * m20 * m32); + return f; + } + + /** + * Calculate the determinant of a 3x3 matrix + * + * @return result + */ + + private static float determinant3x3(float t00, float t01, float t02, float t10, float t11, float t12, float t20, + float t21, float t22) { + return t00 * (t11 * t22 - t12 * t21) + t01 * (t12 * t20 - t10 * t22) + t02 * (t10 * t21 - t11 * t20); + } + + /** + * Invert this matrix + * + * @return this if successful, null otherwise + */ + public Matrix invert() { + return invert(this, this); + } + + /** + * Invert the source matrix and put the result in the destination + * + * @param src The source matrix + * @param dest The destination matrix, or null if a new matrix is to be created + * @return The inverted matrix if successful, null otherwise + */ + public static Matrix4f invert(Matrix4f src, Matrix4f dest) { + float determinant = src.determinant(); + + if (determinant != 0) { + /* + * m00 m01 m02 m03 m10 m11 m12 m13 m20 m21 m22 m23 m30 m31 m32 m33 + */ + if (dest == null) + dest = new Matrix4f(); + float determinant_inv = 1f / determinant; + + // first row + float t00 = determinant3x3(src.m11, src.m12, src.m13, src.m21, src.m22, src.m23, src.m31, src.m32, src.m33); + float t01 = -determinant3x3(src.m10, src.m12, src.m13, src.m20, src.m22, src.m23, src.m30, src.m32, + src.m33); + float t02 = determinant3x3(src.m10, src.m11, src.m13, src.m20, src.m21, src.m23, src.m30, src.m31, src.m33); + float t03 = -determinant3x3(src.m10, src.m11, src.m12, src.m20, src.m21, src.m22, src.m30, src.m31, + src.m32); + // second row + float t10 = -determinant3x3(src.m01, src.m02, src.m03, src.m21, src.m22, src.m23, src.m31, src.m32, + src.m33); + float t11 = determinant3x3(src.m00, src.m02, src.m03, src.m20, src.m22, src.m23, src.m30, src.m32, src.m33); + float t12 = -determinant3x3(src.m00, src.m01, src.m03, src.m20, src.m21, src.m23, src.m30, src.m31, + src.m33); + float t13 = determinant3x3(src.m00, src.m01, src.m02, src.m20, src.m21, src.m22, src.m30, src.m31, src.m32); + // third row + float t20 = determinant3x3(src.m01, src.m02, src.m03, src.m11, src.m12, src.m13, src.m31, src.m32, src.m33); + float t21 = -determinant3x3(src.m00, src.m02, src.m03, src.m10, src.m12, src.m13, src.m30, src.m32, + src.m33); + float t22 = determinant3x3(src.m00, src.m01, src.m03, src.m10, src.m11, src.m13, src.m30, src.m31, src.m33); + float t23 = -determinant3x3(src.m00, src.m01, src.m02, src.m10, src.m11, src.m12, src.m30, src.m31, + src.m32); + // fourth row + float t30 = -determinant3x3(src.m01, src.m02, src.m03, src.m11, src.m12, src.m13, src.m21, src.m22, + src.m23); + float t31 = determinant3x3(src.m00, src.m02, src.m03, src.m10, src.m12, src.m13, src.m20, src.m22, src.m23); + float t32 = -determinant3x3(src.m00, src.m01, src.m03, src.m10, src.m11, src.m13, src.m20, src.m21, + src.m23); + float t33 = determinant3x3(src.m00, src.m01, src.m02, src.m10, src.m11, src.m12, src.m20, src.m21, src.m22); + + // transpose and divide by the determinant + dest.m00 = t00 * determinant_inv; + dest.m11 = t11 * determinant_inv; + dest.m22 = t22 * determinant_inv; + dest.m33 = t33 * determinant_inv; + dest.m01 = t10 * determinant_inv; + dest.m10 = t01 * determinant_inv; + dest.m20 = t02 * determinant_inv; + dest.m02 = t20 * determinant_inv; + dest.m12 = t21 * determinant_inv; + dest.m21 = t12 * determinant_inv; + dest.m03 = t30 * determinant_inv; + dest.m30 = t03 * determinant_inv; + dest.m13 = t31 * determinant_inv; + dest.m31 = t13 * determinant_inv; + dest.m32 = t23 * determinant_inv; + dest.m23 = t32 * determinant_inv; + return dest; + } else + return null; + } + + /** + * Negate this matrix + * + * @return this + */ + public Matrix negate() { + return negate(this); + } + + /** + * Negate this matrix and place the result in a destination matrix. + * + * @param dest The destination matrix, or null if a new matrix is to be created + * @return the negated matrix + */ + public Matrix4f negate(Matrix4f dest) { + return negate(this, dest); + } + + /** + * Negate this matrix and place the result in a destination matrix. + * + * @param src The source matrix + * @param dest The destination matrix, or null if a new matrix is to be created + * @return The negated matrix + */ + public static Matrix4f negate(Matrix4f src, Matrix4f dest) { + if (dest == null) + dest = new Matrix4f(); + + dest.m00 = -src.m00; + dest.m01 = -src.m01; + dest.m02 = -src.m02; + dest.m03 = -src.m03; + dest.m10 = -src.m10; + dest.m11 = -src.m11; + dest.m12 = -src.m12; + dest.m13 = -src.m13; + dest.m20 = -src.m20; + dest.m21 = -src.m21; + dest.m22 = -src.m22; + dest.m23 = -src.m23; + dest.m30 = -src.m30; + dest.m31 = -src.m31; + dest.m32 = -src.m32; + dest.m33 = -src.m33; + + return dest; + } + + public boolean equals(Object m) { + return (m instanceof Matrix4f) && equal(this, (Matrix4f) m); + } + + public static boolean equal(Matrix4f a, Matrix4f b) { + return a.m00 == b.m00 && a.m01 == b.m01 && a.m02 == b.m02 && a.m03 == b.m03 && a.m10 == b.m10 && a.m11 == b.m11 + && a.m12 == b.m12 && a.m13 == b.m13 && a.m20 == b.m20 && a.m21 == b.m21 && a.m22 == b.m22 + && a.m23 == b.m23 && a.m30 == b.m30 && a.m31 == b.m31 && a.m32 == b.m32 && a.m33 == b.m33; + } +} diff --git a/src/net/PeytonPlayz585/glemu/vector/Quaternion.java b/src/net/PeytonPlayz585/glemu/vector/Quaternion.java new file mode 100644 index 0000000..663448f --- /dev/null +++ b/src/net/PeytonPlayz585/glemu/vector/Quaternion.java @@ -0,0 +1,506 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.PeytonPlayz585.glemu.vector; + +/** + * + * Quaternions for LWJGL! + * + * @author fbi + * @version $Revision$ + * $Id$ + */ + +import java.nio.FloatBuffer; + +public class Quaternion extends Vector implements ReadableVector4f { + private static final long serialVersionUID = 1L; + + public float x, y, z, w; + + /** + * C'tor. The quaternion will be initialized to the identity. + */ + public Quaternion() { + super(); + setIdentity(); + } + + /** + * C'tor + * + * @param src + */ + public Quaternion(ReadableVector4f src) { + set(src); + } + + /** + * C'tor + * + */ + public Quaternion(float x, float y, float z, float w) { + set(x, y, z, w); + } + + /* + * (non-Javadoc) + * + * @see org.lwjgl.util.vector.WritableVector2f#set(float, float) + */ + public void set(float x, float y) { + this.x = x; + this.y = y; + } + + /* + * (non-Javadoc) + * + * @see org.lwjgl.util.vector.WritableVector3f#set(float, float, float) + */ + public void set(float x, float y, float z) { + this.x = x; + this.y = y; + this.z = z; + } + + /* + * (non-Javadoc) + * + * @see org.lwjgl.util.vector.WritableVector4f#set(float, float, float, float) + */ + public void set(float x, float y, float z, float w) { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + /** + * Load from another Vector4f + * + * @param src The source vector + * @return this + */ + public Quaternion set(ReadableVector4f src) { + x = src.getX(); + y = src.getY(); + z = src.getZ(); + w = src.getW(); + return this; + } + + /** + * Set this quaternion to the multiplication identity. + * + * @return this + */ + public Quaternion setIdentity() { + return setIdentity(this); + } + + /** + * Set the given quaternion to the multiplication identity. + * + * @param q The quaternion + * @return q + */ + public static Quaternion setIdentity(Quaternion q) { + q.x = 0; + q.y = 0; + q.z = 0; + q.w = 1; + return q; + } + + /** + * @return the length squared of the quaternion + */ + public float lengthSquared() { + return x * x + y * y + z * z + w * w; + } + + /** + * Normalise the source quaternion and place the result in another quaternion. + * + * @param src The source quaternion + * @param dest The destination quaternion, or null if a new quaternion is to be + * created + * @return The normalised quaternion + */ + public static Quaternion normalise(Quaternion src, Quaternion dest) { + float inv_l = 1f / src.length(); + + if (dest == null) + dest = new Quaternion(); + + dest.set(src.x * inv_l, src.y * inv_l, src.z * inv_l, src.w * inv_l); + + return dest; + } + + /** + * Normalise this quaternion and place the result in another quaternion. + * + * @param dest The destination quaternion, or null if a new quaternion is to be + * created + * @return the normalised quaternion + */ + public Quaternion normalise(Quaternion dest) { + return normalise(this, dest); + } + + /** + * The dot product of two quaternions + * + * @param left The LHS quat + * @param right The RHS quat + * @return left dot right + */ + public static float dot(Quaternion left, Quaternion right) { + return left.x * right.x + left.y * right.y + left.z * right.z + left.w * right.w; + } + + /** + * Calculate the conjugate of this quaternion and put it into the given one + * + * @param dest The quaternion which should be set to the conjugate of this + * quaternion + */ + public Quaternion negate(Quaternion dest) { + return negate(this, dest); + } + + /** + * Calculate the conjugate of this quaternion and put it into the given one + * + * @param src The source quaternion + * @param dest The quaternion which should be set to the conjugate of this + * quaternion + */ + public static Quaternion negate(Quaternion src, Quaternion dest) { + if (dest == null) + dest = new Quaternion(); + + dest.x = -src.x; + dest.y = -src.y; + dest.z = -src.z; + dest.w = src.w; + + return dest; + } + + /** + * Calculate the conjugate of this quaternion + */ + public Vector negate() { + return negate(this, this); + } + + /* + * (non-Javadoc) + * + * @see org.lwjgl.util.vector.Vector#load(java.nio.FloatBuffer) + */ + public Vector load(FloatBuffer buf) { + x = buf.get(); + y = buf.get(); + z = buf.get(); + w = buf.get(); + return this; + } + + /* + * (non-Javadoc) + * + * @see org.lwjgl.vector.Vector#scale(float) + */ + public Vector scale(float scale) { + return scale(scale, this, this); + } + + /** + * Scale the source quaternion by scale and put the result in the destination + * + * @param scale The amount to scale by + * @param src The source quaternion + * @param dest The destination quaternion, or null if a new quaternion is to be + * created + * @return The scaled quaternion + */ + public static Quaternion scale(float scale, Quaternion src, Quaternion dest) { + if (dest == null) + dest = new Quaternion(); + dest.x = src.x * scale; + dest.y = src.y * scale; + dest.z = src.z * scale; + dest.w = src.w * scale; + return dest; + } + + /* + * (non-Javadoc) + * + * @see org.lwjgl.util.vector.ReadableVector#store(java.nio.FloatBuffer) + */ + public Vector store(FloatBuffer buf) { + buf.put(x); + buf.put(y); + buf.put(z); + buf.put(w); + + return this; + } + + /** + * @return x + */ + public final float getX() { + return x; + } + + /** + * @return y + */ + public final float getY() { + return y; + } + + /** + * Set X + * + * @param x + */ + public final void setX(float x) { + this.x = x; + } + + /** + * Set Y + * + * @param y + */ + public final void setY(float y) { + this.y = y; + } + + /** + * Set Z + * + * @param z + */ + public void setZ(float z) { + this.z = z; + } + + /* + * (Overrides) + * + * @see org.lwjgl.vector.ReadableVector3f#getZ() + */ + public float getZ() { + return z; + } + + /** + * Set W + * + * @param w + */ + public void setW(float w) { + this.w = w; + } + + /* + * (Overrides) + * + * @see org.lwjgl.vector.ReadableVector3f#getW() + */ + public float getW() { + return w; + } + + public String toString() { + return "Quaternion: " + x + " " + y + " " + z + " " + w; + } + + /** + * Sets the value of this quaternion to the quaternion product of quaternions + * left and right (this = left * right). Note that this is safe for aliasing + * (e.g. this can be left or right). + * + * @param left the first quaternion + * @param right the second quaternion + */ + public static Quaternion mul(Quaternion left, Quaternion right, Quaternion dest) { + if (dest == null) + dest = new Quaternion(); + dest.set(left.x * right.w + left.w * right.x + left.y * right.z - left.z * right.y, + left.y * right.w + left.w * right.y + left.z * right.x - left.x * right.z, + left.z * right.w + left.w * right.z + left.x * right.y - left.y * right.x, + left.w * right.w - left.x * right.x - left.y * right.y - left.z * right.z); + return dest; + } + + /** + * + * Multiplies quaternion left by the inverse of quaternion right and places the + * value into this quaternion. The value of both argument quaternions is + * preservered (this = left * right^-1). + * + * @param left the left quaternion + * @param right the right quaternion + */ + public static Quaternion mulInverse(Quaternion left, Quaternion right, Quaternion dest) { + float n = right.lengthSquared(); + // zero-div may occur. + n = (n == 0.0 ? n : 1 / n); + // store on stack once for aliasing-safty + if (dest == null) + dest = new Quaternion(); + dest.set((left.x * right.w - left.w * right.x - left.y * right.z + left.z * right.y) * n, + (left.y * right.w - left.w * right.y - left.z * right.x + left.x * right.z) * n, + (left.z * right.w - left.w * right.z - left.x * right.y + left.y * right.x) * n, + (left.w * right.w + left.x * right.x + left.y * right.y + left.z * right.z) * n); + + return dest; + } + + /** + * Sets the value of this quaternion to the equivalent rotation of the + * Axis-Angle argument. + * + * @param a1 the axis-angle: (x,y,z) is the axis and w is the angle + */ + public final void setFromAxisAngle(Vector4f a1) { + x = a1.x; + y = a1.y; + z = a1.z; + float n = (float) Math.sqrt(x * x + y * y + z * z); + // zero-div may occur. + float s = (float) (Math.sin(0.5 * a1.w) / n); + x *= s; + y *= s; + z *= s; + w = (float) Math.cos(0.5 * a1.w); + } + + /** + * Sets the value of this quaternion using the rotational component of the + * passed matrix. + * + * @param m The matrix + * @return this + */ + public final Quaternion setFromMatrix(Matrix4f m) { + return setFromMatrix(m, this); + } + + /** + * Sets the value of the source quaternion using the rotational component of the + * passed matrix. + * + * @param m The source matrix + * @param q The destination quaternion, or null if a new quaternion is to be + * created + * @return q + */ + public static Quaternion setFromMatrix(Matrix4f m, Quaternion q) { + return q.setFromMat(m.m00, m.m01, m.m02, m.m10, m.m11, m.m12, m.m20, m.m21, m.m22); + } + + /** + * Sets the value of this quaternion using the rotational component of the + * passed matrix. + * + * @param m The source matrix + */ + public final Quaternion setFromMatrix(Matrix3f m) { + return setFromMatrix(m, this); + } + + /** + * Sets the value of the source quaternion using the rotational component of the + * passed matrix. + * + * @param m The source matrix + * @param q The destination quaternion, or null if a new quaternion is to be + * created + * @return q + */ + public static Quaternion setFromMatrix(Matrix3f m, Quaternion q) { + return q.setFromMat(m.m00, m.m01, m.m02, m.m10, m.m11, m.m12, m.m20, m.m21, m.m22); + } + + /** + * Private method to perform the matrix-to-quaternion conversion + */ + private Quaternion setFromMat(float m00, float m01, float m02, float m10, float m11, float m12, float m20, + float m21, float m22) { + + float s; + float tr = m00 + m11 + m22; + if (tr >= 0.0) { + s = (float) Math.sqrt(tr + 1.0); + w = s * 0.5f; + s = 0.5f / s; + x = (m21 - m12) * s; + y = (m02 - m20) * s; + z = (m10 - m01) * s; + } else { + float max = Math.max(Math.max(m00, m11), m22); + if (max == m00) { + s = (float) Math.sqrt(m00 - (m11 + m22) + 1.0); + x = s * 0.5f; + s = 0.5f / s; + y = (m01 + m10) * s; + z = (m20 + m02) * s; + w = (m21 - m12) * s; + } else if (max == m11) { + s = (float) Math.sqrt(m11 - (m22 + m00) + 1.0); + y = s * 0.5f; + s = 0.5f / s; + z = (m12 + m21) * s; + x = (m01 + m10) * s; + w = (m02 - m20) * s; + } else { + s = (float) Math.sqrt(m22 - (m00 + m11) + 1.0); + z = s * 0.5f; + s = 0.5f / s; + x = (m20 + m02) * s; + y = (m12 + m21) * s; + w = (m10 - m01) * s; + } + } + return this; + } +} diff --git a/src/net/PeytonPlayz585/glemu/vector/ReadableVector.java b/src/net/PeytonPlayz585/glemu/vector/ReadableVector.java new file mode 100644 index 0000000..e72d312 --- /dev/null +++ b/src/net/PeytonPlayz585/glemu/vector/ReadableVector.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.PeytonPlayz585.glemu.vector; + +import java.nio.FloatBuffer; + +/** + * @author foo + */ +public interface ReadableVector { + /** + * @return the length of the vector + */ + float length(); + + /** + * @return the length squared of the vector + */ + float lengthSquared(); + + /** + * Store this vector in a FloatBuffer + * + * @param buf The buffer to store it in, at the current position + * @return this + */ + Vector store(FloatBuffer buf); +} \ No newline at end of file diff --git a/src/net/PeytonPlayz585/glemu/vector/ReadableVector2f.java b/src/net/PeytonPlayz585/glemu/vector/ReadableVector2f.java new file mode 100644 index 0000000..f857c6f --- /dev/null +++ b/src/net/PeytonPlayz585/glemu/vector/ReadableVector2f.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.PeytonPlayz585.glemu.vector; + +/** + * @author foo + */ +public interface ReadableVector2f extends ReadableVector { + /** + * @return x + */ + float getX(); + + /** + * @return y + */ + float getY(); +} \ No newline at end of file diff --git a/src/net/PeytonPlayz585/glemu/vector/ReadableVector3f.java b/src/net/PeytonPlayz585/glemu/vector/ReadableVector3f.java new file mode 100644 index 0000000..6eae2ec --- /dev/null +++ b/src/net/PeytonPlayz585/glemu/vector/ReadableVector3f.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.PeytonPlayz585.glemu.vector; + +/** + * @author foo + */ +public interface ReadableVector3f extends ReadableVector2f { + /** + * @return z + */ + float getZ(); +} \ No newline at end of file diff --git a/src/net/PeytonPlayz585/glemu/vector/ReadableVector4f.java b/src/net/PeytonPlayz585/glemu/vector/ReadableVector4f.java new file mode 100644 index 0000000..feedc20 --- /dev/null +++ b/src/net/PeytonPlayz585/glemu/vector/ReadableVector4f.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.PeytonPlayz585.glemu.vector; + +/** + * @author foo + */ +public interface ReadableVector4f extends ReadableVector3f { + + /** + * @return w + */ + float getW(); + +} diff --git a/src/net/PeytonPlayz585/glemu/vector/Vector.java b/src/net/PeytonPlayz585/glemu/vector/Vector.java new file mode 100644 index 0000000..61dfb43 --- /dev/null +++ b/src/net/PeytonPlayz585/glemu/vector/Vector.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.PeytonPlayz585.glemu.vector; + +import java.io.Serializable; +import java.nio.FloatBuffer; + +/** + * + * Base class for vectors. + * + * @author cix_foo + * @version $Revision$ $Id$ + */ +public abstract class Vector implements Serializable, ReadableVector { + + /** + * Constructor for Vector. + */ + protected Vector() { + super(); + } + + /** + * @return the length of the vector + */ + public final float length() { + return (float) Math.sqrt(lengthSquared()); + } + + /** + * @return the length squared of the vector + */ + public abstract float lengthSquared(); + + /** + * Load this vector from a FloatBuffer + * + * @param buf The buffer to load it from, at the current position + * @return this + */ + public abstract Vector load(FloatBuffer buf); + + /** + * Negate a vector + * + * @return this + */ + public abstract Vector negate(); + + /** + * Normalise this vector + * + * @return this + */ + public final Vector normalise() { + float len = length(); + if (len != 0.0f) { + float l = 1.0f / len; + return scale(l); + } else + throw new IllegalStateException("Zero length vector"); + } + + /** + * Store this vector in a FloatBuffer + * + * @param buf The buffer to store it in, at the current position + * @return this + */ + public abstract Vector store(FloatBuffer buf); + + /** + * Scale this vector + * + * @param scale The scale factor + * @return this + */ + public abstract Vector scale(float scale); + +} diff --git a/src/net/PeytonPlayz585/glemu/vector/Vector2f.java b/src/net/PeytonPlayz585/glemu/vector/Vector2f.java new file mode 100644 index 0000000..4201c7a --- /dev/null +++ b/src/net/PeytonPlayz585/glemu/vector/Vector2f.java @@ -0,0 +1,319 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.PeytonPlayz585.glemu.vector; + +import java.io.Serializable; +import java.nio.FloatBuffer; + +/** + * + * Holds a 2-tuple vector. + * + * @author cix_foo + * @version $Revision$ $Id$ + */ + +public class Vector2f extends Vector implements Serializable, ReadableVector2f, WritableVector2f { + + private static final long serialVersionUID = 1L; + + public float x, y; + + /** + * Constructor for Vector2f. + */ + public Vector2f() { + super(); + } + + /** + * Constructor. + */ + public Vector2f(ReadableVector2f src) { + set(src); + } + + /** + * Constructor. + */ + public Vector2f(float x, float y) { + set(x, y); + } + + /* + * (non-Javadoc) + * + * @see org.lwjgl.util.vector.WritableVector2f#set(float, float) + */ + public void set(float x, float y) { + this.x = x; + this.y = y; + } + + /** + * Load from another Vector2f + * + * @param src The source vector + * @return this + */ + public Vector2f set(ReadableVector2f src) { + x = src.getX(); + y = src.getY(); + return this; + } + + /** + * @return the length squared of the vector + */ + public float lengthSquared() { + return x * x + y * y; + } + + /** + * Translate a vector + * + * @param x The translation in x + * @param y the translation in y + * @return this + */ + public Vector2f translate(float x, float y) { + this.x += x; + this.y += y; + return this; + } + + /** + * Negate a vector + * + * @return this + */ + public Vector negate() { + x = -x; + y = -y; + return this; + } + + /** + * Negate a vector and place the result in a destination vector. + * + * @param dest The destination vector or null if a new vector is to be created + * @return the negated vector + */ + public Vector2f negate(Vector2f dest) { + if (dest == null) + dest = new Vector2f(); + dest.x = -x; + dest.y = -y; + return dest; + } + + /** + * Normalise this vector and place the result in another vector. + * + * @param dest The destination vector, or null if a new vector is to be created + * @return the normalised vector + */ + public Vector2f normalise(Vector2f dest) { + float l = length(); + + if (dest == null) + dest = new Vector2f(x / l, y / l); + else + dest.set(x / l, y / l); + + return dest; + } + + /** + * The dot product of two vectors is calculated as v1.x * v2.x + v1.y * v2.y + + * v1.z * v2.z + * + * @param left The LHS vector + * @param right The RHS vector + * @return left dot right + */ + public static float dot(Vector2f left, Vector2f right) { + return left.x * right.x + left.y * right.y; + } + + /** + * Calculate the angle between two vectors, in radians + * + * @param a A vector + * @param b The other vector + * @return the angle between the two vectors, in radians + */ + public static float angle(Vector2f a, Vector2f b) { + float dls = dot(a, b) / (a.length() * b.length()); + if (dls < -1f) + dls = -1f; + else if (dls > 1.0f) + dls = 1.0f; + return (float) Math.acos(dls); + } + + /** + * Add a vector to another vector and place the result in a destination vector. + * + * @param left The LHS vector + * @param right The RHS vector + * @param dest The destination vector, or null if a new vector is to be created + * @return the sum of left and right in dest + */ + public static Vector2f add(Vector2f left, Vector2f right, Vector2f dest) { + if (dest == null) + return new Vector2f(left.x + right.x, left.y + right.y); + else { + dest.set(left.x + right.x, left.y + right.y); + return dest; + } + } + + /** + * Subtract a vector from another vector and place the result in a destination + * vector. + * + * @param left The LHS vector + * @param right The RHS vector + * @param dest The destination vector, or null if a new vector is to be created + * @return left minus right in dest + */ + public static Vector2f sub(Vector2f left, Vector2f right, Vector2f dest) { + if (dest == null) + return new Vector2f(left.x - right.x, left.y - right.y); + else { + dest.set(left.x - right.x, left.y - right.y); + return dest; + } + } + + /** + * Store this vector in a FloatBuffer + * + * @param buf The buffer to store it in, at the current position + * @return this + */ + public Vector store(FloatBuffer buf) { + buf.put(x); + buf.put(y); + return this; + } + + /** + * Load this vector from a FloatBuffer + * + * @param buf The buffer to load it from, at the current position + * @return this + */ + public Vector load(FloatBuffer buf) { + x = buf.get(); + y = buf.get(); + return this; + } + + /* + * (non-Javadoc) + * + * @see org.lwjgl.vector.Vector#scale(float) + */ + public Vector scale(float scale) { + + x *= scale; + y *= scale; + + return this; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#toString() + */ + public String toString() { + StringBuilder sb = new StringBuilder(64); + + sb.append("Vector2f["); + sb.append(x); + sb.append(", "); + sb.append(y); + sb.append(']'); + return sb.toString(); + } + + /** + * @return x + */ + public final float getX() { + return x; + } + + /** + * @return y + */ + public final float getY() { + return y; + } + + /** + * Set X + * + * @param x + */ + public final void setX(float x) { + this.x = x; + } + + /** + * Set Y + * + * @param y + */ + public final void setY(float y) { + this.y = y; + } + + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Vector2f other = (Vector2f) obj; + + if (x == other.x && y == other.y) + return true; + + return false; + } + +} diff --git a/src/net/PeytonPlayz585/glemu/vector/Vector3f.java b/src/net/PeytonPlayz585/glemu/vector/Vector3f.java new file mode 100644 index 0000000..282170f --- /dev/null +++ b/src/net/PeytonPlayz585/glemu/vector/Vector3f.java @@ -0,0 +1,376 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.PeytonPlayz585.glemu.vector; + +import java.io.Serializable; +import java.nio.FloatBuffer; + +/** + * + * Holds a 3-tuple vector. + * + * @author cix_foo + * @version $Revision$ $Id$ + */ + +public class Vector3f extends Vector implements Serializable, ReadableVector3f, WritableVector3f { + + private static final long serialVersionUID = 1L; + + public float x, y, z; + + /** + * Constructor for Vector3f. + */ + public Vector3f() { + super(); + } + + /** + * Constructor + */ + public Vector3f(ReadableVector3f src) { + set(src); + } + + /** + * Constructor + */ + public Vector3f(float x, float y, float z) { + set(x, y, z); + } + + /* + * (non-Javadoc) + * + * @see org.lwjgl.util.vector.WritableVector2f#set(float, float) + */ + public void set(float x, float y) { + this.x = x; + this.y = y; + } + + /* + * (non-Javadoc) + * + * @see org.lwjgl.util.vector.WritableVector3f#set(float, float, float) + */ + public void set(float x, float y, float z) { + this.x = x; + this.y = y; + this.z = z; + } + + /** + * Load from another Vector3f + * + * @param src The source vector + * @return this + */ + public Vector3f set(ReadableVector3f src) { + x = src.getX(); + y = src.getY(); + z = src.getZ(); + return this; + } + + /** + * @return the length squared of the vector + */ + public float lengthSquared() { + return x * x + y * y + z * z; + } + + /** + * Translate a vector + * + * @param x The translation in x + * @param y the translation in y + * @return this + */ + public Vector3f translate(float x, float y, float z) { + this.x += x; + this.y += y; + this.z += z; + return this; + } + + /** + * Add a vector to another vector and place the result in a destination vector. + * + * @param left The LHS vector + * @param right The RHS vector + * @param dest The destination vector, or null if a new vector is to be created + * @return the sum of left and right in dest + */ + public static Vector3f add(Vector3f left, Vector3f right, Vector3f dest) { + if (dest == null) + return new Vector3f(left.x + right.x, left.y + right.y, left.z + right.z); + else { + dest.set(left.x + right.x, left.y + right.y, left.z + right.z); + return dest; + } + } + + /** + * Subtract a vector from another vector and place the result in a destination + * vector. + * + * @param left The LHS vector + * @param right The RHS vector + * @param dest The destination vector, or null if a new vector is to be created + * @return left minus right in dest + */ + public static Vector3f sub(Vector3f left, Vector3f right, Vector3f dest) { + if (dest == null) + return new Vector3f(left.x - right.x, left.y - right.y, left.z - right.z); + else { + dest.set(left.x - right.x, left.y - right.y, left.z - right.z); + return dest; + } + } + + /** + * The cross product of two vectors. + * + * @param left The LHS vector + * @param right The RHS vector + * @param dest The destination result, or null if a new vector is to be created + * @return left cross right + */ + public static Vector3f cross(Vector3f left, Vector3f right, Vector3f dest) { + + if (dest == null) + dest = new Vector3f(); + + dest.set(left.y * right.z - left.z * right.y, right.x * left.z - right.z * left.x, + left.x * right.y - left.y * right.x); + + return dest; + } + + /** + * Negate a vector + * + * @return this + */ + public Vector negate() { + x = -x; + y = -y; + z = -z; + return this; + } + + /** + * Negate a vector and place the result in a destination vector. + * + * @param dest The destination vector or null if a new vector is to be created + * @return the negated vector + */ + public Vector3f negate(Vector3f dest) { + if (dest == null) + dest = new Vector3f(); + dest.x = -x; + dest.y = -y; + dest.z = -z; + return dest; + } + + /** + * Normalise this vector and place the result in another vector. + * + * @param dest The destination vector, or null if a new vector is to be created + * @return the normalised vector + */ + public Vector3f normalise(Vector3f dest) { + float l = length(); + + if (dest == null) + dest = new Vector3f(x / l, y / l, z / l); + else + dest.set(x / l, y / l, z / l); + + return dest; + } + + /** + * The dot product of two vectors is calculated as v1.x * v2.x + v1.y * v2.y + + * v1.z * v2.z + * + * @param left The LHS vector + * @param right The RHS vector + * @return left dot right + */ + public static float dot(Vector3f left, Vector3f right) { + return left.x * right.x + left.y * right.y + left.z * right.z; + } + + /** + * Calculate the angle between two vectors, in radians + * + * @param a A vector + * @param b The other vector + * @return the angle between the two vectors, in radians + */ + public static float angle(Vector3f a, Vector3f b) { + float dls = dot(a, b) / (a.length() * b.length()); + if (dls < -1f) + dls = -1f; + else if (dls > 1.0f) + dls = 1.0f; + return (float) Math.acos(dls); + } + + /* + * (non-Javadoc) + * + * @see org.lwjgl.vector.Vector#load(FloatBuffer) + */ + public Vector load(FloatBuffer buf) { + x = buf.get(); + y = buf.get(); + z = buf.get(); + return this; + } + + /* + * (non-Javadoc) + * + * @see org.lwjgl.vector.Vector#scale(float) + */ + public Vector scale(float scale) { + + x *= scale; + y *= scale; + z *= scale; + + return this; + + } + + /* + * (non-Javadoc) + * + * @see org.lwjgl.vector.Vector#store(FloatBuffer) + */ + public Vector store(FloatBuffer buf) { + + buf.put(x); + buf.put(y); + buf.put(z); + + return this; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#toString() + */ + public String toString() { + StringBuilder sb = new StringBuilder(64); + + sb.append("Vector3f["); + sb.append(x); + sb.append(", "); + sb.append(y); + sb.append(", "); + sb.append(z); + sb.append(']'); + return sb.toString(); + } + + /** + * @return x + */ + public final float getX() { + return x; + } + + /** + * @return y + */ + public final float getY() { + return y; + } + + /** + * Set X + * + * @param x + */ + public final void setX(float x) { + this.x = x; + } + + /** + * Set Y + * + * @param y + */ + public final void setY(float y) { + this.y = y; + } + + /** + * Set Z + * + * @param z + */ + public void setZ(float z) { + this.z = z; + } + + /* + * (Overrides) + * + * @see org.lwjgl.vector.ReadableVector3f#getZ() + */ + public float getZ() { + return z; + } + + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Vector3f other = (Vector3f) obj; + + if (x == other.x && y == other.y && z == other.z) + return true; + + return false; + } +} diff --git a/src/net/PeytonPlayz585/glemu/vector/Vector4f.java b/src/net/PeytonPlayz585/glemu/vector/Vector4f.java new file mode 100644 index 0000000..7582396 --- /dev/null +++ b/src/net/PeytonPlayz585/glemu/vector/Vector4f.java @@ -0,0 +1,377 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.PeytonPlayz585.glemu.vector; + +import java.io.Serializable; +import java.nio.FloatBuffer; + +/** + * + * Holds a 4-tuple vector. + * + * @author cix_foo + * @version $Revision$ $Id$ + */ + +public class Vector4f extends Vector implements Serializable, ReadableVector4f, WritableVector4f { + + private static final long serialVersionUID = 1L; + + public float x, y, z, w; + + /** + * Constructor for Vector4f. + */ + public Vector4f() { + super(); + } + + /** + * Constructor + */ + public Vector4f(ReadableVector4f src) { + set(src); + } + + /** + * Constructor + */ + public Vector4f(float x, float y, float z, float w) { + set(x, y, z, w); + } + + /* + * (non-Javadoc) + * + * @see org.lwjgl.util.vector.WritableVector2f#set(float, float) + */ + public void set(float x, float y) { + this.x = x; + this.y = y; + } + + /* + * (non-Javadoc) + * + * @see org.lwjgl.util.vector.WritableVector3f#set(float, float, float) + */ + public void set(float x, float y, float z) { + this.x = x; + this.y = y; + this.z = z; + } + + /* + * (non-Javadoc) + * + * @see org.lwjgl.util.vector.WritableVector4f#set(float, float, float, float) + */ + public void set(float x, float y, float z, float w) { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + /** + * Load from another Vector4f + * + * @param src The source vector + * @return this + */ + public Vector4f set(ReadableVector4f src) { + x = src.getX(); + y = src.getY(); + z = src.getZ(); + w = src.getW(); + return this; + } + + /** + * @return the length squared of the vector + */ + public float lengthSquared() { + return x * x + y * y + z * z + w * w; + } + + /** + * Translate a vector + * + * @param x The translation in x + * @param y the translation in y + * @return this + */ + public Vector4f translate(float x, float y, float z, float w) { + this.x += x; + this.y += y; + this.z += z; + this.w += w; + return this; + } + + /** + * Add a vector to another vector and place the result in a destination vector. + * + * @param left The LHS vector + * @param right The RHS vector + * @param dest The destination vector, or null if a new vector is to be created + * @return the sum of left and right in dest + */ + public static Vector4f add(Vector4f left, Vector4f right, Vector4f dest) { + if (dest == null) + return new Vector4f(left.x + right.x, left.y + right.y, left.z + right.z, left.w + right.w); + else { + dest.set(left.x + right.x, left.y + right.y, left.z + right.z, left.w + right.w); + return dest; + } + } + + /** + * Subtract a vector from another vector and place the result in a destination + * vector. + * + * @param left The LHS vector + * @param right The RHS vector + * @param dest The destination vector, or null if a new vector is to be created + * @return left minus right in dest + */ + public static Vector4f sub(Vector4f left, Vector4f right, Vector4f dest) { + if (dest == null) + return new Vector4f(left.x - right.x, left.y - right.y, left.z - right.z, left.w - right.w); + else { + dest.set(left.x - right.x, left.y - right.y, left.z - right.z, left.w - right.w); + return dest; + } + } + + /** + * Negate a vector + * + * @return this + */ + public Vector negate() { + x = -x; + y = -y; + z = -z; + w = -w; + return this; + } + + /** + * Negate a vector and place the result in a destination vector. + * + * @param dest The destination vector or null if a new vector is to be created + * @return the negated vector + */ + public Vector4f negate(Vector4f dest) { + if (dest == null) + dest = new Vector4f(); + dest.x = -x; + dest.y = -y; + dest.z = -z; + dest.w = -w; + return dest; + } + + /** + * Normalise this vector and place the result in another vector. + * + * @param dest The destination vector, or null if a new vector is to be created + * @return the normalised vector + */ + public Vector4f normalise(Vector4f dest) { + float l = length(); + + if (dest == null) + dest = new Vector4f(x / l, y / l, z / l, w / l); + else + dest.set(x / l, y / l, z / l, w / l); + + return dest; + } + + /** + * The dot product of two vectors is calculated as v1.x * v2.x + v1.y * v2.y + + * v1.z * v2.z + v1.w * v2.w + * + * @param left The LHS vector + * @param right The RHS vector + * @return left dot right + */ + public static float dot(Vector4f left, Vector4f right) { + return left.x * right.x + left.y * right.y + left.z * right.z + left.w * right.w; + } + + /** + * Calculate the angle between two vectors, in radians + * + * @param a A vector + * @param b The other vector + * @return the angle between the two vectors, in radians + */ + public static float angle(Vector4f a, Vector4f b) { + float dls = dot(a, b) / (a.length() * b.length()); + if (dls < -1f) + dls = -1f; + else if (dls > 1.0f) + dls = 1.0f; + return (float) Math.acos(dls); + } + + /* + * (non-Javadoc) + * + * @see org.lwjgl.vector.Vector#load(FloatBuffer) + */ + public Vector load(FloatBuffer buf) { + x = buf.get(); + y = buf.get(); + z = buf.get(); + w = buf.get(); + return this; + } + + /* + * (non-Javadoc) + * + * @see org.lwjgl.vector.Vector#scale(float) + */ + public Vector scale(float scale) { + x *= scale; + y *= scale; + z *= scale; + w *= scale; + return this; + } + + /* + * (non-Javadoc) + * + * @see org.lwjgl.vector.Vector#store(FloatBuffer) + */ + public Vector store(FloatBuffer buf) { + + buf.put(x); + buf.put(y); + buf.put(z); + buf.put(w); + + return this; + } + + public String toString() { + return "Vector4f: " + x + " " + y + " " + z + " " + w; + } + + /** + * @return x + */ + public final float getX() { + return x; + } + + /** + * @return y + */ + public final float getY() { + return y; + } + + /** + * Set X + * + * @param x + */ + public final void setX(float x) { + this.x = x; + } + + /** + * Set Y + * + * @param y + */ + public final void setY(float y) { + this.y = y; + } + + /** + * Set Z + * + * @param z + */ + public void setZ(float z) { + this.z = z; + } + + /* + * (Overrides) + * + * @see org.lwjgl.vector.ReadableVector3f#getZ() + */ + public float getZ() { + return z; + } + + /** + * Set W + * + * @param w + */ + public void setW(float w) { + this.w = w; + } + + /* + * (Overrides) + * + * @see org.lwjgl.vector.ReadableVector3f#getZ() + */ + public float getW() { + return w; + } + + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Vector4f other = (Vector4f) obj; + + if (x == other.x && y == other.y && z == other.z && w == other.w) + return true; + + return false; + } +} diff --git a/src/net/PeytonPlayz585/glemu/vector/WritableVector2f.java b/src/net/PeytonPlayz585/glemu/vector/WritableVector2f.java new file mode 100644 index 0000000..faa6c71 --- /dev/null +++ b/src/net/PeytonPlayz585/glemu/vector/WritableVector2f.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.PeytonPlayz585.glemu.vector; + +/** + * Writable interface to Vector2fs + * + * @author $author$ + * @version $revision$ $Id$ + */ +public interface WritableVector2f { + + /** + * Set the X value + * + * @param x + */ + void setX(float x); + + /** + * Set the Y value + * + * @param y + */ + void setY(float y); + + /** + * Set the X,Y values + * + * @param x + * @param y + */ + void set(float x, float y); + +} \ No newline at end of file diff --git a/src/net/PeytonPlayz585/glemu/vector/WritableVector3f.java b/src/net/PeytonPlayz585/glemu/vector/WritableVector3f.java new file mode 100644 index 0000000..6c6e004 --- /dev/null +++ b/src/net/PeytonPlayz585/glemu/vector/WritableVector3f.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.PeytonPlayz585.glemu.vector; + +/** + * Writable interface to Vector3fs + * + * @author $author$ + * @version $revision$ $Id$ + */ +public interface WritableVector3f extends WritableVector2f { + + /** + * Set the Z value + * + * @param z + */ + void setZ(float z); + + /** + * Set the X,Y,Z values + * + * @param x + * @param y + * @param z + */ + void set(float x, float y, float z); + +} \ No newline at end of file diff --git a/src/net/PeytonPlayz585/glemu/vector/WritableVector4f.java b/src/net/PeytonPlayz585/glemu/vector/WritableVector4f.java new file mode 100644 index 0000000..9870654 --- /dev/null +++ b/src/net/PeytonPlayz585/glemu/vector/WritableVector4f.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.PeytonPlayz585.glemu.vector; + +/** + * Writable interface to Vector4fs + * + * @author $author$ + * @version $revision$ $Id$ + */ +public interface WritableVector4f extends WritableVector3f { + + /** + * Set the W value + * + * @param w + */ + void setW(float w); + + /** + * Set the X,Y,Z,W values + * + * @param x + * @param y + * @param z + * @param w + */ + void set(float x, float y, float z, float w); + +} \ No newline at end of file diff --git a/src/net/PeytonPlayz585/input/Keyboard.java b/src/net/PeytonPlayz585/input/Keyboard.java new file mode 100644 index 0000000..422c762 --- /dev/null +++ b/src/net/PeytonPlayz585/input/Keyboard.java @@ -0,0 +1,15 @@ +package net.PeytonPlayz585.input; + +import net.PeytonPlayz585.opengl.GL11; + +public class Keyboard extends GL11 { + + public static boolean next() { + return keysNext(); + } + + public static char getEventCharacter() { + return getEventChar(); + } + +} diff --git a/src/net/PeytonPlayz585/input/Mouse.java b/src/net/PeytonPlayz585/input/Mouse.java new file mode 100644 index 0000000..2fa0c85 --- /dev/null +++ b/src/net/PeytonPlayz585/input/Mouse.java @@ -0,0 +1,55 @@ +package net.PeytonPlayz585.input; + +import net.PeytonPlayz585.opengl.GL11; + +public class Mouse extends GL11 { + + public static int getX() { + return mouseGetX(); + } + + public static int getY() { + return mouseGetY(); + } + + public static boolean next() { + return mouseNext(); + } + + public static boolean getEventButtonState() { + return mouseGetEventButtonState(); + } + + public static int getEventX() { + return mouseGetEventX(); + } + + + public static int getEventY() { + return mouseGetEventY(); + } + + public static int getEventButton() { + return mouseGetEventButton(); + } + + public static int getDX() { + return mouseGetDX(); + } + + public static int getDY() { + return mouseGetDY(); + } + + public static void setGrabbed(boolean b) { + mouseSetGrabbed(b); + } + + public static boolean isButtonDown(int i) { + return mouseIsButtonDown(i); + } + + public static int getEventDWheel() { + return mouseGetEventDWheel(); + } +} diff --git a/src/net/PeytonPlayz585/opengl/EaglerAdapterGL30.java b/src/net/PeytonPlayz585/opengl/EaglerAdapterGL30.java new file mode 100644 index 0000000..32b019b --- /dev/null +++ b/src/net/PeytonPlayz585/opengl/EaglerAdapterGL30.java @@ -0,0 +1,1245 @@ +package net.PeytonPlayz585.opengl; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; +import java.util.HashMap; + +import net.PeytonPlayz585.glemu.FixedFunctionShader; +import net.PeytonPlayz585.glemu.GLObjectMap; + +import net.lax1dude.eaglercraft.adapter.EaglerAdapterImpl2; +import net.minecraft.client.Minecraft; +import net.minecraft.src.GLAllocation; +import net.minecraft.src.Vec3D; +import net.PeytonPlayz585.glemu.vector.*; + +import static net.PeytonPlayz585.glemu.StreamBuffer.StreamBufferInstance; + +public class EaglerAdapterGL30 extends EaglerAdapterImpl2 { + + public static final int GL_ZERO = RealOpenGLEnums.GL_ZERO; + public static final int GL_ONE = RealOpenGLEnums.GL_ONE; + public static final int GL_TEXTURE_2D = RealOpenGLEnums.GL_TEXTURE_2D; + public static final int GL_SMOOTH = RealOpenGLEnums.GL_SMOOTH; + public static final int GL_DEPTH_TEST = RealOpenGLEnums.GL_DEPTH_TEST; + public static final int GL_LEQUAL = RealOpenGLEnums.GL_LEQUAL; + public static final int GL_ALPHA_TEST = RealOpenGLEnums.GL_ALPHA_TEST; + public static final int GL_GREATER = RealOpenGLEnums.GL_GREATER; + public static final int GL_BACK = RealOpenGLEnums.GL_BACK; + public static final int GL_PROJECTION = RealOpenGLEnums.GL_PROJECTION; + public static final int GL_MODELVIEW = RealOpenGLEnums.GL_MODELVIEW; + public static final int GL_COLOR_BUFFER_BIT = RealOpenGLEnums.GL_COLOR_BUFFER_BIT; + public static final int GL_DEPTH_BUFFER_BIT = RealOpenGLEnums.GL_DEPTH_BUFFER_BIT; + public static final int GL_LIGHTING = RealOpenGLEnums.GL_LIGHTING; + public static final int GL_FOG = RealOpenGLEnums.GL_FOG; + public static final int GL_COLOR_MATERIAL = RealOpenGLEnums.GL_COLOR_MATERIAL; + public static final int GL_BLEND = RealOpenGLEnums.GL_BLEND; + public static final int GL_RGBA = RealOpenGLEnums.GL_RGBA; + public static final int GL_UNSIGNED_BYTE = RealOpenGLEnums.GL_UNSIGNED_BYTE; + public static final int GL_TEXTURE_WIDTH = RealOpenGLEnums.GL_TEXTURE_WIDTH; + public static final int GL_LIGHT0 = RealOpenGLEnums.GL_LIGHT0; + public static final int GL_LIGHT1 = RealOpenGLEnums.GL_LIGHT1; + public static final int GL_POSITION = RealOpenGLEnums.GL_POSITION; + public static final int GL_DIFFUSE = RealOpenGLEnums.GL_DIFFUSE; + public static final int GL_SPECULAR = RealOpenGLEnums.GL_SPECULAR; + public static final int GL_AMBIENT = RealOpenGLEnums.GL_AMBIENT; + public static final int GL_FLAT = RealOpenGLEnums.GL_FLAT; + public static final int GL_LIGHT_MODEL_AMBIENT = RealOpenGLEnums.GL_LIGHT_MODEL_AMBIENT; + public static final int GL_FRONT_AND_BACK = RealOpenGLEnums.GL_FRONT_AND_BACK; + public static final int GL_AMBIENT_AND_DIFFUSE = RealOpenGLEnums.GL_AMBIENT_AND_DIFFUSE; + public static final int GL_MODELVIEW_MATRIX = RealOpenGLEnums.GL_MODELVIEW_MATRIX; + public static final int GL_PROJECTION_MATRIX = RealOpenGLEnums.GL_PROJECTION_MATRIX; + public static final int GL_VIEWPORT = RealOpenGLEnums.GL_VIEWPORT; + public static final int GL_RESCALE_NORMAL = RealOpenGLEnums.GL_RESCALE_NORMAL; + public static final int GL_SRC_ALPHA = RealOpenGLEnums.GL_SRC_ALPHA; + public static final int GL_ONE_MINUS_SRC_ALPHA = RealOpenGLEnums.GL_ONE_MINUS_SRC_ALPHA; + public static final int GL_ONE_MINUS_DST_COLOR = RealOpenGLEnums.GL_ONE_MINUS_DST_COLOR; + public static final int GL_ONE_MINUS_SRC_COLOR = RealOpenGLEnums.GL_ONE_MINUS_SRC_COLOR; + public static final int GL_CULL_FACE = RealOpenGLEnums.GL_CULL_FACE; + public static final int GL_TEXTURE_MIN_FILTER = RealOpenGLEnums.GL_TEXTURE_MIN_FILTER; + public static final int GL_TEXTURE_MAG_FILTER = RealOpenGLEnums.GL_TEXTURE_MAG_FILTER; + public static final int GL_LINEAR = RealOpenGLEnums.GL_LINEAR; + public static final int GL_COLOR_LOGIC_OP = RealOpenGLEnums.GL_COLOR_LOGIC_OP; + public static final int GL_OR_REVERSE = RealOpenGLEnums.GL_OR_REVERSE; + public static final int GL_EQUAL = RealOpenGLEnums.GL_EQUAL; + public static final int GL_SRC_COLOR = RealOpenGLEnums.GL_SRC_COLOR; + public static final int GL_TEXTURE = RealOpenGLEnums.GL_TEXTURE; + public static final int GL_FRONT = RealOpenGLEnums.GL_FRONT; + public static final int GL_COMPILE = RealOpenGLEnums.GL_COMPILE; + public static final int GL_NEAREST = RealOpenGLEnums.GL_NEAREST; + public static final int GL_CLAMP = RealOpenGLEnums.GL_CLAMP_TO_EDGE; + public static final int GL_TEXTURE_WRAP_S = RealOpenGLEnums.GL_TEXTURE_WRAP_S; + public static final int GL_TEXTURE_WRAP_T = RealOpenGLEnums.GL_TEXTURE_WRAP_T; + public static final int GL_REPEAT = RealOpenGLEnums.GL_REPEAT; + public static final int GL_BGRA = RealOpenGLEnums.GL_BGRA; + public static final int GL_UNSIGNED_INT_8_8_8_8_REV = RealOpenGLEnums.GL_UNSIGNED_INT_8_8_8_8_REV; + public static final int GL_DST_COLOR = RealOpenGLEnums.GL_DST_COLOR; + public static final int GL_POLYGON_OFFSET_FILL = RealOpenGLEnums.GL_POLYGON_OFFSET_FILL; + public static final int GL_NORMALIZE = RealOpenGLEnums.GL_NORMALIZE; + public static final int GL_DST_ALPHA = RealOpenGLEnums.GL_DST_ALPHA; + public static final int GL_FLOAT = RealOpenGLEnums.GL_FLOAT; + public static final int GL_TEXTURE_COORD_ARRAY = RealOpenGLEnums.GL_TEXTURE_COORD_ARRAY; + public static final int GL_SHORT = RealOpenGLEnums.GL_SHORT; + public static final int GL_COLOR_ARRAY = RealOpenGLEnums.GL_COLOR_ARRAY; + public static final int GL_VERTEX_ARRAY = RealOpenGLEnums.GL_VERTEX_ARRAY; + public static final int GL_TRIANGLES = RealOpenGLEnums.GL_TRIANGLES; + public static final int GL_NORMAL_ARRAY = RealOpenGLEnums.GL_NORMAL_ARRAY; + public static final int GL_TEXTURE_3D = RealOpenGLEnums.GL_TEXTURE_3D; + public static final int GL_FOG_MODE = RealOpenGLEnums.GL_FOG_MODE; + public static final int GL_EXP = RealOpenGLEnums.GL_EXP; + public static final int GL_FOG_DENSITY = RealOpenGLEnums.GL_FOG_DENSITY; + public static final int GL_FOG_START = RealOpenGLEnums.GL_FOG_START; + public static final int GL_FOG_END = RealOpenGLEnums.GL_FOG_END; + public static final int GL_FOG_COLOR = RealOpenGLEnums.GL_FOG_COLOR; + public static final int GL_TRIANGLE_STRIP = RealOpenGLEnums.GL_TRIANGLE_STRIP; + public static final int GL_PACK_ALIGNMENT = RealOpenGLEnums.GL_PACK_ALIGNMENT; + public static final int GL_UNPACK_ALIGNMENT = RealOpenGLEnums.GL_UNPACK_ALIGNMENT; + public static final int GL_QUADS = RealOpenGLEnums.GL_QUADS; + public static final int GL_INVALID_ENUM = RealOpenGLEnums.GL_INVALID_ENUM; + public static final int GL_INVALID_VALUE = RealOpenGLEnums.GL_INVALID_VALUE; + public static final int GL_INVALID_OPERATION = RealOpenGLEnums.GL_INVALID_OPERATION; + public static final int GL_OUT_OF_MEMORY = RealOpenGLEnums.GL_OUT_OF_MEMORY; + public static final int GL_CONTEXT_LOST_WEBGL = -144; + public static final int GL_TRIANGLE_FAN = RealOpenGLEnums.GL_TRIANGLE_FAN; + public static final int GL_LINE_STRIP = RealOpenGLEnums.GL_LINE_STRIP; + public static final int GL_LINES = RealOpenGLEnums.GL_LINES; + public static final int GL_NEAREST_MIPMAP_LINEAR = RealOpenGLEnums.GL_NEAREST_MIPMAP_LINEAR; + public static final int GL_TEXTURE_MAX_ANISOTROPY = -150; + public static final int GL_TEXTURE_MAX_LEVEL = RealOpenGLEnums.GL_TEXTURE_MAX_LEVEL; + public static final int GL_LINEAR_MIPMAP_LINEAR = RealOpenGLEnums.GL_LINEAR_MIPMAP_LINEAR; + public static final int GL_LINEAR_MIPMAP_NEAREST = RealOpenGLEnums.GL_LINEAR_MIPMAP_NEAREST; + public static final int GL_NEAREST_MIPMAP_NEAREST = RealOpenGLEnums.GL_NEAREST_MIPMAP_NEAREST; + + public static final boolean isWebGL = _wisWebGL(); + + private static final GLObjectMap texObjects = new GLObjectMap(256); + + private static boolean enableTexture2D = false; + private static boolean enableLighting = false; + private static boolean enableAlphaTest = false; + private static float alphaThresh = 0.1f; + + private static boolean isCompilingDisplayList = false; + private static DisplayList compilingDisplayList = null; + + private static boolean enableColorArray = false; + private static boolean enableNormalArray = false; + private static boolean enableTex0Array = false; + + private static float colorR = 1.0f; + private static float colorG = 1.0f; + private static float colorB = 1.0f; + private static float colorA = 1.0f; + + private static float normalX = 1.0f; + private static float normalY = 0.0f; + private static float normalZ = 0.0f; + + private static float tex0X = 0; + private static float tex0Y = 0; + + private static boolean enableColorMaterial = false; + + private static float fogColorR = 1.0f; + private static float fogColorG = 1.0f; + private static float fogColorB = 1.0f; + private static float fogColorA = 1.0f; + private static int fogMode = 1; + private static boolean fogEnabled = false; + private static boolean fogPremultiply = false; + private static float fogStart = 1.0f; + private static float fogEnd = 1.0f; + private static float fogDensity = 1.0f; + + private static int bytesUploaded = 0; + private static int vertexDrawn = 0; + private static int triangleDrawn = 0; + + private static int matrixMode = GL_MODELVIEW; + + static Matrix4f[] matModelV = new Matrix4f[32]; + static int matModelPointer = 0; + + static Matrix4f[] matProjV = new Matrix4f[6]; + static int matProjPointer = 0; + + static Matrix4f[] matTexV = new Matrix4f[16]; + static int matTexPointer = 0; + + static { + for (int i = 0; i < matModelV.length; ++i) { + matModelV[i] = new Matrix4f(); + } + for (int i = 0; i < matProjV.length; ++i) { + matProjV[i] = new Matrix4f(); + } + for (int i = 0; i < matTexV.length; ++i) { + matTexV[i] = new Matrix4f(); + } + } + + public static void glClearStack() { + matModelV[0].load(matModelV[matModelPointer]); + matModelPointer = 0; + matProjV[0].load(matProjV[matProjPointer]); + matProjPointer = 0; + matTexV[0].load(matTexV[matTexPointer]); + matTexPointer = 0; + } + + private static BufferGL quadsToTrianglesBuffer = null; + private static BufferArrayGL currentArray = null; + + private static class DisplayList { + private final int id; + private BufferArrayGL glarray; + private BufferGL glbuffer; + private int shaderMode; + private int listLength; + + private DisplayList(int id) { + this.id = id; + this.glarray = null; + this.glbuffer = null; + this.shaderMode = -1; + this.listLength = 0; + } + } + + private static final HashMap displayLists = new HashMap(); + private static final HashMap displayListsInitialized = new HashMap(); + + public static final int getDisplayListCount() { + return displayListsInitialized.size(); + } + + public static final void glEnable(int p1) { + switch (p1) { + case GL_DEPTH_TEST: + _wglEnable(_wGL_DEPTH_TEST); + break; + case GL_CULL_FACE: + _wglEnable(_wGL_CULL_FACE); + break; + case GL_BLEND: + _wglEnable(_wGL_BLEND); + break; + case GL_RESCALE_NORMAL: + break; + case GL_TEXTURE_2D: + enableTexture2D = true; + break; + case GL_LIGHTING: + enableLighting = true; + break; + case GL_ALPHA_TEST: + enableAlphaTest = true; + break; + case GL_FOG: + fogEnabled = true; + break; + case GL_COLOR_MATERIAL: + enableColorMaterial = true; + break; + case GL_POLYGON_OFFSET_FILL: + _wglEnable(_wGL_POLYGON_OFFSET_FILL); + default: + break; + } + } + + public static final void glClearDepth(float p1) { + _wglClearDepth(-p1); + } + + public static final void glDepthFunc(int p1) { + int f = _wGL_GEQUAL; + switch (p1) { + case GL_GREATER: + f = _wGL_LESS; + break; + case GL_LEQUAL: + f = _wGL_GEQUAL; + break; + case GL_EQUAL: + f = _wGL_EQUAL; + default: + break; + } + _wglDepthFunc(f); + } + + public static final void glAlphaFunc(int p1, float p2) { + alphaThresh = p2; + } + + public static final void glCullFace(int p1) { + _wglCullFace(p1); + } + + public static final void glMatrixMode(int p1) { + matrixMode = p1; + } + + private static final Matrix4f getMatrix() { + switch (matrixMode) { + case GL_MODELVIEW: + default: + return matModelV[matModelPointer]; + case GL_PROJECTION: + return matProjV[matProjPointer]; + case GL_TEXTURE: + return matTexV[matTexPointer]; + } + } + + public static final void glLoadIdentity() { + getMatrix().setIdentity(); + } + + public static final void glViewport(int p1, int p2, int p3, int p4) { + _wglViewport(p1, p2, p3, p4); + } + + public static final void glClear(int p1) { + _wglClear(p1); + } + + public static final void glOrtho(double left, double right, double bottom, double top, double zNear, double zFar) { + Matrix4f res = getMatrix(); + res.m00 = (float) (2.0f / (right - left)); + res.m01 = 0.0f; + res.m02 = 0.0f; + res.m03 = 0.0f; + res.m10 = 0.0f; + res.m11 = (float) (2.0f / (top - bottom)); + res.m12 = 0.0f; + res.m13 = 0.0f; + res.m20 = 0.0f; + res.m21 = 0.0f; + res.m22 = (float) (2.0f / (zFar - zNear)); + res.m23 = 0.0f; + res.m30 = (float) (-(right + left) / (right - left)); + res.m31 = (float) (-(top + bottom) / (top - bottom)); + res.m32 = (float) ((zFar + zNear) / (zFar - zNear)); + res.m33 = 1.0f; + } + + public static final void glOrtho(float left, float right, float bottom, float top, float zNear, float zFar) { + Matrix4f res = getMatrix(); + res.m00 = 2.0f / (right - left); + res.m01 = 0.0f; + res.m02 = 0.0f; + res.m03 = 0.0f; + res.m10 = 0.0f; + res.m11 = 2.0f / (top - bottom); + res.m12 = 0.0f; + res.m13 = 0.0f; + res.m20 = 0.0f; + res.m21 = 0.0f; + res.m22 = 2.0f / (zFar - zNear); + res.m23 = 0.0f; + res.m30 = -(right + left) / (right - left); + res.m31 = -(top + bottom) / (top - bottom); + res.m32 = (zFar + zNear) / (zFar - zNear); + res.m33 = 1.0f; + } + + private static final Vector3f deevis = new Vector3f(); + + public static final void glTranslatef(float p1, float p2, float p3) { + deevis.set(p1, p2, p3); + getMatrix().translate(deevis); + if (isCompilingDisplayList) { + throw new IllegalArgumentException("matrix is not supported while recording display list use tessellator class instead"); + } + } + + public static final void glClearColor(float p1, float p2, float p3, float p4) { + _wglClearColor(p1, p2, p3, p4); + } + + public static final void glDisable(int p1) { + switch (p1) { + case GL_DEPTH_TEST: + _wglDisable(_wGL_DEPTH_TEST); + break; + case GL_CULL_FACE: + _wglDisable(_wGL_CULL_FACE); + break; + case GL_BLEND: + _wglDisable(_wGL_BLEND); + break; + case GL_RESCALE_NORMAL: + break; + case GL_TEXTURE_2D: + enableTexture2D = false; + break; + case GL_LIGHTING: + enableLighting = false; + break; + case GL_ALPHA_TEST: + enableAlphaTest = false; + break; + case GL_FOG: + fogEnabled = false; + break; + case GL_COLOR_MATERIAL: + enableColorMaterial = false; + break; + case GL_POLYGON_OFFSET_FILL: + _wglDisable(_wGL_POLYGON_OFFSET_FILL); + default: + break; + } + } + + public static final void glColor4f(float p1, float p2, float p3, float p4) { + colorR = p1; + colorG = p2; + colorB = p3; + colorA = p4; + } + + public static final int glGetError() { + int err = _wglGetError(); + if (err == _wGL_CONTEXT_LOST_WEBGL) + return GL_CONTEXT_LOST_WEBGL; + return err; + } + + public static final void glFlush() { + _wglFlush(); + } + + public static final void glLineWidth(float p1) { + + } + + public static final void glTexImage2D(int p1, int p2, int p3, int p4, int p5, int p6, int p7, int p8, + ByteBuffer p9) { + _wglTexImage2D(_wGL_TEXTURE_2D, p2, _wGL_RGBA8, p4, p5, p6, _wGL_RGBA, _wGL_UNSIGNED_BYTE, p9); + } + + private static Vector4f lightPos0vec0 = new Vector4f(); + private static Vector4f lightPos1vec0 = new Vector4f(); + private static Vector4f lightPos0vec = new Vector4f(); + private static Vector4f lightPos1vec = new Vector4f(); + + public static final void copyModelToLightMatrix() { + lightPos0vec0.set(lightPos0vec); + lightPos1vec0.set(lightPos1vec); + lightPos0vec.set(0.2f, 1.0f, -0.7f, 0.0f); + lightPos0vec.normalise(); + lightPos1vec.set(-0.2f, 1.0f, 0.7f, 0.0f); + lightPos1vec.normalise(); + Matrix4f.transform(matModelV[matModelPointer], lightPos0vec, lightPos0vec).normalise(); + Matrix4f.transform(matModelV[matModelPointer], lightPos1vec, lightPos1vec).normalise(); + lightPos0vec.x = -lightPos0vec.x; + lightPos1vec.x = -lightPos1vec.x; + lightPos0vec.y = -lightPos0vec.y; + lightPos1vec.y = -lightPos1vec.y; + lightPos0vec.z = -lightPos0vec.z; + lightPos1vec.z = -lightPos1vec.z; + } + + public static final void revertLightMatrix() { + lightPos0vec.set(lightPos0vec0); + lightPos1vec.set(lightPos1vec0); + } + + public static final void glPushMatrix() { + switch (matrixMode) { + case GL_MODELVIEW: + default: + if (matModelPointer < matModelV.length - 1) { + ++matModelPointer; + matModelV[matModelPointer].load(matModelV[matModelPointer - 1]); + } else { + System.err.println("modelview matrix stack overflow"); + } + break; + case GL_PROJECTION: + if (matProjPointer < matProjV.length - 1) { + ++matProjPointer; + matProjV[matProjPointer].load(matProjV[matProjPointer - 1]); + } else { + System.err.println("projection matrix stack overflow"); + } + break; + case GL_TEXTURE: + if (matTexPointer < matTexV.length - 1) { + ++matTexPointer; + matTexV[matTexPointer].load(matTexV[matTexPointer - 1]); + } else { + System.err.println("texture matrix stack overflow"); + } + break; + } + } + + private static final float toRad = 0.0174532925f; + + public static final void glRotatef(float p1, float p2, float p3, float p4) { + deevis.set(p2, p3, p4); + getMatrix().rotate(p1 * toRad, deevis); + if (isCompilingDisplayList) { + throw new IllegalArgumentException("matrix is not supported while recording display list use tessellator class instead"); + } + } + + public static final void glPopMatrix() { + switch (matrixMode) { + case GL_MODELVIEW: + default: + if (matModelPointer > 0) { + --matModelPointer; + } else { + System.err.println("modelview matrix stack underflow"); + } + break; + case GL_PROJECTION: + if (matProjPointer > 0) { + --matProjPointer; + } else { + System.err.println("projection matrix stack underflow"); + } + break; + case GL_TEXTURE: + if (matTexPointer > 0) { + --matTexPointer; + } else { + System.err.println("texture matrix stack underflow"); + } + break; + } + } + + public static final void glColorMaterial(int p1, int p2) { + + } + + public static final void glGetFloat(int p1, FloatBuffer p2) { + switch (p1) { + case GL_MODELVIEW_MATRIX: + default: + matModelV[matModelPointer].store(p2); + break; + case GL_PROJECTION_MATRIX: + matProjV[matProjPointer].store(p2); + break; + } + } + + public static final void glGetInteger(int p1, int[] p2) { + if (p1 == GL_VIEWPORT) { + _wglGetParameter(_wGL_VIEWPORT, 4, p2); + } + } + + public static final void glScalef(float p1, float p2, float p3) { + deevis.set(p1, p2, p3); + getMatrix().scale(deevis); + if (isCompilingDisplayList) { + throw new IllegalArgumentException("matrix is not supported while recording display list use tessellator class instead"); + } + } + + public static final void glBlendFunc(int p1, int p2) { + fogPremultiply = (p1 == GL_ONE && p2 == GL_ONE_MINUS_SRC_ALPHA); + if(overlayFBOBlending) { + _wglBlendFuncSeparate(p1, p2, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + }else { + _wglBlendFunc(p1, p2); + } + } + + private static boolean overlayFBOBlending = false; + + public static final void enableOverlayFramebufferBlending(boolean en) { + overlayFBOBlending = en; + } + + public static final void glBlendFuncSeparate(int p1, int p2, int p3, int p4) { + fogPremultiply = (p3 == GL_ONE && p4 == GL_ONE_MINUS_SRC_ALPHA); + _wglBlendFuncSeparate(p1, p2, p3, p4); + } + + public static final void glDepthMask(boolean p1) { + _wglDepthMask(p1); + } + + public static final void glColorMask(boolean p1, boolean p2, boolean p3, boolean p4) { + _wglColorMask(p1, p2, p3, p4); + } + + public static final void glBindTexture(int p1, int p2) { + TextureGL t = texObjects.get(p2); + _wglBindTexture(_wGL_TEXTURE_2D, t); + } + + public static final void glBindTexture(int p2) { + TextureGL t = texObjects.get(p2); + _wglBindTexture(_wGL_TEXTURE_2D, t); + } + + public static final void glBindTexture(int p1, TextureGL p2) { + _wglBindTexture(_wGL_TEXTURE_2D, p2); + } + + public static final void glCopyTexSubImage2D(int p1, int p2, int p3, int p4, int p5, int p6, int p7, int p8) { + _wglCopyTexSubImage2D(_wGL_TEXTURE_2D, p2, p3, p4, p5, p6, p7, p8); + } + + public static final void glTexParameteri(int p1, int p2, int p3) { + if(p3 == RealOpenGLEnums.GL_CLAMP_TO_EDGE || p3 == 10496) { + p3 = _wGL_CLAMP; + } + _wglTexParameteri(p1, p2, p3); + } + + public static final void glTexParameterf(int p1, int p2, float p3) { + int pp1 = 0; + switch (p1) { + default: + case GL_TEXTURE_2D: + pp1 = _wGL_TEXTURE_2D; + break; + // case GL_TEXTURE_3D: pp1 = _wGL_TEXTURE_3D; break; + } + int pp2 = 0; + switch (p2) { + default: + case GL_TEXTURE_MAX_ANISOTROPY: + pp2 = _wGL_TEXTURE_MAX_ANISOTROPY; + break; + } + _wglTexParameterf(pp1, pp2, p3); + } + + public static final void glLogicOp(int p1) { + + } + + public static final void glNormal3f(float p1, float p2, float p3) { + float len = (float) Math.sqrt(p1 * p1 + p2 * p2 + p3 * p3); + normalX = p1 / len; + normalY = p2 / len; + normalZ = p3 / len; + } + + public static final int glGenLists(int p1) { + int base = displayListId + 1; + for (int i = 0; i < p1; i++) { + int id = ++displayListId; + displayLists.put(id, new DisplayList(id)); + } + return base; + } + + public static final void _wglBindVertexArray0(BufferArrayGL p1) { + currentArray = p1; + _wglBindVertexArray(p1); + } + + private static int displayListId = 0; + + public static final void glCallList(int p1) { + if (!isCompilingDisplayList) { + DisplayList d = displayListsInitialized.get(p1); + if (d != null && d.listLength > 0) { + bindTheShader(d.shaderMode | getShaderModeFlag1()); + _wglBindVertexArray0(d.glarray); + _wglDrawQuadArrays(0, d.listLength); + shader.unuseProgram(); + vertexDrawn += d.listLength * 6 / 4; + triangleDrawn += d.listLength / 2; + } + } + } + + public static final void glNewList(int p1, int p2) { + if (!isCompilingDisplayList) { + compilingDisplayList = displayLists.get(p1); + if (compilingDisplayList != null) { + compilingDisplayList.shaderMode = -1; + compilingDisplayList.listLength = 0; + isCompilingDisplayList = true; + } + } + } + + public static final void glEndList() { + if (isCompilingDisplayList) { + isCompilingDisplayList = false; + Object upload = _wGetLowLevelBuffersAppended(); + int l = _wArrayByteLength(upload); + if (l > 0) { + if (compilingDisplayList.glbuffer == null) { + displayListsInitialized.put(compilingDisplayList.id, compilingDisplayList); + compilingDisplayList.glarray = _wglCreateVertexArray(); + compilingDisplayList.glbuffer = _wglCreateBuffer(); + FixedFunctionShader f = FixedFunctionShader.instance(compilingDisplayList.shaderMode); + _wglBindVertexArray0(compilingDisplayList.glarray); + _wglBindBuffer(_wGL_ARRAY_BUFFER, compilingDisplayList.glbuffer); + f.setupArrayForProgram(); + } + _wglBindBuffer(_wGL_ARRAY_BUFFER, compilingDisplayList.glbuffer); + _wglBufferData(_wGL_ARRAY_BUFFER, upload, _wGL_STATIC_DRAW); + bytesUploaded += l; + } + } + } + + public static final void flushDisplayList(int p1) { + DisplayList d = displayListsInitialized.get(p1); + if (d != null) { + if (d.glbuffer != null) { + _wglDeleteBuffer(d.glbuffer); + _wglDeleteVertexArray(d.glarray); + d.glbuffer = null; + d.glarray = null; + } + } + } + + public static final void glColor3f(float p1, float p2, float p3) { + colorR = p1; + colorG = p2; + colorB = p3; + colorA = 1.0f; + } + + public static final void glTexImage2D(int p1, int p2, int p3, int p4, int p5, int p6, int p7, int p8, + IntBuffer p9) { + /* + * int pp2 = 0; switch(p3) { default: case GL_RGBA: pp2 = _wGL_RGBA; break; case + * GL_BGRA: pp2 = _wGL_BGRA; break; } int pp3 = 0; switch(p7) { default: case + * GL_RGBA: pp3 = _wGL_RGBA; break; case GL_BGRA: pp3 = _wGL_BGRA; break; } + */ + bytesUploaded += p9.remaining() * 4; + _wglTexImage2D(_wGL_TEXTURE_2D, p2, _wGL_RGBA8, p4, p5, p6, _wGL_RGBA, _wGL_UNSIGNED_BYTE, p9); + } + + public static final void glTexImage2D_2(int p1, int p2, int p3, int p4, int p5, int p6, int p7, int p8, + IntBuffer p9) { + bytesUploaded += p9.remaining() * 4; + _wglTexImage2D(_wGL_TEXTURE_2D, p2, _wGL_RGB8, p4, p5, p6, _wGL_RGB, _wGL_UNSIGNED_BYTE, p9); + } + + public static final void glTexSubImage2D(int p1, int p2, int p3, int p4, int p5, int p6, int p7, int p8, + IntBuffer p9) { + int pp1 = 0; + switch (p1) { + default: + case GL_TEXTURE_2D: + pp1 = _wGL_TEXTURE_2D; + break; + // case GL_TEXTURE_3D: pp1 = _wGL_TEXTURE_3D; break; + } + /* + * int pp3 = 0; switch(p7) { default: case GL_RGBA: pp3 = _wGL_RGBA; break; case + * GL_BGRA: pp3 = _wGL_BGRA; break; } + */ + bytesUploaded += p9.remaining() * 4; + _wglTexSubImage2D(pp1, p2, p3, p4, p5, p6, _wGL_RGBA, _wGL_UNSIGNED_BYTE, p9); + } + + public static final void glDeleteTextures(int p1) { + _wglDeleteTextures(texObjects.free(p1)); + } + + public static final void glPolygonOffset(float p1, float p2) { + _wglPolygonOffset(p1, p2); + } + + public static final void glCallLists(IntBuffer p1) { + while (p1.hasRemaining()) { + glCallList(p1.get()); + } + } + + public static final void glEnableVertexAttrib(int p1) { + switch (p1) { + case GL_COLOR_ARRAY: + enableColorArray = true; + break; + case GL_NORMAL_ARRAY: + enableNormalArray = true; + break; + case GL_TEXTURE_COORD_ARRAY: + enableTex0Array = true; + break; + default: + break; + } + } + + public static final void glDisableVertexAttrib(int p1) { + switch (p1) { + case GL_COLOR_ARRAY: + enableColorArray = false; + break; + case GL_NORMAL_ARRAY: + enableNormalArray = false; + break; + case GL_TEXTURE_COORD_ARRAY: + enableTex0Array = false; + break; + default: + break; + } + } + + private static final int getShaderModeFlag0() { + int mode = 0; + mode = (mode | (enableColorArray ? FixedFunctionShader.COLOR : 0)); + mode = (mode | (enableNormalArray ? FixedFunctionShader.NORMAL : 0)); + mode = (mode | (enableTex0Array ? FixedFunctionShader.TEXTURE0 : 0)); + return mode; + } + + private static final int getShaderModeFlag1() { + int mode = 0; + mode = (mode | ((enableColorMaterial && enableLighting) ? FixedFunctionShader.LIGHTING : 0)); + mode = (mode | (fogEnabled ? FixedFunctionShader.FOG : 0)); + mode = (mode | (enableAlphaTest ? FixedFunctionShader.ALPHATEST : 0)); + mode = (mode | (enableTexture2D ? FixedFunctionShader.UNIT0 : 0)); + return mode; + } + + private static final int getShaderModeFlag() { + int mode = 0; + mode = (mode | (enableColorArray ? FixedFunctionShader.COLOR : 0)); + mode = (mode | (enableNormalArray ? FixedFunctionShader.NORMAL : 0)); + mode = (mode | (enableTex0Array ? FixedFunctionShader.TEXTURE0 : 0)); + mode = (mode | ((enableColorMaterial && enableLighting) ? FixedFunctionShader.LIGHTING : 0)); + mode = (mode | (fogEnabled ? FixedFunctionShader.FOG : 0)); + mode = (mode | (enableAlphaTest ? FixedFunctionShader.ALPHATEST : 0)); + mode = (mode | (enableTexture2D ? FixedFunctionShader.UNIT0 : 0)); + return mode; + } + + private static FixedFunctionShader shader = null; + + private static final void bindTheShader() { + bindTheShader(getShaderModeFlag()); + } + + private static final void bindTheShader(int mode) { + FixedFunctionShader s = shader = FixedFunctionShader.instance(mode); + s.useProgram(); + if (enableAlphaTest) { + s.setAlphaTest(alphaThresh); + } + s.setColor(colorR, colorG, colorB, colorA); + if (fogEnabled) { + s.setFogMode((fogPremultiply ? 2 : 0) + fogMode); + s.setFogColor(fogColorR, fogColorG, fogColorB, fogColorA); + s.setFogDensity(fogDensity); + s.setFogStartEnd(fogStart, fogEnd); + } + s.setModelMatrix(matModelV[matModelPointer]); + s.setProjectionMatrix(matProjV[matProjPointer]); + s.setTextureMatrix(matTexV[matTexPointer]); + if (enableColorMaterial && enableLighting) { + s.setNormal(normalX, normalY, normalZ); + s.setLightPositions(lightPos0vec, lightPos1vec); + } + s.setTex0Coords(tex0X, tex0Y); + } + + private static Object blankUploadArray = _wCreateLowLevelIntBuffer(525000); + + public static final void glDrawArrays(int p1, int p2, int p3, Object buffer) { + if (isCompilingDisplayList) { + if (p1 == GL_QUADS) { + if (compilingDisplayList.shaderMode == -1) { + compilingDisplayList.shaderMode = getShaderModeFlag0(); + } else { + if (compilingDisplayList.shaderMode != getShaderModeFlag0()) { + System.err.println("vertex format inconsistent in display list"); + } + } + compilingDisplayList.listLength += p3; + _wAppendLowLevelBuffer(buffer); + } else { + System.err.println("only GL_QUADS supported in a display list"); + } + } else { + int bl = _wArrayByteLength(buffer); + bytesUploaded += bl; + vertexDrawn += p3; + + bindTheShader(); + + StreamBufferInstance sb = shader.streamBuffer.getBuffer(bl); + _wglBindVertexArray0(sb.vertexArray); + _wglBindBuffer(_wGL_ARRAY_BUFFER, sb.vertexBuffer); + if (!shader.bufferIsInitialized) { + shader.bufferIsInitialized = true; + _wglBufferData(_wGL_ARRAY_BUFFER, blankUploadArray, _wGL_DYNAMIC_DRAW); + } + _wglBufferSubData(_wGL_ARRAY_BUFFER, 0, buffer); + + if (p1 == GL_QUADS) { + _wglDrawQuadArrays(p2, p3); + triangleDrawn += p3 / 2; + } else { + int drawMode = 0; + switch (p1) { + default: + case GL_TRIANGLES: + drawMode = _wGL_TRIANGLES; + triangleDrawn += p3 / 3; + break; + case GL_TRIANGLE_STRIP: + drawMode = _wGL_TRIANGLE_STRIP; + triangleDrawn += p3 - 2; + break; + case GL_TRIANGLE_FAN: + drawMode = _wGL_TRIANGLE_FAN; + triangleDrawn += p3 - 2; + break; + case GL_LINE_STRIP: + drawMode = _wGL_LINE_STRIP; + triangleDrawn += p3 - 1; + break; + case GL_LINES: + drawMode = _wGL_LINES; + triangleDrawn += p3 / 2; + break; + } + _wglDrawArrays(drawMode, p2, p3); + } + + shader.unuseProgram(); + + } + } + + private static final void _wglDrawQuadArrays(int p2, int p3) { + if (quadsToTrianglesBuffer == null) { + IntBuffer upload = isWebGL ? IntBuffer.wrap(new int[98400 / 2]) + : ByteBuffer.allocateDirect(98400 * 2).order(ByteOrder.nativeOrder()).asIntBuffer(); + for (int i = 0; i < 16384; ++i) { + int v1 = i * 4; + int v2 = i * 4 + 1; + int v3 = i * 4 + 2; + int v4 = i * 4 + 3; + upload.put(v1 | (v2 << 16)); + upload.put(v4 | (v2 << 16)); + upload.put(v3 | (v4 << 16)); + } + upload.flip(); + quadsToTrianglesBuffer = _wglCreateBuffer(); + _wglBindBuffer(_wGL_ELEMENT_ARRAY_BUFFER, quadsToTrianglesBuffer); + _wglBufferData0(_wGL_ELEMENT_ARRAY_BUFFER, upload, _wGL_STATIC_DRAW); + } + if (!currentArray.isQuadBufferBound) { + currentArray.isQuadBufferBound = true; + _wglBindBuffer(_wGL_ELEMENT_ARRAY_BUFFER, quadsToTrianglesBuffer); + } + _wglDrawElements(_wGL_TRIANGLES, p3 * 6 / 4, _wGL_UNSIGNED_SHORT, p2 * 6 / 4); + } + + private static BufferArrayGL occlusion_vao = null; + private static BufferGL occlusion_vbo = null; + private static ProgramGL occlusion_program = null; + private static UniformGL occlusion_matrix_m = null; + private static UniformGL occlusion_matrix_p = null; + + private static final void initializeOcclusionObjects() { + occlusion_vao = _wglCreateVertexArray(); + occlusion_vbo = _wglCreateBuffer(); + + IntBuffer upload = (isWebGL ? IntBuffer.wrap(new int[108]) + : ByteBuffer.allocateDirect(108 << 2).order(ByteOrder.nativeOrder()).asIntBuffer()); + float[] verts = new float[] { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, + 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, + 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, + 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, + 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f }; + for (int i = 0; i < verts.length; i++) { + upload.put(Float.floatToRawIntBits(verts[i])); + } + upload.flip(); + + _wglBindVertexArray(occlusion_vao); + _wglBindBuffer(_wGL_ARRAY_BUFFER, occlusion_vbo); + _wglBufferData0(_wGL_ARRAY_BUFFER, upload, _wGL_STATIC_DRAW); + _wglEnableVertexAttribArray(0); + _wglVertexAttribPointer(0, 3, _wGL_FLOAT, false, 12, 0); + + ShaderGL vert = _wglCreateShader(_wGL_VERTEX_SHADER); + ShaderGL frag = _wglCreateShader(_wGL_FRAGMENT_SHADER); + + String src = fileContents("/glsl/occl.glsl"); + _wglShaderSource(vert, _wgetShaderHeader() + "\n#define CC_VERT\n" + src); + _wglShaderSource(frag, _wgetShaderHeader() + "\n#define CC_FRAG\n" + src); + + _wglCompileShader(vert); + if (!_wglGetShaderCompiled(vert)) + System.err.println(("\n" + _wglGetShaderInfoLog(vert)).replace("\n", "\n[/glsl/occl.glsl][VERT] ") + "\n"); + + _wglCompileShader(frag); + if (!_wglGetShaderCompiled(frag)) + System.err.println(("\n" + _wglGetShaderInfoLog(frag)).replace("\n", "\n[/glsl/occl.glsl][FRAG] ") + "\n"); + + occlusion_program = _wglCreateProgram(); + + _wglAttachShader(occlusion_program, vert); + _wglAttachShader(occlusion_program, frag); + _wglLinkProgram(occlusion_program); + _wglDetachShader(occlusion_program, vert); + _wglDetachShader(occlusion_program, frag); + _wglDeleteShader(vert); + _wglDeleteShader(frag); + + if (!_wglGetProgramLinked(occlusion_program)) + System.err.println( + ("\n\n" + _wglGetProgramInfoLog(occlusion_program)).replace("\n", "\n[/glsl/occl.glsl][LINKER] ")); + + _wglUseProgram(occlusion_program); + occlusion_matrix_m = _wglGetUniformLocation(occlusion_program, "matrix_m"); + occlusion_matrix_p = _wglGetUniformLocation(occlusion_program, "matrix_p"); + + } + + private static final GLObjectMap queryObjs = new GLObjectMap(256); + + public static final int glCreateQuery() { + return queryObjs.register(_wglCreateQuery()); + } + + public static final void glBeginQuery(int obj) { + _wglBeginQuery(_wGL_ANY_SAMPLES_PASSED, queryObjs.get(obj)); + } + + public static final void glDeleteQuery(int obj) { + _wglDeleteQuery(queryObjs.free(obj)); + } + + private static final Matrix4f cachedOcclusionP = (Matrix4f) (new Matrix4f()).setZero(); + private static float[] occlusionModel = new float[16]; + private static float[] occlusionProj = new float[16]; + + public static final void glBindOcclusionBB() { + if (occlusion_vao == null) + initializeOcclusionObjects(); + _wglUseProgram(occlusion_program); + _wglBindVertexArray(occlusion_vao); + if (!cachedOcclusionP.equals(matProjV[matProjPointer])) { + cachedOcclusionP.load(matProjV[matProjPointer]); + cachedOcclusionP.store(occlusionProj); + _wglUniformMat4fv(occlusion_matrix_p, occlusionProj); + } + } + + public static final void glEndOcclusionBB() { + + } + + public static final void glDrawOcclusionBB(float posX, float posY, float posZ, float sizeX, float sizeY, + float sizeZ) { + glPushMatrix(); + glTranslatef(posX - sizeX * 0.01f, posY - sizeY * 0.01f, posZ - sizeZ * 0.01f); + glScalef(sizeX * 1.02f, sizeY * 1.02f, sizeZ * 1.02f); + matModelV[matModelPointer].store(occlusionModel); + _wglUniformMat4fv(occlusion_matrix_m, occlusionModel); + _wglDrawArrays(_wGL_TRIANGLES, 0, 36); + glPopMatrix(); + + } + + public static final void glEndQuery() { + _wglEndQuery(_wGL_ANY_SAMPLES_PASSED); + } + + public static final int glGenTextures() { + return texObjects.register(_wglGenTextures()); + } + + public static final void glTexSubImage2D(int p1, int p2, int p3, int p4, int p5, int p6, int p7, int p8, + ByteBuffer p9) { + int pp1 = 0; + switch (p1) { + default: + case GL_TEXTURE_2D: + pp1 = _wGL_TEXTURE_2D; + break; + // case GL_TEXTURE_3D: pp1 = _wGL_TEXTURE_3D; break; + } + /* + * int pp3 = 0; switch(p7) { default: case GL_RGBA: pp3 = _wGL_RGBA; break; case + * GL_BGRA: pp3 = _wGL_BGRA; break; } + */ + bytesUploaded += p9.remaining(); + _wglTexSubImage2D(pp1, p2, p3, p4, p5, p6, _wGL_RGBA, _wGL_UNSIGNED_BYTE, p9); + } + + public static final void glFogi(int p1, int p2) { + if (p1 == GL_FOG_MODE) { + switch (p2) { + default: + case GL_LINEAR: + fogMode = 1; + break; + case GL_EXP: + fogMode = 2; + break; + } + } + } + + public static final void glFogf(int p1, float p2) { + switch (p1) { + case GL_FOG_START: + fogStart = p2; + break; + case GL_FOG_END: + fogEnd = p2; + break; + case GL_FOG_DENSITY: + fogDensity = p2; + break; + default: + break; + } + } + + public static final void glFog(int p1, FloatBuffer p2) { + if (p1 == GL_FOG_COLOR) { + fogColorR = p2.get(); + fogColorG = p2.get(); + fogColorB = p2.get(); + fogColorA = p2.get(); + } + } + + public static final void glDeleteLists(int p1, int p2) { + for (int i = 0; i < p2; i++) { + DisplayList d = displayListsInitialized.remove(p1 + i); + if (d != null) { + _wglDeleteVertexArray(d.glarray); + _wglDeleteBuffer(d.glbuffer); + } + displayLists.remove(p1 + i); + } + } + + public static final void glMultiTexCoord2f(int p1, float p2, float p3) { + tex0X = p2; + tex0Y = p3; + } + + private static Matrix4f unprojA = new Matrix4f(); + private static Matrix4f unprojB = new Matrix4f(); + private static Vector4f unprojC = new Vector4f(); + + public static final void gluUnProject(float p1, float p2, float p3, FloatBuffer p4, FloatBuffer p5, int[] p6, + FloatBuffer p7) { + unprojA.load(p4); + unprojB.load(p5); + Matrix4f.mul(unprojA, unprojB, unprojB); + unprojB.invert(); + unprojC.set(((p1 - (float) p6[0]) / (float) p6[2]) * 2f - 1f, ((p2 - (float) p6[1]) / (float) p6[3]) * 2f - 1f, + p3, 1.0f); + Matrix4f.transform(unprojB, unprojC, unprojC); + p7.put(unprojC.x / unprojC.w); + p7.put(unprojC.y / unprojC.w); + p7.put(unprojC.z / unprojC.w); + } + + public static final void gluPerspective(float fovy, float aspect, float zNear, float zFar) { + Matrix4f res = getMatrix(); + float cotangent = (float) Math.cos(fovy * toRad * 0.5f) / (float) Math.sin(fovy * toRad * 0.5f); + res.m00 = cotangent / aspect; + res.m01 = 0.0f; + res.m02 = 0.0f; + res.m03 = 0.0f; + res.m10 = 0.0f; + res.m11 = cotangent; + res.m12 = 0.0f; + res.m13 = 0.0f; + res.m20 = 0.0f; + res.m21 = 0.0f; + res.m22 = (zFar + zNear) / (zFar - zNear); + res.m23 = -1.0f; + res.m30 = 0.0f; + res.m31 = 0.0f; + res.m32 = 2.0f * zFar * zNear / (zFar - zNear); + res.m33 = 0.0f; + } + + public static final void gluPerspectiveFlat(float fovy, float aspect, float zNear, float zFar) { + Matrix4f res = getMatrix(); + float cotangent = (float) Math.cos(fovy * toRad * 0.5f) / (float) Math.sin(fovy * toRad * 0.5f); + res.m00 = cotangent / aspect; + res.m01 = 0.0f; + res.m02 = 0.0f; + res.m03 = 0.0f; + res.m10 = 0.0f; + res.m11 = cotangent; + res.m12 = 0.0f; + res.m13 = 0.0f; + res.m20 = 0.0f; + res.m21 = 0.0f; + res.m22 = ((zFar + zNear) / (zFar - zNear)) * 0.001f; + res.m23 = -1.0f; + res.m30 = 0.0f; + res.m31 = 0.0f; + res.m32 = 2.0f * zFar * zNear / (zFar - zNear); + res.m33 = 0.0f; + } + + public static final String gluErrorString(int p1) { + switch (p1) { + case GL_INVALID_ENUM: + return "GL_INVALID_ENUM"; + case GL_INVALID_VALUE: + return "GL_INVALID_VALUE"; + case GL_INVALID_OPERATION: + return "GL_INVALID_OPERATION"; + case GL_OUT_OF_MEMORY: + return "GL_OUT_OF_MEMORY"; + case GL_CONTEXT_LOST_WEBGL: + return "CONTEXT_LOST_WEBGL"; + default: + return "Unknown Error"; + } + } + + public static final void optimize() { + FixedFunctionShader.optimize(); + } + + private static long lastBandwidthReset = 0l; + private static int lastBandwidth = 0; + + public static final int getBitsPerSecond() { + if (System.currentTimeMillis() - lastBandwidthReset > 1000) { + lastBandwidthReset = System.currentTimeMillis(); + lastBandwidth = bytesUploaded * 8; + bytesUploaded = 0; + } + return lastBandwidth; + } + + public static final int getVertexesPerSecond() { + int ret = vertexDrawn; + vertexDrawn = 0; + return ret; + } + + public static final int getTrianglesPerSecond() { + int ret = triangleDrawn; + triangleDrawn = 0; + return ret; + } + +} \ No newline at end of file diff --git a/src/net/PeytonPlayz585/opengl/GL11.java b/src/net/PeytonPlayz585/opengl/GL11.java new file mode 100644 index 0000000..a8fa83a --- /dev/null +++ b/src/net/PeytonPlayz585/opengl/GL11.java @@ -0,0 +1,9 @@ +package net.PeytonPlayz585.opengl; + +public class GL11 extends EaglerAdapterGL30 { + + public static void glPixelStorei(int glUnpackAlignment, int i) { + _wglPixelStorei(glUnpackAlignment, i); + } + +} diff --git a/src/net/PeytonPlayz585/opengl/GL12.java b/src/net/PeytonPlayz585/opengl/GL12.java new file mode 100644 index 0000000..097e82f --- /dev/null +++ b/src/net/PeytonPlayz585/opengl/GL12.java @@ -0,0 +1,5 @@ +package net.PeytonPlayz585.opengl; + +public class GL12 extends GL11 { + +} diff --git a/src/net/PeytonPlayz585/opengl/RealOpenGLEnums.java b/src/net/PeytonPlayz585/opengl/RealOpenGLEnums.java new file mode 100644 index 0000000..c29118b --- /dev/null +++ b/src/net/PeytonPlayz585/opengl/RealOpenGLEnums.java @@ -0,0 +1,2417 @@ +package net.PeytonPlayz585.opengl; + +public class RealOpenGLEnums { + + // Field descriptor #544 I + public static final int GL_ACCUM = 256; + + // Field descriptor #544 I + public static final int GL_LOAD = 257; + + // Field descriptor #544 I + public static final int GL_RETURN = 258; + + // Field descriptor #544 I + public static final int GL_MULT = 259; + + // Field descriptor #544 I + public static final int GL_ADD = 260; + + // Field descriptor #544 I + public static final int GL_NEVER = 512; + + // Field descriptor #544 I + public static final int GL_LESS = 513; + + // Field descriptor #544 I + public static final int GL_EQUAL = 514; + + // Field descriptor #544 I + public static final int GL_LEQUAL = 515; + + // Field descriptor #544 I + public static final int GL_GREATER = 516; + + // Field descriptor #544 I + public static final int GL_NOTEQUAL = 517; + + // Field descriptor #544 I + public static final int GL_GEQUAL = 518; + + // Field descriptor #544 I + public static final int GL_ALWAYS = 519; + + // Field descriptor #544 I + public static final int GL_CURRENT_BIT = 1; + + // Field descriptor #544 I + public static final int GL_POINT_BIT = 2; + + // Field descriptor #544 I + public static final int GL_LINE_BIT = 4; + + // Field descriptor #544 I + public static final int GL_POLYGON_BIT = 8; + + // Field descriptor #544 I + public static final int GL_POLYGON_STIPPLE_BIT = 16; + + // Field descriptor #544 I + public static final int GL_PIXEL_MODE_BIT = 32; + + // Field descriptor #544 I + public static final int GL_LIGHTING_BIT = 64; + + // Field descriptor #544 I + public static final int GL_FOG_BIT = 128; + + // Field descriptor #544 I + public static final int GL_DEPTH_BUFFER_BIT = 256; + + // Field descriptor #544 I + public static final int GL_ACCUM_BUFFER_BIT = 512; + + // Field descriptor #544 I + public static final int GL_STENCIL_BUFFER_BIT = 1024; + + // Field descriptor #544 I + public static final int GL_VIEWPORT_BIT = 2048; + + // Field descriptor #544 I + public static final int GL_TRANSFORM_BIT = 4096; + + // Field descriptor #544 I + public static final int GL_ENABLE_BIT = 8192; + + // Field descriptor #544 I + public static final int GL_COLOR_BUFFER_BIT = 16384; + + // Field descriptor #544 I + public static final int GL_HINT_BIT = 32768; + + // Field descriptor #544 I + public static final int GL_EVAL_BIT = 65536; + + // Field descriptor #544 I + public static final int GL_LIST_BIT = 131072; + + // Field descriptor #544 I + public static final int GL_TEXTURE_BIT = 262144; + + // Field descriptor #544 I + public static final int GL_SCISSOR_BIT = 524288; + + // Field descriptor #544 I + public static final int GL_ALL_ATTRIB_BITS = 1048575; + + // Field descriptor #544 I + public static final int GL_POINTS = 0; + + // Field descriptor #544 I + public static final int GL_LINES = 1; + + // Field descriptor #544 I + public static final int GL_LINE_LOOP = 2; + + // Field descriptor #544 I + public static final int GL_LINE_STRIP = 3; + + // Field descriptor #544 I + public static final int GL_TRIANGLES = 4; + + // Field descriptor #544 I + public static final int GL_TRIANGLE_STRIP = 5; + + // Field descriptor #544 I + public static final int GL_TRIANGLE_FAN = 6; + + // Field descriptor #544 I + public static final int GL_QUADS = 7; + + // Field descriptor #544 I + public static final int GL_QUAD_STRIP = 8; + + // Field descriptor #544 I + public static final int GL_POLYGON = 9; + + // Field descriptor #544 I + public static final int GL_ZERO = 0; + + // Field descriptor #544 I + public static final int GL_ONE = 1; + + // Field descriptor #544 I + public static final int GL_SRC_COLOR = 768; + + // Field descriptor #544 I + public static final int GL_ONE_MINUS_SRC_COLOR = 769; + + // Field descriptor #544 I + public static final int GL_SRC_ALPHA = 770; + + // Field descriptor #544 I + public static final int GL_ONE_MINUS_SRC_ALPHA = 771; + + // Field descriptor #544 I + public static final int GL_DST_ALPHA = 772; + + // Field descriptor #544 I + public static final int GL_ONE_MINUS_DST_ALPHA = 773; + + // Field descriptor #544 I + public static final int GL_DST_COLOR = 774; + + // Field descriptor #544 I + public static final int GL_ONE_MINUS_DST_COLOR = 775; + + // Field descriptor #544 I + public static final int GL_SRC_ALPHA_SATURATE = 776; + + // Field descriptor #544 I + public static final int GL_CONSTANT_COLOR = 32769; + + // Field descriptor #544 I + public static final int GL_ONE_MINUS_CONSTANT_COLOR = 32770; + + // Field descriptor #544 I + public static final int GL_CONSTANT_ALPHA = 32771; + + // Field descriptor #544 I + public static final int GL_ONE_MINUS_CONSTANT_ALPHA = 32772; + + // Field descriptor #544 I + public static final int GL_TRUE = 1; + + // Field descriptor #544 I + public static final int GL_FALSE = 0; + + // Field descriptor #544 I + public static final int GL_CLIP_PLANE0 = 12288; + + // Field descriptor #544 I + public static final int GL_CLIP_PLANE1 = 12289; + + // Field descriptor #544 I + public static final int GL_CLIP_PLANE2 = 12290; + + // Field descriptor #544 I + public static final int GL_CLIP_PLANE3 = 12291; + + // Field descriptor #544 I + public static final int GL_CLIP_PLANE4 = 12292; + + // Field descriptor #544 I + public static final int GL_CLIP_PLANE5 = 12293; + + // Field descriptor #544 I + public static final int GL_BYTE = 5120; + + // Field descriptor #544 I + public static final int GL_UNSIGNED_BYTE = 5121; + + // Field descriptor #544 I + public static final int GL_SHORT = 5122; + + // Field descriptor #544 I + public static final int GL_UNSIGNED_SHORT = 5123; + + // Field descriptor #544 I + public static final int GL_INT = 5124; + + // Field descriptor #544 I + public static final int GL_UNSIGNED_INT = 5125; + + // Field descriptor #544 I + public static final int GL_FLOAT = 5126; + + // Field descriptor #544 I + public static final int GL_2_BYTES = 5127; + + // Field descriptor #544 I + public static final int GL_3_BYTES = 5128; + + // Field descriptor #544 I + public static final int GL_4_BYTES = 5129; + + // Field descriptor #544 I + public static final int GL_DOUBLE = 5130; + + // Field descriptor #544 I + public static final int GL_NONE = 0; + + // Field descriptor #544 I + public static final int GL_FRONT_LEFT = 1024; + + // Field descriptor #544 I + public static final int GL_FRONT_RIGHT = 1025; + + // Field descriptor #544 I + public static final int GL_BACK_LEFT = 1026; + + // Field descriptor #544 I + public static final int GL_BACK_RIGHT = 1027; + + // Field descriptor #544 I + public static final int GL_FRONT = 1028; + + // Field descriptor #544 I + public static final int GL_BACK = 1029; + + // Field descriptor #544 I + public static final int GL_LEFT = 1030; + + // Field descriptor #544 I + public static final int GL_RIGHT = 1031; + + // Field descriptor #544 I + public static final int GL_FRONT_AND_BACK = 1032; + + // Field descriptor #544 I + public static final int GL_AUX0 = 1033; + + // Field descriptor #544 I + public static final int GL_AUX1 = 1034; + + // Field descriptor #544 I + public static final int GL_AUX2 = 1035; + + // Field descriptor #544 I + public static final int GL_AUX3 = 1036; + + // Field descriptor #544 I + public static final int GL_NO_ERROR = 0; + + // Field descriptor #544 I + public static final int GL_INVALID_ENUM = 1280; + + // Field descriptor #544 I + public static final int GL_INVALID_VALUE = 1281; + + // Field descriptor #544 I + public static final int GL_INVALID_OPERATION = 1282; + + // Field descriptor #544 I + public static final int GL_STACK_OVERFLOW = 1283; + + // Field descriptor #544 I + public static final int GL_STACK_UNDERFLOW = 1284; + + // Field descriptor #544 I + public static final int GL_OUT_OF_MEMORY = 1285; + + // Field descriptor #544 I + public static final int GL_2D = 1536; + + // Field descriptor #544 I + public static final int GL_3D = 1537; + + // Field descriptor #544 I + public static final int GL_3D_COLOR = 1538; + + // Field descriptor #544 I + public static final int GL_3D_COLOR_TEXTURE = 1539; + + // Field descriptor #544 I + public static final int GL_4D_COLOR_TEXTURE = 1540; + + // Field descriptor #544 I + public static final int GL_PASS_THROUGH_TOKEN = 1792; + + // Field descriptor #544 I + public static final int GL_POINT_TOKEN = 1793; + + // Field descriptor #544 I + public static final int GL_LINE_TOKEN = 1794; + + // Field descriptor #544 I + public static final int GL_POLYGON_TOKEN = 1795; + + // Field descriptor #544 I + public static final int GL_BITMAP_TOKEN = 1796; + + // Field descriptor #544 I + public static final int GL_DRAW_PIXEL_TOKEN = 1797; + + // Field descriptor #544 I + public static final int GL_COPY_PIXEL_TOKEN = 1798; + + // Field descriptor #544 I + public static final int GL_LINE_RESET_TOKEN = 1799; + + // Field descriptor #544 I + public static final int GL_EXP = 2048; + + // Field descriptor #544 I + public static final int GL_EXP2 = 2049; + + // Field descriptor #544 I + public static final int GL_CW = 2304; + + // Field descriptor #544 I + public static final int GL_CCW = 2305; + + // Field descriptor #544 I + public static final int GL_COEFF = 2560; + + // Field descriptor #544 I + public static final int GL_ORDER = 2561; + + // Field descriptor #544 I + public static final int GL_DOMAIN = 2562; + + // Field descriptor #544 I + public static final int GL_CURRENT_COLOR = 2816; + + // Field descriptor #544 I + public static final int GL_CURRENT_INDEX = 2817; + + // Field descriptor #544 I + public static final int GL_CURRENT_NORMAL = 2818; + + // Field descriptor #544 I + public static final int GL_CURRENT_TEXTURE_COORDS = 2819; + + // Field descriptor #544 I + public static final int GL_CURRENT_RASTER_COLOR = 2820; + + // Field descriptor #544 I + public static final int GL_CURRENT_RASTER_INDEX = 2821; + + // Field descriptor #544 I + public static final int GL_CURRENT_RASTER_TEXTURE_COORDS = 2822; + + // Field descriptor #544 I + public static final int GL_CURRENT_RASTER_POSITION = 2823; + + // Field descriptor #544 I + public static final int GL_CURRENT_RASTER_POSITION_VALID = 2824; + + // Field descriptor #544 I + public static final int GL_CURRENT_RASTER_DISTANCE = 2825; + + // Field descriptor #544 I + public static final int GL_POINT_SMOOTH = 2832; + + // Field descriptor #544 I + public static final int GL_POINT_SIZE = 2833; + + // Field descriptor #544 I + public static final int GL_POINT_SIZE_RANGE = 2834; + + // Field descriptor #544 I + public static final int GL_POINT_SIZE_GRANULARITY = 2835; + + // Field descriptor #544 I + public static final int GL_LINE_SMOOTH = 2848; + + // Field descriptor #544 I + public static final int GL_LINE_WIDTH = 2849; + + // Field descriptor #544 I + public static final int GL_LINE_WIDTH_RANGE = 2850; + + // Field descriptor #544 I + public static final int GL_LINE_WIDTH_GRANULARITY = 2851; + + // Field descriptor #544 I + public static final int GL_LINE_STIPPLE = 2852; + + // Field descriptor #544 I + public static final int GL_LINE_STIPPLE_PATTERN = 2853; + + // Field descriptor #544 I + public static final int GL_LINE_STIPPLE_REPEAT = 2854; + + // Field descriptor #544 I + public static final int GL_LIST_MODE = 2864; + + // Field descriptor #544 I + public static final int GL_MAX_LIST_NESTING = 2865; + + // Field descriptor #544 I + public static final int GL_LIST_BASE = 2866; + + // Field descriptor #544 I + public static final int GL_LIST_INDEX = 2867; + + // Field descriptor #544 I + public static final int GL_POLYGON_MODE = 2880; + + // Field descriptor #544 I + public static final int GL_POLYGON_SMOOTH = 2881; + + // Field descriptor #544 I + public static final int GL_POLYGON_STIPPLE = 2882; + + // Field descriptor #544 I + public static final int GL_EDGE_FLAG = 2883; + + // Field descriptor #544 I + public static final int GL_CULL_FACE = 2884; + + // Field descriptor #544 I + public static final int GL_CULL_FACE_MODE = 2885; + + // Field descriptor #544 I + public static final int GL_FRONT_FACE = 2886; + + // Field descriptor #544 I + public static final int GL_LIGHTING = 2896; + + // Field descriptor #544 I + public static final int GL_LIGHT_MODEL_LOCAL_VIEWER = 2897; + + // Field descriptor #544 I + public static final int GL_LIGHT_MODEL_TWO_SIDE = 2898; + + // Field descriptor #544 I + public static final int GL_LIGHT_MODEL_AMBIENT = 2899; + + // Field descriptor #544 I + public static final int GL_SHADE_MODEL = 2900; + + // Field descriptor #544 I + public static final int GL_COLOR_MATERIAL_FACE = 2901; + + // Field descriptor #544 I + public static final int GL_COLOR_MATERIAL_PARAMETER = 2902; + + // Field descriptor #544 I + public static final int GL_COLOR_MATERIAL = 2903; + + // Field descriptor #544 I + public static final int GL_FOG = 2912; + + // Field descriptor #544 I + public static final int GL_FOG_INDEX = 2913; + + // Field descriptor #544 I + public static final int GL_FOG_DENSITY = 2914; + + // Field descriptor #544 I + public static final int GL_FOG_START = 2915; + + // Field descriptor #544 I + public static final int GL_FOG_END = 2916; + + // Field descriptor #544 I + public static final int GL_FOG_MODE = 2917; + + // Field descriptor #544 I + public static final int GL_FOG_COLOR = 2918; + + // Field descriptor #544 I + public static final int GL_DEPTH_RANGE = 2928; + + // Field descriptor #544 I + public static final int GL_DEPTH_TEST = 2929; + + // Field descriptor #544 I + public static final int GL_DEPTH_WRITEMASK = 2930; + + // Field descriptor #544 I + public static final int GL_DEPTH_CLEAR_VALUE = 2931; + + // Field descriptor #544 I + public static final int GL_DEPTH_FUNC = 2932; + + // Field descriptor #544 I + public static final int GL_ACCUM_CLEAR_VALUE = 2944; + + // Field descriptor #544 I + public static final int GL_STENCIL_TEST = 2960; + + // Field descriptor #544 I + public static final int GL_STENCIL_CLEAR_VALUE = 2961; + + // Field descriptor #544 I + public static final int GL_STENCIL_FUNC = 2962; + + // Field descriptor #544 I + public static final int GL_STENCIL_VALUE_MASK = 2963; + + // Field descriptor #544 I + public static final int GL_STENCIL_FAIL = 2964; + + // Field descriptor #544 I + public static final int GL_STENCIL_PASS_DEPTH_FAIL = 2965; + + // Field descriptor #544 I + public static final int GL_STENCIL_PASS_DEPTH_PASS = 2966; + + // Field descriptor #544 I + public static final int GL_STENCIL_REF = 2967; + + // Field descriptor #544 I + public static final int GL_STENCIL_WRITEMASK = 2968; + + // Field descriptor #544 I + public static final int GL_MATRIX_MODE = 2976; + + // Field descriptor #544 I + public static final int GL_NORMALIZE = 2977; + + // Field descriptor #544 I + public static final int GL_VIEWPORT = 2978; + + // Field descriptor #544 I + public static final int GL_MODELVIEW_STACK_DEPTH = 2979; + + // Field descriptor #544 I + public static final int GL_PROJECTION_STACK_DEPTH = 2980; + + // Field descriptor #544 I + public static final int GL_TEXTURE_STACK_DEPTH = 2981; + + // Field descriptor #544 I + public static final int GL_MODELVIEW_MATRIX = 2982; + + // Field descriptor #544 I + public static final int GL_PROJECTION_MATRIX = 2983; + + // Field descriptor #544 I + public static final int GL_TEXTURE_MATRIX = 2984; + + // Field descriptor #544 I + public static final int GL_ATTRIB_STACK_DEPTH = 2992; + + // Field descriptor #544 I + public static final int GL_CLIENT_ATTRIB_STACK_DEPTH = 2993; + + // Field descriptor #544 I + public static final int GL_ALPHA_TEST = 3008; + + // Field descriptor #544 I + public static final int GL_ALPHA_TEST_FUNC = 3009; + + // Field descriptor #544 I + public static final int GL_ALPHA_TEST_REF = 3010; + + // Field descriptor #544 I + public static final int GL_DITHER = 3024; + + // Field descriptor #544 I + public static final int GL_BLEND_DST = 3040; + + // Field descriptor #544 I + public static final int GL_BLEND_SRC = 3041; + + // Field descriptor #544 I + public static final int GL_BLEND = 3042; + + // Field descriptor #544 I + public static final int GL_LOGIC_OP_MODE = 3056; + + // Field descriptor #544 I + public static final int GL_INDEX_LOGIC_OP = 3057; + + // Field descriptor #544 I + public static final int GL_COLOR_LOGIC_OP = 3058; + + // Field descriptor #544 I + public static final int GL_AUX_BUFFERS = 3072; + + // Field descriptor #544 I + public static final int GL_DRAW_BUFFER = 3073; + + // Field descriptor #544 I + public static final int GL_READ_BUFFER = 3074; + + // Field descriptor #544 I + public static final int GL_SCISSOR_BOX = 3088; + + // Field descriptor #544 I + public static final int GL_SCISSOR_TEST = 3089; + + // Field descriptor #544 I + public static final int GL_INDEX_CLEAR_VALUE = 3104; + + // Field descriptor #544 I + public static final int GL_INDEX_WRITEMASK = 3105; + + // Field descriptor #544 I + public static final int GL_COLOR_CLEAR_VALUE = 3106; + + // Field descriptor #544 I + public static final int GL_COLOR_WRITEMASK = 3107; + + // Field descriptor #544 I + public static final int GL_INDEX_MODE = 3120; + + // Field descriptor #544 I + public static final int GL_RGBA_MODE = 3121; + + // Field descriptor #544 I + public static final int GL_DOUBLEBUFFER = 3122; + + // Field descriptor #544 I + public static final int GL_STEREO = 3123; + + // Field descriptor #544 I + public static final int GL_RENDER_MODE = 3136; + + // Field descriptor #544 I + public static final int GL_PERSPECTIVE_CORRECTION_HINT = 3152; + + // Field descriptor #544 I + public static final int GL_POINT_SMOOTH_HINT = 3153; + + // Field descriptor #544 I + public static final int GL_LINE_SMOOTH_HINT = 3154; + + // Field descriptor #544 I + public static final int GL_POLYGON_SMOOTH_HINT = 3155; + + // Field descriptor #544 I + public static final int GL_FOG_HINT = 3156; + + // Field descriptor #544 I + public static final int GL_TEXTURE_GEN_S = 3168; + + // Field descriptor #544 I + public static final int GL_TEXTURE_GEN_T = 3169; + + // Field descriptor #544 I + public static final int GL_TEXTURE_GEN_R = 3170; + + // Field descriptor #544 I + public static final int GL_TEXTURE_GEN_Q = 3171; + + // Field descriptor #544 I + public static final int GL_PIXEL_MAP_I_TO_I = 3184; + + // Field descriptor #544 I + public static final int GL_PIXEL_MAP_S_TO_S = 3185; + + // Field descriptor #544 I + public static final int GL_PIXEL_MAP_I_TO_R = 3186; + + // Field descriptor #544 I + public static final int GL_PIXEL_MAP_I_TO_G = 3187; + + // Field descriptor #544 I + public static final int GL_PIXEL_MAP_I_TO_B = 3188; + + // Field descriptor #544 I + public static final int GL_PIXEL_MAP_I_TO_A = 3189; + + // Field descriptor #544 I + public static final int GL_PIXEL_MAP_R_TO_R = 3190; + + // Field descriptor #544 I + public static final int GL_PIXEL_MAP_G_TO_G = 3191; + + // Field descriptor #544 I + public static final int GL_PIXEL_MAP_B_TO_B = 3192; + + // Field descriptor #544 I + public static final int GL_PIXEL_MAP_A_TO_A = 3193; + + // Field descriptor #544 I + public static final int GL_PIXEL_MAP_I_TO_I_SIZE = 3248; + + // Field descriptor #544 I + public static final int GL_PIXEL_MAP_S_TO_S_SIZE = 3249; + + // Field descriptor #544 I + public static final int GL_PIXEL_MAP_I_TO_R_SIZE = 3250; + + // Field descriptor #544 I + public static final int GL_PIXEL_MAP_I_TO_G_SIZE = 3251; + + // Field descriptor #544 I + public static final int GL_PIXEL_MAP_I_TO_B_SIZE = 3252; + + // Field descriptor #544 I + public static final int GL_PIXEL_MAP_I_TO_A_SIZE = 3253; + + // Field descriptor #544 I + public static final int GL_PIXEL_MAP_R_TO_R_SIZE = 3254; + + // Field descriptor #544 I + public static final int GL_PIXEL_MAP_G_TO_G_SIZE = 3255; + + // Field descriptor #544 I + public static final int GL_PIXEL_MAP_B_TO_B_SIZE = 3256; + + // Field descriptor #544 I + public static final int GL_PIXEL_MAP_A_TO_A_SIZE = 3257; + + // Field descriptor #544 I + public static final int GL_UNPACK_SWAP_BYTES = 3312; + + // Field descriptor #544 I + public static final int GL_UNPACK_LSB_FIRST = 3313; + + // Field descriptor #544 I + public static final int GL_UNPACK_ROW_LENGTH = 3314; + + // Field descriptor #544 I + public static final int GL_UNPACK_SKIP_ROWS = 3315; + + // Field descriptor #544 I + public static final int GL_UNPACK_SKIP_PIXELS = 3316; + + // Field descriptor #544 I + public static final int GL_UNPACK_ALIGNMENT = 3317; + + // Field descriptor #544 I + public static final int GL_PACK_SWAP_BYTES = 3328; + + // Field descriptor #544 I + public static final int GL_PACK_LSB_FIRST = 3329; + + // Field descriptor #544 I + public static final int GL_PACK_ROW_LENGTH = 3330; + + // Field descriptor #544 I + public static final int GL_PACK_SKIP_ROWS = 3331; + + // Field descriptor #544 I + public static final int GL_PACK_SKIP_PIXELS = 3332; + + // Field descriptor #544 I + public static final int GL_PACK_ALIGNMENT = 3333; + + // Field descriptor #544 I + public static final int GL_MAP_COLOR = 3344; + + // Field descriptor #544 I + public static final int GL_MAP_STENCIL = 3345; + + // Field descriptor #544 I + public static final int GL_INDEX_SHIFT = 3346; + + // Field descriptor #544 I + public static final int GL_INDEX_OFFSET = 3347; + + // Field descriptor #544 I + public static final int GL_RED_SCALE = 3348; + + // Field descriptor #544 I + public static final int GL_RED_BIAS = 3349; + + // Field descriptor #544 I + public static final int GL_ZOOM_X = 3350; + + // Field descriptor #544 I + public static final int GL_ZOOM_Y = 3351; + + // Field descriptor #544 I + public static final int GL_GREEN_SCALE = 3352; + + // Field descriptor #544 I + public static final int GL_GREEN_BIAS = 3353; + + // Field descriptor #544 I + public static final int GL_BLUE_SCALE = 3354; + + // Field descriptor #544 I + public static final int GL_BLUE_BIAS = 3355; + + // Field descriptor #544 I + public static final int GL_ALPHA_SCALE = 3356; + + // Field descriptor #544 I + public static final int GL_ALPHA_BIAS = 3357; + + // Field descriptor #544 I + public static final int GL_DEPTH_SCALE = 3358; + + // Field descriptor #544 I + public static final int GL_DEPTH_BIAS = 3359; + + // Field descriptor #544 I + public static final int GL_MAX_EVAL_ORDER = 3376; + + // Field descriptor #544 I + public static final int GL_MAX_LIGHTS = 3377; + + // Field descriptor #544 I + public static final int GL_MAX_CLIP_PLANES = 3378; + + // Field descriptor #544 I + public static final int GL_MAX_TEXTURE_SIZE = 3379; + + // Field descriptor #544 I + public static final int GL_MAX_PIXEL_MAP_TABLE = 3380; + + // Field descriptor #544 I + public static final int GL_MAX_ATTRIB_STACK_DEPTH = 3381; + + // Field descriptor #544 I + public static final int GL_MAX_MODELVIEW_STACK_DEPTH = 3382; + + // Field descriptor #544 I + public static final int GL_MAX_NAME_STACK_DEPTH = 3383; + + // Field descriptor #544 I + public static final int GL_MAX_PROJECTION_STACK_DEPTH = 3384; + + // Field descriptor #544 I + public static final int GL_MAX_TEXTURE_STACK_DEPTH = 3385; + + // Field descriptor #544 I + public static final int GL_MAX_VIEWPORT_DIMS = 3386; + + // Field descriptor #544 I + public static final int GL_MAX_CLIENT_ATTRIB_STACK_DEPTH = 3387; + + // Field descriptor #544 I + public static final int GL_SUBPIXEL_BITS = 3408; + + // Field descriptor #544 I + public static final int GL_INDEX_BITS = 3409; + + // Field descriptor #544 I + public static final int GL_RED_BITS = 3410; + + // Field descriptor #544 I + public static final int GL_GREEN_BITS = 3411; + + // Field descriptor #544 I + public static final int GL_BLUE_BITS = 3412; + + // Field descriptor #544 I + public static final int GL_ALPHA_BITS = 3413; + + // Field descriptor #544 I + public static final int GL_DEPTH_BITS = 3414; + + // Field descriptor #544 I + public static final int GL_STENCIL_BITS = 3415; + + // Field descriptor #544 I + public static final int GL_ACCUM_RED_BITS = 3416; + + // Field descriptor #544 I + public static final int GL_ACCUM_GREEN_BITS = 3417; + + // Field descriptor #544 I + public static final int GL_ACCUM_BLUE_BITS = 3418; + + // Field descriptor #544 I + public static final int GL_ACCUM_ALPHA_BITS = 3419; + + // Field descriptor #544 I + public static final int GL_NAME_STACK_DEPTH = 3440; + + // Field descriptor #544 I + public static final int GL_AUTO_NORMAL = 3456; + + // Field descriptor #544 I + public static final int GL_MAP1_COLOR_4 = 3472; + + // Field descriptor #544 I + public static final int GL_MAP1_INDEX = 3473; + + // Field descriptor #544 I + public static final int GL_MAP1_NORMAL = 3474; + + // Field descriptor #544 I + public static final int GL_MAP1_TEXTURE_COORD_1 = 3475; + + // Field descriptor #544 I + public static final int GL_MAP1_TEXTURE_COORD_2 = 3476; + + // Field descriptor #544 I + public static final int GL_MAP1_TEXTURE_COORD_3 = 3477; + + // Field descriptor #544 I + public static final int GL_MAP1_TEXTURE_COORD_4 = 3478; + + // Field descriptor #544 I + public static final int GL_MAP1_VERTEX_3 = 3479; + + // Field descriptor #544 I + public static final int GL_MAP1_VERTEX_4 = 3480; + + // Field descriptor #544 I + public static final int GL_MAP2_COLOR_4 = 3504; + + // Field descriptor #544 I + public static final int GL_MAP2_INDEX = 3505; + + // Field descriptor #544 I + public static final int GL_MAP2_NORMAL = 3506; + + // Field descriptor #544 I + public static final int GL_MAP2_TEXTURE_COORD_1 = 3507; + + // Field descriptor #544 I + public static final int GL_MAP2_TEXTURE_COORD_2 = 3508; + + // Field descriptor #544 I + public static final int GL_MAP2_TEXTURE_COORD_3 = 3509; + + // Field descriptor #544 I + public static final int GL_MAP2_TEXTURE_COORD_4 = 3510; + + // Field descriptor #544 I + public static final int GL_MAP2_VERTEX_3 = 3511; + + // Field descriptor #544 I + public static final int GL_MAP2_VERTEX_4 = 3512; + + // Field descriptor #544 I + public static final int GL_MAP1_GRID_DOMAIN = 3536; + + // Field descriptor #544 I + public static final int GL_MAP1_GRID_SEGMENTS = 3537; + + // Field descriptor #544 I + public static final int GL_MAP2_GRID_DOMAIN = 3538; + + // Field descriptor #544 I + public static final int GL_MAP2_GRID_SEGMENTS = 3539; + + // Field descriptor #544 I + public static final int GL_TEXTURE_1D = 3552; + + // Field descriptor #544 I + public static final int GL_TEXTURE_2D = 3553; + + // Field descriptor #544 I + public static final int GL_FEEDBACK_BUFFER_POINTER = 3568; + + // Field descriptor #544 I + public static final int GL_FEEDBACK_BUFFER_SIZE = 3569; + + // Field descriptor #544 I + public static final int GL_FEEDBACK_BUFFER_TYPE = 3570; + + // Field descriptor #544 I + public static final int GL_SELECTION_BUFFER_POINTER = 3571; + + // Field descriptor #544 I + public static final int GL_SELECTION_BUFFER_SIZE = 3572; + + // Field descriptor #544 I + public static final int GL_TEXTURE_WIDTH = 4096; + + // Field descriptor #544 I + public static final int GL_TEXTURE_HEIGHT = 4097; + + // Field descriptor #544 I + public static final int GL_TEXTURE_INTERNAL_FORMAT = 4099; + + // Field descriptor #544 I + public static final int GL_TEXTURE_BORDER_COLOR = 4100; + + // Field descriptor #544 I + public static final int GL_TEXTURE_BORDER = 4101; + + // Field descriptor #544 I + public static final int GL_DONT_CARE = 4352; + + // Field descriptor #544 I + public static final int GL_FASTEST = 4353; + + // Field descriptor #544 I + public static final int GL_NICEST = 4354; + + // Field descriptor #544 I + public static final int GL_LIGHT0 = 16384; + + // Field descriptor #544 I + public static final int GL_LIGHT1 = 16385; + + // Field descriptor #544 I + public static final int GL_LIGHT2 = 16386; + + // Field descriptor #544 I + public static final int GL_LIGHT3 = 16387; + + // Field descriptor #544 I + public static final int GL_LIGHT4 = 16388; + + // Field descriptor #544 I + public static final int GL_LIGHT5 = 16389; + + // Field descriptor #544 I + public static final int GL_LIGHT6 = 16390; + + // Field descriptor #544 I + public static final int GL_LIGHT7 = 16391; + + // Field descriptor #544 I + public static final int GL_AMBIENT = 4608; + + // Field descriptor #544 I + public static final int GL_DIFFUSE = 4609; + + // Field descriptor #544 I + public static final int GL_SPECULAR = 4610; + + // Field descriptor #544 I + public static final int GL_POSITION = 4611; + + // Field descriptor #544 I + public static final int GL_SPOT_DIRECTION = 4612; + + // Field descriptor #544 I + public static final int GL_SPOT_EXPONENT = 4613; + + // Field descriptor #544 I + public static final int GL_SPOT_CUTOFF = 4614; + + // Field descriptor #544 I + public static final int GL_CONSTANT_ATTENUATION = 4615; + + // Field descriptor #544 I + public static final int GL_LINEAR_ATTENUATION = 4616; + + // Field descriptor #544 I + public static final int GL_QUADRATIC_ATTENUATION = 4617; + + // Field descriptor #544 I + public static final int GL_COMPILE = 4864; + + // Field descriptor #544 I + public static final int GL_COMPILE_AND_EXECUTE = 4865; + + // Field descriptor #544 I + public static final int GL_CLEAR = 5376; + + // Field descriptor #544 I + public static final int GL_AND = 5377; + + // Field descriptor #544 I + public static final int GL_AND_REVERSE = 5378; + + // Field descriptor #544 I + public static final int GL_COPY = 5379; + + // Field descriptor #544 I + public static final int GL_AND_INVERTED = 5380; + + // Field descriptor #544 I + public static final int GL_NOOP = 5381; + + // Field descriptor #544 I + public static final int GL_XOR = 5382; + + // Field descriptor #544 I + public static final int GL_OR = 5383; + + // Field descriptor #544 I + public static final int GL_NOR = 5384; + + // Field descriptor #544 I + public static final int GL_EQUIV = 5385; + + // Field descriptor #544 I + public static final int GL_INVERT = 5386; + + // Field descriptor #544 I + public static final int GL_OR_REVERSE = 5387; + + // Field descriptor #544 I + public static final int GL_COPY_INVERTED = 5388; + + // Field descriptor #544 I + public static final int GL_OR_INVERTED = 5389; + + // Field descriptor #544 I + public static final int GL_NAND = 5390; + + // Field descriptor #544 I + public static final int GL_SET = 5391; + + // Field descriptor #544 I + public static final int GL_EMISSION = 5632; + + // Field descriptor #544 I + public static final int GL_SHININESS = 5633; + + // Field descriptor #544 I + public static final int GL_AMBIENT_AND_DIFFUSE = 5634; + + // Field descriptor #544 I + public static final int GL_COLOR_INDEXES = 5635; + + // Field descriptor #544 I + public static final int GL_MODELVIEW = 5888; + + // Field descriptor #544 I + public static final int GL_PROJECTION = 5889; + + // Field descriptor #544 I + public static final int GL_TEXTURE = 5890; + + // Field descriptor #544 I + public static final int GL_COLOR = 6144; + + // Field descriptor #544 I + public static final int GL_DEPTH = 6145; + + // Field descriptor #544 I + public static final int GL_STENCIL = 6146; + + // Field descriptor #544 I + public static final int GL_COLOR_INDEX = 6400; + + // Field descriptor #544 I + public static final int GL_STENCIL_INDEX = 6401; + + // Field descriptor #544 I + public static final int GL_DEPTH_COMPONENT = 6402; + + // Field descriptor #544 I + public static final int GL_RED = 6403; + + // Field descriptor #544 I + public static final int GL_GREEN = 6404; + + // Field descriptor #544 I + public static final int GL_BLUE = 6405; + + // Field descriptor #544 I + public static final int GL_ALPHA = 6406; + + // Field descriptor #544 I + public static final int GL_RGB = 6407; + + // Field descriptor #544 I + public static final int GL_RGBA = 6408; + + // Field descriptor #544 I + public static final int GL_LUMINANCE = 6409; + + // Field descriptor #544 I + public static final int GL_LUMINANCE_ALPHA = 6410; + + // Field descriptor #544 I + public static final int GL_BITMAP = 6656; + + // Field descriptor #544 I + public static final int GL_POINT = 6912; + + // Field descriptor #544 I + public static final int GL_LINE = 6913; + + // Field descriptor #544 I + public static final int GL_FILL = 6914; + + // Field descriptor #544 I + public static final int GL_RENDER = 7168; + + // Field descriptor #544 I + public static final int GL_FEEDBACK = 7169; + + // Field descriptor #544 I + public static final int GL_SELECT = 7170; + + // Field descriptor #544 I + public static final int GL_FLAT = 7424; + + // Field descriptor #544 I + public static final int GL_SMOOTH = 7425; + + // Field descriptor #544 I + public static final int GL_KEEP = 7680; + + // Field descriptor #544 I + public static final int GL_REPLACE = 7681; + + // Field descriptor #544 I + public static final int GL_INCR = 7682; + + // Field descriptor #544 I + public static final int GL_DECR = 7683; + + // Field descriptor #544 I + public static final int GL_VENDOR = 7936; + + // Field descriptor #544 I + public static final int GL_RENDERER = 7937; + + // Field descriptor #544 I + public static final int GL_VERSION = 7938; + + // Field descriptor #544 I + public static final int GL_EXTENSIONS = 7939; + + // Field descriptor #544 I + public static final int GL_S = 8192; + + // Field descriptor #544 I + public static final int GL_T = 8193; + + // Field descriptor #544 I + public static final int GL_R = 8194; + + // Field descriptor #544 I + public static final int GL_Q = 8195; + + // Field descriptor #544 I + public static final int GL_MODULATE = 8448; + + // Field descriptor #544 I + public static final int GL_DECAL = 8449; + + // Field descriptor #544 I + public static final int GL_TEXTURE_ENV_MODE = 8704; + + // Field descriptor #544 I + public static final int GL_TEXTURE_ENV_COLOR = 8705; + + // Field descriptor #544 I + public static final int GL_TEXTURE_ENV = 8960; + + // Field descriptor #544 I + public static final int GL_EYE_LINEAR = 9216; + + // Field descriptor #544 I + public static final int GL_OBJECT_LINEAR = 9217; + + // Field descriptor #544 I + public static final int GL_SPHERE_MAP = 9218; + + // Field descriptor #544 I + public static final int GL_TEXTURE_GEN_MODE = 9472; + + // Field descriptor #544 I + public static final int GL_OBJECT_PLANE = 9473; + + // Field descriptor #544 I + public static final int GL_EYE_PLANE = 9474; + + // Field descriptor #544 I + public static final int GL_NEAREST = 9728; + + // Field descriptor #544 I + public static final int GL_LINEAR = 9729; + + // Field descriptor #544 I + public static final int GL_NEAREST_MIPMAP_NEAREST = 9984; + + // Field descriptor #544 I + public static final int GL_LINEAR_MIPMAP_NEAREST = 9985; + + // Field descriptor #544 I + public static final int GL_NEAREST_MIPMAP_LINEAR = 9986; + + // Field descriptor #544 I + public static final int GL_LINEAR_MIPMAP_LINEAR = 9987; + + // Field descriptor #544 I + public static final int GL_TEXTURE_MAG_FILTER = 10240; + + // Field descriptor #544 I + public static final int GL_TEXTURE_MIN_FILTER = 10241; + + // Field descriptor #544 I + public static final int GL_TEXTURE_WRAP_S = 10242; + + // Field descriptor #544 I + public static final int GL_TEXTURE_WRAP_T = 10243; + + // Field descriptor #544 I + public static final int GL_CLAMP = 10496; + + // Field descriptor #544 I + public static final int GL_REPEAT = 10497; + + // Field descriptor #544 I + public static final int GL_CLIENT_PIXEL_STORE_BIT = 1; + + // Field descriptor #544 I + public static final int GL_CLIENT_VERTEX_ARRAY_BIT = 2; + + // Field descriptor #544 I + public static final int GL_ALL_CLIENT_ATTRIB_BITS = -1; + + // Field descriptor #544 I + public static final int GL_POLYGON_OFFSET_FACTOR = 32824; + + // Field descriptor #544 I + public static final int GL_POLYGON_OFFSET_UNITS = 10752; + + // Field descriptor #544 I + public static final int GL_POLYGON_OFFSET_POINT = 10753; + + // Field descriptor #544 I + public static final int GL_POLYGON_OFFSET_LINE = 10754; + + // Field descriptor #544 I + public static final int GL_POLYGON_OFFSET_FILL = 32823; + + // Field descriptor #544 I + public static final int GL_ALPHA4 = 32827; + + // Field descriptor #544 I + public static final int GL_ALPHA8 = 32828; + + // Field descriptor #544 I + public static final int GL_ALPHA12 = 32829; + + // Field descriptor #544 I + public static final int GL_ALPHA16 = 32830; + + // Field descriptor #544 I + public static final int GL_LUMINANCE4 = 32831; + + // Field descriptor #544 I + public static final int GL_LUMINANCE8 = 32832; + + // Field descriptor #544 I + public static final int GL_LUMINANCE12 = 32833; + + // Field descriptor #544 I + public static final int GL_LUMINANCE16 = 32834; + + // Field descriptor #544 I + public static final int GL_LUMINANCE4_ALPHA4 = 32835; + + // Field descriptor #544 I + public static final int GL_LUMINANCE6_ALPHA2 = 32836; + + // Field descriptor #544 I + public static final int GL_LUMINANCE8_ALPHA8 = 32837; + + // Field descriptor #544 I + public static final int GL_LUMINANCE12_ALPHA4 = 32838; + + // Field descriptor #544 I + public static final int GL_LUMINANCE12_ALPHA12 = 32839; + + // Field descriptor #544 I + public static final int GL_LUMINANCE16_ALPHA16 = 32840; + + // Field descriptor #544 I + public static final int GL_INTENSITY = 32841; + + // Field descriptor #544 I + public static final int GL_INTENSITY4 = 32842; + + // Field descriptor #544 I + public static final int GL_INTENSITY8 = 32843; + + // Field descriptor #544 I + public static final int GL_INTENSITY12 = 32844; + + // Field descriptor #544 I + public static final int GL_INTENSITY16 = 32845; + + // Field descriptor #544 I + public static final int GL_R3_G3_B2 = 10768; + + // Field descriptor #544 I + public static final int GL_RGB4 = 32847; + + // Field descriptor #544 I + public static final int GL_RGB5 = 32848; + + // Field descriptor #544 I + public static final int GL_RGB8 = 32849; + + // Field descriptor #544 I + public static final int GL_RGB10 = 32850; + + // Field descriptor #544 I + public static final int GL_RGB12 = 32851; + + // Field descriptor #544 I + public static final int GL_RGB16 = 32852; + + // Field descriptor #544 I + public static final int GL_RGBA2 = 32853; + + // Field descriptor #544 I + public static final int GL_RGBA4 = 32854; + + // Field descriptor #544 I + public static final int GL_RGB5_A1 = 32855; + + // Field descriptor #544 I + public static final int GL_RGBA8 = 32856; + + // Field descriptor #544 I + public static final int GL_RGB10_A2 = 32857; + + // Field descriptor #544 I + public static final int GL_RGBA12 = 32858; + + // Field descriptor #544 I + public static final int GL_RGBA16 = 32859; + + // Field descriptor #544 I + public static final int GL_TEXTURE_RED_SIZE = 32860; + + // Field descriptor #544 I + public static final int GL_TEXTURE_GREEN_SIZE = 32861; + + // Field descriptor #544 I + public static final int GL_TEXTURE_BLUE_SIZE = 32862; + + // Field descriptor #544 I + public static final int GL_TEXTURE_ALPHA_SIZE = 32863; + + // Field descriptor #544 I + public static final int GL_TEXTURE_LUMINANCE_SIZE = 32864; + + // Field descriptor #544 I + public static final int GL_TEXTURE_INTENSITY_SIZE = 32865; + + // Field descriptor #544 I + public static final int GL_PROXY_TEXTURE_1D = 32867; + + // Field descriptor #544 I + public static final int GL_PROXY_TEXTURE_2D = 32868; + + // Field descriptor #544 I + public static final int GL_TEXTURE_PRIORITY = 32870; + + // Field descriptor #544 I + public static final int GL_TEXTURE_RESIDENT = 32871; + + // Field descriptor #544 I + public static final int GL_TEXTURE_BINDING_1D = 32872; + + // Field descriptor #544 I + public static final int GL_TEXTURE_BINDING_2D = 32873; + + // Field descriptor #544 I + public static final int GL_VERTEX_ARRAY = 32884; + + // Field descriptor #544 I + public static final int GL_NORMAL_ARRAY = 32885; + + // Field descriptor #544 I + public static final int GL_COLOR_ARRAY = 32886; + + // Field descriptor #544 I + public static final int GL_INDEX_ARRAY = 32887; + + // Field descriptor #544 I + public static final int GL_TEXTURE_COORD_ARRAY = 32888; + + // Field descriptor #544 I + public static final int GL_EDGE_FLAG_ARRAY = 32889; + + // Field descriptor #544 I + public static final int GL_VERTEX_ARRAY_SIZE = 32890; + + // Field descriptor #544 I + public static final int GL_VERTEX_ARRAY_TYPE = 32891; + + // Field descriptor #544 I + public static final int GL_VERTEX_ARRAY_STRIDE = 32892; + + // Field descriptor #544 I + public static final int GL_NORMAL_ARRAY_TYPE = 32894; + + // Field descriptor #544 I + public static final int GL_NORMAL_ARRAY_STRIDE = 32895; + + // Field descriptor #544 I + public static final int GL_COLOR_ARRAY_SIZE = 32897; + + // Field descriptor #544 I + public static final int GL_COLOR_ARRAY_TYPE = 32898; + + // Field descriptor #544 I + public static final int GL_COLOR_ARRAY_STRIDE = 32899; + + // Field descriptor #544 I + public static final int GL_INDEX_ARRAY_TYPE = 32901; + + // Field descriptor #544 I + public static final int GL_INDEX_ARRAY_STRIDE = 32902; + + // Field descriptor #544 I + public static final int GL_TEXTURE_COORD_ARRAY_SIZE = 32904; + + // Field descriptor #544 I + public static final int GL_TEXTURE_COORD_ARRAY_TYPE = 32905; + + // Field descriptor #544 I + public static final int GL_TEXTURE_COORD_ARRAY_STRIDE = 32906; + + // Field descriptor #544 I + public static final int GL_EDGE_FLAG_ARRAY_STRIDE = 32908; + + // Field descriptor #544 I + public static final int GL_VERTEX_ARRAY_POINTER = 32910; + + // Field descriptor #544 I + public static final int GL_NORMAL_ARRAY_POINTER = 32911; + + // Field descriptor #544 I + public static final int GL_COLOR_ARRAY_POINTER = 32912; + + // Field descriptor #544 I + public static final int GL_INDEX_ARRAY_POINTER = 32913; + + // Field descriptor #544 I + public static final int GL_TEXTURE_COORD_ARRAY_POINTER = 32914; + + // Field descriptor #544 I + public static final int GL_EDGE_FLAG_ARRAY_POINTER = 32915; + + // Field descriptor #544 I + public static final int GL_V2F = 10784; + + // Field descriptor #544 I + public static final int GL_V3F = 10785; + + // Field descriptor #544 I + public static final int GL_C4UB_V2F = 10786; + + // Field descriptor #544 I + public static final int GL_C4UB_V3F = 10787; + + // Field descriptor #544 I + public static final int GL_C3F_V3F = 10788; + + // Field descriptor #544 I + public static final int GL_N3F_V3F = 10789; + + // Field descriptor #544 I + public static final int GL_C4F_N3F_V3F = 10790; + + // Field descriptor #544 I + public static final int GL_T2F_V3F = 10791; + + // Field descriptor #544 I + public static final int GL_T4F_V4F = 10792; + + // Field descriptor #544 I + public static final int GL_T2F_C4UB_V3F = 10793; + + // Field descriptor #544 I + public static final int GL_T2F_C3F_V3F = 10794; + + // Field descriptor #544 I + public static final int GL_T2F_N3F_V3F = 10795; + + // Field descriptor #544 I + public static final int GL_T2F_C4F_N3F_V3F = 10796; + + // Field descriptor #544 I + public static final int GL_T4F_C4F_N3F_V4F = 10797; + + // Field descriptor #544 I + public static final int GL_LOGIC_OP = 3057; + + // Field descriptor #544 I + public static final int GL_TEXTURE_COMPONENTS = 4099; + + // Field descriptor #45 I + public static final int GL_TEXTURE_BINDING_3D = 32874; + + // Field descriptor #45 I + public static final int GL_PACK_SKIP_IMAGES = 32875; + + // Field descriptor #45 I + public static final int GL_PACK_IMAGE_HEIGHT = 32876; + + // Field descriptor #45 I + public static final int GL_UNPACK_SKIP_IMAGES = 32877; + + // Field descriptor #45 I + public static final int GL_UNPACK_IMAGE_HEIGHT = 32878; + + // Field descriptor #45 I + public static final int GL_TEXTURE_3D = 32879; + + // Field descriptor #45 I + public static final int GL_PROXY_TEXTURE_3D = 32880; + + // Field descriptor #45 I + public static final int GL_TEXTURE_DEPTH = 32881; + + // Field descriptor #45 I + public static final int GL_TEXTURE_WRAP_R = 32882; + + // Field descriptor #45 I + public static final int GL_MAX_3D_TEXTURE_SIZE = 32883; + + // Field descriptor #45 I + public static final int GL_BGR = 32992; + + // Field descriptor #45 I + public static final int GL_BGRA = 32993; + + // Field descriptor #45 I + public static final int GL_UNSIGNED_BYTE_3_3_2 = 32818; + + // Field descriptor #45 I + public static final int GL_UNSIGNED_BYTE_2_3_3_REV = 33634; + + // Field descriptor #45 I + public static final int GL_UNSIGNED_SHORT_5_6_5 = 33635; + + // Field descriptor #45 I + public static final int GL_UNSIGNED_SHORT_5_6_5_REV = 33636; + + // Field descriptor #45 I + public static final int GL_UNSIGNED_SHORT_4_4_4_4 = 32819; + + // Field descriptor #45 I + public static final int GL_UNSIGNED_SHORT_4_4_4_4_REV = 33637; + + // Field descriptor #45 I + public static final int GL_UNSIGNED_SHORT_5_5_5_1 = 32820; + + // Field descriptor #45 I + public static final int GL_UNSIGNED_SHORT_1_5_5_5_REV = 33638; + + // Field descriptor #45 I + public static final int GL_UNSIGNED_INT_8_8_8_8 = 32821; + + // Field descriptor #45 I + public static final int GL_UNSIGNED_INT_8_8_8_8_REV = 33639; + + // Field descriptor #45 I + public static final int GL_UNSIGNED_INT_10_10_10_2 = 32822; + + // Field descriptor #45 I + public static final int GL_UNSIGNED_INT_2_10_10_10_REV = 33640; + + // Field descriptor #45 I + public static final int GL_RESCALE_NORMAL = 32826; + + // Field descriptor #45 I + public static final int GL_LIGHT_MODEL_COLOR_CONTROL = 33272; + + // Field descriptor #45 I + public static final int GL_SINGLE_COLOR = 33273; + + // Field descriptor #45 I + public static final int GL_SEPARATE_SPECULAR_COLOR = 33274; + + // Field descriptor #45 I + public static final int GL_CLAMP_TO_EDGE = 33071; + + // Field descriptor #45 I + public static final int GL_TEXTURE_MIN_LOD = 33082; + + // Field descriptor #45 I + public static final int GL_TEXTURE_MAX_LOD = 33083; + + // Field descriptor #45 I + public static final int GL_TEXTURE_BASE_LEVEL = 33084; + + // Field descriptor #45 I + public static final int GL_TEXTURE_MAX_LEVEL = 33085; + + // Field descriptor #45 I + public static final int GL_MAX_ELEMENTS_VERTICES = 33000; + + // Field descriptor #45 I + public static final int GL_MAX_ELEMENTS_INDICES = 33001; + + // Field descriptor #45 I + public static final int GL_ALIASED_POINT_SIZE_RANGE = 33901; + + // Field descriptor #45 I + public static final int GL_ALIASED_LINE_WIDTH_RANGE = 33902; + + // Field descriptor #45 I + public static final int GL_SMOOTH_POINT_SIZE_RANGE = 2834; + + // Field descriptor #45 I + public static final int GL_SMOOTH_POINT_SIZE_GRANULARITY = 2835; + + // Field descriptor #45 I + public static final int GL_SMOOTH_LINE_WIDTH_RANGE = 2850; + + // Field descriptor #45 I + public static final int GL_SMOOTH_LINE_WIDTH_GRANULARITY = 2851; + + // Field descriptor #76 I + public static final int GL_TEXTURE0 = 33984; + + // Field descriptor #76 I + public static final int GL_TEXTURE1 = 33985; + + // Field descriptor #76 I + public static final int GL_TEXTURE2 = 33986; + + // Field descriptor #76 I + public static final int GL_TEXTURE3 = 33987; + + // Field descriptor #76 I + public static final int GL_TEXTURE4 = 33988; + + // Field descriptor #76 I + public static final int GL_TEXTURE5 = 33989; + + // Field descriptor #76 I + public static final int GL_TEXTURE6 = 33990; + + // Field descriptor #76 I + public static final int GL_TEXTURE7 = 33991; + + // Field descriptor #76 I + public static final int GL_TEXTURE8 = 33992; + + // Field descriptor #76 I + public static final int GL_TEXTURE9 = 33993; + + // Field descriptor #76 I + public static final int GL_TEXTURE10 = 33994; + + // Field descriptor #76 I + public static final int GL_TEXTURE11 = 33995; + + // Field descriptor #76 I + public static final int GL_TEXTURE12 = 33996; + + // Field descriptor #76 I + public static final int GL_TEXTURE13 = 33997; + + // Field descriptor #76 I + public static final int GL_TEXTURE14 = 33998; + + // Field descriptor #76 I + public static final int GL_TEXTURE15 = 33999; + + // Field descriptor #76 I + public static final int GL_TEXTURE16 = 34000; + + // Field descriptor #76 I + public static final int GL_TEXTURE17 = 34001; + + // Field descriptor #76 I + public static final int GL_TEXTURE18 = 34002; + + // Field descriptor #76 I + public static final int GL_TEXTURE19 = 34003; + + // Field descriptor #76 I + public static final int GL_TEXTURE20 = 34004; + + // Field descriptor #76 I + public static final int GL_TEXTURE21 = 34005; + + // Field descriptor #76 I + public static final int GL_TEXTURE22 = 34006; + + // Field descriptor #76 I + public static final int GL_TEXTURE23 = 34007; + + // Field descriptor #76 I + public static final int GL_TEXTURE24 = 34008; + + // Field descriptor #76 I + public static final int GL_TEXTURE25 = 34009; + + // Field descriptor #76 I + public static final int GL_TEXTURE26 = 34010; + + // Field descriptor #76 I + public static final int GL_TEXTURE27 = 34011; + + // Field descriptor #76 I + public static final int GL_TEXTURE28 = 34012; + + // Field descriptor #76 I + public static final int GL_TEXTURE29 = 34013; + + // Field descriptor #76 I + public static final int GL_TEXTURE30 = 34014; + + // Field descriptor #76 I + public static final int GL_TEXTURE31 = 34015; + + // Field descriptor #76 I + public static final int GL_ACTIVE_TEXTURE = 34016; + + // Field descriptor #76 I + public static final int GL_CLIENT_ACTIVE_TEXTURE = 34017; + + // Field descriptor #76 I + public static final int GL_MAX_TEXTURE_UNITS = 34018; + + // Field descriptor #76 I + public static final int GL_NORMAL_MAP = 34065; + + // Field descriptor #76 I + public static final int GL_REFLECTION_MAP = 34066; + + // Field descriptor #76 I + public static final int GL_TEXTURE_CUBE_MAP = 34067; + + // Field descriptor #76 I + public static final int GL_TEXTURE_BINDING_CUBE_MAP = 34068; + + // Field descriptor #76 I + public static final int GL_TEXTURE_CUBE_MAP_POSITIVE_X = 34069; + + // Field descriptor #76 I + public static final int GL_TEXTURE_CUBE_MAP_NEGATIVE_X = 34070; + + // Field descriptor #76 I + public static final int GL_TEXTURE_CUBE_MAP_POSITIVE_Y = 34071; + + // Field descriptor #76 I + public static final int GL_TEXTURE_CUBE_MAP_NEGATIVE_Y = 34072; + + // Field descriptor #76 I + public static final int GL_TEXTURE_CUBE_MAP_POSITIVE_Z = 34073; + + // Field descriptor #76 I + public static final int GL_TEXTURE_CUBE_MAP_NEGATIVE_Z = 34074; + + // Field descriptor #76 I + public static final int GL_PROXY_TEXTURE_CUBE_MAP = 34075; + + // Field descriptor #76 I + public static final int GL_MAX_CUBE_MAP_TEXTURE_SIZE = 34076; + + // Field descriptor #76 I + public static final int GL_COMPRESSED_ALPHA = 34025; + + // Field descriptor #76 I + public static final int GL_COMPRESSED_LUMINANCE = 34026; + + // Field descriptor #76 I + public static final int GL_COMPRESSED_LUMINANCE_ALPHA = 34027; + + // Field descriptor #76 I + public static final int GL_COMPRESSED_INTENSITY = 34028; + + // Field descriptor #76 I + public static final int GL_COMPRESSED_RGB = 34029; + + // Field descriptor #76 I + public static final int GL_COMPRESSED_RGBA = 34030; + + // Field descriptor #76 I + public static final int GL_TEXTURE_COMPRESSION_HINT = 34031; + + // Field descriptor #76 I + public static final int GL_TEXTURE_COMPRESSED_IMAGE_SIZE = 34464; + + // Field descriptor #76 I + public static final int GL_TEXTURE_COMPRESSED = 34465; + + // Field descriptor #76 I + public static final int GL_NUM_COMPRESSED_TEXTURE_FORMATS = 34466; + + // Field descriptor #76 I + public static final int GL_COMPRESSED_TEXTURE_FORMATS = 34467; + + // Field descriptor #76 I + public static final int GL_MULTISAMPLE = 32925; + + // Field descriptor #76 I + public static final int GL_SAMPLE_ALPHA_TO_COVERAGE = 32926; + + // Field descriptor #76 I + public static final int GL_SAMPLE_ALPHA_TO_ONE = 32927; + + // Field descriptor #76 I + public static final int GL_SAMPLE_COVERAGE = 32928; + + // Field descriptor #76 I + public static final int GL_SAMPLE_BUFFERS = 32936; + + // Field descriptor #76 I + public static final int GL_SAMPLES = 32937; + + // Field descriptor #76 I + public static final int GL_SAMPLE_COVERAGE_VALUE = 32938; + + // Field descriptor #76 I + public static final int GL_SAMPLE_COVERAGE_INVERT = 32939; + + // Field descriptor #76 I + public static final int GL_MULTISAMPLE_BIT = 536870912; + + // Field descriptor #76 I + public static final int GL_TRANSPOSE_MODELVIEW_MATRIX = 34019; + + // Field descriptor #76 I + public static final int GL_TRANSPOSE_PROJECTION_MATRIX = 34020; + + // Field descriptor #76 I + public static final int GL_TRANSPOSE_TEXTURE_MATRIX = 34021; + + // Field descriptor #76 I + public static final int GL_TRANSPOSE_COLOR_MATRIX = 34022; + + // Field descriptor #76 I + public static final int GL_COMBINE = 34160; + + // Field descriptor #76 I + public static final int GL_COMBINE_RGB = 34161; + + // Field descriptor #76 I + public static final int GL_COMBINE_ALPHA = 34162; + + // Field descriptor #76 I + public static final int GL_SOURCE0_RGB = 34176; + + // Field descriptor #76 I + public static final int GL_SOURCE1_RGB = 34177; + + // Field descriptor #76 I + public static final int GL_SOURCE2_RGB = 34178; + + // Field descriptor #76 I + public static final int GL_SOURCE0_ALPHA = 34184; + + // Field descriptor #76 I + public static final int GL_SOURCE1_ALPHA = 34185; + + // Field descriptor #76 I + public static final int GL_SOURCE2_ALPHA = 34186; + + // Field descriptor #76 I + public static final int GL_OPERAND0_RGB = 34192; + + // Field descriptor #76 I + public static final int GL_OPERAND1_RGB = 34193; + + // Field descriptor #76 I + public static final int GL_OPERAND2_RGB = 34194; + + // Field descriptor #76 I + public static final int GL_OPERAND0_ALPHA = 34200; + + // Field descriptor #76 I + public static final int GL_OPERAND1_ALPHA = 34201; + + // Field descriptor #76 I + public static final int GL_OPERAND2_ALPHA = 34202; + + // Field descriptor #76 I + public static final int GL_RGB_SCALE = 34163; + + // Field descriptor #76 I + public static final int GL_ADD_SIGNED = 34164; + + // Field descriptor #76 I + public static final int GL_INTERPOLATE = 34165; + + // Field descriptor #76 I + public static final int GL_SUBTRACT = 34023; + + // Field descriptor #76 I + public static final int GL_CONSTANT = 34166; + + // Field descriptor #76 I + public static final int GL_PRIMARY_COLOR = 34167; + + // Field descriptor #76 I + public static final int GL_PREVIOUS = 34168; + + // Field descriptor #76 I + public static final int GL_DOT3_RGB = 34478; + + // Field descriptor #76 I + public static final int GL_DOT3_RGBA = 34479; + + // Field descriptor #76 I + public static final int GL_CLAMP_TO_BORDER = 33069; + + // Field descriptor #71 I + public static final int GL_ARRAY_BUFFER = 34962; + + // Field descriptor #71 I + public static final int GL_ELEMENT_ARRAY_BUFFER = 34963; + + // Field descriptor #71 I + public static final int GL_ARRAY_BUFFER_BINDING = 34964; + + // Field descriptor #71 I + public static final int GL_ELEMENT_ARRAY_BUFFER_BINDING = 34965; + + // Field descriptor #71 I + public static final int GL_VERTEX_ARRAY_BUFFER_BINDING = 34966; + + // Field descriptor #71 I + public static final int GL_NORMAL_ARRAY_BUFFER_BINDING = 34967; + + // Field descriptor #71 I + public static final int GL_COLOR_ARRAY_BUFFER_BINDING = 34968; + + // Field descriptor #71 I + public static final int GL_INDEX_ARRAY_BUFFER_BINDING = 34969; + + // Field descriptor #71 I + public static final int GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING = 34970; + + // Field descriptor #71 I + public static final int GL_EDGE_FLAG_ARRAY_BUFFER_BINDING = 34971; + + // Field descriptor #71 I + public static final int GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING = 34972; + + // Field descriptor #71 I + public static final int GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING = 34973; + + // Field descriptor #71 I + public static final int GL_WEIGHT_ARRAY_BUFFER_BINDING = 34974; + + // Field descriptor #71 I + public static final int GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING = 34975; + + // Field descriptor #71 I + public static final int GL_STREAM_DRAW = 35040; + + // Field descriptor #71 I + public static final int GL_STREAM_READ = 35041; + + // Field descriptor #71 I + public static final int GL_STREAM_COPY = 35042; + + // Field descriptor #71 I + public static final int GL_STATIC_DRAW = 35044; + + // Field descriptor #71 I + public static final int GL_STATIC_READ = 35045; + + // Field descriptor #71 I + public static final int GL_STATIC_COPY = 35046; + + // Field descriptor #71 I + public static final int GL_DYNAMIC_DRAW = 35048; + + // Field descriptor #71 I + public static final int GL_DYNAMIC_READ = 35049; + + // Field descriptor #71 I + public static final int GL_DYNAMIC_COPY = 35050; + + // Field descriptor #71 I + public static final int GL_READ_ONLY = 35000; + + // Field descriptor #71 I + public static final int GL_WRITE_ONLY = 35001; + + // Field descriptor #71 I + public static final int GL_READ_WRITE = 35002; + + // Field descriptor #71 I + public static final int GL_BUFFER_SIZE = 34660; + + // Field descriptor #71 I + public static final int GL_BUFFER_USAGE = 34661; + + // Field descriptor #71 I + public static final int GL_BUFFER_ACCESS = 35003; + + // Field descriptor #71 I + public static final int GL_BUFFER_MAPPED = 35004; + + // Field descriptor #71 I + public static final int GL_BUFFER_MAP_POINTER = 35005; + + // Field descriptor #71 I + public static final int GL_FOG_COORD_SRC = 33872; + + // Field descriptor #71 I + public static final int GL_FOG_COORD = 33873; + + // Field descriptor #71 I + public static final int GL_CURRENT_FOG_COORD = 33875; + + // Field descriptor #71 I + public static final int GL_FOG_COORD_ARRAY_TYPE = 33876; + + // Field descriptor #71 I + public static final int GL_FOG_COORD_ARRAY_STRIDE = 33877; + + // Field descriptor #71 I + public static final int GL_FOG_COORD_ARRAY_POINTER = 33878; + + // Field descriptor #71 I + public static final int GL_FOG_COORD_ARRAY = 33879; + + // Field descriptor #71 I + public static final int GL_FOG_COORD_ARRAY_BUFFER_BINDING = 34973; + + // Field descriptor #71 I + public static final int GL_SRC0_RGB = 34176; + + // Field descriptor #71 I + public static final int GL_SRC1_RGB = 34177; + + // Field descriptor #71 I + public static final int GL_SRC2_RGB = 34178; + + // Field descriptor #71 I + public static final int GL_SRC0_ALPHA = 34184; + + // Field descriptor #71 I + public static final int GL_SRC1_ALPHA = 34185; + + // Field descriptor #71 I + public static final int GL_SRC2_ALPHA = 34186; + + // Field descriptor #71 I + public static final int GL_SAMPLES_PASSED = 35092; + + // Field descriptor #71 I + public static final int GL_QUERY_COUNTER_BITS = 34916; + + // Field descriptor #71 I + public static final int GL_CURRENT_QUERY = 34917; + + // Field descriptor #71 I + public static final int GL_QUERY_RESULT = 34918; + + // Field descriptor #71 I + public static final int GL_QUERY_RESULT_AVAILABLE = 34919; + + // Field descriptor #194 I + public static final int GL_SHADING_LANGUAGE_VERSION = 35724; + + // Field descriptor #194 I + public static final int GL_CURRENT_PROGRAM = 35725; + + // Field descriptor #194 I + public static final int GL_SHADER_TYPE = 35663; + + // Field descriptor #194 I + public static final int GL_DELETE_STATUS = 35712; + + // Field descriptor #194 I + public static final int GL_COMPILE_STATUS = 35713; + + // Field descriptor #194 I + public static final int GL_LINK_STATUS = 35714; + + // Field descriptor #194 I + public static final int GL_VALIDATE_STATUS = 35715; + + // Field descriptor #194 I + public static final int GL_INFO_LOG_LENGTH = 35716; + + // Field descriptor #194 I + public static final int GL_ATTACHED_SHADERS = 35717; + + // Field descriptor #194 I + public static final int GL_ACTIVE_UNIFORMS = 35718; + + // Field descriptor #194 I + public static final int GL_ACTIVE_UNIFORM_MAX_LENGTH = 35719; + + // Field descriptor #194 I + public static final int GL_ACTIVE_ATTRIBUTES = 35721; + + // Field descriptor #194 I + public static final int GL_ACTIVE_ATTRIBUTE_MAX_LENGTH = 35722; + + // Field descriptor #194 I + public static final int GL_SHADER_SOURCE_LENGTH = 35720; + + // Field descriptor #194 I + public static final int GL_SHADER_OBJECT = 35656; + + // Field descriptor #194 I + public static final int GL_FLOAT_VEC2 = 35664; + + // Field descriptor #194 I + public static final int GL_FLOAT_VEC3 = 35665; + + // Field descriptor #194 I + public static final int GL_FLOAT_VEC4 = 35666; + + // Field descriptor #194 I + public static final int GL_INT_VEC2 = 35667; + + // Field descriptor #194 I + public static final int GL_INT_VEC3 = 35668; + + // Field descriptor #194 I + public static final int GL_INT_VEC4 = 35669; + + // Field descriptor #194 I + public static final int GL_BOOL = 35670; + + // Field descriptor #194 I + public static final int GL_BOOL_VEC2 = 35671; + + // Field descriptor #194 I + public static final int GL_BOOL_VEC3 = 35672; + + // Field descriptor #194 I + public static final int GL_BOOL_VEC4 = 35673; + + // Field descriptor #194 I + public static final int GL_FLOAT_MAT2 = 35674; + + // Field descriptor #194 I + public static final int GL_FLOAT_MAT3 = 35675; + + // Field descriptor #194 I + public static final int GL_FLOAT_MAT4 = 35676; + + // Field descriptor #194 I + public static final int GL_SAMPLER_1D = 35677; + + // Field descriptor #194 I + public static final int GL_SAMPLER_2D = 35678; + + // Field descriptor #194 I + public static final int GL_SAMPLER_3D = 35679; + + // Field descriptor #194 I + public static final int GL_SAMPLER_CUBE = 35680; + + // Field descriptor #194 I + public static final int GL_SAMPLER_1D_SHADOW = 35681; + + // Field descriptor #194 I + public static final int GL_SAMPLER_2D_SHADOW = 35682; + + // Field descriptor #194 I + public static final int GL_VERTEX_SHADER = 35633; + + // Field descriptor #194 I + public static final int GL_MAX_VERTEX_UNIFORM_COMPONENTS = 35658; + + // Field descriptor #194 I + public static final int GL_MAX_VARYING_FLOATS = 35659; + + // Field descriptor #194 I + public static final int GL_MAX_VERTEX_ATTRIBS = 34921; + + // Field descriptor #194 I + public static final int GL_MAX_TEXTURE_IMAGE_UNITS = 34930; + + // Field descriptor #194 I + public static final int GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS = 35660; + + // Field descriptor #194 I + public static final int GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS = 35661; + + // Field descriptor #194 I + public static final int GL_MAX_TEXTURE_COORDS = 34929; + + // Field descriptor #194 I + public static final int GL_VERTEX_PROGRAM_POINT_SIZE = 34370; + + // Field descriptor #194 I + public static final int GL_VERTEX_PROGRAM_TWO_SIDE = 34371; + + // Field descriptor #194 I + public static final int GL_VERTEX_ATTRIB_ARRAY_ENABLED = 34338; + + // Field descriptor #194 I + public static final int GL_VERTEX_ATTRIB_ARRAY_SIZE = 34339; + + // Field descriptor #194 I + public static final int GL_VERTEX_ATTRIB_ARRAY_STRIDE = 34340; + + // Field descriptor #194 I + public static final int GL_VERTEX_ATTRIB_ARRAY_TYPE = 34341; + + // Field descriptor #194 I + public static final int GL_VERTEX_ATTRIB_ARRAY_NORMALIZED = 34922; + + // Field descriptor #194 I + public static final int GL_CURRENT_VERTEX_ATTRIB = 34342; + + // Field descriptor #194 I + public static final int GL_VERTEX_ATTRIB_ARRAY_POINTER = 34373; + + // Field descriptor #194 I + public static final int GL_FRAGMENT_SHADER = 35632; + + // Field descriptor #194 I + public static final int GL_MAX_FRAGMENT_UNIFORM_COMPONENTS = 35657; + + // Field descriptor #194 I + public static final int GL_FRAGMENT_SHADER_DERIVATIVE_HINT = 35723; + + // Field descriptor #194 I + public static final int GL_MAX_DRAW_BUFFERS = 34852; + + // Field descriptor #194 I + public static final int GL_DRAW_BUFFER0 = 34853; + + // Field descriptor #194 I + public static final int GL_DRAW_BUFFER1 = 34854; + + // Field descriptor #194 I + public static final int GL_DRAW_BUFFER2 = 34855; + + // Field descriptor #194 I + public static final int GL_DRAW_BUFFER3 = 34856; + + // Field descriptor #194 I + public static final int GL_DRAW_BUFFER4 = 34857; + + // Field descriptor #194 I + public static final int GL_DRAW_BUFFER5 = 34858; + + // Field descriptor #194 I + public static final int GL_DRAW_BUFFER6 = 34859; + + // Field descriptor #194 I + public static final int GL_DRAW_BUFFER7 = 34860; + + // Field descriptor #194 I + public static final int GL_DRAW_BUFFER8 = 34861; + + // Field descriptor #194 I + public static final int GL_DRAW_BUFFER9 = 34862; + + // Field descriptor #194 I + public static final int GL_DRAW_BUFFER10 = 34863; + + // Field descriptor #194 I + public static final int GL_DRAW_BUFFER11 = 34864; + + // Field descriptor #194 I + public static final int GL_DRAW_BUFFER12 = 34865; + + // Field descriptor #194 I + public static final int GL_DRAW_BUFFER13 = 34866; + + // Field descriptor #194 I + public static final int GL_DRAW_BUFFER14 = 34867; + + // Field descriptor #194 I + public static final int GL_DRAW_BUFFER15 = 34868; + + // Field descriptor #194 I + public static final int GL_POINT_SPRITE = 34913; + + // Field descriptor #194 I + public static final int GL_COORD_REPLACE = 34914; + + // Field descriptor #194 I + public static final int GL_POINT_SPRITE_COORD_ORIGIN = 36000; + + // Field descriptor #194 I + public static final int GL_LOWER_LEFT = 36001; + + // Field descriptor #194 I + public static final int GL_UPPER_LEFT = 36002; + + // Field descriptor #194 I + public static final int GL_STENCIL_BACK_FUNC = 34816; + + // Field descriptor #194 I + public static final int GL_STENCIL_BACK_FAIL = 34817; + + // Field descriptor #194 I + public static final int GL_STENCIL_BACK_PASS_DEPTH_FAIL = 34818; + + // Field descriptor #194 I + public static final int GL_STENCIL_BACK_PASS_DEPTH_PASS = 34819; + + // Field descriptor #194 I + public static final int GL_STENCIL_BACK_REF = 36003; + + // Field descriptor #194 I + public static final int GL_STENCIL_BACK_VALUE_MASK = 36004; + + // Field descriptor #194 I + public static final int GL_STENCIL_BACK_WRITEMASK = 36005; + + // Field descriptor #194 I + public static final int GL_BLEND_EQUATION_RGB = 32777; + + // Field descriptor #194 I + public static final int GL_BLEND_EQUATION_ALPHA = 34877; + +} \ No newline at end of file diff --git a/src/net/PeytonPlayz585/storage/LocalStorageManager.java b/src/net/PeytonPlayz585/storage/LocalStorageManager.java new file mode 100644 index 0000000..d9c5b7b --- /dev/null +++ b/src/net/PeytonPlayz585/storage/LocalStorageManager.java @@ -0,0 +1,80 @@ +package net.PeytonPlayz585.storage; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import net.PeytonPlayz585.opengl.GL11; +import net.PeytonPlayz595.nbt.NBTBase; +import net.PeytonPlayz595.nbt.NBTTagCompound; +import net.lax1dude.eaglercraft.Base64; + +public class LocalStorageManager { + + public static NBTTagCompound gameSettingsStorage = null; + public static NBTTagCompound levelSettingsStorage = null; + + public static void loadStorage() { + byte[] g = GL11.loadLocalStorage("g"); + byte[] p = GL11.loadLocalStorage("p"); + + if(g != null) { + try { + NBTBase t = NBTBase.readTag(new DataInputStream(new ByteArrayInputStream(g))); + if(t != null && t instanceof NBTTagCompound) { + gameSettingsStorage = (NBTTagCompound)t; + } + }catch(IOException e) { + ; + } + } + + if(p != null) { + try { + NBTBase t = NBTBase.readTag(new DataInputStream(new ByteArrayInputStream(p))); + if(t != null && t instanceof NBTTagCompound) { + levelSettingsStorage = (NBTTagCompound)t; + } + }catch(IOException e) { + ; + } + } + + if(gameSettingsStorage == null) gameSettingsStorage = new NBTTagCompound(); + if(levelSettingsStorage == null) levelSettingsStorage = new NBTTagCompound(); + + } + + public static void saveStorageG() { + try { + ByteArrayOutputStream s = new ByteArrayOutputStream(); + NBTBase.writeTag(gameSettingsStorage, new DataOutputStream(s)); + GL11.saveLocalStorage("g", s.toByteArray()); + } catch (IOException e) { + ; + } + } + + public static void saveStorageP() { + try { + ByteArrayOutputStream s = new ByteArrayOutputStream(); + NBTBase.writeTag(levelSettingsStorage, new DataOutputStream(s)); + GL11.saveLocalStorage("p", s.toByteArray()); + } catch (IOException e) { + ; + } + } + + public static String dumpConfiguration() { + try { + ByteArrayOutputStream s = new ByteArrayOutputStream(); + NBTBase.writeTag(gameSettingsStorage, new DataOutputStream(s)); + return Base64.encodeBase64String(s.toByteArray()); + } catch(Throwable e) { + return ""; + } + } + +} \ No newline at end of file diff --git a/src/net/PeytonPlayz585/util/glu/GLU.java b/src/net/PeytonPlayz585/util/glu/GLU.java new file mode 100644 index 0000000..e369197 --- /dev/null +++ b/src/net/PeytonPlayz585/util/glu/GLU.java @@ -0,0 +1,7 @@ +package net.PeytonPlayz585.util.glu; + +import net.PeytonPlayz585.opengl.GL11; + +public class GLU extends GL11 { + +} diff --git a/src/net/PeytonPlayz595/nbt/NBTBase.java b/src/net/PeytonPlayz595/nbt/NBTBase.java new file mode 100644 index 0000000..a9bb352 --- /dev/null +++ b/src/net/PeytonPlayz595/nbt/NBTBase.java @@ -0,0 +1,130 @@ +package net.PeytonPlayz595.nbt; + +import java.io.*; + +public abstract class NBTBase { + + public NBTBase() { + key = null; + } + + abstract void writeTagContents(DataOutput dataoutput) throws IOException; + + abstract void readTagContents(DataInput datainput) throws IOException; + + public abstract byte getType(); + + public String getKey() { + if (key == null) { + return ""; + } else { + return key; + } + } + + public NBTBase setKey(String s) { + key = s; + return this; + } + + public static NBTBase readTag(DataInput datainput) throws IOException { + byte byte0 = datainput.readByte(); + if (byte0 == 0) { + return new NBTTagEnd(); + } else { + NBTBase nbtbase = createTagOfType(byte0); + nbtbase.key = datainput.readUTF(); + nbtbase.readTagContents(datainput); + return nbtbase; + } + } + + public static void writeTag(NBTBase nbtbase, DataOutput dataoutput) throws IOException { + dataoutput.writeByte(nbtbase.getType()); + if (nbtbase.getType() == 0) { + return; + } else { + dataoutput.writeUTF(nbtbase.getKey()); + nbtbase.writeTagContents(dataoutput); + return; + } + } + + public static NBTBase createTagOfType(byte byte0) { + switch (byte0) { + case 0: // '\0' + return new NBTTagEnd(); + + case 1: // '\001' + return new NBTTagByte(); + + case 2: // '\002' + return new NBTTagShort(); + + case 3: // '\003' + return new NBTTagInt(); + + case 4: // '\004' + return new NBTTagLong(); + + case 5: // '\005' + return new NBTTagFloat(); + + case 6: // '\006' + return new NBTTagDouble(); + + case 7: // '\007' + return new NBTTagByteArray(); + + case 8: // '\b' + return new NBTTagString(); + + case 9: // '\t' + return new NBTTagList(); + + case 10: // '\n' + return new NBTTagCompound(); + } + return null; + } + + public static String getTagName(byte byte0) { + switch (byte0) { + case 0: // '\0' + return "TAG_End"; + + case 1: // '\001' + return "TAG_Byte"; + + case 2: // '\002' + return "TAG_Short"; + + case 3: // '\003' + return "TAG_Int"; + + case 4: // '\004' + return "TAG_Long"; + + case 5: // '\005' + return "TAG_Float"; + + case 6: // '\006' + return "TAG_Double"; + + case 7: // '\007' + return "TAG_Byte_Array"; + + case 8: // '\b' + return "TAG_String"; + + case 9: // '\t' + return "TAG_List"; + + case 10: // '\n' + return "TAG_Compound"; + } + return "UNKNOWN"; + } + + private String key; +} \ No newline at end of file diff --git a/src/net/PeytonPlayz595/nbt/NBTTagByte.java b/src/net/PeytonPlayz595/nbt/NBTTagByte.java new file mode 100644 index 0000000..22fcab6 --- /dev/null +++ b/src/net/PeytonPlayz595/nbt/NBTTagByte.java @@ -0,0 +1,31 @@ +package net.PeytonPlayz595.nbt; + +import java.io.*; + +public class NBTTagByte extends NBTBase { + + public NBTTagByte() { + } + + public NBTTagByte(byte byte0) { + byteValue = byte0; + } + + void writeTagContents(DataOutput dataoutput) throws IOException { + dataoutput.writeByte(byteValue); + } + + void readTagContents(DataInput datainput) throws IOException { + byteValue = datainput.readByte(); + } + + public byte getType() { + return 1; + } + + public String toString() { + return (new StringBuilder()).append("").append(byteValue).toString(); + } + + public byte byteValue; +} \ No newline at end of file diff --git a/src/net/PeytonPlayz595/nbt/NBTTagByteArray.java b/src/net/PeytonPlayz595/nbt/NBTTagByteArray.java new file mode 100644 index 0000000..0cd9af0 --- /dev/null +++ b/src/net/PeytonPlayz595/nbt/NBTTagByteArray.java @@ -0,0 +1,34 @@ +package net.PeytonPlayz595.nbt; + +import java.io.*; + +public class NBTTagByteArray extends NBTBase { + + public NBTTagByteArray() { + } + + public NBTTagByteArray(byte abyte0[]) { + byteArray = abyte0; + } + + void writeTagContents(DataOutput dataoutput) throws IOException { + dataoutput.writeInt(byteArray.length); + dataoutput.write(byteArray); + } + + void readTagContents(DataInput datainput) throws IOException { + int i = datainput.readInt(); + byteArray = new byte[i]; + datainput.readFully(byteArray); + } + + public byte getType() { + return 7; + } + + public String toString() { + return (new StringBuilder()).append("[").append(byteArray.length).append(" bytes]").toString(); + } + + public byte byteArray[]; +} \ No newline at end of file diff --git a/src/net/PeytonPlayz595/nbt/NBTTagCompound.java b/src/net/PeytonPlayz595/nbt/NBTTagCompound.java new file mode 100644 index 0000000..8f71b26 --- /dev/null +++ b/src/net/PeytonPlayz595/nbt/NBTTagCompound.java @@ -0,0 +1,194 @@ +package net.PeytonPlayz595.nbt; + +import java.io.*; +import java.util.*; + +public class NBTTagCompound extends NBTBase { + + public NBTTagCompound() { + tagMap = new HashMap(); + } + + void writeTagContents(DataOutput dataoutput) throws IOException { + NBTBase nbtbase; + for (Iterator iterator = tagMap.values().iterator(); iterator.hasNext(); NBTBase.writeTag(nbtbase, + dataoutput)) { + nbtbase = (NBTBase) iterator.next(); + } + + dataoutput.writeByte(0); + } + + void readTagContents(DataInput datainput) throws IOException { + tagMap.clear(); + NBTBase nbtbase; + for (; (nbtbase = NBTBase.readTag(datainput)).getType() != 0; tagMap.put(nbtbase.getKey(), nbtbase)) { + } + } + + public byte getType() { + return 10; + } + + public void setTag(String s, NBTBase nbtbase) { + tagMap.put(s, nbtbase.setKey(s)); + } + + public void setByte(String s, byte byte0) { + tagMap.put(s, (new NBTTagByte(byte0)).setKey(s)); + } + + public void setShort(String s, short word0) { + tagMap.put(s, (new NBTTagShort(word0)).setKey(s)); + } + + public void setInteger(String s, int i) { + tagMap.put(s, (new NBTTagInt(i)).setKey(s)); + } + + public void setLong(String s, long l) { + tagMap.put(s, (new NBTTagLong(l)).setKey(s)); + } + + public void setFloat(String s, float f) { + tagMap.put(s, (new NBTTagFloat(f)).setKey(s)); + } + + public void setDouble(String s, double d) { + tagMap.put(s, (new NBTTagDouble(d)).setKey(s)); + } + + public void setString(String s, String s1) { + tagMap.put(s, (new NBTTagString(s1)).setKey(s)); + } + + public void setByteArray(String s, byte abyte0[]) { + tagMap.put(s, (new NBTTagByteArray(abyte0)).setKey(s)); + } + + public void setObject(String s, Object obj) { + tagMap.put(s, obj); + } + + public void setCompoundTag(String s, NBTTagCompound nbttagcompound) { + tagMap.put(s, nbttagcompound.setKey(s)); + } + + public void setBoolean(String s, boolean flag) { + setByte(s, ((byte) (flag ? 1 : 0))); + } + + public boolean hasKey(String s) { + return tagMap.containsKey(s); + } + + public byte getByte(String s) { + if (!tagMap.containsKey(s)) { + return 0; + } else { + return ((NBTTagByte) tagMap.get(s)).byteValue; + } + } + + public short getShort(String s) { + if (!tagMap.containsKey(s)) { + return 0; + } else { + return ((NBTTagShort) tagMap.get(s)).shortValue; + } + } + + public int getInteger(String s) { + if (!tagMap.containsKey(s)) { + return 0; + } else { + return ((NBTTagInt) tagMap.get(s)).intValue; + } + } + + public long getLong(String s) { + if (!tagMap.containsKey(s)) { + return 0L; + } else { + return ((NBTTagLong) tagMap.get(s)).longValue; + } + } + + public float getFloat(String s) { + if (!tagMap.containsKey(s)) { + return 0.0F; + } else { + return ((NBTTagFloat) tagMap.get(s)).floatValue; + } + } + + public double getDouble(String s) { + if (!tagMap.containsKey(s)) { + return 0.0D; + } else { + return ((NBTTagDouble) tagMap.get(s)).doubleValue; + } + } + + public String getString(String s) { + if (!tagMap.containsKey(s)) { + return ""; + } else { + return ((NBTTagString) tagMap.get(s)).stringValue; + } + } + + public byte[] getByteArray(String s) { + if (!tagMap.containsKey(s)) { + return new byte[0]; + } else { + return ((NBTTagByteArray) tagMap.get(s)).byteArray; + } + } + + public NBTTagCompound getCompoundTag(String s) { + if (!tagMap.containsKey(s)) { + return new NBTTagCompound(); + } else { + return (NBTTagCompound) tagMap.get(s); + } + } + + public NBTTagList getTagList(String s) { + if (!tagMap.containsKey(s)) { + return new NBTTagList(); + } else { + return (NBTTagList) tagMap.get(s); + } + } + + public boolean getBoolean(String s) { + return getByte(s) != 0; + } + + public Object getObject(String s) { + if(!tagMap.containsKey(s)) { + return null; + } else { + return tagMap.get(s); + } + } + + public String toString() { + return (new StringBuilder()).append("").append(tagMap.size()).append(" entries").toString(); + } + + public boolean hasNoTags() { + return tagMap.size() == 0; + } + + public Map tagMap; + + public NBTBase getTag(String s) { + return (NBTBase) tagMap.get(s); + } + + public static Map getTagMap(NBTTagCompound nb) { + return nb.tagMap; + } +} \ No newline at end of file diff --git a/src/net/PeytonPlayz595/nbt/NBTTagDouble.java b/src/net/PeytonPlayz595/nbt/NBTTagDouble.java new file mode 100644 index 0000000..3c07cc9 --- /dev/null +++ b/src/net/PeytonPlayz595/nbt/NBTTagDouble.java @@ -0,0 +1,31 @@ +package net.PeytonPlayz595.nbt; + +import java.io.*; + +public class NBTTagDouble extends NBTBase { + + public NBTTagDouble() { + } + + public NBTTagDouble(double d) { + doubleValue = d; + } + + void writeTagContents(DataOutput dataoutput) throws IOException { + dataoutput.writeDouble(doubleValue); + } + + void readTagContents(DataInput datainput) throws IOException { + doubleValue = datainput.readDouble(); + } + + public byte getType() { + return 6; + } + + public String toString() { + return (new StringBuilder()).append("").append(doubleValue).toString(); + } + + public double doubleValue; +} \ No newline at end of file diff --git a/src/net/PeytonPlayz595/nbt/NBTTagEnd.java b/src/net/PeytonPlayz595/nbt/NBTTagEnd.java new file mode 100644 index 0000000..8310773 --- /dev/null +++ b/src/net/PeytonPlayz595/nbt/NBTTagEnd.java @@ -0,0 +1,23 @@ +package net.PeytonPlayz595.nbt; + +import java.io.*; + +public class NBTTagEnd extends NBTBase { + + public NBTTagEnd() { + } + + void readTagContents(DataInput datainput) throws IOException { + } + + void writeTagContents(DataOutput dataoutput) throws IOException { + } + + public byte getType() { + return 0; + } + + public String toString() { + return "END"; + } +} \ No newline at end of file diff --git a/src/net/PeytonPlayz595/nbt/NBTTagFloat.java b/src/net/PeytonPlayz595/nbt/NBTTagFloat.java new file mode 100644 index 0000000..c547134 --- /dev/null +++ b/src/net/PeytonPlayz595/nbt/NBTTagFloat.java @@ -0,0 +1,31 @@ +package net.PeytonPlayz595.nbt; + +import java.io.*; + +public class NBTTagFloat extends NBTBase { + + public NBTTagFloat() { + } + + public NBTTagFloat(float f) { + floatValue = f; + } + + void writeTagContents(DataOutput dataoutput) throws IOException { + dataoutput.writeFloat(floatValue); + } + + void readTagContents(DataInput datainput) throws IOException { + floatValue = datainput.readFloat(); + } + + public byte getType() { + return 5; + } + + public String toString() { + return (new StringBuilder()).append("").append(floatValue).toString(); + } + + public float floatValue; +} \ No newline at end of file diff --git a/src/net/PeytonPlayz595/nbt/NBTTagInt.java b/src/net/PeytonPlayz595/nbt/NBTTagInt.java new file mode 100644 index 0000000..2f5996c --- /dev/null +++ b/src/net/PeytonPlayz595/nbt/NBTTagInt.java @@ -0,0 +1,31 @@ +package net.PeytonPlayz595.nbt; + +import java.io.*; + +public class NBTTagInt extends NBTBase { + + public NBTTagInt() { + } + + public NBTTagInt(int i) { + intValue = i; + } + + void writeTagContents(DataOutput dataoutput) throws IOException { + dataoutput.writeInt(intValue); + } + + void readTagContents(DataInput datainput) throws IOException { + intValue = datainput.readInt(); + } + + public byte getType() { + return 3; + } + + public String toString() { + return (new StringBuilder()).append("").append(intValue).toString(); + } + + public int intValue; +} \ No newline at end of file diff --git a/src/net/PeytonPlayz595/nbt/NBTTagList.java b/src/net/PeytonPlayz595/nbt/NBTTagList.java new file mode 100644 index 0000000..d1fac48 --- /dev/null +++ b/src/net/PeytonPlayz595/nbt/NBTTagList.java @@ -0,0 +1,63 @@ +package net.PeytonPlayz595.nbt; + +import java.io.*; +import java.util.ArrayList; +import java.util.List; + +public class NBTTagList extends NBTBase { + + public NBTTagList() { + tagList = new ArrayList(); + } + + void writeTagContents(DataOutput dataoutput) throws IOException { + if (tagList.size() > 0) { + tagType = ((NBTBase) tagList.get(0)).getType(); + } else { + tagType = 1; + } + dataoutput.writeByte(tagType); + dataoutput.writeInt(tagList.size()); + for (int i = 0; i < tagList.size(); i++) { + ((NBTBase) tagList.get(i)).writeTagContents(dataoutput); + } + + } + + void readTagContents(DataInput datainput) throws IOException { + tagType = datainput.readByte(); + int i = datainput.readInt(); + tagList = new ArrayList(); + for (int j = 0; j < i; j++) { + NBTBase nbtbase = NBTBase.createTagOfType(tagType); + nbtbase.readTagContents(datainput); + tagList.add(nbtbase); + } + + } + + public byte getType() { + return 9; + } + + public String toString() { + return (new StringBuilder()).append("").append(tagList.size()).append(" entries of type ") + .append(NBTBase.getTagName(tagType)).toString(); + } + + public void setTag(NBTBase nbtbase) { + tagType = nbtbase.getType(); + tagList.add(nbtbase); + } + + public NBTBase tagAt(int i) { + return (NBTBase) tagList.get(i); + } + + public int tagCount() { + return tagList.size(); + } + + private List tagList; + private byte tagType; +} \ No newline at end of file diff --git a/src/net/PeytonPlayz595/nbt/NBTTagLong.java b/src/net/PeytonPlayz595/nbt/NBTTagLong.java new file mode 100644 index 0000000..252515a --- /dev/null +++ b/src/net/PeytonPlayz595/nbt/NBTTagLong.java @@ -0,0 +1,31 @@ +package net.PeytonPlayz595.nbt; + +import java.io.*; + +public class NBTTagLong extends NBTBase { + + public NBTTagLong() { + } + + public NBTTagLong(long l) { + longValue = l; + } + + void writeTagContents(DataOutput dataoutput) throws IOException { + dataoutput.writeLong(longValue); + } + + void readTagContents(DataInput datainput) throws IOException { + longValue = datainput.readLong(); + } + + public byte getType() { + return 4; + } + + public String toString() { + return (new StringBuilder()).append("").append(longValue).toString(); + } + + public long longValue; +} \ No newline at end of file diff --git a/src/net/PeytonPlayz595/nbt/NBTTagShort.java b/src/net/PeytonPlayz595/nbt/NBTTagShort.java new file mode 100644 index 0000000..481557d --- /dev/null +++ b/src/net/PeytonPlayz595/nbt/NBTTagShort.java @@ -0,0 +1,31 @@ +package net.PeytonPlayz595.nbt; + +import java.io.*; + +public class NBTTagShort extends NBTBase { + + public NBTTagShort() { + } + + public NBTTagShort(short word0) { + shortValue = word0; + } + + void writeTagContents(DataOutput dataoutput) throws IOException { + dataoutput.writeShort(shortValue); + } + + void readTagContents(DataInput datainput) throws IOException { + shortValue = datainput.readShort(); + } + + public byte getType() { + return 2; + } + + public String toString() { + return (new StringBuilder()).append("").append(shortValue).toString(); + } + + public short shortValue; +} \ No newline at end of file diff --git a/src/net/PeytonPlayz595/nbt/NBTTagString.java b/src/net/PeytonPlayz595/nbt/NBTTagString.java new file mode 100644 index 0000000..307bf26 --- /dev/null +++ b/src/net/PeytonPlayz595/nbt/NBTTagString.java @@ -0,0 +1,36 @@ +package net.PeytonPlayz595.nbt; + +import java.io.*; + +public class NBTTagString extends NBTBase { + + public NBTTagString() { + } + + public NBTTagString(String s) { + stringValue = s; + if (s == null) { + throw new IllegalArgumentException("Empty string not allowed"); + } else { + return; + } + } + + void writeTagContents(DataOutput dataoutput) throws IOException { + dataoutput.writeUTF(stringValue); + } + + void readTagContents(DataInput datainput) throws IOException { + stringValue = datainput.readUTF(); + } + + public byte getType() { + return 8; + } + + public String toString() { + return (new StringBuilder()).append("").append(stringValue).toString(); + } + + public String stringValue; +} \ No newline at end of file diff --git a/src/net/lax1dude/eaglercraft/AssetRepository.java b/src/net/lax1dude/eaglercraft/AssetRepository.java new file mode 100644 index 0000000..f952b08 --- /dev/null +++ b/src/net/lax1dude/eaglercraft/AssetRepository.java @@ -0,0 +1,55 @@ +package net.lax1dude.eaglercraft; + +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.IOException; +import java.nio.charset.Charset; +import java.util.Arrays; +import java.util.HashMap; + +import com.jcraft.jzlib.InflaterInputStream; + +public class AssetRepository { + + private static final HashMap filePool = new HashMap(); + + public static final void install(byte[] pkg) throws IOException { + ByteArrayInputStream in2 = new ByteArrayInputStream(pkg); + DataInputStream in = new DataInputStream(in2); + byte[] header = new byte[8]; + in.read(header); + if (!"EAGPKG!!".equals(new String(header, Charset.forName("UTF-8")))) + throw new IOException("invalid epk file"); + in.readUTF(); + in = new DataInputStream(new InflaterInputStream(in2)); + String s = null; + SHA1Digest dg = new SHA1Digest(); + while ("".equals(s = in.readUTF())) { + String path = in.readUTF(); + byte[] digest = new byte[20]; + byte[] digest2 = new byte[20]; + in.read(digest); + int len = in.readInt(); + byte[] file = new byte[len]; + in.read(file); + if (filePool.containsKey(path)) + continue; + dg.update(file, 0, len); + dg.doFinal(digest2, 0); + if (!Arrays.equals(digest, digest2)) + throw new IOException("invalid file hash for " + path); + filePool.put(path, file); + if (!"".equals(in.readUTF())) + throw new IOException("invalid epk file"); + } + if (in.available() > 0 || !" end".equals(s)) + throw new IOException("invalid epk file"); + } + + public static final byte[] getResource(String path) { + if (path.startsWith("/")) + path = path.substring(1); + return filePool.get(path); + } + +} \ No newline at end of file diff --git a/src/net/lax1dude/eaglercraft/Base64.java b/src/net/lax1dude/eaglercraft/Base64.java new file mode 100644 index 0000000..c8b3f69 --- /dev/null +++ b/src/net/lax1dude/eaglercraft/Base64.java @@ -0,0 +1,857 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.lax1dude.eaglercraft; + +import java.math.BigInteger; +import java.nio.charset.Charset; + +/** + * Provides Base64 encoding and decoding as defined by + * RFC 2045. + * + *

+ * This class implements section 6.8. Base64 + * Content-Transfer-Encoding from RFC 2045 Multipurpose Internet + * Mail Extensions (MIME) Part One: Format of Internet Message Bodies by + * Freed and Borenstein. + *

+ *

+ * The class can be parameterized in the following manner with various + * constructors: + *

+ *
    + *
  • URL-safe mode: Default off.
  • + *
  • Line length: Default 76. Line length that aren't multiples of 4 will + * still essentially end up being multiples of 4 in the encoded data. + *
  • Line separator: Default is CRLF ("\r\n")
  • + *
+ *

+ * The URL-safe parameter is only applied to encode operations. Decoding + * seamlessly handles both modes. + *

+ *

+ * Since this class operates directly on byte streams, and not character + * streams, it is hard-coded to only encode/decode character encodings which are + * compatible with the lower 127 ASCII chart (ISO-8859-1, Windows-1252, UTF-8, + * etc). + *

+ *

+ * This class is thread-safe. + *

+ * + * @see RFC 2045 + * @since 1.0 + */ +public class Base64 extends BaseNCodec { + + /** + * BASE32 characters are 6 bits in length. They are formed by taking a block of + * 3 octets to form a 24-bit string, which is converted into 4 BASE64 + * characters. + */ + private static final int BITS_PER_ENCODED_BYTE = 6; + private static final int BYTES_PER_UNENCODED_BLOCK = 3; + private static final int BYTES_PER_ENCODED_BLOCK = 4; + + /** + * This array is a lookup table that translates 6-bit positive integer index + * values into their "Base64 Alphabet" equivalents as specified in Table 1 of + * RFC 2045. + * + * Thanks to "commons" project in ws.apache.org for this code. + * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ + */ + private static final byte[] STANDARD_ENCODE_TABLE = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', + 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', + 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', + '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' }; + + /** + * This is a copy of the STANDARD_ENCODE_TABLE above, but with + and / changed + * to - and _ to make the encoded Base64 results more URL-SAFE. This table is + * only used when the Base64's mode is set to URL-SAFE. + */ + private static final byte[] URL_SAFE_ENCODE_TABLE = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', + 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', + 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', + '2', '3', '4', '5', '6', '7', '8', '9', '-', '_' }; + + /** + * This array is a lookup table that translates Unicode characters drawn from + * the "Base64 Alphabet" (as specified in Table 1 of RFC 2045) into their 6-bit + * positive integer equivalents. Characters that are not in the Base64 alphabet + * but fall within the bounds of the array are translated to -1. + * + * Note: '+' and '-' both decode to 62. '/' and '_' both decode to 63. This + * means decoder seamlessly handles both URL_SAFE and STANDARD base64. (The + * encoder, on the other hand, needs to know ahead of time what to emit). + * + * Thanks to "commons" project in ws.apache.org for this code. + * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ + */ + private static final byte[] DECODE_TABLE = { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 00-0f + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 10-1f + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, 62, -1, 63, // 20-2f + - / + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, // 30-3f 0-9 + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 40-4f A-O + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63, // 50-5f P-Z _ + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // 60-6f a-o + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 // 70-7a p-z + }; + + /** + * Base64 uses 6-bit fields. + */ + /** Mask used to extract 6 bits, used when encoding */ + private static final int MASK_6BITS = 0x3f; + /** Mask used to extract 4 bits, used when decoding final trailing character. */ + private static final int MASK_4BITS = 0xf; + /** Mask used to extract 2 bits, used when decoding final trailing character. */ + private static final int MASK_2BITS = 0x3; + + // The static final fields above are used for the original static byte[] methods + // on Base64. + // The private member fields below are used with the new streaming approach, + // which requires + // some state be preserved between calls of encode() and decode(). + + /** + * Decodes Base64 data into octets. + *

+ * Note: this method seamlessly handles data encoded in URL-safe or + * normal mode. + *

+ * + * @param base64Data Byte array containing Base64 data + * @return Array containing decoded data. + */ + public static byte[] decodeBase64(final byte[] base64Data) { + return new Base64().decode(base64Data); + } + + /** + * Decodes a Base64 String into octets. + *

+ * Note: this method seamlessly handles data encoded in URL-safe or + * normal mode. + *

+ * + * @param base64String String containing Base64 data + * @return Array containing decoded data. + * @since 1.4 + */ + public static byte[] decodeBase64(final String base64String) { + return new Base64().decode(base64String); + } + + // Implementation of integer encoding used for crypto + /** + * Decodes a byte64-encoded integer according to crypto standards such as W3C's + * XML-Signature. + * + * @param pArray a byte array containing base64 character data + * @return A BigInteger + * @since 1.4 + */ + public static BigInteger decodeInteger(final byte[] pArray) { + return new BigInteger(1, decodeBase64(pArray)); + } + + /** + * Encodes binary data using the base64 algorithm but does not chunk the output. + * + * @param binaryData binary data to encode + * @return byte[] containing Base64 characters in their UTF-8 representation. + */ + public static byte[] encodeBase64(final byte[] binaryData) { + return encodeBase64(binaryData, false); + } + + /** + * Encodes binary data using the base64 algorithm, optionally chunking the + * output into 76 character blocks. + * + * @param binaryData Array containing binary data to encode. + * @param isChunked if {@code true} this encoder will chunk the base64 output + * into 76 character blocks + * @return Base64-encoded data. + * @throws IllegalArgumentException Thrown when the input array needs an output + * array bigger than {@link Integer#MAX_VALUE} + */ + public static byte[] encodeBase64(final byte[] binaryData, final boolean isChunked) { + return encodeBase64(binaryData, isChunked, false); + } + + /** + * Encodes binary data using the base64 algorithm, optionally chunking the + * output into 76 character blocks. + * + * @param binaryData Array containing binary data to encode. + * @param isChunked if {@code true} this encoder will chunk the base64 output + * into 76 character blocks + * @param urlSafe if {@code true} this encoder will emit - and _ instead of + * the usual + and / characters. Note: no padding is added + * when encoding using the URL-safe alphabet. + * @return Base64-encoded data. + * @throws IllegalArgumentException Thrown when the input array needs an output + * array bigger than {@link Integer#MAX_VALUE} + * @since 1.4 + */ + public static byte[] encodeBase64(final byte[] binaryData, final boolean isChunked, final boolean urlSafe) { + return encodeBase64(binaryData, isChunked, urlSafe, Integer.MAX_VALUE); + } + + /** + * Encodes binary data using the base64 algorithm, optionally chunking the + * output into 76 character blocks. + * + * @param binaryData Array containing binary data to encode. + * @param isChunked if {@code true} this encoder will chunk the base64 + * output into 76 character blocks + * @param urlSafe if {@code true} this encoder will emit - and _ instead + * of the usual + and / characters. Note: no padding is + * added when encoding using the URL-safe alphabet. + * @param maxResultSize The maximum result size to accept. + * @return Base64-encoded data. + * @throws IllegalArgumentException Thrown when the input array needs an output + * array bigger than maxResultSize + * @since 1.4 + */ + public static byte[] encodeBase64(final byte[] binaryData, final boolean isChunked, final boolean urlSafe, + final int maxResultSize) { + if (binaryData == null || binaryData.length == 0) { + return binaryData; + } + + // Create this so can use the super-class method + // Also ensures that the same roundings are performed by the ctor and the code + final Base64 b64 = isChunked ? new Base64(urlSafe) : new Base64(0, CHUNK_SEPARATOR, urlSafe); + final long len = b64.getEncodedLength(binaryData); + if (len > maxResultSize) { + throw new IllegalArgumentException("Input array too big, the output array would be bigger (" + len + + ") than the specified maximum size of " + maxResultSize); + } + + return b64.encode(binaryData); + } + + /** + * Encodes binary data using the base64 algorithm and chunks the encoded output + * into 76 character blocks + * + * @param binaryData binary data to encode + * @return Base64 characters chunked in 76 character blocks + */ + public static byte[] encodeBase64Chunked(final byte[] binaryData) { + return encodeBase64(binaryData, true); + } + + /** + * Encodes binary data using the base64 algorithm but does not chunk the output. + * + * NOTE: We changed the behavior of this method from multi-line chunking + * (commons-codec-1.4) to single-line non-chunking (commons-codec-1.5). + * + * @param binaryData binary data to encode + * @return String containing Base64 characters. + * @since 1.4 (NOTE: 1.4 chunked the output, whereas 1.5 does not). + */ + public static String encodeBase64String(final byte[] binaryData) { + return new String(encodeBase64(binaryData, false), Charset.forName("UTF-8")); + } + + /** + * Encodes binary data using a URL-safe variation of the base64 algorithm but + * does not chunk the output. The url-safe variation emits - and _ instead of + + * and / characters. Note: no padding is added. + * + * @param binaryData binary data to encode + * @return byte[] containing Base64 characters in their UTF-8 representation. + * @since 1.4 + */ + public static byte[] encodeBase64URLSafe(final byte[] binaryData) { + return encodeBase64(binaryData, false, true); + } + + /** + * Encodes binary data using a URL-safe variation of the base64 algorithm but + * does not chunk the output. The url-safe variation emits - and _ instead of + + * and / characters. Note: no padding is added. + * + * @param binaryData binary data to encode + * @return String containing Base64 characters + * @since 1.4 + */ + public static String encodeBase64URLSafeString(final byte[] binaryData) { + return new String(encodeBase64(binaryData, false, true), Charset.forName("UTF-8")); + } + + /** + * Encodes to a byte64-encoded integer according to crypto standards such as + * W3C's XML-Signature. + * + * @param bigInteger a BigInteger + * @return A byte array containing base64 character data + * @throws NullPointerException if null is passed in + * @since 1.4 + */ + public static byte[] encodeInteger(final BigInteger bigInteger) { + return encodeBase64(toIntegerBytes(bigInteger), false); + } + + /** + * Tests a given byte array to see if it contains only valid characters within + * the Base64 alphabet. Currently the method treats whitespace as valid. + * + * @param arrayOctet byte array to test + * @return {@code true} if all bytes are valid characters in the Base64 alphabet + * or if the byte array is empty; {@code false}, otherwise + * @deprecated 1.5 Use {@link #isBase64(byte[])}, will be removed in 2.0. + */ + @Deprecated + public static boolean isArrayByteBase64(final byte[] arrayOctet) { + return isBase64(arrayOctet); + } + + /** + * Returns whether or not the {@code octet} is in the base 64 alphabet. + * + * @param octet The value to test + * @return {@code true} if the value is defined in the the base 64 alphabet, + * {@code false} otherwise. + * @since 1.4 + */ + public static boolean isBase64(final byte octet) { + return octet == PAD_DEFAULT || (octet >= 0 && octet < DECODE_TABLE.length && DECODE_TABLE[octet] != -1); + } + + /** + * Tests a given byte array to see if it contains only valid characters within + * the Base64 alphabet. Currently the method treats whitespace as valid. + * + * @param arrayOctet byte array to test + * @return {@code true} if all bytes are valid characters in the Base64 alphabet + * or if the byte array is empty; {@code false}, otherwise + * @since 1.5 + */ + public static boolean isBase64(final byte[] arrayOctet) { + for (int i = 0; i < arrayOctet.length; i++) { + if (!isBase64(arrayOctet[i]) && !isWhiteSpace(arrayOctet[i])) { + return false; + } + } + return true; + } + + /** + * Tests a given String to see if it contains only valid characters within the + * Base64 alphabet. Currently the method treats whitespace as valid. + * + * @param base64 String to test + * @return {@code true} if all characters in the String are valid characters in + * the Base64 alphabet or if the String is empty; {@code false}, + * otherwise + * @since 1.5 + */ + public static boolean isBase64(final String base64) { + return isBase64(base64.getBytes(Charset.forName("UTF-8"))); + } + + /** + * Returns a byte-array representation of a {@code BigInteger} without sign bit. + * + * @param bigInt {@code BigInteger} to be converted + * @return a byte array representation of the BigInteger parameter + */ + static byte[] toIntegerBytes(final BigInteger bigInt) { + int bitlen = bigInt.bitLength(); + // round bitlen + bitlen = ((bitlen + 7) >> 3) << 3; + final byte[] bigBytes = bigInt.toByteArray(); + + if (((bigInt.bitLength() % 8) != 0) && (((bigInt.bitLength() / 8) + 1) == (bitlen / 8))) { + return bigBytes; + } + // set up params for copying everything but sign bit + int startSrc = 0; + int len = bigBytes.length; + + // if bigInt is exactly byte-aligned, just skip signbit in copy + if ((bigInt.bitLength() % 8) == 0) { + startSrc = 1; + len--; + } + final int startDst = bitlen / 8 - len; // to pad w/ nulls as per spec + final byte[] resizedBytes = new byte[bitlen / 8]; + System.arraycopy(bigBytes, startSrc, resizedBytes, startDst, len); + return resizedBytes; + } + + /** + * Encode table to use: either STANDARD or URL_SAFE. Note: the DECODE_TABLE + * above remains static because it is able to decode both STANDARD and URL_SAFE + * streams, but the encodeTable must be a member variable so we can switch + * between the two modes. + */ + private final byte[] encodeTable; + + // Only one decode table currently; keep for consistency with Base32 code + private final byte[] decodeTable = DECODE_TABLE; + + /** + * Line separator for encoding. Not used when decoding. Only used if lineLength + * > 0. + */ + private final byte[] lineSeparator; + + /** + * Convenience variable to help us determine when our buffer is going to run out + * of room and needs resizing. {@code decodeSize = 3 + lineSeparator.length;} + */ + private final int decodeSize; + + /** + * Convenience variable to help us determine when our buffer is going to run out + * of room and needs resizing. {@code encodeSize = 4 + lineSeparator.length;} + */ + private final int encodeSize; + + /** + * Creates a Base64 codec used for decoding (all modes) and encoding in + * URL-unsafe mode. + *

+ * When encoding the line length is 0 (no chunking), and the encoding table is + * STANDARD_ENCODE_TABLE. + *

+ * + *

+ * When decoding all variants are supported. + *

+ */ + public Base64() { + this(0); + } + + /** + * Creates a Base64 codec used for decoding (all modes) and encoding in the + * given URL-safe mode. + *

+ * When encoding the line length is 76, the line separator is CRLF, and the + * encoding table is STANDARD_ENCODE_TABLE. + *

+ * + *

+ * When decoding all variants are supported. + *

+ * + * @param urlSafe if {@code true}, URL-safe encoding is used. In most cases this + * should be set to {@code false}. + * @since 1.4 + */ + public Base64(final boolean urlSafe) { + this(MIME_CHUNK_SIZE, CHUNK_SEPARATOR, urlSafe); + } + + /** + * Creates a Base64 codec used for decoding (all modes) and encoding in + * URL-unsafe mode. + *

+ * When encoding the line length is given in the constructor, the line separator + * is CRLF, and the encoding table is STANDARD_ENCODE_TABLE. + *

+ *

+ * Line lengths that aren't multiples of 4 will still essentially end up being + * multiples of 4 in the encoded data. + *

+ *

+ * When decoding all variants are supported. + *

+ * + * @param lineLength Each line of encoded data will be at most of the given + * length (rounded down to nearest multiple of 4). If + * lineLength <= 0, then the output will not be divided + * into lines (chunks). Ignored when decoding. + * @since 1.4 + */ + public Base64(final int lineLength) { + this(lineLength, CHUNK_SEPARATOR); + } + + /** + * Creates a Base64 codec used for decoding (all modes) and encoding in + * URL-unsafe mode. + *

+ * When encoding the line length and line separator are given in the + * constructor, and the encoding table is STANDARD_ENCODE_TABLE. + *

+ *

+ * Line lengths that aren't multiples of 4 will still essentially end up being + * multiples of 4 in the encoded data. + *

+ *

+ * When decoding all variants are supported. + *

+ * + * @param lineLength Each line of encoded data will be at most of the given + * length (rounded down to nearest multiple of 4). If + * lineLength <= 0, then the output will not be divided + * into lines (chunks). Ignored when decoding. + * @param lineSeparator Each line of encoded data will end with this sequence of + * bytes. + * @throws IllegalArgumentException Thrown when the provided lineSeparator + * included some base64 characters. + * @since 1.4 + */ + public Base64(final int lineLength, final byte[] lineSeparator) { + this(lineLength, lineSeparator, false); + } + + /** + * Creates a Base64 codec used for decoding (all modes) and encoding in + * URL-unsafe mode. + *

+ * When encoding the line length and line separator are given in the + * constructor, and the encoding table is STANDARD_ENCODE_TABLE. + *

+ *

+ * Line lengths that aren't multiples of 4 will still essentially end up being + * multiples of 4 in the encoded data. + *

+ *

+ * When decoding all variants are supported. + *

+ * + * @param lineLength Each line of encoded data will be at most of the given + * length (rounded down to nearest multiple of 4). If + * lineLength <= 0, then the output will not be divided + * into lines (chunks). Ignored when decoding. + * @param lineSeparator Each line of encoded data will end with this sequence of + * bytes. + * @param urlSafe Instead of emitting '+' and '/' we emit '-' and '_' + * respectively. urlSafe is only applied to encode + * operations. Decoding seamlessly handles both modes. + * Note: no padding is added when using the URL-safe + * alphabet. + * @throws IllegalArgumentException Thrown when the {@code lineSeparator} + * contains Base64 characters. + * @since 1.4 + */ + public Base64(final int lineLength, final byte[] lineSeparator, final boolean urlSafe) { + this(lineLength, lineSeparator, urlSafe, CodecPolicy.LENIANT); + } + + /** + * Creates a Base64 codec used for decoding (all modes) and encoding in + * URL-unsafe mode. + *

+ * When encoding the line length and line separator are given in the + * constructor, and the encoding table is STANDARD_ENCODE_TABLE. + *

+ *

+ * Line lengths that aren't multiples of 4 will still essentially end up being + * multiples of 4 in the encoded data. + *

+ *

+ * When decoding all variants are supported. + *

+ * + * @param lineLength Each line of encoded data will be at most of the given + * length (rounded down to nearest multiple of 4). If + * lineLength <= 0, then the output will not be divided + * into lines (chunks). Ignored when decoding. + * @param lineSeparator Each line of encoded data will end with this sequence + * of bytes. + * @param urlSafe Instead of emitting '+' and '/' we emit '-' and '_' + * respectively. urlSafe is only applied to encode + * operations. Decoding seamlessly handles both modes. + * Note: no padding is added when using the URL-safe + * alphabet. + * @param decodingPolicy The decoding policy. + * @throws IllegalArgumentException Thrown when the {@code lineSeparator} + * contains Base64 characters. + * @since 1.15 + */ + public Base64(final int lineLength, final byte[] lineSeparator, final boolean urlSafe, + final CodecPolicy decodingPolicy) { + super(BYTES_PER_UNENCODED_BLOCK, BYTES_PER_ENCODED_BLOCK, lineLength, + lineSeparator == null ? 0 : lineSeparator.length, PAD_DEFAULT, decodingPolicy); + // TODO could be simplified if there is no requirement to reject invalid line + // sep when length <=0 + // @see test case Base64Test.testConstructors() + if (lineSeparator != null) { + if (containsAlphabetOrPad(lineSeparator)) { + final String sep = new String(lineSeparator, Charset.forName("UTF-8")); + throw new IllegalArgumentException("lineSeparator must not contain base64 characters: [" + sep + "]"); + } + if (lineLength > 0) { // null line-sep forces no chunking rather than throwing IAE + this.encodeSize = BYTES_PER_ENCODED_BLOCK + lineSeparator.length; + this.lineSeparator = new byte[lineSeparator.length]; + System.arraycopy(lineSeparator, 0, this.lineSeparator, 0, lineSeparator.length); + } else { + this.encodeSize = BYTES_PER_ENCODED_BLOCK; + this.lineSeparator = null; + } + } else { + this.encodeSize = BYTES_PER_ENCODED_BLOCK; + this.lineSeparator = null; + } + this.decodeSize = this.encodeSize - 1; + this.encodeTable = urlSafe ? URL_SAFE_ENCODE_TABLE : STANDARD_ENCODE_TABLE; + } + + // Implementation of the Encoder Interface + + /** + *

+ * Decodes all of the provided data, starting at inPos, for inAvail bytes. + * Should be called at least twice: once with the data to decode, and once with + * inAvail set to "-1" to alert decoder that EOF has been reached. The "-1" call + * is not necessary when decoding, but it doesn't hurt, either. + *

+ *

+ * Ignores all non-base64 characters. This is how chunked (e.g. 76 character) + * data is handled, since CR and LF are silently ignored, but has implications + * for other bytes, too. This method subscribes to the garbage-in, garbage-out + * philosophy: it will not check the provided data for validity. + *

+ *

+ * Thanks to "commons" project in ws.apache.org for the bitwise operations, and + * general approach. + * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ + *

+ * + * @param in byte[] array of ascii data to base64 decode. + * @param inPos Position to start reading data from. + * @param inAvail Amount of bytes available from input for decoding. + * @param context the context to be used + */ + @Override + void decode(final byte[] in, int inPos, final int inAvail, final Context context) { + if (context.eof) { + return; + } + if (inAvail < 0) { + context.eof = true; + } + for (int i = 0; i < inAvail; i++) { + final byte[] buffer = ensureBufferSize(decodeSize, context); + final byte b = in[inPos++]; + if (b == pad) { + // We're done. + context.eof = true; + break; + } + if (b >= 0 && b < DECODE_TABLE.length) { + final int result = DECODE_TABLE[b]; + if (result >= 0) { + context.modulus = (context.modulus + 1) % BYTES_PER_ENCODED_BLOCK; + context.ibitWorkArea = (context.ibitWorkArea << BITS_PER_ENCODED_BYTE) + result; + if (context.modulus == 0) { + buffer[context.pos++] = (byte) ((context.ibitWorkArea >> 16) & MASK_8BITS); + buffer[context.pos++] = (byte) ((context.ibitWorkArea >> 8) & MASK_8BITS); + buffer[context.pos++] = (byte) (context.ibitWorkArea & MASK_8BITS); + } + } + } + } + + // Two forms of EOF as far as base64 decoder is concerned: actual + // EOF (-1) and first time '=' character is encountered in stream. + // This approach makes the '=' padding characters completely optional. + if (context.eof && context.modulus != 0) { + final byte[] buffer = ensureBufferSize(decodeSize, context); + + // We have some spare bits remaining + // Output all whole multiples of 8 bits and ignore the rest + switch (context.modulus) { +// case 0 : // impossible, as excluded above + case 1: // 6 bits - either ignore entirely, or raise an exception + validateTrailingCharacter(); + break; + case 2: // 12 bits = 8 + 4 + validateCharacter(MASK_4BITS, context); + context.ibitWorkArea = context.ibitWorkArea >> 4; // dump the extra 4 bits + buffer[context.pos++] = (byte) ((context.ibitWorkArea) & MASK_8BITS); + break; + case 3: // 18 bits = 8 + 8 + 2 + validateCharacter(MASK_2BITS, context); + context.ibitWorkArea = context.ibitWorkArea >> 2; // dump 2 bits + buffer[context.pos++] = (byte) ((context.ibitWorkArea >> 8) & MASK_8BITS); + buffer[context.pos++] = (byte) ((context.ibitWorkArea) & MASK_8BITS); + break; + default: + throw new IllegalStateException("Impossible modulus " + context.modulus); + } + } + } + + /** + *

+ * Encodes all of the provided data, starting at inPos, for inAvail bytes. Must + * be called at least twice: once with the data to encode, and once with inAvail + * set to "-1" to alert encoder that EOF has been reached, to flush last + * remaining bytes (if not multiple of 3). + *

+ *

+ * Note: no padding is added when encoding using the URL-safe alphabet. + *

+ *

+ * Thanks to "commons" project in ws.apache.org for the bitwise operations, and + * general approach. + * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ + *

+ * + * @param in byte[] array of binary data to base64 encode. + * @param inPos Position to start reading data from. + * @param inAvail Amount of bytes available from input for encoding. + * @param context the context to be used + */ + @Override + void encode(final byte[] in, int inPos, final int inAvail, final Context context) { + if (context.eof) { + return; + } + // inAvail < 0 is how we're informed of EOF in the underlying data we're + // encoding. + if (inAvail < 0) { + context.eof = true; + if (0 == context.modulus && lineLength == 0) { + return; // no leftovers to process and not using chunking + } + final byte[] buffer = ensureBufferSize(encodeSize, context); + final int savedPos = context.pos; + switch (context.modulus) { // 0-2 + case 0: // nothing to do here + break; + case 1: // 8 bits = 6 + 2 + // top 6 bits: + buffer[context.pos++] = encodeTable[(context.ibitWorkArea >> 2) & MASK_6BITS]; + // remaining 2: + buffer[context.pos++] = encodeTable[(context.ibitWorkArea << 4) & MASK_6BITS]; + // URL-SAFE skips the padding to further reduce size. + if (encodeTable == STANDARD_ENCODE_TABLE) { + buffer[context.pos++] = pad; + buffer[context.pos++] = pad; + } + break; + + case 2: // 16 bits = 6 + 6 + 4 + buffer[context.pos++] = encodeTable[(context.ibitWorkArea >> 10) & MASK_6BITS]; + buffer[context.pos++] = encodeTable[(context.ibitWorkArea >> 4) & MASK_6BITS]; + buffer[context.pos++] = encodeTable[(context.ibitWorkArea << 2) & MASK_6BITS]; + // URL-SAFE skips the padding to further reduce size. + if (encodeTable == STANDARD_ENCODE_TABLE) { + buffer[context.pos++] = pad; + } + break; + default: + throw new IllegalStateException("Impossible modulus " + context.modulus); + } + context.currentLinePos += context.pos - savedPos; // keep track of current line position + // if currentPos == 0 we are at the start of a line, so don't add CRLF + if (lineLength > 0 && context.currentLinePos > 0) { + System.arraycopy(lineSeparator, 0, buffer, context.pos, lineSeparator.length); + context.pos += lineSeparator.length; + } + } else { + for (int i = 0; i < inAvail; i++) { + final byte[] buffer = ensureBufferSize(encodeSize, context); + context.modulus = (context.modulus + 1) % BYTES_PER_UNENCODED_BLOCK; + int b = in[inPos++]; + if (b < 0) { + b += 256; + } + context.ibitWorkArea = (context.ibitWorkArea << 8) + b; // BITS_PER_BYTE + if (0 == context.modulus) { // 3 bytes = 24 bits = 4 * 6 bits to extract + buffer[context.pos++] = encodeTable[(context.ibitWorkArea >> 18) & MASK_6BITS]; + buffer[context.pos++] = encodeTable[(context.ibitWorkArea >> 12) & MASK_6BITS]; + buffer[context.pos++] = encodeTable[(context.ibitWorkArea >> 6) & MASK_6BITS]; + buffer[context.pos++] = encodeTable[context.ibitWorkArea & MASK_6BITS]; + context.currentLinePos += BYTES_PER_ENCODED_BLOCK; + if (lineLength > 0 && lineLength <= context.currentLinePos) { + System.arraycopy(lineSeparator, 0, buffer, context.pos, lineSeparator.length); + context.pos += lineSeparator.length; + context.currentLinePos = 0; + } + } + } + } + } + + /** + * Returns whether or not the {@code octet} is in the Base64 alphabet. + * + * @param octet The value to test + * @return {@code true} if the value is defined in the the Base64 alphabet + * {@code false} otherwise. + */ + @Override + protected boolean isInAlphabet(final byte octet) { + return octet >= 0 && octet < decodeTable.length && decodeTable[octet] != -1; + } + + /** + * Returns our current encode mode. True if we're URL-SAFE, false otherwise. + * + * @return true if we're in URL-SAFE mode, false otherwise. + * @since 1.4 + */ + public boolean isUrlSafe() { + return this.encodeTable == URL_SAFE_ENCODE_TABLE; + } + + /** + * Validates whether decoding the final trailing character is possible in the + * context of the set of possible base 64 values. + * + *

+ * The character is valid if the lower bits within the provided mask are zero. + * This is used to test the final trailing base-64 digit is zero in the bits + * that will be discarded. + * + * @param emptyBitsMask The mask of the lower bits that should be empty + * @param context the context to be used + * + * @throws IllegalArgumentException if the bits being checked contain any + * non-zero value + */ + private void validateCharacter(final int emptyBitsMask, final Context context) { + if (isStrictDecoding() && (context.ibitWorkArea & emptyBitsMask) != 0) { + throw new IllegalArgumentException( + "Strict decoding: Last encoded character (before the paddings if any) is a valid base 64 alphabet but not a possible encoding. " + + "Expected the discarded bits from the character to be zero."); + } + } + + /** + * Validates whether decoding allows an entire final trailing character that + * cannot be used for a complete byte. + * + * @throws IllegalArgumentException if strict decoding is enabled + */ + private void validateTrailingCharacter() { + if (isStrictDecoding()) { + throw new IllegalArgumentException( + "Strict decoding: Last encoded character (before the paddings if any) is a valid base 64 alphabet but not a possible encoding. " + + "Decoding requires at least two trailing 6-bit characters to create bytes."); + } + } + +} \ No newline at end of file diff --git a/src/net/lax1dude/eaglercraft/BaseNCodec.java b/src/net/lax1dude/eaglercraft/BaseNCodec.java new file mode 100644 index 0000000..9ab019c --- /dev/null +++ b/src/net/lax1dude/eaglercraft/BaseNCodec.java @@ -0,0 +1,694 @@ +package net.lax1dude.eaglercraft; + +import java.nio.charset.Charset; +import java.util.Arrays; + +import net.lax1dude.eaglercraft.BaseNCodec.CodecPolicy; + +public abstract class BaseNCodec { + + static enum CodecPolicy { + STRICT, LENIANT; + } + + /** + * Holds thread context so classes can be thread-safe. + * + * This class is not itself thread-safe; each thread must allocate its own copy. + * + * @since 1.7 + */ + static class Context { + + /** + * Place holder for the bytes we're dealing with for our based logic. Bitwise + * operations store and extract the encoding or decoding from this variable. + */ + int ibitWorkArea; + + /** + * Place holder for the bytes we're dealing with for our based logic. Bitwise + * operations store and extract the encoding or decoding from this variable. + */ + long lbitWorkArea; + + /** + * Buffer for streaming. + */ + byte[] buffer; + + /** + * Position where next character should be written in the buffer. + */ + int pos; + + /** + * Position where next character should be read from the buffer. + */ + int readPos; + + /** + * Boolean flag to indicate the EOF has been reached. Once EOF has been reached, + * this object becomes useless, and must be thrown away. + */ + boolean eof; + + /** + * Variable tracks how many characters have been written to the current line. + * Only used when encoding. We use it to make sure each encoded line never goes + * beyond lineLength (if lineLength > 0). + */ + int currentLinePos; + + /** + * Writes to the buffer only occur after every 3/5 reads when encoding, and + * every 4/8 reads when decoding. This variable helps track that. + */ + int modulus; + + Context() { + } + + /** + * Returns a String useful for debugging (especially within a debugger.) + * + * @return a String useful for debugging. + */ + @SuppressWarnings("boxing") // OK to ignore boxing here + @Override + public String toString() { + return String.format( + "%s[buffer=%s, currentLinePos=%s, eof=%s, ibitWorkArea=%s, lbitWorkArea=%s, " + + "modulus=%s, pos=%s, readPos=%s]", + this.getClass().getSimpleName(), Arrays.toString(buffer), currentLinePos, eof, ibitWorkArea, + lbitWorkArea, modulus, pos, readPos); + } + } + + /** + * EOF + * + * @since 1.7 + */ + static final int EOF = -1; + + /** + * MIME chunk size per RFC 2045 section 6.8. + * + *

+ * The {@value} character limit does not count the trailing CRLF, but counts all + * other characters, including any equal signs. + *

+ * + * @see RFC 2045 section 6.8 + */ + public static final int MIME_CHUNK_SIZE = 76; + + /** + * PEM chunk size per RFC 1421 section 4.3.2.4. + * + *

+ * The {@value} character limit does not count the trailing CRLF, but counts all + * other characters, including any equal signs. + *

+ * + * @see RFC 1421 section + * 4.3.2.4 + */ + public static final int PEM_CHUNK_SIZE = 64; + + private static final int DEFAULT_BUFFER_RESIZE_FACTOR = 2; + + /** + * Defines the default buffer size - currently {@value} - must be large enough + * for at least one encoded block+separator + */ + private static final int DEFAULT_BUFFER_SIZE = 8192; + + /** + * The maximum size buffer to allocate. + * + *

+ * This is set to the same size used in the JDK {@code java.util.ArrayList}: + *

+ *
Some VMs reserve some header words in an array. Attempts to + * allocate larger arrays may result in OutOfMemoryError: Requested array size + * exceeds VM limit.
+ */ + private static final int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8; + + /** Mask used to extract 8 bits, used in decoding bytes */ + protected static final int MASK_8BITS = 0xff; + + /** + * Byte used to pad output. + */ + protected static final byte PAD_DEFAULT = '='; // Allow static access to default + + /** + * Chunk separator per RFC 2045 section 2.1. + * + * @see RFC 2045 section 2.1 + */ + static final byte[] CHUNK_SEPARATOR = { '\r', '\n' }; + + /** + * Compares two {@code int} values numerically treating the values as unsigned. + * Taken from JDK 1.8. + * + *

+ * TODO: Replace with JDK 1.8 Integer::compareUnsigned(int, int). + *

+ * + * @param x the first {@code int} to compare + * @param y the second {@code int} to compare + * @return the value {@code 0} if {@code x == y}; a value less than {@code 0} if + * {@code x < y} as unsigned values; and a value greater than {@code 0} + * if {@code x > y} as unsigned values + */ + private static int compareUnsigned(final int xx, final int yy) { + int x = xx + Integer.MIN_VALUE; + int y = yy + Integer.MIN_VALUE; + return (x < y) ? -1 : ((x == y) ? 0 : 1); + } + + /** + * Create a positive capacity at least as large the minimum required capacity. + * If the minimum capacity is negative then this throws an OutOfMemoryError as + * no array can be allocated. + * + * @param minCapacity the minimum capacity + * @return the capacity + * @throws OutOfMemoryError if the {@code minCapacity} is negative + */ + private static int createPositiveCapacity(final int minCapacity) { + if (minCapacity < 0) { + // overflow + throw new OutOfMemoryError("Unable to allocate array size: " + (minCapacity & 0xffffffffL)); + } + // This is called when we require buffer expansion to a very big array. + // Use the conservative maximum buffer size if possible, otherwise the biggest + // required. + // + // Note: In this situation JDK 1.8 java.util.ArrayList returns + // Integer.MAX_VALUE. + // This excludes some VMs that can exceed MAX_BUFFER_SIZE but not allocate a + // full + // Integer.MAX_VALUE length array. + // The result is that we may have to allocate an array of this size more than + // once if + // the capacity must be expanded again. + return (minCapacity > MAX_BUFFER_SIZE) ? minCapacity : MAX_BUFFER_SIZE; + } + + /** + * Gets a copy of the chunk separator per RFC 2045 section 2.1. + * + * @return the chunk separator + * @see RFC 2045 section 2.1 + * @since 1.15 + */ + public static byte[] getChunkSeparator() { + return CHUNK_SEPARATOR.clone(); + } + + /** + * Checks if a byte value is whitespace or not. Whitespace is taken to mean: + * space, tab, CR, LF + * + * @param byteToCheck the byte to check + * @return true if byte is whitespace, false otherwise + */ + protected static boolean isWhiteSpace(final byte byteToCheck) { + switch (byteToCheck) { + case ' ': + case '\n': + case '\r': + case '\t': + return true; + default: + return false; + } + } + + /** + * Increases our buffer by the {@link #DEFAULT_BUFFER_RESIZE_FACTOR}. + * + * @param context the context to be used + * @param minCapacity the minimum required capacity + * @return the resized byte[] buffer + * @throws OutOfMemoryError if the {@code minCapacity} is negative + */ + private static byte[] resizeBuffer(final Context context, final int minCapacity) { + // Overflow-conscious code treats the min and new capacity as unsigned. + final int oldCapacity = context.buffer.length; + int newCapacity = oldCapacity * DEFAULT_BUFFER_RESIZE_FACTOR; + if (compareUnsigned(newCapacity, minCapacity) < 0) { + newCapacity = minCapacity; + } + if (compareUnsigned(newCapacity, MAX_BUFFER_SIZE) > 0) { + newCapacity = createPositiveCapacity(minCapacity); + } + + final byte[] b = new byte[newCapacity]; + System.arraycopy(context.buffer, 0, b, 0, context.buffer.length); + context.buffer = b; + return b; + } + + /** + * @deprecated Use {@link #pad}. Will be removed in 2.0. + */ + @Deprecated + protected final byte PAD = PAD_DEFAULT; // instance variable just in case it needs to vary later + + protected final byte pad; // instance variable just in case it needs to vary later + + /** + * Number of bytes in each full block of unencoded data, e.g. 4 for Base64 and 5 + * for Base32 + */ + private final int unencodedBlockSize; + + /** + * Number of bytes in each full block of encoded data, e.g. 3 for Base64 and 8 + * for Base32 + */ + private final int encodedBlockSize; + + /** + * Chunksize for encoding. Not used when decoding. A value of zero or less + * implies no chunking of the encoded data. Rounded down to nearest multiple of + * encodedBlockSize. + */ + protected final int lineLength; + + /** + * Size of chunk separator. Not used unless {@link #lineLength} > 0. + */ + private final int chunkSeparatorLength; + + /** + * Defines the decoding behavior when the input bytes contain leftover trailing + * bits that cannot be created by a valid encoding. These can be bits that are + * unused from the final character or entire characters. The default mode is + * lenient decoding. Set this to {@code true} to enable strict decoding. + *
    + *
  • Lenient: Any trailing bits are composed into 8-bit bytes where possible. + * The remainder are discarded. + *
  • Strict: The decoding will raise an {@link IllegalArgumentException} if + * trailing bits are not part of a valid encoding. Any unused bits from the + * final character must be zero. Impossible counts of entire final characters + * are not allowed. + *
+ * + *

+ * When strict decoding is enabled it is expected that the decoded bytes will be + * re-encoded to a byte array that matches the original, i.e. no changes occur + * on the final character. This requires that the input bytes use the same + * padding and alphabet as the encoder. + */ + private final CodecPolicy decodingPolicy; + + /** + * Note {@code lineLength} is rounded down to the nearest multiple of the + * encoded block size. If {@code chunkSeparatorLength} is zero, then chunking is + * disabled. + * + * @param unencodedBlockSize the size of an unencoded block (e.g. Base64 = 3) + * @param encodedBlockSize the size of an encoded block (e.g. Base64 = 4) + * @param lineLength if > 0, use chunking with a length + * {@code lineLength} + * @param chunkSeparatorLength the chunk separator length, if relevant + */ + protected BaseNCodec(final int unencodedBlockSize, final int encodedBlockSize, final int lineLength, + final int chunkSeparatorLength) { + this(unencodedBlockSize, encodedBlockSize, lineLength, chunkSeparatorLength, PAD_DEFAULT); + } + + /** + * Note {@code lineLength} is rounded down to the nearest multiple of the + * encoded block size. If {@code chunkSeparatorLength} is zero, then chunking is + * disabled. + * + * @param unencodedBlockSize the size of an unencoded block (e.g. Base64 = 3) + * @param encodedBlockSize the size of an encoded block (e.g. Base64 = 4) + * @param lineLength if > 0, use chunking with a length + * {@code lineLength} + * @param chunkSeparatorLength the chunk separator length, if relevant + * @param pad byte used as padding byte. + */ + protected BaseNCodec(final int unencodedBlockSize, final int encodedBlockSize, final int lineLength, + final int chunkSeparatorLength, final byte pad) { + this(unencodedBlockSize, encodedBlockSize, lineLength, chunkSeparatorLength, pad, CodecPolicy.LENIANT); + } + + /** + * Note {@code lineLength} is rounded down to the nearest multiple of the + * encoded block size. If {@code chunkSeparatorLength} is zero, then chunking is + * disabled. + * + * @param unencodedBlockSize the size of an unencoded block (e.g. Base64 = 3) + * @param encodedBlockSize the size of an encoded block (e.g. Base64 = 4) + * @param lineLength if > 0, use chunking with a length + * {@code lineLength} + * @param chunkSeparatorLength the chunk separator length, if relevant + * @param pad byte used as padding byte. + * @param decodingPolicy Decoding policy. + * @since 1.15 + */ + protected BaseNCodec(final int unencodedBlockSize, final int encodedBlockSize, final int lineLength, + final int chunkSeparatorLength, final byte pad, final CodecPolicy decodingPolicy) { + this.unencodedBlockSize = unencodedBlockSize; + this.encodedBlockSize = encodedBlockSize; + final boolean useChunking = lineLength > 0 && chunkSeparatorLength > 0; + this.lineLength = useChunking ? (lineLength / encodedBlockSize) * encodedBlockSize : 0; + this.chunkSeparatorLength = chunkSeparatorLength; + this.pad = pad; + this.decodingPolicy = decodingPolicy; + } + + /** + * Returns the amount of buffered data available for reading. + * + * @param context the context to be used + * @return The amount of buffered data available for reading. + */ + int available(final Context context) { // package protected for access from I/O streams + return context.buffer != null ? context.pos - context.readPos : 0; + } + + /** + * Tests a given byte array to see if it contains any characters within the + * alphabet or PAD. + * + * Intended for use in checking line-ending arrays + * + * @param arrayOctet byte array to test + * @return {@code true} if any byte is a valid character in the alphabet or PAD; + * {@code false} otherwise + */ + protected boolean containsAlphabetOrPad(final byte[] arrayOctet) { + if (arrayOctet == null) { + return false; + } + for (final byte element : arrayOctet) { + if (pad == element || isInAlphabet(element)) { + return true; + } + } + return false; + } + + /** + * Decodes a byte[] containing characters in the Base-N alphabet. + * + * @param pArray A byte array containing Base-N character data + * @return a byte array containing binary data + */ + public byte[] decode(final byte[] pArray) { + if (pArray == null || pArray.length == 0) { + return pArray; + } + final Context context = new Context(); + decode(pArray, 0, pArray.length, context); + decode(pArray, 0, EOF, context); // Notify decoder of EOF. + final byte[] result = new byte[context.pos]; + readResults(result, 0, result.length, context); + return result; + } + + // package protected for access from I/O streams + abstract void decode(byte[] pArray, int i, int length, Context context); + + /** + * Decodes an Object using the Base-N algorithm. This method is provided in + * order to satisfy the requirements of the Decoder interface, and will throw a + * DecoderException if the supplied object is not of type byte[] or String. + * + * @param obj Object to decode + * @return An object (of type byte[]) containing the binary data which + * corresponds to the byte[] or String supplied. + * @throws DecoderException if the parameter supplied is not of type byte[] + */ + public Object decode(final Object obj) { + if (obj instanceof byte[]) { + return decode((byte[]) obj); + } else if (obj instanceof String) { + return decode((String) obj); + } else { + return null; + } + } + + /** + * Decodes a String containing characters in the Base-N alphabet. + * + * @param pArray A String containing Base-N character data + * @return a byte array containing binary data + */ + public byte[] decode(final String pArray) { + return decode(pArray.getBytes(Charset.forName("UTF-8"))); + } + + /** + * Encodes a byte[] containing binary data, into a byte[] containing characters + * in the alphabet. + * + * @param pArray a byte array containing binary data + * @return A byte array containing only the base N alphabetic character data + */ + public byte[] encode(final byte[] pArray) { + if (pArray == null || pArray.length == 0) { + return pArray; + } + return encode(pArray, 0, pArray.length); + } + + /** + * Encodes a byte[] containing binary data, into a byte[] containing characters + * in the alphabet. + * + * @param pArray a byte array containing binary data + * @param offset initial offset of the subarray. + * @param length length of the subarray. + * @return A byte array containing only the base N alphabetic character data + * @since 1.11 + */ + public byte[] encode(final byte[] pArray, final int offset, final int length) { + if (pArray == null || pArray.length == 0) { + return pArray; + } + final Context context = new Context(); + encode(pArray, offset, length, context); + encode(pArray, offset, EOF, context); // Notify encoder of EOF. + final byte[] buf = new byte[context.pos - context.readPos]; + readResults(buf, 0, buf.length, context); + return buf; + } + + // package protected for access from I/O streams + abstract void encode(byte[] pArray, int i, int length, Context context); + + /** + * Encodes an Object using the Base-N algorithm. This method is provided in + * order to satisfy the requirements of the Encoder interface, and will throw an + * EncoderException if the supplied object is not of type byte[]. + * + * @param obj Object to encode + * @return An object (of type byte[]) containing the Base-N encoded data which + * corresponds to the byte[] supplied. + * @throws EncoderException if the parameter supplied is not of type byte[] + */ + public Object encode(final Object obj) { + return encode((byte[]) obj); + } + + /** + * Encodes a byte[] containing binary data, into a String containing characters + * in the appropriate alphabet. Uses UTF8 encoding. + * + * @param pArray a byte array containing binary data + * @return String containing only character data in the appropriate alphabet. + * @since 1.5 This is a duplicate of {@link #encodeToString(byte[])}; it was + * merged during refactoring. + */ + public String encodeAsString(final byte[] pArray) { + return new String(encode(pArray), Charset.forName("UTF-8")); + } + + /** + * Encodes a byte[] containing binary data, into a String containing characters + * in the Base-N alphabet. Uses UTF8 encoding. + * + * @param pArray a byte array containing binary data + * @return A String containing only Base-N character data + */ + public String encodeToString(final byte[] pArray) { + return new String(encode(pArray), Charset.forName("UTF-8")); + } + + /** + * Ensure that the buffer has room for {@code size} bytes + * + * @param size minimum spare space required + * @param context the context to be used + * @return the buffer + */ + protected byte[] ensureBufferSize(final int size, final Context context) { + if (context.buffer == null) { + context.buffer = new byte[Math.max(size, getDefaultBufferSize())]; + context.pos = 0; + context.readPos = 0; + + // Overflow-conscious: + // x + y > z == x + y - z > 0 + } else if (context.pos + size - context.buffer.length > 0) { + return resizeBuffer(context, context.pos + size); + } + return context.buffer; + } + + /** + * Returns the decoding behavior policy. + * + *

+ * The default is lenient. If the decoding policy is strict, then decoding will + * raise an {@link IllegalArgumentException} if trailing bits are not part of a + * valid encoding. Decoding will compose trailing bits into 8-bit bytes and + * discard the remainder. + *

+ * + * @return true if using strict decoding + * @since 1.15 + */ + public CodecPolicy getCodecPolicy() { + return decodingPolicy; + } + + /** + * Get the default buffer size. Can be overridden. + * + * @return the default buffer size. + */ + protected int getDefaultBufferSize() { + return DEFAULT_BUFFER_SIZE; + } + + /** + * Calculates the amount of space needed to encode the supplied array. + * + * @param pArray byte[] array which will later be encoded + * + * @return amount of space needed to encoded the supplied array. Returns a long + * since a max-len array will require > Integer.MAX_VALUE + */ + public long getEncodedLength(final byte[] pArray) { + // Calculate non-chunked size - rounded up to allow for padding + // cast to long is needed to avoid possibility of overflow + long len = ((pArray.length + unencodedBlockSize - 1) / unencodedBlockSize) * (long) encodedBlockSize; + if (lineLength > 0) { // We're using chunking + // Round up to nearest multiple + len += ((len + lineLength - 1) / lineLength) * chunkSeparatorLength; + } + return len; + } + + /** + * Returns true if this object has buffered data for reading. + * + * @param context the context to be used + * @return true if there is data still available for reading. + */ + boolean hasData(final Context context) { // package protected for access from I/O streams + return context.buffer != null; + } + + /** + * Returns whether or not the {@code octet} is in the current alphabet. Does not + * allow whitespace or pad. + * + * @param value The value to test + * + * @return {@code true} if the value is defined in the current alphabet, + * {@code false} otherwise. + */ + protected abstract boolean isInAlphabet(byte value); + + /** + * Tests a given byte array to see if it contains only valid characters within + * the alphabet. The method optionally treats whitespace and pad as valid. + * + * @param arrayOctet byte array to test + * @param allowWSPad if {@code true}, then whitespace and PAD are also allowed + * + * @return {@code true} if all bytes are valid characters in the alphabet or if + * the byte array is empty; {@code false}, otherwise + */ + public boolean isInAlphabet(final byte[] arrayOctet, final boolean allowWSPad) { + for (final byte octet : arrayOctet) { + if (!isInAlphabet(octet) && (!allowWSPad || (octet != pad) && !isWhiteSpace(octet))) { + return false; + } + } + return true; + } + + /** + * Tests a given String to see if it contains only valid characters within the + * alphabet. The method treats whitespace and PAD as valid. + * + * @param basen String to test + * @return {@code true} if all characters in the String are valid characters in + * the alphabet or if the String is empty; {@code false}, otherwise + * @see #isInAlphabet(byte[], boolean) + */ + public boolean isInAlphabet(final String basen) { + return isInAlphabet(basen.getBytes(Charset.forName("UTF-8")), true); + } + + /** + * Returns true if decoding behavior is strict. Decoding will raise an + * {@link IllegalArgumentException} if trailing bits are not part of a valid + * encoding. + * + *

+ * The default is false for lenient decoding. Decoding will compose trailing + * bits into 8-bit bytes and discard the remainder. + *

+ * + * @return true if using strict decoding + * @since 1.15 + */ + public boolean isStrictDecoding() { + return decodingPolicy == CodecPolicy.STRICT; + } + + /** + * Extracts buffered data into the provided byte[] array, starting at position + * bPos, up to a maximum of bAvail bytes. Returns how many bytes were actually + * extracted. + *

+ * Package protected for access from I/O streams. + * + * @param b byte[] array to extract the buffered data into. + * @param bPos position in byte[] array to start extraction at. + * @param bAvail amount of bytes we're allowed to extract. We may extract fewer + * (if fewer are available). + * @param context the context to be used + * @return The number of bytes successfully extracted into the provided byte[] + * array. + */ + int readResults(final byte[] b, final int bPos, final int bAvail, final Context context) { + if (context.buffer != null) { + final int len = Math.min(available(context), bAvail); + System.arraycopy(context.buffer, context.readPos, b, bPos, len); + context.readPos += len; + if (context.readPos >= context.pos) { + context.buffer = null; // so hasData() will return false, and this method can return -1 + } + return len; + } + return context.eof ? EOF : 0; + } +} \ No newline at end of file diff --git a/src/net/lax1dude/eaglercraft/Client.java b/src/net/lax1dude/eaglercraft/Client.java new file mode 100644 index 0000000..919fcbd --- /dev/null +++ b/src/net/lax1dude/eaglercraft/Client.java @@ -0,0 +1,132 @@ +package net.lax1dude.eaglercraft; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; + +import org.teavm.jso.JSBody; +import org.teavm.jso.browser.Window; +import org.teavm.jso.core.JSError; +import org.teavm.jso.dom.html.HTMLDocument; +import org.teavm.jso.dom.html.HTMLElement; + +import net.PeytonPlayz585.opengl.GL11; +import net.PeytonPlayz585.storage.LocalStorageManager; +import net.lax1dude.eaglercraft.adapter.EaglerAdapterImpl2; +import net.minecraft.client.Minecraft; +import net.minecraft.src.Session; + +public class Client { + private static final String crashImage = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAATEAAABxCAYAAAC9SpSwAAAQtnpUWHRSYXcgcHJvZmlsZSB0eXBlIGV4aWYAAHjarZlrkly7jYT/cxVeAt8gl0OAZMTsYJY/H1jdsqQrh+2Y2yXV4/QpPoBEZoIdzv/+zw3/4KemFkNtMvrsPfJTZ5158WbEz896zynW9/x+Svp6l369HuTrTcxcKn7n5+Pon9f0ff37vq/XtHjXfhpo2Ncv9NdfzPo1/vhtoPLTyjJv9tdA82ugkj+/SF8DrM+2Yp9Dft6Cns/r1/c/YeB/8Kcib+wfg/z+uQrR242LJedTUok851I/Cyj+P4eyeJN45hfZ39V35fM8v1ZCQP4Up/jTqsLvWfnx7restPPnpJT+uSNw4ddg9h+vf7wOZP4Y/PBC/DNO7Otd/vX6rfH8vp3v//fuEe49n92t2glp/9rU9xbfO25UQl7e1zoP4X/jvbzH5DEC6DWys6NF5WFppkxabqppp5VuOu/VkrHEmk8WXnO2XN61USTPbCWGlyce6WYps+wyyJ+R3sLV/GMt6c0733SWBhPvxJ05MVj6pD//PY9/OdC9DvmUPJikPr38pOxAZRmeOX/mLhKS7jeO2gvw9+P3H89rIYPthXmwwRX1M4S29IUtx1F5iS7c2Hj9lEWS/TUAIWLuxmJSIQOxp9JST1FylpSI4yA/i4GG14aSgtRa3qwy11I6yRnZ5+Y7kt69ueXPZTiLRLTSi5CaWRa5qhAb+JE6wNBqpdXWWm/SRptt9dJrb7136U5+S4pUadJFZMiUNcqoo40+ZIww5lgzzwI5ttmnzDHnXItJFyMvvr24YS3NWrRq066iQ6cuAz5WrVk3sRFs2tp5lw1P7L5ljz33OukApVNPO/3IGWeedYHaLbfedvuVO+6860fWUvik9S+P/zxr6Ttr+WXKb5QfWeOrIt9DJKeT5jkjYxnFIGOeAQCdPWdxpFpz8NR5zuLMVEXLrLJ5cnbyjJHBelJuN/3I3T8z90veQq3/r7zl78wFT93fkbngqfsXmftr3v6Qte1qY7GElyEvQw9qLJQfN608+Icm/eev4b/9wt8/0In35Clj53MtbQbay3TJha/Pkal9UOin9o2snXLdVJzrX8x6El9Up6p2YeDZ7wV5Y/ZWZzDrsVZAxUREcEtXINlSba6zTUo7DqNNZZ7E0GlIa3OfMnNv2cYao2mOEnZWMnx6MUFcO2kfd3QoZ7IO65tFgligM06VYamjx10GGcZxALBZbupiJbS1j5a+V9tDt/GvGR/r3nEymiW+cplN17qzsLtxyazNKjvJParJP+8Y0tKjru0vjl+vc9j299JPInSpnbbXGwBy3FFMWMZI5Uw7N5pqa6FLzXXavN2aLGB6zMbTnLuwLg3RomLPiV3HgUku87QbJ/vPsqlllauVYKcDOZfiTyyjorvMlm2f3G+8RnHU26nhpTqhsBk7QSEPiSKACKic+QARYJfY662kSbJyz20y4WC4mxqDTLRvdiqn4XOONR0EhnG4or7ZVKSV3SRYHcXIcdzjpK7spLVzqLEac1lnJ7T3trXSAgEbJb917dLbbgUs5cy+0mgiQa2kju+LR8HSIRLpggxyCUvEO5hWkQyq/UJFkMvIOmO9ZkIOtggga2opgLhVd2LLrZ6LMPGFTTjGXQBFsi8/GtWg+xxlaYQtH4WpABhgjToaKW0BWEBqZ7Y9xSprJzQ4EBIz9EBImNHdT7FThzuVx8CT7d25bm06r5Y7TGu4MJT0wm74vCZBJPbp4jZI7ny5A1NsEWq8x86u0RbOxjTLOXgVIZTNDfssWH8lcOSOaDIXN5OAWiFCpBuA4hObzbQJ2jLbnaKdN1H96XZFoVm6BGh3b2Pxslg5TpdBdNiNwEFbnxTSYvEwY1WBMoou0quCj2erCyAMT/EM5c4tk7ITRwOpJb98gV0Il6/gw4jLnqSA/MbVxAVtuan02dhz39d6C8uBxw0yG4qguQ8tE9Jm3Y1NqxiqA4OkzSC7rmOJSQ0FA6+TYqSCZM4bjl1+2TcoQAQQiWK9wts5euIHQkcNIQwogqJEiaVFG6cpl7rXy6vIuAP1VJ0J7yC3G7Xy3XXwnNGTm/CratGOxFJ8InCPUc3crSdDUCmfyZ1XQ+sehTxAakljQkbCHUTrIcSUhXU2v+m72mUcWwqiL5AZaA52YBaWoTnI7dBKVmOjR0gmpWJOfqwuFp8ecJTuAiaiS/ds2PPqVhqkZmQZ+WaTgUZIWTLEjKceUE2bxicDi9PrCi43qCEDowuMjkcOXrnQQKJEIK6tCoeFTmhZy4QzjTXCgQDTOerenNAaalzHI4ziLMR64mnMRN8KDUKviZqL47hkAzKzBUHYxXAcah6yVw88vlPGrWUkoRYzvgP/Oy+sQ8sCA+anbvRz17B+SM51PQdXw43GKZBNupUqE+e2jQRUihD2jXclhnCpS9QJXFkzHQN0SLAHqM6Z5oAqhb1ZdzN3FUtBdFEh+g1CgvNJ+GoQBby22qMXaoqw5IbDD/V5N5g68zUS2+eN+0IxOKFxk+3nahRGavBX1kwG8c3XnRD5Rwevl9IIWg60XPMS7FWOw5BK7W8+34HrNGFs6AKiuTtQRS4vrdHqUrQn6BI1GiVQ29QxSrQoLFKEgG7WfZR9fqSvbnG12rhGw+wutwG7Yc4obQqqysLVUWvMCTq8PduHQAHBtSfM53L44Hv6E3Hg4ClgEmpTLh1lX5fpG8WzzgxbcocKWyeLKH1TYuOKEtn8rAXD3fZW58hbKmZPF/fiRvGJ+EDA5/3xXCeQdAHTdKLU4llYinQGxd8Nwpm44WTUlYzM0BiBYy5q1SGZ4fiizmbQggZEkU2fgzftJR13OLaEeihuGy8a1yCjBjZc24kRECWrCZuCYaaqWK5SO2FNInPp7SbaQSdKr4XngTInYZuQhPL+uvt+RiY197sHtYRmV4Z+J6leOYcN7hy2hdmJ3HCa2Smz45pWgc2nIuUT6UTz6HmxEr65thqqTn43ecYfWJB6pvusxL1EcbVJvdaCaaCCqLlqVBob2cTVzf+HOROZ6PkSnYc4nDdbW1R5r3WjZvKYHi5sh8LGasG7/QMFGGS5HyMh4/g01IU12spNOMlQKLSOJBsNeZhRDBq2Ca6wS+3rvhvwIWp1RAhK6CeQlLMbdxUnvUFoFSCEjq5hHYSFetT4Fc0nOXJeZ6x2n/oPNL9UrJnrMqNHdzlVend/tolGDriXJWAYm+RcstiIk8XO6xL3jmO79BNwILKp0H0GynCHw2Gft4erFLqFg+JUcrEhNDaxoPl89vCTMfxCLwvYu7Ok/vVQDKVgYeFT/Dfliu/FqhYBR3i1ZUxQKoveQhAVycoHW00NemeHVzF5fvVO2ATGplIaUKLrmS6IlNXIwXPhEQJLhtPyksOctOc7PVeveGFurBNcBXkPLJnLMI3SPngyJEqIBlmrhYLYyzuJPPBr0BtWZMC3eCqaUQiFNvJiHRIG5Sz6OfqHXeVspDaxKN9bwONqMTfVbAUVceMH8zZc3jVwCaxhLLKeGMNPG/B9mD6bznYXT4xIYPopEYp8u1+l9pTmoj92nJAQVUuJbLzTQCUIO9saYB2rh33FUdOcQnnUo1dkeF0IvhSM2RCMEp4P37SIK87IDtx4rpNjceB2DCCQEDwm8xwcNrwPZ5F+BlbvZ+iUKGndCyCYpYVwUpYlOp2s6oLGXgZb78N5Zafup1V1Is6VPuu1WVRDnt3GhtwEIcN2swl3R03rwr3jOTdNG6R1n5O9NPzg0/ud5ITrDBeIuLnpXMC+Og/Q7R8luPA1C4sbQdw7pwhJ4liQABaNYRKmBwZ0/4YvXjmgG7sBb8xlN0jQCwmvTHjhw4yPw0ZGsEchK734RqoWcVsULPn1rlAJ69ru2FwNuHczIXJeux54qcA2NHrY0lxeR6Bkb7P749pB0XunMyr1pd614vx1jF3gmOLOFWX1GhOY/uM09wD43swqRZxrtuOIoorpNWlmMNMVZJPHAPXofVEyPfgAmOMg+AkePn7wiF+ODmt7ZYuPw3YDnF1KBUg0Xi6PuOWAn8gdssLzOjTbddueqHPtiDhMTysJVTvNA1bnDYonejAj6fEAgsYlNTDngDDZRaK5modo0JRdvvIQHmH/V76NFt2dAyWApSHTNMjcKJWVOSWFpuiMa1k3P2RB2jAqQ2DlgssUsASTYRZ3Nu/wsBxEFV+DVLUBj2IP8Z5lhEML/XBh8fXPM2HDvH1GN+4krwRoAdbsfPZO2WkycKDChN40J9wiYk0LwRLhgyOVBG9kBmntrMzQtVgRlaW9REcw5YO2YAc+PZxC4cttFyigJwh4KGI9xTkKDp6XIeGSwjS5K5bfT7kSfQglvDZ9pzCsxgqQysRl5EnJE2eK1k0QqtH+DSMeVJE0Z0KcjsdiFUV01TsinsN0MmeWnDo4XN7HDe8NvUEin+4QsFKUA02X293xBIuUj5Kun3O/1n1D/gN+IH6wJyPSqy7NsE3OTn14xNYoqwZ+/ESBRtAgEqz+PYOdT6KKGPspRUD8Bshj0bTMluEwgtGxl158e08/KLm0ITgFmhTgMG+rNICG7uNvsQk4MmoeHOHCqhFm2hBGY4HtyEe/5dElQJfh6MOtdAoMLLjppIvGmyJLfr78VkQzd8gpJVCQNkoP64jBwznSiqsfeOIX8B74EUQeaoFIWTEstV4vTDOGHQh92XQS8aaXqhx+lKXkkShCYpimC5N6t3fBGETtWe3s3Q8mqF2ak4NFKjN4Xlitx571mru5Nb271cL4F5iyYD8qEidIKAqFhsgu6k4m0BznhqkW8Jcld6GIbHnVwjjdMD5IS8EBDRejTmvvUMM/k0L2Qsil9kd2uI0Kn/Xg1cDOlcjSs0PHNRr0QKzxiGPhI1FJPx6dyc2EL2awLcKOTPixghGwjYdEDUQxA6Wiu62MMUgVvouX1q8f1A03jEx6HCUIip8OY/KgrARQAVrbADc4wg6qh8yiQXCyHyusipfJljJU54koJTZfG7J1SCqmFRkg+Xt6tSeKd2G0WCXRYmgWMhD8RABpAJ2GQJQSDoLdhe5Y+/BjSHx4MUgCZqKxYXr3RQFCzB+yYe90qd3PEJEhP/zFmFLyaCnvWuJuqET84A+6O9WJaNDcQ1l9WsDLGGaGrn/7qWAmngb7l4+N1te44P38EBk/SI/FvntzlgL04qfJpIAbQ8emODPjRtJEjpA0erPKenW8v86hJ6D8xzmt/w2odn/ClBI6NoT1ySmgy7dxlzcEP91ObRjLJrXIEf4yAZtJC71sNbgAoHdcVHdf1RcdxA1YL2/DIC7aBqrAOnLrR/XJkQi1OpfNzDfdjoEQPN3BCezs1AsY/IQVyQmV9orsT8yf/3HU/BO9Y4I9GIwGiYL2Y2B6H/WWEUR5awuPszBvaYr/daJL8NOHCQrdHuF6EadM9yfU2hp0hKy60KdTfMSK1g+w4QUajQkyDWpaxt3glWfAkk0ylLxeBw4isbTkHRI9ZYMxZcJg6SMJ5gaT5tvTNegyS+0oPxaymQZECg+qa0HX9dI6M/Eq8C0+kWD4oYafVHrcticUeio06LAhyMOLXBjX5SewUOQLeMRBHw/Nt/SOX18Oc0yuNRmX43iPBam3TosB1vG96acj9PDjLP23V8OwMW4rER1BD+iK4vKDk11fK1l68WOfsRs6ktd6f6YvxGxi4djsB3OsxTHy3/w9IfwNf8n440BILET+f7LnjZBrgBfeAAABhGlDQ1BJQ0MgcHJvZmlsZQAAeJx9kT1Iw1AUhU9TRZGKg0GKOGSoThZERRylikWwUNoKrTqYvPQPmjQkKS6OgmvBwZ/FqoOLs64OroIg+APi6OSk6CIl3pcUWsR44fE+zrvn8N59gNCoMM3qmgA03TZT8ZiUza1KPa8IIIwhCBBlZhmJ9GIGvvV1T91Ud1Ge5d/3Z/WreYsBAYl4jhmmTbxBPLNpG5z3iUVWklXic+Jxky5I/Mh1xeM3zkWXBZ4pmpnUPLFILBU7WOlgVjI14mniiKrplC9kPVY5b3HWKjXWuid/YSivr6S5TmsEcSwhgSQkKKihjApsRGnXSbGQovOYj3/Y9SfJpZCrDEaOBVShQXb94H/we7ZWYWrSSwrFgO4Xx/kYBXp2gWbdcb6PHad5AgSfgSu97a82gNlP0uttLXIEDGwDF9dtTdkDLneA8JMhm7IrBWkJhQLwfkbflAMGb4G+NW9urXOcPgAZmtXyDXBwCIwVKXvd5929nXP7t6c1vx8743KRRjbQVgAADfdpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+Cjx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IlhNUCBDb3JlIDQuNC4wLUV4aXYyIj4KIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+CiAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIgogICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgIHhtbG5zOkdJTVA9Imh0dHA6Ly93d3cuZ2ltcC5vcmcveG1wLyIKICAgIHhtbG5zOnRpZmY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vdGlmZi8xLjAvIgogICAgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIgogICB4bXBNTTpEb2N1bWVudElEPSJnaW1wOmRvY2lkOmdpbXA6NDJlMTU3MGEtNmMyZS00Y2E1LWI3ZTMtOGI4ODI1MmMwZDMwIgogICB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjU1NGY3N2UwLTc4NmEtNGFlZS1iYjhmLWNhYTBiZGNiYzE3MSIKICAgeG1wTU06T3JpZ2luYWxEb2N1bWVudElEPSJ4bXAuZGlkOmNmMWYyMjUxLWIwY2QtNDE1NS1hMjAyLTExNGI0ZGM2MmFhNSIKICAgZGM6Rm9ybWF0PSJpbWFnZS9wbmciCiAgIEdJTVA6QVBJPSIyLjAiCiAgIEdJTVA6UGxhdGZvcm09IldpbmRvd3MiCiAgIEdJTVA6VGltZVN0YW1wPSIxNjQzMDYxODUwNDk0OTc0IgogICBHSU1QOlZlcnNpb249IjIuMTAuMjQiCiAgIHRpZmY6T3JpZW50YXRpb249IjEiCiAgIHhtcDpDcmVhdG9yVG9vbD0iR0lNUCAyLjEwIj4KICAgPHhtcE1NOkhpc3Rvcnk+CiAgICA8cmRmOlNlcT4KICAgICA8cmRmOmxpCiAgICAgIHN0RXZ0OmFjdGlvbj0ic2F2ZWQiCiAgICAgIHN0RXZ0OmNoYW5nZWQ9Ii8iCiAgICAgIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6ODUyMGQ4YTMtMWRhZC00ZjIwLWFjOTktODg4OTJkZDExNDQ0IgogICAgICBzdEV2dDpzb2Z0d2FyZUFnZW50PSJHaW1wIDIuMTAgKFdpbmRvd3MpIgogICAgICBzdEV2dDp3aGVuPSIyMDIxLTEyLTE3VDE3OjIyOjQ4Ii8+CiAgICAgPHJkZjpsaQogICAgICBzdEV2dDphY3Rpb249InNhdmVkIgogICAgICBzdEV2dDpjaGFuZ2VkPSIvIgogICAgICBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjJkY2U5N2M4LTBkZjItNGQzNi1iMzE1LWE0YjdmMmUyMjJiNSIKICAgICAgc3RFdnQ6c29mdHdhcmVBZ2VudD0iR2ltcCAyLjEwIChXaW5kb3dzKSIKICAgICAgc3RFdnQ6d2hlbj0iMjAyMi0wMS0yNFQxNDowNDoxMCIvPgogICAgPC9yZGY6U2VxPgogICA8L3htcE1NOkhpc3Rvcnk+CiAgPC9yZGY6RGVzY3JpcHRpb24+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgCjw/eHBhY2tldCBlbmQ9InciPz61xwk6AAAABmJLR0QAnQCdAJ2roJyEAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH5gEYFgQKOBb3JwAAIABJREFUeNrtvXl0lFWePv7UvlelKvu+koSQRQiyBJGISEB0hFYZwW1sp4/2csaZ1jlz5sz80cc5c7rnaI8zju2o09qiIrKowEGURXYI+5IASQjZl0plT2rff3/073O/byVVlUQSRPs+5+QkkMpbb9333ud+lufzuaJgMBgEBwcHxw8UYj4EHBwcnMQ4ODg4OIlxcHBwcBLj4ODgJMbBwcHBSYyDg4ODkxgHBwcHJzEODg5OYhwcHBycxDg4ODg4iXFw/GWAqgHHfv+ufyv8+q7XvVMh4rWTHBwc3BLj4ODg4CTGwcHBwUmMg4ODkxgHBwcHJzEODg4OTmIcHBwcnMQ4ODg4iXFwcHBwEuPg4ODgJMbBwcHBSYyDg+NHBCkfAo6/ZAiLov1+P3w+H/x+P/x+P4LBIDQaDWQyGR+oSYyjSCSK+G9OYhwctwiHw4Guri44HA54vV74fD72FQgE4Pf74Xa74XA44Ha74Xa7EQgEsGrVKqSnp9+2BTlT5EJETT+PJZxgMAixWMx+FwgEIBb/2VGjn4W/o78P9/qxPSXo2pFez0mMg2MSC7mrqwt/+MMfYDabYbfb4XQ64XQ64XA42HeHw4HBwUFIJBJIJBLExMRg4cKFSEtL+0GSmJAwxsLn80EqlSIQCIRYomKxGD6fD2KxGH6/n31un88HiUQCv98fcm26Pr2H0LIlkhLeg/B3RIbCa30XS46TGMePHn6/HxaLBVu2bIFcLg9ZXLRQpFIpDAYDDAYDW0RyufwHRV7ChU9kQeQhtHyInLxeL7OwyFoS/o7caCI1+k7XlEgkjIiEJOXz+SASiSCVStl9SKX/j2pEIhH8fj8kEkmI1Sa0Gqcy7pzEOH708Hq9GBoagtPphEaj+dF+zrEEJvw/oTtHxCMWi0NcTaG7SeQmvMZYt3Ts/wnJSPh9rLUVjqTGur1TITKeneT40UMsFsNoNGLjxo3IyMiAyWSCSqX6wca5JrLGxhIDEVYwGGRJC7vdDofDAb/fz6wocifJQqPfjf0/YZdYkUjEXHOKMXo8HhZ39Hq9zJqj1wvd1bEu6Fi3M1yMjVtiHBEhjJ0Id9ofOuRyORYsWIDS0lIMDw+js7MTp0+fxs6dO9HZ2fmjs8aE1pbQPZRIJBCLxXC5XLDb7VAoFFAoFAgEArDZbJBKpZBKpZDL5XA4HFCpVAAAp9MJhUIBh8MBpVIJt9sNsVgMmUwGt9vNMroej4f9rFarYbVaEQwG4fF4EB8fz0hOrVZDJBJBIpEwciNSI8KUyWRRkxGcxDjC7uCtra1wOByQy+WQy+VQqVRQKBSQyWSQy+U/WKmBSCRin0mn0yEtLQ2JiYmoq6tDR0fHj4KoiQCECz8QCDCioCwsEQK9zuVyQSwWQ61Ww+PxIBgMwmazQavVMotJqVTC6XSy10gkEuam03vp9Xq4XC4olUq4XC60trbCaDQiEAjAaDTC4XBAo9EwCzgQCITE5AKBAKRSKSMwipmNjedxEuOIGjf69NNP8dVXXyEQCLAFn5SUhOTkZGzYsAGzZ89mE+uHbKmIRCLIZLJpS/HfCRAG2YWkRmRAlo3L5YLNZoPNZoPX62UWkVwuZ1aWVqsNIUWynnw+HxQKBQvc0/WlUincbjdkMhmGhoZw9uxZdHR0YMmSJYiJicHQ0BBMJhMkEgm8Xi/kcjl8Ph/kcjl7HyI2IjBKBIyVfnAS44iIwcFBNDU1obe3NyQGUVdXB6/Xi6qqKvAzZe5sSzpcZpJcSrK+Tp06hT179qCvrw9xcXFITExEQUEBli1bhmAwiJGRESQlJUGpVIaQIhGX8FokrSC5RktLC7Zt24a6ujrIZDLcuHEDOp0OFRUVyM3NRUZGBrO2iGjpGmKxeFycLFoigJMYxzh0dnZiYGAgLFGFy0Bx3FkEJnw+JN4F/ixtcDqdzLru7u7GoUOHUF9fz17/wgsvID4+HkajEXFxcbBarRCLxZDL5ex6ZIUR6dB7+nw+RnLDw8N47bXXYLfb2bWfeeYZmM1maLVaJCcnM6kGWY6kVRNaaGQhC63+aCTGs5McCAaDqK+vR19fHx+MH+jzE36nBU9EQe4aiXiFmi0AuH79OrZv346+vj7o9XrI5XLY7fYQl1Emk41T7stkMhYzHR4eRjAYhFqtDiGejz76CDt27EBHRwcjV6HmjK5D90rvMzZ2F20T5STGAafTiaamJgwNDfHB+AGCLBdhjI8sHKlUCoVCAY1Gg2AwCLvdDrfbHfL3x48fx7vvvgsA0Ov10Gq1UKvVLFutUqmYJRYMBiGVShlRKhQKuFwuZrWNJdf09HRcvHgRVqsVEomEXUutVjO5BxEaxcjofYhEJ7LEuDvJgf7+fpjNZrjd7h+ldurHDmHsi6wbl8vFYkqk16LAvtVqDXsdt9vNpBcej4dlo+12O5RKJex2O1QqFcto0u9IIxYMBjE8PBxyzRUrViA7OxuLFi2CWCyGw+GAQqHA6Ogo9Ho9vF4vI1uPxwOpVAqXy8Vc12AwOGFWnFtiHOjo6EBvby8fiB8ohPWLfr8fLpcLHo+H/dvn8zEiWLx4Mf7t3/4NTz31FHP9qqqq8PrrryMpKQlutxsjIyNQqVRMsCqXy2G1WqFQKOB0OhEMBuH1elmxvMPhgM1mg16vx5tvvomKigoAwEsvvYSqqirce++9MJlMTIM2OjrKZBvkMlJG0uVyQSqVMtd3Mtnw226JCdW+Xq+X7RCUSpXJZCHp7+myDMYqgClYSfqZkEH5/0V/QrP5dlkowvEh8SDttDQJ6MFOxz35/X40NTVNSi8108F9YVaN5gZ9duHnp39zqzHUpaR5LRS2isViRkgqlQoZGRkQi8W4dOkSMjMz4XA4UF5ejtWrV0Or1SIYDEKv18Pj8UClUjHrTavVwuv1QqlUMq2YRCJh1lMgEEBMTAzmzJmDhx56CK2trbhx4waqqqqQn58Po9HI6i71ej3cbjc0Gk1I9pNcSYrZTfYZS2/XwvT5fLBarejs7ERrayva2trQ3d2N0dFRlnKVy+XQ6/VIS0tDRkYGcnNzkZycDK1WC4lEMqVJS+weCATgcDhgtVrR29uL/v5+DAwMoL+/H729vcwcFha1qtVqGI1GpKWlIT09HZmZmYiPj4dWq2Xm+nSPj9/vh81mg9lsRlNTE9rb29HZ2YnR0VF4PB6IxWLodDqkp6cjLy8Ps2bNgk6nm9S9KJVK6PV6iEQiuN3uEJPfZrOhoaEB/f39Ya9FAdaBgQFYLJawpn0wGIRKpYJOp5uy9oo+u9VqhdlsRktLC9ra2tDV1YWRkRF4PB42N7RaLVJTU5GZmYmsrCykpaVBr9dPeW7MVFzRZrOF7RgRzYIyGAxQKBTTcg80DiqVKkS2QBuzSCRCXFwcVCoVXn75ZfzTP/0Ts9JiYmJY0F8mk4Vs3nR/dH2aAySEjY2NZdcvLi5GRkYGHnvsMUgkEuj1eqhUqhBSJbIiMa5YLGbF4uRC3jEF4MFgEG63G11dXaiursaRI0fQ0tLC2p643W6mO6EBk0gkUCqVUKvV0Gg0mDVrFh544AFUVFSwwZoIPp8PnZ2duHz5Murr69HY2Aiz2Qyn0wmPxxPyJawdE04GqVQKpVIJlUoFrVaL/Px8LF++HAsWLEB8fHyIlXYr4+P1emGxWHD27FkcOnQIjY2NsNlscDgccLlcIeNDY6PRaFizvonuQSKRYN26dXjqqacgkUjQ2tqKX//61+z3Xq8XPT09Ua8jl8vx+uuvw2AwhCUpr9eLDRs24LHHHoNOp5vSZ+/u7mafvampidX0UTnL2LmhUCjY3MjOzsaKFSuwZMkSJCYmfq8VBWfPnsWHH34Ii8Uy6XlhMpnw8ssvo6ys7Jbm0tisJFmzwp+pjQ4RhE6ng9FoZNYuWfc0p+RyOUsAiMViuN1uqNVqZnAIu1PQ60ltTxsmCWz9fj+TUIhEIng8HiaspcQDbZZkRd4RJObz+dDb24vDhw9j69ataGpqYo3mxj4A4Q1TDdbo6CgAoLW1FdXV1ViyZAmeeeYZlJWVTbhz2Ww27Nu3D6+99hqkUmlYlzHaJCCT3Ol0soxdU1MTDh8+jAULFuDJJ5/E/PnzJ20JRQrGjoyM4OzZs9iyZQsuXLjASCva33i93oiB2bAPWCrFtWvX2HX7+vpw7tw5aLXaKd1vT08Penp6wv7OarVi+fLlcDgckyIxv9+P/v5+HDt2DDt27EB9fT0rRp7M3KDPT3OjoqICzzzzDObNmxeS4r+dMJvNqK2txeDg4KT/JjU1FT09PSgrK5uWuBiRFMXCKGBOJCSXy5kRoNfrWaAeADweD7Rabch4kzrfarVCrVbD6XRCLpezwL5IJGKB+pGRESiVypCMIs1ZymxSfE4ikaCnpwcqlQoejwcKhYLdR7iNmdZuJCt/RkjM7XajoaEBmzdvxvbt28e5lWRO6nQ6xvjDw8Ms7jF2wo+MjOCrr77CwMAAfv7zn2PRokVhU7pj/25wcBAxMTFhCdbn8zGrhlK65HaSeTv2ena7HYcOHcKVK1fw8ssvo6qqCkajccpEFggEYDabsWfPHrz33nsYHBwc1xDO4/Ew91oqlcJms8Fut7NJGe1zU5yRHnxnZyfbcbu7u9nPt1J2I4zd+f1+9PX1hYgcI8Hj8aCpqQnbt2/Htm3b4HA4QtxKCiTrdDooFAoWaCZCHjuODocD+/btQ0dHB1555RVUVFSwBXG7QELPwcHBkJq/icbPZrOho6Pjlls5U2BfSGD0M4HKeajO0eFwsPIichnJQhq7CWq1WvZ6ioGRYaBSqRhxk1VGsS8iOoqjUf1qY2Mj+vv72fpXq9VQKpVQKBSw2WxQq9WQy+Uhsetohsu0k5jH48GFCxfwhz/8AceOHWMmvt/vh16vx4IFC5CdnY2EhAQWz/B4POjp6cGFCxdQXV0dNmgtEolw7NgxGAwGmEwmzJkz5zs9eLlcjmXLlqGoqAgmkwlqtZotDqfTiZ6eHly+fBnnzp1jpRZj72NgYACvvvoq5HI5Vq9ePaXdPxgMwmKxYOvWrXjjjTfGuaUikQgZGRm47777kJyczMbIbrejra0Np06dQkNDQ1gC8vv9qKioQGFhYUjDOZ1Ox3ZAuVzOAq83b95kE3EiQvN4PHC5XGx3T0lJQXx8PEwmE2JiYpCWljapuVFbW4sPPvgAX375ZciCoU4T+fn5iI+Ph8FggEwmg8fjQV9fH86fP4+zZ8/C4/GMeyYSiQRXr17F//7v/yIuLg5z5syZVNhhOud8TEwMVqxYgYGBARYSiER4LpcLGRkZKCwsDGkcOB3upNPpZIXVtFELyUcikbD6SHL/iPjGEhhtFB6PBxqNBl6vl20sJFAlN9PpdLKYl7BUiLKQSqUSwWAQPT09sNls2LNnD27cuIGnn34aS5cuZfer0+ngcDhYsoAqDW5b7aTf70dtbS3eeOMNnD17lhFYIBBAXl4enn76acybNw9JSUkwGAzMRw4EArBarViyZAnS0tKwa9cu5nsLoVAosHPnTpSVlSE1NRVGo3HK9xgXF4eNGzdi/vz50Gq1ISRCVl9TUxP27t2L7du3M0thLJG5XC689957yMvLQ0lJyaStGrvdjgMHDuDNN98MG8NRqVRYv3491q5di9jYWDZGPp8P/f39KC8vx8cff4xz586FHf/09HSsXr0a6enpIVlMcn0XL16MrKws9PX1wWKxoKmpCUeOHEFra2vESRIIBLBy5UoUFxdDq9VCo9EgJiaGiSI1Gg0MBgNiY2Ojzo3Gxkb86U9/wrZt20LcTqPRiBdffBHl5eVITU1lwW5aDDabDUuWLMHu3buxa9eusO60TCbDiRMnsGfPHqSnp8NkMt02ElMqlVi+fDlKS0vR0dGBd955B7W1teNCGHa7HZWVlbj33nuRnZ2NxMREVoozXa6kRCKB1WrFwMAA7HY71Go1tFotDAYDtFoti3+5XC5mnZEVR9YSuY+0YYjFYthsNvY+9DyFbit5KqOjo1CpVEzN73a7YbPZWNueEydOsHlG4tbOzk5kZ2dDo9GwzCfFy4RdYG+LJdbW1oZNmzbh0qVLIW/qcDiwdOlSrFq1CklJSWHTwwaDAQsWLIBUKkVvby++/fbbsItcJpPh4MGDWLx4MWJiYqZkjYlEIqSkpKCkpCQsAUokEphMJhiNRphMJvT29mL//v1h41QikQhNTU346quvkJeXN6mOoX6/H3V1dfjwww8j3ndMTAzuvfdeJCcnh7xGKpUiKSkJK1asgFQqhdVqDal/o7E5fPgw4uPjsX79emRkZIx7n8TERCQmJgL4cxuW5uZm9Pf3o6mpKaL14vP5sGbNGtxzzz3M1J9K62ayPrdv347t27eHEJhIJMLs2bPxyCOPICEhIew463Q6FoNsaWlBdXV12BinWq3Gl19+iVWrVsFgMNy2jhtisRgJCQlQKBS4efMm3G73OEmP0+nE+vXrsX79ehQXF0957k40vmKxmJGO1WrF5cuXUVNTA5lMhlmzZiEvL4/Fk4WCVgrIk6SChKs2mw0ajQYSiQROp5PNF51Ox1r2kOXX2dkJsVgMhUKBffv2oaSkhM0zkUiEoaEhRq4XLlzAJ598AovFgueee471FqPNklxI8iSEh4zMOImRiXjkyJFxE8xms6GgoGDCYLJYLEZRUREWL16M8+fPh7WCZDIZjh07hps3byI/P39KrpxMJkNZWVnYONnYhZOZmYm1a9fi9OnTEctxAoEAjh49iscffxx5eXkTTkqn04mvv/4abW1tEeUMubm5SElJiXgtpVKJiooKXL58GZ2dnSFui0gkQl9fH9566y1IpVI8/fTTiIuLi2pBxMbGsh060gIRiUSIj4+fdHY43Oc+fPgwtmzZMo7sRSIRSktLodfrJ7xOTk4OVq5ciWvXro1ThhMGBgZw9uxZFBUV3dbYWE9PD7755ht89NFHaG1tDZkjCoUCf/u3f4t169YhLy9vwnjudwUFxWNjY1FQUID29nacPHkS/f39OH/+PG7cuAGFQoGenh4kJSXBbrcjMzMTZrOZlQRZLBYYDAZYLBYkJiYyAjEYDMydbG5uZhsZSZcozLB161ZUVFQgISEBCQkJEIlEIZZYbW0tW082m41ZhGKxmMkthPKQ20pily9fxsGDB8fFAoLBIDObJ6OHUavVyM/PR3Z2Nq5evRpxsdfV1WHp0qVTIjGFQoHy8vJJpeIlEgnKysqQm5uL8+fPR3zd0NAQGhsbkZubG5XEgsEgzGYzTp48GTEGIhKJMGvWrAk/k06nw4IFC/Dtt9+OG2+aBJ988gkKCwtx3333hY11jI2nTDbu8l2ysG1tbdiyZUvYeJZYLGYxvMlkWufOnQuj0RiRxCh2+vjjj7Ns2UyCmknu2rUL27ZtQ3d3d8hRZ2lpaXj22Wfx4IMPIikpaUasw7EF1S6XC7GxsdDpdKitrcX+/fvDzu8lS5agtLQUPp8PhYWF2LRpE44ePRryOoPBgLVr18JoNGLu3Lk4efIk/uu//ivq/dy8eXNS9221WiGVShETE8NE3BSnE3azELYECvv5p2MQR0ZGcPToUTQ1NYWdxDk5OTAajZPW8aSlpSElJSXiwlEqlbhy5cqUpAYUe5mIbISLQavVYt68eVFf73Q60djYGFUaQePQ2NiIgYGBqO85a9asCXceshQjWS9isZi5wpPJGM4k3G43Dh48iIaGhrDjKJPJkJKSMqm4EIUD4uPjoz6T5uZm9PX1zXiFgd/vx7Vr1/D+++/j448/htlsDinGLisrwyuvvIL169cjJSVlxtxbqpkk6UNsbCzcbjeSkpKwcePGca/XaDRYs2YNli1bhr6+PqSkpOD06dOoq6sb91qXy4UzZ87g5MmT2L17N06dOsWe22R0ipGeIwAUFRUhMzOTGTdUckRZeMq0C09YmjFLrKGhAbW1tcx3HhtPyc3NnZIuyWAwQKfTRZyElI0aGRmJKBWgwyFInxIMBpmvPtlAqlQqRU5OTtR+Wh6PB52dnaxdSTQSu3HjBlwuV9SHGxsbO6mJodFoorolMpkMx48fx9DQECv5uN0g6/Obb76JSPJGo3HSn5kSH/QMI13T7XbDYrEgPz9/RjOSNTU1+NOf/oTDhw/D6XSyz6BUKrF48WI8//zzmDdvXlRLeLosMdKBUb1jeno6nE4nrFYrMjIyYLfbYbVaWZaS2udUVlbigw8+QGNjY1jr1ufzYXBwkMWq4uPjsWLFCmRkZMDn8+HcuXNhyQ8A4uPjmdRJiOTkZDz++ONYu3Yt5HI5qwTRarWw2WxQqVSsnz+pF6KNoXS6dqOxQWbhw05PT5+S26dSqaKeRiMSiTA4OAibzcZ2obELuLCwEC+99BKGh4cxOjoKt9uNysrKKZV4kKYm2gILBAKw2+0T7vper5fptSZyeSezoCl+MJGFbLPZvreGhoFAADU1NRFFshRq0Ov1kyZZsVg8IYmRmHamPrfdbseZM2fw7rvv4tKlSyFSnNjYWNx///147rnnkJubO+NSDyIXuVzOOk9IpVIMDw8jNzcXPp8PjzzyCLxeL86fP4/z588jIyMDP/nJT6DX6zEyMoLKykrY7fawJJaZmYmHH34YMTExuP/+++F2uzE4OIji4mKIxWK89dZbaGhoCGspPf7443C5XPjggw9C/j8rKwv33HMP8vLyWMyQEg2kSaNWQH6/H3K5PKqu8ZZHeGBgAG1tbSwDEY7E9Hr9lEpCpFIpS9NGmqik3BZW6AutlHvvvRf33HMPGwiqkp9qUHUiUiGR5kQLxufzYWRkZEK3cybqMr8vkGYwkksbDAaRkpIypYUuEokQExMzoeC3v79/SnWMkx3L0dFRHDt2DK+//jo6OztDmvbl5ORg3bp1eOyxx5CQkHBbrF+huFTYl56ErPHx8cjNzcX169cZ2RYVFTFLTKlUwmAwRDz1aXh4GPX19UhNTUVnZyckEklI4XZycjKKiopQV1c3bm673e5xvcsAsMy2xWIJCe8I+4mRrEJ4DuaMWWIWiyXkYYabyFMtcpXJZNDpdKyHUaQJZbfbI05UYfHrrU6S6dwxp4t0hMfPT7QhfF8YHh5GXV0dK+IO9xmmGisSi8UwmUxRn20gEGCdRqfTquzv78fXX3+N//mf/wlxkQKBAIqLi/HMM89g1apVt1SOdqtxMap6IBJJSkrCgw8+CLlcjtraWqxduxaJiYnYvHkzBgcHsXz5clRXV0OtVrNSPyEGBwdx/PhxFBYWQqFQQK/Xo729HVlZWcjKyoJUKmWHgIwlsUhr0Ol0wu/3Iykpid0r9Q3zer0sHkZdLMJ5W9NOYi0tLREnokajgcPhgMViCTtIkR5IJMWzcHGSOzmTu9x075iTcVUms/ioTU80mEymcfVwtzMe1t3dPWHyheJmkw03UC1ftDGiutTpssT8fj+6urqwY8cOvP/+++OsC4/Hg4ULF+L++++flFRkpghMqMonz4i6n6SmpqK8vByZmZnYtm0bE0srlUqUl5fDbDZHdPtJMaBWq9HS0oL8/Hx4PB44HA50dHSgr68v7Dr8v//7v4gxy/7+fvT39yMmJoa13yGBq9PphF6vZ4F9KiSfEcU+mdfRerNrtVrs2rULNTU1k7YKvF4vrl+/HlYnNlOuElXQU00Y7WjTFVOSyWRsx4q0uCiDWVlZOaH7bbVaw5rqwok9b96879QeZ7rQ398fNZEhlUrx9ddfo729fdJzIxAIoK6uLupnp9dNx5yw2Wy4fv06tm3bhh07doSNacpkMly5cgVms5m1tLmdoK6oSqWSzV+j0cjKjiQSCXQ6HfLz80MIDAAOHDiAAwcOTPgcP/vss7C/e/XVV5GZmYmWlhbEx8ejoKBgnEwjnGGjVqvR29uLlJQUphOjMyspRjr2WLgZscSoOHd0dDTiQhGLxWhoaMDVq1envOhnavEFAgGMjo6yoL/NZmP9oOx2O2vIR6Uy07EgZDIZCgoKoFAoIgb3g8EgTpw4gXXr1iE1NTWqBdXV1RXVWnU4HFiyZMn31tWBVPrhMtZCdHR0oLm5+Y6ZG0LL2e/348CBA2htbcXJkycjWr4SiYSdKJ6cnAyTyXTbrV+hKyaRSFhwnCyZtLQ0WCwWFBQU4ObNmxGtru8SMqAOJElJSSgvL5+QxEZHR9Hc3AyxWMzKr6ioXFjHS7WTwkNKZoTErFYrq3CPFseYrsZvtxrTaG9vR0dHBzo7O9HV1YXOzk40NTVheHgYIyMjzA0hf5yaut0qxGIxysvLERsbG5F8gsEg6urqcPDgQaxfvz4iARG5RqokoELw8vLyGU/vTzTeE1lMd8LciEbEb7/9NjsBPRrkcjm2b9+O0tJSrFq1asZU+dHcbOFp39Q0gIqzVSoViouLUV9fH3G88/PzsXTpUjQ1NSE2NhaXL18Oq/0UgpoUxMTEID09HR6PBw899BD27NkT1Yvw+/0oLCxkB4bQGNIp4sJ4Gp1rOSOWGPmsE1kq4RoP3iomK+KkAtMLFy7g6tWrqKurQ01NDfr7+1nLHcqCKJXKGVv0YrEYubm5ePDBB/Huu+9GHA+73Y4tW7ZAr9dj+fLlYUukLBYLrl+/HjXGuGHDBqSnp39vriRJTyaK283E3LjV1jZjXZ/Jwmq1YuvWrSgoKEB+fv5ttcaEqn06kIMaD9L/0XFskZ7Jxo0bsXr1aly6dIklzp5//nl88MEHmDdvHlJTU/HGG2+E/M25c+fQ1tYGuVyOnJwcLFmyBCUlJSgpKUFvby/ef//9ce8TExODnJwcpKSkMK+HLDFhQwbhwbpR3enpILFoD0skEuHv//7vcffdd09L2xEhkpKSolpJIyMjqK6uxsGDB3HmzBm0t7ezBx5ucgp7t48dyOmAWq3GI488gpqaGpw8eTLiAmxqasI777yDxsZGVFRUIC0tjYn+LBYLTp48iYsXL4aKXQI8AAAa20lEQVS9L5/Ph1/96ldYunTplBbgTIA690azHv7u7/4OCxYsmNaurGRBT6c1JJVKkZaWBrvdHjUGfOnSJezcuRO/+MUvJt3ldjpAGzF5EdQfn8IXMTEx8Pl8KCsrw7p16zAwMACTyQSVSoWenh588sknKCkpQUFBAbq6unD+/HlYLBZcvHgRQ0NDaG5uDqsj27ZtGwAgNzcX2dnZWLJkCbv2119/HfZedTodEhMTWRss6pFHWUmKi1M9JT3TGSGxiYLrtCPm5+dj/vz5005iwg859n17e3uxe/dubN++HS0tLczEHvvgVSoV1Go1a9eblpaGzMxMFhBvaGjAtm3bpuW+KQbw61//GsFgEGfPng27KwYCAdy8eZPVWpLi3ufzYXR0FO3t7eOsMCqT2rBhA9avXz+uC8b34YpNJAPx+/3Izc1lqvbpmhu0CU1XgF2n0+Ghhx7Cxo0bcfXqVbz66qsRY31utxsHDhxAaWkpqqqqbqslLNyAybIRlkHJZDIYjUYkJSXB6/XCYDBArVbD4XCw1ljkOlutVla4L5VK0d7ejosXL0YleWqbPjg4iMHBQXY9av1DaGhowI4dO+D3+7Fo0SIYDIaQeUNERvN3onlxW0REVOZwO7I2dKjF559/jj/+8Y8YGhoad2qKTCZDeno6CgsLUV5ejtmzZyM5OZk9SLpPn8+H6upqNuDTFYAtLi7GP//zP2PLli04dOhQxBS13W6fMCFCu25eXh7Wrl2L1atXs+4Bdzqo/xWpzO9EJCUl4cknn8Sjjz6KuLg4xMXF4cKFC9i+fXvYMQ4Gg2hvb8euXbuQn5+P3Nzc2zaW1KyQOkdoNBrY7XbIZDLWMTU1NRULFy6E3+9nPeHcbjdMJhN8Ph88Hg8yMzPxwAMPsBbWcXFxsNls+N3vfhfVy6D+dzqdDpWVlUhMTIRGo0FNTU1IBpQO66FzKmUyGVQqFSudstlsrOyQRLwzqtifjMs5WWHmdMDhcODIkSN4++232VHsY62vefPm4Ze//CVKSkqYjirc8VBk4k43IchkMhQVFeEf/uEfMHv2bLz33ntoa2tjD2misQoGg1AqlTCZTMjIyEBZWRmqqqpQVFQ06bKl22kdRPvd7Zwb3yVY/vOf/xwPP/wwDAYDRCIRTCYT1q9fj0uXLqGxsTHswqKawr179+KnP/3pbXHricBIGkT1h3q9HqOjo8ytpP+z2+3YvHkzAoEAnE4nurq6WFdY8kyo46rL5cLly5cBABkZGVAqlWhvb2cH9GZnZyM9PR3JyckIBoOIj4/H4OAgSktLAfw5A11UVMRO7woEAtizZw/uuusuLFy4kAll1Wo1bDYbvF4vC/BTsiJauOGWSSyauUwN2sIdEDJTweSuri68//77YQkM+PMBCVVVVVi8ePFt1/OMJSJSKdN9BINBZGdnIysrC06nk53ORONMrYb1ej0rJ7nrrruQlZUVtdb0+yKviU6toflxJ5JYMBiEWq1GaWlpiAKfpDJPPvkkfvvb34ZtLwT8Wel++PBhlJSUYOnSpTM616hUhwLj1BlVp9OxVtSUoTQYDKyffV9fX0iGm7KGVLhN9adUOSMWi7Fq1SqkpaXhyJEjOHToEKRSKe655x6UlpZCq9WyInS9Xg+fz4fKykpIpVLU19djx44dOH36NHs/m80Gi8XCjt6jBph00hg9BwpDzYjYlVrQTrRDDA8PM1NxJuHxeHDq1Clcv3494qTR6/VTaic9U2Tb09OD3bt346233mIq/bKyMjz11FMoKSkJObqM3C6qc4uNjYVer//eNGCTJTFq/x1JZiEWi9Hd3T2jVRe3SmThLHStVovKykpcuXIFW7duDZvRFolEuHjxIvbt24ecnBxkZGTM2H1S7aRwM6QgOWUjqU+9yWRCa2srhoaGxukV6YxX6oOWkZHBsswAsHnzZqSlpaGoqAg9PT04dOgQOzuTevvRe1O1gM/nw3333QeTyYRvv/025P1u3ryJq1evsvbmUqk0pO+/MCY2Y2VHcrkcGo0maoZQLpeju7sbdrt9xrM1drsdR48ejbrraTQa1p75+1oYPT09+Pzzz/Haa6+xNrw5OTl4/vnn8cADD9z203pmAhKJBHFxcawdcqTXXLt2jVkLPxRQX7NHHnmEdXAJF9MTiUQ4ePAgiouLsXbt2hlzK4VF6PSdPB+hy07F1FqtFgkJCeP0ij6fj204QguIDJDOzk52buyRI0eY4ZCbm4v4+HjWA1AYIqCDRgKBAObPn4/6+nq0tLQA+LPUQq1WsxPQ6LRxiuvROp7Iir0lc0SlUiE2NhZGozGiS6BWq3Ht2rWIwszptG6Gh4ejBsJFIhEj3e+rnnB4eBi7d+/Gf/7nf7KHI5fL8Td/8zdYtmzZHUtgUx0vkUiE9PT0qAtXJBKhvr6etTf+IUEul6OkpARr166NGK8RiUQwm83Yu3cvrl+/flvqfMlyJOtF+J2ylmq1GiaTCb/5zW8wb968cZ4VdZOgmDAdqfbiiy8iLi4OZrOZkd1zzz3HTpgXKu3pO1ljfr8fxcXFIWdbkLXmdrsZ0dHf0f1OJk4svtWBS0xMRE5OTsQ3kUqlOH36NBobGycsQZkOgphIIS7MPk73wp0IXq8Xp06dYoWxdBry8uXLsXTp0u+leHgyY0CC5qnErujouYmsj6GhIVRXV0/YZ+1OhMlkwrJly1BVVRWxRlQul+PIkSM4cOAALBbLjLmTwu90IjcRk1gshlwuZ7/XaDSoqqrCo48+iqysrJC1Si296QAPoQi8srISKpUKmzZtQlNTE1avXo17770XarWabb4k5aDvZPmlp6dDoVDgvvvuY6di9fb2MheXOsUS8Qld0onW4i0HhjIyMjBr1qyIOymdiLJ//35YLJYZDeJOpljb7XZPesH4fD60t7dPWzGx2WzG1q1bmeyD3iMxMfF76zZBuqBoMYeRkRE4nc4pj0NKSgpmzZoVVT4RCASwd+9etLe337FZymhEnZeXh6qqKhQUFEQcH5lMhi+++AIXLlyYkY1c6CqO7S9G+kiKmZFoOjs7e9wBMeROKpVKFpui11NHFOE8IT2Y3++Hy+UKEdxSTI6ObVOpVNDpdEhOTkZhYSF+//vf45e//CWKiorYGaOUmKBeYpM97eiWSSwpKQnFxcVR40wKhQIHDx7EwYMH2WnOM2XiT9TA0Gq1YnR0dMIF4/F4cPz4cezatWtaSCwQCODcuXPj3F2lUomzZ89i3759uHLlCpqamtDS0hL1q62tDd3d3RgcHBx3PNhUoVAoJnWgxuDgYMS+YJGgVqtx//33R42FBoNBXL9+HV988cW4NsY/BCgUCixevBgPPPBARLdSLBbDbDZjx44daG1tnXbXWajUp5gUWdB0+AZZ/R6PBwqFAhaLBWq1OsT6l0ql7BRumUwGp9PJ4mHDw8MsAE84c+YME5zTKd/UeJHOj3Q6nawmUiaTMX1YbGwsiouLkZCQwN6L3G3KtAqPcIuGW5ZYyGQyLFy4ECdPnsTBgwfDLiiRSAS3242PPvoIcrkcf/VXfzWllsQ02SnIaLfbodVqQ7JCpOGZSDQ5NDSEq1evIjs7O6JY0Wq14uDBg/j000+ZPmYy9zaRBXj+/PlxQW6JRIKGhgb88Y9/RHJy8qTidTRxaHeLiYlBVlYWcnJykJ6eztrBTMayk8lk0Gg0bPcNB6PRiPPnz6OysjIiIYVLgUskEixcuBDz5s3D4cOHI05Gl8uFnTt3Qq1W44knnkBcXNyUrFIaf6/Xi8HBQSa6jBYEp0Uymc1H2BAgHOLj47Fy5UrU1dVFLCdTKBQ4duwYysrKkJCQAKPRGPWaU7XEhEedAWDta8iqoV5jIpEIIyMjiImJQWtrK1QqFf71X/8VmzdvZjoylUrFBKfAn7tOkOBVuOao+zL19ouNjUUgEGDWHx3xNjIywjRlCxYsgF6vR05ODtOVCYP31KaaVA+T2aAlv/nNb35zq4NIRcpNTU0RA/gikQijo6O4ceMG+vv7Q8xTYVaFvmjyeL1e2Gw2NDc34+TJk/jss8/Q3NyMnJyccQvK7/fj9OnTMJvNUcnE5/Nhzpw5bBei9/N6vWhubsZHH32ELVu24MaNG+wE5WhugF6vR2lpaQgBjZ2gTqcTu3fvRnNzc9gHMzo6iq6uLrS1taG1tTXqV0tLC5qbm9HY2Ii6ujrU1tbi8uXLqK6uRnV1NQYGBmAwGNjhp9EWikQiQXd3N65duxaxoFwikaCjowMlJSWsnbTwOXk8HvT19cHj8YTIPiieEh8fjwsXLkS1wh0OB27evIne3l7o9fpxm9zYuSHs99bW1obTp09j27ZtOHr0KMrLy0MOZaVSFqvVis7OTly+fBn79u3DqVOnInYUoVY8wsNiqcaPel0JXxsbGwu73Y6GhoaoLZJaW1vhdrvhdDpZtcJkzkuYbFB/rHVGandhyZ9SqYTT6YROp0NnZycOHTqErKwsVFZWIicnh/UhE76exmJ0dBRbtmwB8Oce+gsXLkRpaSmSk5PZ+5IrSC4iWX50Xujdd9+N7Oxs6PV6Nj8phiZssy3sZDGjtZNkhi5fvhxmsxmbN29Gd3d3RAbt7u7G1q1bceLECZSUlGDBggXIzMwMaSbn8XgwMDCA7u5utLS0oLa2Fr29vRgdHUVvby+efvrpcbsoFf2uXLkSly9fjpgJ8vl8OHHiBABg7dq1yM7OBgD09fXhwoULOH78OBoaGuByuZCRkYHVq1dDq9Xit7/9bVgrLxgM4tq1a3j55ZeRlpaG0tJSFBYWYu7cuezBksUTFxcXto3vrbioRL52ux1dXV2oq6vDxYsXsXv3bqxfvx4rVqxAfHx8xGQG1bZmZ2ejo6Mj4mvsdjt+//vfo6OjA/Pnz2diRrPZjIsXL+LatWv4xS9+wYK2wrlx11134YUXXsDbb7+Njo6OiBZwX18fvvzyS1RXV6OsrAzz589HdnY2jEYjG3uPx4ORkRF0dHSgvb0dtbW1MJvNsFqtGB4eRmlp6bi54Xa7UV1djU2bNsFsNsNms2F0dHTCppsikQifffYZvvrqK1a0vHLlSjz22GPjtGEKhQIrV67ElStX8PXXX4d1velw448//hhffvklYmJiUFJSgmeffRZFRUXT4laOXXdj5RfCnylbT38XyYsS1qLSPFq2bBkyMjJQWlrKqhmEmdGx7ynUylHGU/j7sXNiKhbqtJUd6XQ6PPHEExCLxdi1axeuX78e8WacTidu3ryJlpYW7Nu3D3K5fFwLDuqySt9pB3Y4HBGb4lH24+jRozh+/HjEhet0OnHo0CFUV1ezyUilDm63GyKRCCUlJXj00Ufx8MMPo6enB1u2bEF7e3vY9yULrrW1FdXV1dDpdHjttdeQlJQUcgjCkiVLcOTIEXR3d89YEJ9aM4+OjuJ3v/sdamtr8cILLyArKyvibp+bm4u7774bNTU1UQ+lbW1txZtvvgmVSsV2W4/HA6fTieTk5IjkrNFosHr1aohEInz66aeora2NqMB2uVxobW1FR0cH9u/fz+aGMH0vnBderzfEchc21RMuWpvNhp07d8JkMk1pPO12O+x2O3p6etDV1YWCgoKIzy4pKQnr1q3DjRs3UF9fH3Ejt9lssNls6OnpYY0LpyuwL5wH5NKR4JhixnRASG9vLxITEzF//nxs2bKFNTiUy+UsZENWslwux+joKPx+PxYuXIif/vSnjMD0ej1cLhcjJgrQe71eVgsplG5QDFZ4yA49Q7FYzP6O/l+YtJhREiO38sknn0R6ejoz1zs7OxnpjL0Jv98Ph8MRcUekD+nz+WAwGJCfn4/Zs2dj0aJFYWMzYrEY6enp+NnPfsa6pAr97bHvTZNJSJxpaWksbV5RUQGVSgW/349Vq1bhv//7v6NKBugamZmZISfC0L0tWrQIP/nJT/D555+zHkwzRWbBYBAOhwM7d+6ERCLBSy+9FDH5olKpsHr1anR1deGLL75grk6k+BXJCYTuZGFhYdT6NoPBgIceegiJiYnYu3cvqqur2dkMkeYGlV5NNDc0Gg3mzJmDwsJCLFq0KMSVpJ3fYDBEbQ0+GahUKqSkpESMt4nFYtx9991Ys2YN2traJjwngor3w/WM+y4WmPA7PQuKLQndfJPJhJ6eHhiNRoyMjGD//v1sPqrValitVtaskO7RarUyly8jIwPp6elIS0uDwWCAw+GARqNhr6dsN1nPRHLkPpOrTqRFMUciMGEs77acdhRu1125ciXy8/NRUVGBK1euoKamBteuXcPAwADkcjn7kMKJRjupx+OB2+2GXq9Heno6Zs2ahZycHGRnZyMnJwd5eXlITEyMuMgUCgUWLlwIpVKJgoICnDhxAjU1Ncw3F/4dpYZlMhnmzJmDefPmYf78+Vi0aFFIQ0GtVov58+ezBAUtILIG6IGQWLCgoGDcxBSJRDAajXj66aeRkZGB6upqnD17Fl1dXSHjMJnj3+h64RZ/OCtx//79KC4uxuOPPx6RaLKzs/Hss88iPj4e3377Lc6fP88Cs8K4GhE1dT4oLS3FvHnzMHfuXKSnp084N5YuXYrs7GwsXrwYV65cwaVLl1BfX4+enh4m9xgbxxO6zW63G2q1GhkZGcjJycGsWbOQmZmJ3NxczJo1C0lJSePcfqlUCqPRiIULF2J0dPQ7bxwmkykkRBAOSqUSa9aswfXr13HlypUJn2FiYmKIAHQ642LRXpuQkIDu7m4AwD/+4z/i+vXr0Gq18Hg8rOaSepK53W4YDAbY7XZoNBrk5uYiLS0NRqMRXq+XxQ0VCgWzlMcSvVKpZLFM6psv1IURWVFgn/5+MhILUXAGxTlerxc9PT3o7OxEd3c3LBYL+vr60NPTw4SpVBeoVqthNBpZ5sZkMrHWJwkJCYiNjZ1SG2OqT2xsbERLSwtrRd3f38+yJwkJCUhPT0dqairS09ORlZWF1NTUcQ8gGAyiubkZb7/9Ngua63Q66PV6KJVKRswkFkxJSUFJSUlIOnrsuNTX1+Ozzz7DRx99xIjF4/GgqKhoQoGo3+9np1zX19dDIpHAaDRGtRCWLl2Kf//3f0dKSkrUa/f397PSEBozCtpTd9DU1FQkJSUhKSkJqampyMjIYH3SJwufz4fe3l50dHSwk3b6+/thNpuZW0P3Tqn9sXMjNjYWSUlJrLnfRFlpOnvxu0IikbCOpBN9tvr6egwMDEx4Ta1Wi7vuuuu2NyMgGQXV5w4NDSE2NhaxsbHs1CEhKZJO8OzZs/jwww/xyiuvoLi4OMRlFc63scQj7G1GRDbWDSZyGyvenYicZ5TExi5cii84HA54PJ6Q5mcSiQQKhYKpf1Uq1bT0Xqc2u1arlXXUoMFTKpWMjCaSNjidTnR3d7NdQi6Xs6OmKOBJX8IYTjhYrVacOXMGX3zxBfbs2cNOq/nZz36GqqqqCQu7yRK02WwYGRlBV1cXzpw5g8OHD497+IT8/Hz8y7/8C5YuXTqpcXM4HGzMXC4XM/lJkqHVaqHRaKalqN/n87G5Ybfbw84NcnWEc+OH0DPt+4BwSRMpENFQmQ+51XSoCB1yTfOTxpsSI7QG2tvbcenSJSxbtgy5ublMviEs2g43/8jVJIuaYqrCLP7Y7OodR2Ic/69h4969e7F161ZcvHiRNYP767/+a7z44ovIy8ubdLqdTHdqmVxdXY133nkHZrN53DUSExPxq1/9Chs3buSL/0c6t4TPVZhtHKt1E/6brB/aNAKBANvoqbsxhUwoQUAaMq1WO2kLkkiMAv/kgQl1YtG6Vsy4xIJjcpNscHAQn332GZOhUNKgvLwcGzZsQE5OzpT0QlQTJ5fLERMTA6PRCI/Hg1dffXXcdUjIyPHjRDSJArl3RBjCwDllMYWlSkJyoUA9NSaUyWQsuzjZzVBocQm7U9A9CX8vJLDJXl/MH//tgd1ux44dO/DJJ5/AYrGwB+n3+/HEE0+gsLDwllo0i0QixMfHo6ysDLNnzx4nd5gudTjHD88yo+9EZmRVkUsplDdQkJ2sNLKcqGssvWYqAl3hXAynVxMmqKZKYJzEbhMCgQBOnjyJL7/8MuSkHK/Xi6VLl6KsrGzaeq3pdDqkpaWNIzGlUomYmBhOZH9BltlYIiOrhzRcwsaDwt+PJRXhwbzkFk7GjSQCFFqBdF3hKUZj7yGcaDcauDt5G9DX14e9e/eOKzlyOp2YO3cu4uLippUwSbArRExMDKtO4PjLcjHHumnkQgoJayzJjH09ySfIgpvobE+y7sIduUbXFXaiDXfPnMTuINTX1+PmzZvj0vukXp7Ok37sdvu4wL5EIkF6evqMtkjm+GEQmpBEwv0uHMZ2WJ2MFRbNWruVEiPuTn5P6OjoCNtmRqVS4caNG+jr65uWXlpOpxOdnZ24ceNGyAQymUyszzkHx48N3BK7DYh05qZCocA333yD2bNnQywWIzExESqVakKdmdCS83g8cLlcsNvtaG1txbFjx0LiYSKRCA8++CAqKyu/19OdODg4if2AQQcpUJmHkGD8fj/+4z/+AzU1NVi0aBGSkpKg0+mYkFTYOYDiB1SsTp0bzGYz6urqcPr0aSbdoKDqk08+iQ0bNkxr3I2D445ylbnYdeZhtVqxadMmvPPOO7DZbGGtLLfbjdHRUbhcLqSlpSE3NxdGo3Fc5UIgEIDVakV3dzfq6upYsa5arWYF5S6XC4mJiXj22WexZs2aKQloOTg4iXGERVdXF/bs2YNNmzahpaUFCoUiIrEIW8uE6/MUTqdDWUmVSoU1a9ZgzZo1mDt3LhISErisgoOTGMf0YHBwEE1NTTh+/Di+/fZb1NTUAAgtuZgM4QibzpHyurCwEPfccw8qKiqQl5eHhISEsIe6cnBwEuO4JVCt48jICLq7u9HW1oabN29iYGAAFosFPT09zK0cK8mQyWRQqVSse0RcXBzrypqamsoOI53pk9Y5ODiJcYQ09aN+ZG63m50BQL2XhK+nDhl0QpGwa4awMy4HBycxju+N2ML9HPLAvoOimYODkxgHBwfHHQqed+fg4OAkxsHBwcFJjIODg4OTGAcHBycxDg4ODk5iHBwcHJzEODg4ODiJcXBwcBLj4ODg4CTGwcHBwUmMg4ODg5MYBwcHJzEODg4OTmIcHBwcnMQ4ODg4iXFwcHBwEuPg4ODgJMbBwcHBSYyDg+MvCv8foPuErXNuO3cAAAAASUVORK5CYII="; + + public static class AbortedLaunchException extends RuntimeException { + // yee + } + + public static HTMLElement rootElement = null; + public static Minecraft instance = null; + public static void main(String[] args) { + registerErrorHandler(); + try { + JSONObject e = new JSONObject(getOpts()); + EaglerAdapterImpl2.initializeContext(rootElement = Window.current().getDocument().getElementById(e.getString("gameContainer")), e.getString("assetsLocation"), e); + }catch(Throwable ex2) { + StringWriter s = new StringWriter(); + ex2.printStackTrace(new PrintWriter(s)); + return; + } + run0(); + } + + private static void run0() { + System.out.println(" -------- starting minecraft -------- "); + instance = new Minecraft(); + instance.field_6320_i = new Session("Player"); + LocalStorageManager.loadStorage(); + run1(); + } + + private static void run1() { + GL11.canvas.focus(); + instance.run(); + } + + @JSBody(params = { }, script = "return JSON.stringify(window.config);") + public static native String getOpts(); + + @JSBody(params = { }, script = "window.minecraftError = null; window.onerror = function(message, file, line, column, errorObj) { if(errorObj) { window.minecraftError = errorObj; window.minecraftErrorL = \"\"+line+\":\"+column; javaMethods.get(\"net.lax1dude.eaglercraft.Client.handleNativeError()V\").invoke(); } else { alert(\"a native browser exception was thrown but your browser does not support fith argument in onerror\"); } };") + public static native void registerErrorHandler(); + + @JSBody(params = { }, script = "return window.minecraftError;") + public static native JSError getWindowError(); + + @JSBody(params = { }, script = "return window.minecraftErrorL;") + public static native String getWindowErrorL(); + + public static void handleNativeError() { + JSError e = getWindowError(); + StringBuilder str = new StringBuilder(); + str.append("Native Browser Exception\n"); + str.append("----------------------------------\n"); + str.append(" Line: ").append(getWindowErrorL()).append('\n'); + str.append(" Type: ").append(e.getName()).append('\n'); + str.append(" Message: ").append(e.getMessage()).append('\n'); + str.append("----------------------------------\n\n"); + str.append(e.getStack()).append('\n'); + } + + private static boolean isCrashed = false; + + public static void showDatabaseLockedScreen(String msg) { + String s = rootElement.getAttribute("style"); + rootElement.setAttribute("style", (s == null ? "" : s) + "position:relative;"); + HTMLDocument doc = Window.current().getDocument(); + HTMLElement img = doc.createElement("img"); + HTMLElement div = doc.createElement("div"); + img.setAttribute("style", "z-index:100;position:absolute;top:10px;left:calc(50% - 151px);"); + img.setAttribute("src", crashImage); + div.setAttribute("style", "z-index:100;position:absolute;top:135px;left:10%;right:10%;bottom:30px;background-color:white;border:1px solid #cccccc;overflow-x:hidden;overflow-y:scroll;overflow-wrap:break-word;white-space:pre-wrap;font: 24px sans-serif;padding:10px;"); + rootElement.appendChild(img); + rootElement.appendChild(div); + div.appendChild(doc.createTextNode(msg)); + } + + @JSBody(params = { "v" }, script = "try { return \"\"+window[v]; } catch(e) { return \"\"; }") + private static native String getString(String var); + + @JSBody(params = { "v" }, script = "try { return \"\"+window.navigator[v]; } catch(e) { return \"\"; }") + private static native String getStringNav(String var); + + @JSBody(params = { "v" }, script = "try { return \"\"+window.screen[v]; } catch(e) { return \"\"; }") + private static native String getStringScreen(String var); + + @JSBody(params = { "v" }, script = "try { return \"\"+window.location[v]; } catch(e) { return \"\"; }") + private static native String getStringLocation(String var); + + @JSBody(params = { }, script = "for(var i = 0; i < window.minecraftOpts.length; ++i) { if(window.minecraftOpts[i].length > 2048) window.minecraftOpts[i] = \"[\" + Math.floor(window.minecraftOpts[i].length * 0.001) + \"k characters]\"; }") + private static native void shortenMinecraftOpts(); + + private static void addDebug(StringBuilder str, String var) { + str.append("window.").append(var).append(" = ").append(getString(var)).append('\n'); + } + + private static void addDebugNav(StringBuilder str, String var) { + str.append("window.navigator.").append(var).append(" = ").append(getStringNav(var)).append('\n'); + } + + private static void addDebugScreen(StringBuilder str, String var) { + str.append("window.screen.").append(var).append(" = ").append(getStringScreen(var)).append('\n'); + } + + private static void addDebugLocation(StringBuilder str, String var) { + str.append("window.location.").append(var).append(" = ").append(getStringLocation(var)).append('\n'); + } + + private static void addArray(StringBuilder str, String var) { + str.append("window.").append(var).append(" = ").append(getArray(var)).append('\n'); + } + + @JSBody(params = { "v" }, script = "try { return (typeof window[v] !== \"undefined\") ? JSON.stringify(window[v]) : \"[\\\"\\\"]\"; } catch(e) { return \"[\\\"\\\"]\"; }") + private static native String getArray(String v); + +} diff --git a/src/net/lax1dude/eaglercraft/EaglerImage.java b/src/net/lax1dude/eaglercraft/EaglerImage.java new file mode 100644 index 0000000..9a7a8a9 --- /dev/null +++ b/src/net/lax1dude/eaglercraft/EaglerImage.java @@ -0,0 +1,59 @@ +package net.lax1dude.eaglercraft; + +public class EaglerImage { + + public final int[] data; + public final int w; + public final int h; + public final boolean alpha; + + public EaglerImage(int width, int height, int[] pixels, boolean alpha) { + this.w = width; + this.h = height; + this.data = pixels; + this.alpha = alpha; + } + + public EaglerImage(int width, int height, boolean alpha) { + this.w = width; + this.h = height; + this.data = new int[width * height]; + this.alpha = alpha; + } + + public EaglerImage getSubImage(int x, int y, int pw, int ph) { + int[] img = new int[pw * ph]; + for(int i = 0; i < ph; ++i) { + System.arraycopy(data, (i + y) * this.w + x, img, i * pw, pw); + } + return new EaglerImage(pw, ph, img, alpha); + } + + public int[] data() { + return this.data; + } + + public int[] getRGB(int startX, int startY, int w, int h, int[] rgbArray, int offset, int scansize) { + if (startX < 0 || startY < 0 || w <= 0 || h <= 0 || + startX + w > this.w || startY + h > this.h || + rgbArray.length < offset + w * h) { + throw new IllegalArgumentException("Suck my dick nigga"); + } + + for (int y = startY; y < startY + h; y++) { + for (int x = startX; x < startX + w; x++) { + int imageDataIndex = y * this.w + x; + int argb = data[imageDataIndex]; + int alpha = (argb >> 24) & 0xff; + int red = (argb >> 16) & 0xff; + int green = (argb >> 8) & 0xff; + int blue = argb & 0xff; + int rgb = (alpha << 24) | (red << 16) | (green << 8) | blue; + + rgbArray[offset + (y - startY) * scansize + (x - startX)] = rgb; + } + } + + return rgbArray; + } +} \ No newline at end of file diff --git a/src/net/lax1dude/eaglercraft/GLAllocation.java b/src/net/lax1dude/eaglercraft/GLAllocation.java new file mode 100644 index 0000000..c13b85f --- /dev/null +++ b/src/net/lax1dude/eaglercraft/GLAllocation.java @@ -0,0 +1,60 @@ +package net.lax1dude.eaglercraft; + +import java.nio.*; +import java.util.ArrayList; +import java.util.List; + +import net.PeytonPlayz585.opengl.GL11; + +public class GLAllocation { + + public GLAllocation() { + } + + public static synchronized int generateDisplayLists(int i) { + int j = GL11.glGenLists(i); + displayLists.add(Integer.valueOf(j)); + displayLists.add(Integer.valueOf(i)); + return j; + } + + public static synchronized void generateTextureNames(IntBuffer intbuffer) { + + for (int i = intbuffer.position(); i < intbuffer.limit(); i++) { + int tx = GL11.glGenTextures(); + intbuffer.put(i, tx); + textureNames.add(Integer.valueOf(tx)); + } + + } + + public static synchronized void deleteTexturesAndDisplayLists() { + for (int i = 0; i < displayLists.size(); i += 2) { + GL11.glDeleteLists(((Integer) displayLists.get(i)).intValue(), + ((Integer) displayLists.get(i + 1)).intValue()); + } + + for (int j = 0; j < textureNames.size(); j++) { + GL11.glDeleteTextures(((Integer) textureNames.get(j)).intValue()); + } + + displayLists.clear(); + textureNames.clear(); + } + + public static ByteBuffer createDirectByteBuffer(int par0) { + return GL11.isWebGL ? ByteBuffer.wrap(new byte[par0]).order(ByteOrder.nativeOrder()) : ByteBuffer.allocateDirect(par0).order(ByteOrder.nativeOrder()); + } + + public static IntBuffer createDirectIntBuffer(int par0) { + return GL11.isWebGL ? IntBuffer.wrap(new int[par0]) : createDirectByteBuffer(par0 << 2).asIntBuffer(); + } + + public static FloatBuffer createDirectFloatBuffer(int par0) { + return GL11.isWebGL ? FloatBuffer.wrap(new float[par0]) : createDirectByteBuffer(par0 << 2).asFloatBuffer(); + } + + private static List displayLists = new ArrayList(); + private static List textureNames = new ArrayList(); + +} \ No newline at end of file diff --git a/src/net/lax1dude/eaglercraft/GeneralDigest.java b/src/net/lax1dude/eaglercraft/GeneralDigest.java new file mode 100644 index 0000000..079352d --- /dev/null +++ b/src/net/lax1dude/eaglercraft/GeneralDigest.java @@ -0,0 +1,108 @@ +package net.lax1dude.eaglercraft; + +/** + * base implementation of MD4 family style digest as outlined in "Handbook of + * Applied Cryptography", pages 344 - 347. + */ +public abstract class GeneralDigest { + private byte[] xBuf; + private int xBufOff; + + private long byteCount; + + /** + * Standard constructor + */ + protected GeneralDigest() { + xBuf = new byte[4]; + xBufOff = 0; + } + + /** + * Copy constructor. We are using copy constructors in place of the + * Object.clone() interface as this interface is not supported by J2ME. + */ + protected GeneralDigest(GeneralDigest t) { + xBuf = new byte[t.xBuf.length]; + System.arraycopy(t.xBuf, 0, xBuf, 0, t.xBuf.length); + + xBufOff = t.xBufOff; + byteCount = t.byteCount; + } + + public void update(byte in) { + xBuf[xBufOff++] = in; + + if (xBufOff == xBuf.length) { + processWord(xBuf, 0); + xBufOff = 0; + } + + byteCount++; + } + + public void update(byte[] in, int inOff, int len) { + // + // fill the current word + // + while ((xBufOff != 0) && (len > 0)) { + update(in[inOff]); + + inOff++; + len--; + } + + // + // process whole words. + // + while (len > xBuf.length) { + processWord(in, inOff); + + inOff += xBuf.length; + len -= xBuf.length; + byteCount += xBuf.length; + } + + // + // load in the remainder. + // + while (len > 0) { + update(in[inOff]); + + inOff++; + len--; + } + } + + public void finish() { + long bitLength = (byteCount << 3); + + // + // add the pad bytes. + // + update((byte) 128); + + while (xBufOff != 0) { + update((byte) 0); + } + + processLength(bitLength); + + processBlock(); + } + + public void reset() { + byteCount = 0; + + xBufOff = 0; + for (int i = 0; i < xBuf.length; i++) { + xBuf[i] = 0; + } + } + + protected abstract void processWord(byte[] in, int inOff); + + protected abstract void processLength(long bitLength); + + protected abstract void processBlock(); +} \ No newline at end of file diff --git a/src/net/lax1dude/eaglercraft/JSONArray.java b/src/net/lax1dude/eaglercraft/JSONArray.java new file mode 100644 index 0000000..8aebfbf --- /dev/null +++ b/src/net/lax1dude/eaglercraft/JSONArray.java @@ -0,0 +1,1716 @@ +package net.lax1dude.eaglercraft; + +/* + Copyright (c) 2002 JSON.org + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + The Software shall be used for Good, not Evil. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + */ + +import java.io.IOException; +import java.io.StringWriter; +import java.io.Writer; +import java.lang.reflect.Array; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + + +/** + * A JSONArray is an ordered sequence of values. Its external text form is a + * string wrapped in square brackets with commas separating the values. The + * internal form is an object having get and opt + * methods for accessing the values by index, and put methods for + * adding or replacing values. The values can be any of these types: + * Boolean, JSONArray, JSONObject, + * Number, String, or the + * JSONObject.NULL object. + *

+ * The constructor can convert a JSON text into a Java object. The + * toString method converts to JSON text. + *

+ * A get method returns a value if one can be found, and throws an + * exception if one cannot be found. An opt method returns a + * default value instead of throwing an exception, and so is useful for + * obtaining optional values. + *

+ * The generic get() and opt() methods return an + * object which you can cast or query for type. There are also typed + * get and opt methods that do type checking and type + * coercion for you. + *

+ * The texts produced by the toString methods strictly conform to + * JSON syntax rules. The constructors are more forgiving in the texts they will + * accept: + *

    + *
  • An extra , (comma) may appear just + * before the closing bracket.
  • + *
  • The null value will be inserted when there is , + *  (comma) elision.
  • + *
  • Strings may be quoted with ' (single + * quote).
  • + *
  • Strings do not need to be quoted at all if they do not begin with a quote + * or single quote, and if they do not contain leading or trailing spaces, and + * if they do not contain any of these characters: + * { } [ ] / \ : , # and if they do not look like numbers and + * if they are not the reserved words true, false, or + * null.
  • + *
+ * + * @author JSON.org + * @version 2016-08/15 + */ +public class JSONArray implements Iterable { + + /** + * The arrayList where the JSONArray's properties are kept. + */ + private final ArrayList myArrayList; + + /** + * Construct an empty JSONArray. + */ + public JSONArray() { + this.myArrayList = new ArrayList(); + } + + /** + * Construct a JSONArray from a JSONTokener. + * + * @param x + * A JSONTokener + * @throws JSONException + * If there is a syntax error. + */ + public JSONArray(JSONTokener x) throws JSONException { + this(); + if (x.nextClean() != '[') { + throw x.syntaxError("A JSONArray text must start with '['"); + } + + char nextChar = x.nextClean(); + if (nextChar == 0) { + // array is unclosed. No ']' found, instead EOF + throw x.syntaxError("Expected a ',' or ']'"); + } + if (nextChar != ']') { + x.back(); + for (;;) { + if (x.nextClean() == ',') { + x.back(); + this.myArrayList.add(JSONObject.NULL); + } else { + x.back(); + this.myArrayList.add(x.nextValue()); + } + switch (x.nextClean()) { + case 0: + // array is unclosed. No ']' found, instead EOF + throw x.syntaxError("Expected a ',' or ']'"); + case ',': + nextChar = x.nextClean(); + if (nextChar == 0) { + // array is unclosed. No ']' found, instead EOF + throw x.syntaxError("Expected a ',' or ']'"); + } + if (nextChar == ']') { + return; + } + x.back(); + break; + case ']': + return; + default: + throw x.syntaxError("Expected a ',' or ']'"); + } + } + } + } + + /** + * Construct a JSONArray from a source JSON text. + * + * @param source + * A string that begins with [ (left + * bracket) and ends with ] + *  (right bracket). + * @throws JSONException + * If there is a syntax error. + */ + public JSONArray(String source) throws JSONException { + this(new JSONTokener(source)); + } + + /** + * Construct a JSONArray from a Collection. + * + * @param collection + * A Collection. + */ + public JSONArray(Collection collection) { + if (collection == null) { + this.myArrayList = new ArrayList(); + } else { + this.myArrayList = new ArrayList(collection.size()); + this.addAll(collection, true); + } + } + + /** + * Construct a JSONArray from an Iterable. This is a shallow copy. + * + * @param iter + * A Iterable collection. + */ + public JSONArray(Iterable iter) { + this(); + if (iter == null) { + return; + } + this.addAll(iter, true); + } + + /** + * Construct a JSONArray from another JSONArray. This is a shallow copy. + * + * @param array + * A array. + */ + public JSONArray(JSONArray array) { + if (array == null) { + this.myArrayList = new ArrayList(); + } else { + // shallow copy directly the internal array lists as any wrapping + // should have been done already in the original JSONArray + this.myArrayList = new ArrayList(array.myArrayList); + } + } + + /** + * Construct a JSONArray from an array. + * + * @param array + * Array. If the parameter passed is null, or not an array, an + * exception will be thrown. + * + * @throws JSONException + * If not an array or if an array value is non-finite number. + * @throws NullPointerException + * Thrown if the array parameter is null. + */ + public JSONArray(Object array) throws JSONException { + this(); + if (!array.getClass().isArray()) { + throw new JSONException( + "JSONArray initial value should be a string or collection or array."); + } + this.addAll(array, true); + } + + /** + * Construct a JSONArray with the specified initial capacity. + * + * @param initialCapacity + * the initial capacity of the JSONArray. + * @throws JSONException + * If the initial capacity is negative. + */ + public JSONArray(int initialCapacity) throws JSONException { + if (initialCapacity < 0) { + throw new JSONException( + "JSONArray initial capacity cannot be negative."); + } + this.myArrayList = new ArrayList(initialCapacity); + } + + @Override + public Iterator iterator() { + return this.myArrayList.iterator(); + } + + /** + * Get the object value associated with an index. + * + * @param index + * The index must be between 0 and length() - 1. + * @return An object value. + * @throws JSONException + * If there is no value for the index. + */ + public Object get(int index) throws JSONException { + Object object = this.opt(index); + if (object == null) { + throw new JSONException("JSONArray[" + index + "] not found."); + } + return object; + } + + /** + * Get the boolean value associated with an index. The string values "true" + * and "false" are converted to boolean. + * + * @param index + * The index must be between 0 and length() - 1. + * @return The truth. + * @throws JSONException + * If there is no value for the index or if the value is not + * convertible to boolean. + */ + public boolean getBoolean(int index) throws JSONException { + Object object = this.get(index); + if (object.equals(Boolean.FALSE) + || (object instanceof String && ((String) object) + .equalsIgnoreCase("false"))) { + return false; + } else if (object.equals(Boolean.TRUE) + || (object instanceof String && ((String) object) + .equalsIgnoreCase("true"))) { + return true; + } + throw wrongValueFormatException(index, "boolean", null); + } + + /** + * Get the double value associated with an index. + * + * @param index + * The index must be between 0 and length() - 1. + * @return The value. + * @throws JSONException + * If the key is not found or if the value cannot be converted + * to a number. + */ + public double getDouble(int index) throws JSONException { + final Object object = this.get(index); + if(object instanceof Number) { + return ((Number)object).doubleValue(); + } + try { + return Double.parseDouble(object.toString()); + } catch (Exception e) { + throw wrongValueFormatException(index, "double", e); + } + } + + /** + * Get the float value associated with a key. + * + * @param index + * The index must be between 0 and length() - 1. + * @return The numeric value. + * @throws JSONException + * if the key is not found or if the value is not a Number + * object and cannot be converted to a number. + */ + public float getFloat(int index) throws JSONException { + final Object object = this.get(index); + if(object instanceof Number) { + return ((Number)object).floatValue(); + } + try { + return Float.parseFloat(object.toString()); + } catch (Exception e) { + throw wrongValueFormatException(index, "float", e); + } + } + + /** + * Get the Number value associated with a key. + * + * @param index + * The index must be between 0 and length() - 1. + * @return The numeric value. + * @throws JSONException + * if the key is not found or if the value is not a Number + * object and cannot be converted to a number. + */ + public Number getNumber(int index) throws JSONException { + Object object = this.get(index); + try { + if (object instanceof Number) { + return (Number)object; + } + return JSONObject.stringToNumber(object.toString()); + } catch (Exception e) { + throw wrongValueFormatException(index, "number", e); + } + } + + /** + * Get the enum value associated with an index. + * + * @param + * Enum Type + * @param clazz + * The type of enum to retrieve. + * @param index + * The index must be between 0 and length() - 1. + * @return The enum value at the index location + * @throws JSONException + * if the key is not found or if the value cannot be converted + * to an enum. + */ + public > E getEnum(Class clazz, int index) throws JSONException { + E val = optEnum(clazz, index); + if(val==null) { + // JSONException should really take a throwable argument. + // If it did, I would re-implement this with the Enum.valueOf + // method and place any thrown exception in the JSONException + throw wrongValueFormatException(index, "enum of type " + + JSONObject.quote(clazz.getSimpleName()), null); + } + return val; + } + + /** + * Get the BigDecimal value associated with an index. If the value is float + * or double, the {@link BigDecimal#BigDecimal(double)} constructor + * will be used. See notes on the constructor for conversion issues that + * may arise. + * + * @param index + * The index must be between 0 and length() - 1. + * @return The value. + * @throws JSONException + * If the key is not found or if the value cannot be converted + * to a BigDecimal. + */ + public BigDecimal getBigDecimal (int index) throws JSONException { + Object object = this.get(index); + BigDecimal val = JSONObject.objectToBigDecimal(object, null); + if(val == null) { + throw wrongValueFormatException(index, "BigDecimal", object, null); + } + return val; + } + + /** + * Get the BigInteger value associated with an index. + * + * @param index + * The index must be between 0 and length() - 1. + * @return The value. + * @throws JSONException + * If the key is not found or if the value cannot be converted + * to a BigInteger. + */ + public BigInteger getBigInteger (int index) throws JSONException { + Object object = this.get(index); + BigInteger val = JSONObject.objectToBigInteger(object, null); + if(val == null) { + throw wrongValueFormatException(index, "BigInteger", object, null); + } + return val; + } + + /** + * Get the int value associated with an index. + * + * @param index + * The index must be between 0 and length() - 1. + * @return The value. + * @throws JSONException + * If the key is not found or if the value is not a number. + */ + public int getInt(int index) throws JSONException { + final Object object = this.get(index); + if(object instanceof Number) { + return ((Number)object).intValue(); + } + try { + return Integer.parseInt(object.toString()); + } catch (Exception e) { + throw wrongValueFormatException(index, "int", e); + } + } + + /** + * Get the JSONArray associated with an index. + * + * @param index + * The index must be between 0 and length() - 1. + * @return A JSONArray value. + * @throws JSONException + * If there is no value for the index. or if the value is not a + * JSONArray + */ + public JSONArray getJSONArray(int index) throws JSONException { + Object object = this.get(index); + if (object instanceof JSONArray) { + return (JSONArray) object; + } + throw wrongValueFormatException(index, "JSONArray", null); + } + + /** + * Get the JSONObject associated with an index. + * + * @param index + * subscript + * @return A JSONObject value. + * @throws JSONException + * If there is no value for the index or if the value is not a + * JSONObject + */ + public JSONObject getJSONObject(int index) throws JSONException { + Object object = this.get(index); + if (object instanceof JSONObject) { + return (JSONObject) object; + } + throw wrongValueFormatException(index, "JSONObject", null); + } + + /** + * Get the long value associated with an index. + * + * @param index + * The index must be between 0 and length() - 1. + * @return The value. + * @throws JSONException + * If the key is not found or if the value cannot be converted + * to a number. + */ + public long getLong(int index) throws JSONException { + final Object object = this.get(index); + if(object instanceof Number) { + return ((Number)object).longValue(); + } + try { + return Long.parseLong(object.toString()); + } catch (Exception e) { + throw wrongValueFormatException(index, "long", e); + } + } + + /** + * Get the string associated with an index. + * + * @param index + * The index must be between 0 and length() - 1. + * @return A string value. + * @throws JSONException + * If there is no string value for the index. + */ + public String getString(int index) throws JSONException { + Object object = this.get(index); + if (object instanceof String) { + return (String) object; + } + throw wrongValueFormatException(index, "String", null); + } + + /** + * Determine if the value is null. + * + * @param index + * The index must be between 0 and length() - 1. + * @return true if the value at the index is null, or if there is no value. + */ + public boolean isNull(int index) { + return JSONObject.NULL.equals(this.opt(index)); + } + + /** + * Make a string from the contents of this JSONArray. The + * separator string is inserted between each element. Warning: + * This method assumes that the data structure is acyclical. + * + * @param separator + * A string that will be inserted between the elements. + * @return a string. + * @throws JSONException + * If the array contains an invalid number. + */ + public String join(String separator) throws JSONException { + int len = this.length(); + if (len == 0) { + return ""; + } + + StringBuilder sb = new StringBuilder( + JSONObject.valueToString(this.myArrayList.get(0))); + + for (int i = 1; i < len; i++) { + sb.append(separator) + .append(JSONObject.valueToString(this.myArrayList.get(i))); + } + return sb.toString(); + } + + /** + * Get the number of elements in the JSONArray, included nulls. + * + * @return The length (or size). + */ + public int length() { + return this.myArrayList.size(); + } + + /** + * Removes all of the elements from this JSONArray. + * The JSONArray will be empty after this call returns. + */ + public void clear() { + this.myArrayList.clear(); + } + + /** + * Get the optional object value associated with an index. + * + * @param index + * The index must be between 0 and length() - 1. If not, null is returned. + * @return An object value, or null if there is no object at that index. + */ + public Object opt(int index) { + return (index < 0 || index >= this.length()) ? null : this.myArrayList + .get(index); + } + + /** + * Get the optional boolean value associated with an index. It returns false + * if there is no value at that index, or if the value is not Boolean.TRUE + * or the String "true". + * + * @param index + * The index must be between 0 and length() - 1. + * @return The truth. + */ + public boolean optBoolean(int index) { + return this.optBoolean(index, false); + } + + /** + * Get the optional boolean value associated with an index. It returns the + * defaultValue if there is no value at that index or if it is not a Boolean + * or the String "true" or "false" (case insensitive). + * + * @param index + * The index must be between 0 and length() - 1. + * @param defaultValue + * A boolean default. + * @return The truth. + */ + public boolean optBoolean(int index, boolean defaultValue) { + try { + return this.getBoolean(index); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * Get the optional double value associated with an index. NaN is returned + * if there is no value for the index, or if the value is not a number and + * cannot be converted to a number. + * + * @param index + * The index must be between 0 and length() - 1. + * @return The value. + */ + public double optDouble(int index) { + return this.optDouble(index, Double.NaN); + } + + /** + * Get the optional double value associated with an index. The defaultValue + * is returned if there is no value for the index, or if the value is not a + * number and cannot be converted to a number. + * + * @param index + * subscript + * @param defaultValue + * The default value. + * @return The value. + */ + public double optDouble(int index, double defaultValue) { + final Number val = this.optNumber(index, null); + if (val == null) { + return defaultValue; + } + final double doubleValue = val.doubleValue(); + // if (Double.isNaN(doubleValue) || Double.isInfinite(doubleValue)) { + // return defaultValue; + // } + return doubleValue; + } + + /** + * Get the optional float value associated with an index. NaN is returned + * if there is no value for the index, or if the value is not a number and + * cannot be converted to a number. + * + * @param index + * The index must be between 0 and length() - 1. + * @return The value. + */ + public float optFloat(int index) { + return this.optFloat(index, Float.NaN); + } + + /** + * Get the optional float value associated with an index. The defaultValue + * is returned if there is no value for the index, or if the value is not a + * number and cannot be converted to a number. + * + * @param index + * subscript + * @param defaultValue + * The default value. + * @return The value. + */ + public float optFloat(int index, float defaultValue) { + final Number val = this.optNumber(index, null); + if (val == null) { + return defaultValue; + } + final float floatValue = val.floatValue(); + // if (Float.isNaN(floatValue) || Float.isInfinite(floatValue)) { + // return floatValue; + // } + return floatValue; + } + + /** + * Get the optional int value associated with an index. Zero is returned if + * there is no value for the index, or if the value is not a number and + * cannot be converted to a number. + * + * @param index + * The index must be between 0 and length() - 1. + * @return The value. + */ + public int optInt(int index) { + return this.optInt(index, 0); + } + + /** + * Get the optional int value associated with an index. The defaultValue is + * returned if there is no value for the index, or if the value is not a + * number and cannot be converted to a number. + * + * @param index + * The index must be between 0 and length() - 1. + * @param defaultValue + * The default value. + * @return The value. + */ + public int optInt(int index, int defaultValue) { + final Number val = this.optNumber(index, null); + if (val == null) { + return defaultValue; + } + return val.intValue(); + } + + /** + * Get the enum value associated with a key. + * + * @param + * Enum Type + * @param clazz + * The type of enum to retrieve. + * @param index + * The index must be between 0 and length() - 1. + * @return The enum value at the index location or null if not found + */ + public > E optEnum(Class clazz, int index) { + return this.optEnum(clazz, index, null); + } + + /** + * Get the enum value associated with a key. + * + * @param + * Enum Type + * @param clazz + * The type of enum to retrieve. + * @param index + * The index must be between 0 and length() - 1. + * @param defaultValue + * The default in case the value is not found + * @return The enum value at the index location or defaultValue if + * the value is not found or cannot be assigned to clazz + */ + public > E optEnum(Class clazz, int index, E defaultValue) { + try { + Object val = this.opt(index); + if (JSONObject.NULL.equals(val)) { + return defaultValue; + } + if (clazz.isAssignableFrom(val.getClass())) { + // we just checked it! + @SuppressWarnings("unchecked") + E myE = (E) val; + return myE; + } + return Enum.valueOf(clazz, val.toString()); + } catch (IllegalArgumentException e) { + return defaultValue; + } catch (NullPointerException e) { + return defaultValue; + } + } + + /** + * Get the optional BigInteger value associated with an index. The + * defaultValue is returned if there is no value for the index, or if the + * value is not a number and cannot be converted to a number. + * + * @param index + * The index must be between 0 and length() - 1. + * @param defaultValue + * The default value. + * @return The value. + */ + public BigInteger optBigInteger(int index, BigInteger defaultValue) { + Object val = this.opt(index); + return JSONObject.objectToBigInteger(val, defaultValue); + } + + /** + * Get the optional BigDecimal value associated with an index. The + * defaultValue is returned if there is no value for the index, or if the + * value is not a number and cannot be converted to a number. If the value + * is float or double, the {@link BigDecimal#BigDecimal(double)} + * constructor will be used. See notes on the constructor for conversion + * issues that may arise. + * + * @param index + * The index must be between 0 and length() - 1. + * @param defaultValue + * The default value. + * @return The value. + */ + public BigDecimal optBigDecimal(int index, BigDecimal defaultValue) { + Object val = this.opt(index); + return JSONObject.objectToBigDecimal(val, defaultValue); + } + + /** + * Get the optional JSONArray associated with an index. + * + * @param index + * subscript + * @return A JSONArray value, or null if the index has no value, or if the + * value is not a JSONArray. + */ + public JSONArray optJSONArray(int index) { + Object o = this.opt(index); + return o instanceof JSONArray ? (JSONArray) o : null; + } + + /** + * Get the optional JSONObject associated with an index. Null is returned if + * the key is not found, or null if the index has no value, or if the value + * is not a JSONObject. + * + * @param index + * The index must be between 0 and length() - 1. + * @return A JSONObject value. + */ + public JSONObject optJSONObject(int index) { + Object o = this.opt(index); + return o instanceof JSONObject ? (JSONObject) o : null; + } + + /** + * Get the optional long value associated with an index. Zero is returned if + * there is no value for the index, or if the value is not a number and + * cannot be converted to a number. + * + * @param index + * The index must be between 0 and length() - 1. + * @return The value. + */ + public long optLong(int index) { + return this.optLong(index, 0); + } + + /** + * Get the optional long value associated with an index. The defaultValue is + * returned if there is no value for the index, or if the value is not a + * number and cannot be converted to a number. + * + * @param index + * The index must be between 0 and length() - 1. + * @param defaultValue + * The default value. + * @return The value. + */ + public long optLong(int index, long defaultValue) { + final Number val = this.optNumber(index, null); + if (val == null) { + return defaultValue; + } + return val.longValue(); + } + + /** + * Get an optional {@link Number} value associated with a key, or null + * if there is no such key or if the value is not a number. If the value is a string, + * an attempt will be made to evaluate it as a number ({@link BigDecimal}). This method + * would be used in cases where type coercion of the number value is unwanted. + * + * @param index + * The index must be between 0 and length() - 1. + * @return An object which is the value. + */ + public Number optNumber(int index) { + return this.optNumber(index, null); + } + + /** + * Get an optional {@link Number} value associated with a key, or the default if there + * is no such key or if the value is not a number. If the value is a string, + * an attempt will be made to evaluate it as a number ({@link BigDecimal}). This method + * would be used in cases where type coercion of the number value is unwanted. + * + * @param index + * The index must be between 0 and length() - 1. + * @param defaultValue + * The default. + * @return An object which is the value. + */ + public Number optNumber(int index, Number defaultValue) { + Object val = this.opt(index); + if (JSONObject.NULL.equals(val)) { + return defaultValue; + } + if (val instanceof Number){ + return (Number) val; + } + + if (val instanceof String) { + try { + return JSONObject.stringToNumber((String) val); + } catch (Exception e) { + return defaultValue; + } + } + return defaultValue; + } + + /** + * Get the optional string value associated with an index. It returns an + * empty string if there is no value at that index. If the value is not a + * string and is not null, then it is converted to a string. + * + * @param index + * The index must be between 0 and length() - 1. + * @return A String value. + */ + public String optString(int index) { + return this.optString(index, ""); + } + + /** + * Get the optional string associated with an index. The defaultValue is + * returned if the key is not found. + * + * @param index + * The index must be between 0 and length() - 1. + * @param defaultValue + * The default value. + * @return A String value. + */ + public String optString(int index, String defaultValue) { + Object object = this.opt(index); + return JSONObject.NULL.equals(object) ? defaultValue : object + .toString(); + } + + /** + * Append a boolean value. This increases the array's length by one. + * + * @param value + * A boolean value. + * @return this. + */ + public JSONArray put(boolean value) { + return this.put(value ? Boolean.TRUE : Boolean.FALSE); + } + + /** + * Put a value in the JSONArray, where the value will be a JSONArray which + * is produced from a Collection. + * + * @param value + * A Collection value. + * @return this. + * @throws JSONException + * If the value is non-finite number. + */ + public JSONArray put(Collection value) { + return this.put(new JSONArray(value)); + } + + /** + * Append a double value. This increases the array's length by one. + * + * @param value + * A double value. + * @return this. + * @throws JSONException + * if the value is not finite. + */ + public JSONArray put(double value) throws JSONException { + return this.put(Double.valueOf(value)); + } + + /** + * Append a float value. This increases the array's length by one. + * + * @param value + * A float value. + * @return this. + * @throws JSONException + * if the value is not finite. + */ + public JSONArray put(float value) throws JSONException { + return this.put(Float.valueOf(value)); + } + + /** + * Append an int value. This increases the array's length by one. + * + * @param value + * An int value. + * @return this. + */ + public JSONArray put(int value) { + return this.put(Integer.valueOf(value)); + } + + /** + * Append an long value. This increases the array's length by one. + * + * @param value + * A long value. + * @return this. + */ + public JSONArray put(long value) { + return this.put(Long.valueOf(value)); + } + + /** + * Put a value in the JSONArray, where the value will be a JSONObject which + * is produced from a Map. + * + * @param value + * A Map value. + * @return this. + * @throws JSONException + * If a value in the map is non-finite number. + * @throws NullPointerException + * If a key in the map is null + */ + public JSONArray put(Map value) { + return this.put(new JSONObject(value)); + } + + /** + * Append an object value. This increases the array's length by one. + * + * @param value + * An object value. The value should be a Boolean, Double, + * Integer, JSONArray, JSONObject, Long, or String, or the + * JSONObject.NULL object. + * @return this. + * @throws JSONException + * If the value is non-finite number. + */ + public JSONArray put(Object value) { + JSONObject.testValidity(value); + this.myArrayList.add(value); + return this; + } + + /** + * Put or replace a boolean value in the JSONArray. If the index is greater + * than the length of the JSONArray, then null elements will be added as + * necessary to pad it out. + * + * @param index + * The subscript. + * @param value + * A boolean value. + * @return this. + * @throws JSONException + * If the index is negative. + */ + public JSONArray put(int index, boolean value) throws JSONException { + return this.put(index, value ? Boolean.TRUE : Boolean.FALSE); + } + + /** + * Put a value in the JSONArray, where the value will be a JSONArray which + * is produced from a Collection. + * + * @param index + * The subscript. + * @param value + * A Collection value. + * @return this. + * @throws JSONException + * If the index is negative or if the value is non-finite. + */ + public JSONArray put(int index, Collection value) throws JSONException { + return this.put(index, new JSONArray(value)); + } + + /** + * Put or replace a double value. If the index is greater than the length of + * the JSONArray, then null elements will be added as necessary to pad it + * out. + * + * @param index + * The subscript. + * @param value + * A double value. + * @return this. + * @throws JSONException + * If the index is negative or if the value is non-finite. + */ + public JSONArray put(int index, double value) throws JSONException { + return this.put(index, Double.valueOf(value)); + } + + /** + * Put or replace a float value. If the index is greater than the length of + * the JSONArray, then null elements will be added as necessary to pad it + * out. + * + * @param index + * The subscript. + * @param value + * A float value. + * @return this. + * @throws JSONException + * If the index is negative or if the value is non-finite. + */ + public JSONArray put(int index, float value) throws JSONException { + return this.put(index, Float.valueOf(value)); + } + + /** + * Put or replace an int value. If the index is greater than the length of + * the JSONArray, then null elements will be added as necessary to pad it + * out. + * + * @param index + * The subscript. + * @param value + * An int value. + * @return this. + * @throws JSONException + * If the index is negative. + */ + public JSONArray put(int index, int value) throws JSONException { + return this.put(index, Integer.valueOf(value)); + } + + /** + * Put or replace a long value. If the index is greater than the length of + * the JSONArray, then null elements will be added as necessary to pad it + * out. + * + * @param index + * The subscript. + * @param value + * A long value. + * @return this. + * @throws JSONException + * If the index is negative. + */ + public JSONArray put(int index, long value) throws JSONException { + return this.put(index, Long.valueOf(value)); + } + + /** + * Put a value in the JSONArray, where the value will be a JSONObject that + * is produced from a Map. + * + * @param index + * The subscript. + * @param value + * The Map value. + * @return this. + * @throws JSONException + * If the index is negative or if the value is an invalid + * number. + * @throws NullPointerException + * If a key in the map is null + */ + public JSONArray put(int index, Map value) throws JSONException { + this.put(index, new JSONObject(value)); + return this; + } + + /** + * Put or replace an object value in the JSONArray. If the index is greater + * than the length of the JSONArray, then null elements will be added as + * necessary to pad it out. + * + * @param index + * The subscript. + * @param value + * The value to put into the array. The value should be a + * Boolean, Double, Integer, JSONArray, JSONObject, Long, or + * String, or the JSONObject.NULL object. + * @return this. + * @throws JSONException + * If the index is negative or if the value is an invalid + * number. + */ + public JSONArray put(int index, Object value) throws JSONException { + if (index < 0) { + throw new JSONException("JSONArray[" + index + "] not found."); + } + if (index < this.length()) { + JSONObject.testValidity(value); + this.myArrayList.set(index, value); + return this; + } + if(index == this.length()){ + // simple append + return this.put(value); + } + // if we are inserting past the length, we want to grow the array all at once + // instead of incrementally. + this.myArrayList.ensureCapacity(index + 1); + while (index != this.length()) { + // we don't need to test validity of NULL objects + this.myArrayList.add(JSONObject.NULL); + } + return this.put(value); + } + + /** + * Put a collection's elements in to the JSONArray. + * + * @param collection + * A Collection. + * @return this. + */ + public JSONArray putAll(Collection collection) { + this.addAll(collection, false); + return this; + } + + /** + * Put an Iterable's elements in to the JSONArray. + * + * @param iter + * An Iterable. + * @return this. + */ + public JSONArray putAll(Iterable iter) { + this.addAll(iter, false); + return this; + } + + /** + * Put a JSONArray's elements in to the JSONArray. + * + * @param array + * A JSONArray. + * @return this. + */ + public JSONArray putAll(JSONArray array) { + // directly copy the elements from the source array to this one + // as all wrapping should have been done already in the source. + this.myArrayList.addAll(array.myArrayList); + return this; + } + + /** + * Put an array's elements in to the JSONArray. + * + * @param array + * Array. If the parameter passed is null, or not an array or Iterable, an + * exception will be thrown. + * @return this. + * + * @throws JSONException + * If not an array, JSONArray, Iterable or if an value is non-finite number. + * @throws NullPointerException + * Thrown if the array parameter is null. + */ + public JSONArray putAll(Object array) throws JSONException { + this.addAll(array, false); + return this; + } + + /** + * Creates a JSONPointer using an initialization string and tries to + * match it to an item within this JSONArray. For example, given a + * JSONArray initialized with this document: + *
+     * [
+     *     {"b":"c"}
+     * ]
+     * 
+ * and this JSONPointer string: + *
+     * "/0/b"
+     * 
+ * Then this method will return the String "c" + * A JSONPointerException may be thrown from code called by this method. + * + * @param jsonPointer string that can be used to create a JSONPointer + * @return the item matched by the JSONPointer, otherwise null + */ + public Object query(String jsonPointer) { + return query(new JSONPointer(jsonPointer)); + } + + /** + * Uses a user initialized JSONPointer and tries to + * match it to an item within this JSONArray. For example, given a + * JSONArray initialized with this document: + *
+     * [
+     *     {"b":"c"}
+     * ]
+     * 
+ * and this JSONPointer: + *
+     * "/0/b"
+     * 
+ * Then this method will return the String "c" + * A JSONPointerException may be thrown from code called by this method. + * + * @param jsonPointer string that can be used to create a JSONPointer + * @return the item matched by the JSONPointer, otherwise null + */ + public Object query(JSONPointer jsonPointer) { + return jsonPointer.queryFrom(this); + } + + /** + * Queries and returns a value from this object using {@code jsonPointer}, or + * returns null if the query fails due to a missing key. + * + * @param jsonPointer the string representation of the JSON pointer + * @return the queried value or {@code null} + * @throws IllegalArgumentException if {@code jsonPointer} has invalid syntax + */ + public Object optQuery(String jsonPointer) { + return optQuery(new JSONPointer(jsonPointer)); + } + + /** + * Queries and returns a value from this object using {@code jsonPointer}, or + * returns null if the query fails due to a missing key. + * + * @param jsonPointer The JSON pointer + * @return the queried value or {@code null} + * @throws IllegalArgumentException if {@code jsonPointer} has invalid syntax + */ + public Object optQuery(JSONPointer jsonPointer) { + try { + return jsonPointer.queryFrom(this); + } catch (JSONPointerException e) { + return null; + } + } + + /** + * Remove an index and close the hole. + * + * @param index + * The index of the element to be removed. + * @return The value that was associated with the index, or null if there + * was no value. + */ + public Object remove(int index) { + return index >= 0 && index < this.length() + ? this.myArrayList.remove(index) + : null; + } + + /** + * Determine if two JSONArrays are similar. + * They must contain similar sequences. + * + * @param other The other JSONArray + * @return true if they are equal + */ + public boolean similar(Object other) { + if (!(other instanceof JSONArray)) { + return false; + } + int len = this.length(); + if (len != ((JSONArray)other).length()) { + return false; + } + for (int i = 0; i < len; i += 1) { + Object valueThis = this.myArrayList.get(i); + Object valueOther = ((JSONArray)other).myArrayList.get(i); + if(valueThis == valueOther) { + continue; + } + if(valueThis == null) { + return false; + } + if (valueThis instanceof JSONObject) { + if (!((JSONObject)valueThis).similar(valueOther)) { + return false; + } + } else if (valueThis instanceof JSONArray) { + if (!((JSONArray)valueThis).similar(valueOther)) { + return false; + } + } else if (valueThis instanceof Number && valueOther instanceof Number) { + if (!JSONObject.isNumberSimilar((Number)valueThis, (Number)valueOther)) { + return false; + } + } else if (!valueThis.equals(valueOther)) { + return false; + } + } + return true; + } + + /** + * Produce a JSONObject by combining a JSONArray of names with the values of + * this JSONArray. + * + * @param names + * A JSONArray containing a list of key strings. These will be + * paired with the values. + * @return A JSONObject, or null if there are no names or if this JSONArray + * has no values. + * @throws JSONException + * If any of the names are null. + */ + public JSONObject toJSONObject(JSONArray names) throws JSONException { + if (names == null || names.isEmpty() || this.isEmpty()) { + return null; + } + JSONObject jo = new JSONObject(names.length()); + for (int i = 0; i < names.length(); i += 1) { + jo.put(names.getString(i), this.opt(i)); + } + return jo; + } + + /** + * Make a JSON text of this JSONArray. For compactness, no unnecessary + * whitespace is added. If it is not possible to produce a syntactically + * correct JSON text then null will be returned instead. This could occur if + * the array contains an invalid number. + *

+ * Warning: This method assumes that the data structure is acyclical. + * + * + * @return a printable, displayable, transmittable representation of the + * array. + */ + @Override + public String toString() { + try { + return this.toString(0); + } catch (Exception e) { + return null; + } + } + + /** + * Make a pretty-printed JSON text of this JSONArray. + * + *

If

 {@code indentFactor > 0}
and the {@link JSONArray} has only + * one element, then the array will be output on a single line: + *
{@code [1]}
+ * + *

If an array has 2 or more elements, then it will be output across + * multiple lines:

{@code
+     * [
+     * 1,
+     * "value 2",
+     * 3
+     * ]
+     * }
+ *

+ * Warning: This method assumes that the data structure is acyclical. + * + * + * @param indentFactor + * The number of spaces to add to each level of indentation. + * @return a printable, displayable, transmittable representation of the + * object, beginning with [ (left + * bracket) and ending with ] + *  (right bracket). + * @throws JSONException if a called function fails + */ + public String toString(int indentFactor) throws JSONException { + StringWriter sw = new StringWriter(); + synchronized (sw.getBuffer()) { + return this.write(sw, indentFactor, 0).toString(); + } + } + + /** + * Write the contents of the JSONArray as JSON text to a writer. For + * compactness, no whitespace is added. + *

+ * Warning: This method assumes that the data structure is acyclical. + * + * @param writer the writer object + * @return The writer. + * @throws JSONException if a called function fails + */ + public Writer write(Writer writer) throws JSONException { + return this.write(writer, 0, 0); + } + + /** + * Write the contents of the JSONArray as JSON text to a writer. + * + *

If

{@code indentFactor > 0}
and the {@link JSONArray} has only + * one element, then the array will be output on a single line: + *
{@code [1]}
+ * + *

If an array has 2 or more elements, then it will be output across + * multiple lines:

{@code
+     * [
+     * 1,
+     * "value 2",
+     * 3
+     * ]
+     * }
+ *

+ * Warning: This method assumes that the data structure is acyclical. + * + * + * @param writer + * Writes the serialized JSON + * @param indentFactor + * The number of spaces to add to each level of indentation. + * @param indent + * The indentation of the top level. + * @return The writer. + * @throws JSONException if a called function fails or unable to write + */ + public Writer write(Writer writer, int indentFactor, int indent) + throws JSONException { + try { + boolean needsComma = false; + int length = this.length(); + writer.write('['); + + if (length == 1) { + try { + JSONObject.writeValue(writer, this.myArrayList.get(0), + indentFactor, indent); + } catch (Exception e) { + throw new JSONException("Unable to write JSONArray value at index: 0", e); + } + } else if (length != 0) { + final int newIndent = indent + indentFactor; + + for (int i = 0; i < length; i += 1) { + if (needsComma) { + writer.write(','); + } + if (indentFactor > 0) { + writer.write('\n'); + } + JSONObject.indent(writer, newIndent); + try { + JSONObject.writeValue(writer, this.myArrayList.get(i), + indentFactor, newIndent); + } catch (Exception e) { + throw new JSONException("Unable to write JSONArray value at index: " + i, e); + } + needsComma = true; + } + if (indentFactor > 0) { + writer.write('\n'); + } + JSONObject.indent(writer, indent); + } + writer.write(']'); + return writer; + } catch (IOException e) { + throw new JSONException(e); + } + } + + /** + * Returns a java.util.List containing all of the elements in this array. + * If an element in the array is a JSONArray or JSONObject it will also + * be converted to a List and a Map respectively. + *

+ * Warning: This method assumes that the data structure is acyclical. + * + * @return a java.util.List containing the elements of this array + */ + public List toList() { + List results = new ArrayList(this.myArrayList.size()); + for (Object element : this.myArrayList) { + if (element == null || JSONObject.NULL.equals(element)) { + results.add(null); + } else if (element instanceof JSONArray) { + results.add(((JSONArray) element).toList()); + } else if (element instanceof JSONObject) { + results.add(((JSONObject) element).toMap()); + } else { + results.add(element); + } + } + return results; + } + + /** + * Check if JSONArray is empty. + * + * @return true if JSONArray is empty, otherwise false. + */ + public boolean isEmpty() { + return this.myArrayList.isEmpty(); + } + + /** + * Add a collection's elements to the JSONArray. + * + * @param collection + * A Collection. + * @param wrap + * {@code true} to call {@link JSONObject#wrap(Object)} for each item, + * {@code false} to add the items directly + * + */ + private void addAll(Collection collection, boolean wrap) { + this.myArrayList.ensureCapacity(this.myArrayList.size() + collection.size()); + if (wrap) { + for (Object o: collection){ + this.put(JSONObject.wrap(o)); + } + } else { + for (Object o: collection){ + this.put(o); + } + } + } + + /** + * Add an Iterable's elements to the JSONArray. + * + * @param iter + * An Iterable. + * @param wrap + * {@code true} to call {@link JSONObject#wrap(Object)} for each item, + * {@code false} to add the items directly + */ + private void addAll(Iterable iter, boolean wrap) { + if (wrap) { + for (Object o: iter){ + this.put(JSONObject.wrap(o)); + } + } else { + for (Object o: iter){ + this.put(o); + } + } + } + + /** + * Add an array's elements to the JSONArray. + * + * @param array + * Array. If the parameter passed is null, or not an array, + * JSONArray, Collection, or Iterable, an exception will be + * thrown. + * @param wrap + * {@code true} to call {@link JSONObject#wrap(Object)} for each item, + * {@code false} to add the items directly + * + * @throws JSONException + * If not an array or if an array value is non-finite number. + * @throws NullPointerException + * Thrown if the array parameter is null. + */ + private void addAll(Object array, boolean wrap) throws JSONException { + if (array.getClass().isArray()) { + int length = Array.getLength(array); + this.myArrayList.ensureCapacity(this.myArrayList.size() + length); + if (wrap) { + for (int i = 0; i < length; i += 1) { + this.put(JSONObject.wrap(Array.get(array, i))); + } + } else { + for (int i = 0; i < length; i += 1) { + this.put(Array.get(array, i)); + } + } + } else if (array instanceof JSONArray) { + // use the built in array list `addAll` as all object + // wrapping should have been completed in the original + // JSONArray + this.myArrayList.addAll(((JSONArray)array).myArrayList); + } else if (array instanceof Collection) { + this.addAll((Collection)array, wrap); + } else if (array instanceof Iterable) { + this.addAll((Iterable)array, wrap); + } else { + throw new JSONException( + "JSONArray initial value should be a string or collection or array."); + } + } + + /** + * Create a new JSONException in a common format for incorrect conversions. + * @param idx index of the item + * @param valueType the type of value being coerced to + * @param cause optional cause of the coercion failure + * @return JSONException that can be thrown. + */ + private static JSONException wrongValueFormatException( + int idx, + String valueType, + Throwable cause) { + return new JSONException( + "JSONArray[" + idx + "] is not a " + valueType + "." + , cause); + } + + /** + * Create a new JSONException in a common format for incorrect conversions. + * @param idx index of the item + * @param valueType the type of value being coerced to + * @param cause optional cause of the coercion failure + * @return JSONException that can be thrown. + */ + private static JSONException wrongValueFormatException( + int idx, + String valueType, + Object value, + Throwable cause) { + return new JSONException( + "JSONArray[" + idx + "] is not a " + valueType + " (" + value + ")." + , cause); + } + +} \ No newline at end of file diff --git a/src/net/lax1dude/eaglercraft/JSONException.java b/src/net/lax1dude/eaglercraft/JSONException.java new file mode 100644 index 0000000..c2766bf --- /dev/null +++ b/src/net/lax1dude/eaglercraft/JSONException.java @@ -0,0 +1,69 @@ +package net.lax1dude.eaglercraft; + +/* +Copyright (c) 2002 JSON.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +The Software shall be used for Good, not Evil. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + */ + +/** + * The JSONException is thrown by the JSON.org classes when things are amiss. + * + * @author JSON.org + * @version 2015-12-09 + */ +public class JSONException extends RuntimeException { + /** Serialization ID */ + private static final long serialVersionUID = 0; + + /** + * Constructs a JSONException with an explanatory message. + * + * @param message + * Detail about the reason for the exception. + */ + public JSONException(final String message) { + super(message); + } + + /** + * Constructs a JSONException with an explanatory message and cause. + * + * @param message + * Detail about the reason for the exception. + * @param cause + * The cause. + */ + public JSONException(final String message, final Throwable cause) { + super(message, cause); + } + + /** + * Constructs a new JSONException with the specified cause. + * + * @param cause + * The cause. + */ + public JSONException(final Throwable cause) { + super(cause.getMessage(), cause); + } + +} \ No newline at end of file diff --git a/src/net/lax1dude/eaglercraft/JSONObject.java b/src/net/lax1dude/eaglercraft/JSONObject.java new file mode 100644 index 0000000..f9d1d22 --- /dev/null +++ b/src/net/lax1dude/eaglercraft/JSONObject.java @@ -0,0 +1,2732 @@ +package net.lax1dude.eaglercraft; + +import java.io.Closeable; + +/* + Copyright (c) 2002 JSON.org + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + The Software shall be used for Good, not Evil. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + */ + +import java.io.IOException; +import java.io.StringWriter; +import java.io.Writer; +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.IdentityHashMap; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; +import java.util.Map.Entry; +import java.util.ResourceBundle; +import java.util.Set; +import java.util.regex.Pattern; + +/** + * A JSONObject is an unordered collection of name/value pairs. Its external + * form is a string wrapped in curly braces with colons between the names and + * values, and commas between the values and names. The internal form is an + * object having get and opt methods for accessing + * the values by name, and put methods for adding or replacing + * values by name. The values can be any of these types: Boolean, + * JSONArray, JSONObject, Number, + * String, or the JSONObject.NULL object. A + * JSONObject constructor can be used to convert an external form JSON text + * into an internal form whose values can be retrieved with the + * get and opt methods, or to convert values into a + * JSON text using the put and toString methods. A + * get method returns a value if one can be found, and throws an + * exception if one cannot be found. An opt method returns a + * default value instead of throwing an exception, and so is useful for + * obtaining optional values. + *

+ * The generic get() and opt() methods return an + * object, which you can cast or query for type. There are also typed + * get and opt methods that do type checking and type + * coercion for you. The opt methods differ from the get methods in that they + * do not throw. Instead, they return a specified value, such as null. + *

+ * The put methods add or replace values in an object. For + * example, + * + *

+ * myString = new JSONObject()
+ *         .put("JSON", "Hello, World!").toString();
+ * 
+ * + * produces the string {"JSON": "Hello, World"}. + *

+ * The texts produced by the toString methods strictly conform to + * the JSON syntax rules. The constructors are more forgiving in the texts they + * will accept: + *

    + *
  • An extra , (comma) may appear just + * before the closing brace.
  • + *
  • Strings may be quoted with ' (single + * quote).
  • + *
  • Strings do not need to be quoted at all if they do not begin with a + * quote or single quote, and if they do not contain leading or trailing + * spaces, and if they do not contain any of these characters: + * { } [ ] / \ : , # and if they do not look like numbers and + * if they are not the reserved words true, false, + * or null.
  • + *
+ * + * @author JSON.org + * @version 2016-08-15 + */ +public class JSONObject { + /** + * JSONObject.NULL is equivalent to the value that JavaScript calls null, + * whilst Java's null is equivalent to the value that JavaScript calls + * undefined. + */ + private static final class Null { + + /** + * There is only intended to be a single instance of the NULL object, + * so the clone method returns itself. + * + * @return NULL. + */ + @Override + protected final Object clone() { + return this; + } + + /** + * A Null object is equal to the null value and to itself. + * + * @param object + * An object to test for nullness. + * @return true if the object parameter is the JSONObject.NULL object or + * null. + */ + @Override + @SuppressWarnings("lgtm[java/unchecked-cast-in-equals]") + public boolean equals(Object object) { + return object == null || object == this; + } + /** + * A Null object is equal to the null value and to itself. + * + * @return always returns 0. + */ + @Override + public int hashCode() { + return 0; + } + + /** + * Get the "null" string value. + * + * @return The string "null". + */ + @Override + public String toString() { + return "null"; + } + } + + /** + * Regular Expression Pattern that matches JSON Numbers. This is primarily used for + * output to guarantee that we are always writing valid JSON. + */ + static final Pattern NUMBER_PATTERN = Pattern.compile("-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?"); + + /** + * The map where the JSONObject's properties are kept. + */ + private final Map map; + + /** + * It is sometimes more convenient and less ambiguous to have a + * NULL object than to use Java's null value. + * JSONObject.NULL.equals(null) returns true. + * JSONObject.NULL.toString() returns "null". + */ + public static final Object NULL = new Null(); + + /** + * Construct an empty JSONObject. + */ + public JSONObject() { + // HashMap is used on purpose to ensure that elements are unordered by + // the specification. + // JSON tends to be a portable transfer format to allows the container + // implementations to rearrange their items for a faster element + // retrieval based on associative access. + // Therefore, an implementation mustn't rely on the order of the item. + this.map = new HashMap(); + } + + /** + * Construct a JSONObject from a subset of another JSONObject. An array of + * strings is used to identify the keys that should be copied. Missing keys + * are ignored. + * + * @param jo + * A JSONObject. + * @param names + * An array of strings. + */ + public JSONObject(JSONObject jo, String ... names) { + this(names.length); + for (int i = 0; i < names.length; i += 1) { + try { + this.putOnce(names[i], jo.opt(names[i])); + } catch (Exception ignore) { + } + } + } + + /** + * Construct a JSONObject from a JSONTokener. + * + * @param x + * A JSONTokener object containing the source string. + * @throws JSONException + * If there is a syntax error in the source string or a + * duplicated key. + */ + public JSONObject(JSONTokener x) throws JSONException { + this(); + char c; + String key; + + if (x.nextClean() != '{') { + throw x.syntaxError("A JSONObject text must begin with '{'"); + } + for (;;) { + char prev = x.getPrevious(); + c = x.nextClean(); + switch (c) { + case 0: + throw x.syntaxError("A JSONObject text must end with '}'"); + case '}': + return; + case '{': + case '[': + if(prev=='{') { + throw x.syntaxError("A JSON Object can not directly nest another JSON Object or JSON Array."); + } + // fall through + default: + x.back(); + key = x.nextValue().toString(); + } + + // The key is followed by ':'. + + c = x.nextClean(); + if (c != ':') { + throw x.syntaxError("Expected a ':' after a key"); + } + + // Use syntaxError(..) to include error location + + if (key != null) { + // Check if key exists + if (this.opt(key) != null) { + // key already exists + throw x.syntaxError("Duplicate key \"" + key + "\""); + } + // Only add value if non-null + Object value = x.nextValue(); + if (value!=null) { + this.put(key, value); + } + } + + // Pairs are separated by ','. + + switch (x.nextClean()) { + case ';': + case ',': + if (x.nextClean() == '}') { + return; + } + x.back(); + break; + case '}': + return; + default: + throw x.syntaxError("Expected a ',' or '}'"); + } + } + } + + /** + * Construct a JSONObject from a Map. + * + * @param m + * A map object that can be used to initialize the contents of + * the JSONObject. + * @throws JSONException + * If a value in the map is non-finite number. + * @throws NullPointerException + * If a key in the map is null + */ + public JSONObject(Map m) { + if (m == null) { + this.map = new HashMap(); + } else { + this.map = new HashMap(m.size()); + for (final Entry e : m.entrySet()) { + if(e.getKey() == null) { + throw new NullPointerException("Null key."); + } + final Object value = e.getValue(); + if (value != null) { + this.map.put(String.valueOf(e.getKey()), wrap(value)); + } + } + } + } + + /** + * Construct a JSONObject from an Object using bean getters. It reflects on + * all of the public methods of the object. For each of the methods with no + * parameters and a name starting with "get" or + * "is" followed by an uppercase letter, the method is invoked, + * and a key and the value returned from the getter method are put into the + * new JSONObject. + *

+ * The key is formed by removing the "get" or "is" + * prefix. If the second remaining character is not upper case, then the + * first character is converted to lower case. + *

+ * Methods that are static, return void, + * have parameters, or are "bridge" methods, are ignored. + *

+ * For example, if an object has a method named "getName", and + * if the result of calling object.getName() is + * "Larry Fine", then the JSONObject will contain + * "name": "Larry Fine". + *

+ * The {@link JSONPropertyName} annotation can be used on a bean getter to + * override key name used in the JSONObject. For example, using the object + * above with the getName method, if we annotated it with: + *

+     * @JSONPropertyName("FullName")
+     * public String getName() { return this.name; }
+     * 
+ * The resulting JSON object would contain "FullName": "Larry Fine" + *

+ * Similarly, the {@link JSONPropertyName} annotation can be used on non- + * get and is methods. We can also override key + * name used in the JSONObject as seen below even though the field would normally + * be ignored: + *

+     * @JSONPropertyName("FullName")
+     * public String fullName() { return this.name; }
+     * 
+ * The resulting JSON object would contain "FullName": "Larry Fine" + *

+ * The {@link JSONPropertyIgnore} annotation can be used to force the bean property + * to not be serialized into JSON. If both {@link JSONPropertyIgnore} and + * {@link JSONPropertyName} are defined on the same method, a depth comparison is + * performed and the one closest to the concrete class being serialized is used. + * If both annotations are at the same level, then the {@link JSONPropertyIgnore} + * annotation takes precedent and the field is not serialized. + * For example, the following declaration would prevent the getName + * method from being serialized: + *

+     * @JSONPropertyName("FullName")
+     * @JSONPropertyIgnore
+     * public String getName() { return this.name; }
+     * 
+ *

+ * + * @param bean + * An object that has getter methods that should be used to make + * a JSONObject. + */ + public JSONObject(Object bean) { + this(); + this.populateMap(bean); + } + + private JSONObject(Object bean, Set objectsRecord) { + this(); + this.populateMap(bean, objectsRecord); + } + + /** + * Construct a JSONObject from an Object, using reflection to find the + * public members. The resulting JSONObject's keys will be the strings from + * the names array, and the values will be the field values associated with + * those keys in the object. If a key is not found or not visible, then it + * will not be copied into the new JSONObject. + * + * @param object + * An object that has fields that should be used to make a + * JSONObject. + * @param names + * An array of strings, the names of the fields to be obtained + * from the object. + */ + public JSONObject(Object object, String ... names) { + this(names.length); + Class c = object.getClass(); + for (int i = 0; i < names.length; i += 1) { + String name = names[i]; + try { + this.putOpt(name, c.getField(name).get(object)); + } catch (Exception ignore) { + } + } + } + + /** + * Construct a JSONObject from a source JSON text string. This is the most + * commonly used JSONObject constructor. + * + * @param source + * A string beginning with { (left + * brace) and ending with } + *  (right brace). + * @exception JSONException + * If there is a syntax error in the source string or a + * duplicated key. + */ + public JSONObject(String source) throws JSONException { + this(new JSONTokener(source)); + } + + /** + * Construct a JSONObject from a ResourceBundle. + * + * @param baseName + * The ResourceBundle base name. + * @param locale + * The Locale to load the ResourceBundle for. + * @throws JSONException + * If any JSONExceptions are detected. + */ + public JSONObject(String baseName, Locale locale) throws JSONException { + this(); + ResourceBundle bundle = ResourceBundle.getBundle(baseName, locale, + Thread.currentThread().getContextClassLoader()); + +// Iterate through the keys in the bundle. + + Enumeration keys = bundle.getKeys(); + while (keys.hasMoreElements()) { + Object key = keys.nextElement(); + if (key != null) { + +// Go through the path, ensuring that there is a nested JSONObject for each +// segment except the last. Add the value using the last segment's name into +// the deepest nested JSONObject. + + String[] path = ((String) key).split("\\."); + int last = path.length - 1; + JSONObject target = this; + for (int i = 0; i < last; i += 1) { + String segment = path[i]; + JSONObject nextTarget = target.optJSONObject(segment); + if (nextTarget == null) { + nextTarget = new JSONObject(); + target.put(segment, nextTarget); + } + target = nextTarget; + } + target.put(path[last], bundle.getString((String) key)); + } + } + } + + /** + * Constructor to specify an initial capacity of the internal map. Useful for library + * internal calls where we know, or at least can best guess, how big this JSONObject + * will be. + * + * @param initialCapacity initial capacity of the internal map. + */ + protected JSONObject(int initialCapacity){ + this.map = new HashMap(initialCapacity); + } + + /** + * Accumulate values under a key. It is similar to the put method except + * that if there is already an object stored under the key then a JSONArray + * is stored under the key to hold all of the accumulated values. If there + * is already a JSONArray, then the new value is appended to it. In + * contrast, the put method replaces the previous value. + * + * If only one value is accumulated that is not a JSONArray, then the result + * will be the same as using put. But if multiple values are accumulated, + * then the result will be like append. + * + * @param key + * A key string. + * @param value + * An object to be accumulated under the key. + * @return this. + * @throws JSONException + * If the value is non-finite number. + * @throws NullPointerException + * If the key is null. + */ + public JSONObject accumulate(String key, Object value) throws JSONException { + testValidity(value); + Object object = this.opt(key); + if (object == null) { + this.put(key, + value instanceof JSONArray ? new JSONArray().put(value) + : value); + } else if (object instanceof JSONArray) { + ((JSONArray) object).put(value); + } else { + this.put(key, new JSONArray().put(object).put(value)); + } + return this; + } + + /** + * Append values to the array under a key. If the key does not exist in the + * JSONObject, then the key is put in the JSONObject with its value being a + * JSONArray containing the value parameter. If the key was already + * associated with a JSONArray, then the value parameter is appended to it. + * + * @param key + * A key string. + * @param value + * An object to be accumulated under the key. + * @return this. + * @throws JSONException + * If the value is non-finite number or if the current value associated with + * the key is not a JSONArray. + * @throws NullPointerException + * If the key is null. + */ + public JSONObject append(String key, Object value) throws JSONException { + testValidity(value); + Object object = this.opt(key); + if (object == null) { + this.put(key, new JSONArray().put(value)); + } else if (object instanceof JSONArray) { + this.put(key, ((JSONArray) object).put(value)); + } else { + throw wrongValueFormatException(key, "JSONArray", null, null); + } + return this; + } + + /** + * Produce a string from a double. The string "null" will be returned if the + * number is not finite. + * + * @param d + * A double. + * @return A String. + */ + public static String doubleToString(double d) { + if (Double.isInfinite(d) || Double.isNaN(d)) { + return "null"; + } + +// Shave off trailing zeros and decimal point, if possible. + + String string = Double.toString(d); + if (string.indexOf('.') > 0 && string.indexOf('e') < 0 + && string.indexOf('E') < 0) { + while (string.endsWith("0")) { + string = string.substring(0, string.length() - 1); + } + if (string.endsWith(".")) { + string = string.substring(0, string.length() - 1); + } + } + return string; + } + + /** + * Get the value object associated with a key. + * + * @param key + * A key string. + * @return The object associated with the key. + * @throws JSONException + * if the key is not found. + */ + public Object get(String key) throws JSONException { + if (key == null) { + throw new JSONException("Null key."); + } + Object object = this.opt(key); + if (object == null) { + throw new JSONException("JSONObject[" + quote(key) + "] not found."); + } + return object; + } + + /** + * Get the enum value associated with a key. + * + * @param + * Enum Type + * @param clazz + * The type of enum to retrieve. + * @param key + * A key string. + * @return The enum value associated with the key + * @throws JSONException + * if the key is not found or if the value cannot be converted + * to an enum. + */ + public > E getEnum(Class clazz, String key) throws JSONException { + E val = optEnum(clazz, key); + if(val==null) { + // JSONException should really take a throwable argument. + // If it did, I would re-implement this with the Enum.valueOf + // method and place any thrown exception in the JSONException + throw wrongValueFormatException(key, "enum of type " + quote(clazz.getSimpleName()), null); + } + return val; + } + + /** + * Get the boolean value associated with a key. + * + * @param key + * A key string. + * @return The truth. + * @throws JSONException + * if the value is not a Boolean or the String "true" or + * "false". + */ + public boolean getBoolean(String key) throws JSONException { + Object object = this.get(key); + if (object.equals(Boolean.FALSE) + || (object instanceof String && ((String) object) + .equalsIgnoreCase("false"))) { + return false; + } else if (object.equals(Boolean.TRUE) + || (object instanceof String && ((String) object) + .equalsIgnoreCase("true"))) { + return true; + } + throw wrongValueFormatException(key, "Boolean", null); + } + + /** + * Get the BigInteger value associated with a key. + * + * @param key + * A key string. + * @return The numeric value. + * @throws JSONException + * if the key is not found or if the value cannot + * be converted to BigInteger. + */ + public BigInteger getBigInteger(String key) throws JSONException { + Object object = this.get(key); + BigInteger ret = objectToBigInteger(object, null); + if (ret != null) { + return ret; + } + throw wrongValueFormatException(key, "BigInteger", object, null); + } + + /** + * Get the BigDecimal value associated with a key. If the value is float or + * double, the {@link BigDecimal#BigDecimal(double)} constructor will + * be used. See notes on the constructor for conversion issues that may + * arise. + * + * @param key + * A key string. + * @return The numeric value. + * @throws JSONException + * if the key is not found or if the value + * cannot be converted to BigDecimal. + */ + public BigDecimal getBigDecimal(String key) throws JSONException { + Object object = this.get(key); + BigDecimal ret = objectToBigDecimal(object, null); + if (ret != null) { + return ret; + } + throw wrongValueFormatException(key, "BigDecimal", object, null); + } + + /** + * Get the double value associated with a key. + * + * @param key + * A key string. + * @return The numeric value. + * @throws JSONException + * if the key is not found or if the value is not a Number + * object and cannot be converted to a number. + */ + public double getDouble(String key) throws JSONException { + final Object object = this.get(key); + if(object instanceof Number) { + return ((Number)object).doubleValue(); + } + try { + return Double.parseDouble(object.toString()); + } catch (Exception e) { + throw wrongValueFormatException(key, "double", e); + } + } + + /** + * Get the float value associated with a key. + * + * @param key + * A key string. + * @return The numeric value. + * @throws JSONException + * if the key is not found or if the value is not a Number + * object and cannot be converted to a number. + */ + public float getFloat(String key) throws JSONException { + final Object object = this.get(key); + if(object instanceof Number) { + return ((Number)object).floatValue(); + } + try { + return Float.parseFloat(object.toString()); + } catch (Exception e) { + throw wrongValueFormatException(key, "float", e); + } + } + + /** + * Get the Number value associated with a key. + * + * @param key + * A key string. + * @return The numeric value. + * @throws JSONException + * if the key is not found or if the value is not a Number + * object and cannot be converted to a number. + */ + public Number getNumber(String key) throws JSONException { + Object object = this.get(key); + try { + if (object instanceof Number) { + return (Number)object; + } + return stringToNumber(object.toString()); + } catch (Exception e) { + throw wrongValueFormatException(key, "number", e); + } + } + + /** + * Get the int value associated with a key. + * + * @param key + * A key string. + * @return The integer value. + * @throws JSONException + * if the key is not found or if the value cannot be converted + * to an integer. + */ + public int getInt(String key) throws JSONException { + final Object object = this.get(key); + if(object instanceof Number) { + return ((Number)object).intValue(); + } + try { + return Integer.parseInt(object.toString()); + } catch (Exception e) { + throw wrongValueFormatException(key, "int", e); + } + } + + /** + * Get the JSONArray value associated with a key. + * + * @param key + * A key string. + * @return A JSONArray which is the value. + * @throws JSONException + * if the key is not found or if the value is not a JSONArray. + */ + public JSONArray getJSONArray(String key) throws JSONException { + Object object = this.get(key); + if (object instanceof JSONArray) { + return (JSONArray) object; + } + throw wrongValueFormatException(key, "JSONArray", null); + } + + /** + * Get the JSONObject value associated with a key. + * + * @param key + * A key string. + * @return A JSONObject which is the value. + * @throws JSONException + * if the key is not found or if the value is not a JSONObject. + */ + public JSONObject getJSONObject(String key) throws JSONException { + Object object = this.get(key); + if (object instanceof JSONObject) { + return (JSONObject) object; + } + throw wrongValueFormatException(key, "JSONObject", null); + } + + /** + * Get the long value associated with a key. + * + * @param key + * A key string. + * @return The long value. + * @throws JSONException + * if the key is not found or if the value cannot be converted + * to a long. + */ + public long getLong(String key) throws JSONException { + final Object object = this.get(key); + if(object instanceof Number) { + return ((Number)object).longValue(); + } + try { + return Long.parseLong(object.toString()); + } catch (Exception e) { + throw wrongValueFormatException(key, "long", e); + } + } + + /** + * Get an array of field names from a JSONObject. + * + * @param jo + * JSON object + * @return An array of field names, or null if there are no names. + */ + public static String[] getNames(JSONObject jo) { + if (jo.isEmpty()) { + return null; + } + return jo.keySet().toArray(new String[jo.length()]); + } + + /** + * Get an array of public field names from an Object. + * + * @param object + * object to read + * @return An array of field names, or null if there are no names. + */ + public static String[] getNames(Object object) { + if (object == null) { + return null; + } + Class klass = object.getClass(); + Field[] fields = klass.getFields(); + int length = fields.length; + if (length == 0) { + return null; + } + String[] names = new String[length]; + for (int i = 0; i < length; i += 1) { + names[i] = fields[i].getName(); + } + return names; + } + + /** + * Get the string associated with a key. + * + * @param key + * A key string. + * @return A string which is the value. + * @throws JSONException + * if there is no string value for the key. + */ + public String getString(String key) throws JSONException { + Object object = this.get(key); + if (object instanceof String) { + return (String) object; + } + throw wrongValueFormatException(key, "string", null); + } + + /** + * Determine if the JSONObject contains a specific key. + * + * @param key + * A key string. + * @return true if the key exists in the JSONObject. + */ + public boolean has(String key) { + return this.map.containsKey(key); + } + + /** + * Increment a property of a JSONObject. If there is no such property, + * create one with a value of 1 (Integer). If there is such a property, and if it is + * an Integer, Long, Double, Float, BigInteger, or BigDecimal then add one to it. + * No overflow bounds checking is performed, so callers should initialize the key + * prior to this call with an appropriate type that can handle the maximum expected + * value. + * + * @param key + * A key string. + * @return this. + * @throws JSONException + * If there is already a property with this name that is not an + * Integer, Long, Double, or Float. + */ + public JSONObject increment(String key) throws JSONException { + Object value = this.opt(key); + if (value == null) { + this.put(key, 1); + } else if (value instanceof Integer) { + this.put(key, ((Integer) value).intValue() + 1); + } else if (value instanceof Long) { + this.put(key, ((Long) value).longValue() + 1L); + } else if (value instanceof BigInteger) { + this.put(key, ((BigInteger)value).add(BigInteger.ONE)); + } else if (value instanceof Float) { + this.put(key, ((Float) value).floatValue() + 1.0f); + } else if (value instanceof Double) { + this.put(key, ((Double) value).doubleValue() + 1.0d); + } else if (value instanceof BigDecimal) { + this.put(key, ((BigDecimal)value).add(BigDecimal.ONE)); + } else { + throw new JSONException("Unable to increment [" + quote(key) + "]."); + } + return this; + } + + /** + * Determine if the value associated with the key is null or if there is no + * value. + * + * @param key + * A key string. + * @return true if there is no value associated with the key or if the value + * is the JSONObject.NULL object. + */ + public boolean isNull(String key) { + return JSONObject.NULL.equals(this.opt(key)); + } + + /** + * Get an enumeration of the keys of the JSONObject. Modifying this key Set will also + * modify the JSONObject. Use with caution. + * + * @see Set#iterator() + * + * @return An iterator of the keys. + */ + public Iterator keys() { + return this.keySet().iterator(); + } + + /** + * Get a set of keys of the JSONObject. Modifying this key Set will also modify the + * JSONObject. Use with caution. + * + * @see Map#keySet() + * + * @return A keySet. + */ + public Set keySet() { + return this.map.keySet(); + } + + /** + * Get a set of entries of the JSONObject. These are raw values and may not + * match what is returned by the JSONObject get* and opt* functions. Modifying + * the returned EntrySet or the Entry objects contained therein will modify the + * backing JSONObject. This does not return a clone or a read-only view. + * + * Use with caution. + * + * @see Map#entrySet() + * + * @return An Entry Set + */ + protected Set> entrySet() { + return this.map.entrySet(); + } + + /** + * Get the number of keys stored in the JSONObject. + * + * @return The number of keys in the JSONObject. + */ + public int length() { + return this.map.size(); + } + + /** + * Removes all of the elements from this JSONObject. + * The JSONObject will be empty after this call returns. + */ + public void clear() { + this.map.clear(); + } + + /** + * Check if JSONObject is empty. + * + * @return true if JSONObject is empty, otherwise false. + */ + public boolean isEmpty() { + return this.map.isEmpty(); + } + + /** + * Produce a JSONArray containing the names of the elements of this + * JSONObject. + * + * @return A JSONArray containing the key strings, or null if the JSONObject + * is empty. + */ + public JSONArray names() { + if(this.map.isEmpty()) { + return null; + } + return new JSONArray(this.map.keySet()); + } + + /** + * Produce a string from a Number. + * + * @param number + * A Number + * @return A String. + * @throws JSONException + * If n is a non-finite number. + */ + public static String numberToString(Number number) throws JSONException { + if (number == null) { + throw new JSONException("Null pointer"); + } + testValidity(number); + + // Shave off trailing zeros and decimal point, if possible. + + String string = number.toString(); + if (string.indexOf('.') > 0 && string.indexOf('e') < 0 + && string.indexOf('E') < 0) { + while (string.endsWith("0")) { + string = string.substring(0, string.length() - 1); + } + if (string.endsWith(".")) { + string = string.substring(0, string.length() - 1); + } + } + return string; + } + + /** + * Get an optional value associated with a key. + * + * @param key + * A key string. + * @return An object which is the value, or null if there is no value. + */ + public Object opt(String key) { + return key == null ? null : this.map.get(key); + } + + /** + * Get the enum value associated with a key. + * + * @param + * Enum Type + * @param clazz + * The type of enum to retrieve. + * @param key + * A key string. + * @return The enum value associated with the key or null if not found + */ + public > E optEnum(Class clazz, String key) { + return this.optEnum(clazz, key, null); + } + + /** + * Get the enum value associated with a key. + * + * @param + * Enum Type + * @param clazz + * The type of enum to retrieve. + * @param key + * A key string. + * @param defaultValue + * The default in case the value is not found + * @return The enum value associated with the key or defaultValue + * if the value is not found or cannot be assigned to clazz + */ + public > E optEnum(Class clazz, String key, E defaultValue) { + try { + Object val = this.opt(key); + if (NULL.equals(val)) { + return defaultValue; + } + if (clazz.isAssignableFrom(val.getClass())) { + // we just checked it! + @SuppressWarnings("unchecked") + E myE = (E) val; + return myE; + } + return Enum.valueOf(clazz, val.toString()); + } catch (IllegalArgumentException e) { + return defaultValue; + } catch (NullPointerException e) { + return defaultValue; + } + } + + /** + * Get an optional boolean associated with a key. It returns false if there + * is no such key, or if the value is not Boolean.TRUE or the String "true". + * + * @param key + * A key string. + * @return The truth. + */ + public boolean optBoolean(String key) { + return this.optBoolean(key, false); + } + + /** + * Get an optional boolean associated with a key. It returns the + * defaultValue if there is no such key, or if it is not a Boolean or the + * String "true" or "false" (case insensitive). + * + * @param key + * A key string. + * @param defaultValue + * The default. + * @return The truth. + */ + public boolean optBoolean(String key, boolean defaultValue) { + Object val = this.opt(key); + if (NULL.equals(val)) { + return defaultValue; + } + if (val instanceof Boolean){ + return ((Boolean) val).booleanValue(); + } + try { + // we'll use the get anyway because it does string conversion. + return this.getBoolean(key); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * Get an optional BigDecimal associated with a key, or the defaultValue if + * there is no such key or if its value is not a number. If the value is a + * string, an attempt will be made to evaluate it as a number. If the value + * is float or double, then the {@link BigDecimal#BigDecimal(double)} + * constructor will be used. See notes on the constructor for conversion + * issues that may arise. + * + * @param key + * A key string. + * @param defaultValue + * The default. + * @return An object which is the value. + */ + public BigDecimal optBigDecimal(String key, BigDecimal defaultValue) { + Object val = this.opt(key); + return objectToBigDecimal(val, defaultValue); + } + + /** + * @param val value to convert + * @param defaultValue default value to return is the conversion doesn't work or is null. + * @return BigDecimal conversion of the original value, or the defaultValue if unable + * to convert. + */ + static BigDecimal objectToBigDecimal(Object val, BigDecimal defaultValue) { + return objectToBigDecimal(val, defaultValue, true); + } + + /** + * @param val value to convert + * @param defaultValue default value to return is the conversion doesn't work or is null. + * @param exact When true, then {@link Double} and {@link Float} values will be converted exactly. + * When false, they will be converted to {@link String} values before converting to {@link BigDecimal}. + * @return BigDecimal conversion of the original value, or the defaultValue if unable + * to convert. + */ + static BigDecimal objectToBigDecimal(Object val, BigDecimal defaultValue, boolean exact) { + if (NULL.equals(val)) { + return defaultValue; + } + if (val instanceof BigDecimal){ + return (BigDecimal) val; + } + if (val instanceof BigInteger){ + return new BigDecimal((BigInteger) val); + } + if (val instanceof Double || val instanceof Float){ + if (!numberIsFinite((Number)val)) { + return defaultValue; + } + if (exact) { + return new BigDecimal(((Number)val).doubleValue()); + }else { + // use the string constructor so that we maintain "nice" values for doubles and floats + // the double constructor will translate doubles to "exact" values instead of the likely + // intended representation + return new BigDecimal(val.toString()); + } + } + if (val instanceof Long || val instanceof Integer + || val instanceof Short || val instanceof Byte){ + return new BigDecimal(((Number) val).longValue()); + } + // don't check if it's a string in case of unchecked Number subclasses + try { + return new BigDecimal(val.toString()); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * Get an optional BigInteger associated with a key, or the defaultValue if + * there is no such key or if its value is not a number. If the value is a + * string, an attempt will be made to evaluate it as a number. + * + * @param key + * A key string. + * @param defaultValue + * The default. + * @return An object which is the value. + */ + public BigInteger optBigInteger(String key, BigInteger defaultValue) { + Object val = this.opt(key); + return objectToBigInteger(val, defaultValue); + } + + /** + * @param val value to convert + * @param defaultValue default value to return is the conversion doesn't work or is null. + * @return BigInteger conversion of the original value, or the defaultValue if unable + * to convert. + */ + static BigInteger objectToBigInteger(Object val, BigInteger defaultValue) { + if (NULL.equals(val)) { + return defaultValue; + } + if (val instanceof BigInteger){ + return (BigInteger) val; + } + if (val instanceof BigDecimal){ + return ((BigDecimal) val).toBigInteger(); + } + if (val instanceof Double || val instanceof Float){ + if (!numberIsFinite((Number)val)) { + return defaultValue; + } + return new BigDecimal(((Number) val).doubleValue()).toBigInteger(); + } + if (val instanceof Long || val instanceof Integer + || val instanceof Short || val instanceof Byte){ + return BigInteger.valueOf(((Number) val).longValue()); + } + // don't check if it's a string in case of unchecked Number subclasses + try { + // the other opt functions handle implicit conversions, i.e. + // jo.put("double",1.1d); + // jo.optInt("double"); -- will return 1, not an error + // this conversion to BigDecimal then to BigInteger is to maintain + // that type cast support that may truncate the decimal. + final String valStr = val.toString(); + if(isDecimalNotation(valStr)) { + return new BigDecimal(valStr).toBigInteger(); + } + return new BigInteger(valStr); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * Get an optional double associated with a key, or NaN if there is no such + * key or if its value is not a number. If the value is a string, an attempt + * will be made to evaluate it as a number. + * + * @param key + * A string which is the key. + * @return An object which is the value. + */ + public double optDouble(String key) { + return this.optDouble(key, Double.NaN); + } + + /** + * Get an optional double associated with a key, or the defaultValue if + * there is no such key or if its value is not a number. If the value is a + * string, an attempt will be made to evaluate it as a number. + * + * @param key + * A key string. + * @param defaultValue + * The default. + * @return An object which is the value. + */ + public double optDouble(String key, double defaultValue) { + Number val = this.optNumber(key); + if (val == null) { + return defaultValue; + } + final double doubleValue = val.doubleValue(); + // if (Double.isNaN(doubleValue) || Double.isInfinite(doubleValue)) { + // return defaultValue; + // } + return doubleValue; + } + + /** + * Get the optional double value associated with an index. NaN is returned + * if there is no value for the index, or if the value is not a number and + * cannot be converted to a number. + * + * @param key + * A key string. + * @return The value. + */ + public float optFloat(String key) { + return this.optFloat(key, Float.NaN); + } + + /** + * Get the optional double value associated with an index. The defaultValue + * is returned if there is no value for the index, or if the value is not a + * number and cannot be converted to a number. + * + * @param key + * A key string. + * @param defaultValue + * The default value. + * @return The value. + */ + public float optFloat(String key, float defaultValue) { + Number val = this.optNumber(key); + if (val == null) { + return defaultValue; + } + final float floatValue = val.floatValue(); + // if (Float.isNaN(floatValue) || Float.isInfinite(floatValue)) { + // return defaultValue; + // } + return floatValue; + } + + /** + * Get an optional int value associated with a key, or zero if there is no + * such key or if the value is not a number. If the value is a string, an + * attempt will be made to evaluate it as a number. + * + * @param key + * A key string. + * @return An object which is the value. + */ + public int optInt(String key) { + return this.optInt(key, 0); + } + + /** + * Get an optional int value associated with a key, or the default if there + * is no such key or if the value is not a number. If the value is a string, + * an attempt will be made to evaluate it as a number. + * + * @param key + * A key string. + * @param defaultValue + * The default. + * @return An object which is the value. + */ + public int optInt(String key, int defaultValue) { + final Number val = this.optNumber(key, null); + if (val == null) { + return defaultValue; + } + return val.intValue(); + } + + /** + * Get an optional JSONArray associated with a key. It returns null if there + * is no such key, or if its value is not a JSONArray. + * + * @param key + * A key string. + * @return A JSONArray which is the value. + */ + public JSONArray optJSONArray(String key) { + Object o = this.opt(key); + return o instanceof JSONArray ? (JSONArray) o : null; + } + + /** + * Get an optional JSONObject associated with a key. It returns null if + * there is no such key, or if its value is not a JSONObject. + * + * @param key + * A key string. + * @return A JSONObject which is the value. + */ + public JSONObject optJSONObject(String key) { return this.optJSONObject(key, null); } + + /** + * Get an optional JSONObject associated with a key, or the default if there + * is no such key or if the value is not a JSONObject. + * + * @param key + * A key string. + * @param defaultValue + * The default. + * @return An JSONObject which is the value. + */ + public JSONObject optJSONObject(String key, JSONObject defaultValue) { + Object object = this.opt(key); + return object instanceof JSONObject ? (JSONObject) object : defaultValue; + } + + /** + * Get an optional long value associated with a key, or zero if there is no + * such key or if the value is not a number. If the value is a string, an + * attempt will be made to evaluate it as a number. + * + * @param key + * A key string. + * @return An object which is the value. + */ + public long optLong(String key) { + return this.optLong(key, 0); + } + + /** + * Get an optional long value associated with a key, or the default if there + * is no such key or if the value is not a number. If the value is a string, + * an attempt will be made to evaluate it as a number. + * + * @param key + * A key string. + * @param defaultValue + * The default. + * @return An object which is the value. + */ + public long optLong(String key, long defaultValue) { + final Number val = this.optNumber(key, null); + if (val == null) { + return defaultValue; + } + + return val.longValue(); + } + + /** + * Get an optional {@link Number} value associated with a key, or null + * if there is no such key or if the value is not a number. If the value is a string, + * an attempt will be made to evaluate it as a number ({@link BigDecimal}). This method + * would be used in cases where type coercion of the number value is unwanted. + * + * @param key + * A key string. + * @return An object which is the value. + */ + public Number optNumber(String key) { + return this.optNumber(key, null); + } + + /** + * Get an optional {@link Number} value associated with a key, or the default if there + * is no such key or if the value is not a number. If the value is a string, + * an attempt will be made to evaluate it as a number. This method + * would be used in cases where type coercion of the number value is unwanted. + * + * @param key + * A key string. + * @param defaultValue + * The default. + * @return An object which is the value. + */ + public Number optNumber(String key, Number defaultValue) { + Object val = this.opt(key); + if (NULL.equals(val)) { + return defaultValue; + } + if (val instanceof Number){ + return (Number) val; + } + + try { + return stringToNumber(val.toString()); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * Get an optional string associated with a key. It returns an empty string + * if there is no such key. If the value is not a string and is not null, + * then it is converted to a string. + * + * @param key + * A key string. + * @return A string which is the value. + */ + public String optString(String key) { + return this.optString(key, ""); + } + + /** + * Get an optional string associated with a key. It returns the defaultValue + * if there is no such key. + * + * @param key + * A key string. + * @param defaultValue + * The default. + * @return A string which is the value. + */ + public String optString(String key, String defaultValue) { + Object object = this.opt(key); + return NULL.equals(object) ? defaultValue : object.toString(); + } + + /** + * Populates the internal map of the JSONObject with the bean properties. The + * bean can not be recursive. + * + * @see JSONObject#JSONObject(Object) + * + * @param bean + * the bean + */ + private void populateMap(Object bean) { + populateMap(bean, Collections.newSetFromMap(new IdentityHashMap())); + } + + private void populateMap(Object bean, Set objectsRecord) { + Class klass = bean.getClass(); + + // If klass is a System class then set includeSuperClass to false. + + boolean includeSuperClass = klass.getClassLoader() != null; + + Method[] methods = includeSuperClass ? klass.getMethods() : klass.getDeclaredMethods(); + for (final Method method : methods) { + final int modifiers = method.getModifiers(); + if (Modifier.isPublic(modifiers) + && !Modifier.isStatic(modifiers) + && method.getParameterTypes().length == 0 + && !method.isBridge() + && method.getReturnType() != Void.TYPE + && isValidMethodName(method.getName())) { + final String key = getKeyNameFromMethod(method); + if (key != null && !key.isEmpty()) { + try { + final Object result = method.invoke(bean); + if (result != null) { + // check cyclic dependency and throw error if needed + // the wrap and populateMap combination method is + // itself DFS recursive + if (objectsRecord.contains(result)) { + throw recursivelyDefinedObjectException(key); + } + + objectsRecord.add(result); + + this.map.put(key, wrap(result, objectsRecord)); + + objectsRecord.remove(result); + + // we don't use the result anywhere outside of wrap + // if it's a resource we should be sure to close it + // after calling toString + if (result instanceof Closeable) { + try { + ((Closeable) result).close(); + } catch (IOException ignore) { + } + } + } + } catch (IllegalAccessException ignore) { + } catch (IllegalArgumentException ignore) { + } catch (InvocationTargetException ignore) { + } + } + } + } + } + + private static boolean isValidMethodName(String name) { + return !"getClass".equals(name) && !"getDeclaringClass".equals(name); + } + + private static String getKeyNameFromMethod(Method method) { + final int ignoreDepth = getAnnotationDepth(method, JSONPropertyIgnore.class); + if (ignoreDepth > 0) { + final int forcedNameDepth = getAnnotationDepth(method, JSONPropertyName.class); + if (forcedNameDepth < 0 || ignoreDepth <= forcedNameDepth) { + // the hierarchy asked to ignore, and the nearest name override + // was higher or non-existent + return null; + } + } + JSONPropertyName annotation = getAnnotation(method, JSONPropertyName.class); + if (annotation != null && annotation.value() != null && !annotation.value().isEmpty()) { + return annotation.value(); + } + String key; + final String name = method.getName(); + if (name.startsWith("get") && name.length() > 3) { + key = name.substring(3); + } else if (name.startsWith("is") && name.length() > 2) { + key = name.substring(2); + } else { + return null; + } + // if the first letter in the key is not uppercase, then skip. + // This is to maintain backwards compatibility before PR406 + // (https://github.com/stleary/JSON-java/pull/406/) + if (key.length() == 0 || Character.isLowerCase(key.charAt(0))) { + return null; + } + if (key.length() == 1) { + key = key.toLowerCase(Locale.ROOT); + } else if (!Character.isUpperCase(key.charAt(1))) { + key = key.substring(0, 1).toLowerCase(Locale.ROOT) + key.substring(1); + } + return key; + } + + /** + * Searches the class hierarchy to see if the method or it's super + * implementations and interfaces has the annotation. + * + * @param + * type of the annotation + * + * @param m + * method to check + * @param annotationClass + * annotation to look for + * @return the {@link Annotation} if the annotation exists on the current method + * or one of its super class definitions + */ + private static A getAnnotation(final Method m, final Class annotationClass) { + // if we have invalid data the result is null + if (m == null || annotationClass == null) { + return null; + } + + if (m.isAnnotationPresent(annotationClass)) { + return m.getAnnotation(annotationClass); + } + + // if we've already reached the Object class, return null; + Class c = m.getDeclaringClass(); + if (c.getSuperclass() == null) { + return null; + } + + // check directly implemented interfaces for the method being checked + for (Class i : c.getInterfaces()) { + try { + Method im = i.getMethod(m.getName(), m.getParameterTypes()); + return getAnnotation(im, annotationClass); + } catch (final SecurityException ex) { + continue; + } catch (final NoSuchMethodException ex) { + continue; + } + } + + try { + return getAnnotation( + c.getSuperclass().getMethod(m.getName(), m.getParameterTypes()), + annotationClass); + } catch (final SecurityException ex) { + return null; + } catch (final NoSuchMethodException ex) { + return null; + } + } + + /** + * Searches the class hierarchy to see if the method or it's super + * implementations and interfaces has the annotation. Returns the depth of the + * annotation in the hierarchy. + * + * @param m + * method to check + * @param annotationClass + * annotation to look for + * @return Depth of the annotation or -1 if the annotation is not on the method. + */ + private static int getAnnotationDepth(final Method m, final Class annotationClass) { + // if we have invalid data the result is -1 + if (m == null || annotationClass == null) { + return -1; + } + + if (m.isAnnotationPresent(annotationClass)) { + return 1; + } + + // if we've already reached the Object class, return -1; + Class c = m.getDeclaringClass(); + if (c.getSuperclass() == null) { + return -1; + } + + // check directly implemented interfaces for the method being checked + for (Class i : c.getInterfaces()) { + try { + Method im = i.getMethod(m.getName(), m.getParameterTypes()); + int d = getAnnotationDepth(im, annotationClass); + if (d > 0) { + // since the annotation was on the interface, add 1 + return d + 1; + } + } catch (final SecurityException ex) { + continue; + } catch (final NoSuchMethodException ex) { + continue; + } + } + + try { + int d = getAnnotationDepth( + c.getSuperclass().getMethod(m.getName(), m.getParameterTypes()), + annotationClass); + if (d > 0) { + // since the annotation was on the superclass, add 1 + return d + 1; + } + return -1; + } catch (final SecurityException ex) { + return -1; + } catch (final NoSuchMethodException ex) { + return -1; + } + } + + /** + * Put a key/boolean pair in the JSONObject. + * + * @param key + * A key string. + * @param value + * A boolean which is the value. + * @return this. + * @throws JSONException + * If the value is non-finite number. + * @throws NullPointerException + * If the key is null. + */ + public JSONObject put(String key, boolean value) throws JSONException { + return this.put(key, value ? Boolean.TRUE : Boolean.FALSE); + } + + /** + * Put a key/value pair in the JSONObject, where the value will be a + * JSONArray which is produced from a Collection. + * + * @param key + * A key string. + * @param value + * A Collection value. + * @return this. + * @throws JSONException + * If the value is non-finite number. + * @throws NullPointerException + * If the key is null. + */ + public JSONObject put(String key, Collection value) throws JSONException { + return this.put(key, new JSONArray(value)); + } + + /** + * Put a key/double pair in the JSONObject. + * + * @param key + * A key string. + * @param value + * A double which is the value. + * @return this. + * @throws JSONException + * If the value is non-finite number. + * @throws NullPointerException + * If the key is null. + */ + public JSONObject put(String key, double value) throws JSONException { + return this.put(key, Double.valueOf(value)); + } + + /** + * Put a key/float pair in the JSONObject. + * + * @param key + * A key string. + * @param value + * A float which is the value. + * @return this. + * @throws JSONException + * If the value is non-finite number. + * @throws NullPointerException + * If the key is null. + */ + public JSONObject put(String key, float value) throws JSONException { + return this.put(key, Float.valueOf(value)); + } + + /** + * Put a key/int pair in the JSONObject. + * + * @param key + * A key string. + * @param value + * An int which is the value. + * @return this. + * @throws JSONException + * If the value is non-finite number. + * @throws NullPointerException + * If the key is null. + */ + public JSONObject put(String key, int value) throws JSONException { + return this.put(key, Integer.valueOf(value)); + } + + /** + * Put a key/long pair in the JSONObject. + * + * @param key + * A key string. + * @param value + * A long which is the value. + * @return this. + * @throws JSONException + * If the value is non-finite number. + * @throws NullPointerException + * If the key is null. + */ + public JSONObject put(String key, long value) throws JSONException { + return this.put(key, Long.valueOf(value)); + } + + /** + * Put a key/value pair in the JSONObject, where the value will be a + * JSONObject which is produced from a Map. + * + * @param key + * A key string. + * @param value + * A Map value. + * @return this. + * @throws JSONException + * If the value is non-finite number. + * @throws NullPointerException + * If the key is null. + */ + public JSONObject put(String key, Map value) throws JSONException { + return this.put(key, new JSONObject(value)); + } + + /** + * Put a key/value pair in the JSONObject. If the value is null, then the + * key will be removed from the JSONObject if it is present. + * + * @param key + * A key string. + * @param value + * An object which is the value. It should be of one of these + * types: Boolean, Double, Integer, JSONArray, JSONObject, Long, + * String, or the JSONObject.NULL object. + * @return this. + * @throws JSONException + * If the value is non-finite number. + * @throws NullPointerException + * If the key is null. + */ + public JSONObject put(String key, Object value) throws JSONException { + if (key == null) { + throw new NullPointerException("Null key."); + } + if (value != null) { + testValidity(value); + this.map.put(key, value); + } else { + this.remove(key); + } + return this; + } + + /** + * Put a key/value pair in the JSONObject, but only if the key and the value + * are both non-null, and only if there is not already a member with that + * name. + * + * @param key + * key to insert into + * @param value + * value to insert + * @return this. + * @throws JSONException + * if the key is a duplicate + */ + public JSONObject putOnce(String key, Object value) throws JSONException { + if (key != null && value != null) { + if (this.opt(key) != null) { + throw new JSONException("Duplicate key \"" + key + "\""); + } + return this.put(key, value); + } + return this; + } + + /** + * Put a key/value pair in the JSONObject, but only if the key and the value + * are both non-null. + * + * @param key + * A key string. + * @param value + * An object which is the value. It should be of one of these + * types: Boolean, Double, Integer, JSONArray, JSONObject, Long, + * String, or the JSONObject.NULL object. + * @return this. + * @throws JSONException + * If the value is a non-finite number. + */ + public JSONObject putOpt(String key, Object value) throws JSONException { + if (key != null && value != null) { + return this.put(key, value); + } + return this; + } + + /** + * Creates a JSONPointer using an initialization string and tries to + * match it to an item within this JSONObject. For example, given a + * JSONObject initialized with this document: + *
+     * {
+     *     "a":{"b":"c"}
+     * }
+     * 
+ * and this JSONPointer string: + *
+     * "/a/b"
+     * 
+ * Then this method will return the String "c". + * A JSONPointerException may be thrown from code called by this method. + * + * @param jsonPointer string that can be used to create a JSONPointer + * @return the item matched by the JSONPointer, otherwise null + */ + public Object query(String jsonPointer) { + return query(new JSONPointer(jsonPointer)); + } + /** + * Uses a user initialized JSONPointer and tries to + * match it to an item within this JSONObject. For example, given a + * JSONObject initialized with this document: + *
+     * {
+     *     "a":{"b":"c"}
+     * }
+     * 
+ * and this JSONPointer: + *
+     * "/a/b"
+     * 
+ * Then this method will return the String "c". + * A JSONPointerException may be thrown from code called by this method. + * + * @param jsonPointer string that can be used to create a JSONPointer + * @return the item matched by the JSONPointer, otherwise null + */ + public Object query(JSONPointer jsonPointer) { + return jsonPointer.queryFrom(this); + } + + /** + * Queries and returns a value from this object using {@code jsonPointer}, or + * returns null if the query fails due to a missing key. + * + * @param jsonPointer the string representation of the JSON pointer + * @return the queried value or {@code null} + * @throws IllegalArgumentException if {@code jsonPointer} has invalid syntax + */ + public Object optQuery(String jsonPointer) { + return optQuery(new JSONPointer(jsonPointer)); + } + + /** + * Queries and returns a value from this object using {@code jsonPointer}, or + * returns null if the query fails due to a missing key. + * + * @param jsonPointer The JSON pointer + * @return the queried value or {@code null} + * @throws IllegalArgumentException if {@code jsonPointer} has invalid syntax + */ + public Object optQuery(JSONPointer jsonPointer) { + try { + return jsonPointer.queryFrom(this); + } catch (JSONPointerException e) { + return null; + } + } + + /** + * Produce a string in double quotes with backslash sequences in all the + * right places. A backslash will be inserted within </, producing + * <\/, allowing JSON text to be delivered in HTML. In JSON text, a + * string cannot contain a control character or an unescaped quote or + * backslash. + * + * @param string + * A String + * @return A String correctly formatted for insertion in a JSON text. + */ + public static String quote(String string) { + StringWriter sw = new StringWriter(); + synchronized (sw.getBuffer()) { + try { + return quote(string, sw).toString(); + } catch (IOException ignored) { + // will never happen - we are writing to a string writer + return ""; + } + } + } + + public static Writer quote(String string, Writer w) throws IOException { + if (string == null || string.isEmpty()) { + w.write("\"\""); + return w; + } + + char b; + char c = 0; + String hhhh; + int i; + int len = string.length(); + + w.write('"'); + for (i = 0; i < len; i += 1) { + b = c; + c = string.charAt(i); + switch (c) { + case '\\': + case '"': + w.write('\\'); + w.write(c); + break; + case '/': + if (b == '<') { + w.write('\\'); + } + w.write(c); + break; + case '\b': + w.write("\\b"); + break; + case '\t': + w.write("\\t"); + break; + case '\n': + w.write("\\n"); + break; + case '\f': + w.write("\\f"); + break; + case '\r': + w.write("\\r"); + break; + default: + if (c < ' ' || (c >= '\u0080' && c < '\u00a0') + || (c >= '\u2000' && c < '\u2100')) { + w.write("\\u"); + hhhh = Integer.toHexString(c); + w.write("0000", 0, 4 - hhhh.length()); + w.write(hhhh); + } else { + w.write(c); + } + } + } + w.write('"'); + return w; + } + + /** + * Remove a name and its value, if present. + * + * @param key + * The name to be removed. + * @return The value that was associated with the name, or null if there was + * no value. + */ + public Object remove(String key) { + return this.map.remove(key); + } + + /** + * Determine if two JSONObjects are similar. + * They must contain the same set of names which must be associated with + * similar values. + * + * @param other The other JSONObject + * @return true if they are equal + */ + public boolean similar(Object other) { + try { + if (!(other instanceof JSONObject)) { + return false; + } + if (!this.keySet().equals(((JSONObject)other).keySet())) { + return false; + } + for (final Entry entry : this.entrySet()) { + String name = entry.getKey(); + Object valueThis = entry.getValue(); + Object valueOther = ((JSONObject)other).get(name); + if(valueThis == valueOther) { + continue; + } + if(valueThis == null) { + return false; + } + if (valueThis instanceof JSONObject) { + if (!((JSONObject)valueThis).similar(valueOther)) { + return false; + } + } else if (valueThis instanceof JSONArray) { + if (!((JSONArray)valueThis).similar(valueOther)) { + return false; + } + } else if (valueThis instanceof Number && valueOther instanceof Number) { + if (!isNumberSimilar((Number)valueThis, (Number)valueOther)) { + return false; + }; + } else if (!valueThis.equals(valueOther)) { + return false; + } + } + return true; + } catch (Throwable exception) { + return false; + } + } + + /** + * Compares two numbers to see if they are similar. + * + * If either of the numbers are Double or Float instances, then they are checked to have + * a finite value. If either value is not finite (NaN or ±infinity), then this + * function will always return false. If both numbers are finite, they are first checked + * to be the same type and implement {@link Comparable}. If they do, then the actual + * {@link Comparable#compareTo(Object)} is called. If they are not the same type, or don't + * implement Comparable, then they are converted to {@link BigDecimal}s. Finally the + * BigDecimal values are compared using {@link BigDecimal#compareTo(BigDecimal)}. + * + * @param l the Left value to compare. Can not be null. + * @param r the right value to compare. Can not be null. + * @return true if the numbers are similar, false otherwise. + */ + static boolean isNumberSimilar(Number l, Number r) { + if (!numberIsFinite(l) || !numberIsFinite(r)) { + // non-finite numbers are never similar + return false; + } + + // if the classes are the same and implement Comparable + // then use the built in compare first. + if(l.getClass().equals(r.getClass()) && l instanceof Comparable) { + @SuppressWarnings({ "rawtypes", "unchecked" }) + int compareTo = ((Comparable)l).compareTo(r); + return compareTo==0; + } + + // BigDecimal should be able to handle all of our number types that we support through + // documentation. Convert to BigDecimal first, then use the Compare method to + // decide equality. + final BigDecimal lBigDecimal = objectToBigDecimal(l, null, false); + final BigDecimal rBigDecimal = objectToBigDecimal(r, null, false); + if (lBigDecimal == null || rBigDecimal == null) { + return false; + } + return lBigDecimal.compareTo(rBigDecimal) == 0; + } + + private static boolean numberIsFinite(Number n) { + if (n instanceof Double && (((Double) n).isInfinite() || ((Double) n).isNaN())) { + return false; + } else if (n instanceof Float && (((Float) n).isInfinite() || ((Float) n).isNaN())) { + return false; + } + return true; + } + + /** + * Tests if the value should be tried as a decimal. It makes no test if there are actual digits. + * + * @param val value to test + * @return true if the string is "-0" or if it contains '.', 'e', or 'E', false otherwise. + */ + protected static boolean isDecimalNotation(final String val) { + return val.indexOf('.') > -1 || val.indexOf('e') > -1 + || val.indexOf('E') > -1 || "-0".equals(val); + } + + /** + * Converts a string to a number using the narrowest possible type. Possible + * returns for this function are BigDecimal, Double, BigInteger, Long, and Integer. + * When a Double is returned, it should always be a valid Double and not NaN or +-infinity. + * + * @param val value to convert + * @return Number representation of the value. + * @throws NumberFormatException thrown if the value is not a valid number. A public + * caller should catch this and wrap it in a {@link JSONException} if applicable. + */ + protected static Number stringToNumber(final String val) throws NumberFormatException { + char initial = val.charAt(0); + if ((initial >= '0' && initial <= '9') || initial == '-') { + // decimal representation + if (isDecimalNotation(val)) { + // Use a BigDecimal all the time so we keep the original + // representation. BigDecimal doesn't support -0.0, ensure we + // keep that by forcing a decimal. + try { + BigDecimal bd = new BigDecimal(val); + if(initial == '-' && BigDecimal.ZERO.compareTo(bd)==0) { + return Double.valueOf(-0.0); + } + return bd; + } catch (NumberFormatException retryAsDouble) { + // this is to support "Hex Floats" like this: 0x1.0P-1074 + try { + Double d = Double.valueOf(val); + if(d.isNaN() || d.isInfinite()) { + throw new NumberFormatException("val ["+val+"] is not a valid number."); + } + return d; + } catch (NumberFormatException ignore) { + throw new NumberFormatException("val ["+val+"] is not a valid number."); + } + } + } + // block items like 00 01 etc. Java number parsers treat these as Octal. + if(initial == '0' && val.length() > 1) { + char at1 = val.charAt(1); + if(at1 >= '0' && at1 <= '9') { + throw new NumberFormatException("val ["+val+"] is not a valid number."); + } + } else if (initial == '-' && val.length() > 2) { + char at1 = val.charAt(1); + char at2 = val.charAt(2); + if(at1 == '0' && at2 >= '0' && at2 <= '9') { + throw new NumberFormatException("val ["+val+"] is not a valid number."); + } + } + // integer representation. + // This will narrow any values to the smallest reasonable Object representation + // (Integer, Long, or BigInteger) + + // BigInteger down conversion: We use a similar bitLength compare as + // BigInteger#intValueExact uses. Increases GC, but objects hold + // only what they need. i.e. Less runtime overhead if the value is + // long lived. + BigInteger bi = new BigInteger(val); + if(bi.bitLength() <= 31){ + return Integer.valueOf(bi.intValue()); + } + if(bi.bitLength() <= 63){ + return Long.valueOf(bi.longValue()); + } + return bi; + } + throw new NumberFormatException("val ["+val+"] is not a valid number."); + } + + /** + * Try to convert a string into a number, boolean, or null. If the string + * can't be converted, return the string. + * + * @param string + * A String. can not be null. + * @return A simple JSON value. + * @throws NullPointerException + * Thrown if the string is null. + */ + // Changes to this method must be copied to the corresponding method in + // the XML class to keep full support for Android + public static Object stringToValue(String string) { + if ("".equals(string)) { + return string; + } + + // check JSON key words true/false/null + if ("true".equalsIgnoreCase(string)) { + return Boolean.TRUE; + } + if ("false".equalsIgnoreCase(string)) { + return Boolean.FALSE; + } + if ("null".equalsIgnoreCase(string)) { + return JSONObject.NULL; + } + + /* + * If it might be a number, try converting it. If a number cannot be + * produced, then the value will just be a string. + */ + + char initial = string.charAt(0); + if ((initial >= '0' && initial <= '9') || initial == '-') { + try { + return stringToNumber(string); + } catch (Exception ignore) { + } + } + return string; + } + + /** + * Throw an exception if the object is a NaN or infinite number. + * + * @param o + * The object to test. + * @throws JSONException + * If o is a non-finite number. + */ + public static void testValidity(Object o) throws JSONException { + if (o instanceof Number && !numberIsFinite((Number) o)) { + throw new JSONException("JSON does not allow non-finite numbers."); + } + } + + /** + * Produce a JSONArray containing the values of the members of this + * JSONObject. + * + * @param names + * A JSONArray containing a list of key strings. This determines + * the sequence of the values in the result. + * @return A JSONArray of values. + * @throws JSONException + * If any of the values are non-finite numbers. + */ + public JSONArray toJSONArray(JSONArray names) throws JSONException { + if (names == null || names.isEmpty()) { + return null; + } + JSONArray ja = new JSONArray(); + for (int i = 0; i < names.length(); i += 1) { + ja.put(this.opt(names.getString(i))); + } + return ja; + } + + /** + * Make a JSON text of this JSONObject. For compactness, no whitespace is + * added. If this would not result in a syntactically correct JSON text, + * then null will be returned instead. + *

+ * Warning: This method assumes that the data structure is acyclical. + * + * + * @return a printable, displayable, portable, transmittable representation + * of the object, beginning with { (left + * brace) and ending with } (right + * brace). + */ + @Override + public String toString() { + try { + return this.toString(0); + } catch (Exception e) { + return null; + } + } + + /** + * Make a pretty-printed JSON text of this JSONObject. + * + *

If

{@code indentFactor > 0}
and the {@link JSONObject} + * has only one key, then the object will be output on a single line: + *
{@code {"key": 1}}
+ * + *

If an object has 2 or more keys, then it will be output across + * multiple lines:

{@code {
+     *  "key1": 1,
+     *  "key2": "value 2",
+     *  "key3": 3
+     * }}
+ *

+ * Warning: This method assumes that the data structure is acyclical. + * + * + * @param indentFactor + * The number of spaces to add to each level of indentation. + * @return a printable, displayable, portable, transmittable representation + * of the object, beginning with { (left + * brace) and ending with } (right + * brace). + * @throws JSONException + * If the object contains an invalid number. + */ + public String toString(int indentFactor) throws JSONException { + StringWriter w = new StringWriter(); + synchronized (w.getBuffer()) { + return this.write(w, indentFactor, 0).toString(); + } + } + + /** + * Make a JSON text of an Object value. If the object has an + * value.toJSONString() method, then that method will be used to produce the + * JSON text. The method is required to produce a strictly conforming text. + * If the object does not contain a toJSONString method (which is the most + * common case), then a text will be produced by other means. If the value + * is an array or Collection, then a JSONArray will be made from it and its + * toJSONString method will be called. If the value is a MAP, then a + * JSONObject will be made from it and its toJSONString method will be + * called. Otherwise, the value's toString method will be called, and the + * result will be quoted. + * + *

+ * Warning: This method assumes that the data structure is acyclical. + * + * @param value + * The value to be serialized. + * @return a printable, displayable, transmittable representation of the + * object, beginning with { (left + * brace) and ending with } (right + * brace). + * @throws JSONException + * If the value is or contains an invalid number. + */ + public static String valueToString(Object value) throws JSONException { + // moves the implementation to JSONWriter as: + // 1. It makes more sense to be part of the writer class + // 2. For Android support this method is not available. By implementing it in the Writer + // Android users can use the writer with the built in Android JSONObject implementation. + return JSONWriter.valueToString(value); + } + + /** + * Wrap an object, if necessary. If the object is null, return the NULL + * object. If it is an array or collection, wrap it in a JSONArray. If it is + * a map, wrap it in a JSONObject. If it is a standard property (Double, + * String, et al) then it is already wrapped. Otherwise, if it comes from + * one of the java packages, turn it into a string. And if it doesn't, try + * to wrap it in a JSONObject. If the wrapping fails, then null is returned. + * + * @param object + * The object to wrap + * @return The wrapped value + */ + public static Object wrap(Object object) { + return wrap(object, null); + } + + private static Object wrap(Object object, Set objectsRecord) { + try { + if (NULL.equals(object)) { + return NULL; + } + if (object instanceof JSONObject || object instanceof JSONArray + || NULL.equals(object) || object instanceof JSONString + || object instanceof Byte || object instanceof Character + || object instanceof Short || object instanceof Integer + || object instanceof Long || object instanceof Boolean + || object instanceof Float || object instanceof Double + || object instanceof String || object instanceof BigInteger + || object instanceof BigDecimal || object instanceof Enum) { + return object; + } + + if (object instanceof Collection) { + Collection coll = (Collection) object; + return new JSONArray(coll); + } + if (object.getClass().isArray()) { + return new JSONArray(object); + } + if (object instanceof Map) { + Map map = (Map) object; + return new JSONObject(map); + } + Package objectPackage = object.getClass().getPackage(); + String objectPackageName = objectPackage != null ? objectPackage + .getName() : ""; + if (objectPackageName.startsWith("java.") + || objectPackageName.startsWith("javax.") + || object.getClass().getClassLoader() == null) { + return object.toString(); + } + if (objectsRecord != null) { + return new JSONObject(object, objectsRecord); + } + else { + return new JSONObject(object); + } + } + catch (JSONException exception) { + throw exception; + } catch (Exception exception) { + return null; + } + } + + /** + * Write the contents of the JSONObject as JSON text to a writer. For + * compactness, no whitespace is added. + *

+ * Warning: This method assumes that the data structure is acyclical. + * + * @param writer the writer object + * @return The writer. + * @throws JSONException if a called function has an error + */ + public Writer write(Writer writer) throws JSONException { + return this.write(writer, 0, 0); + } + + static final Writer writeValue(Writer writer, Object value, + int indentFactor, int indent) throws JSONException, IOException { + if (value == null || value.equals(null)) { + writer.write("null"); + } else if (value instanceof JSONString) { + Object o; + try { + o = ((JSONString) value).toJSONString(); + } catch (Exception e) { + throw new JSONException(e); + } + writer.write(o != null ? o.toString() : quote(value.toString())); + } else if (value instanceof Number) { + // not all Numbers may match actual JSON Numbers. i.e. fractions or Imaginary + final String numberAsString = numberToString((Number) value); + if(NUMBER_PATTERN.matcher(numberAsString).matches()) { + writer.write(numberAsString); + } else { + // The Number value is not a valid JSON number. + // Instead we will quote it as a string + quote(numberAsString, writer); + } + } else if (value instanceof Boolean) { + writer.write(value.toString()); + } else if (value instanceof Enum) { + writer.write(quote(((Enum)value).name())); + } else if (value instanceof JSONObject) { + ((JSONObject) value).write(writer, indentFactor, indent); + } else if (value instanceof JSONArray) { + ((JSONArray) value).write(writer, indentFactor, indent); + } else if (value instanceof Map) { + Map map = (Map) value; + new JSONObject(map).write(writer, indentFactor, indent); + } else if (value instanceof Collection) { + Collection coll = (Collection) value; + new JSONArray(coll).write(writer, indentFactor, indent); + } else if (value.getClass().isArray()) { + new JSONArray(value).write(writer, indentFactor, indent); + } else { + quote(value.toString(), writer); + } + return writer; + } + + static final void indent(Writer writer, int indent) throws IOException { + for (int i = 0; i < indent; i += 1) { + writer.write(' '); + } + } + + /** + * Write the contents of the JSONObject as JSON text to a writer. + * + *

If

{@code indentFactor > 0}
and the {@link JSONObject} + * has only one key, then the object will be output on a single line: + *
{@code {"key": 1}}
+ * + *

If an object has 2 or more keys, then it will be output across + * multiple lines:

{@code {
+     *  "key1": 1,
+     *  "key2": "value 2",
+     *  "key3": 3
+     * }}
+ *

+ * Warning: This method assumes that the data structure is acyclical. + * + * + * @param writer + * Writes the serialized JSON + * @param indentFactor + * The number of spaces to add to each level of indentation. + * @param indent + * The indentation of the top level. + * @return The writer. + * @throws JSONException if a called function has an error or a write error + * occurs + */ + public Writer write(Writer writer, int indentFactor, int indent) + throws JSONException { + try { + boolean needsComma = false; + final int length = this.length(); + writer.write('{'); + + if (length == 1) { + final Entry entry = this.entrySet().iterator().next(); + final String key = entry.getKey(); + writer.write(quote(key)); + writer.write(':'); + if (indentFactor > 0) { + writer.write(' '); + } + try{ + writeValue(writer, entry.getValue(), indentFactor, indent); + } catch (Exception e) { + throw new JSONException("Unable to write JSONObject value for key: " + key, e); + } + } else if (length != 0) { + final int newIndent = indent + indentFactor; + for (final Entry entry : this.entrySet()) { + if (needsComma) { + writer.write(','); + } + if (indentFactor > 0) { + writer.write('\n'); + } + indent(writer, newIndent); + final String key = entry.getKey(); + writer.write(quote(key)); + writer.write(':'); + if (indentFactor > 0) { + writer.write(' '); + } + try { + writeValue(writer, entry.getValue(), indentFactor, newIndent); + } catch (Exception e) { + throw new JSONException("Unable to write JSONObject value for key: " + key, e); + } + needsComma = true; + } + if (indentFactor > 0) { + writer.write('\n'); + } + indent(writer, indent); + } + writer.write('}'); + return writer; + } catch (IOException exception) { + throw new JSONException(exception); + } + } + + /** + * Returns a java.util.Map containing all of the entries in this object. + * If an entry in the object is a JSONArray or JSONObject it will also + * be converted. + *

+ * Warning: This method assumes that the data structure is acyclical. + * + * @return a java.util.Map containing the entries of this object + */ + public Map toMap() { + Map results = new HashMap(); + for (Entry entry : this.entrySet()) { + Object value; + if (entry.getValue() == null || NULL.equals(entry.getValue())) { + value = null; + } else if (entry.getValue() instanceof JSONObject) { + value = ((JSONObject) entry.getValue()).toMap(); + } else if (entry.getValue() instanceof JSONArray) { + value = ((JSONArray) entry.getValue()).toList(); + } else { + value = entry.getValue(); + } + results.put(entry.getKey(), value); + } + return results; + } + + /** + * Create a new JSONException in a common format for incorrect conversions. + * @param key name of the key + * @param valueType the type of value being coerced to + * @param cause optional cause of the coercion failure + * @return JSONException that can be thrown. + */ + private static JSONException wrongValueFormatException( + String key, + String valueType, + Throwable cause) { + return new JSONException( + "JSONObject[" + quote(key) + "] is not a " + valueType + "." + , cause); + } + + /** + * Create a new JSONException in a common format for incorrect conversions. + * @param key name of the key + * @param valueType the type of value being coerced to + * @param cause optional cause of the coercion failure + * @return JSONException that can be thrown. + */ + private static JSONException wrongValueFormatException( + String key, + String valueType, + Object value, + Throwable cause) { + return new JSONException( + "JSONObject[" + quote(key) + "] is not a " + valueType + " (" + value + ")." + , cause); + } + + /** + * Create a new JSONException in a common format for recursive object definition. + * @param key name of the key + * @return JSONException that can be thrown. + */ + private static JSONException recursivelyDefinedObjectException(String key) { + return new JSONException( + "JavaBean object contains recursively defined member variable of key " + quote(key) + ); + } +} \ No newline at end of file diff --git a/src/net/lax1dude/eaglercraft/JSONPointer.java b/src/net/lax1dude/eaglercraft/JSONPointer.java new file mode 100644 index 0000000..5e3c658 --- /dev/null +++ b/src/net/lax1dude/eaglercraft/JSONPointer.java @@ -0,0 +1,295 @@ +package net.lax1dude.eaglercraft; + +import static java.lang.String.format; + +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/* +Copyright (c) 2002 JSON.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +The Software shall be used for Good, not Evil. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +/** + * A JSON Pointer is a simple query language defined for JSON documents by + * RFC 6901. + * + * In a nutshell, JSONPointer allows the user to navigate into a JSON document + * using strings, and retrieve targeted objects, like a simple form of XPATH. + * Path segments are separated by the '/' char, which signifies the root of + * the document when it appears as the first char of the string. Array + * elements are navigated using ordinals, counting from 0. JSONPointer strings + * may be extended to any arbitrary number of segments. If the navigation + * is successful, the matched item is returned. A matched item may be a + * JSONObject, a JSONArray, or a JSON value. If the JSONPointer string building + * fails, an appropriate exception is thrown. If the navigation fails to find + * a match, a JSONPointerException is thrown. + * + * @author JSON.org + * @version 2016-05-14 + */ +public class JSONPointer { + + // used for URL encoding and decoding + private static final String ENCODING = "utf-8"; + + /** + * This class allows the user to build a JSONPointer in steps, using + * exactly one segment in each step. + */ + public static class Builder { + + // Segments for the eventual JSONPointer string + private final List refTokens = new ArrayList(); + + /** + * Creates a {@code JSONPointer} instance using the tokens previously set using the + * {@link #append(String)} method calls. + * @return a JSONPointer object + */ + public JSONPointer build() { + return new JSONPointer(this.refTokens); + } + + /** + * Adds an arbitrary token to the list of reference tokens. It can be any non-null value. + * + * Unlike in the case of JSON string or URI fragment representation of JSON pointers, the + * argument of this method MUST NOT be escaped. If you want to query the property called + * {@code "a~b"} then you should simply pass the {@code "a~b"} string as-is, there is no + * need to escape it as {@code "a~0b"}. + * + * @param token the new token to be appended to the list + * @return {@code this} + * @throws NullPointerException if {@code token} is null + */ + public Builder append(String token) { + if (token == null) { + throw new NullPointerException("token cannot be null"); + } + this.refTokens.add(token); + return this; + } + + /** + * Adds an integer to the reference token list. Although not necessarily, mostly this token will + * denote an array index. + * + * @param arrayIndex the array index to be added to the token list + * @return {@code this} + */ + public Builder append(int arrayIndex) { + this.refTokens.add(String.valueOf(arrayIndex)); + return this; + } + } + + /** + * Static factory method for {@link Builder}. Example usage: + * + *


+     * JSONPointer pointer = JSONPointer.builder()
+     *       .append("obj")
+     *       .append("other~key").append("another/key")
+     *       .append("\"")
+     *       .append(0)
+     *       .build();
+     * 
+ * + * @return a builder instance which can be used to construct a {@code JSONPointer} instance by chained + * {@link Builder#append(String)} calls. + */ + public static Builder builder() { + return new Builder(); + } + + // Segments for the JSONPointer string + private final List refTokens; + + /** + * Pre-parses and initializes a new {@code JSONPointer} instance. If you want to + * evaluate the same JSON Pointer on different JSON documents then it is recommended + * to keep the {@code JSONPointer} instances due to performance considerations. + * + * @param pointer the JSON String or URI Fragment representation of the JSON pointer. + * @throws IllegalArgumentException if {@code pointer} is not a valid JSON pointer + */ + public JSONPointer(final String pointer) { + if (pointer == null) { + throw new NullPointerException("pointer cannot be null"); + } + if (pointer.isEmpty() || pointer.equals("#")) { + this.refTokens = Collections.emptyList(); + return; + } + String refs; + if (pointer.startsWith("#/")) { + refs = pointer.substring(2); + try { + refs = URLDecoder.decode(refs, ENCODING); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } else if (pointer.startsWith("/")) { + refs = pointer.substring(1); + } else { + throw new IllegalArgumentException("a JSON pointer should start with '/' or '#/'"); + } + this.refTokens = new ArrayList(); + int slashIdx = -1; + int prevSlashIdx = 0; + do { + prevSlashIdx = slashIdx + 1; + slashIdx = refs.indexOf('/', prevSlashIdx); + if(prevSlashIdx == slashIdx || prevSlashIdx == refs.length()) { + // found 2 slashes in a row ( obj//next ) + // or single slash at the end of a string ( obj/test/ ) + this.refTokens.add(""); + } else if (slashIdx >= 0) { + final String token = refs.substring(prevSlashIdx, slashIdx); + this.refTokens.add(unescape(token)); + } else { + // last item after separator, or no separator at all. + final String token = refs.substring(prevSlashIdx); + this.refTokens.add(unescape(token)); + } + } while (slashIdx >= 0); + // using split does not take into account consecutive separators or "ending nulls" + //for (String token : refs.split("/")) { + // this.refTokens.add(unescape(token)); + //} + } + + public JSONPointer(List refTokens) { + this.refTokens = new ArrayList(refTokens); + } + + /** + * @see rfc6901 section 3 + */ + private static String unescape(String token) { + return token.replace("~1", "/").replace("~0", "~"); + } + + /** + * Evaluates this JSON Pointer on the given {@code document}. The {@code document} + * is usually a {@link JSONObject} or a {@link JSONArray} instance, but the empty + * JSON Pointer ({@code ""}) can be evaluated on any JSON values and in such case the + * returned value will be {@code document} itself. + * + * @param document the JSON document which should be the subject of querying. + * @return the result of the evaluation + * @throws JSONPointerException if an error occurs during evaluation + */ + public Object queryFrom(Object document) throws JSONPointerException { + if (this.refTokens.isEmpty()) { + return document; + } + Object current = document; + for (String token : this.refTokens) { + if (current instanceof JSONObject) { + current = ((JSONObject) current).opt(unescape(token)); + } else if (current instanceof JSONArray) { + current = readByIndexToken(current, token); + } else { + throw new JSONPointerException(format( + "value [%s] is not an array or object therefore its key %s cannot be resolved", current, + token)); + } + } + return current; + } + + /** + * Matches a JSONArray element by ordinal position + * @param current the JSONArray to be evaluated + * @param indexToken the array index in string form + * @return the matched object. If no matching item is found a + * @throws JSONPointerException is thrown if the index is out of bounds + */ + private static Object readByIndexToken(Object current, String indexToken) throws JSONPointerException { + try { + int index = Integer.parseInt(indexToken); + JSONArray currentArr = (JSONArray) current; + if (index >= currentArr.length()) { + throw new JSONPointerException(format("index %s is out of bounds - the array has %d elements", indexToken, + Integer.valueOf(currentArr.length()))); + } + try { + return currentArr.get(index); + } catch (JSONException e) { + throw new JSONPointerException("Error reading value at index position " + index, e); + } + } catch (NumberFormatException e) { + throw new JSONPointerException(format("%s is not an array index", indexToken), e); + } + } + + /** + * Returns a string representing the JSONPointer path value using string + * representation + */ + @Override + public String toString() { + StringBuilder rval = new StringBuilder(""); + for (String token: this.refTokens) { + rval.append('/').append(escape(token)); + } + return rval.toString(); + } + + /** + * Escapes path segment values to an unambiguous form. + * The escape char to be inserted is '~'. The chars to be escaped + * are ~, which maps to ~0, and /, which maps to ~1. + * @param token the JSONPointer segment value to be escaped + * @return the escaped value for the token + * + * @see rfc6901 section 3 + */ + private static String escape(String token) { + return token.replace("~", "~0") + .replace("/", "~1"); + } + + /** + * Returns a string representing the JSONPointer path value using URI + * fragment identifier representation + * @return a uri fragment string + */ + public String toURIFragment() { + try { + StringBuilder rval = new StringBuilder("#"); + for (String token : this.refTokens) { + rval.append('/').append(URLEncoder.encode(token, ENCODING)); + } + return rval.toString(); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } + +} \ No newline at end of file diff --git a/src/net/lax1dude/eaglercraft/JSONPointerException.java b/src/net/lax1dude/eaglercraft/JSONPointerException.java new file mode 100644 index 0000000..66a2b29 --- /dev/null +++ b/src/net/lax1dude/eaglercraft/JSONPointerException.java @@ -0,0 +1,45 @@ +package net.lax1dude.eaglercraft; + +/* +Copyright (c) 2002 JSON.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +The Software shall be used for Good, not Evil. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +/** + * The JSONPointerException is thrown by {@link JSONPointer} if an error occurs + * during evaluating a pointer. + * + * @author JSON.org + * @version 2016-05-13 + */ +public class JSONPointerException extends JSONException { + private static final long serialVersionUID = 8872944667561856751L; + + public JSONPointerException(String message) { + super(message); + } + + public JSONPointerException(String message, Throwable cause) { + super(message, cause); + } + +} \ No newline at end of file diff --git a/src/net/lax1dude/eaglercraft/JSONPropertyIgnore.java b/src/net/lax1dude/eaglercraft/JSONPropertyIgnore.java new file mode 100644 index 0000000..0eb1350 --- /dev/null +++ b/src/net/lax1dude/eaglercraft/JSONPropertyIgnore.java @@ -0,0 +1,43 @@ +package net.lax1dude.eaglercraft; + +/* +Copyright (c) 2018 JSON.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +The Software shall be used for Good, not Evil. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Documented +@Retention(RUNTIME) +@Target({METHOD}) +/** + * Use this annotation on a getter method to override the Bean name + * parser for Bean -> JSONObject mapping. If this annotation is + * present at any level in the class hierarchy, then the method will + * not be serialized from the bean into the JSONObject. + */ +public @interface JSONPropertyIgnore { } \ No newline at end of file diff --git a/src/net/lax1dude/eaglercraft/JSONPropertyName.java b/src/net/lax1dude/eaglercraft/JSONPropertyName.java new file mode 100644 index 0000000..94338e7 --- /dev/null +++ b/src/net/lax1dude/eaglercraft/JSONPropertyName.java @@ -0,0 +1,47 @@ +package net.lax1dude.eaglercraft; + +/* +Copyright (c) 2018 JSON.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +The Software shall be used for Good, not Evil. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Documented +@Retention(RUNTIME) +@Target({METHOD}) +/** + * Use this annotation on a getter method to override the Bean name + * parser for Bean -> JSONObject mapping. A value set to empty string "" + * will have the Bean parser fall back to the default field name processing. + */ +public @interface JSONPropertyName { + /** + * @return The name of the property as to be used in the JSON Object. + */ + String value(); +} \ No newline at end of file diff --git a/src/net/lax1dude/eaglercraft/JSONString.java b/src/net/lax1dude/eaglercraft/JSONString.java new file mode 100644 index 0000000..85fa355 --- /dev/null +++ b/src/net/lax1dude/eaglercraft/JSONString.java @@ -0,0 +1,43 @@ +package net.lax1dude.eaglercraft; + +/* +Copyright (c) 2002 JSON.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +The Software shall be used for Good, not Evil. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + */ + +/** + * The JSONString interface allows a toJSONString() + * method so that a class can change the behavior of + * JSONObject.toString(), JSONArray.toString(), + * and JSONWriter.value(Object). The + * toJSONString method will be used instead of the default behavior + * of using the Object's toString() method and quoting the result. + */ +public interface JSONString { + /** + * The toJSONString method allows a class to produce its own JSON + * serialization. + * + * @return A strictly syntactically correct JSON text. + */ + public String toJSONString(); +} \ No newline at end of file diff --git a/src/net/lax1dude/eaglercraft/JSONTokener.java b/src/net/lax1dude/eaglercraft/JSONTokener.java new file mode 100644 index 0000000..534c3f4 --- /dev/null +++ b/src/net/lax1dude/eaglercraft/JSONTokener.java @@ -0,0 +1,545 @@ +package net.lax1dude.eaglercraft; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.StringReader; + +/* +Copyright (c) 2002 JSON.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +The Software shall be used for Good, not Evil. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + */ + +/** + * A JSONTokener takes a source string and extracts characters and tokens from + * it. It is used by the JSONObject and JSONArray constructors to parse + * JSON source strings. + * @author JSON.org + * @version 2014-05-03 + */ +public class JSONTokener { + /** current read character position on the current line. */ + private long character; + /** flag to indicate if the end of the input has been found. */ + private boolean eof; + /** current read index of the input. */ + private long index; + /** current line of the input. */ + private long line; + /** previous character read from the input. */ + private char previous; + /** Reader for the input. */ + private final Reader reader; + /** flag to indicate that a previous character was requested. */ + private boolean usePrevious; + /** the number of characters read in the previous line. */ + private long characterPreviousLine; + + + /** + * Construct a JSONTokener from a Reader. The caller must close the Reader. + * + * @param reader A reader. + */ + public JSONTokener(Reader reader) { + this.reader = reader.markSupported() + ? reader + : new BufferedReader(reader); + this.eof = false; + this.usePrevious = false; + this.previous = 0; + this.index = 0; + this.character = 1; + this.characterPreviousLine = 0; + this.line = 1; + } + + + /** + * Construct a JSONTokener from an InputStream. The caller must close the input stream. + * @param inputStream The source. + */ + public JSONTokener(InputStream inputStream) { + this(new InputStreamReader(inputStream)); + } + + + /** + * Construct a JSONTokener from a string. + * + * @param s A source string. + */ + public JSONTokener(String s) { + this(new StringReader(s)); + } + + + /** + * Back up one character. This provides a sort of lookahead capability, + * so that you can test for a digit or letter before attempting to parse + * the next number or identifier. + * @throws JSONException Thrown if trying to step back more than 1 step + * or if already at the start of the string + */ + public void back() throws JSONException { + if (this.usePrevious || this.index <= 0) { + throw new JSONException("Stepping back two steps is not supported"); + } + this.decrementIndexes(); + this.usePrevious = true; + this.eof = false; + } + + /** + * Decrements the indexes for the {@link #back()} method based on the previous character read. + */ + private void decrementIndexes() { + this.index--; + if(this.previous=='\r' || this.previous == '\n') { + this.line--; + this.character=this.characterPreviousLine ; + } else if(this.character > 0){ + this.character--; + } + } + + /** + * Get the hex value of a character (base16). + * @param c A character between '0' and '9' or between 'A' and 'F' or + * between 'a' and 'f'. + * @return An int between 0 and 15, or -1 if c was not a hex digit. + */ + public static int dehexchar(char c) { + if (c >= '0' && c <= '9') { + return c - '0'; + } + if (c >= 'A' && c <= 'F') { + return c - ('A' - 10); + } + if (c >= 'a' && c <= 'f') { + return c - ('a' - 10); + } + return -1; + } + + /** + * Checks if the end of the input has been reached. + * + * @return true if at the end of the file and we didn't step back + */ + public boolean end() { + return this.eof && !this.usePrevious; + } + + + /** + * Determine if the source string still contains characters that next() + * can consume. + * @return true if not yet at the end of the source. + * @throws JSONException thrown if there is an error stepping forward + * or backward while checking for more data. + */ + public boolean more() throws JSONException { + if(this.usePrevious) { + return true; + } + try { + this.reader.mark(1); + } catch (IOException e) { + throw new JSONException("Unable to preserve stream position", e); + } + try { + // -1 is EOF, but next() can not consume the null character '\0' + if(this.reader.read() <= 0) { + this.eof = true; + return false; + } + this.reader.reset(); + } catch (IOException e) { + throw new JSONException("Unable to read the next character from the stream", e); + } + return true; + } + + + /** + * Get the next character in the source string. + * + * @return The next character, or 0 if past the end of the source string. + * @throws JSONException Thrown if there is an error reading the source string. + */ + public char next() throws JSONException { + int c; + if (this.usePrevious) { + this.usePrevious = false; + c = this.previous; + } else { + try { + c = this.reader.read(); + } catch (IOException exception) { + throw new JSONException(exception); + } + } + if (c <= 0) { // End of stream + this.eof = true; + return 0; + } + this.incrementIndexes(c); + this.previous = (char) c; + return this.previous; + } + + /** + * Get the last character read from the input or '\0' if nothing has been read yet. + * @return the last character read from the input. + */ + protected char getPrevious() { return this.previous;} + + /** + * Increments the internal indexes according to the previous character + * read and the character passed as the current character. + * @param c the current character read. + */ + private void incrementIndexes(int c) { + if(c > 0) { + this.index++; + if(c=='\r') { + this.line++; + this.characterPreviousLine = this.character; + this.character=0; + }else if (c=='\n') { + if(this.previous != '\r') { + this.line++; + this.characterPreviousLine = this.character; + } + this.character=0; + } else { + this.character++; + } + } + } + + /** + * Consume the next character, and check that it matches a specified + * character. + * @param c The character to match. + * @return The character. + * @throws JSONException if the character does not match. + */ + public char next(char c) throws JSONException { + char n = this.next(); + if (n != c) { + if(n > 0) { + throw this.syntaxError("Expected '" + c + "' and instead saw '" + + n + "'"); + } + throw this.syntaxError("Expected '" + c + "' and instead saw ''"); + } + return n; + } + + + /** + * Get the next n characters. + * + * @param n The number of characters to take. + * @return A string of n characters. + * @throws JSONException + * Substring bounds error if there are not + * n characters remaining in the source string. + */ + public String next(int n) throws JSONException { + if (n == 0) { + return ""; + } + + char[] chars = new char[n]; + int pos = 0; + + while (pos < n) { + chars[pos] = this.next(); + if (this.end()) { + throw this.syntaxError("Substring bounds error"); + } + pos += 1; + } + return new String(chars); + } + + + /** + * Get the next char in the string, skipping whitespace. + * @throws JSONException Thrown if there is an error reading the source string. + * @return A character, or 0 if there are no more characters. + */ + public char nextClean() throws JSONException { + for (;;) { + char c = this.next(); + if (c == 0 || c > ' ') { + return c; + } + } + } + + + /** + * Return the characters up to the next close quote character. + * Backslash processing is done. The formal JSON format does not + * allow strings in single quotes, but an implementation is allowed to + * accept them. + * @param quote The quoting character, either + * " (double quote) or + * ' (single quote). + * @return A String. + * @throws JSONException Unterminated string. + */ + public String nextString(char quote) throws JSONException { + char c; + StringBuilder sb = new StringBuilder(); + for (;;) { + c = this.next(); + switch (c) { + case 0: + case '\n': + case '\r': + throw this.syntaxError("Unterminated string"); + case '\\': + c = this.next(); + switch (c) { + case 'b': + sb.append('\b'); + break; + case 't': + sb.append('\t'); + break; + case 'n': + sb.append('\n'); + break; + case 'f': + sb.append('\f'); + break; + case 'r': + sb.append('\r'); + break; + case 'u': + try { + sb.append((char)Integer.parseInt(this.next(4), 16)); + } catch (NumberFormatException e) { + throw this.syntaxError("Illegal escape.", e); + } + break; + case '"': + case '\'': + case '\\': + case '/': + sb.append(c); + break; + default: + throw this.syntaxError("Illegal escape."); + } + break; + default: + if (c == quote) { + return sb.toString(); + } + sb.append(c); + } + } + } + + + /** + * Get the text up but not including the specified character or the + * end of line, whichever comes first. + * @param delimiter A delimiter character. + * @return A string. + * @throws JSONException Thrown if there is an error while searching + * for the delimiter + */ + public String nextTo(char delimiter) throws JSONException { + StringBuilder sb = new StringBuilder(); + for (;;) { + char c = this.next(); + if (c == delimiter || c == 0 || c == '\n' || c == '\r') { + if (c != 0) { + this.back(); + } + return sb.toString().trim(); + } + sb.append(c); + } + } + + + /** + * Get the text up but not including one of the specified delimiter + * characters or the end of line, whichever comes first. + * @param delimiters A set of delimiter characters. + * @return A string, trimmed. + * @throws JSONException Thrown if there is an error while searching + * for the delimiter + */ + public String nextTo(String delimiters) throws JSONException { + char c; + StringBuilder sb = new StringBuilder(); + for (;;) { + c = this.next(); + if (delimiters.indexOf(c) >= 0 || c == 0 || + c == '\n' || c == '\r') { + if (c != 0) { + this.back(); + } + return sb.toString().trim(); + } + sb.append(c); + } + } + + + /** + * Get the next value. The value can be a Boolean, Double, Integer, + * JSONArray, JSONObject, Long, or String, or the JSONObject.NULL object. + * @throws JSONException If syntax error. + * + * @return An object. + */ + public Object nextValue() throws JSONException { + char c = this.nextClean(); + String string; + + switch (c) { + case '"': + case '\'': + return this.nextString(c); + case '{': + this.back(); + try { + return new JSONObject(this); + } catch (StackOverflowError e) { + throw new JSONException("JSON Array or Object depth too large to process.", e); + } + case '[': + this.back(); + try { + return new JSONArray(this); + } catch (StackOverflowError e) { + throw new JSONException("JSON Array or Object depth too large to process.", e); + } + } + + /* + * Handle unquoted text. This could be the values true, false, or + * null, or it can be a number. An implementation (such as this one) + * is allowed to also accept non-standard forms. + * + * Accumulate characters until we reach the end of the text or a + * formatting character. + */ + + StringBuilder sb = new StringBuilder(); + while (c >= ' ' && ",:]}/\\\"[{;=#".indexOf(c) < 0) { + sb.append(c); + c = this.next(); + } + if (!this.eof) { + this.back(); + } + + string = sb.toString().trim(); + if ("".equals(string)) { + throw this.syntaxError("Missing value"); + } + return JSONObject.stringToValue(string); + } + + + /** + * Skip characters until the next character is the requested character. + * If the requested character is not found, no characters are skipped. + * @param to A character to skip to. + * @return The requested character, or zero if the requested character + * is not found. + * @throws JSONException Thrown if there is an error while searching + * for the to character + */ + public char skipTo(char to) throws JSONException { + char c; + try { + long startIndex = this.index; + long startCharacter = this.character; + long startLine = this.line; + this.reader.mark(1000000); + do { + c = this.next(); + if (c == 0) { + // in some readers, reset() may throw an exception if + // the remaining portion of the input is greater than + // the mark size (1,000,000 above). + this.reader.reset(); + this.index = startIndex; + this.character = startCharacter; + this.line = startLine; + return 0; + } + } while (c != to); + this.reader.mark(1); + } catch (IOException exception) { + throw new JSONException(exception); + } + this.back(); + return c; + } + + /** + * Make a JSONException to signal a syntax error. + * + * @param message The error message. + * @return A JSONException object, suitable for throwing + */ + public JSONException syntaxError(String message) { + return new JSONException(message + this.toString()); + } + + /** + * Make a JSONException to signal a syntax error. + * + * @param message The error message. + * @param causedBy The throwable that caused the error. + * @return A JSONException object, suitable for throwing + */ + public JSONException syntaxError(String message, Throwable causedBy) { + return new JSONException(message + this.toString(), causedBy); + } + + /** + * Make a printable string of this JSONTokener. + * + * @return " at {index} [character {character} line {line}]" + */ + @Override + public String toString() { + return " at " + this.index + " [character " + this.character + " line " + + this.line + "]"; + } +} \ No newline at end of file diff --git a/src/net/lax1dude/eaglercraft/JSONWriter.java b/src/net/lax1dude/eaglercraft/JSONWriter.java new file mode 100644 index 0000000..d24d31b --- /dev/null +++ b/src/net/lax1dude/eaglercraft/JSONWriter.java @@ -0,0 +1,414 @@ +package net.lax1dude.eaglercraft; + +import java.io.IOException; +import java.util.Collection; +import java.util.Map; + +/* +Copyright (c) 2006 JSON.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +The Software shall be used for Good, not Evil. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +/** + * JSONWriter provides a quick and convenient way of producing JSON text. + * The texts produced strictly conform to JSON syntax rules. No whitespace is + * added, so the results are ready for transmission or storage. Each instance of + * JSONWriter can produce one JSON text. + *

+ * A JSONWriter instance provides a value method for appending + * values to the + * text, and a key + * method for adding keys before values in objects. There are array + * and endArray methods that make and bound array values, and + * object and endObject methods which make and bound + * object values. All of these methods return the JSONWriter instance, + * permitting a cascade style. For example,

+ * new JSONWriter(myWriter)
+ *     .object()
+ *         .key("JSON")
+ *         .value("Hello, World!")
+ *     .endObject();
which writes
+ * {"JSON":"Hello, World!"}
+ *

+ * The first method called must be array or object. + * There are no methods for adding commas or colons. JSONWriter adds them for + * you. Objects and arrays can be nested up to 200 levels deep. + *

+ * This can sometimes be easier than using a JSONObject to build a string. + * @author JSON.org + * @version 2016-08-08 + */ +public class JSONWriter { + private static final int maxdepth = 200; + + /** + * The comma flag determines if a comma should be output before the next + * value. + */ + private boolean comma; + + /** + * The current mode. Values: + * 'a' (array), + * 'd' (done), + * 'i' (initial), + * 'k' (key), + * 'o' (object). + */ + protected char mode; + + /** + * The object/array stack. + */ + private final JSONObject stack[]; + + /** + * The stack top index. A value of 0 indicates that the stack is empty. + */ + private int top; + + /** + * The writer that will receive the output. + */ + protected Appendable writer; + + /** + * Make a fresh JSONWriter. It can be used to build one JSON text. + * @param w an appendable object + */ + public JSONWriter(Appendable w) { + this.comma = false; + this.mode = 'i'; + this.stack = new JSONObject[maxdepth]; + this.top = 0; + this.writer = w; + } + + /** + * Append a value. + * @param string A string value. + * @return this + * @throws JSONException If the value is out of sequence. + */ + private JSONWriter append(String string) throws JSONException { + if (string == null) { + throw new JSONException("Null pointer"); + } + if (this.mode == 'o' || this.mode == 'a') { + try { + if (this.comma && this.mode == 'a') { + this.writer.append(','); + } + this.writer.append(string); + } catch (IOException e) { + // Android as of API 25 does not support this exception constructor + // however we won't worry about it. If an exception is happening here + // it will just throw a "Method not found" exception instead. + throw new JSONException(e); + } + if (this.mode == 'o') { + this.mode = 'k'; + } + this.comma = true; + return this; + } + throw new JSONException("Value out of sequence."); + } + + /** + * Begin appending a new array. All values until the balancing + * endArray will be appended to this array. The + * endArray method must be called to mark the array's end. + * @return this + * @throws JSONException If the nesting is too deep, or if the object is + * started in the wrong place (for example as a key or after the end of the + * outermost array or object). + */ + public JSONWriter array() throws JSONException { + if (this.mode == 'i' || this.mode == 'o' || this.mode == 'a') { + this.push(null); + this.append("["); + this.comma = false; + return this; + } + throw new JSONException("Misplaced array."); + } + + /** + * End something. + * @param m Mode + * @param c Closing character + * @return this + * @throws JSONException If unbalanced. + */ + private JSONWriter end(char m, char c) throws JSONException { + if (this.mode != m) { + throw new JSONException(m == 'a' + ? "Misplaced endArray." + : "Misplaced endObject."); + } + this.pop(m); + try { + this.writer.append(c); + } catch (IOException e) { + // Android as of API 25 does not support this exception constructor + // however we won't worry about it. If an exception is happening here + // it will just throw a "Method not found" exception instead. + throw new JSONException(e); + } + this.comma = true; + return this; + } + + /** + * End an array. This method most be called to balance calls to + * array. + * @return this + * @throws JSONException If incorrectly nested. + */ + public JSONWriter endArray() throws JSONException { + return this.end('a', ']'); + } + + /** + * End an object. This method most be called to balance calls to + * object. + * @return this + * @throws JSONException If incorrectly nested. + */ + public JSONWriter endObject() throws JSONException { + return this.end('k', '}'); + } + + /** + * Append a key. The key will be associated with the next value. In an + * object, every value must be preceded by a key. + * @param string A key string. + * @return this + * @throws JSONException If the key is out of place. For example, keys + * do not belong in arrays or if the key is null. + */ + public JSONWriter key(String string) throws JSONException { + if (string == null) { + throw new JSONException("Null key."); + } + if (this.mode == 'k') { + try { + JSONObject topObject = this.stack[this.top - 1]; + // don't use the built in putOnce method to maintain Android support + if(topObject.has(string)) { + throw new JSONException("Duplicate key \"" + string + "\""); + } + topObject.put(string, true); + if (this.comma) { + this.writer.append(','); + } + this.writer.append(JSONObject.quote(string)); + this.writer.append(':'); + this.comma = false; + this.mode = 'o'; + return this; + } catch (IOException e) { + // Android as of API 25 does not support this exception constructor + // however we won't worry about it. If an exception is happening here + // it will just throw a "Method not found" exception instead. + throw new JSONException(e); + } + } + throw new JSONException("Misplaced key."); + } + + + /** + * Begin appending a new object. All keys and values until the balancing + * endObject will be appended to this object. The + * endObject method must be called to mark the object's end. + * @return this + * @throws JSONException If the nesting is too deep, or if the object is + * started in the wrong place (for example as a key or after the end of the + * outermost array or object). + */ + public JSONWriter object() throws JSONException { + if (this.mode == 'i') { + this.mode = 'o'; + } + if (this.mode == 'o' || this.mode == 'a') { + this.append("{"); + this.push(new JSONObject()); + this.comma = false; + return this; + } + throw new JSONException("Misplaced object."); + + } + + + /** + * Pop an array or object scope. + * @param c The scope to close. + * @throws JSONException If nesting is wrong. + */ + private void pop(char c) throws JSONException { + if (this.top <= 0) { + throw new JSONException("Nesting error."); + } + char m = this.stack[this.top - 1] == null ? 'a' : 'k'; + if (m != c) { + throw new JSONException("Nesting error."); + } + this.top -= 1; + this.mode = this.top == 0 + ? 'd' + : this.stack[this.top - 1] == null + ? 'a' + : 'k'; + } + + /** + * Push an array or object scope. + * @param jo The scope to open. + * @throws JSONException If nesting is too deep. + */ + private void push(JSONObject jo) throws JSONException { + if (this.top >= maxdepth) { + throw new JSONException("Nesting too deep."); + } + this.stack[this.top] = jo; + this.mode = jo == null ? 'a' : 'k'; + this.top += 1; + } + + /** + * Make a JSON text of an Object value. If the object has an + * value.toJSONString() method, then that method will be used to produce the + * JSON text. The method is required to produce a strictly conforming text. + * If the object does not contain a toJSONString method (which is the most + * common case), then a text will be produced by other means. If the value + * is an array or Collection, then a JSONArray will be made from it and its + * toJSONString method will be called. If the value is a MAP, then a + * JSONObject will be made from it and its toJSONString method will be + * called. Otherwise, the value's toString method will be called, and the + * result will be quoted. + * + *

+ * Warning: This method assumes that the data structure is acyclical. + * + * @param value + * The value to be serialized. + * @return a printable, displayable, transmittable representation of the + * object, beginning with { (left + * brace) and ending with } (right + * brace). + * @throws JSONException + * If the value is or contains an invalid number. + */ + public static String valueToString(Object value) throws JSONException { + if (value == null || value.equals(null)) { + return "null"; + } + if (value instanceof JSONString) { + String object; + try { + object = ((JSONString) value).toJSONString(); + } catch (Exception e) { + throw new JSONException(e); + } + if (object != null) { + return object; + } + throw new JSONException("Bad value from toJSONString: " + object); + } + if (value instanceof Number) { + // not all Numbers may match actual JSON Numbers. i.e. Fractions or Complex + final String numberAsString = JSONObject.numberToString((Number) value); + if(JSONObject.NUMBER_PATTERN.matcher(numberAsString).matches()) { + // Close enough to a JSON number that we will return it unquoted + return numberAsString; + } + // The Number value is not a valid JSON number. + // Instead we will quote it as a string + return JSONObject.quote(numberAsString); + } + if (value instanceof Boolean || value instanceof JSONObject + || value instanceof JSONArray) { + return value.toString(); + } + if (value instanceof Map) { + Map map = (Map) value; + return new JSONObject(map).toString(); + } + if (value instanceof Collection) { + Collection coll = (Collection) value; + return new JSONArray(coll).toString(); + } + if (value.getClass().isArray()) { + return new JSONArray(value).toString(); + } + if(value instanceof Enum){ + return JSONObject.quote(((Enum)value).name()); + } + return JSONObject.quote(value.toString()); + } + + /** + * Append either the value true or the value + * false. + * @param b A boolean. + * @return this + * @throws JSONException if a called function has an error + */ + public JSONWriter value(boolean b) throws JSONException { + return this.append(b ? "true" : "false"); + } + + /** + * Append a double value. + * @param d A double. + * @return this + * @throws JSONException If the number is not finite. + */ + public JSONWriter value(double d) throws JSONException { + return this.value(Double.valueOf(d)); + } + + /** + * Append a long value. + * @param l A long. + * @return this + * @throws JSONException if a called function has an error + */ + public JSONWriter value(long l) throws JSONException { + return this.append(Long.toString(l)); + } + + + /** + * Append an object value. + * @param object The object to append. It can be null, or a Boolean, Number, + * String, JSONObject, or JSONArray, or an object that implements JSONString. + * @return this + * @throws JSONException If the value is out of sequence. + */ + public JSONWriter value(Object object) throws JSONException { + return this.append(valueToString(object)); + } +} \ No newline at end of file diff --git a/src/net/lax1dude/eaglercraft/SHA1Digest.java b/src/net/lax1dude/eaglercraft/SHA1Digest.java new file mode 100644 index 0000000..9ba1d4d --- /dev/null +++ b/src/net/lax1dude/eaglercraft/SHA1Digest.java @@ -0,0 +1,213 @@ +package net.lax1dude.eaglercraft; + +/** + * implementation of SHA-1 as outlined in "Handbook of Applied Cryptography", + * pages 346 - 349. + * + * It is interesting to ponder why the, apart from the extra IV, the other + * difference here from MD5 is the "endienness" of the word processing! + */ +public class SHA1Digest extends GeneralDigest { + private static final int DIGEST_LENGTH = 20; + + private int H1, H2, H3, H4, H5; + + private int[] X = new int[80]; + private int xOff; + + /** + * Standard constructor + */ + public SHA1Digest() { + reset(); + } + + /** + * Copy constructor. This will copy the state of the provided message digest. + */ + public SHA1Digest(SHA1Digest t) { + super(t); + + H1 = t.H1; + H2 = t.H2; + H3 = t.H3; + H4 = t.H4; + H5 = t.H5; + + System.arraycopy(t.X, 0, X, 0, t.X.length); + xOff = t.xOff; + } + + public String getAlgorithmName() { + return "SHA-1"; + } + + public int getDigestSize() { + return DIGEST_LENGTH; + } + + protected void processWord(byte[] in, int inOff) { + X[xOff++] = ((in[inOff] & 0xff) << 24) | ((in[inOff + 1] & 0xff) << 16) | ((in[inOff + 2] & 0xff) << 8) + | ((in[inOff + 3] & 0xff)); + + if (xOff == 16) { + processBlock(); + } + } + + private void unpackWord(int word, byte[] out, int outOff) { + out[outOff] = (byte) (word >>> 24); + out[outOff + 1] = (byte) (word >>> 16); + out[outOff + 2] = (byte) (word >>> 8); + out[outOff + 3] = (byte) word; + } + + protected void processLength(long bitLength) { + if (xOff > 14) { + processBlock(); + } + + X[14] = (int) (bitLength >>> 32); + X[15] = (int) (bitLength & 0xffffffff); + } + + public int doFinal(byte[] out, int outOff) { + finish(); + + unpackWord(H1, out, outOff); + unpackWord(H2, out, outOff + 4); + unpackWord(H3, out, outOff + 8); + unpackWord(H4, out, outOff + 12); + unpackWord(H5, out, outOff + 16); + + reset(); + + return DIGEST_LENGTH; + } + + /** + * reset the chaining variables + */ + public void reset() { + super.reset(); + + H1 = 0x67452301; + H2 = 0xefcdab89; + H3 = 0x98badcfe; + H4 = 0x10325476; + H5 = 0xc3d2e1f0; + + xOff = 0; + for (int i = 0; i != X.length; i++) { + X[i] = 0; + } + } + + // + // Additive constants + // + private static final int Y1 = 0x5a827999; + private static final int Y2 = 0x6ed9eba1; + private static final int Y3 = 0x8f1bbcdc; + private static final int Y4 = 0xca62c1d6; + + private int f(int u, int v, int w) { + return ((u & v) | ((~u) & w)); + } + + private int h(int u, int v, int w) { + return (u ^ v ^ w); + } + + private int g(int u, int v, int w) { + return ((u & v) | (u & w) | (v & w)); + } + + private int rotateLeft(int x, int n) { + return (x << n) | (x >>> (32 - n)); + } + + protected void processBlock() { + // + // expand 16 word block into 80 word block. + // + for (int i = 16; i <= 79; i++) { + X[i] = rotateLeft((X[i - 3] ^ X[i - 8] ^ X[i - 14] ^ X[i - 16]), 1); + } + + // + // set up working variables. + // + int A = H1; + int B = H2; + int C = H3; + int D = H4; + int E = H5; + + // + // round 1 + // + for (int j = 0; j <= 19; j++) { + int t = rotateLeft(A, 5) + f(B, C, D) + E + X[j] + Y1; + + E = D; + D = C; + C = rotateLeft(B, 30); + B = A; + A = t; + } + + // + // round 2 + // + for (int j = 20; j <= 39; j++) { + int t = rotateLeft(A, 5) + h(B, C, D) + E + X[j] + Y2; + + E = D; + D = C; + C = rotateLeft(B, 30); + B = A; + A = t; + } + + // + // round 3 + // + for (int j = 40; j <= 59; j++) { + int t = rotateLeft(A, 5) + g(B, C, D) + E + X[j] + Y3; + + E = D; + D = C; + C = rotateLeft(B, 30); + B = A; + A = t; + } + + // + // round 4 + // + for (int j = 60; j <= 79; j++) { + int t = rotateLeft(A, 5) + h(B, C, D) + E + X[j] + Y4; + + E = D; + D = C; + C = rotateLeft(B, 30); + B = A; + A = t; + } + + H1 += A; + H2 += B; + H3 += C; + H4 += D; + H5 += E; + + // + // reset the offset and clean out the word buffer. + // + xOff = 0; + for (int i = 0; i != X.length; i++) { + X[i] = 0; + } + } +} \ No newline at end of file diff --git a/src/net/lax1dude/eaglercraft/adapter/EaglerAdapterImpl2.java b/src/net/lax1dude/eaglercraft/adapter/EaglerAdapterImpl2.java new file mode 100644 index 0000000..3eed1dc --- /dev/null +++ b/src/net/lax1dude/eaglercraft/adapter/EaglerAdapterImpl2.java @@ -0,0 +1,2067 @@ +package net.lax1dude.eaglercraft.adapter; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.nio.IntBuffer; +import java.nio.charset.Charset; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.Set; + +import org.teavm.interop.Async; +import org.teavm.interop.AsyncCallback; +import org.teavm.jso.JSBody; +import org.teavm.jso.JSFunctor; +import org.teavm.jso.JSObject; +import org.teavm.jso.ajax.ReadyStateChangeHandler; +import org.teavm.jso.ajax.XMLHttpRequest; +import org.teavm.jso.browser.TimerHandler; +import org.teavm.jso.browser.Window; +import org.teavm.jso.canvas.CanvasRenderingContext2D; +import org.teavm.jso.canvas.ImageData; +import org.teavm.jso.dom.css.CSSStyleDeclaration; +import org.teavm.jso.dom.events.Event; +import org.teavm.jso.dom.events.EventListener; +import org.teavm.jso.dom.events.KeyboardEvent; +import org.teavm.jso.dom.events.MessageEvent; +import org.teavm.jso.dom.events.MouseEvent; +import org.teavm.jso.dom.events.WheelEvent; +import org.teavm.jso.dom.html.HTMLCanvasElement; +import org.teavm.jso.dom.html.HTMLDocument; +import org.teavm.jso.dom.html.HTMLElement; +import org.teavm.jso.dom.html.HTMLImageElement; +import org.teavm.jso.dom.html.HTMLInputElement; +import org.teavm.jso.typedarrays.ArrayBuffer; +import org.teavm.jso.typedarrays.DataView; +import org.teavm.jso.typedarrays.Float32Array; +import org.teavm.jso.typedarrays.Int32Array; +import org.teavm.jso.typedarrays.Int8Array; +import org.teavm.jso.typedarrays.Uint8Array; +import org.teavm.jso.typedarrays.Uint8ClampedArray; +import org.teavm.jso.webaudio.AudioBuffer; +import org.teavm.jso.webaudio.AudioBufferSourceNode; +import org.teavm.jso.webaudio.AudioContext; +import org.teavm.jso.webaudio.AudioListener; +import org.teavm.jso.webaudio.DecodeErrorCallback; +import org.teavm.jso.webaudio.DecodeSuccessCallback; +import org.teavm.jso.webaudio.GainNode; +import org.teavm.jso.webaudio.MediaEvent; +import org.teavm.jso.webaudio.PannerNode; +import org.teavm.jso.webgl.WebGLBuffer; +import org.teavm.jso.webgl.WebGLFramebuffer; +import org.teavm.jso.webgl.WebGLProgram; +import org.teavm.jso.webgl.WebGLRenderbuffer; +import org.teavm.jso.webgl.WebGLShader; +import org.teavm.jso.webgl.WebGLTexture; +import org.teavm.jso.webgl.WebGLUniformLocation; +import org.teavm.jso.websocket.CloseEvent; +import org.teavm.jso.websocket.WebSocket; + +import net.lax1dude.eaglercraft.adapter.teavm.WebGLQuery; +import net.lax1dude.eaglercraft.adapter.teavm.WebGLVertexArray; +import net.minecraft.src.MathHelper; +import net.PeytonPlayz585.storage.LocalStorageManager; +import net.lax1dude.eaglercraft.AssetRepository; +import net.lax1dude.eaglercraft.Base64; +import net.lax1dude.eaglercraft.Client; +import net.lax1dude.eaglercraft.EaglerImage; +import net.lax1dude.eaglercraft.JSONObject; +import net.lax1dude.eaglercraft.adapter.teavm.IndexedDBFilesystem; +import net.lax1dude.eaglercraft.adapter.teavm.IndexedDBFilesystem.OpenState; +//import net.lax1dude.eaglercraft.adapter.teavm.IndexedDBFilesystem; +//import net.lax1dude.eaglercraft.adapter.teavm.IndexedDBFilesystem.OpenState; +import net.lax1dude.eaglercraft.adapter.teavm.WebGL2RenderingContext; +import static net.lax1dude.eaglercraft.adapter.teavm.WebGL2RenderingContext.*; + +public class EaglerAdapterImpl2 { + + public static final boolean _wisWebGL() { + return true; + } + private static boolean isAnisotropicPatched = false; + public static final boolean _wisAnisotropicPatched() { + return isAnisotropicPatched; + } + public static final String _wgetShaderHeader() { + return "#version 300 es"; + } + + @JSBody(params = { }, script = "return window.location.href;") + private static native String getLocationString(); + + public static final boolean isSSLPage() { + return getLocationString().startsWith("https"); + } + + public static final InputStream loadResource(String path) { + byte[] file = loadResourceBytes(path); + if (file != null) { + return new ByteArrayInputStream(file); + } else { + return null; + } + } + + public static final byte[] loadResourceBytes(String path) { + return AssetRepository.getResource(path); + } + + public static final String fileContents(String path) { + byte[] contents = loadResourceBytes(path); + if(contents == null) { + return null; + }else { + return new String(contents, Charset.forName("UTF-8")); + } + } + + @JSBody(params = { }, script = "window.addEventListener('beforeunload', (event) => {event.preventDefault(); return '';});") + private static native void onBeforeCloseRegister(); + + public static final String[] fileContentsLines(String path) { + String contents = fileContents(path); + if(contents == null) { + return null; + }else { + return contents.replace("\r\n", "\n").split("[\r\n]"); + } + } + + @Async + public static native String downloadAssetPack(String assetPackageURI); + + private static void downloadAssetPack(String assetPackageURI, final AsyncCallback cb) { + final XMLHttpRequest request = XMLHttpRequest.create(); + request.setResponseType("arraybuffer"); + request.open("GET", assetPackageURI, true); + request.setOnReadyStateChange(new ReadyStateChangeHandler() { + public void stateChanged() { + if(request.getReadyState() == XMLHttpRequest.DONE) { + Uint8Array bl = Uint8Array.create((ArrayBuffer)request.getResponse()); + loadedPackage = new byte[bl.getByteLength()]; + for(int i = 0; i < loadedPackage.length; ++i) { + loadedPackage[i] = (byte) bl.get(i); + } + cb.complete("yee"); + } + } + }); + request.send(); + } + + @JSBody(params = { "obj" }, script = "window.currentContext = obj;") + private static native int setContextVar(JSObject obj); + + @JSBody(params = { "v", "s" }, script = "window[v] = s;") + public static native void setDebugVar(String v, String s); + + @JSBody(params = { }, script = "if(window.navigator.userActivation){return window.navigator.userActivation.hasBeenActive;}else{return false;}") + public static native boolean hasBeenActive(); + + public static HTMLDocument doc = null; + public static HTMLElement parent = null; + public static HTMLCanvasElement canvas = null; + public static WebGL2RenderingContext webgl = null; + public static FramebufferGL backBuffer = null; + public static RenderbufferGL backBufferColor = null; + public static RenderbufferGL backBufferDepth = null; + public static Window win = null; + private static byte[] loadedPackage = null; + private static EventListener contextmenu = null; + private static EventListener mousedown = null; + private static EventListener mouseup = null; + private static EventListener mousemove = null; + private static EventListener keydown = null; + private static EventListener keyup = null; + private static EventListener keypress = null; + private static EventListener wheel = null; + private static String[] identifier = new String[0]; + + public static final String[] getIdentifier() { + return identifier; + } + + @JSBody(params = { }, script = "return window.navigator.userAgent;") + private static native String getUA(); + + @JSBody(params = { }, script = "return window.navigator.platform;") + private static native String getPlaf(); + + @JSBody(params = { "m" }, script = "return m.offsetX;") + private static native int getOffsetX(MouseEvent m); + + @JSBody(params = { "m" }, script = "return m.offsetY;") + private static native int getOffsetY(MouseEvent m); + + @JSBody(params = { "e" }, script = "return e.which;") + private static native int getWhich(KeyboardEvent e); + + public static final void initializeContext(HTMLElement rootElement, String assetPackageURI, JSONObject config) { + parent = rootElement; + String s = parent.getAttribute("style"); + parent.setAttribute("style", (s == null ? "" : s)+"overflow-x:hidden;overflow-y:hidden;"); + win = Window.current(); + doc = win.getDocument(); + canvas = (HTMLCanvasElement)doc.createElement("canvas"); + double r = win.getDevicePixelRatio(); + width = (int)(rootElement.getClientWidth() * r); + height = (int)(rootElement.getClientHeight() * r); + canvas.setWidth(width); + canvas.setHeight(height); + canvas.setAttribute("id", "deevis589723589"); + rootElement.appendChild(canvas); + CSSStyleDeclaration canvasStyle = canvas.getStyle(); + canvasStyle.setProperty("width", "100%"); + canvasStyle.setProperty("height", "100%"); + canvasStyle.setProperty("image-rendering", "pixelated"); + webgl = (WebGL2RenderingContext) canvas.getContext("webgl2", youEagler()); + if(webgl == null) { + throw new RuntimeException("WebGL 2.0 is not supported in your browser ("+getUA()+")"); + } + setContextVar(webgl); + setupBackBuffer(); + resizeBackBuffer(width, height); + + webgl.getExtension("EXT_texture_filter_anisotropic"); + + win.addEventListener("contextmenu", contextmenu = new EventListener() { + public void handleEvent(MouseEvent evt) { + evt.preventDefault(); + evt.stopPropagation(); + } + }); + canvas.addEventListener("mousedown", mousedown = new EventListener() { + public void handleEvent(MouseEvent evt) { + int b = evt.getButton(); + buttonStates[b == 1 ? 2 : (b == 2 ? 1 : b)] = true; + mouseEvents.add(evt); + evt.preventDefault(); + evt.stopPropagation(); + forceMouseGrabbed(); + } + }); + canvas.addEventListener("mouseup", mouseup = new EventListener() { + public void handleEvent(MouseEvent evt) { + int b = evt.getButton(); + buttonStates[b == 1 ? 2 : (b == 2 ? 1 : b)] = false; + mouseEvents.add(evt); + evt.preventDefault(); + evt.stopPropagation(); + } + }); + canvas.addEventListener("mousemove", mousemove = new EventListener() { + public void handleEvent(MouseEvent evt) { + double r = win.getDevicePixelRatio(); + mouseX = (int)(getOffsetX(evt) * r); + mouseY = (int)((canvas.getClientHeight() - getOffsetY(evt)) * r); + mouseDX += evt.getMovementX(); + mouseDY += -evt.getMovementY(); + evt.preventDefault(); + evt.stopPropagation(); + } + }); + win.addEventListener("keydown", keydown = new EventListener() { + public void handleEvent(KeyboardEvent evt) { + //keyStates[remapKey(evt.getKeyCode())] = true; + keyStates[remapKey(getWhich(evt))] = true; + keyEvents.add(evt); + evt.preventDefault(); + evt.stopPropagation(); + forceMouseGrabbed(); + } + }); + win.addEventListener("keyup", keyup = new EventListener() { + public void handleEvent(KeyboardEvent evt) { + //keyStates[remapKey(evt.getKeyCode())] = false; + keyStates[remapKey(getWhich(evt))] = false; + keyEvents.add(evt); + evt.preventDefault(); + evt.stopPropagation(); + } + }); + win.addEventListener("keypress", keypress = new EventListener() { + public void handleEvent(KeyboardEvent evt) { + if(enableRepeatEvents && evt.isRepeat()) keyEvents.add(evt); + evt.preventDefault(); + evt.stopPropagation(); + } + }); + canvas.addEventListener("wheel", wheel = new EventListener() { + public void handleEvent(WheelEvent evt) { + mouseEvents.add(evt); + evt.preventDefault(); + evt.stopPropagation(); + } + }); + win.addEventListener("blur", new EventListener() { + public void handleEvent(WheelEvent evt) { + isWindowFocused = false; + } + }); + win.addEventListener("focus", new EventListener() { + public void handleEvent(WheelEvent evt) { + isWindowFocused = true; + forceMouseGrabbed(); + } + }); + onBeforeCloseRegister(); + + if(!config.isNull("dataBaseName")) { + dataBaseName = config.getString("dataBaseName"); + } + + OpenState st = IndexedDBFilesystem.initialize(); + if(st != OpenState.OPENED) { + if(st == OpenState.LOCKED) { + Client.showDatabaseLockedScreen("\nError: World folder is locked!\n\nYou are already playing Eaglercraft in a different tab.\nClose all other Eaglercraft tabs and reload"); + }else { + Client.showDatabaseLockedScreen("\nError: World folder could not be loaded!\n\n" + IndexedDBFilesystem.errorDetail()); + } + throw new Client.AbortedLaunchException(); + } + + downloadAssetPack(assetPackageURI); + + try { + AssetRepository.install(loadedPackage); + } catch (IOException e) { + e.printStackTrace(); + } + + audioctx = AudioContext.create(); + + mouseEvents.clear(); + keyEvents.clear(); + + if(!config.isNull("playerUsername")) { + forcedUser = config.getString("playerUsername"); + } + if(!config.isNull("serverIP")) { + forcedServer = config.getString("serverIP"); + } + + if(!config.isNull("joinServerOnLaunch")) { + joinServerOnLaunch = config.getBoolean("joinServerOnLaunch"); + } + } + + public static String forcedUser = null; + public static String forcedServer = null; + public static String dataBaseName = null; + public static boolean joinServerOnLaunch = false; + + public static final void destroyContext() { + + } + + public static final void removeEventHandlers() { + win.removeEventListener("contextmenu", contextmenu); + win.removeEventListener("mousedown", mousedown); + win.removeEventListener("mouseup", mouseup); + win.removeEventListener("mousemove", mousemove); + win.removeEventListener("keydown", keydown); + win.removeEventListener("keyup", keyup); + win.removeEventListener("keypress", keypress); + win.removeEventListener("wheel", wheel); + } + + private static LinkedList mouseEvents = new LinkedList(); + private static LinkedList keyEvents = new LinkedList(); + + private static int mouseX = 0; + private static int mouseY = 0; + private static double mouseDX = 0.0D; + private static double mouseDY = 0.0D; + private static int width = 0; + private static int height = 0; + private static boolean enableRepeatEvents = false; + private static boolean isWindowFocused = true; + + @JSBody(params = { }, script = "return {antialias: false, depth: true, powerPreference: \"high-performance\", desynchronized: true, preserveDrawingBuffer: false, premultipliedAlpha: false, alpha: false};") + public static native JSObject youEagler(); + + public static final int _wGL_TEXTURE_2D = TEXTURE_2D; + public static final int _wGL_DEPTH_TEST = DEPTH_TEST; + public static final int _wGL_LEQUAL = LEQUAL; + public static final int _wGL_GEQUAL = GEQUAL; + public static final int _wGL_GREATER = GREATER; + public static final int _wGL_LESS = LESS; + public static final int _wGL_BACK = BACK; + public static final int _wGL_FRONT = FRONT; + public static final int _wGL_FRONT_AND_BACK = FRONT_AND_BACK; + public static final int _wGL_COLOR_BUFFER_BIT = COLOR_BUFFER_BIT; + public static final int _wGL_DEPTH_BUFFER_BIT = DEPTH_BUFFER_BIT; + public static final int _wGL_BLEND = BLEND; + public static final int _wGL_RGBA = RGBA; + public static final int _wGL_RGB = RGB; + public static final int _wGL_RGB8 = RGB8; + public static final int _wGL_RGBA8 = RGBA8; + public static final int _wGL_UNSIGNED_BYTE = UNSIGNED_BYTE; + public static final int _wGL_UNSIGNED_SHORT = UNSIGNED_SHORT; + public static final int _wGL_SRC_ALPHA = SRC_ALPHA; + public static final int _wGL_ONE_MINUS_SRC_ALPHA = ONE_MINUS_SRC_ALPHA; + public static final int _wGL_ONE_MINUS_DST_COLOR = ONE_MINUS_DST_COLOR; + public static final int _wGL_ONE_MINUS_SRC_COLOR = ONE_MINUS_SRC_COLOR; + public static final int _wGL_ZERO = ZERO; + public static final int _wGL_CULL_FACE = CULL_FACE; + public static final int _wGL_TEXTURE_MIN_FILTER = TEXTURE_MIN_FILTER; + public static final int _wGL_TEXTURE_MAG_FILTER = TEXTURE_MAG_FILTER; + public static final int _wGL_LINEAR = LINEAR; + public static final int _wGL_EQUAL = EQUAL; + public static final int _wGL_SRC_COLOR = SRC_COLOR; + public static final int _wGL_ONE = ONE; + public static final int _wGL_NEAREST = NEAREST; + public static final int _wGL_CLAMP = CLAMP_TO_EDGE; + public static final int _wGL_TEXTURE_WRAP_S = TEXTURE_WRAP_S; + public static final int _wGL_TEXTURE_WRAP_T = TEXTURE_WRAP_T; + public static final int _wGL_REPEAT = REPEAT; + public static final int _wGL_DST_COLOR = DST_COLOR; + public static final int _wGL_DST_ALPHA = DST_ALPHA; + public static final int _wGL_FLOAT = FLOAT; + public static final int _wGL_SHORT = SHORT; + public static final int _wGL_TRIANGLES = TRIANGLES; + public static final int _wGL_TRIANGLE_STRIP = TRIANGLE_STRIP; + public static final int _wGL_TRIANGLE_FAN = TRIANGLE_FAN; + public static final int _wGL_LINE_STRIP = LINE_STRIP; + public static final int _wGL_LINES = LINES; + public static final int _wGL_PACK_ALIGNMENT = PACK_ALIGNMENT; + public static final int _wGL_UNPACK_ALIGNMENT = UNPACK_ALIGNMENT; + public static final int _wGL_TEXTURE0 = TEXTURE0; + public static final int _wGL_TEXTURE1 = TEXTURE1; + public static final int _wGL_TEXTURE2 = TEXTURE2; + public static final int _wGL_TEXTURE3 = TEXTURE3; + public static final int _wGL_VIEWPORT = VIEWPORT; + public static final int _wGL_VERTEX_SHADER = VERTEX_SHADER; + public static final int _wGL_FRAGMENT_SHADER = FRAGMENT_SHADER; + public static final int _wGL_ARRAY_BUFFER = ARRAY_BUFFER; + public static final int _wGL_ELEMENT_ARRAY_BUFFER = ELEMENT_ARRAY_BUFFER; + public static final int _wGL_STATIC_DRAW = STATIC_DRAW; + public static final int _wGL_DYNAMIC_DRAW = DYNAMIC_DRAW; + public static final int _wGL_STREAM_DRAW = STREAM_DRAW; + public static final int _wGL_INVALID_ENUM = INVALID_ENUM; + public static final int _wGL_INVALID_VALUE= INVALID_VALUE; + public static final int _wGL_INVALID_OPERATION = INVALID_OPERATION; + public static final int _wGL_OUT_OF_MEMORY = OUT_OF_MEMORY; + public static final int _wGL_CONTEXT_LOST_WEBGL = CONTEXT_LOST_WEBGL; + public static final int _wGL_FRAMEBUFFER_COMPLETE = FRAMEBUFFER_COMPLETE; + public static final int _wGL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT = FRAMEBUFFER_INCOMPLETE_ATTACHMENT; + public static final int _wGL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT = FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT; + public static final int _wGL_COLOR_ATTACHMENT0 = COLOR_ATTACHMENT0; + public static final int _wGL_DEPTH_STENCIL_ATTACHMENT = DEPTH_STENCIL_ATTACHMENT; + public static final int _wGL_DEPTH_STENCIL = DEPTH_STENCIL; + public static final int _wGL_NEAREST_MIPMAP_LINEAR = NEAREST_MIPMAP_LINEAR; + public static final int _wGL_LINEAR_MIPMAP_LINEAR = LINEAR_MIPMAP_LINEAR; + public static final int _wGL_LINEAR_MIPMAP_NEAREST = LINEAR_MIPMAP_NEAREST; + public static final int _wGL_NEAREST_MIPMAP_NEAREST = NEAREST_MIPMAP_NEAREST; + public static final int _wGL_TEXTURE_MAX_LEVEL = TEXTURE_MAX_LEVEL; + public static final int _wGL_UNSIGNED_INT_24_8 = UNSIGNED_INT_24_8; + public static final int _wGL_UNSIGNED_INT = UNSIGNED_INT; + public static final int _wGL_ANY_SAMPLES_PASSED = ANY_SAMPLES_PASSED; + public static final int _wGL_QUERY_RESULT = QUERY_RESULT; + public static final int _wGL_QUERY_RESULT_AVAILABLE = QUERY_RESULT_AVAILABLE; + public static final int _wGL_TEXTURE_MAX_ANISOTROPY = TEXTURE_MAX_ANISOTROPY_EXT; + public static final int _wGL_DEPTH24_STENCIL8 = DEPTH24_STENCIL8; + public static final int _wGL_DEPTH_COMPONENT32F = DEPTH_COMPONENT32F; + public static final int _wGL_DEPTH_ATTACHMENT = DEPTH_ATTACHMENT; + public static final int _wGL_MULTISAMPLE = -1; + public static final int _wGL_LINE_SMOOTH = -1; + public static final int _wGL_READ_FRAMEBUFFER = READ_FRAMEBUFFER; + public static final int _wGL_DRAW_FRAMEBUFFER = DRAW_FRAMEBUFFER; + public static final int _wGL_FRAMEBUFFER = FRAMEBUFFER; + public static final int _wGL_POLYGON_OFFSET_FILL = POLYGON_OFFSET_FILL; + + public static final class TextureGL { + protected final WebGLTexture obj; + public int w = -1; + public int h = -1; + public boolean nearest = true; + public boolean anisotropic = false; + protected TextureGL(WebGLTexture obj) { + this.obj = obj; + } + } + public static final class BufferGL { + protected final WebGLBuffer obj; + protected BufferGL(WebGLBuffer obj) { + this.obj = obj; + } + } + public static final class ShaderGL { + protected final WebGLShader obj; + protected ShaderGL(WebGLShader obj) { + this.obj = obj; + } + } + private static int progId = 0; + public static final class ProgramGL { + protected final WebGLProgram obj; + protected final int hashcode; + protected ProgramGL(WebGLProgram obj) { + this.obj = obj; + this.hashcode = ++progId; + } + } + public static final class UniformGL { + protected final WebGLUniformLocation obj; + protected UniformGL(WebGLUniformLocation obj) { + this.obj = obj; + } + } + public static final class BufferArrayGL { + protected final WebGLVertexArray obj; + public boolean isQuadBufferBound; + protected BufferArrayGL(WebGLVertexArray obj) { + this.obj = obj; + this.isQuadBufferBound = false; + } + } + public static final class FramebufferGL { + protected final WebGLFramebuffer obj; + protected FramebufferGL(WebGLFramebuffer obj) { + this.obj = obj; + } + } + public static final class RenderbufferGL { + protected final WebGLRenderbuffer obj; + protected RenderbufferGL(WebGLRenderbuffer obj) { + this.obj = obj; + } + } + public static final class QueryGL { + protected final WebGLQuery obj; + protected QueryGL(WebGLQuery obj) { + this.obj = obj; + } + } + + public static final void _wglEnable(int p1) { + webgl.enable(p1); + } + public static final void _wglClearDepth(float p1) { + webgl.clearDepth(p1); + } + public static final void _wglDepthFunc(int p1) { + webgl.depthFunc(p1); + } + public static final void _wglCullFace(int p1) { + webgl.cullFace(p1); + } + private static int[] viewportCache = new int[4]; + public static final void _wglViewport(int p1, int p2, int p3, int p4) { + viewportCache[0] = p1; viewportCache[1] = p2; + viewportCache[2] = p3; viewportCache[3] = p4; + webgl.viewport(p1, p2, p3, p4); + } + public static final void _wglClear(int p1) { + webgl.clear(p1); + } + public static final void _wglClearColor(float p1, float p2, float p3, float p4) { + webgl.clearColor(p1, p2, p3, p4); + } + public static final void _wglDisable(int p1) { + webgl.disable(p1); + } + public static final int _wglGetError() { + return webgl.getError(); + } + public static final void _wglFlush() { + webgl.flush(); + } + private static Uint8Array uploadBuffer = Uint8Array.create(ArrayBuffer.create(4 * 1024 * 1024)); + public static final void _wglTexImage2D(int p1, int p2, int p3, int p4, int p5, int p6, int p7, int p8, ByteBuffer p9) { + if(p9 == null) { + webgl.texImage2D(p1, p2, p3, p4, p5, p6, p7, p8, null); + }else { + int len = p9.remaining(); + Uint8Array uploadBuffer1 = uploadBuffer; + for(int i = 0; i < len; ++i) { + uploadBuffer1.set(i, (short) ((int)p9.get() & 0xff)); + } + Uint8Array data = Uint8Array.create(uploadBuffer.getBuffer(), 0, len); + webgl.texImage2D(p1, p2, p3, p4, p5, p6, p7, p8, data); + } + } + public static final void _wglBlendFunc(int p1, int p2) { + webgl.blendFunc(p1, p2); + } + public static final void _wglBlendFuncSeparate(int p1, int p2, int p3, int p4) { + webgl.blendFuncSeparate(p1, p2, p3, p4); + } + public static final void _wglDepthMask(boolean p1) { + webgl.depthMask(p1); + } + public static final void _wglColorMask(boolean p1, boolean p2, boolean p3, boolean p4) { + webgl.colorMask(p1, p2, p3, p4); + } + public static final void _wglBindTexture(int p1, TextureGL p2) { + webgl.bindTexture(p1, p2 == null ? null : p2.obj); + } + public static final void _wglCopyTexSubImage2D(int p1, int p2, int p3, int p4, int p5, int p6, int p7, int p8) { + webgl.copyTexSubImage2D(p1, p2, p3, p4, p5, p6, p7, p8); + } + public static final void _wglTexParameteri(int p1, int p2, int p3) { + webgl.texParameteri(p1, p2, p3); + } + public static final void _wglTexParameterf(int p1, int p2, float p3) { + webgl.texParameterf(p1, p2, p3); + } + public static final void _wglTexImage2D(int p1, int p2, int p3, int p4, int p5, int p6, int p7, int p8, IntBuffer p9) { + int len = p9.remaining(); + Int32Array deevis = Int32Array.create(uploadBuffer.getBuffer()); + for(int i = 0; i < len; ++i) { + deevis.set(i, p9.get()); + } + Uint8Array data = Uint8Array.create(uploadBuffer.getBuffer(), 0, len*4); + webgl.texImage2D(p1, p2, p3, p4, p5, p6, p7, p8, data); + } + public static final void _wglTexSubImage2D(int p1, int p2, int p3, int p4, int p5, int p6, int p7, int p8, IntBuffer p9) { + int len = p9.remaining(); + Int32Array deevis = Int32Array.create(uploadBuffer.getBuffer()); + for(int i = 0; i < len; ++i) { + deevis.set(i, p9.get()); + } + Uint8Array data = Uint8Array.create(uploadBuffer.getBuffer(), 0, len*4); + webgl.texSubImage2D(p1, p2, p3, p4, p5, p6, p7, p8, data); + } + public static final void _wglDeleteTextures(TextureGL p1) { + webgl.deleteTexture(p1.obj); + } + public static final void _wglDrawArrays(int p1, int p2, int p3) { + webgl.drawArrays(p1, p2, p3); + } + public static final void _wglDrawElements(int p1, int p2, int p3, int p4) { + webgl.drawElements(p1, p2, p3, p4); + } + public static final TextureGL _wglGenTextures() { + return new TextureGL(webgl.createTexture()); + } + public static final void _wglTexSubImage2D(int p1, int p2, int p3, int p4, int p5, int p6, int p7, int p8, ByteBuffer p9) { + int len = p9.remaining(); + for(int i = 0; i < len; ++i) { + uploadBuffer.set(i, (short) ((int)p9.get() & 0xff)); + } + Uint8Array data = Uint8Array.create(uploadBuffer.getBuffer(), 0, len); + webgl.texSubImage2D(p1, p2, p3, p4, p5, p6, p7, p8, data); + } + public static final void _wglActiveTexture(int p1) { + webgl.activeTexture(p1); + } + public static final ProgramGL _wglCreateProgram() { + return new ProgramGL(webgl.createProgram()); + } + public static final ShaderGL _wglCreateShader(int p1) { + return new ShaderGL(webgl.createShader(p1)); + } + public static final void _wglAttachShader(ProgramGL p1, ShaderGL p2) { + webgl.attachShader(p1.obj, p2.obj); + } + public static final void _wglDetachShader(ProgramGL p1, ShaderGL p2) { + webgl.detachShader(p1.obj, p2.obj); + } + public static final void _wglCompileShader(ShaderGL p1) { + webgl.compileShader(p1.obj); + } + public static final void _wglLinkProgram(ProgramGL p1) { + webgl.linkProgram(p1.obj); + } + public static final void _wglShaderSource(ShaderGL p1, String p2) { + webgl.shaderSource(p1.obj, p2); + } + public static final String _wglGetShaderInfoLog(ShaderGL p1) { + return webgl.getShaderInfoLog(p1.obj); + } + public static final String _wglGetProgramInfoLog(ProgramGL p1) { + return webgl.getProgramInfoLog(p1.obj); + } + public static final boolean _wglGetShaderCompiled(ShaderGL p1) { + return webgl.getShaderParameteri(p1.obj, COMPILE_STATUS) == 1; + } + public static final boolean _wglGetProgramLinked(ProgramGL p1) { + return webgl.getProgramParameteri(p1.obj, LINK_STATUS) == 1; + } + public static final void _wglDeleteShader(ShaderGL p1) { + webgl.deleteShader(p1.obj); + } + public static final void _wglDeleteProgram(ProgramGL p1) { + webgl.deleteProgram(p1.obj); + } + public static final BufferGL _wglCreateBuffer() { + return new BufferGL(webgl.createBuffer()); + } + public static final void _wglDeleteBuffer(BufferGL p1) { + webgl.deleteBuffer(p1.obj); + } + public static final void _wglBindBuffer(int p1, BufferGL p2) { + webgl.bindBuffer(p1, p2 == null ? null : p2.obj); + } + public static final void _wglBufferData0(int p1, IntBuffer p2, int p3) { + int len = p2.remaining(); + Int32Array deevis = Int32Array.create(uploadBuffer.getBuffer()); + for(int i = 0; i < len; ++i) { + deevis.set(i, p2.get()); + } + Uint8Array data = Uint8Array.create(uploadBuffer.getBuffer(), 0, len*4); + webgl.bufferData(p1, data, p3); + } + public static final void _wglBufferData00(int p1, long len, int p3) { + webgl.bufferData(p1, (int)len, p3); + } + public static final void _wglBufferSubData0(int p1, int p2, IntBuffer p3) { + int len = p3.remaining(); + Int32Array deevis = Int32Array.create(uploadBuffer.getBuffer()); + for(int i = 0; i < len; ++i) { + deevis.set(i, p3.get()); + } + Uint8Array data = Uint8Array.create(uploadBuffer.getBuffer(), 0, len*4); + webgl.bufferSubData(p1, p2, data); + } + public static final void _wglBufferData(int p1, Object p2, int p3) { + webgl.bufferData(p1, (Int32Array)p2, p3); + } + public static final void _wglBufferSubData(int p1, int p2, Object p3) { + webgl.bufferSubData(p1, p2, (Int32Array)p3); + } + public static final void _wglBindAttribLocation(ProgramGL p1, int p2, String p3) { + webgl.bindAttribLocation(p1.obj, p2, p3); + } + public static final void _wglEnableVertexAttribArray(int p1) { + webgl.enableVertexAttribArray(p1); + } + public static final void _wglDisableVertexAttribArray(int p1) { + webgl.disableVertexAttribArray(p1); + } + public static final UniformGL _wglGetUniformLocation(ProgramGL p1, String p2) { + WebGLUniformLocation u = webgl.getUniformLocation(p1.obj, p2); + return u == null ? null : new UniformGL(u); + } + public static final void _wglBindAttributeLocation(ProgramGL p1, int p2, String p3) { + webgl.bindAttribLocation(p1.obj, p2, p3); + } + public static final void _wglUniform1f(UniformGL p1, float p2) { + if(p1 != null) webgl.uniform1f(p1.obj, p2); + } + public static final void _wglUniform2f(UniformGL p1, float p2, float p3) { + if(p1 != null) webgl.uniform2f(p1.obj, p2, p3); + } + public static final void _wglUniform3f(UniformGL p1, float p2, float p3, float p4) { + if(p1 != null) webgl.uniform3f(p1.obj, p2, p3, p4); + } + public static final void _wglUniform4f(UniformGL p1, float p2, float p3, float p4, float p5) { + if(p1 != null) webgl.uniform4f(p1.obj, p2, p3, p4, p5); + } + public static final void _wglUniform1i(UniformGL p1, int p2) { + if(p1 != null) webgl.uniform1i(p1.obj, p2); + } + public static final void _wglUniform2i(UniformGL p1, int p2, int p3) { + if(p1 != null) webgl.uniform2i(p1.obj, p2, p3); + } + public static final void _wglUniform3i(UniformGL p1, int p2, int p3, int p4) { + if(p1 != null) webgl.uniform3i(p1.obj, p2, p3, p4); + } + public static final void _wglUniform4i(UniformGL p1, int p2, int p3, int p4, int p5) { + if(p1 != null) webgl.uniform4i(p1.obj, p2, p3, p4, p5); + } + private static Float32Array mat2 = Float32Array.create(4); + private static Float32Array mat3 = Float32Array.create(9); + private static Float32Array mat4 = Float32Array.create(16); + public static final void _wglUniformMat2fv(UniformGL p1, float[] mat) { + mat2.set(mat); + if(p1 != null) webgl.uniformMatrix2fv(p1.obj, false, mat2); + } + public static final void _wglUniformMat3fv(UniformGL p1, float[] mat) { + mat3.set(mat); + if(p1 != null) webgl.uniformMatrix3fv(p1.obj, false, mat3); + } + public static final void _wglUniformMat4fv(UniformGL p1, float[] mat) { + mat4.set(mat); + if(p1 != null) webgl.uniformMatrix4fv(p1.obj, false, mat4); + } + private static int currentProgram = -1; + public static final void _wglUseProgram(ProgramGL p1) { + if(p1 != null && currentProgram != p1.hashcode) { + currentProgram = p1.hashcode; + webgl.useProgram(p1.obj); + } + } + public static final void _wglGetParameter(int p1, int size, int[] ret) { + if(p1 == _wGL_VIEWPORT) { + ret[0] = viewportCache[0]; + ret[1] = viewportCache[1]; + ret[2] = viewportCache[2]; + ret[3] = viewportCache[3]; + } + } + public static final void _wglPolygonOffset(float p1, float p2) { + webgl.polygonOffset(p1, p2); + } + public static final void _wglVertexAttribPointer(int p1, int p2, int p3, boolean p4, int p5, int p6) { + webgl.vertexAttribPointer(p1, p2, p3, p4, p5, p6); + } + public static final void _wglBindFramebuffer(int p1, FramebufferGL p2) { + webgl.bindFramebuffer(p1, p2 == null ? backBuffer.obj : p2.obj); + } + public static final FramebufferGL _wglCreateFramebuffer() { + return new FramebufferGL(webgl.createFramebuffer()); + } + public static final void _wglDeleteFramebuffer(FramebufferGL p1) { + webgl.deleteFramebuffer(p1.obj); + } + public static final void _wglFramebufferTexture2D(int p1, TextureGL p2) { + webgl.framebufferTexture2D(FRAMEBUFFER, p1, TEXTURE_2D, p2 == null ? null : p2.obj, 0); + } + public static final void _wglFramebufferTexture2D(int p1, TextureGL p2, int p3) { + webgl.framebufferTexture2D(FRAMEBUFFER, p1, TEXTURE_2D, p2 == null ? null : p2.obj, p3); + } + public static final QueryGL _wglCreateQuery() { + return new QueryGL(webgl.createQuery()); + } + public static final void _wglBeginQuery(int p1, QueryGL p2) { + webgl.beginQuery(p1, p2.obj); + } + public static final void _wglEndQuery(int p1) { + webgl.endQuery(p1); + } + public static final void _wglDeleteQuery(QueryGL p1) { + webgl.deleteQuery(p1.obj); + } + public static final int _wglGetQueryObjecti(QueryGL p1, int p2) { + return webgl.getQueryParameter(p1.obj, p2); + } + public static final BufferArrayGL _wglCreateVertexArray() { + return new BufferArrayGL(webgl.createVertexArray()); + } + public static final void _wglDeleteVertexArray(BufferArrayGL p1) { + webgl.deleteVertexArray(p1.obj); + } + public static final void _wglBindVertexArray(BufferArrayGL p1) { + webgl.bindVertexArray(p1 == null ? null : p1.obj); + } + public static final void _wglDrawBuffer(int p1) { + webgl.drawBuffers(new int[] { p1 }); + } + public static final RenderbufferGL _wglCreateRenderBuffer() { + return new RenderbufferGL(webgl.createRenderbuffer()); + } + public static final void _wglBindRenderbuffer(RenderbufferGL p1) { + webgl.bindRenderbuffer(RENDERBUFFER, p1 == null ? null : p1.obj); + } + public static final void _wglRenderbufferStorage(int p1, int p2, int p3) { + webgl.renderbufferStorage(RENDERBUFFER, p1, p2, p3); + } + public static final void _wglFramebufferRenderbuffer(int p1, RenderbufferGL p2) { + webgl.framebufferRenderbuffer(FRAMEBUFFER, p1, RENDERBUFFER, p2 == null ? null : p2.obj); + } + public static final void _wglDeleteRenderbuffer(RenderbufferGL p1) { + webgl.deleteRenderbuffer(p1.obj); + } + public static final void _wglRenderbufferStorageMultisample(int p1, int p2, int p3, int p4) { + webgl.renderbufferStorageMultisample(RENDERBUFFER, p1, p2, p3, p4); + } + public static final void _wglBlitFramebuffer(int p1, int p2, int p3, int p4, int p5, int p6, int p7, int p8, int p9, int p10) { + webgl.blitFramebuffer(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10); + } + public static final int _wglGetAttribLocation(ProgramGL p1, String p2) { + return webgl.getAttribLocation(p1.obj, p2); + } + + @JSBody(params = { "ctx", "p" }, script = "return ctx.getTexParameter(0x0DE1, p) | 0;") + private static final native int __wglGetTexParameteri(WebGL2RenderingContext ctx, int p); + public static final int _wglGetTexParameteri(int p1) { + return __wglGetTexParameteri(webgl, p1); + } + @JSBody(params = { "ctx", "p" }, script = "return (0.0 + ctx.getTexParameter(0x0DE1, p));") + private static final native float __wglGetTexParameterf(WebGL2RenderingContext ctx, int p); + public static final float _wglGetTexParameterf(int p1) { + return __wglGetTexParameterf(webgl, p1); + } + public static final void _wglPixelStorei(int i1, int i2) { + webgl.pixelStorei(i1, i2); + } + public static final boolean isWindows() { + return getPlaf().toLowerCase().contains("win"); + } + private static HTMLCanvasElement imageLoadCanvas = null; + private static CanvasRenderingContext2D imageLoadContext = null; + + @JSBody(params = { "buf", "mime" }, script = "return URL.createObjectURL(new Blob([buf], {type: mime}));") + private static native String getDataURL(ArrayBuffer buf, String mime); + + @JSBody(params = { "url" }, script = "URL.revokeObjectURL(url);") + private static native void freeDataURL(String url); + + public static final EaglerImage loadPNG(byte[] data) { + ArrayBuffer arr = ArrayBuffer.create(data.length); + Uint8Array.create(arr).set(data); + return loadPNG0(arr); + } + + @JSBody(params = { "cccc", "ennn" }, script = "cccc.imageSmoothingEnabled = ennn;") + private static native void setImageSmoothingMode(CanvasRenderingContext2D cc, boolean en); + + @Async + private static native EaglerImage loadPNG0(ArrayBuffer data); + + private static void loadPNG0(ArrayBuffer data, final AsyncCallback ret) { + final HTMLImageElement toLoad = (HTMLImageElement) doc.createElement("img"); + toLoad.addEventListener("load", new EventListener() { + public void handleEvent(Event evt) { + if(imageLoadCanvas == null) { + imageLoadCanvas = (HTMLCanvasElement) doc.createElement("canvas"); + } + if(imageLoadCanvas.getWidth() < toLoad.getWidth()) { + imageLoadCanvas.setWidth(toLoad.getWidth()); + } + if(imageLoadCanvas.getHeight() < toLoad.getHeight()) { + imageLoadCanvas.setHeight(toLoad.getHeight()); + } + if(imageLoadContext == null) { + imageLoadContext = (CanvasRenderingContext2D) imageLoadCanvas.getContext("2d"); + setImageSmoothingMode(imageLoadContext, false); + } + imageLoadContext.clearRect(0, 0, toLoad.getWidth(), toLoad.getHeight()); + imageLoadContext.drawImage(toLoad, 0, 0, toLoad.getWidth(), toLoad.getHeight()); + ImageData pxlsDat = imageLoadContext.getImageData(0, 0, toLoad.getWidth(), toLoad.getHeight()); + Uint8ClampedArray pxls = pxlsDat.getData(); + int totalPixels = pxlsDat.getWidth() * pxlsDat.getHeight(); + freeDataURL(toLoad.getSrc()); + if(pxls.getByteLength() < totalPixels * 4) { + ret.complete(null); + return; + } + DataView dv = DataView.create(pxls.getBuffer()); + int[] pixels = new int[totalPixels]; + for(int i = 0, j; i < pixels.length; ++i) { + j = dv.getUint32(i << 2, false); + pixels[i] = ((j >> 8) & 0xFFFFFF) | ((j & 0xFF) << 24); + } + ret.complete(new EaglerImage(pxlsDat.getWidth(), pxlsDat.getHeight(), pixels, true)); + } + }); + toLoad.addEventListener("error", new EventListener() { + public void handleEvent(Event evt) { + freeDataURL(toLoad.getSrc()); + ret.complete(null); + } + }); + String src = getDataURL(data, "image/png"); + if(src == null) { + ret.complete(null); + }else { + toLoad.setSrc(src); + } + } + + private static MouseEvent currentEvent = null; + private static KeyboardEvent currentEventK = null; + private static boolean[] buttonStates = new boolean[8]; + private static boolean[] keyStates = new boolean[256]; + public static final boolean mouseNext() { + currentEvent = null; + return !mouseEvents.isEmpty() && (currentEvent = mouseEvents.remove(0)) != null; + } + public static final int mouseGetEventButton() { + if(currentEvent == null) return -1; + int b = currentEvent.getButton(); + return b == 1 ? 2 : (b == 2 ? 1 : b); + } + public static final boolean mouseGetEventButtonState() { + return currentEvent == null ? false : currentEvent.getType().equals(MouseEvent.MOUSEDOWN); + } + public static final boolean mouseIsButtonDown(int p1) { + return buttonStates[p1]; + } + public static final int mouseGetEventDWheel() { + return ("wheel".equals(currentEvent.getType())) ? (((WheelEvent)currentEvent).getDeltaY() == 0.0D ? 0 : (((WheelEvent)currentEvent).getDeltaY() > 0.0D ? -1 : 1)) : 0; + } + public static final void mouseSetCursorPosition(int x, int y) { + + } + private static long mouseUngrabTimer = 0l; + private static int mouseUngrabTimeout = 0; + private static boolean needsPointerLock = false; + public static final void mouseSetGrabbed(boolean grabbed) { + needsPointerLock = grabbed; + if(grabbed) { + mouseDX = 0.0D; + mouseDY = 0.0D; + Window.setTimeout(new TimerHandler() { + public void onTimer() { + canvas.requestPointerLock(); + long t = System.currentTimeMillis(); + if(mouseUngrabTimeout != 0) Window.clearTimeout(mouseUngrabTimeout); + mouseUngrabTimeout = 0; + if(t - mouseUngrabTimer < 3000l) { + mouseUngrabTimeout = Window.setTimeout(new TimerHandler() { + public void onTimer() { + canvas.requestPointerLock(); + mouseUngrabTimeout = 0; + } + }, 3000 - (int)(t - mouseUngrabTimer)); + } + } + }, 200); + }else { + mouseUngrabTimer = System.currentTimeMillis(); + if(mouseUngrabTimeout != 0) Window.clearTimeout(mouseUngrabTimeout); + mouseUngrabTimeout = 0; + doc.exitPointerLock(); + } + } + private static void forceMouseGrabbed() { + long t = System.currentTimeMillis(); + if(t - mouseUngrabTimer > 3000l) { + if(needsPointerLock && !isPointerLocked()) { + canvas.requestPointerLock(); + if(isPointerLocked()) { + needsPointerLock = false; + } + } + } + } + public static final int mouseGetDX() { + double dx = mouseDX; + mouseDX = 0.0D; + return (int)dx; + } + public static final int mouseGetDY() { + double dy = mouseDY; + mouseDY = 0.0D; + return (int)dy; + } + public static final int mouseGetX() { + return mouseX; + } + public static final int mouseGetY() { + return mouseY; + } + public static final int mouseGetEventX() { + return currentEvent == null ? -1 : (int)(currentEvent.getClientX() * win.getDevicePixelRatio()); + } + public static final int mouseGetEventY() { + return currentEvent == null ? -1 : (int)((canvas.getClientHeight() - currentEvent.getClientY()) * win.getDevicePixelRatio()); + } + public static final boolean keysNext() { + if(unpressCTRL) { //un-press ctrl after copy/paste permission + keyEvents.clear(); + currentEventK = null; + keyStates[29] = false; + keyStates[157] = false; + keyStates[28] = false; + keyStates[219] = false; + keyStates[220] = false; + unpressCTRL = false; + return false; + } + currentEventK = null; + return !keyEvents.isEmpty() && (currentEventK = keyEvents.remove(0)) != null; + } + public static final int getEventKey() { + return currentEventK == null ? -1 : remapKey(getWhich(currentEventK)); + } + public static final char getEventChar() { + if(currentEventK == null) return '\0'; + String s = currentEventK.getKey(); + return currentEventK == null ? ' ' : (char) (s.length() > 1 ? '\0' : s.charAt(0)); + } + public static final boolean getEventKeyState() { + return currentEventK == null? false : !currentEventK.getType().equals("keyup"); + } + public static final boolean isKeyDown(int p1) { + if(unpressCTRL) { //un-press ctrl after copy/paste permission + keyStates[28] = false; + keyStates[29] = false; + keyStates[157] = false; + keyStates[219] = false; + keyStates[220] = false; + } + return keyStates[p1]; + } + public static final String getKeyName(int p1) { + return (p1 >= 0 && p1 < 256) ? LWJGLKeyNames[p1] : "null"; + } + public static final void setFullscreen(boolean p1) { + if(p1) { + fullscreen(); + } else { + exitFullscreen(); + } + } + + @JSBody(params = { }, script = "if(!document.fullscreenElement){document.documentElement.requestFullscreen();}") + public static final native void fullscreen(); + + @JSBody(params = { }, script = "if(document.fullscreenElement){document.exitFullscreen();}") + public static final native void exitFullscreen(); + + public static final boolean shouldShutdown() { + return false; + } + public static final boolean isFunctionKeyDown(boolean mod, int p1) { + return mod && p1 >= 59 && p1 <= 67 && getEventKey() == (2 + (p1 - 59)); + } + public static final boolean isFunctionKeyDown(int mod, int p1) { + return isKeyDown(mod) && p1 >= 59 && p1 <= 67 & getEventKey() == (2 + (p1 - 59)); + } + public static final boolean isFunctionKeyHeldDown(int mod, int p1) { + return isKeyDown(mod) && p1 >= 59 && p1 <= 67 & isKeyDown(2 + (p1 - 59)); + } + + public static final void updateDisplay() { + double r = win.getDevicePixelRatio(); + int w = (int)(canvas.getClientWidth() * r); + int h = (int)(canvas.getClientHeight() * r); + if(canvas.getWidth() != w) { + canvas.setWidth(w); + } + if(canvas.getHeight() != h) { + canvas.setHeight(h); + } + + webgl.bindFramebuffer(FRAMEBUFFER, null); + webgl.bindFramebuffer(READ_FRAMEBUFFER, backBuffer.obj); + webgl.bindFramebuffer(DRAW_FRAMEBUFFER, null); + webgl.blitFramebuffer(0, 0, backBufferWidth, backBufferHeight, 0, 0, w, h, COLOR_BUFFER_BIT, NEAREST); + webgl.bindFramebuffer(FRAMEBUFFER, backBuffer.obj); + resizeBackBuffer(w, h); + try { + Thread.sleep(1l); + } catch (InterruptedException e) { + ; + } + } + public static final void setupBackBuffer() { + backBuffer = _wglCreateFramebuffer(); + _wglBindFramebuffer(_wGL_FRAMEBUFFER, null); + backBufferColor = _wglCreateRenderBuffer(); + _wglBindRenderbuffer(backBufferColor); + _wglFramebufferRenderbuffer(_wGL_COLOR_ATTACHMENT0, backBufferColor); + backBufferDepth = _wglCreateRenderBuffer(); + _wglBindRenderbuffer(backBufferDepth); + _wglFramebufferRenderbuffer(_wGL_DEPTH_ATTACHMENT, backBufferDepth); + } + private static int backBufferWidth = -1; + private static int backBufferHeight = -1; + public static final void resizeBackBuffer(int w, int h) { + if(w != backBufferWidth || h != backBufferHeight) { + _wglBindRenderbuffer(backBufferColor); + _wglRenderbufferStorage(_wGL_RGBA8, w, h); + _wglBindRenderbuffer(backBufferDepth); + _wglRenderbufferStorage(_wGL_DEPTH_COMPONENT32F, w, h); + backBufferWidth = w; + backBufferHeight = h; + } + } + public static final void setVSyncEnabled(boolean p1) { + + } + public static final void enableRepeatEvents(boolean b) { + enableRepeatEvents = b; + } + + public static boolean isPointerLocked2() { + return mouseUngrabTimeout != 0 || isPointerLocked(); + } + + @JSBody(params = { }, script = "return document.pointerLockElement != null;") + public static native boolean isPointerLocked(); + + private static boolean pointerLockFlag = false; + + public static final boolean isFocused() { + boolean yee = isPointerLocked(); + boolean dee = pointerLockFlag; + pointerLockFlag = yee; + if(!dee && yee) { + mouseDX = 0.0D; + mouseDY = 0.0D; + } + return isWindowFocused && !(dee && !yee); + } + public static final int getScreenWidth() { + return win.getScreen().getAvailWidth(); + } + public static final int getScreenHeight() { + return win.getScreen().getAvailHeight(); + } + public static final int getCanvasWidth() { + return (int)(parent.getClientWidth() * win.getDevicePixelRatio()); + } + public static final int getCanvasHeight() { + return (int)(parent.getClientHeight() * win.getDevicePixelRatio()); + } + public static final void setDisplaySize(int x, int y) { + + } + public static final void syncDisplay(int performanceToFps) { + + } + + private static final DateFormat dateFormatSS = new SimpleDateFormat("yyyy-MM-dd_HH.mm.ss"); + public static final void saveScreenshot() { + saveScreenshot("screenshot_" + dateFormatSS.format(new Date()).toString() + ".png", canvas); + } + + @JSBody(params = { "name", "cvs" }, script = "var a=document.createElement(\"a\");a.href=cvs.toDataURL(\"image/png\");a.download=name;a.click();") + private static native void saveScreenshot(String name, HTMLCanvasElement cvs); + + public static enum RateLimit { + NONE, FAILED, BLOCKED, FAILED_POSSIBLY_LOCKED, LOCKED, NOW_LOCKED; + } + + private static final Set rateLimitedAddresses = new HashSet(); + private static final Set blockedAddresses = new HashSet(); + + private static WebSocket sock = null; + private static boolean sockIsConnecting = false; + private static boolean sockIsConnected = false; + private static boolean sockIsAlive = false; + private static LinkedList readPackets = new LinkedList(); + private static RateLimit rateLimitStatus = null; + private static String currentSockURI = null; + + public static final RateLimit getRateLimitStatus() { + RateLimit l = rateLimitStatus; + rateLimitStatus = null; + return l; + } + public static final void logRateLimit(String addr, RateLimit l) { + if(l == RateLimit.BLOCKED) { + blockedAddresses.add(addr); + }else { + rateLimitedAddresses.add(addr); + } + } + public static final RateLimit checkRateLimitHistory(String addr) { + if(blockedAddresses.contains(addr)) { + return RateLimit.LOCKED; + }else if(rateLimitedAddresses.contains(addr)) { + return RateLimit.BLOCKED; + }else { + return RateLimit.NONE; + } + } + + @Async + public static native String connectWebSocket(String sockURI); + + private static void connectWebSocket(String sockURI, final AsyncCallback cb) { + sockIsConnecting = true; + sockIsConnected = false; + sockIsAlive = false; + rateLimitStatus = null; + currentSockURI = sockURI; + try { + sock = WebSocket.create(sockURI); + } catch(Throwable t) { + sockIsConnecting = false; + sockIsAlive = false; + return; + } + sock.setBinaryType("arraybuffer"); + sock.onOpen(new EventListener() { + @Override + public void handleEvent(Event evt) { + sockIsConnecting = false; + sockIsAlive = false; + sockIsConnected = true; + readPackets.clear(); + cb.complete("okay"); + } + }); + sock.onClose(new EventListener() { + @Override + public void handleEvent(CloseEvent evt) { + sock = null; + if(sockIsConnecting) { + if(rateLimitStatus == null) { + if(blockedAddresses.contains(currentSockURI)) { + rateLimitStatus = RateLimit.LOCKED; + }else if(rateLimitedAddresses.contains(currentSockURI)) { + rateLimitStatus = RateLimit.FAILED_POSSIBLY_LOCKED; + }else { + rateLimitStatus = RateLimit.FAILED; + } + } + }else if(!sockIsAlive) { + if(rateLimitStatus == null) { + if(blockedAddresses.contains(currentSockURI)) { + rateLimitStatus = RateLimit.LOCKED; + }else if(rateLimitedAddresses.contains(currentSockURI)) { + rateLimitStatus = RateLimit.BLOCKED; + } + } + } + boolean b = sockIsConnecting; + sockIsConnecting = false; + sockIsConnected = false; + sockIsAlive = false; + if(b) cb.complete("fail"); + } + }); + sock.onMessage(new EventListener() { + @Override + public void handleEvent(MessageEvent evt) { + sockIsAlive = true; + if(isString(evt.getData())) { + String stat = evt.getDataAsString(); + if(stat.equalsIgnoreCase("BLOCKED")) { + if(rateLimitStatus == null) { + rateLimitStatus = RateLimit.BLOCKED; + } + rateLimitedAddresses.add(currentSockURI); + }else if(stat.equalsIgnoreCase("LOCKED")) { + if(rateLimitStatus == null) { + rateLimitStatus = RateLimit.NOW_LOCKED; + } + rateLimitedAddresses.add(currentSockURI); + blockedAddresses.add(currentSockURI); + } + sockIsConnecting = false; + sockIsConnected = false; + sock.close(); + return; + } + Uint8Array a = Uint8Array.create(evt.getDataAsArray()); + byte[] b = new byte[a.getByteLength()]; + for(int i = 0; i < b.length; ++i) { + b[i] = (byte) (a.get(i) & 0xFF); + } + readPackets.add(b); + } + }); + } + + public static final boolean startConnection(String uri) { + String res = connectWebSocket(uri); + return "fail".equals(res) ? false : true; + } + public static final void endConnection() { + if(sock == null || sock.getReadyState() == 3) { + sockIsConnecting = false; + } + if(sock != null && !sockIsConnecting) sock.close(); + } + public static final boolean connectionOpen() { + if(sock == null || sock.getReadyState() == 3) { + sockIsConnecting = false; + } + return sock != null && !sockIsConnecting && sock.getReadyState() != 3; + } + @JSBody(params = { "sock", "buffer" }, script = "sock.send(buffer);") + private static native void nativeBinarySend(WebSocket sock, ArrayBuffer buffer); + public static final void writePacket(byte[] packet) { + if(sock != null && !sockIsConnecting) { + Uint8Array arr = Uint8Array.create(packet.length); + arr.set(packet); + nativeBinarySend(sock, arr.getBuffer()); + } + } + public static final byte[] readPacket() { + if(!readPackets.isEmpty()) { + return readPackets.remove(0); + }else { + return null; + } + } + + public static final byte[] loadLocalStorage(String key) { + String s = win.getLocalStorage().getItem("_eaglercraft_beta."+key); + if(s != null) { + return Base64.decodeBase64(s); + }else { + return null; + } + } + public static final void saveLocalStorage(String key, byte[] data) { + win.getLocalStorage().setItem("_eaglercraft_beta."+key, Base64.encodeBase64String(data)); + } + public static final void openLink(String url) { + win.open(url, "_blank"); + } + public static final void redirectTo(String url) { + Window.current().getLocation().setFullURL(url); + } + + @JSFunctor + private static interface FileChooserCallback extends JSObject { + void accept(String name, ArrayBuffer buffer); + } + + private static class FileChooserCallbackImpl implements FileChooserCallback { + + private static final FileChooserCallbackImpl instance = new FileChooserCallbackImpl(); + + @Override + public void accept(String name, ArrayBuffer buffer) { + fileChooserHasResult = true; + if(name == null) { + fileChooserResultObject = null; + }else { + Int8Array typedArray = Int8Array.create(buffer); + byte[] bytes = new byte[typedArray.getByteLength()]; + for(int i = 0; i < bytes.length; ++i) { + bytes[i] = typedArray.get(i); + } + fileChooserResultObject = new FileChooserResult(name, bytes); + } + } + + } + + private static volatile boolean fileChooserHasResult = false; + private static volatile FileChooserResult fileChooserResultObject = null; + + @JSBody(params = { "inputElement", "callback" }, script = + "if(inputElement.files.length > 0) {" + + "const value = inputElement.files[0];" + + "value.arrayBuffer().then(function(arr){ callback(value.name, arr); })" + + ".catch(function(){ callback(null, null); });" + + "} else callback(null, null);") + private static native void getFileChooserResult(HTMLInputElement inputElement, FileChooserCallback callback); + + @JSBody(params = { "inputElement", "value" }, script = "inputElement.accept = value;") + private static native void setAcceptSelection(HTMLInputElement inputElement, String value); + + @JSBody(params = { "inputElement", "enable" }, script = "inputElement.multiple = enable;") + private static native void setMultipleSelection(HTMLInputElement inputElement, boolean enable); + + public static void displayFileChooser(String mime, String ext) { + final HTMLInputElement inputElement = (HTMLInputElement) Window.current().getDocument().createElement("input"); + inputElement.setType("file"); + if(mime == null) { + setAcceptSelection(inputElement, "." + ext); + }else { + setAcceptSelection(inputElement, mime); + } + setMultipleSelection(inputElement, false); + inputElement.addEventListener("change", new EventListener() { + @Override + public void handleEvent(Event evt) { + getFileChooserResult(inputElement, FileChooserCallbackImpl.instance); + } + }); + inputElement.click(); + } + + public static boolean fileChooserHasResult() { + return fileChooserHasResult; + } + + public static FileChooserResult getFileChooserResult() { + fileChooserHasResult = false; + FileChooserResult res = fileChooserResultObject; + fileChooserResultObject = null; + return res; + } + + public static final void setListenerPos(float x, float y, float z, float vx, float vy, float vz, float pitch, float yaw) { + float var2 = MathHelper.cos(-yaw * 0.017453292F); + float var3 = MathHelper.sin(-yaw * 0.017453292F); + float var4 = -MathHelper.cos(pitch * 0.017453292F); + float var5 = MathHelper.sin(pitch * 0.017453292F); + AudioListener l = audioctx.getListener(); + l.setPosition(x, y, z); + l.setOrientation(-var3 * var4, -var5, -var2 * var4, 0.0f, 1.0f, 0.0f); + } + + private static int playbackId = 0; + private static final HashMap loadedSoundFiles = new HashMap(); + private static AudioContext audioctx = null; + private static float playbackOffsetDelay = 0.03f; + + public static final void setPlaybackOffsetDelay(float f) { + playbackOffsetDelay = f; + } + + @Async + public static native AudioBuffer decodeAudioAsync(ArrayBuffer buffer); + + private static void decodeAudioAsync(ArrayBuffer buffer, final AsyncCallback cb) { + audioctx.decodeAudioData(buffer, new DecodeSuccessCallback() { + public void onSuccess(AudioBuffer decodedData) { + cb.complete(decodedData); + } + }, new DecodeErrorCallback() { + public void onError(JSObject error) { + cb.complete(null); + } + }); + } + + public static final HashMap activeSoundEffects = new HashMap(); + + private static class AudioBufferX { + private final AudioBuffer buffer; + private AudioBufferX(AudioBuffer buffer) { + this.buffer = buffer; + } + } + + public static class AudioBufferSourceNodeX { + private final AudioBufferSourceNode source; + private final PannerNode panner; + private final GainNode gain; + private AudioBufferSourceNodeX(AudioBufferSourceNode source, PannerNode panner, GainNode gain) { + this.source = source; + this.panner = panner; + this.gain = gain; + } + } + + private static final AudioBuffer getBufferFor(String fileName) { + AudioBufferX ret = loadedSoundFiles.get(fileName); + if(ret == null) { + byte[] file = loadResourceBytes(fileName); + if(file == null) return null; + Uint8Array buf = Uint8Array.create(file.length); + buf.set(file); + ret = new AudioBufferX(decodeAudioAsync(buf.getBuffer())); + loadedSoundFiles.put(fileName, ret); + } + return ret.buffer; + } + + public static final int beginPlayback(String fileName, float x, float y, float z, float volume, float pitch) { + AudioBuffer b = getBufferFor(fileName); + if(b == null) return -1; + AudioBufferSourceNode s = audioctx.createBufferSource(); + s.setBuffer(b); + s.getPlaybackRate().setValue(pitch); + PannerNode p = audioctx.createPanner(); + p.setPosition(x, y, z); + p.setMaxDistance(volume * 16f + 0.1f); + p.setRolloffFactor(1f); + //p.setVelocity(0f, 0f, 0f); + p.setDistanceModel("linear"); + p.setPanningModel("HRTF"); + p.setConeInnerAngle(360f); + p.setConeOuterAngle(0f); + p.setConeOuterGain(0f); + p.setOrientation(0f, 1f, 0f); + GainNode g = audioctx.createGain(); + g.getGain().setValue(volume > 1.0f ? 1.0f : volume); + s.connect(g); + g.connect(p); + p.connect(audioctx.getDestination()); + s.start(0.0d, playbackOffsetDelay); + final int theId = ++playbackId; + activeSoundEffects.put(theId, new AudioBufferSourceNodeX(s, p, g)); + s.setOnEnded(new EventListener() { + + @Override + public void handleEvent(MediaEvent evt) { + activeSoundEffects.remove(theId); + } + + }); + return theId; + } + public static final int beginPlaybackStatic(String fileName, float volume, float pitch) { + AudioBuffer b = getBufferFor(fileName); + if(b == null) return -1; + AudioBufferSourceNode s = audioctx.createBufferSource(); + s.setBuffer(b); + s.getPlaybackRate().setValue(pitch); + GainNode g = audioctx.createGain(); + g.getGain().setValue(volume > 1.0f ? 1.0f : volume); + s.connect(g); + g.connect(audioctx.getDestination()); + s.start(0.0d, playbackOffsetDelay); + final int theId = ++playbackId; + activeSoundEffects.put(theId, new AudioBufferSourceNodeX(s, null, g)); + s.setOnEnded(new EventListener() { + @Override + public void handleEvent(MediaEvent evt) { + activeSoundEffects.remove(theId); + } + + }); + return playbackId; + } + public static final void setPitch(int id, float pitch) { + AudioBufferSourceNodeX b = activeSoundEffects.get(id); + if(b != null) { + b.source.getPlaybackRate().setValue(pitch); + } + } + public static final void setVolume(int id, float volume) { + AudioBufferSourceNodeX b = activeSoundEffects.get(id); + if(b != null) { + b.gain.getGain().setValue(volume); + if(b.panner != null) b.panner.setMaxDistance(volume * 16f + 0.1f); + } + } + public static float getVolume(int id) { + AudioBufferSourceNodeX b = activeSoundEffects.get(id); + if(b != null) { + return b.gain.getGain().getValue(); + } + return 0.0F; + } + public static final void moveSound(int id, float x, float y, float z, float vx, float vy, float vz) { + AudioBufferSourceNodeX b = activeSoundEffects.get(id); + if(b != null && b.panner != null) { + b.panner.setPosition(x, y, z); + //b.panner.setVelocity(vx, vy, vz); + } + } + public static final void endSound(int id) { + AudioBufferSourceNodeX b = activeSoundEffects.get(id); + if(b != null) { + b.source.stop(); + activeSoundEffects.remove(id); + } + } + public static final boolean isPlaying(int id) { + return activeSoundEffects.containsKey(id); + } + public static final void openConsole() { + + } + private static boolean connected = false; + public static final void voiceConnect(String channel) { + win.alert("voice channels are not implemented yet"); + connected = true; + } + public static final void voiceVolume(float volume) { + + } + public static final boolean voiceActive() { + return connected; + } + public static final boolean voiceRelayed() { + return connected; + } + public static final String[] voiceUsers() { + return new String[0]; + } + public static final String[] voiceUsersTalking() { + return new String[0]; + } + public static final void voiceEnd() { + connected = false; + } + public static final void doJavascriptCoroutines() { + + } + public static final long maxMemory() { + return 1024*1024*1024; + } + public static final long totalMemory() { + return 1024*1024*1024; + } + public static final long freeMemory() { + return 0l; + } + public static final void exit() { + + } + + @JSBody(params = { }, script = "return window.navigator.userAgent;") + public static native String getUserAgent(); + + private static String[] LWJGLKeyNames = new String[] {"NONE", "ESCAPE", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "MINUS", "EQUALS", "BACK", "TAB", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "LBRACKET", "RBRACKET", "RETURN", "LCONTROL", "A", "S", "D", "F", "G", "H", "J", "K", "L", "SEMICOLON", "APOSTROPHE", "GRAVE", "LSHIFT", "BACKSLASH", "Z", "X", "C", "V", "B", "N", "M", "COMMA", "PERIOD", "SLASH", "RSHIFT", "MULTIPLY", "LMENU", "SPACE", "CAPITAL", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "NUMLOCK", "SCROLL", "NUMPAD7", "NUMPAD8", "NUMPAD9", "SUBTRACT", "NUMPAD4", "NUMPAD5", "NUMPAD6", "ADD", "NUMPAD1", "NUMPAD2", "NUMPAD3", "NUMPAD0", "DECIMAL", "null", "null", "null", "F11", "F12", "null", "null", "null", "null", "null", "null", "null", "null", "null", "null", "null", "F13", "F14", "F15", "F16", "F17", "F18", "null", "null", "null", "null", "null", "null", "KANA", "F19", "null", "null", "null", "null", "null", "null", "null", "CONVERT", "null", "NOCONVERT", "null", "YEN", "null", "null", "null", "null", "null", "null", "null", "null", "null", "null", "null", "null", "null", "null", "null", "NUMPADEQUALS", "null", "null", "CIRCUMFLEX", "AT", "COLON", "UNDERLINE", "KANJI", "STOP", "AX", "UNLABELED", "null", "null", "null", "null", "NUMPADENTER", "RCONTROL", "null", "null", "null", "null", "null", "null", "null", "null", "null", "SECTION", "null", "null", "null", "null", "null", "null", "null", "null", "null", "null", "null", "NUMPADCOMMA", "null", "DIVIDE", "null", "SYSRQ", "RMENU", "null", "null", "null", "null", "null", "null", "null", "null", "null", "null", "null", "FUNCTION", "PAUSE", "null", "HOME", "UP", "PRIOR", "null", "LEFT", "null", "RIGHT", "null", "END", "DOWN", "NEXT", "INSERT", "DELETE", "null", "null", "null", "null", "null", "null", "CLEAR", "LMETA", "RMETA", "APPS", "POWER", "SLEEP", "null", "null", "null", "null", "null", "null", "null", "null", "null", "null", "null", "null", "null", "null", "null", "null", "null", "null", "null", "null", "null", "null", "null", "null", "null", "null", "null", "null", "null", "null", "null", "null"}; + + private static int[] LWJGLKeyCodes = new int[] { + /* 0 */ -1, + /* 1 */ -1, + /* 2 */ -1, + /* 3 */ -1, + /* 4 */ -1, + /* 5 */ -1, + /* 6 */ -1, + /* 7 */ -1, + /* 8 */ 14, + /* 9 */ 15, + /* 10 */ -1, + /* 11 */ -1, + /* 12 */ -1, + /* 13 */ 28, + /* 14 */ -1, + /* 15 */ -1, + /* 16 */ 42, + /* 17 */ 29, + /* 18 */ 56, + /* 19 */ -1, + /* 20 */ -1, + /* 21 */ -1, + /* 22 */ -1, + /* 23 */ -1, + /* 24 */ -1, + /* 25 */ -1, + /* 26 */ -1, + /* 27 */ 1, + /* 28 */ -1, + /* 29 */ -1, + /* 30 */ -1, + /* 31 */ -1, + /* 32 */ 57, + /* 33 */ 210, + /* 34 */ 201, + /* 35 */ 207, + /* 36 */ 199, + /* 37 */ 203, + /* 38 */ 200, + /* 39 */ 205, + /* 40 */ 208, + /* 41 */ 205, + /* 42 */ 208, + /* 43 */ -1, + /* 44 */ -1, + /* 45 */ 210, + /* 46 */ 211, + /* 47 */ 211, + /* 48 */ 11, + /* 49 */ 2, + /* 50 */ 3, + /* 51 */ 4, + /* 52 */ 5, + /* 53 */ 6, + /* 54 */ 7, + /* 55 */ 8, + /* 56 */ 9, + /* 57 */ 10, + /* 58 */ -1, + /* 59 */ -1, + /* 60 */ -1, + /* 61 */ -1, + /* 62 */ -1, + /* 63 */ -1, + /* 64 */ -1, + /* 65 */ 30, + /* 66 */ 48, + /* 67 */ 46, + /* 68 */ 32, + /* 69 */ 18, + /* 70 */ 33, + /* 71 */ 34, + /* 72 */ 35, + /* 73 */ 23, + /* 74 */ 36, + /* 75 */ 37, + /* 76 */ 38, + /* 77 */ 50, + /* 78 */ 49, + /* 79 */ 24, + /* 80 */ 25, + /* 81 */ 16, + /* 82 */ 19, + /* 83 */ 31, + /* 84 */ 20, + /* 85 */ 22, + /* 86 */ 47, + /* 87 */ 17, + /* 88 */ 45, + /* 89 */ 21, + /* 90 */ 44, + /* 91 */ -1, + /* 92 */ -1, + /* 93 */ -1, + /* 94 */ -1, + /* 95 */ -1, + /* 96 */ -1, + /* 97 */ -1, + /* 98 */ -1, + /* 99 */ -1, + /* 100 */ -1, + /* 101 */ -1, + /* 102 */ -1, + /* 103 */ -1, + /* 104 */ -1, + /* 105 */ -1, + /* 106 */ -1, + /* 107 */ -1, + /* 108 */ -1, + /* 109 */ 12, + /* 110 */ 52, + /* 111 */ 53, + /* 112 */ -1, + /* 113 */ -1, + /* 114 */ -1, + /* 115 */ -1, + /* 116 */ -1, + /* 117 */ -1, + /* 118 */ -1, + /* 119 */ -1, + /* 120 */ -1, + /* 121 */ -1, + /* 122 */ -1, + /* 123 */ -1, + /* 124 */ -1, + /* 125 */ -1, + /* 126 */ -1, + /* 127 */ -1, + /* 128 */ -1, + /* 129 */ -1, + /* 130 */ -1, + /* 131 */ -1, + /* 132 */ -1, + /* 133 */ -1, + /* 134 */ -1, + /* 135 */ -1, + /* 136 */ -1, + /* 137 */ -1, + /* 138 */ -1, + /* 139 */ -1, + /* 140 */ -1, + /* 141 */ -1, + /* 142 */ -1, + /* 143 */ -1, + /* 144 */ -1, + /* 145 */ -1, + /* 146 */ -1, + /* 147 */ -1, + /* 148 */ -1, + /* 149 */ -1, + /* 150 */ -1, + /* 151 */ -1, + /* 152 */ -1, + /* 153 */ -1, + /* 154 */ -1, + /* 155 */ -1, + /* 156 */ -1, + /* 157 */ -1, + /* 158 */ -1, + /* 159 */ -1, + /* 160 */ -1, + /* 161 */ -1, + /* 162 */ -1, + /* 163 */ -1, + /* 164 */ -1, + /* 165 */ -1, + /* 166 */ -1, + /* 167 */ -1, + /* 168 */ -1, + /* 169 */ -1, + /* 170 */ -1, + /* 171 */ -1, + /* 172 */ -1, + /* 173 */ -1, + /* 174 */ -1, + /* 175 */ -1, + /* 176 */ -1, + /* 177 */ -1, + /* 178 */ -1, + /* 179 */ -1, + /* 180 */ -1, + /* 181 */ -1, + /* 182 */ -1, + /* 183 */ -1, + /* 184 */ -1, + /* 185 */ -1, + /* 186 */ 39, + /* 187 */ 13, + /* 188 */ 51, + /* 189 */ 12, + /* 190 */ 52, + /* 191 */ 53, + /* 192 */ -1, + /* 193 */ -1, + /* 194 */ -1, + /* 195 */ -1, + /* 196 */ -1, + /* 197 */ -1, + /* 198 */ -1, + /* 199 */ -1, + /* 200 */ -1, + /* 200 */ -1, + /* 201 */ -1, + /* 202 */ -1, + /* 203 */ -1, + /* 204 */ -1, + /* 205 */ -1, + /* 206 */ -1, + /* 207 */ -1, + /* 208 */ -1, + /* 209 */ -1, + /* 210 */ -1, + /* 211 */ -1, + /* 212 */ -1, + /* 213 */ -1, + /* 214 */ -1, + /* 215 */ -1, + /* 216 */ -1, + /* 217 */ -1, + /* 218 */ -1, + /* 219 */ 26, + /* 220 */ 43, + /* 221 */ 27, + /* 222 */ 40 + }; + + public static final int _wArrayByteLength(Object obj) { + return ((Int32Array)obj).getByteLength(); + } + + public static final Object _wCreateLowLevelIntBuffer(int len) { + return Int32Array.create(len); + } + + private static int appendbufferindex = 0; + private static Int32Array appendbuffer = Int32Array.create(ArrayBuffer.create(525000*4)); + + public static final void _wAppendLowLevelBuffer(Object arr) { + Int32Array a = ((Int32Array)arr); + if(appendbufferindex + a.getLength() < appendbuffer.getLength()) { + appendbuffer.set(a, appendbufferindex); + appendbufferindex += a.getLength(); + } + } + + public static final Object _wGetLowLevelBuffersAppended() { + Int32Array ret = Int32Array.create(appendbuffer.getBuffer(), 0, appendbufferindex); + appendbufferindex = 0; + return ret; + } + + private static int remapKey(int k) { + return (k > LWJGLKeyCodes.length || k < 0) ? -1 : LWJGLKeyCodes[k]; + } + + @JSFunctor + private static interface StupidFunctionResolveString extends JSObject { + void resolveStr(String s); + } + + private static boolean unpressCTRL = false; + + @Async + public static native String getClipboard(); + + private static void getClipboard(final AsyncCallback cb) { + final long start = System.currentTimeMillis(); + getClipboard0(new StupidFunctionResolveString() { + public void resolveStr(String s) { + if(System.currentTimeMillis() - start > 500l) { + unpressCTRL = true; + } + cb.complete(s); + } + }); + } + + @JSBody(params = { "cb" }, script = "if(!window.navigator.clipboard) cb(null); else window.navigator.clipboard.readText().then(function(s) { cb(s); }, function(s) { cb(null); });") + private static native void getClipboard0(StupidFunctionResolveString cb); + + @JSBody(params = { "str" }, script = "if(window.navigator.clipboard) window.navigator.clipboard.writeText(str);") + public static native void setClipboard(String str); + + @JSBody(params = { "obj" }, script = "return typeof obj === \"string\";") + private static native boolean isString(JSObject obj); + + public static final boolean fileExists(String path) { + return IndexedDBFilesystem.fileExists(path); + } + + public static final boolean directoryExists(String path) { + return IndexedDBFilesystem.directoryExists(path); + } + + public static final boolean pathExists(String path) { + return IndexedDBFilesystem.pathExists(path); + } + + public static final void writeFile(String path, byte[] data) { + IndexedDBFilesystem.writeFile(path, data); + } + + public static final byte[] readFile(String path) { + return IndexedDBFilesystem.readFile(path); + } + + public static final long getLastModified(String path) { + return IndexedDBFilesystem.getLastModified(path); + } + + public static final int getFileSize(String path) { + return IndexedDBFilesystem.getFileSize(path); + } + + public static final void renameFile(String oldPath, String newPath) { + IndexedDBFilesystem.renameFile(oldPath, newPath); + } + + public static final void copyFile(String oldPath, String newPath) { + IndexedDBFilesystem.copyFile(oldPath, newPath); + } + + public static final void deleteFile(String path) { + IndexedDBFilesystem.deleteFile(path); + } + + public static final Collection listFiles(String path, boolean listDirs, boolean recursiveDirs) { + return IndexedDBFilesystem.listFiles(path, listDirs, recursiveDirs); + } + + public static final Collection listFilesAndDirectories(String path) { + return listFiles(path, true, false); + } + + public static final Collection listFilesRecursive(String path) { + return listFiles(path, false, true); + } + + public static class FileEntry { + + public final String path; + public final boolean isDirectory; + public final long lastModified; + + public FileEntry(String path, boolean isDirectory, long lastModified) { + this.path = path; + this.isDirectory = isDirectory; + this.lastModified = lastModified; + } + + public String getName() { + int i = path.indexOf('/'); + if(i >= 0) { + return path.substring(i + 1); + }else { + return path; + } + } + + } + + private static String stripPath(String str) { + if(str.startsWith("/")) { + str = str.substring(1); + } + if(str.endsWith("/")) { + str = str.substring(0, str.length() - 1); + } + return str; + } + + @JSBody(params = { "name", "cvs" }, script = "var a=document.createElement(\"a\");a.href=URL.createObjectURL(new Blob([cvs],{type:\"application/octet-stream\"}));a.download=name;a.click();URL.revokeObjectURL(a.href);") + private static native void downloadFile0(String name, ArrayBuffer cvs); + + public static final void downloadFile(String filename, byte[] data) { + Uint8Array b = Uint8Array.create(data.length); + b.set(data); + downloadFile0(filename, b.getBuffer()); + } + + public static boolean isCompressed(byte[] b) { + if(b == null || b.length < 2) { + return false; + } + return (b[0] == (byte) 0x1F) && (b[1] == (byte) 0x8B); + } + + public static boolean escapeKeyPressed() { + return getEventKey() == 1 || escapeKeyPressed2(); + } + + @JSBody(params = {}, script = "return document.esc;") + public static native boolean escapeKeyPressed2(); + +} \ No newline at end of file diff --git a/src/net/lax1dude/eaglercraft/adapter/FileChooserResult.java b/src/net/lax1dude/eaglercraft/adapter/FileChooserResult.java new file mode 100644 index 0000000..5ec8561 --- /dev/null +++ b/src/net/lax1dude/eaglercraft/adapter/FileChooserResult.java @@ -0,0 +1,26 @@ +package net.lax1dude.eaglercraft.adapter; + +/** + * Copyright (c) 2022-2023 LAX1DUDE. All Rights Reserved. + * + * WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES + * NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED + * TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE + * SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR. + * + * NOT FOR COMMERCIAL OR MALICIOUS USE + * + * (please read the 'LICENSE' file this repo's root directory for more info) + * + */ +public class FileChooserResult { + + public final String fileName; + public final byte[] fileData; + + public FileChooserResult(String fileName, byte[] fileData) { + this.fileName = fileName; + this.fileData = fileData; + } + +} \ No newline at end of file diff --git a/src/net/lax1dude/eaglercraft/adapter/teavm/BufferConverter.java b/src/net/lax1dude/eaglercraft/adapter/teavm/BufferConverter.java new file mode 100644 index 0000000..6125bc7 --- /dev/null +++ b/src/net/lax1dude/eaglercraft/adapter/teavm/BufferConverter.java @@ -0,0 +1,34 @@ +package net.lax1dude.eaglercraft.adapter.teavm; + +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; +import java.nio.ShortBuffer; + +public class BufferConverter { + + public static final byte[] convertByteBuffer(ByteBuffer b) { + byte[] ret = new byte[b.limit() - b.position()]; + b.get(ret); + return ret; + } + + public static final short[] convertShortBuffer(ShortBuffer b) { + short[] ret = new short[b.limit() - b.position()]; + b.get(ret); + return ret; + } + + public static final int[] convertIntBuffer(IntBuffer b) { + int[] ret = new int[b.limit() - b.position()]; + b.get(ret); + return ret; + } + + public static final float[] convertFloatBuffer(FloatBuffer b) { + float[] ret = new float[b.limit() - b.position()]; + b.get(ret); + return ret; + } + +} diff --git a/src/net/lax1dude/eaglercraft/adapter/teavm/IDBObjectStorePatched.java b/src/net/lax1dude/eaglercraft/adapter/teavm/IDBObjectStorePatched.java new file mode 100644 index 0000000..d4e8403 --- /dev/null +++ b/src/net/lax1dude/eaglercraft/adapter/teavm/IDBObjectStorePatched.java @@ -0,0 +1,79 @@ +package net.lax1dude.eaglercraft.adapter.teavm; + +import org.teavm.jso.JSBody; +import org.teavm.jso.JSObject; +import org.teavm.jso.JSProperty; +import org.teavm.jso.core.JSString; +import org.teavm.jso.indexeddb.IDBCountRequest; +import org.teavm.jso.indexeddb.IDBCursorRequest; +import org.teavm.jso.indexeddb.IDBCursorSource; +import org.teavm.jso.indexeddb.IDBDatabase; +import org.teavm.jso.indexeddb.IDBGetRequest; +import org.teavm.jso.indexeddb.IDBIndex; +import org.teavm.jso.indexeddb.IDBKeyRange; +import org.teavm.jso.indexeddb.IDBObjectStoreParameters; +import org.teavm.jso.indexeddb.IDBRequest; +import org.teavm.jso.indexeddb.IDBTransaction; + +public abstract class IDBObjectStorePatched implements JSObject, IDBCursorSource { + + @JSBody(params = { "db", "name", "optionalParameters" }, script = "return db.createObjectStore(name, optionalParameters);") + public static native IDBObjectStorePatched createObjectStorePatch(IDBDatabase db, String name, IDBObjectStoreParameters optionalParameters); + + @JSBody(params = { "tx", "name" }, script = "return tx.objectStore(name);") + public static native IDBObjectStorePatched objectStorePatch(IDBTransaction tx, String name); + + @JSProperty + public abstract String getName(); + + @JSProperty("keyPath") + abstract JSObject getKeyPathImpl(); + + public final String[] getKeyPath() { + JSObject result = getKeyPathImpl(); + if (JSString.isInstance(result)) { + return new String[] { result.cast().stringValue() }; + } else { + return unwrapStringArray(result); + } + } + + @JSBody(params = { "obj" }, script = "return this;") + private static native String[] unwrapStringArray(JSObject obj); + + @JSProperty + public abstract String[] getIndexNames(); + + @JSProperty + public abstract boolean isAutoIncrement(); + + public abstract IDBRequest put(JSObject value, JSObject key); + + public abstract IDBRequest put(JSObject value); + + public abstract IDBRequest add(JSObject value, JSObject key); + + public abstract IDBRequest add(JSObject value); + + public abstract IDBRequest delete(JSObject key); + + public abstract IDBGetRequest get(JSObject key); + + public abstract IDBRequest clear(); + + public abstract IDBCursorRequest openCursor(); + + public abstract IDBCursorRequest openCursor(IDBKeyRange range); + + public abstract IDBIndex createIndex(String name, String key); + + public abstract IDBIndex createIndex(String name, String[] keys); + + public abstract IDBIndex index(String name); + + public abstract void deleteIndex(String name); + + public abstract IDBCountRequest count(); + + public abstract IDBCountRequest count(JSObject key); +} \ No newline at end of file diff --git a/src/net/lax1dude/eaglercraft/adapter/teavm/IndexedDBFilesystem.java b/src/net/lax1dude/eaglercraft/adapter/teavm/IndexedDBFilesystem.java new file mode 100644 index 0000000..7a72269 --- /dev/null +++ b/src/net/lax1dude/eaglercraft/adapter/teavm/IndexedDBFilesystem.java @@ -0,0 +1,414 @@ +package net.lax1dude.eaglercraft.adapter.teavm; + +import java.util.Collection; +import java.util.LinkedList; + +import org.teavm.interop.Async; +import org.teavm.interop.AsyncCallback; +import org.teavm.jso.JSBody; +import org.teavm.jso.JSObject; +import org.teavm.jso.dom.events.EventListener; +import org.teavm.jso.indexeddb.EventHandler; +import org.teavm.jso.indexeddb.IDBCountRequest; +import org.teavm.jso.indexeddb.IDBCursor; +import org.teavm.jso.indexeddb.IDBCursorRequest; +import org.teavm.jso.indexeddb.IDBDatabase; +import org.teavm.jso.indexeddb.IDBFactory; +import org.teavm.jso.indexeddb.IDBGetRequest; +import org.teavm.jso.indexeddb.IDBObjectStoreParameters; +import org.teavm.jso.indexeddb.IDBOpenDBRequest; +import org.teavm.jso.indexeddb.IDBRequest; +import org.teavm.jso.indexeddb.IDBTransaction; +import org.teavm.jso.indexeddb.IDBVersionChangeEvent; +import org.teavm.jso.typedarrays.ArrayBuffer; +import org.teavm.jso.typedarrays.Uint8Array; + +import net.PeytonPlayz585.opengl.GL11; +import net.lax1dude.eaglercraft.adapter.EaglerAdapterImpl2; +import net.lax1dude.eaglercraft.adapter.EaglerAdapterImpl2.FileEntry; + +public class IndexedDBFilesystem { + + public static enum OpenState { + OPENED, LOCKED, ERROR + } + + private static String err = ""; + private static IDBDatabase db = null; + + public static final OpenState initialize() { + DatabaseOpen dbo; + if(GL11.dataBaseName != null) { + System.out.println("Setting custom database name to " + GL11.dataBaseName); + dbo = AsyncHandlers.openDB(GL11.dataBaseName); + } else { + dbo = AsyncHandlers.openDB("_net_lax1dude_eaglercraft_beta_IndexedDBFilesystem_1_3"); + } + if(dbo == null) { + err = "Unknown Error"; + return OpenState.ERROR; + } + if(dbo.failedLocked) { + return OpenState.LOCKED; + } + if(dbo.failedInit || dbo.database == null) { + err = dbo.failedError == null ? "Initialization Failed" : dbo.failedError; + return OpenState.ERROR; + } + db = dbo.database; + return OpenState.OPENED; + } + + public static final String errorDetail() { + return err; + } + + public static final boolean fileExists(String path) { + return AsyncHandlers.fileGetType(db, path) == FileExists.FILE; + } + + public static final boolean directoryExists(String path) { + return AsyncHandlers.fileGetType(db, path) == FileExists.DIRECTORY; + } + + public static final boolean pathExists(String path) { + return AsyncHandlers.fileExists(db, path).bool; + } + + private static final void mkdir(String dir) { + if(directoryExists(dir)) { + return; + } + int i = dir.lastIndexOf('/'); + if(i > 0) { + mkdir(dir.substring(0, i)); + } + AsyncHandlers.writeWholeFile(db, dir, true, ArrayBuffer.create(0)); + } + + public static final void writeFile(String path, byte[] data) { + int i = path.lastIndexOf('/'); + if(i > 0) { + mkdir(path.substring(0, i)); + } + Uint8Array arr = Uint8Array.create(data.length); + arr.set(data); + AsyncHandlers.writeWholeFile(db, path, false, arr.getBuffer()); + } + + public static final byte[] readFile(String path) { + ArrayBuffer arr = AsyncHandlers.readWholeFile(db, path); + if(arr == null) { + return null; + } + byte[] data = new byte[arr.getByteLength()]; + Uint8Array arrr = Uint8Array.create(arr); + for(int i = 0; i < data.length; ++i) { + data[i] = (byte) arrr.get(i); + } + return data; + } + + public static final long getLastModified(String path) { + int lm = AsyncHandlers.fileGetLastModified(db, path); + return lm == -1 ? -1l : AsyncHandlers.eaglercraftEpoch + lm; + } + + public static final int getFileSize(String path) { + ArrayBuffer arr = AsyncHandlers.readWholeFile(db, path); + if(arr == null) { + return -1; + }else { + return arr.getByteLength(); + } + } + + public static final void renameFile(String oldPath, String newPath) { + copyFile(oldPath, newPath); + AsyncHandlers.deleteFile(db, oldPath); + } + + public static final void copyFile(String oldPath, String newPath) { + ArrayBuffer arr = AsyncHandlers.readWholeFile(db, oldPath); + int i = newPath.lastIndexOf('/'); + if(i > 0) { + mkdir(newPath.substring(0, i)); + } + AsyncHandlers.writeWholeFile(db, newPath, false, arr); + } + + public static final void deleteFile(String path) { + AsyncHandlers.deleteFile(db, path); + } + + public static final Collection listFiles(String path, boolean listDirs, boolean recursiveDirs) { + LinkedList lst = new LinkedList(); + AsyncHandlers.iterateFiles(db, path, listDirs, recursiveDirs, lst); + return lst; + } + + protected static class BooleanResult { + + protected static final BooleanResult TRUE = new BooleanResult(true); + protected static final BooleanResult FALSE = new BooleanResult(false); + + protected final boolean bool; + + private BooleanResult(boolean b) { + bool = b; + } + + protected static BooleanResult _new(boolean b) { + return b ? TRUE : FALSE; + } + + } + + protected static class DatabaseOpen { + + protected final boolean failedInit; + protected final boolean failedLocked; + protected final String failedError; + + protected final IDBDatabase database; + + protected DatabaseOpen(boolean init, boolean locked, String error, IDBDatabase db) { + failedInit = init; + failedLocked = locked; + failedError = error; + database = db; + } + + } + + protected static enum FileExists { + FILE, DIRECTORY, FALSE + } + + @JSBody(script = "return ((typeof indexedDB) !== 'undefined') ? indexedDB : null;") + protected static native IDBFactory createIDBFactory(); + + protected static class AsyncHandlers { + + protected static final long eaglercraftEpoch = 1645568542000l; + + @Async + protected static native DatabaseOpen openDB(String name); + + private static void openDB(String name, final AsyncCallback cb) { + IDBFactory i = createIDBFactory(); + if(i == null) { + cb.complete(new DatabaseOpen(false, false, "window.indexedDB was null or undefined", null)); + return; + } + final IDBOpenDBRequest f = i.open(name, 1); + f.setOnBlocked(new EventHandler() { + public void handleEvent() { + cb.complete(new DatabaseOpen(false, true, null, null)); + } + }); + f.setOnSuccess(new EventHandler() { + public void handleEvent() { + cb.complete(new DatabaseOpen(false, false, null, f.getResult())); + } + }); + f.setOnError(new EventHandler() { + public void handleEvent() { + cb.complete(new DatabaseOpen(true, false, "open error", null)); + } + }); + f.setOnUpgradeNeeded(new EventListener() { + public void handleEvent(IDBVersionChangeEvent evt) { + IDBObjectStorePatched.createObjectStorePatch(f.getResult(), "filesystem", IDBObjectStoreParameters.create().keyPath("path")); + } + }); + } + + @Async + protected static native BooleanResult deleteFile(IDBDatabase db, String name); + + private static void deleteFile(IDBDatabase db, String name, final AsyncCallback cb) { + IDBTransaction tx = db.transaction("filesystem", "readwrite"); + final IDBRequest r = IDBObjectStorePatched.objectStorePatch(tx, "filesystem").delete(makeTheFuckingKeyWork(name)); + + r.setOnSuccess(new EventHandler() { + public void handleEvent() { + cb.complete(BooleanResult._new(true)); + } + }); + r.setOnError(new EventHandler() { + public void handleEvent() { + cb.complete(BooleanResult._new(false)); + } + }); + } + + @JSBody(params = { "obj" }, script = "return (typeof obj === 'undefined') ? null : ((typeof obj.data === 'undefined') ? null : obj.data);") + protected static native ArrayBuffer readRow(JSObject obj); + + @JSBody(params = { "obj" }, script = "return (typeof obj === 'undefined') ? false : ((typeof obj.directory === 'undefined') ? false : obj.directory);") + protected static native boolean isRowDirectory(JSObject obj); + + @JSBody(params = { "obj" }, script = "return (typeof obj === 'undefined') ? -1 : ((typeof obj.lastModified === 'undefined') ? -1 : obj.lastModified);") + protected static native int readLastModified(JSObject obj); + + @JSBody(params = { "obj" }, script = "return [obj];") + private static native JSObject makeTheFuckingKeyWork(String k); + + @Async + protected static native ArrayBuffer readWholeFile(IDBDatabase db, String name); + + private static void readWholeFile(IDBDatabase db, String name, final AsyncCallback cb) { + IDBTransaction tx = db.transaction("filesystem", "readonly"); + final IDBGetRequest r = IDBObjectStorePatched.objectStorePatch(tx, "filesystem").get(makeTheFuckingKeyWork(name)); + r.setOnSuccess(new EventHandler() { + public void handleEvent() { + cb.complete(isRowDirectory(r.getResult()) ? null : readRow(r.getResult())); + } + }); + r.setOnError(new EventHandler() { + public void handleEvent() { + cb.complete(null); + } + }); + + } + + @Async + protected static native Integer readLastModified(IDBDatabase db, String name); + + private static void readLastModified(IDBDatabase db, String name, final AsyncCallback cb) { + IDBTransaction tx = db.transaction("filesystem", "readonly"); + final IDBGetRequest r = IDBObjectStorePatched.objectStorePatch(tx, "filesystem").get(makeTheFuckingKeyWork(name)); + r.setOnSuccess(new EventHandler() { + public void handleEvent() { + cb.complete(readLastModified(r.getResult())); + } + }); + r.setOnError(new EventHandler() { + public void handleEvent() { + cb.complete(-1); + } + }); + + } + + @JSBody(params = { "k" }, script = "return ((typeof k) === \"string\") ? k : (((typeof k) === \"undefined\") ? null : (((typeof k[0]) === \"string\") ? k[0] : null));") + private static native String readKey(JSObject k); + + @Async + protected static native Integer iterateFiles(IDBDatabase db, final String prefix, final boolean listDirs, final boolean recursiveDirs, final Collection lst); + + private static void iterateFiles(IDBDatabase db, final String prefix, final boolean listDirs, final boolean recursiveDirs, final Collection lst, final AsyncCallback cb) { + IDBTransaction tx = db.transaction("filesystem", "readonly"); + final IDBCursorRequest r = IDBObjectStorePatched.objectStorePatch(tx, "filesystem").openCursor(); + final int[] res = new int[1]; + r.setOnSuccess(new EventHandler() { + public void handleEvent() { + IDBCursor c = r.getResult(); + if(c == null || c.getKey() == null || c.getValue() == null) { + cb.complete(res[0]); + return; + } + String k = readKey(c.getKey()); + if(k != null) { + if(k.startsWith(prefix)) { + if(recursiveDirs || k.indexOf('/', prefix.length() + 1) == -1) { + boolean dir = isRowDirectory(c.getValue()); + if(dir) { + if(listDirs) { + lst.add(new EaglerAdapterImpl2.FileEntry(k, true, -1)); + } + }else { + lst.add(new EaglerAdapterImpl2.FileEntry(k, false, eaglercraftEpoch + readLastModified(c.getValue()))); + } + } + } + } + c.doContinue(); + } + }); + r.setOnError(new EventHandler() { + public void handleEvent() { + cb.complete(res[0] > 0 ? res[0] : -1); + } + }); + } + + @Async + protected static native BooleanResult fileExists(IDBDatabase db, String name); + + private static void fileExists(IDBDatabase db, String name, final AsyncCallback cb) { + IDBTransaction tx = db.transaction("filesystem", "readonly"); + final IDBCountRequest r = IDBObjectStorePatched.objectStorePatch(tx, "filesystem").count(makeTheFuckingKeyWork(name)); + r.setOnSuccess(new EventHandler() { + public void handleEvent() { + cb.complete(BooleanResult._new(r.getResult() > 0)); + } + }); + r.setOnError(new EventHandler() { + public void handleEvent() { + cb.complete(BooleanResult._new(false)); + } + }); + } + + @Async + protected static native Integer fileGetLastModified(IDBDatabase db, String name); + + private static void fileGetLastModified(IDBDatabase db, String name, final AsyncCallback cb) { + IDBTransaction tx = db.transaction("filesystem", "readonly"); + final IDBGetRequest r = IDBObjectStorePatched.objectStorePatch(tx, "filesystem").get(makeTheFuckingKeyWork(name)); + r.setOnSuccess(new EventHandler() { + public void handleEvent() { + cb.complete(readLastModified(r.getResult())); + } + }); + r.setOnError(new EventHandler() { + public void handleEvent() { + cb.complete(-1); + } + }); + } + + @Async + protected static native FileExists fileGetType(IDBDatabase db, String name); + + private static void fileGetType(IDBDatabase db, String name, final AsyncCallback cb) { + IDBTransaction tx = db.transaction("filesystem", "readonly"); + final IDBGetRequest r = IDBObjectStorePatched.objectStorePatch(tx, "filesystem").get(makeTheFuckingKeyWork(name)); + r.setOnSuccess(new EventHandler() { + public void handleEvent() { + cb.complete(r.getResult() == null ? FileExists.FALSE : (isRowDirectory(r.getResult()) ? FileExists.DIRECTORY : FileExists.FILE)); + } + }); + r.setOnError(new EventHandler() { + public void handleEvent() { + cb.complete(FileExists.FALSE); + } + }); + } + + @JSBody(params = { "pat", "dir", "lm", "dat" }, script = "return { path: pat, directory: dir, lastModified: lm, data: dat };") + protected static native JSObject writeRow(String name, boolean directory, int lm, ArrayBuffer data); + + @Async + protected static native BooleanResult writeWholeFile(IDBDatabase db, String name, boolean directory, ArrayBuffer data); + + private static void writeWholeFile(IDBDatabase db, String name, boolean directory, ArrayBuffer data, final AsyncCallback cb) { + IDBTransaction tx = db.transaction("filesystem", "readwrite"); + final IDBRequest r = IDBObjectStorePatched.objectStorePatch(tx, "filesystem").put(writeRow(name, directory, (int)(System.currentTimeMillis() - eaglercraftEpoch), data)); + r.setOnSuccess(new EventHandler() { + public void handleEvent() { + cb.complete(BooleanResult._new(true)); + } + }); + r.setOnError(new EventHandler() { + public void handleEvent() { + cb.complete(BooleanResult._new(false)); + } + }); + } + } + +} \ No newline at end of file diff --git a/src/net/lax1dude/eaglercraft/adapter/teavm/WebGL2RenderingContext.java b/src/net/lax1dude/eaglercraft/adapter/teavm/WebGL2RenderingContext.java new file mode 100644 index 0000000..91c0487 --- /dev/null +++ b/src/net/lax1dude/eaglercraft/adapter/teavm/WebGL2RenderingContext.java @@ -0,0 +1,42 @@ +package net.lax1dude.eaglercraft.adapter.teavm; + +import org.teavm.jso.webgl.WebGLRenderingContext; + +public interface WebGL2RenderingContext extends WebGLRenderingContext { + + int TEXTURE_MAX_LEVEL = 0x0000813D; + int TEXTURE_MAX_ANISOTROPY_EXT = 0x000084FE; + int UNSIGNED_INT_24_8 = 0x000084FA; + int ANY_SAMPLES_PASSED = 0x00008D6A; + int QUERY_RESULT = 0x00008866; + int QUERY_RESULT_AVAILABLE = 0x00008867; + int DEPTH24_STENCIL8 = 0x000088F0; + int DEPTH_COMPONENT32F = 0x00008CAC; + int READ_FRAMEBUFFER = 0x00008CA8; + int DRAW_FRAMEBUFFER = 0x00008CA9; + int RGB8 = 0x00008051; + int RGBA8 = 0x00008058; + + WebGLQuery createQuery(); + + void beginQuery(int p1, WebGLQuery obj); + + void endQuery(int p1); + + void deleteQuery(WebGLQuery obj); + + int getQueryParameter(WebGLQuery obj, int p2); + + WebGLVertexArray createVertexArray(); + + void deleteVertexArray(WebGLVertexArray obj); + + void bindVertexArray(WebGLVertexArray obj); + + void renderbufferStorageMultisample(int p1, int p2, int p3, int p4, int p5); + + void blitFramebuffer(int p1, int p2, int p3, int p4, int p5, int p6, int p7, int p8, int p9, int p10); + + void drawBuffers(int[] p1); + +} diff --git a/src/net/lax1dude/eaglercraft/adapter/teavm/WebGLQuery.java b/src/net/lax1dude/eaglercraft/adapter/teavm/WebGLQuery.java new file mode 100644 index 0000000..40c65c0 --- /dev/null +++ b/src/net/lax1dude/eaglercraft/adapter/teavm/WebGLQuery.java @@ -0,0 +1,6 @@ +package net.lax1dude.eaglercraft.adapter.teavm; + +import org.teavm.jso.JSObject; + +public interface WebGLQuery extends JSObject { +} diff --git a/src/net/lax1dude/eaglercraft/adapter/teavm/WebGLVertexArray.java b/src/net/lax1dude/eaglercraft/adapter/teavm/WebGLVertexArray.java new file mode 100644 index 0000000..34d1c5f --- /dev/null +++ b/src/net/lax1dude/eaglercraft/adapter/teavm/WebGLVertexArray.java @@ -0,0 +1,6 @@ +package net.lax1dude.eaglercraft.adapter.teavm; + +import org.teavm.jso.JSObject; + +public interface WebGLVertexArray extends JSObject { +}