code:
-- Minetest: builtin/static_spawn.lua

local function warn_invalid_static_spawnpoint()
	if core.settings:get("static_spawnpoint") and
			not core.setting_get_pos("static_spawnpoint") then
		core.log("error", "The static_spawnpoint setting is invalid: \""..
				core.settings:get("static_spawnpoint").."\"")
	end
end

warn_invalid_static_spawnpoint()

local function put_player_in_spawn(player_obj)
	local static_spawnpoint = core.setting_get_pos("static_spawnpoint")
	if not static_spawnpoint then
		return false
	end
	core.log("action", "Moving " .. player_obj:get_player_name() ..
		" to static spawnpoint at " .. core.pos_to_string(static_spawnpoint))
	player_obj:setpos(static_spawnpoint)
	return true
end

core.register_on_newplayer(put_player_in_spawn)
core.register_on_respawnplayer(put_player_in_spawn)


code:
VoxelArea = {
	MinEdge = {x=1, y=1, z=1},
	MaxEdge = {x=0, y=0, z=0},
	ystride = 0,
	zstride = 0,
}

function VoxelArea:new(o)
	o = o or {}
	setmetatable(o, self)
	self.__index = self

	local e = o:getExtent()
	o.ystride = e.x
	o.zstride = e.x * e.y

	return o
end

function VoxelArea:getExtent()
	local MaxEdge, MinEdge = self.MaxEdge, self.MinEdge
	return {
		x = MaxEdge.x - MinEdge.x + 1,
		y = MaxEdge.y - MinEdge.y + 1,
		z = MaxEdge.z - MinEdge.z + 1,
	}
end

function VoxelArea:getVolume()
	local e = self:getExtent()
	return e.x * e.y * e.z
end

function VoxelArea:index(x, y, z)
	local MinEdge = self.MinEdge
	local i = (z - MinEdge.z) * self.zstride +
			  (y - MinEdge.y) * self.ystride +
			  (x - MinEdge.x) + 1
	return math.floor(i)
end

function VoxelArea:indexp(p)
	local MinEdge = self.MinEdge
	local i = (p.z - MinEdge.z) * self.zstride +
			  (p.y - MinEdge.y) * self.ystride +
			  (p.x - MinEdge.x) + 1
	return math.floor(i)
end

function VoxelArea:position(i)
	local p = {}
	local MinEdge = self.MinEdge

	i = i - 1

	p.z = math.floor(i / self.zstride) + MinEdge.z
	i = i % self.zstride

	p.y = math.floor(i / self.ystride) + MinEdge.y
	i = i % self.ystride

	p.x = math.floor(i) + MinEdge.x

	return p
end

function VoxelArea:contains(x, y, z)
	local MaxEdge, MinEdge = self.MaxEdge, self.MinEdge
	return (x >= MinEdge.x) and (x <= MaxEdge.x) and
		   (y >= MinEdge.y) and (y <= MaxEdge.y) and
		   (z >= MinEdge.z) and (z <= MaxEdge.z)
end

function VoxelArea:containsp(p)
	local MaxEdge, MinEdge = self.MaxEdge, self.MinEdge
	return (p.x >= MinEdge.x) and (p.x <= MaxEdge.x) and
		   (p.y >= MinEdge.y) and (p.y <= MaxEdge.y) and
		   (p.z >= MinEdge.z) and (p.z <= MaxEdge.z)
end

function VoxelArea:containsi(i)
	return (i >= 1) and (i <= self:getVolume())
end

function VoxelArea:iter(minx, miny, minz, maxx, maxy, maxz)
	local i = self:index(minx, miny, minz) - 1
	local xrange = maxx - minx + 1
	local nextaction = i + 1 + xrange

	local y = 0
	local yrange = maxy - miny + 1
	local yreqstride = self.ystride - xrange

	local z = 0
	local zrange = maxz - minz + 1
	local multistride = self.zstride - ((yrange - 1) * self.ystride + xrange)

	return function()
		-- continue i until it needs to jump
		i = i + 1
		if i ~= nextaction then
			return i
		end

		-- continue y until maxy is exceeded
		y = y + 1
		if y ~= yrange then
			-- set i to index(minx, miny + y, minz + z) - 1
			i = i + yreqstride
			nextaction = i + xrange
			return i
		end

		-- continue z until maxz is exceeded
		z = z + 1
		if z == zrange then
			-- cuboid finished, return nil
			return
		end

		-- set i to index(minx, miny, minz + z) - 1
		i = i + multistride

		y = 0
		nextaction = i + xrange
		return i
	end
end

function VoxelArea:iterp(minp, maxp)
	return self:iter(minp.x, minp.y, minp.z, maxp.x, maxp.y, maxp.z)
end

repo_name: pgimeno/minetest

license: unknown


code:
--
-- This file contains built-in stuff in Minetest implemented in Lua.
--
-- It is always loaded and executed after registration of the C API,
-- before loading and running any mods.
--

-- Initialize some very basic things
function core.debug(...) core.log(table.concat({...}, "\t")) end
if core.print then
	local core_print = core.print
	-- Override native print and use
	-- terminal if that's turned on
	function print(...)
		local n, t = select("#", ...), {...}
		for i = 1, n do
			t[i] = tostring(t[i])
		end
		core_print(table.concat(t, "\t"))
	end
	core.print = nil -- don't pollute our namespace
end
math.randomseed(os.time())
os.setlocale("C", "numeric")
minetest = core

-- Load other files
local scriptdir = core.get_builtin_path() .. DIR_DELIM
local gamepath = scriptdir .. "game" .. DIR_DELIM
local clientpath = scriptdir .. "client" .. DIR_DELIM
local commonpath = scriptdir .. "common" .. DIR_DELIM
local asyncpath = scriptdir .. "async" .. DIR_DELIM

dofile(commonpath .. "strict.lua")
dofile(commonpath .. "serialize.lua")
dofile(commonpath .. "misc_helpers.lua")

if INIT == "game" then
	dofile(gamepath .. "init.lua")
elseif INIT == "mainmenu" then
	local mm_script = core.settings:get("main_menu_script")
	if mm_script and mm_script ~= "" then
		dofile(mm_script)
	else
		dofile(core.get_mainmenu_path() .. DIR_DELIM .. "init.lua")
	end
elseif INIT == "async" then
	dofile(asyncpath .. "init.lua")
elseif INIT == "client" then
	os.setlocale = nil
	dofile(clientpath .. "init.lua")
else
	error(("Unrecognized builtin initialization type %s!"):format(tostring(INIT)))
end


code:
--Minetest
--Copyright (C) 2014 sapier
--
--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.
--------------------------------------------------------------------------------
-- Global menu data
--------------------------------------------------------------------------------
menudata = {}

--------------------------------------------------------------------------------
-- Local cached values
--------------------------------------------------------------------------------
local min_supp_proto, max_supp_proto

function common_update_cached_supp_proto()
	min_supp_proto = core.get_min_supp_proto()
	max_supp_proto = core.get_max_supp_proto()
end
common_update_cached_supp_proto()
--------------------------------------------------------------------------------
-- Menu helper functions
--------------------------------------------------------------------------------

--------------------------------------------------------------------------------
local function render_client_count(n)
	if     n > 99 then return '99+'
	elseif n >= 0 then return tostring(n)
	else return '?' end
end

local function configure_selected_world_params(idx)
	local worldconfig = modmgr.get_worldconfig(menudata.worldlist:get_list()[idx].path)
	if worldconfig.creative_mode then
		core.settings:set("creative_mode", worldconfig.creative_mode)
	end
	if worldconfig.enable_damage then
		core.settings:set("enable_damage", worldconfig.enable_damage)
	end
end

--------------------------------------------------------------------------------
function image_column(tooltip, flagname)
	return "image,tooltip=" .. core.formspec_escape(tooltip) .. "," ..
		"0=" .. core.formspec_escape(defaulttexturedir .. "blank.png") .. "," ..
		"1=" .. core.formspec_escape(defaulttexturedir ..
			(flagname and "server_flags_" .. flagname .. ".png" or "blank.png")) .. "," ..
		"2=" .. core.formspec_escape(defaulttexturedir .. "server_ping_4.png") .. "," ..
		"3=" .. core.formspec_escape(defaulttexturedir .. "server_ping_3.png") .. "," ..
		"4=" .. core.formspec_escape(defaulttexturedir .. "server_ping_2.png") .. "," ..
		"5=" .. core.formspec_escape(defaulttexturedir .. "server_ping_1.png")
end

--------------------------------------------------------------------------------
function order_favorite_list(list)
	local res = {}
	--orders the favorite list after support
	for i = 1, #list do
		local fav = list[i]
		if is_server_protocol_compat(fav.proto_min, fav.proto_max) then
			res[#res + 1] = fav
		end
	end
	for i = 1, #list do
		local fav = list[i]
		if not is_server_protocol_compat(fav.proto_min, fav.proto_max) then
			res[#res + 1] = fav
		end
	end
	return res
end

--------------------------------------------------------------------------------
function render_serverlist_row(spec, is_favorite)
	local text = ""
	if spec.name then
		text = text .. core.formspec_escape(spec.name:trim())
	elseif spec.address then
		text = text .. spec.address:trim()
		if spec.port then
			text = text .. ":" .. spec.port
		end
	end

	local details = ""
	local grey_out = not is_server_protocol_compat(spec.proto_min, spec.proto_max)

	if is_favorite then
		details = "1,"
	else
		details = "0,"
	end

	if spec.ping then
		local ping = spec.ping * 1000
		if ping <= 50 then
			details = details .. "2,"
		elseif ping <= 100 then
			details = details .. "3,"
		elseif ping <= 250 then
			details = details .. "4,"
		else
			details = details .. "5,"
		end
	else
		details = details .. "0,"
	end

	if spec.clients and spec.clients_max then
		local clients_color = ''
		local clients_percent = 100 * spec.clients / spec.clients_max

		-- Choose a color depending on how many clients are connected
		-- (relatively to clients_max)
		if     grey_out		      then clients_color = '#aaaaaa'
		elseif spec.clients == 0      then clients_color = ''        -- 0 players: default/white
		elseif clients_percent <= 60  then clients_color = '#a1e587' -- 0-60%: green
		elseif clients_percent <= 90  then clients_color = '#ffdc97' -- 60-90%: yellow
		elseif clients_percent == 100 then clients_color = '#dd5b5b' -- full server: red (darker)
		else				   clients_color = '#ffba97' -- 90-100%: orange
		end

		details = details .. clients_color .. ',' ..
			render_client_count(spec.clients) .. ',/,' ..
			render_client_count(spec.clients_max) .. ','

	elseif grey_out then
		details = details .. '#aaaaaa,?,/,?,'
	else
		details = details .. ',?,/,?,'
	end

	if spec.creative then
		details = details .. "1,"
	else
		details = details .. "0,"
	end

	if spec.damage then
		details = details .. "1,"
	else
		details = details .. "0,"
	end

	if spec.pvp then
		details = details .. "1,"
	else
		details = details .. "0,"
	end

	return details .. (grey_out and '#aaaaaa,' or ',') .. text
end

--------------------------------------------------------------------------------
os.tempfolder = function()
	if core.settings:get("TMPFolder") then
		return core.settings:get("TMPFolder") .. DIR_DELIM .. "MT_" .. math.random(0,10000)
	end

	local filetocheck = os.tmpname()
	os.remove(filetocheck)

	local randname = "MTTempModFolder_" .. math.random(0,10000)
	if DIR_DELIM == "\\" then
		local tempfolder = os.getenv("TEMP")
		return tempfolder .. filetocheck
	else
		local backstring = filetocheck:reverse()
		return filetocheck:sub(0,filetocheck:len()-backstring:find(DIR_DELIM)+1) ..randname
	end

end

--------------------------------------------------------------------------------
function menu_render_worldlist()
	local retval = ""
	local current_worldlist = menudata.worldlist:get_list()

	for i, v in ipairs(current_worldlist) do
		if retval ~= "" then retval = retval .. "," end
		retval = retval .. core.formspec_escape(v.name) ..
				" \\[" .. core.formspec_escape(v.gameid) .. "\\]"
	end

	return retval
end

--------------------------------------------------------------------------------
function menu_handle_key_up_down(fields, textlist, settingname)
	local oldidx, newidx = core.get_textlist_index(textlist), 1
	if fields.key_up or fields.key_down then
		if fields.key_up and oldidx and oldidx > 1 then
			newidx = oldidx - 1
		elseif fields.key_down and oldidx and
				oldidx < menudata.worldlist:size() then
			newidx = oldidx + 1
		end
		core.settings:set(settingname, menudata.worldlist:get_raw_index(newidx))
		configure_selected_world_params(newidx)
		return true
	end
	return false
end

--------------------------------------------------------------------------------
function asyncOnlineFavourites()
	if not menudata.public_known then
		menudata.public_known = {{
			name = fgettext("Loading..."),
			description = fgettext_ne("Try reenabling public serverlist and check your internet connection.")
		}}
	end
	menudata.favorites = menudata.public_known
	menudata.favorites_is_public = true

	if not menudata.public_downloading then
		menudata.public_downloading = true
	else
		return
	end

	core.handle_async(
		function(param)
			return core.get_favorites("online")
		end,
		nil,
		function(result)
			menudata.public_downloading = nil
			local favs = order_favorite_list(result)
			if favs[1] then
				menudata.public_known = favs
				menudata.favorites = menudata.public_known
				menudata.favorites_is_public = true
			end
			core.event_handler("Refresh")
		end
	)
end

--------------------------------------------------------------------------------
function text2textlist(xpos, ypos, width, height, tl_name, textlen, text, transparency)
	local textlines = core.wrap_text(text, textlen, true)
	local retval = "textlist[" .. xpos .. "," .. ypos .. ";" .. width ..
			"," .. height .. ";" .. tl_name .. ";"

	for i = 1, #textlines do
		textlines[i] = textlines[i]:gsub("\r", "")
		retval = retval .. core.formspec_escape(textlines[i]) .. ","
	end

	retval = retval .. ";0;"
	if transparency then retval = retval .. "true" end
	retval = retval .. "]"

	return retval
end

--------------------------------------------------------------------------------
function is_server_protocol_compat(server_proto_min, server_proto_max)
	if (not server_proto_min) or (not server_proto_max) then
		-- There is no info. Assume the best and act as if we would be compatible.
		return true
	end
	return min_supp_proto <= server_proto_max and max_supp_proto >= server_proto_min
end
--------------------------------------------------------------------------------
function is_server_protocol_compat_or_error(server_proto_min, server_proto_max)
	if not is_server_protocol_compat(server_proto_min, server_proto_max) then
		local server_prot_ver_info, client_prot_ver_info
		local s_p_min = server_proto_min
		local s_p_max = server_proto_max

		if s_p_min ~= s_p_max then
			server_prot_ver_info = fgettext_ne("Server supports protocol versions between $1 and $2. ",
				s_p_min, s_p_max)
		else
			server_prot_ver_info = fgettext_ne("Server enforces protocol version $1. ",
				s_p_min)
		end
		if min_supp_proto ~= max_supp_proto then
			client_prot_ver_info= fgettext_ne("We support protocol versions between version $1 and $2.",
				min_supp_proto, max_supp_proto)
		else
			client_prot_ver_info = fgettext_ne("We only support protocol version $1.", min_supp_proto)
		end
		gamedata.errormessage = fgettext_ne("Protocol version mismatch. ")
			.. server_prot_ver_info
			.. client_prot_ver_info
		return false
	end

	return true
end
--------------------------------------------------------------------------------
function menu_worldmt(selected, setting, value)
	local world = menudata.worldlist:get_list()[selected]
	if world then
		local filename = world.path .. DIR_DELIM .. "world.mt"
		local world_conf = Settings(filename)

		if value then
			if not world_conf:write() then
				core.log("error", "Failed to write world config file")
			end
			world_conf:set(setting, value)
			world_conf:write()
		else
			return world_conf:get(setting)
		end
	else
		return nil
	end
end

function menu_worldmt_legacy(selected)
	local modes_names = {"creative_mode", "enable_damage", "server_announce"}
	for _, mode_name in pairs(modes_names) do
		local mode_val = menu_worldmt(selected, mode_name)
		if mode_val then
			core.settings:set(mode_name, mode_val)
		else
			menu_worldmt(selected, mode_name, core.settings:get(mode_name))
		end
	end
end


code:
--Minetest
--Copyright (C) 2013 sapier
--
--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.

--------------------------------------------------------------------------------

local enabled_all = false

local function modname_valid(name)
	return not name:find("[^a-z0-9_]")
end

local function get_formspec(data)
	local mod = data.list:get_list()[data.selected_mod]

	local retval =
		"size[11.5,7.5,true]" ..
		"label[0.5,0;" .. fgettext("World:") .. "]" ..
		"label[1.75,0;" .. data.worldspec.name .. "]"

	if mod == nil then
		mod = {name=""}
	end

	local hard_deps, soft_deps = modmgr.get_dependencies(mod.path)

	retval = retval ..
		"label[0,0.7;" .. fgettext("Mod:") .. "]" ..
		"label[0.75,0.7;" .. mod.name .. "]" ..
		"label[0,1.25;" .. fgettext("Dependencies:") .. "]" ..
		"textlist[0,1.75;5,2.125;world_config_depends;" ..
		hard_deps .. ";0]" ..
		"label[0,3.875;" .. fgettext("Optional dependencies:") .. "]" ..
		"textlist[0,4.375;5,1.8;world_config_optdepends;" ..
		soft_deps .. ";0]" ..
		"button[3.25,7;2.5,0.5;btn_config_world_save;" .. fgettext("Save") .. "]" ..
		"button[5.75,7;2.5,0.5;btn_config_world_cancel;" .. fgettext("Cancel") .. "]"

	if mod and mod.name ~= "" and not mod.is_game_content then
		if mod.is_modpack then
			local rawlist = data.list:get_raw_list()

			local all_enabled = true
			for j = 1, #rawlist, 1 do
				if rawlist[j].modpack == mod.name and not rawlist[j].enabled then
					all_enabled = false
					break
				end
			end

			if all_enabled then
				retval = retval .. "button[5.5,0.125;2.5,0.5;btn_mp_disable;" ..
					fgettext("Disable MP") .. "]"
			else
				retval = retval .. "button[5.5,0.125;2.5,0.5;btn_mp_enable;" ..
					fgettext("Enable MP") .. "]"
			end
		else
			if mod.enabled then
				retval = retval .. "checkbox[5.5,-0.125;cb_mod_enable;" ..
					fgettext("enabled") .. ";true]"
			else
				retval = retval .. "checkbox[5.5,-0.125;cb_mod_enable;" ..
					fgettext("enabled") .. ";false]"
			end
		end
	end
	if enabled_all then
		retval = retval ..
			"button[8.75,0.125;2.5,0.5;btn_disable_all_mods;" .. fgettext("Disable all") .. "]"
	else
		retval = retval ..
			"button[8.75,0.125;2.5,0.5;btn_enable_all_mods;" .. fgettext("Enable all") .. "]"
	end
	retval = retval ..
		"tablecolumns[color;tree;text]" ..
		"table[5.5,0.75;5.75,6;world_config_modlist;"
	retval = retval .. modmgr.render_modlist(data.list)
	retval = retval .. ";" .. data.selected_mod .."]"

	return retval
end

local function enable_mod(this, toset)
	local mod = this.data.list:get_list()[this.data.selected_mod]

	if mod.is_game_content then
		-- game mods can't be enabled or disabled
	elseif not mod.is_modpack then
		if toset == nil then
			mod.enabled = not mod.enabled
		else
			mod.enabled = toset
		end
	else
		local list = this.data.list:get_raw_list()
		for i=1,#list,1 do
			if list[i].modpack == mod.name then
				if toset == nil then
					toset = not list[i].enabled
				end
				list[i].enabled = toset
			end
		end
	end
end

local function handle_buttons(this, fields)
	if fields["world_config_modlist"] ~= nil then
		local event = core.explode_table_event(fields["world_config_modlist"])
		this.data.selected_mod = event.row
		core.settings:set("world_config_selected_mod", event.row)

		if event.type == "DCL" then
			enable_mod(this)
		end

		return true
	end

	if fields["key_enter"] ~= nil then
		enable_mod(this)
		return true
	end

	if fields["cb_mod_enable"] ~= nil then
		local toset = core.is_yes(fields["cb_mod_enable"])
		enable_mod(this,toset)
		return true
	end

	if fields["btn_mp_enable"] ~= nil or
		fields["btn_mp_disable"] then
		local toset = (fields["btn_mp_enable"] ~= nil)
		enable_mod(this,toset)
		return true
	end

	if fields["btn_config_world_save"] then
		local filename = this.data.worldspec.path ..
				DIR_DELIM .. "world.mt"

		local worldfile = Settings(filename)
		local mods = worldfile:to_table()

		local rawlist = this.data.list:get_raw_list()

		local i,mod
		for i,mod in ipairs(rawlist) do
			if not mod.is_modpack and
					not mod.is_game_content then
				if modname_valid(mod.name) then
					worldfile:set("load_mod_"..mod.name, tostring(mod.enabled))
				else
					if mod.enabled then
						gamedata.errormessage = fgettext_ne("Failed to enable mod \"$1\" as it contains disallowed characters. Only chararacters [a-z0-9_] are allowed.", mod.name)
					end
				end
				mods["load_mod_"..mod.name] = nil
			end
		end

		-- Remove mods that are not present anymore
		for key,value in pairs(mods) do
			if key:sub(1,9) == "load_mod_" then
				worldfile:remove(key)
			end
		end

		if not worldfile:write() then
			core.log("error", "Failed to write world config file")
		end

		this:delete()
		return true
	end

	if fields["btn_config_world_cancel"] then
		this:delete()
		return true
	end

	if fields.btn_enable_all_mods then
		local list = this.data.list:get_raw_list()

		for i = 1, #list do
			if not list[i].is_game_content
					and not list[i].is_modpack then
				list[i].enabled = true
			end
		end
		enabled_all = true
		return true
	end

	if fields.btn_disable_all_mods then
		local list = this.data.list:get_raw_list()

		for i = 1, #list do
			if not list[i].is_game_content
					and not list[i].is_modpack then
				list[i].enabled = false
			end
		end
		enabled_all = false
		return true
	end

	return false
end

function create_configure_world_dlg(worldidx)
	local dlg = dialog_create("sp_config_world",
					get_formspec,
					handle_buttons,
					nil)

	dlg.data.selected_mod = tonumber(core.settings:get("world_config_selected_mod"))
	if dlg.data.selected_mod == nil then
		dlg.data.selected_mod = 0
	end

	dlg.data.worldspec = core.get_worlds()[worldidx]
	if dlg.data.worldspec == nil then dlg:delete() return nil end

	dlg.data.worldconfig = modmgr.get_worldconfig(dlg.data.worldspec.path)

	if dlg.data.worldconfig == nil or dlg.data.worldconfig.id == nil or
			dlg.data.worldconfig.id == "" then

		dlg:delete()
		return nil
	end

	dlg.data.list = filterlist.create(
			modmgr.preparemodlist, --refresh
			modmgr.comparemod, --compare
			function(element,uid) --uid match
					if element.name == uid then
						return true
					end
				end,
				function(element, criteria)
					if criteria.hide_game and
							element.is_game_content then
						return false
					end

					if criteria.hide_modpackcontents and
							element.modpack ~= nil then
						return false
					end
					return true
				end, --filter
				{ worldpath= dlg.data.worldspec.path,
				  gameid = dlg.data.worldspec.gameid }
			)

	if dlg.data.selected_mod > dlg.data.list:size() then
		dlg.data.selected_mod = 0
	end

	dlg.data.list:set_filtercriteria(
		{
			hide_game=dlg.data.hide_gamemods,
			hide_modpackcontents= dlg.data.hide_modpackcontents
		})
	dlg.data.list:add_sort_mechanism("alphabetic", sort_mod_list)
	dlg.data.list:set_sortmode("alphabetic")

	return dlg
end


code:
--Minetest
--Copyright (C) 2014 sapier
--
--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.

local function create_world_formspec(dialogdata)
	local mapgens = core.get_mapgen_names()

	local current_seed = core.settings:get("fixed_map_seed") or ""
	local current_mg   = core.settings:get("mg_name")

	local mglist = ""
	local selindex = 1
	local i = 1
	for k,v in pairs(mapgens) do
		if current_mg == v then
			selindex = i
		end
		i = i + 1
		mglist = mglist .. v .. ","
	end
	mglist = mglist:sub(1, -2)

	local gameid = core.settings:get("menu_last_game")

	local game, gameidx = nil , 0
	if gameid ~= nil then
		game, gameidx = gamemgr.find_by_gameid(gameid)

		if gameidx == nil then
			gameidx = 0
		end
	end

	current_seed = core.formspec_escape(current_seed)
	local retval =
		"size[11.5,6.5,true]" ..
		"label[2,0;" .. fgettext("World name") .. "]"..
		"field[4.5,0.4;6,0.5;te_world_name;;]" ..

		"label[2,1;" .. fgettext("Seed") .. "]"..
		"field[4.5,1.4;6,0.5;te_seed;;".. current_seed .. "]" ..

		"label[2,2;" .. fgettext("Mapgen") .. "]"..
		"dropdown[4.2,2;6.3;dd_mapgen;" .. mglist .. ";" .. selindex .. "]" ..

		"label[2,3;" .. fgettext("Game") .. "]"..
		"textlist[4.2,3;5.8,2.3;games;" .. gamemgr.gamelist() ..
		";" .. gameidx .. ";true]" ..

		"button[3.25,6;2.5,0.5;world_create_confirm;" .. fgettext("Create") .. "]" ..
		"button[5.75,6;2.5,0.5;world_create_cancel;" .. fgettext("Cancel") .. "]"

	if #gamemgr.games == 0 then
		retval = retval .. "box[2,4;8,1;#ff8800]label[2.25,4;" ..
				fgettext("You have no subgames installed.") .. "]label[2.25,4.4;" ..
				fgettext("Download one from minetest.net") .. "]"
	elseif #gamemgr.games == 1 and gamemgr.games[1].id == "minimal" then
		retval = retval .. "box[1.75,4;8.7,1;#ff8800]label[2,4;" ..
				fgettext("Warning: The minimal development test is meant for developers.") .. "]label[2,4.4;" ..
				fgettext("Download a subgame, such as minetest_game, from minetest.net") .. "]"
	end

	return retval

end

local function create_world_buttonhandler(this, fields)

	if fields["world_create_confirm"] or
		fields["key_enter"] then

		local worldname = fields["te_world_name"]
		local gameindex = core.get_textlist_index("games")

		if gameindex ~= nil and
			worldname ~= "" then

			local message = nil

			core.settings:set("fixed_map_seed", fields["te_seed"])

			if not menudata.worldlist:uid_exists_raw(worldname) then
				core.settings:set("mg_name",fields["dd_mapgen"])
				message = core.create_world(worldname,gameindex)
			else
				message = fgettext("A world named \"$1\" already exists", worldname)
			end

			if message ~= nil then
				gamedata.errormessage = message
			else
				core.settings:set("menu_last_game",gamemgr.games[gameindex].id)
				if this.data.update_worldlist_filter then
					menudata.worldlist:set_filtercriteria(gamemgr.games[gameindex].id)
					mm_texture.update("singleplayer", gamemgr.games[gameindex].id)
				end
				menudata.worldlist:refresh()
				core.settings:set("mainmenu_last_selected_world",
									menudata.worldlist:raw_index_by_uid(worldname))
			end
		else
			gamedata.errormessage =
				fgettext("No worldname given or no game selected")
		end
		this:delete()
		return true
	end

	if fields["games"] then
		return true
	end

	if fields["world_create_cancel"] then
		this:delete()
		return true
	end

	return false
end

function create_create_world_dlg(update_worldlistfilter)
	local retval = dialog_create("sp_create_world",
					create_world_formspec,
					create_world_buttonhandler,
					nil)
	retval.update_worldlist_filter = update_worldlistfilter

	return retval
end


code:
--Minetest
--Copyright (C) 2014 sapier
--
--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.

--------------------------------------------------------------------------------

local function delete_mod_formspec(dialogdata)

	dialogdata.mod = modmgr.global_mods:get_list()[dialogdata.selected]

	local retval =
		"size[11.5,4.5,true]" ..
		"label[2,2;" ..
		fgettext("Are you sure you want to delete \"$1\"?", dialogdata.mod.name) .. "]"..
		"button[3.25,3.5;2.5,0.5;dlg_delete_mod_confirm;" .. fgettext("Delete") .. "]" ..
		"button[5.75,3.5;2.5,0.5;dlg_delete_mod_cancel;" .. fgettext("Cancel") .. "]"

	return retval
end

--------------------------------------------------------------------------------
local function delete_mod_buttonhandler(this, fields)
	if fields["dlg_delete_mod_confirm"] ~= nil then

		if this.data.mod.path ~= nil and
			this.data.mod.path ~= "" and
			this.data.mod.path ~= core.get_modpath() then
			if not core.delete_dir(this.data.mod.path) then
				gamedata.errormessage = fgettext("Modmgr: failed to delete \"$1\"", this.data.mod.path)
			end
			modmgr.refresh_globals()
		else
			gamedata.errormessage = fgettext("Modmgr: invalid modpath \"$1\"", this.data.mod.path)
		end
		this:delete()
		return true
	end

	if fields["dlg_delete_mod_cancel"] then
		this:delete()
		return true
	end

	return false
end

--------------------------------------------------------------------------------
function create_delete_mod_dlg(selected_index)

	local retval = dialog_create("dlg_delete_mod",
					delete_mod_formspec,
					delete_mod_buttonhandler,
					nil)
	retval.data.selected = selected_index
	return retval
end


code:
--Minetest
--Copyright (C) 2014 sapier
--
--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.

local function delete_world_formspec(dialogdata)

	local retval =
		"size[11.5,4.5,true]" ..
		"label[2,2;" ..
		fgettext("Delete World \"$1\"?", dialogdata.delete_name) .. "]" ..
		"button[3.25,3.5;2.5,0.5;world_delete_confirm;" .. fgettext("Delete") .. "]" ..
		"button[5.75,3.5;2.5,0.5;world_delete_cancel;" .. fgettext("Cancel") .. "]"
	return retval
end

local function delete_world_buttonhandler(this, fields)
	if fields["world_delete_confirm"] then

		if this.data.delete_index > 0 and
			this.data.delete_index <= #menudata.worldlist:get_raw_list() then
			core.delete_world(this.data.delete_index)
			menudata.worldlist:refresh()
		end
		this:delete()
		return true
	end

	if fields["world_delete_cancel"] then
		this:delete()
		return true
	end

	return false
end

function create_delete_world_dlg(name_to_del,index_to_del)

	assert(name_to_del ~= nil and type(name_to_del) == "string" and name_to_del ~= "")
	assert(index_to_del ~= nil and type(index_to_del) == "number")

	local retval = dialog_create("delete_world",
					delete_world_formspec,
					delete_world_buttonhandler,
					nil)
	retval.data.delete_name  = name_to_del
	retval.data.delete_index = index_to_del

	return retval
end


code:
--Minetest
--Copyright (C) 2014 sapier
--
--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.

--------------------------------------------------------------------------------

local function rename_modpack_formspec(dialogdata)

	dialogdata.mod = modmgr.global_mods:get_list()[dialogdata.selected]

	local retval =
		"size[11.5,4.5,true]" ..
		"field[2.5,2;7,0.5;te_modpack_name;".. fgettext("Rename Modpack:") .. ";" ..
		dialogdata.mod.name .. "]" ..
		"button[3.25,3.5;2.5,0.5;dlg_rename_modpack_confirm;"..
				fgettext("Accept") .. "]" ..
		"button[5.75,3.5;2.5,0.5;dlg_rename_modpack_cancel;"..
				fgettext("Cancel") .. "]"

	return retval
end

--------------------------------------------------------------------------------
local function rename_modpack_buttonhandler(this, fields)
	if fields["dlg_rename_modpack_confirm"] ~= nil then
		local oldpath = core.get_modpath() .. DIR_DELIM .. this.data.mod.name
		local targetpath = core.get_modpath() .. DIR_DELIM .. fields["te_modpack_name"]
		core.copy_dir(oldpath,targetpath,false)
		modmgr.refresh_globals()
		modmgr.selected_mod = modmgr.global_mods:get_current_index(
			modmgr.global_mods:raw_index_by_uid(fields["te_modpack_name"]))

		this:delete()
		return true
	end

	if fields["dlg_rename_modpack_cancel"] then
		this:delete()
		return true
	end

	return false
end

--------------------------------------------------------------------------------
function create_rename_modpack_dlg(selected_index)

	local retval = dialog_create("dlg_delete_mod",
					rename_modpack_formspec,
					rename_modpack_buttonhandler,
					nil)
	retval.data.selected = selected_index
	return retval
end


code:
--Minetest
--Copyright (C) 2015 PilzAdam
--
--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.

local FILENAME = "settingtypes.txt"

local CHAR_CLASSES = {
	SPACE = "[%s]",
	VARIABLE = "[%w_%-%.]",
	INTEGER = "[+-]?[%d]",
	FLOAT = "[+-]?[%d%.]",
	FLAGS = "[%w_%-%.,]",
}

-- returns error message, or nil
local function parse_setting_line(settings, line, read_all, base_level, allow_secure)
	-- comment
	local comment = line:match("^#" .. CHAR_CLASSES.SPACE .. "*(.*)$")
	if comment then
		if settings.current_comment == "" then
			settings.current_comment = comment
		else
			settings.current_comment = settings.current_comment .. "\n" .. comment
		end
		return
	end

	-- clear current_comment so only comments directly above a setting are bound to it
	-- but keep a local reference to it for variables in the current line
	local current_comment = settings.current_comment
	settings.current_comment = ""

	-- empty lines
	if line:match("^" .. CHAR_CLASSES.SPACE .. "*$") then
		return
	end

	-- category
	local stars, category = line:match("^%[([%*]*)([^%]]+)%]$")
	if category then
		table.insert(settings, {
			name = category,
			level = stars:len() + base_level,
			type = "category",
		})
		return
	end

	-- settings
	local first_part, name, readable_name, setting_type = line:match("^"
			-- this first capture group matches the whole first part,
			--  so we can later strip it from the rest of the line
			.. "("
				.. "([" .. CHAR_CLASSES.VARIABLE .. "+)" -- variable name
				.. CHAR_CLASSES.SPACE .. "*"
				.. "%(([^%)]*)%)"  -- readable name
				.. CHAR_CLASSES.SPACE .. "*"
				.. "(" .. CHAR_CLASSES.VARIABLE .. "+)" -- type
				.. CHAR_CLASSES.SPACE .. "*"
			.. ")")

	if not first_part then
		return "Invalid line"
	end

	if name:match("secure%.[.]*") and not allow_secure then
		return "Tried to add \"secure.\" setting"
	end

	if readable_name == "" then
		readable_name = nil
	end
	local remaining_line = line:sub(first_part:len() + 1)

	if setting_type == "int" then
		local default, min, max = remaining_line:match("^"
				-- first int is required, the last 2 are optional
				.. "(" .. CHAR_CLASSES.INTEGER .. "+)" .. CHAR_CLASSES.SPACE .. "*"
				.. "(" .. CHAR_CLASSES.INTEGER .. "*)" .. CHAR_CLASSES.SPACE .. "*"
				.. "(" .. CHAR_CLASSES.INTEGER .. "*)"
				.. "$")

		if not default or not tonumber(default) then
			return "Invalid integer setting"
		end

		min = tonumber(min)
		max = tonumber(max)
		table.insert(settings, {
			name = name,
			readable_name = readable_name,
			type = "int",
			default = default,
			min = min,
			max = max,
			comment = current_comment,
		})
		return
	end

	if setting_type == "string" or setting_type == "noise_params"
			or setting_type == "key" or setting_type == "v3f" then
		local default = remaining_line:match("^(.*)$")

		if not default then
			return "Invalid string setting"
		end
		if setting_type == "key" and not read_all then
			-- ignore key type if read_all is false
			return
		end

		table.insert(settings, {
			name = name,
			readable_name = readable_name,
			type = setting_type,
			default = default,
			comment = current_comment,
		})
		return
	end

	if setting_type == "bool" then
		if remaining_line ~= "false" and remaining_line ~= "true" then
			return "Invalid boolean setting"
		end

		table.insert(settings, {
			name = name,
			readable_name = readable_name,
			type = "bool",
			default = remaining_line,
			comment = current_comment,
		})
		return
	end

	if setting_type == "float" then
		local default, min, max = remaining_line:match("^"
				-- first float is required, the last 2 are optional
				.. "(" .. CHAR_CLASSES.FLOAT .. "+)" .. CHAR_CLASSES.SPACE .. "*"
				.. "(" .. CHAR_CLASSES.FLOAT .. "*)" .. CHAR_CLASSES.SPACE .. "*"
				.. "(" .. CHAR_CLASSES.FLOAT .. "*)"
				.."$")

		if not default or not tonumber(default) then
			return "Invalid float setting"
		end

		min = tonumber(min)
		max = tonumber(max)
		table.insert(settings, {
			name = name,
			readable_name = readable_name,
			type = "float",
			default = default,
			min = min,
			max = max,
			comment = current_comment,
		})
		return
	end

	if setting_type == "enum" then
		local default, values = remaining_line:match("^"
				-- first value (default) may be empty (i.e. is optional)
				.. "(" .. CHAR_CLASSES.VARIABLE .. "*)" .. CHAR_CLASSES.SPACE .. "*"
				.. "(" .. CHAR_CLASSES.FLAGS .. "+)"
				.. "$")

		if not default or values == "" then
			return "Invalid enum setting"
		end

		table.insert(settings, {
			name = name,
			readable_name = readable_name,
			type = "enum",
			default = default,
			values = values:split(",", true),
			comment = current_comment,
		})
		return
	end

	if setting_type == "path" then
		local default = remaining_line:match("^(.*)$")

		if not default then
			return "Invalid path setting"
		end

		table.insert(settings, {
			name = name,
			readable_name = readable_name,
			type = "path",
			default = default,
			comment = current_comment,
		})
		return
	end

	if setting_type == "flags" then
		local default, possible = remaining_line:match("^"
				-- first value (default) may be empty (i.e. is optional)
				-- this is implemented by making the last value optional, and
				-- swapping them around if it turns out empty.
				.. "(" .. CHAR_CLASSES.FLAGS .. "+)" .. CHAR_CLASSES.SPACE .. "*"
				.. "(" .. CHAR_CLASSES.FLAGS .. "*)"
				.. "$")

		if not default or not possible then
			return "Invalid flags setting"
		end

		if possible == "" then
			possible = default
			default = ""
		end

		table.insert(settings, {
			name = name,
			readable_name = readable_name,
			type = "flags",
			default = default,
			possible = possible,
			comment = current_comment,
		})
		return
	end

	return "Invalid setting type \"" .. setting_type .. "\""
end

local function parse_single_file(file, filepath, read_all, result, base_level, allow_secure)
	-- store this helper variable in the table so it's easier to pass to parse_setting_line()
	result.current_comment = ""

	local line = file:read("*line")
	while line do
		local error_msg = parse_setting_line(result, line, read_all, base_level, allow_secure)
		if error_msg then
			core.log("error", error_msg .. " in " .. filepath .. " \"" .. line .. "\"")
		end
		line = file:read("*line")
	end

	result.current_comment = nil
end

-- read_all: whether to ignore certain setting types for GUI or not
-- parse_mods: whether to parse settingtypes.txt in mods and games
local function parse_config_file(read_all, parse_mods)
	local builtin_path = core.get_builtin_path() .. DIR_DELIM .. FILENAME
	local file = io.open(builtin_path, "r")
	local settings = {}
	if not file then
		core.log("error", "Can't load " .. FILENAME)
		return settings
	end

	parse_single_file(file, builtin_path, read_all, settings, 0, true)

	file:close()

	if parse_mods then
		-- Parse games
		local games_category_initialized = false
		local index = 1
		local game = gamemgr.get_game(index)
		while game do
			local path = game.path .. DIR_DELIM .. FILENAME
			local file = io.open(path, "r")
			if file then
				if not games_category_initialized then
					local translation = fgettext_ne("Games"), -- not used, but needed for xgettext
					table.insert(settings, {
						name = "Games",
						level = 0,
						type = "category",
					})
					games_category_initialized = true
				end

				table.insert(settings, {
					name = game.name,
					level = 1,
					type = "category",
				})

				parse_single_file(file, path, read_all, settings, 2, false)

				file:close()
			end

			index = index + 1
			game = gamemgr.get_game(index)
		end

		-- Parse mods
		local mods_category_initialized = false
		local mods = {}
		get_mods(core.get_modpath(), mods)
		for _, mod in ipairs(mods) do
			local path = mod.path .. DIR_DELIM .. FILENAME
			local file = io.open(path, "r")
			if file then
				if not mods_category_initialized then
					local translation = fgettext_ne("Mods"), -- not used, but needed for xgettext
					table.insert(settings, {
						name = "Mods",
						level = 0,
						type = "category",
					})
					mods_category_initialized = true
				end

				table.insert(settings, {
					name = mod.name,
					level = 1,
					type = "category",
				})

				parse_single_file(file, path, read_all, settings, 2, false)

				file:close()
			end
		end
	end

	return settings
end

local function filter_settings(settings, searchstring)
	if not searchstring or searchstring == "" then
		return settings, -1
	end

	-- Setup the keyword list
	local keywords = {}
	for word in searchstring:lower():gmatch("%S+") do
		table.insert(keywords, word)
	end

	local result = {}
	local category_stack = {}
	local current_level = 0
	local best_setting = nil
	for _, entry in pairs(settings) do
		if entry.type == "category" then
			-- Remove all settingless categories
			while #category_stack > 0 and entry.level <= current_level do
				table.remove(category_stack, #category_stack)
				if #category_stack > 0 then
					current_level = category_stack[#category_stack].level
				else
					current_level = 0
				end
			end

			-- Push category onto stack
			category_stack[#category_stack + 1] = entry
			current_level = entry.level
		else
			-- See if setting matches keywords
			local setting_score = 0
			for k = 1, #keywords do
				local keyword = keywords[k]

				if string.find(entry.name:lower(), keyword, 1, true) then
					setting_score = setting_score + 1
				end

				if entry.readable_name and
						string.find(fgettext(entry.readable_name):lower(), keyword, 1, true) then
					setting_score = setting_score + 1
				end

				if entry.comment and
						string.find(fgettext_ne(entry.comment):lower(), keyword, 1, true) then
					setting_score = setting_score + 1
				end
			end

			-- Add setting to results if match
			if setting_score > 0 then
				-- Add parent categories
				for _, category in pairs(category_stack) do
					result[#result + 1] = category
				end
				category_stack = {}

				-- Add setting
				result[#result + 1] = entry
				entry.score = setting_score

				if not best_setting or
						setting_score > result[best_setting].score then
					best_setting = #result
				end
			end
		end
	end
	return result, best_setting or -1
end

local full_settings = parse_config_file(false, true)
local search_string = ""
local settings = full_settings
local selected_setting = 1

local function get_current_value(setting)
	local value = core.settings:get(setting.name)
	if value == nil then
		value = setting.default
	end
	return value
end

local function create_change_setting_formspec(dialogdata)
	local setting = settings[selected_setting]
	local formspec = "size[10,5.2,true]" ..
			"button[5,4.5;2,1;btn_done;" .. fgettext("Save") .. "]" ..
			"button[3,4.5;2,1;btn_cancel;" .. fgettext("Cancel") .. "]" ..
			"tablecolumns[color;text]" ..
			"tableoptions[background=#00000000;highlight=#00000000;border=false]" ..
			"table[0,0;10,3;info;"

	if setting.readable_name then
		formspec = formspec .. "#FFFF00," .. fgettext(setting.readable_name)
				.. " (" .. core.formspec_escape(setting.name) .. "),"
	else
		formspec = formspec .. "#FFFF00," .. core.formspec_escape(setting.name) .. ","
	end

	formspec = formspec .. ",,"

	local comment_text = ""

	if setting.comment == "" then
		comment_text = fgettext_ne("(No description of setting given)")
	else
		comment_text = fgettext_ne(setting.comment)
	end
	for _, comment_line in ipairs(comment_text:split("\n", true)) do
		formspec = formspec .. "," .. core.formspec_escape(comment_line) .. ","
	end

	if setting.type == "flags" then
		formspec = formspec .. ",,"
				.. "," .. fgettext("Please enter a comma seperated list of flags.") .. ","
				.. "," .. fgettext("Possible values are: ")
				.. core.formspec_escape(setting.possible:gsub(",", ", ")) .. ","
	elseif setting.type == "noise_params" then
		formspec = formspec .. ",,"
				.. "," .. fgettext("Format: <offset>, <scale>, (<spreadX>, <spreadY>, <spreadZ>), <seed>, <octaves>, <persistence>") .. ","
				.. "," .. fgettext("Optionally the lacunarity can be appended with a leading comma.") .. ","
	elseif setting.type == "v3f" then
		formspec = formspec .. ",,"
				.. "," .. fgettext_ne("Format is 3 numbers separated by commas and inside brackets.") .. ","
	end

	formspec = formspec:sub(1, -2) -- remove trailing comma

	formspec = formspec .. ";1]"

	if setting.type == "bool" then
		local selected_index
		if core.is_yes(get_current_value(setting)) then
			selected_index = 2
		else
			selected_index = 1
		end
		formspec = formspec .. "dropdown[0.5,3.5;3,1;dd_setting_value;"
				.. fgettext("Disabled") .. "," .. fgettext("Enabled") .. ";"
				.. selected_index .. "]"

	elseif setting.type == "enum" then
		local selected_index = 0
		formspec = formspec .. "dropdown[0.5,3.5;3,1;dd_setting_value;"
		for index, value in ipairs(setting.values) do
			-- translating value is not possible, since it's the value
			--  that we set the setting to
			formspec = formspec ..  core.formspec_escape(value) .. ","
			if get_current_value(setting) == value then
				selected_index = index
			end
		end
		if #setting.values > 0 then
			formspec = formspec:sub(1, -2) -- remove trailing comma
		end
		formspec = formspec .. ";" .. selected_index .. "]"

	elseif setting.type == "path" then
		local current_value = dialogdata.selected_path
		if not current_value then
			current_value = get_current_value(setting)
		end
		formspec = formspec .. "field[0.5,4;7.5,1;te_setting_value;;"
				.. core.formspec_escape(current_value) .. "]"
				.. "button[8,3.75;2,1;btn_browser_path;" .. fgettext("Browse") .. "]"

	else
		-- TODO: fancy input for float, int, flags, noise_params, v3f
		local width = 10
		local text = get_current_value(setting)
		if dialogdata.error_message then
			formspec = formspec .. "tablecolumns[color;text]" ..
			"tableoptions[background=#00000000;highlight=#00000000;border=false]" ..
			"table[5,3.9;5,0.6;error_message;#FF0000,"
					.. core.formspec_escape(dialogdata.error_message) .. ";0]"
			width = 5
			if dialogdata.entered_text then
				text = dialogdata.entered_text
			end
		end
		formspec = formspec .. "field[0.5,4;" .. width .. ",1;te_setting_value;;"
				.. core.formspec_escape(text) .. "]"
	end
	return formspec
end

local function handle_change_setting_buttons(this, fields)
	if fields["btn_done"] or fields["key_enter"] then
		local setting = settings[selected_setting]
		if setting.type == "bool" then
			local new_value = fields["dd_setting_value"]
			-- Note: new_value is the actual (translated) value shown in the dropdown
			core.settings:set_bool(setting.name, new_value == fgettext("Enabled"))

		elseif setting.type == "enum" then
			local new_value = fields["dd_setting_value"]
			core.settings:set(setting.name, new_value)

		elseif setting.type == "int" then
			local new_value = tonumber(fields["te_setting_value"])
			if not new_value or math.floor(new_value) ~= new_value then
				this.data.error_message = fgettext_ne("Please enter a valid integer.")
				this.data.entered_text = fields["te_setting_value"]
				core.update_formspec(this:get_formspec())
				return true
			end
			if setting.min and new_value < setting.min then
				this.data.error_message = fgettext_ne("The value must be at least $1.", setting.min)
				this.data.entered_text = fields["te_setting_value"]
				core.update_formspec(this:get_formspec())
				return true
			end
			if setting.max and new_value > setting.max then
				this.data.error_message = fgettext_ne("The value must not be larger than $1.", setting.max)
				this.data.entered_text = fields["te_setting_value"]
				core.update_formspec(this:get_formspec())
				return true
			end
			core.settings:set(setting.name, new_value)

		elseif setting.type == "float" then
			local new_value = tonumber(fields["te_setting_value"])
			if not new_value then
				this.data.error_message = fgettext_ne("Please enter a valid number.")
				this.data.entered_text = fields["te_setting_value"]
				core.update_formspec(this:get_formspec())
				return true
			end
			core.settings:set(setting.name, new_value)

		elseif setting.type == "flags" then
			local new_value = fields["te_setting_value"]
			for _,value in ipairs(new_value:split(",", true)) do
				value = value:trim()
				local possible = "," .. setting.possible .. ","
				if not possible:find("," .. value .. ",", 0, true) then
					this.data.error_message = fgettext_ne("\"$1\" is not a valid flag.", value)
					this.data.entered_text = fields["te_setting_value"]
					core.update_formspec(this:get_formspec())
					return true
				end
			end
			core.settings:set(setting.name, new_value)

		else
			local new_value = fields["te_setting_value"]
			core.settings:set(setting.name, new_value)
		end
		core.settings:write()
		this:delete()
		return true
	end

	if fields["btn_cancel"] then
		this:delete()
		return true
	end

	if fields["btn_browser_path"] then
		core.show_file_open_dialog("dlg_browse_path", fgettext_ne("Select path"))
	end

	if fields["dlg_browse_path_accepted"] then
		this.data.selected_path = fields["dlg_browse_path_accepted"]
		core.update_formspec(this:get_formspec())
	end

	return false
end

local function create_settings_formspec(tabview, name, tabdata)
	local formspec = "size[12,6.5;true]" ..
			"tablecolumns[color;tree;text,width=32;text]" ..
			"tableoptions[background=#00000000;border=false]" ..
			"field[0.3,0.1;10.2,1;search_string;;" .. core.formspec_escape(search_string) .. "]" ..
			"field_close_on_enter[search_string;false]" ..
			"button[10.2,-0.2;2,1;search;" .. fgettext("Search") .. "]" ..
			"table[0,0.8;12,4.5;list_settings;"

	local current_level = 0
	for _, entry in ipairs(settings) do
		local name
		if not core.settings:get_bool("main_menu_technical_settings") and entry.readable_name then
			name = fgettext_ne(entry.readable_name)
		else
			name = entry.name
		end

		if entry.type == "category" then
			current_level = entry.level
			formspec = formspec .. "#FFFF00," .. current_level .. "," .. fgettext(name) .. ",,"

		elseif entry.type == "bool" then
			local value = get_current_value(entry)
			if core.is_yes(value) then
				value = fgettext("Enabled")
			else
				value = fgettext("Disabled")
			end
			formspec = formspec .. "," .. (current_level + 1) .. "," .. core.formspec_escape(name) .. ","
					.. value .. ","

		elseif entry.type == "key" then
			-- ignore key settings, since we have a special dialog for them

		else
			formspec = formspec .. "," .. (current_level + 1) .. "," .. core.formspec_escape(name) .. ","
					.. core.formspec_escape(get_current_value(entry)) .. ","
		end
	end

	if #settings > 0 then
		formspec = formspec:sub(1, -2) -- remove trailing comma
	end
	formspec = formspec .. ";" .. selected_setting .. "]" ..
			"button[0,6;4,1;btn_back;".. fgettext("< Back to Settings page") .. "]" ..
			"button[10,6;2,1;btn_edit;" .. fgettext("Edit") .. "]" ..
			"button[7,6;3,1;btn_restore;" .. fgettext("Restore Default") .. "]" ..
			"checkbox[0,5.3;cb_tech_settings;" .. fgettext("Show technical names") .. ";"
					.. dump(core.settings:get_bool("main_menu_technical_settings")) .. "]"

	return formspec
end

local function handle_settings_buttons(this, fields, tabname, tabdata)
	local list_enter = false
	if fields["list_settings"] then
		selected_setting = core.get_table_index("list_settings")
		if core.explode_table_event(fields["list_settings"]).type == "DCL" then
			-- Directly toggle booleans
			local setting = settings[selected_setting]
			if setting and setting.type == "bool" then
				local current_value = get_current_value(setting)
				core.settings:set_bool(setting.name, not core.is_yes(current_value))
				core.settings:write()
				return true
			else
				list_enter = true
			end
		else
			return true
		end
	end

	if fields.search or fields.key_enter_field == "search_string" then
		if search_string == fields.search_string then
			if selected_setting > 0 then
				-- Go to next result on enter press
				local i = selected_setting + 1
				local looped = false
				while i > #settings or settings[i].type == "category" do
					i = i + 1
					if i > #settings then
						-- Stop infinte looping
						if looped then
							return false
						end
						i = 1
						looped = true
					end
				end
				selected_setting = i
				core.update_formspec(this:get_formspec())
				return true
			end
		else
			-- Search for setting
			search_string = fields.search_string
			settings, selected_setting = filter_settings(full_settings, search_string)
			core.update_formspec(this:get_formspec())
		end
		return true
	end

	if fields["btn_edit"] or list_enter then
		local setting = settings[selected_setting]
		if setting and setting.type ~= "category" then
			local edit_dialog = dialog_create("change_setting", create_change_setting_formspec,
					handle_change_setting_buttons)
			edit_dialog:set_parent(this)
			this:hide()
			edit_dialog:show()
		end
		return true
	end

	if fields["btn_restore"] then
		local setting = settings[selected_setting]
		if setting and setting.type ~= "category" then
			core.settings:set(setting.name, setting.default)
			core.settings:write()
			core.update_formspec(this:get_formspec())
		end
		return true
	end

	if fields["btn_back"] then
		this:delete()
		return true
	end

	if fields["cb_tech_settings"] then
		core.settings:set("main_menu_technical_settings", fields["cb_tech_settings"])
		core.settings:write()
		core.update_formspec(this:get_formspec())
		return true
	end

	return false
end

function create_adv_settings_dlg()
	local dlg = dialog_create("settings_advanced",
				create_settings_formspec,
				handle_settings_buttons,
				nil)

				return dlg
end

-- Generate minetest.conf.example and settings_translation_file.cpp

--assert(loadfile(core.get_builtin_path()..DIR_DELIM.."mainmenu"..DIR_DELIM.."generate_from_settingtypes.lua"))(parse_config_file(true, false))


code:
--Minetest
--Copyright (C) 2013 sapier
--
--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.

gamemgr = {}

--------------------------------------------------------------------------------
function gamemgr.find_by_gameid(gameid)
	for i=1,#gamemgr.games,1 do
		if gamemgr.games[i].id == gameid then
			return gamemgr.games[i], i
		end
	end
	return nil, nil
end

--------------------------------------------------------------------------------
function gamemgr.get_game_mods(gamespec, retval)
	if gamespec ~= nil and
		gamespec.gamemods_path ~= nil and
		gamespec.gamemods_path ~= "" then
		get_mods(gamespec.gamemods_path, retval)
	end
end

--------------------------------------------------------------------------------
function gamemgr.get_game_modlist(gamespec)
	local retval = ""
	local game_mods = {}
	gamemgr.get_game_mods(gamespec, game_mods)
	for i=1,#game_mods,1 do
		if retval ~= "" then
			retval = retval..","
		end
		retval = retval .. game_mods[i].name
	end
	return retval
end

--------------------------------------------------------------------------------
function gamemgr.get_game(index)
	if index > 0 and index <= #gamemgr.games then
		return gamemgr.games[index]
	end

	return nil
end

--------------------------------------------------------------------------------
function gamemgr.update_gamelist()
	gamemgr.games = core.get_games()
end

--------------------------------------------------------------------------------
function gamemgr.gamelist()
	local retval = ""
	if #gamemgr.games > 0 then
		retval = retval .. core.formspec_escape(gamemgr.games[1].name)

		for i=2,#gamemgr.games,1 do
			retval = retval .. "," .. core.formspec_escape(gamemgr.games[i].name)
		end
	end
	return retval
end

--------------------------------------------------------------------------------
-- read initial data
--------------------------------------------------------------------------------
gamemgr.update_gamelist()


code:
local settings = ...

local concat = table.concat
local insert = table.insert
local sprintf = string.format
local rep = string.rep

local minetest_example_header = [[
#    This file contains a list of all available settings and their default value for minetest.conf

#    By default, all the settings are commented and not functional.
#    Uncomment settings by removing the preceding #.

#    minetest.conf is read by default from:
#    ../minetest.conf
#    ../../minetest.conf
#    Any other path can be chosen by passing the path as a parameter
#    to the program, eg. "minetest.exe --config ../minetest.conf.example".

#    Further documentation:
#    http://wiki.minetest.net/

]]

local function create_minetest_conf_example()
	local result = { minetest_example_header }
	for _, entry in ipairs(settings) do
		if entry.type == "category" then
			if entry.level == 0 then
				insert(result, "#\n# " .. entry.name .. "\n#\n\n")
			else
				insert(result, rep("#", entry.level))
				insert(result, "# " .. entry.name .. "\n\n")
			end
		else
			if entry.comment ~= "" then
				for _, comment_line in ipairs(entry.comment:split("\n", true)) do
					insert(result, "#    " .. comment_line .. "\n")
				end
			end
			insert(result, "#    type: " .. entry.type)
			if entry.min then
				insert(result, " min: " .. entry.min)
			end
			if entry.max then
				insert(result, " max: " .. entry.max)
			end
			if entry.values then
				insert(result, " values: " .. concat(entry.values, ", "))
			end
			if entry.possible then
				insert(result, " possible values: " .. entry.possible:gsub(",", ", "))
			end
			insert(result, "\n")
			local append
			if entry.default ~= "" then
				append = " " .. entry.default
			end
			insert(result, sprintf("# %s =%s\n\n", entry.name, append or ""))
		end
	end
	return concat(result)
end

local translation_file_header = [[
// This file is automatically generated
// It conatins a bunch of fake gettext calls, to tell xgettext about the strings in config files
// To update it, refer to the bottom of builtin/mainmenu/dlg_settings_advanced.lua

fake_function() {]]

local function create_translation_file()
	local result = { translation_file_header }
	for _, entry in ipairs(settings) do
		if entry.type == "category" then
			insert(result, sprintf("\tgettext(%q);", entry.name))
		else
			if entry.readable_name then
				insert(result, sprintf("\tgettext(%q);", entry.readable_name))
			end
			if entry.comment ~= "" then
				local comment_escaped = entry.comment:gsub("\n", "\\n")
				comment_escaped = comment_escaped:gsub("\"", "\\\"")
				insert(result, "\tgettext(\"" .. comment_escaped .. "\");")
			end
		end
	end
	insert(result, "}\n")
	return concat(result, "\n")
end

local file = assert(io.open("minetest.conf.example", "w"))
file:write(create_minetest_conf_example())
file:close()

file = assert(io.open("src/settings_translation_file.cpp", "w"))
file:write(create_translation_file())
file:close()


code:
--Minetest
--Copyright (C) 2014 sapier
--
--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.

mt_color_grey  = "#AAAAAA"
mt_color_blue  = "#6389FF"
mt_color_green = "#72FF63"
mt_color_dark_green = "#25C191"

--for all other colors ask sfan5 to complete his work!

local menupath = core.get_mainmenu_path()
local basepath = core.get_builtin_path()
defaulttexturedir = core.get_texturepath_share() .. DIR_DELIM .. "base" ..
					DIR_DELIM .. "pack" .. DIR_DELIM

dofile(basepath .. DIR_DELIM .. "common" .. DIR_DELIM .. "async_event.lua")
dofile(basepath .. DIR_DELIM .. "common" .. DIR_DELIM .. "filterlist.lua")
dofile(basepath .. DIR_DELIM .. "fstk" .. DIR_DELIM .. "buttonbar.lua")
dofile(basepath .. DIR_DELIM .. "fstk" .. DIR_DELIM .. "dialog.lua")
dofile(basepath .. DIR_DELIM .. "fstk" .. DIR_DELIM .. "tabview.lua")
dofile(basepath .. DIR_DELIM .. "fstk" .. DIR_DELIM .. "ui.lua")
dofile(menupath .. DIR_DELIM .. "common.lua")
dofile(menupath .. DIR_DELIM .. "gamemgr.lua")
dofile(menupath .. DIR_DELIM .. "modmgr.lua")
dofile(menupath .. DIR_DELIM .. "store.lua")
dofile(menupath .. DIR_DELIM .. "textures.lua")

dofile(menupath .. DIR_DELIM .. "dlg_config_world.lua")
dofile(menupath .. DIR_DELIM .. "dlg_settings_advanced.lua")
if PLATFORM ~= "Android" then
	dofile(menupath .. DIR_DELIM .. "dlg_create_world.lua")
	dofile(menupath .. DIR_DELIM .. "dlg_delete_mod.lua")
	dofile(menupath .. DIR_DELIM .. "dlg_delete_world.lua")
	dofile(menupath .. DIR_DELIM .. "dlg_rename_modpack.lua")
end

local tabs = {}

tabs.settings = dofile(menupath .. DIR_DELIM .. "tab_settings.lua")
tabs.mods = dofile(menupath .. DIR_DELIM .. "tab_mods.lua")
tabs.credits = dofile(menupath .. DIR_DELIM .. "tab_credits.lua")
if PLATFORM == "Android" then
	tabs.simple_main = dofile(menupath .. DIR_DELIM .. "tab_simple_main.lua")
else
	tabs.local_game = dofile(menupath .. DIR_DELIM .. "tab_local.lua")
	tabs.play_online = dofile(menupath .. DIR_DELIM .. "tab_online.lua")
	tabs.texturepacks = dofile(menupath .. DIR_DELIM .. "tab_texturepacks.lua")
end

--------------------------------------------------------------------------------
local function main_event_handler(tabview, event)
	if event == "MenuQuit" then
		core.close()
	end
	return true
end

--------------------------------------------------------------------------------
local function init_globals()
	-- Init gamedata
	gamedata.worldindex = 0

	if PLATFORM == "Android" then
		local world_list = core.get_worlds()
		local world_index

		local found_singleplayerworld = false
		for i, world in ipairs(world_list) do
			if world.name == "singleplayerworld" then
				found_singleplayerworld = true
				world_index = i
				break
			end
		end

		if not found_singleplayerworld then
			core.create_world("singleplayerworld", 1)

			world_list = core.get_worlds()

			for i, world in ipairs(world_list) do
				if world.name == "singleplayerworld" then
					world_index = i
					break
				end
			end
		end

		gamedata.worldindex = world_index
	else
		menudata.worldlist = filterlist.create(
			core.get_worlds,
			compare_worlds,
			-- Unique id comparison function
			function(element, uid)
				return element.name == uid
			end,
			-- Filter function
			function(element, gameid)
				return element.gameid == gameid
			end
		)

		menudata.worldlist:add_sort_mechanism("alphabetic", sort_worlds_alphabetic)
		menudata.worldlist:set_sortmode("alphabetic")

		if not core.settings:get("menu_last_game") then
			local default_game = core.settings:get("default_game") or "minetest"
			core.settings:set("menu_last_game", default_game)
		end

		mm_texture.init()
	end

	-- Create main tabview
	local tv_main = tabview_create("maintab", {x = 12, y = 5.4}, {x = 0, y = 0})

	if PLATFORM == "Android" then
		tv_main:add(tabs.simple_main)
		tv_main:add(tabs.settings)
	else
		tv_main:set_autosave_tab(true)
		tv_main:add(tabs.local_game)
		tv_main:add(tabs.play_online)
		tv_main:add(tabs.settings)
		tv_main:add(tabs.texturepacks)
	end

	tv_main:add(tabs.mods)
	tv_main:add(tabs.credits)

	tv_main:set_global_event_handler(main_event_handler)
	tv_main:set_fixed_size(false)

	if PLATFORM ~= "Android" then
		tv_main:set_tab(core.settings:get("maintab_LAST"))
	end
	ui.set_default("maintab")
	tv_main:show()

	-- Create modstore ui
	if PLATFORM == "Android" then
		modstore.init({x = 12, y = 6}, 3, 2)
	else
		modstore.init({x = 12, y = 8}, 4, 3)
	end

	ui.update()

	core.sound_play("main_menu", true)
end

init_globals()


code:
-- helper file to be able to debug the simple menu on PC
-- without messing around with actual menu code!
PLATFORM = "Android"
dofile("builtin/mainmenu/init.lua")


code:
--Minetest
--Copyright (C) 2013 sapier
--
--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.

--------------------------------------------------------------------------------
function get_mods(path,retval,modpack)
	local mods = core.get_dir_list(path, true)

	for _, name in ipairs(mods) do
		if name:sub(1, 1) ~= "." then
			local prefix = path .. DIR_DELIM .. name .. DIR_DELIM
			local toadd = {}
			retval[#retval + 1] = toadd

			local mod_conf = Settings(prefix .. "mod.conf"):to_table()
			if mod_conf.name then
				name = mod_conf.name
			end

			toadd.name = name
			toadd.path = prefix

			if modpack ~= nil and modpack ~= "" then
				toadd.modpack = modpack
			else
				local modpackfile = io.open(prefix .. "modpack.txt")
				if modpackfile then
					modpackfile:close()
					toadd.is_modpack = true
					get_mods(prefix, retval, name)
				end
			end
		end
	end
end

--modmanager implementation
modmgr = {}

--------------------------------------------------------------------------------
function modmgr.extract(modfile)
	if modfile.type == "zip" then
		local tempfolder = os.tempfolder()

		if tempfolder ~= nil and
			tempfolder ~= "" then
			core.create_dir(tempfolder)
			if core.extract_zip(modfile.name,tempfolder) then
				return tempfolder
			end
		end
	end
	return nil
end

-------------------------------------------------------------------------------
function modmgr.getbasefolder(temppath)

	if temppath == nil then
		return {
		type = "invalid",
		path = ""
		}
	end

	local testfile = io.open(temppath .. DIR_DELIM .. "init.lua","r")
	if testfile ~= nil then
		testfile:close()
		return {
				type="mod",
				path=temppath
				}
	end

	testfile = io.open(temppath .. DIR_DELIM .. "modpack.txt","r")
	if testfile ~= nil then
		testfile:close()
		return {
				type="modpack",
				path=temppath
				}
	end

	local subdirs = core.get_dir_list(temppath, true)

	--only single mod or modpack allowed
	if #subdirs ~= 1 then
		return {
			type = "invalid",
			path = ""
			}
	end

	testfile =
	io.open(temppath .. DIR_DELIM .. subdirs[1] ..DIR_DELIM .."init.lua","r")
	if testfile ~= nil then
		testfile:close()
		return {
			type="mod",
			path= temppath .. DIR_DELIM .. subdirs[1]
			}
	end

	testfile =
	io.open(temppath .. DIR_DELIM .. subdirs[1] ..DIR_DELIM .."modpack.txt","r")
	if testfile ~= nil then
		testfile:close()
		return {
			type="modpack",
			path=temppath ..  DIR_DELIM .. subdirs[1]
			}
	end

	return {
		type = "invalid",
		path = ""
		}
end

--------------------------------------------------------------------------------
function modmgr.isValidModname(modpath)
	if modpath:find("-") ~= nil then
		return false
	end

	return true
end

--------------------------------------------------------------------------------
function modmgr.parse_register_line(line)
	local pos1 = line:find("\"")
	local pos2 = nil
	if pos1 ~= nil then
		pos2 = line:find("\"",pos1+1)
	end

	if pos1 ~= nil and pos2 ~= nil then
		local item = line:sub(pos1+1,pos2-1)

		if item ~= nil and
			item ~= "" then
			local pos3 = item:find(":")

			if pos3 ~= nil then
				local retval = item:sub(1,pos3-1)
				if retval ~= nil and
					retval ~= "" then
					return retval
				end
			end
		end
	end
	return nil
end

--------------------------------------------------------------------------------
function modmgr.parse_dofile_line(modpath,line)
	local pos1 = line:find("\"")
	local pos2 = nil
	if pos1 ~= nil then
		pos2 = line:find("\"",pos1+1)
	end

	if pos1 ~= nil and pos2 ~= nil then
		local filename = line:sub(pos1+1,pos2-1)

		if filename ~= nil and
			filename ~= "" and
			filename:find(".lua") then
			return modmgr.identify_modname(modpath,filename)
		end
	end
	return nil
end

--------------------------------------------------------------------------------
function modmgr.identify_modname(modpath,filename)
	local testfile = io.open(modpath .. DIR_DELIM .. filename,"r")
	if testfile ~= nil then
		local line = testfile:read()

		while line~= nil do
			local modname = nil

			if line:find("minetest.register_tool") then
				modname = modmgr.parse_register_line(line)
			end

			if line:find("minetest.register_craftitem") then
				modname = modmgr.parse_register_line(line)
			end

			if line:find("minetest.register_node") then
				modname = modmgr.parse_register_line(line)
			end

			if line:find("dofile") then
				modname = modmgr.parse_dofile_line(modpath,line)
			end

			if modname ~= nil then
				testfile:close()
				return modname
			end

			line = testfile:read()
		end
		testfile:close()
	end

	return nil
end
--------------------------------------------------------------------------------
function modmgr.render_modlist(render_list)
	local retval = ""

	if render_list == nil then
		if modmgr.global_mods == nil then
			modmgr.refresh_globals()
		end
		render_list = modmgr.global_mods
	end

	local list = render_list:get_list()
	local last_modpack = nil
	local retval = {}
	for i, v in ipairs(list) do
		local color = ""
		if v.is_modpack then
			local rawlist = render_list:get_raw_list()
			color = mt_color_dark_green

			for j = 1, #rawlist, 1 do
				if rawlist[j].modpack == list[i].name and
						rawlist[j].enabled ~= true then
					-- Modpack not entirely enabled so showing as grey
					color = mt_color_grey
					break
				end
			end
		elseif v.is_game_content then
			color = mt_color_blue
		elseif v.enabled then
			color = mt_color_green
		end

		retval[#retval + 1] = color
		if v.modpack ~= nil or v.typ == "game_mod" then
			retval[#retval + 1] = "1"
		else
			retval[#retval + 1] = "0"
		end
		retval[#retval + 1] = core.formspec_escape(v.name)
	end

	return table.concat(retval, ",")
end

--------------------------------------------------------------------------------
function modmgr.get_dependencies(modfolder)
	local toadd_hard = ""
	local toadd_soft = ""
	if modfolder ~= nil then
		local filename = modfolder ..
					DIR_DELIM .. "depends.txt"

		local hard_dependencies = {}
		local soft_dependencies = {}
		local dependencyfile = io.open(filename,"r")
		if dependencyfile then
			local dependency = dependencyfile:read("*l")
			while dependency do
				dependency = dependency:gsub("\r", "")
				if string.sub(dependency, -1, -1) == "?" then
					table.insert(soft_dependencies, string.sub(dependency, 1, -2))
				else
					table.insert(hard_dependencies, dependency)
				end
				dependency = dependencyfile:read()
			end
			dependencyfile:close()
		end
		toadd_hard = table.concat(hard_dependencies, ",")
		toadd_soft = table.concat(soft_dependencies, ",")
	end

	return toadd_hard, toadd_soft
end

--------------------------------------------------------------------------------
function modmgr.get_worldconfig(worldpath)
	local filename = worldpath ..
				DIR_DELIM .. "world.mt"

	local worldfile = Settings(filename)

	local worldconfig = {}
	worldconfig.global_mods = {}
	worldconfig.game_mods = {}

	for key,value in pairs(worldfile:to_table()) do
		if key == "gameid" then
			worldconfig.id = value
		elseif key:sub(0, 9) == "load_mod_" then
			worldconfig.global_mods[key] = core.is_yes(value)
		else
			worldconfig[key] = value
		end
	end

	--read gamemods
	local gamespec = gamemgr.find_by_gameid(worldconfig.id)
	gamemgr.get_game_mods(gamespec, worldconfig.game_mods)

	return worldconfig
end

--------------------------------------------------------------------------------
function modmgr.installmod(modfilename,basename)
	local modfile = modmgr.identify_filetype(modfilename)
	local modpath = modmgr.extract(modfile)

	if modpath == nil then
		gamedata.errormessage = fgettext("Install Mod: file: \"$1\"", modfile.name) ..
			fgettext("\nInstall Mod: unsupported filetype \"$1\" or broken archive", modfile.type)
		return
	end

	local basefolder = modmgr.getbasefolder(modpath)

	if basefolder.type == "modpack" then
		local clean_path = nil

		if basename ~= nil then
			clean_path = "mp_" .. basename
		end

		if clean_path == nil then
			clean_path = get_last_folder(cleanup_path(basefolder.path))
		end

		if clean_path ~= nil then
			local targetpath = core.get_modpath() .. DIR_DELIM .. clean_path
			if not core.copy_dir(basefolder.path,targetpath) then
				gamedata.errormessage = fgettext("Failed to install $1 to $2", basename, targetpath)
			end
		else
			gamedata.errormessage = fgettext("Install Mod: unable to find suitable foldername for modpack $1", modfilename)
		end
	end

	if basefolder.type == "mod" then
		local targetfolder = basename

		if targetfolder == nil then
			targetfolder = modmgr.identify_modname(basefolder.path,"init.lua")
		end

		--if heuristic failed try to use current foldername
		if targetfolder == nil then
			targetfolder = get_last_folder(basefolder.path)
		end

		if targetfolder ~= nil and modmgr.isValidModname(targetfolder) then
			local targetpath = core.get_modpath() .. DIR_DELIM .. targetfolder
			core.copy_dir(basefolder.path,targetpath)
		else
			gamedata.errormessage = fgettext("Install Mod: unable to find real modname for: $1", modfilename)
		end
	end

	core.delete_dir(modpath)

	modmgr.refresh_globals()

end

--------------------------------------------------------------------------------
function modmgr.preparemodlist(data)
	local retval = {}

	local global_mods = {}
	local game_mods = {}

	--read global mods
	local modpath = core.get_modpath()

	if modpath ~= nil and
		modpath ~= "" then
		get_mods(modpath,global_mods)
	end

	for i=1,#global_mods,1 do
		global_mods[i].typ = "global_mod"
		retval[#retval + 1] = global_mods[i]
	end

	--read game mods
	local gamespec = gamemgr.find_by_gameid(data.gameid)
	gamemgr.get_game_mods(gamespec, game_mods)

	if #game_mods > 0 then
		-- Add title
		retval[#retval + 1] = {
			typ = "game",
			is_game_content = true,
			name = fgettext("Subgame Mods")
		}
	end

	for i=1,#game_mods,1 do
		game_mods[i].typ = "game_mod"
		game_mods[i].is_game_content = true
		retval[#retval + 1] = game_mods[i]
	end

	if data.worldpath == nil then
		return retval
	end

	--read world mod configuration
	local filename = data.worldpath ..
				DIR_DELIM .. "world.mt"

	local worldfile = Settings(filename)

	for key,value in pairs(worldfile:to_table()) do
		if key:sub(1, 9) == "load_mod_" then
			key = key:sub(10)
			local element = nil
			for i=1,#retval,1 do
				if retval[i].name == key and
					not retval[i].is_modpack then
					element = retval[i]
					break
				end
			end
			if element ~= nil then
				element.enabled = core.is_yes(value)
			else
				core.log("info", "Mod: " .. key .. " " .. dump(value) .. " but not found")
			end
		end
	end

	return retval
end

--------------------------------------------------------------------------------
function modmgr.comparemod(elem1,elem2)
	if elem1 == nil or elem2 == nil then
		return false
	end
	if elem1.name ~= elem2.name then
		return false
	end
	if elem1.is_modpack ~= elem2.is_modpack then
		return false
	end
	if elem1.typ ~= elem2.typ then
		return false
	end
	if elem1.modpack ~= elem2.modpack then
		return false
	end

	if elem1.path ~= elem2.path then
		return false
	end

	return true
end

--------------------------------------------------------------------------------
function modmgr.mod_exists(basename)

	if modmgr.global_mods == nil then
		modmgr.refresh_globals()
	end

	if modmgr.global_mods:raw_index_by_uid(basename) > 0 then
		return true
	end

	return false
end

--------------------------------------------------------------------------------
function modmgr.get_global_mod(idx)

	if modmgr.global_mods == nil then
		return nil
	end

	if idx == nil or idx < 1 or
		idx > modmgr.global_mods:size() then
		return nil
	end

	return modmgr.global_mods:get_list()[idx]
end

--------------------------------------------------------------------------------
function modmgr.refresh_globals()
	modmgr.global_mods = filterlist.create(
					modmgr.preparemodlist, --refresh
					modmgr.comparemod, --compare
					function(element,uid) --uid match
						if element.name == uid then
							return true
						end
					end,
					nil, --filter
					{}
					)
	modmgr.global_mods:add_sort_mechanism("alphabetic", sort_mod_list)
	modmgr.global_mods:set_sortmode("alphabetic")
end

--------------------------------------------------------------------------------
function modmgr.identify_filetype(name)

	if name:sub(-3):lower() == "zip" then
		return {
				name = name,
				type = "zip"
				}
	end

	if name:sub(-6):lower() == "tar.gz" or
		name:sub(-3):lower() == "tgz"then
		return {
				name = name,
				type = "tgz"
				}
	end

	if name:sub(-6):lower() == "tar.bz2" then
		return {
				name = name,
				type = "tbz"
				}
	end

	if name:sub(-2):lower() == "7z" then
		return {
				name = name,
				type = "7z"
				}
	end

	return {
		name = name,
		type = "ukn"
	}
end


code:
--Minetest
--Copyright (C) 2013 sapier
--
--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.

--------------------------------------------------------------------------------

--modstore implementation
modstore = {}

--------------------------------------------------------------------------------
-- @function [parent=#modstore] init
function modstore.init(size, unsortedmods, searchmods)

	modstore.mods_on_unsorted_page = unsortedmods
	modstore.mods_on_search_page = searchmods
	modstore.modsperpage = modstore.mods_on_unsorted_page

	modstore.basetexturedir = core.get_texturepath() .. DIR_DELIM .. "base" ..
						DIR_DELIM .. "pack" .. DIR_DELIM

	modstore.lastmodtitle = ""
	modstore.last_search = ""

	modstore.searchlist = filterlist.create(
		function()
			if modstore.modlist_unsorted ~= nil and
				modstore.modlist_unsorted.data ~= nil then
				return modstore.modlist_unsorted.data
			end
			return {}
		end,
		function(element,modid)
			if element.id == modid then
				return true
			end
			return false
		end, --compare fct
		nil, --uid match fct
		function(element,substring)
			if substring == nil or
				substring == "" then
				return false
			end
			substring = substring:upper()

			if element.title ~= nil and
				element.title:upper():find(substring) ~= nil then
				return true
			end

			if element.details ~= nil and
				element.details.author ~= nil and
				element.details.author:upper():find(substring) ~= nil then
				return true
			end

			if element.details ~= nil and
				element.details.description ~= nil and
				element.details.description:upper():find(substring) ~= nil then
				return true
			end
			return false
		end --filter fct
		)

	modstore.current_list = nil

	modstore.tv_store = tabview_create("modstore",size,{x=0,y=0})

	modstore.tv_store:set_global_event_handler(modstore.handle_events)

	modstore.tv_store:add(
		{
		name = "unsorted",
		caption = fgettext("Unsorted"),
		cbf_formspec       = modstore.unsorted_tab,
		cbf_button_handler = modstore.handle_buttons,
		on_change          =
			function() modstore.modsperpage = modstore.mods_on_unsorted_page end
		}
	)

	modstore.tv_store:add(
		{
		name = "search",
		caption            = fgettext("Search"),
		cbf_formspec       = modstore.getsearchpage,
		cbf_button_handler = modstore.handle_buttons,
		on_change          = modstore.activate_search_tab
		}
	)
end

--------------------------------------------------------------------------------
-- @function [parent=#modstore] nametoindex
function modstore.nametoindex(name)

	for i=1,#modstore.tabnames,1 do
		if modstore.tabnames[i] == name then
			return i
		end
	end

	return 1
end

--------------------------------------------------------------------------------
-- @function [parent=#modstore] showdownloading
function modstore.showdownloading(title)
	local new_dlg = dialog_create("store_downloading",
		function(data)
			return "size[6,2]label[0.25,0.75;" ..
				fgettext("Downloading $1, please wait...", data.title) .. "]"
		end,
		function(this,fields)
			if fields["btn_hidden_close_download"] ~= nil then
				if fields["btn_hidden_close_download"].successfull then
					modstore.lastmodentry = fields["btn_hidden_close_download"]
					modstore.successfulldialog(this)
				else
					this.parent:show()
					this:delete()
					modstore.lastmodtitle = ""
				end

				return true
			end

			return false
		end,
		nil)

	new_dlg:set_parent(modstore.tv_store)
	modstore.tv_store:hide()
	new_dlg.data.title = title
	new_dlg:show()
end

--------------------------------------------------------------------------------
-- @function [parent=#modstore] successfulldialog
function modstore.successfulldialog(downloading_dlg)
	local new_dlg = dialog_create("store_downloading",
		function(data)
			local retval = ""
			retval = retval .. "size[6,2,true]"
			if modstore.lastmodentry ~= nil then
				retval = retval .. "label[0,0.25;" .. fgettext("Successfully installed:") .. "]"
				retval = retval .. "label[3,0.25;" .. modstore.lastmodentry.moddetails.title .. "]"
				retval = retval .. "label[0,0.75;" .. fgettext("Shortname:") .. "]"
				retval = retval .. "label[3,0.75;" .. core.formspec_escape(modstore.lastmodentry.moddetails.basename) .. "]"
			end
			retval = retval .. "button[2.2,1.5;1.5,0.5;btn_confirm_mod_successfull;" .. fgettext("Ok") .. "]"
			return retval
		end,
		function(this,fields)
			if fields["btn_confirm_mod_successfull"] ~= nil then
				this.parent:show()
				downloading_dlg:delete()
				this:delete()

				return true
			end

			return false
		end,
		nil)

	new_dlg:set_parent(modstore.tv_store)
	modstore.tv_store:hide()
	new_dlg:show()
end

--------------------------------------------------------------------------------
-- @function [parent=#modstore] handle_buttons
function modstore.handle_buttons(parent, fields, name, data)

	if fields["btn_modstore_page_up"] then
		if modstore.current_list ~= nil and modstore.current_list.page > 0 then
			modstore.current_list.page = modstore.current_list.page - 1
		end
		return true
	end

	if fields["btn_modstore_page_down"] then
		if modstore.current_list ~= nil and
			modstore.current_list.page <modstore.current_list.pagecount-1 then
			modstore.current_list.page = modstore.current_list.page +1
		end
		return true
	end

	if fields["btn_modstore_search"] or
		(fields["key_enter"] and fields["te_modstore_search"] ~= nil) then
		modstore.last_search = fields["te_modstore_search"]
		filterlist.set_filtercriteria(modstore.searchlist,fields["te_modstore_search"])
		filterlist.refresh(modstore.searchlist)
		modstore.currentlist = {
			page = 0,
			pagecount =  math.ceil(filterlist.size(modstore.searchlist) / modstore.modsperpage),
			data = filterlist.get_list(modstore.searchlist),
		}
		return true
	end

	if fields["btn_modstore_close"] then
		local maintab = ui.find_by_name("maintab")
		parent:hide()
		maintab:show()
		return true
	end

	for key,value in pairs(fields) do
		local foundat = key:find("btn_install_mod_")
		if ( foundat == 1) then
			local modid = tonumber(key:sub(17))
			for i=1,#modstore.modlist_unsorted.data,1 do
				if modstore.modlist_unsorted.data[i].id == modid then
					local moddetails = modstore.modlist_unsorted.data[i].details
					modstore.lastmodtitle = moddetails.title

					if not core.handle_async(
						function(param)
							local fullurl = core.settings:get("modstore_download_url") ..
											param.moddetails.download_url

							if param.version ~= nil then
								local found = false
								for i=1,#param.moddetails.versions, 1 do
									if param.moddetails.versions[i].date:sub(1,10) == param.version then
										fullurl = core.settings:get("modstore_download_url") ..
														param.moddetails.versions[i].download_url
										found = true
									end
								end

								if not found then
									core.log("error","no download url found for version " .. dump(param.version))
									return {
										moddetails = param.moddetails,
										successfull = false
									}
								end
							end

							if core.download_file(fullurl,param.filename) then
								return {
									texturename = param.texturename,
									moddetails = param.moddetails,
									filename = param.filename,
									successfull = true
								}
							else
								core.log("error","downloading " .. dump(fullurl) .. " failed")
								return {
									moddetails = param.moddetails,
									successfull = false
								}
							end
						end,
						{
							moddetails = moddetails,
							version = fields["dd_version" .. modid],
							filename = os.tempfolder() .. "_MODNAME_" .. moddetails.basename .. ".zip",
							texturename = modstore.modlist_unsorted.data[i].texturename
						},
						function(result)
							--print("Result from async: " .. dump(result.successfull))
							if result.successfull then
								modmgr.installmod(result.filename,result.moddetails.basename)
								os.remove(result.filename)
							else
								gamedata.errormessage = "Failed to download " .. result.moddetails.title
							end

							if gamedata.errormessage == nil then
								core.button_handler({btn_hidden_close_download=result})
							else
								core.button_handler({btn_hidden_close_download={successfull=false}})
							end
						end
					) then
						print("ERROR: async event failed")
						gamedata.errormessage = "Failed to download " .. modstore.lastmodtitle
					end

					modstore.showdownloading(modstore.lastmodtitle)
					return true
				end
			end
			return true
		end
	end

	return false
end

--------------------------------------------------------------------------------
-- @function [parent=#modstore] handle_events
function modstore.handle_events(this,event)
	if (event == "MenuQuit") then
		this:hide()
		return true
	end
end

--------------------------------------------------------------------------------
-- @function [parent=#modstore] update_modlist
function modstore.update_modlist()
	modstore.modlist_unsorted = {}
	modstore.modlist_unsorted.data = {}
	modstore.modlist_unsorted.pagecount = 1
	modstore.modlist_unsorted.page = 0

	core.handle_async(
		function(param)
			return core.get_modstore_list()
		end,
		nil,
		function(result)
			if result ~= nil then
				modstore.modlist_unsorted = {}
				modstore.modlist_unsorted.data = result

				if modstore.modlist_unsorted.data ~= nil then
					modstore.modlist_unsorted.pagecount =
						math.ceil((#modstore.modlist_unsorted.data / modstore.modsperpage))
				else
					modstore.modlist_unsorted.data = {}
					modstore.modlist_unsorted.pagecount = 1
				end
				modstore.modlist_unsorted.page = 0
				modstore.fetchdetails()
				core.event_handler("Refresh")
			end
		end
	)
end

--------------------------------------------------------------------------------
-- @function [parent=#modstore] fetchdetails
function modstore.fetchdetails()

	for i=1,#modstore.modlist_unsorted.data,1 do
		core.handle_async(
		function(param)
			param.details = core.get_modstore_details(tostring(param.modid))
			return param
		end,
		{
			modid=modstore.modlist_unsorted.data[i].id,
			listindex=i
		},
		function(result)
			if result ~= nil and
				modstore.modlist_unsorted ~= nil
				and modstore.modlist_unsorted.data ~= nil and
				modstore.modlist_unsorted.data[result.listindex] ~= nil and
				modstore.modlist_unsorted.data[result.listindex].id ~= nil then

				modstore.modlist_unsorted.data[result.listindex].details = result.details
				core.event_handler("Refresh")
			end
		end
		)
	end
end

--------------------------------------------------------------------------------
-- @function [parent=#modstore] getscreenshot
function modstore.getscreenshot(ypos,listentry)

	if	listentry.details ~= nil and
		(listentry.details.screenshot_url == nil or
		listentry.details.screenshot_url == "") then

		if listentry.texturename == nil then
			listentry.texturename = defaulttexturedir .. "no_screenshot.png"
		end

		return "image[0,".. ypos .. ";3,2;" ..
			core.formspec_escape(listentry.texturename) .. "]"
	end

	if listentry.details ~= nil and
		listentry.texturename == nil then
		--make sure we don't download multiple times
		listentry.texturename = "in progress"

		--prepare url and filename
		local fullurl = core.settings:get("modstore_download_url") ..
					listentry.details.screenshot_url
		local filename = os.tempfolder() .. "_MID_" .. listentry.id

		--trigger download
		core.handle_async(
			--first param is downloadfct
			function(param)
				param.successfull = core.download_file(param.fullurl,param.filename)
				return param
			end,
			--second parameter is data passed to async job
			{
				fullurl = fullurl,
				filename = filename,
				modid = listentry.id
			},
			--integrate result to raw list
			function(result)
				if result.successfull then
					local found = false
					for i=1,#modstore.modlist_unsorted.data,1 do
						if modstore.modlist_unsorted.data[i].id == result.modid then
							found = true
							modstore.modlist_unsorted.data[i].texturename = result.filename
							break
						end
					end
					if found then
						core.event_handler("Refresh")
					else
						core.log("error","got screenshot but didn't find matching mod: " .. result.modid)
					end
				end
			end
		)
	end

	if listentry.texturename ~= nil and
		listentry.texturename ~= "in progress" then
		return "image[0,".. ypos .. ";3,2;" ..
			core.formspec_escape(listentry.texturename) .. "]"
	end

	return ""
end

--------------------------------------------------------------------------------
--@function [parent=#modstore] getshortmodinfo
function modstore.getshortmodinfo(ypos,listentry,details)
	local retval = ""

	retval = retval .. "box[0," .. ypos .. ";11.4,1.75;#FFFFFF]"

	--screenshot
	retval = retval .. modstore.getscreenshot(ypos,listentry)

	--title + author
	retval = retval .."label[2.75," .. ypos .. ";" ..
		core.formspec_escape(details.title) .. " (" .. details.author .. ")]"

	--description
	local descriptiony = ypos + 0.5
	retval = retval .. "textarea[3," .. descriptiony .. ";6.5,1.55;;" ..
		core.formspec_escape(details.description) .. ";]"

	--rating
	local ratingy = ypos
	retval = retval .."label[7," .. ratingy .. ";" ..
					fgettext("Rating") .. ":]"
	retval = retval .. "label[8.7," .. ratingy .. ";" .. details.rating .."]"

	--versions (IMPORTANT has to be defined AFTER rating)
	if details.versions ~= nil and
		#details.versions > 1 then
		local versiony = ypos + 0.05
		retval = retval .. "dropdown[9.1," .. versiony .. ";2.48,0.25;dd_version" .. details.id .. ";"
		local versions = ""
		for i=1,#details.versions , 1 do
			if versions ~= "" then
				versions = versions .. ","
			end

			versions = versions .. details.versions[i].date:sub(1,10)
		end
		retval = retval .. versions .. ";1]"
	end

	if details.basename then
		--install button
		local buttony = ypos + 1.2
		retval = retval .."button[9.1," .. buttony .. ";2.5,0.5;btn_install_mod_" .. details.id .. ";"

		if modmgr.mod_exists(details.basename) then
			retval = retval .. fgettext("re-Install") .."]"
		else
			retval = retval .. fgettext("Install") .."]"
		end
	end

	return retval
end

--------------------------------------------------------------------------------
--@function [parent=#modstore] getmodlist
function modstore.getmodlist(list,yoffset)
	modstore.current_list = list

	if yoffset == nil then
		yoffset = 0
	end

	local sb_y_start = 0.2 + yoffset
	local sb_y_end   = (modstore.modsperpage * 1.75) + ((modstore.modsperpage-1) * 0.15)
	local close_button = "button[4," .. (sb_y_end + 0.3 + yoffset) ..
			";4,0.5;btn_modstore_close;" .. fgettext("Close store") .. "]"

	if #list.data == 0 then
		return close_button
	end

	local scrollbar = ""
	scrollbar = scrollbar .. "label[0.1,".. (sb_y_end + 0.25 + yoffset) ..";"
				.. fgettext("Page $1 of $2", list.page+1, list.pagecount) .. "]"
	scrollbar = scrollbar .. "box[11.6," .. sb_y_start .. ";0.28," .. sb_y_end .. ";#000000]"
	local scrollbarpos = (sb_y_start + 0.5) +
				((sb_y_end -1.6)/(list.pagecount-1)) * list.page
	scrollbar = scrollbar .. "box[11.6," ..scrollbarpos .. ";0.28,0.5;#32CD32]"
	scrollbar = scrollbar .. "button[11.6," .. (sb_y_start)
				.. ";0.5,0.5;btn_modstore_page_up;^]"
	scrollbar = scrollbar .. "button[11.6," .. (sb_y_start + sb_y_end - 0.5)
				.. ";0.5,0.5;btn_modstore_page_down;v]"

	local retval = ""

	local endmod = (list.page * modstore.modsperpage) + modstore.modsperpage

	if (endmod > #list.data) then
		endmod = #list.data
	end

	for i=(list.page * modstore.modsperpage) +1, endmod, 1 do
		--getmoddetails
		local details = list.data[i].details

		if details == nil then
			details = {}
			details.title = list.data[i].title
			details.author = ""
			details.rating = -1
			details.description = ""
		end

		if details ~= nil then
			local screenshot_ypos =
				yoffset +(i-1 - (list.page * modstore.modsperpage))*1.9 +0.2

			retval = retval .. modstore.getshortmodinfo(screenshot_ypos,
														list.data[i],
														details)
		end
	end

	return retval .. scrollbar .. close_button
end

--------------------------------------------------------------------------------
--@function [parent=#modstore] getsearchpage
function modstore.getsearchpage(tabview, name, tabdata)
	local retval = ""
	local search = ""

	if modstore.last_search ~= nil then
		search = modstore.last_search
	end

	retval = retval ..
		"button[9.5,0.2;2.5,0.5;btn_modstore_search;".. fgettext("Search") .. "]" ..
		"field[0.5,0.5;9,0.5;te_modstore_search;;" .. search .. "]"

	retval = retval ..
		modstore.getmodlist(
			modstore.currentlist,
			1.75)

	return retval;
end

--------------------------------------------------------------------------------
--@function [parent=#modstore] unsorted_tab
function modstore.unsorted_tab()
	return modstore.getmodlist(modstore.modlist_unsorted)
end

--------------------------------------------------------------------------------
--@function [parent=#modstore] activate_search_tab
function modstore.activate_search_tab(type, old_tab, new_tab)

	if old_tab == new_tab then
		return
	end
	filterlist.set_filtercriteria(modstore.searchlist,modstore.last_search)
	filterlist.refresh(modstore.searchlist)
	modstore.modsperpage = modstore.mods_on_search_page
	modstore.currentlist = {
		page = 0,
		pagecount =
			math.ceil(filterlist.size(modstore.searchlist) / modstore.modsperpage),
		data = filterlist.get_list(modstore.searchlist),
	}
end