/*
Minetest
Copyright (C) 2010-2015 celeron55, Perttu Ahola <celeron55@gmail.com>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/


#include "mapgen.h"
#include "voxel.h"
#include "noise.h"
#include "mapblock.h"
#include "mapnode.h"
#include "map.h"
//#include "serverobject.h"
#include "content_sao.h"
#include "nodedef.h"
#include "voxelalgorithms.h"
//#include "profiler.h" // For TimeTaker
#include "settings.h" // For g_settings
#include "emerge.h"
#include "dungeongen.h"
#include "cavegen.h"
#include "treegen.h"
#include "mg_ore.h"
#include "mg_decoration.h"
#include "mapgen_v6.h"


FlagDesc flagdesc_mapgen_v6[] = {
	{"jungles",    MGV6_JUNGLES},
	{"biomeblend", MGV6_BIOMEBLEND},
	{"mudflow",    MGV6_MUDFLOW},
	{"snowbiomes", MGV6_SNOWBIOMES},
	{"flat",       MGV6_FLAT},
	{"trees",      MGV6_TREES},
	{NULL,         0}
};


/////////////////////////////////////////////////////////////////////////////


MapgenV6::MapgenV6(int mapgenid, MapgenParams *params, EmergeManager *emerge)
	: Mapgen(mapgenid, params, emerge)
{
	this->m_emerge = emerge;
	this->ystride = csize.X; //////fix this

	this->heightmap = new s16[csize.X * csize.Z];

	MapgenV6Params *sp = (MapgenV6Params *)params->sparams;
	this->spflags     = sp->spflags;
	this->freq_desert = sp->freq_desert;
	this->freq_beach  = sp->freq_beach;

	np_cave        = &sp->np_cave;
	np_humidity    = &sp->np_humidity;
	np_trees       = &sp->np_trees;
	np_apple_trees = &sp->np_apple_trees;

	//// Create noise objects
	noise_terrain_base   = new Noise(&sp->np_terrain_base,   seed, csize.X, csize.Y);
	noise_terrain_higher = new Noise(&sp->np_terrain_higher, seed, csize.X, csize.Y);
	noise_steepness      = new Noise(&sp->np_steepness,      seed, csize.X, csize.Y);
	noise_height_select  = new Noise(&sp->np_height_select,  seed, csize.X, csize.Y);
	noise_mud            = new Noise(&sp->np_mud,            seed, csize.X, csize.Y);
	noise_beach          = new Noise(&sp->np_beach,          seed, csize.X, csize.Y);
	noise_biome          = new Noise(&sp->np_biome,          seed,
			csize.X + 2 * MAP_BLOCKSIZE, csize.Y + 2 * MAP_BLOCKSIZE);
	noise_humidity       = new Noise(&sp->np_humidity,       seed,
			csize.X + 2 * MAP_BLOCKSIZE, csize.Y + 2 * MAP_BLOCKSIZE);

	//// Resolve nodes to be used
	INodeDefManager *ndef = emerge->ndef;

	c_stone           = ndef->getId("mapgen_stone");
	c_dirt            = ndef->getId("mapgen_dirt");
	c_dirt_with_grass = ndef->getId("mapgen_dirt_with_grass");
	c_sand            = ndef->getId("mapgen_sand");
	c_water_source    = ndef->getId("mapgen_water_source");
	c_lava_source     = ndef->getId("mapgen_lava_source");
	c_gravel          = ndef->getId("mapgen_gravel");
	c_desert_stone    = ndef->getId("mapgen_desert_stone");
	c_desert_sand     = ndef->getId("mapgen_desert_sand");
	c_dirt_with_snow  = ndef->getId("mapgen_dirt_with_snow");
	c_snow            = ndef->getId("mapgen_snow");
	c_snowblock       = ndef->getId("mapgen_snowblock");
	c_ice             = ndef->getId("mapgen_ice");

	c_cobble          = ndef->getId("mapgen_cobble");
	c_stair_cobble    = ndef->getId("mapgen_stair_cobble");
	c_mossycobble     = ndef->getId("mapgen_mossycobble");

	if (c_desert_sand == CONTENT_IGNORE)
		c_desert_sand = c_sand;
	if (c_desert_stone == CONTENT_IGNORE)
		c_desert_stone = c_stone;
	if (c_mossycobble == CONTENT_IGNORE)
		c_mossycobble = c_cobble;
	if (c_stair_cobble == CONTENT_IGNORE)
		c_stair_cobble = c_cobble;
	if (c_dirt_with_snow == CONTENT_IGNORE)
		c_dirt_with_snow = c_dirt_with_grass;
	if (c_snow == CONTENT_IGNORE)
		c_snow = CONTENT_AIR;
	if (c_snowblock == CONTENT_IGNORE)
		c_snowblock = c_dirt_with_grass;
	if (c_ice == CONTENT_IGNORE)
		c_ice = c_water_source;
}


MapgenV6::~MapgenV6()
{
	delete noise_terrain_base;
	delete noise_terrain_higher;
	delete noise_steepness;
	delete noise_height_select;
	delete noise_mud;
	delete noise_beach;
	delete noise_biome;
	delete noise_humidity;

	delete[] heightmap;
}


MapgenV6Params::MapgenV6Params()
{
	spflags     = MGV6_BIOMEBLEND | MGV6_MUDFLOW;
	freq_desert = 0.45;
	freq_beach  = 0.15;

	np_terrain_base   = NoiseParams(-4,   20.0, v3f(250.0, 250.0, 250.0), 82341,  5, 0.6,  2.0);
	np_terrain_higher = NoiseParams(20,   16.0, v3f(500.0, 500.0, 500.0), 85039,  5, 0.6,  2.0);
	np_steepness      = NoiseParams(0.85, 0.5,  v3f(125.0, 125.0, 125.0), -932,   5, 0.7,  2.0);
	np_height_select  = NoiseParams(0,    1.0,  v3f(250.0, 250.0, 250.0), 4213,   5, 0.69, 2.0);
	np_mud            = NoiseParams(4,    2.0,  v3f(200.0, 200.0, 200.0), 91013,  3, 0.55, 2.0);
	np_beach          = NoiseParams(0,    1.0,  v3f(250.0, 250.0, 250.0), 59420,  3, 0.50, 2.0);
	np_biome          = NoiseParams(0,    1.0,  v3f(500.0, 500.0, 500.0), 9130,   3, 0.50, 2.0);
	np_cave           = NoiseParams(6,    6.0,  v3f(250.0, 250.0, 250.0), 34329,  3, 0.50, 2.0);
	np_humidity       = NoiseParams(0.5,  0.5,  v3f(500.0, 500.0, 500.0), 72384,  3, 0.50, 2.0);
	np_trees          = NoiseParams(0,    1.0,  v3f(125.0, 125.0, 125.0), 2,      4, 0.66, 2.0);
	np_apple_trees    = NoiseParams(0,    1.0,  v3f(100.0, 100.0, 100.0), 342902, 3, 0.45, 2.0);
}


void MapgenV6Params::readParams(const Settings *settings)
{
	settings->getFlagStrNoEx("mgv6_spflags", spflags, flagdesc_mapgen_v6);
	settings->getFloatNoEx("mgv6_freq_desert", freq_desert);
	settings->getFloatNoEx("mgv6_freq_beach",  freq_beach);

	settings->getNoiseParams("mgv6_np_terrain_base",   np_terrain_base);
	settings->getNoiseParams("mgv6_np_terrain_higher", np_terrain_higher);
	settings->getNoiseParams("mgv6_np_steepness",      np_steepness);
	settings->getNoiseParams("mgv6_np_height_select",  np_height_select);
	settings->getNoiseParams("mgv6_np_mud",            np_mud);
	settings->getNoiseParams("mgv6_np_beach",          np_beach);
	settings->getNoiseParams("mgv6_np_biome",          np_biome);
	settings->getNoiseParams("mgv6_np_cave",           np_cave);
	settings->getNoiseParams("mgv6_np_humidity",       np_humidity);
	settings->getNoiseParams("mgv6_np_trees",          np_trees);
	settings->getNoiseParams("mgv6_np_apple_trees",    np_apple_trees);
}


void MapgenV6Params::writeParams(Settings *settings) const
{
	settings->setFlagStr("mgv6_spflags", spflags, flagdesc_mapgen_v6, U32_MAX);
	settings->setFloat("mgv6_freq_desert", freq_desert);
	settings->setFloat("mgv6_freq_beach",  freq_beach);

	settings->setNoiseParams("mgv6_np_terrain_base",   np_terrain_base);
	settings->setNoiseParams("mgv6_np_terrain_higher", np_terrain_higher);
	settings->setNoiseParams("mgv6_np_steepness",      np_steepness);
	settings->setNoiseParams("mgv6_np_height_select",  np_height_select);
	settings->setNoiseParams("mgv6_np_mud",            np_mud);
	settings->setNoiseParams("mgv6_np_beach",          np_beach);
	settings->setNoiseParams("mgv6_np_biome",          np_biome);
	settings->setNoiseParams("mgv6_np_cave",           np_cave);
	settings->setNoiseParams("mgv6_np_humidity",       np_humidity);
	settings->setNoiseParams("mgv6_np_trees",          np_trees);
	settings->setNoiseParams("mgv6_np_apple_trees",    np_apple_trees);
}


//////////////////////// Some helper functions for the map generator

// Returns Y one under area minimum if not found
s16 MapgenV6::find_stone_level(v2s16 p2d)
{
	v3s16 em = vm->m_area.getExtent();
	s16 y_nodes_max = vm->m_area.MaxEdge.Y;
	s16 y_nodes_min = vm->m_area.MinEdge.Y;
	u32 i = vm->m_area.index(p2d.X, y_nodes_max, p2d.Y);
	s16 y;

	for (y = y_nodes_max; y >= y_nodes_min; y--) {
		content_t c = vm->m_data[i].getContent();
		if (c != CONTENT_IGNORE && (c == c_stone || c == c_desert_stone))
			break;

		vm->m_area.add_y(em, i, -1);
	}
	return (y >= y_nodes_min) ? y : y_nodes_min - 1;
}


// Required by mapgen.h
bool MapgenV6::block_is_underground(u64 seed, v3s16 blockpos)
{
	/*s16 minimum_groundlevel = (s16)get_sector_minimum_ground_level(
			seed, v2s16(blockpos.X, blockpos.Z));*/
	// Nah, this is just a heuristic, just return something
	s16 minimum_groundlevel = water_level;

	if(blockpos.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE <= minimum_groundlevel)
		return true;
	else
		return false;
}


//////////////////////// Base terrain height functions

float MapgenV6::baseTerrainLevel(float terrain_base, float terrain_higher,
	float steepness, float height_select)
{
	float base   = 1 + terrain_base;
	float higher = 1 + terrain_higher;

	// Limit higher ground level to at least base
	if(higher < base)
		higher = base;

	// Steepness factor of cliffs
	float b = steepness;
	b = rangelim(b, 0.0, 1000.0);
	b = 5 * b * b * b * b * b * b * b;
	b = rangelim(b, 0.5, 1000.0);

	// Values 1.5...100 give quite horrible looking slopes
	if (b > 1.5 && b < 100.0)
		b = (b < 10.0) ? 1.5 : 100.0;

	float a_off = -0.20; // Offset to more low
	float a = 0.5 + b * (a_off + height_select);
	a = rangelim(a, 0.0, 1.0); // Limit

	return base * (1.0 - a) + higher * a;
}


float MapgenV6::baseTerrainLevelFromNoise(v2s16 p)
{
	if ((spflags & MGV6_FLAT) || (flags & MG_FLAT))
		return water_level;

	float terrain_base   = NoisePerlin2D_PO(&noise_terrain_base->np,
							p.X, 0.5, p.Y, 0.5, seed);
	float terrain_higher = NoisePerlin2D_PO(&noise_terrain_higher->np,
							p.X, 0.5, p.Y, 0.5, seed);
	float steepness      = NoisePerlin2D_PO(&noise_steepness->np,
							p.X, 0.5, p.Y, 0.5, seed);
	float height_select  = NoisePerlin2D_PO(&noise_height_select->np,
							p.X, 0.5, p.Y, 0.5, seed);

	return baseTerrainLevel(terrain_base, terrain_higher,
							steepness, height_select);
}


float MapgenV6::baseTerrainLevelFromMap(v2s16 p)
{
	int index = (p.Y - node_min.Z) * ystride + (p.X - node_min.X);
	return baseTerrainLevelFromMap(index);
}


float MapgenV6::baseTerrainLevelFromMap(int index)
{
	if ((spflags & MGV6_FLAT) || (flags & MG_FLAT))
		return water_level;

	float terrain_base   = noise_terrain_base->result[index];
	float terrain_higher = noise_terrain_higher->result[index];
	float steepness      = noise_steepness->result[index];
	float height_select  = noise_height_select->result[index];

	return baseTerrainLevel(terrain_base, terrain_higher,
							steepness, height_select);
}


s16 MapgenV6::find_ground_level_from_noise(u64 seed, v2s16 p2d, s16 precision)
{
	return baseTerrainLevelFromNoise(p2d) + MGV6_AVERAGE_MUD_AMOUNT;
}


int MapgenV6::getGroundLevelAtPoint(v2s16 p)
{
	return baseTerrainLevelFromNoise(p) + MGV6_AVERAGE_MUD_AMOUNT;
}


int MapgenV6::getSpawnLevelAtPoint(v2s16 p)
{
	s16 level_at_point = baseTerrainLevelFromNoise(p) + MGV6_AVERAGE_MUD_AMOUNT;
	if (level_at_point <= water_level ||
			level_at_point > water_level + 16)
		return MAX_MAP_GENERATION_LIMIT;  // Unsuitable spawn point
	else
		return level_at_point;
}


//////////////////////// Noise functions

float MapgenV6::getMudAmount(v2s16 p)
{
	int index = (p.Y - node_min.Z) * ystride + (p.X - node_min.X);
	return getMudAmount(index);
}


bool MapgenV6::getHaveBeach(v2s16 p)
{
	int index = (p.Y - node_min.Z) * ystride + (p.X - node_min.X);
	return getHaveBeach(index);
}


BiomeV6Type MapgenV6::getBiome(v2s16 p)
{
	int index = (p.Y - full_node_min.Z) * (ystride + 2 * MAP_BLOCKSIZE)
			+ (p.X - full_node_min.X);
	return getBiome(index, p);
}


float MapgenV6::getHumidity(v2s16 p)
{
	/*double noise = noise2d_perlin(
		0.5+(float)p.X/500, 0.5+(float)p.Y/500,
		seed+72384, 4, 0.66);
	noise = (noise + 1.0)/2.0;*/

	int index = (p.Y - full_node_min.Z) * (ystride + 2 * MAP_BLOCKSIZE)
			+ (p.X - full_node_min.X);
	float noise = noise_humidity->result[index];

	if (noise < 0.0)
		noise = 0.0;
	if (noise > 1.0)
		noise = 1.0;
	return noise;
}


float MapgenV6::getTreeAmount(v2s16 p)
{
	/*double noise = noise2d_perlin(
			0.5+(float)p.X/125, 0.5+(float)p.Y/125,
			seed+2, 4, 0.66);*/

	float noise = NoisePerlin2D(np_trees, p.X, p.Y, seed);
	float zeroval = -0.39;
	if (noise < zeroval)
		return 0;
	else
		return 0.04 * (noise - zeroval) / (1.0 - zeroval);
}


bool MapgenV6::getHaveAppleTree(v2s16 p)
{
	/*is_apple_tree = noise2d_perlin(
		0.5+(float)p.X/10<span class="hl com">/*</span>
<span class="hl com">Minetest</span>
<span class="hl com">Copyright (C) 2014 celeron55, Perttu Ahola &lt;celeron55&#64;gmail.com&gt;</span>
<span class="hl com"></span>
<span class="hl com">This program is free software; you can redistribute it and/or modify</span>
<span class="hl com">it under the terms of the GNU Lesser General Public License as published by</span>
<span class="hl com">the Free Software Foundation; either version 2.1 of the License, or</span>
<span class="hl com">(at your option) any later version.</span>
<span class="hl com"></span>
<span class="hl com">This program is distributed in the hope that it will be useful,</span>
<span class="hl com">but WITHOUT ANY WARRANTY; without even the implied warranty of</span>
<span class="hl com">MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the</span>
<span class="hl com">GNU Lesser General Public License for more details.</span>
<span class="hl com"></span>
<span class="hl com">You should have received a copy of the GNU Lesser General Public License along</span>
<span class="hl com">with this program; if not, write to the Free Software Foundation, Inc.,</span>
<span class="hl com">51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.</span>
<span class="hl com">*/</span>

<span class="hl ppc">#ifndef __ANDROID__</span>
<span class="hl ppc">#error This file may only be compiled for android!</span>
<span class="hl ppc">#endif</span>

<span class="hl ppc">#include</span> <span class="hl pps">&quot;porting.h&quot;</span><span class="hl ppc"></span>
<span class="hl ppc">#include</span> <span class="hl pps">&quot;porting_android.h&quot;</span><span class="hl ppc"></span>
<span class="hl ppc">#include</span> <span class="hl pps">&quot;config.h&quot;</span><span class="hl ppc"></span>
<span class="hl ppc">#include</span> <span class="hl pps">&quot;filesys.h&quot;</span><span class="hl ppc"></span>
<span class="hl ppc">#include</span> <span class="hl pps">&quot;log.h&quot;</span><span class="hl ppc"></span>
<span class="hl ppc">#include &lt;sstream&gt;</span>

<span class="hl ppc">#ifdef GPROF</span>
<span class="hl ppc">#include</span> <span class="hl pps">&quot;prof.h&quot;</span><span class="hl ppc"></span>
<span class="hl ppc">#endif</span>

<span class="hl kwc">extern</span> <span class="hl kwb">int</span> <span class="hl kwd">main</span><span class="hl opt">(</span><span class="hl kwb">int</span> argc<span class="hl opt">,</span> <span class="hl kwb">char</span> <span class="hl opt">*</span>argv<span class="hl opt">[]);</span>

<span class="hl kwb">void</span> <span class="hl kwd">android_main</span><span class="hl opt">(</span>android_app <span class="hl opt">*</span>app<span class="hl opt">)</span>
<span class="hl opt">{</span>
	<span class="hl kwb">int</span> retval <span class="hl opt">=</span> <span class="hl num">0</span><span class="hl opt">;</span>
	<span class="hl kwc">porting</span><span class="hl opt">::</span>app_global <span class="hl opt">=</span> app<span class="hl opt">;</span>

	<span class="hl kwc">porting</span><span class="hl opt">::</span><span class="hl kwd">setThreadName</span><span class="hl opt">(</span><span class="hl str">&quot;MainThread&quot;</span><span class="hl opt">);</span>

	<span class="hl kwa">try</span> <span class="hl opt">{</span>
		<span class="hl kwd">app_dummy</span><span class="hl opt">();</span>
		<span class="hl kwb">char</span> <span class="hl opt">*</span>argv<span class="hl opt">[] = { (</span><span class="hl kwb">char</span><span class="hl opt">*)</span> <span class="hl str">&quot;minetest&quot;</span> <span class="hl opt">};</span>
		<span class="hl kwd">main</span><span class="hl opt">(</span><span class="hl kwa">sizeof</span><span class="hl opt">(</span>argv<span class="hl opt">) /</span> <span class="hl kwa">sizeof</span><span class="hl opt">(</span>argv<span class="hl opt">[</span><span class="hl num">0</span><span class="hl opt">]),</span> argv<span class="hl opt">);</span>
		<span class="hl opt">}</span>
	<span class="hl kwa">catch</span><span class="hl opt">(</span>BaseException e<span class="hl opt">) {</span>
		<span class="hl kwc">std</span><span class="hl opt">::</span>stringstream msg<span class="hl opt">;</span>
		msg <span class="hl opt">&lt;&lt;</span> <span class="hl str">&quot;Exception handled by main: &quot;</span> <span class="hl opt">&lt;&lt;</span> e<span class="hl opt">.</span><span class="hl kwd">what</span><span class="hl opt">();</span>
		<span class="hl kwb">const char</span><span class="hl opt">*</span> message <span class="hl opt">=</span> msg<span class="hl opt">.</span><span class="hl kwd">str</span><span class="hl opt">().</span><span class="hl kwd">c_str</span><span class="hl opt">();</span>
		<span class="hl kwd">__android_log_print</span><span class="hl opt">(</span>ANDROID_LOG_ERROR<span class="hl opt">,</span> PROJECT_NAME<span class="hl opt">,</span> <span class="hl str">&quot;%s&quot;</span><span class="hl opt">,</span> message<span class="hl opt">);</span>
		errorstream <span class="hl opt">&lt;&lt;</span> msg <span class="hl opt">&lt;&lt;</span> <span class="hl kwc">std</span><span class="hl opt">::</span>endl<span class="hl opt">;</span>
		retval <span class="hl opt">= -</span><span class="hl num">1</span><span class="hl opt">;</span>
	<span class="hl opt">}</span>
	<span class="hl kwa">catch</span><span class="hl opt">(...) {</span>
		<span class="hl kwd">__android_log_print</span><span class="hl opt">(</span>ANDROID_LOG_ERROR<span class="hl opt">,</span> PROJECT_NAME<span class="hl opt">,</span>
				<span class="hl str">&quot;Some exception occured&quot;</span><span class="hl opt">);</span>
		errorstream <span class="hl opt">&lt;&lt;</span> <span class="hl str">&quot;Uncaught exception in main thread!&quot;</span> <span class="hl opt">&lt;&lt;</span> <span class="hl kwc">std</span><span class="hl opt">::</span>endl<span class="hl opt">;</span>
		retval <span class="hl opt">= -</span><span class="hl num">1</span><span class="hl opt">;</span>
	<span class="hl opt">}</span>

	<span class="hl kwc">porting</span><span class="hl opt">::</span><span class="hl kwd">cleanupAndroid</span><span class="hl opt">();</span>
	errorstream <span class="hl opt">&lt;&lt;</span> <span class="hl str">&quot;Shutting down minetest.&quot;</span> <span class="hl opt">&lt;&lt;</span> <span class="hl kwc">std</span><span class="hl opt">::</span>endl<span class="hl opt">;</span>
	<span class="hl kwd">exit</span><span class="hl opt">(</span>retval<span class="hl opt">);</span>
<span class="hl opt">}</span>

<span class="hl com">/* handler for finished message box input */</span>
<span class="hl com">/* Intentionally NOT in namespace porting */</span>
<span class="hl com">/* TODO this doesn&apos;t work as expected, no idea why but there&apos;s a workaround   */</span>
<span class="hl com">/* for it right now */</span>
<span class="hl kwc">extern</span> <span class="hl str">&quot;C&quot;</span> <span class="hl opt">{</span>
	JNIEXPORT <span class="hl kwb">void</span> JNICALL <span class="hl kwd">Java_net_minetest_MtNativeActivity_putMessageBoxResult</span><span class="hl opt">(</span>
			JNIEnv <span class="hl opt">*</span> env<span class="hl opt">,</span> jclass thiz<span class="hl opt">,</span> jstring text<span class="hl opt">)</span>
	<span class="hl opt">{</span>
		errorstream <span class="hl opt">&lt;&lt;</span> <span class="hl str">&quot;Java_net_minetest_MtNativeActivity_putMessageBoxResult got: &quot;</span>
				<span class="hl opt">&lt;&lt;</span> <span class="hl kwc">std</span><span class="hl opt">::</span><span class="hl kwd">string</span><span class="hl opt">((</span><span class="hl kwb">const char</span><span class="hl opt">*)</span>env<span class="hl opt">-&gt;</span><span class="hl kwd">GetStringChars</span><span class="hl opt">(</span>text<span class="hl opt">,</span><span class="hl num">0</span><span class="hl opt">))</span>
				<span class="hl opt">&lt;&lt;</span> <span class="hl kwc">std</span><span class="hl opt">::</span>endl<span class="hl opt">;</span>
	<span class="hl opt">}</span>
<span class="hl opt">}</span>

<span class="hl kwa">namespace</span> porting <span class="hl opt">{</span>

<span class="hl kwc">std</span><span class="hl opt">::</span>string path_storage <span class="hl opt">=</span> DIR_DELIM <span class="hl str">&quot;sdcard&quot;</span> DIR_DELIM<span class="hl opt">;</span>

android_app<span class="hl opt">*</span> app_global<span class="hl opt">;</span>
JNIEnv<span class="hl opt">*</span>      jnienv<span class="hl opt">;</span>
jclass       nativeActivity<span class="hl opt">;</span>

jclass <span class="hl kwd">findClass</span><span class="hl opt">(</span><span class="hl kwc">std</span><span class="hl opt">::</span>string classname<span class="hl opt">)</span>
<span class="hl opt">{</span>
	<span class="hl kwa">if</span> <span class="hl opt">(</span>jnienv <span class="hl opt">==</span> <span class="hl num">0</span><span class="hl opt">) {</span>
		<span class="hl kwa">return</span> <span class="hl num">0</span><span class="hl opt">;</span>
	<span class="hl opt">}</span>

	jclass nativeactivity <span class="hl opt">=</span> jnienv<span class="hl opt">-&gt;</span><span class="hl kwd">FindClass</span><span class="hl opt">(</span><span class="hl str">&quot;android/app/NativeActivity&quot;</span><span class="hl opt">);</span>
	jmethodID getClassLoader <span class="hl opt">=</span>
			jnienv<span class="hl opt">-&gt;</span><span class="hl kwd">GetMethodID</span><span class="hl opt">(</span>nativeactivity<span class="hl opt">,</span><span class="hl str">&quot;getClassLoader&quot;</span><span class="hl opt">,</span>
					<span class="hl str">&quot;()Ljava/lang/ClassLoader;&quot;</span><span class="hl opt">);</span>
	jobject cls <span class="hl opt">=</span>
			jnienv<span class="hl opt">-&gt;</span><span class="hl kwd">CallObjectMethod</span><span class="hl opt">(</span>app_global<span class="hl opt">-&gt;</span>activity<span class="hl opt">-&gt;</span>clazz<span class="hl opt">,</span> getClassLoader<span class="hl opt">);</span>
	jclass classLoader <span class="hl opt">=</span> jnienv<span class="hl opt">-&gt;</span><span class="hl kwd">FindClass</span><span class="hl opt">(</span><span class="hl str">&quot;java/lang/ClassLoader&quot;</span><span class="hl opt">);</span>
	jmethodID findClass <span class="hl opt">=</span>
			jnienv<span class="hl opt">-&gt;</span><span class="hl kwd">GetMethodID</span><span class="hl opt">(</span>classLoader<span class="hl opt">,</span> <span class="hl str">&quot;loadClass&quot;</span><span class="hl opt">,</span>
					<span class="hl str">&quot;(Ljava/lang/String;)Ljava/lang/Class;&quot;</span><span class="hl opt">);</span>
	jstring strClassName <span class="hl opt">=</span>
			jnienv<span class="hl opt">-&gt;</span><span class="hl kwd">NewStringUTF</span><span class="hl opt">(</span>classname<span class="hl opt">.</span><span class="hl kwd">c_str</span><span class="hl opt">());</span>
	<span class="hl kwa">return</span> <span class="hl opt">(</span>jclass<span class="hl opt">)</span> jnienv<span class="hl opt">-&gt;</span><span class="hl kwd">CallObjectMethod</span><span class="hl opt">(</span>cls<span class="hl opt">,</span> findClass<span class="hl opt">,</span> strClassName<span class="hl opt">);</span>
<span class="hl opt">}</span>

<span class="hl kwb">void</span> <span class="hl kwd">copyAssets</span><span class="hl opt">()</span>
<span class="hl opt">{</span>
	jmethodID assetcopy <span class="hl opt">=</span> jnienv<span class="hl opt">-&gt;</span><span class="hl kwd">GetMethodID</span><span class="hl opt">(</span>nativeActivity<span class="hl opt">,</span><span class="hl str">&quot;copyAssets&quot;</span><span class="hl opt">,</span><span class="hl str">&quot;()V&quot;</span><span class="hl opt">);</span>

	<span class="hl kwa">if</span> <span class="hl opt">(</span>assetcopy <span class="hl opt">==</span> <span class="hl num">0</span><span class="hl opt">) {</span>
		<span class="hl kwa">assert</span><span class="hl opt">(</span><span class="hl str">&quot;porting::copyAssets unable to find copy assets method&quot;</span> <span class="hl opt">==</span> <span class="hl num">0</span><span class="hl opt">);</span>
	<span class="hl opt">}</span>

	jnienv<span class="hl opt">-&gt;</span><span class="hl kwd">CallVoidMethod</span><span class="hl opt">(</span>app_global<span class="hl opt">-&gt;</span>activity<span class="hl opt">-&gt;</span>clazz<span class="hl opt">,</span> assetcopy<span class="hl opt">);</span>
<span class="hl opt">}</span>

<span class="hl kwb">void</span> <span class="hl kwd">initAndroid</span><span class="hl opt">()</span>
<span class="hl opt">{</span>
	<span class="hl kwc">porting</span><span class="hl opt">::</span>jnienv <span class="hl opt">=</span> NULL<span class="hl opt">;</span>
	JavaVM <span class="hl opt">*</span>jvm <span class="hl opt">=</span> app_global<span class="hl opt">-&gt;</span>activity<span class="hl opt">-&gt;</span>vm<span class="hl opt">;</span>
	JavaVMAttachArgs lJavaVMAttachArgs<span class="hl opt">;</span>
	lJavaVMAttachArgs<span class="hl opt">.</span>version <span class="hl opt">=</span> JNI_VERSION_1_6<span class="hl opt">;</span>
	lJavaVMAttachArgs<span class="hl opt">.</span>name <span class="hl opt">=</span> <span class="hl str">&quot;MinetestNativeThread&quot;</span><span class="hl opt">;</span>
	lJavaVMAttachArgs<span class="hl opt">.</span>group <span class="hl opt">=</span> NULL<span class="hl opt">;</span>
<span class="hl ppc">#ifdef NDEBUG</span>
	<span class="hl slc">// This is a ugly hack as arm v7a non debuggable builds crash without this</span>
	<span class="hl slc">// printf ... if someone finds out why please fix it!</span>
	infostream <span class="hl opt">&lt;&lt;</span> <span class="hl str">&quot;Attaching native thread. &quot;</span> <span class="hl opt">&lt;&lt;</span> <span class="hl kwc">std</span><span class="hl opt">::</span>endl<span class="hl opt">;</span>
<span class="hl ppc">#endif</span>
	<span class="hl kwa">if</span> <span class="hl opt">(</span> jvm<span class="hl opt">-&gt;</span><span class="hl kwd">AttachCurrentThread</span><span class="hl opt">(&amp;</span><span class="hl kwc">porting</span><span class="hl opt">::</span>jnienv<span class="hl opt">, &amp;</span>lJavaVMAttachArgs<span class="hl opt">) ==</span> JNI_ERR<span class="hl opt">) {</span>
		errorstream <span class="hl opt">&lt;&lt;</span> <span class="hl str">&quot;Failed to attach native thread to jvm&quot;</span> <span class="hl opt">&lt;&lt;</span> <span class="hl kwc">std</span><span class="hl opt">::</span>endl<span class="hl opt">;</span>
		<span class="hl kwd">exit</span><span class="hl opt">(-</span><span class="hl num">1</span><span class="hl opt">);</span>
	<span class="hl opt">}</span>

	nativeActivity <span class="hl opt">=</span> <span class="hl kwd">findClass</span><span class="hl opt">(</span><span class="hl str">&quot;net/minetest/minetest/MtNativeActivity&quot;</span><span class="hl opt">);</span>
	<span class="hl kwa">if</span> <span class="hl opt">(</span>nativeActivity <span class="hl opt">==</span> <span class="hl num">0</span><span class="hl opt">) {</span>
		errorstream <span class="hl opt">&lt;&lt;</span>
			<span class="hl str">&quot;porting::initAndroid unable to find java native activity class&quot;</span> <span class="hl opt">&lt;&lt;</span>
			<span class="hl kwc">std</span><span class="hl opt">::</span>endl<span class="hl opt">;</span>
	<span class="hl opt">}</span>

<span class="hl ppc">#ifdef GPROF</span>
	<span class="hl com">/* in the start-up code */</span>
	<span class="hl kwd">__android_log_print</span><span class="hl opt">(</span>ANDROID_LOG_ERROR<span class="hl opt">,</span> PROJECT_NAME<span class="hl opt">,</span>
			<span class="hl str">&quot;Initializing GPROF profiler&quot;</span><span class="hl opt">);</span>
	<span class="hl kwd">monstartup</span><span class="hl opt">(</span><span class="hl str">&quot;libminetest.so&quot;</span><span class="hl opt">);</span>
<span class="hl ppc">#endif</span>
<span class="hl opt">}</span>

<span class="hl kwb">void</span> <span class="hl kwd">cleanupAndroid</span><span class="hl opt">()</span>
<span class="hl opt">{</span>

<span class="hl ppc">#ifdef GPROF</span>
	errorstream <span class="hl opt">&lt;&lt;</span> <span class="hl str">&quot;Shutting down GPROF profiler&quot;</span> <span class="hl opt">&lt;&lt;</span> <span class="hl kwc">std</span><span class="hl opt">::</span>endl<span class="hl opt">;</span>
	<span class="hl kwd">setenv</span><span class="hl opt">(</span><span class="hl str">&quot;CPUPROFILE&quot;</span><span class="hl opt">, (</span>path_user <span class="hl opt">+</span> DIR_DELIM <span class="hl opt">+</span> <span class="hl str">&quot;gmon.out&quot;</span><span class="hl opt">).</span><span class="hl kwd">c_str</span><span class="hl opt">(),</span> <span class="hl num">1</span><span class="hl opt">);</span>
	<span class="hl kwd">moncleanup</span><span class="hl opt">();</span>
<span class="hl ppc">#endif</span>

	JavaVM <span class="hl opt">*</span>jvm <span class="hl opt">=</span> app_global<span class="hl opt">-&gt;</span>activity<span class="hl opt">-&gt;</span>vm<span class="hl opt">;</span>
	jvm<span class="hl opt">-&gt;</span><span class="hl kwd">DetachCurrentThread</span><span class="hl opt">();</span>
<span class="hl opt">}</span>

<span class="hl kwb">void</span> <span class="hl kwd">setExternalStorageDir</span><span class="hl opt">(</span>JNIEnv<span class="hl opt">*</span> lJNIEnv<span class="hl opt">)</span>
<span class="hl opt">{</span>
	<span class="hl slc">// Android: Retrieve ablsolute path to external storage device (sdcard)</span>
	jclass ClassEnv      <span class="hl opt">=</span> lJNIEnv<span class="hl opt">-&gt;</span><span class="hl kwd">FindClass</span><span class="hl opt">(</span><span class="hl str">&quot;android/os/Environment&quot;</span><span class="hl opt">);</span>
	jmethodID MethodDir  <span class="hl opt">=</span>
			lJNIEnv<span class="hl opt">-&gt;</span><span class="hl kwd">GetStaticMethodID</span><span class="hl opt">(</span>ClassEnv<span class="hl opt">,</span>
					<span class="hl str">&quot;getExternalStorageDirectory&quot;</span><span class="hl opt">,</span><span class="hl str">&quot;()Ljava/io/File;&quot;</span><span class="hl opt">);</span>
	jobject ObjectFile   <span class="hl opt">=</span> lJNIEnv<span class="hl opt">-&gt;</span><span class="hl kwd">CallStaticObjectMethod</span><span class="hl opt">(</span>ClassEnv<span class="hl opt">,</span> MethodDir<span class="hl opt">);</span>
	jclass ClassFile     <span class="hl opt">=</span> lJNIEnv<span class="hl opt">-&gt;</span><span class="hl kwd">FindClass</span><span class="hl opt">(</span><span class="hl str">&quot;java/io/File&quot;</span><span class="hl opt">);</span>

	jmethodID MethodPath <span class="hl opt">=</span>
			lJNIEnv<span class="hl opt">-&gt;</span><span class="hl kwd">GetMethodID</span><