static void loadTextureImage() {
	BitmapImage * image = NULL;
	
	if (texture != NULL) {
		texture->dispose(texture);
		texture = NULL;
	}
	
	if (jsonPath != NULL) {
		JSONDeserializationContext * context;
		
		context = JSONDeserializationContext_createWithFile(jsonPath);
		texture = GLTexture_deserialize(context);
		if (texture == NULL) {
			fprintf(stderr, "Error: Couldn't load %s as gltexture.json file: %d\n", jsonPath, context->status);
			
		} else {
			char imagePath[PATH_MAX];
			
			if (jsonImagePath == NULL) {
				size_t charIndex;
				
				strncpy(imagePath, jsonPath, PATH_MAX);
				for (charIndex = strlen(imagePath) - 1; charIndex > 0; charIndex--) {
					if (imagePath[charIndex] == '/') {
						charIndex++;
						break;
					}
				}
				strncpy(imagePath + charIndex, texture->imageName, PATH_MAX - charIndex);
				
			} else {
				snprintf(imagePath, PATH_MAX, "%s/%s", jsonImagePath, texture->imageName);
			}
			image = PNGImageIO_loadPNGFile(imagePath, PNG_PIXEL_FORMAT_AUTOMATIC, true);
			if (image == NULL) {
				fprintf(stderr, "Error: Couldn't load %s as gltexture.json file\n", imagePath);
				texture->dispose(texture);
				texture = NULL;
			}
		}
	}
	
	if (texture == NULL) {
		image = PNGImageIO_loadPNGFile(resourcePath(textureImages[textureIndex].fileName), PNG_PIXEL_FORMAT_AUTOMATIC, true);
		texture = GLTexture_create(textureImages[textureIndex].dataFormat,
		                           GL_UNSIGNED_BYTE,
		                           minFilters[minFilterIndex],
		                           magFilters[magFilterIndex],
		                           wrapModes[wrapSModeIndex],
		                           wrapModes[wrapTModeIndex],
		                           autoBlendModes[autoBlendModeIndex],
		                           autoMipmap,
		                           anisotropicFilter);
	}
	
	texture->setImage(texture, 0, image->width, image->height, image->bytesPerRow, image->pixels);
	image->dispose(image);
}
static void loadTextureImage() {
	BitmapImage * image;
	
	if (texture != NULL) {
		texture->dispose(texture);
	}
	image = PNGImageIO_loadPNGFile(resourcePath(textureImages[textureIndex].fileName), PNG_PIXEL_FORMAT_AUTOMATIC, true);
	texture = GLTexture_create(textureImages[textureIndex].dataFormat,
	                           GL_UNSIGNED_BYTE, // TODO: Test data formats other than GL_UNSIGNED_BYTE somehow
	                           minFilters[minFilterIndex],
	                           magFilters[magFilterIndex],
	                           wrapModes[wrapSModeIndex],
	                           wrapModes[wrapTModeIndex],
	                           autoBlendModes[autoBlendModeIndex],
	                           autoMipmap,
	                           anisotropicFilter);
	texture->setImage(texture, 0, image->width, image->height, image->bytesPerRow, image->pixels);
	image->dispose(image);
}
void Target_init() {
	JSONDeserializationContext * context;
	GLTexture * texture;
	BitmapImage * image;
	
	context = JSONDeserializationContext_createWithFile(resourcePath("test_font.json"));
	if (context->status != SERIALIZATION_ERROR_OK) {
		fprintf(stderr, "Fatal error: Couldn't load test_font.json (status %d)\n", context->status);
		exit(EXIT_FAILURE);
	}
	font = GLBitmapFont_deserialize((DeserializationContext *) context);
	context->dispose(context);
	if (font == NULL) {
		fprintf(stderr, "Fatal error: Couldn't deserialize test_font.json (status %d)\n", context->status);
		exit(EXIT_FAILURE);
	}
	context = JSONDeserializationContext_createWithFile(resourcePath(font->textureName));
	if (context->status != SERIALIZATION_ERROR_OK) {
		fprintf(stderr, "Fatal error: Couldn't load %s (status %d)\n", font->textureName, context->status);
		exit(EXIT_FAILURE);
	}
	texture = GLTexture_deserialize((DeserializationContext *) context);
	context->dispose(context);
	if (texture == NULL) {
		fprintf(stderr, "Fatal error: Couldn't deserialize %s (status %d)\n", font->textureName, context->status);
		exit(EXIT_FAILURE);
	}
	image = PNGImageIO_loadPNGFile(resourcePath(texture->imageName), PNG_PIXEL_FORMAT_AUTOMATIC, true);
	if (image == NULL) {
		fprintf(stderr, "Fatal error: Couldn't load %s\n", texture->imageName);
		exit(EXIT_FAILURE);
	}
	texture->setImage(texture, 0, image->width, image->height, image->bytesPerRow, image->pixels);
	image->dispose(image);
	font->setTexture(font, texture, true);
	
	memset(freeformText, 0, FREEFORM_LENGTH_MAX + 1);
	
	if (GLGraphics_getOpenGLAPIVersion() == GL_API_VERSION_ES2) {
		void * fileContents;
		size_t fileLength;
		GLint shaderLength;
		GLuint vertexShader, fragmentShader;
		GLuint shaderProgram;
		GLint logLength;
		
		shaderProgram = glCreateProgram();
		vertexShader = glCreateShader(GL_VERTEX_SHADER);
		fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
		
		fileContents = readFileSimple(resourcePath("basic.vert"), &fileLength);
		shaderLength = fileLength;
		glShaderSource(vertexShader, 1, (const GLchar **) &fileContents, &shaderLength);
		free(fileContents);
		
		fileContents = readFileSimple(resourcePath("basic.frag"), &fileLength);
		shaderLength = fileLength;
		glShaderSource(fragmentShader, 1, (const GLchar **) &fileContents, &shaderLength);
		free(fileContents);
		
		glCompileShader(vertexShader);
		glCompileShader(fragmentShader);
		glGetShaderiv(vertexShader, GL_INFO_LOG_LENGTH, &logLength);
		if (logLength > 0) {
			GLchar * log = malloc(logLength);
			glGetShaderInfoLog(vertexShader, logLength, &logLength, log);
			fprintf(stderr, "Vertex shader compile log:\n%s\n", log);
			free(log);
		}
		glGetShaderiv(fragmentShader, GL_INFO_LOG_LENGTH, &logLength);
		if (logLength > 0) {
			GLchar * log = malloc(logLength);
			glGetShaderInfoLog(fragmentShader, logLength, &logLength, log);
			fprintf(stderr, "Fragment shader compile log:\n%s\n", log);
			free(log);
		}
		glAttachShader(shaderProgram, vertexShader);
		glAttachShader(shaderProgram, fragmentShader);
		glBindAttribLocation(shaderProgram, 0, "vertexPositionAttrib");
		glBindAttribLocation(shaderProgram, 1, "vertexTexCoordAttrib");
		glBindAttribLocation(shaderProgram, 2, "vertexColorAttrib");
		glLinkProgram(shaderProgram);
		glGetProgramiv(shaderProgram, GL_INFO_LOG_LENGTH, &logLength);
		if (logLength > 0) {
			GLchar * log = malloc(logLength);
			glGetProgramInfoLog(shaderProgram, logLength, &logLength, log);
			fprintf(stderr, "Program link log:\n%s\n", log);
			free(log);
		}
		glValidateProgram(shaderProgram);
		glGetProgramiv(shaderProgram, GL_INFO_LOG_LENGTH, &logLength);
		if (logLength > 0) {
			GLchar * log = malloc(logLength);
			glGetProgramInfoLog(shaderProgram, logLength, &logLength, log);
			fprintf(stderr, "Program validation log:\n%s\n", log);
			free(log);
		}
		matrixUniform = glGetUniformLocation(shaderProgram, "matrix");
		textureUniform = glGetUniformLocation(shaderProgram, "texture");
		glDeleteShader(vertexShader);
		glDeleteShader(fragmentShader);
		glUseProgram(shaderProgram);
	}
	
	Shell_mainLoop();
}