Browse Source

Show (short) list of possible enum values when an invalid value is encountered (resolves #1318)

All enum errors have (hopefully) been changed to a luax_enumerror, which has a
fixed error message. If additionally a list of valid options is passed, it
lists that in the error message. For every enum error with few options I've
implemented this using a getConstants call.

This solution has been designed specifically to reduce the number of template
instantiations (as I've been told that was a concern). All new template code
happens in places where there already was an instantiation of the relevant
StringMap. Of course there is still std::vector<std::string>...

--HG--
branch : minor
Bart van Strien 7 years ago
parent
commit
9e4374935d
72 changed files with 366 additions and 101 deletions
  1. 15 0
      src/common/StringMap.h
  2. 20 0
      src/common/runtime.cpp
  3. 3 0
      src/common/runtime.h
  4. 5 0
      src/modules/audio/Audio.cpp
  5. 1 0
      src/modules/audio/Audio.h
  6. 6 0
      src/modules/audio/Effect.cpp
  7. 1 0
      src/modules/audio/Effect.h
  8. 5 0
      src/modules/audio/Filter.cpp
  9. 1 0
      src/modules/audio/Filter.h
  10. 10 0
      src/modules/audio/Source.cpp
  11. 2 0
      src/modules/audio/Source.h
  12. 4 4
      src/modules/audio/wrap_Audio.cpp
  13. 4 4
      src/modules/audio/wrap_Source.cpp
  14. 5 0
      src/modules/data/Compressor.cpp
  15. 1 0
      src/modules/data/Compressor.h
  16. 5 0
      src/modules/data/DataModule.cpp
  17. 1 0
      src/modules/data/DataModule.h
  18. 4 0
      src/modules/data/HashFunction.cpp
  19. 1 0
      src/modules/data/HashFunction.h
  20. 1 1
      src/modules/data/wrap_CompressedData.cpp
  21. 5 5
      src/modules/data/wrap_DataModule.cpp
  22. 10 0
      src/modules/filesystem/File.cpp
  23. 2 0
      src/modules/filesystem/File.h
  24. 2 2
      src/modules/filesystem/wrap_File.cpp
  25. 1 1
      src/modules/filesystem/wrap_Filesystem.cpp
  26. 5 0
      src/modules/font/TrueTypeRasterizer.cpp
  27. 1 0
      src/modules/font/TrueTypeRasterizer.h
  28. 2 2
      src/modules/font/wrap_Font.cpp
  29. 1 1
      src/modules/font/wrap_GlyphData.cpp
  30. 5 0
      src/modules/graphics/Canvas.cpp
  31. 1 0
      src/modules/graphics/Canvas.h
  32. 5 0
      src/modules/graphics/Font.cpp
  33. 1 0
      src/modules/graphics/Font.h
  34. 35 0
      src/modules/graphics/Graphics.cpp
  35. 7 0
      src/modules/graphics/Graphics.h
  36. 17 2
      src/modules/graphics/Mesh.cpp
  37. 3 0
      src/modules/graphics/Mesh.h
  38. 10 0
      src/modules/graphics/ParticleSystem.cpp
  39. 2 0
      src/modules/graphics/ParticleSystem.h
  40. 15 0
      src/modules/graphics/Texture.cpp
  41. 3 0
      src/modules/graphics/Texture.h
  42. 10 0
      src/modules/graphics/depthstencil.cpp
  43. 5 0
      src/modules/graphics/depthstencil.h
  44. 10 0
      src/modules/graphics/vertex.cpp
  45. 4 0
      src/modules/graphics/vertex.h
  46. 1 1
      src/modules/graphics/wrap_Canvas.cpp
  47. 2 2
      src/modules/graphics/wrap_Font.cpp
  48. 25 25
      src/modules/graphics/wrap_Graphics.cpp
  49. 4 4
      src/modules/graphics/wrap_Mesh.cpp
  50. 2 2
      src/modules/graphics/wrap_ParticleSystem.cpp
  51. 2 2
      src/modules/graphics/wrap_Shader.cpp
  52. 2 2
      src/modules/graphics/wrap_Text.cpp
  53. 8 8
      src/modules/graphics/wrap_Texture.cpp
  54. 2 2
      src/modules/graphics/wrap_Video.cpp
  55. 5 0
      src/modules/image/ImageData.cpp
  56. 1 0
      src/modules/image/ImageData.h
  57. 1 1
      src/modules/image/wrap_Image.cpp
  58. 1 1
      src/modules/image/wrap_ImageData.cpp
  59. 4 4
      src/modules/joystick/wrap_Joystick.cpp
  60. 4 4
      src/modules/joystick/wrap_JoystickModule.cpp
  61. 6 6
      src/modules/keyboard/wrap_Keyboard.cpp
  62. 5 0
      src/modules/math/Transform.cpp
  63. 1 0
      src/modules/math/Transform.h
  64. 2 2
      src/modules/math/wrap_Math.cpp
  65. 1 1
      src/modules/math/wrap_Transform.cpp
  66. 1 1
      src/modules/mouse/wrap_Mouse.cpp
  67. 6 1
      src/modules/physics/Body.cpp
  68. 1 0
      src/modules/physics/Body.h
  69. 1 1
      src/modules/physics/box2d/wrap_Physics.cpp
  70. 14 4
      src/modules/window/Window.cpp
  71. 2 0
      src/modules/window/Window.h
  72. 5 5
      src/modules/window/wrap_Window.cpp

+ 15 - 0
src/common/StringMap.h

@@ -23,6 +23,9 @@
 
 #include "Exception.h"
 
+#include <string>
+#include <vector>
+
 namespace love
 {
 
@@ -145,6 +148,18 @@ public:
 		return hash;
 	}
 
+	std::vector<std::string> getNames() const
+	{
+		std::vector<std::string> names;
+		names.reserve(SIZE);
+
+		for (unsigned int i = 0; i < SIZE; ++i)
+			if (reverse[i] != nullptr)
+				names.emplace_back(reverse[i]);
+
+		return names;
+	}
+
 private:
 
 	struct Record

+ 20 - 0
src/common/runtime.cpp

@@ -31,6 +31,7 @@
 #include <algorithm>
 #include <iostream>
 #include <cstdio>
+#include <sstream>
 
 namespace love
 {
@@ -832,6 +833,25 @@ extern "C" int luax_typerror(lua_State *L, int narg, const char *tname)
 	return luaL_argerror(L, narg, msg);
 }
 
+int luax_enumerror(lua_State *L, const char *enumName, const char *value)
+{
+	return luaL_error(L, "Invalid %s: %s", enumName, value);
+}
+
+int luax_enumerror(lua_State *L, const char *enumName, const std::vector<std::string> &values, const char *value)
+{
+	std::stringstream valueStream;
+	bool first = true;
+	for (auto value : values)
+	{
+		valueStream << (first ? "'" : ", '") << value << "'";
+		first = false;
+	}
+
+	std::string valueString = valueStream.str();
+	return luaL_error(L, "Invalid %s '%s', expected one of: %s", enumName, value, valueString.c_str());
+}
+
 size_t luax_objlen(lua_State *L, int ndx)
 {
 #if LUA_VERSION_NUM == 501

+ 3 - 0
src/common/runtime.h

@@ -459,6 +459,9 @@ extern "C" { // Also called from luasocket
 	int luax_typerror(lua_State *L, int narg, const char *tname);
 }
 
+int luax_enumerror(lua_State *L, const char *enumName, const char *value);
+int luax_enumerror(lua_State *L, const char *enumName, const std::vector<std::string> &values, const char *value);
+
 /**
  * Calls luax_objlen/lua_rawlen depending on version
  **/

+ 5 - 0
src/modules/audio/Audio.cpp

@@ -64,5 +64,10 @@ bool Audio::getConstant(DistanceModel in, const char  *&out)
 	return distanceModels.find(in, out);
 }
 
+std::vector<std::string> Audio::getConstants(DistanceModel)
+{
+	return distanceModels.getNames();
+}
+
 } // audio
 } // love

+ 1 - 0
src/modules/audio/Audio.h

@@ -69,6 +69,7 @@ public:
 
 	static bool getConstant(const char *in, DistanceModel &out);
 	static bool getConstant(DistanceModel in, const char  *&out);
+	static std::vector<std::string> getConstants(DistanceModel);
 
 	virtual ~Audio() {}
 

+ 6 - 0
src/modules/audio/Effect.cpp

@@ -47,6 +47,12 @@ bool Effect::getConstant(Type in, const char *&out)
 {
 	return types.find(in, out);
 }
+
+std::vector<std::string> Effect::getConstants(Type)
+{
+	return types.getNames();
+}
+
 /*
 bool Effect::getConstant(const char *in, Phoneme &out)
 {

+ 1 - 0
src/modules/audio/Effect.h

@@ -248,6 +248,7 @@ public:
 
 	static bool getConstant(const char *in, Type &out);
 	static bool getConstant(Type in, const char *&out);
+	static std::vector<std::string> getConstants(Type);
 	static bool getConstant(const char *in, Waveform &out);
 	static bool getConstant(Waveform in, const char *&out);
 	//static bool getConstant(const char *in, Direction &out);

+ 5 - 0
src/modules/audio/Filter.cpp

@@ -48,6 +48,11 @@ bool Filter::getConstant(Type in, const char *&out)
 	return types.find(in, out);
 }
 
+std::vector<std::string> Filter::getConstants(Type)
+{
+	return types.getNames();
+}
+
 bool Filter::getConstant(const char *in, Parameter &out, Type t)
 {
 	return parameterNames[t].find(in, out);

+ 1 - 0
src/modules/audio/Filter.h

@@ -109,6 +109,7 @@ public:
 
 	static bool getConstant(const char *in, Type &out);
 	static bool getConstant(Type in, const char *&out);
+	static std::vector<std::string> getConstants(Type);
 	static bool getConstant(const char *in, Parameter &out, Type t);
 	static bool getConstant(Parameter in, const char *&out, Type t);
 	static ParameterType getParameterType(Parameter in);

+ 10 - 0
src/modules/audio/Source.cpp

@@ -51,6 +51,11 @@ bool Source::getConstant(Type in, const char  *&out)
 	return types.find(in, out);
 }
 
+std::vector<std::string> Source::getConstants(Type)
+{
+	return types.getNames();
+}
+
 bool Source::getConstant(const char *in, Unit &out)
 {
 	return units.find(in, out);
@@ -61,6 +66,11 @@ bool Source::getConstant(Unit in, const char  *&out)
 	return units.find(in, out);
 }
 
+std::vector<std::string> Source::getConstants(Unit)
+{
+	return units.getNames();
+}
+
 StringMap<Source::Type, Source::TYPE_MAX_ENUM>::Entry Source::typeEntries[] =
 {
 	{"static", Source::TYPE_STATIC},

+ 2 - 0
src/modules/audio/Source.h

@@ -126,8 +126,10 @@ public:
 
 	static bool getConstant(const char *in, Type &out);
 	static bool getConstant(Type in, const char  *&out);
+	static std::vector<std::string> getConstants(Type);
 	static bool getConstant(const char *in, Unit &out);
 	static bool getConstant(Unit in, const char  *&out);
+	static std::vector<std::string> getConstants(Unit);
 
 protected:
 

+ 4 - 4
src/modules/audio/wrap_Audio.cpp

@@ -51,7 +51,7 @@ int w_newSource(lua_State *L)
 	{
 		const char *stypestr = luaL_checkstring(L, 2);
 		if (stypestr && !Source::getConstant(stypestr, stype))
-			return luaL_error(L, "Invalid source type: %s", stypestr);
+			return luax_enumerror(L, "source type", Source::getConstants(stype), stypestr);
 	}
 
 	if (lua_isstring(L, 1) || luax_istype(L, 1, love::filesystem::File::type) || luax_istype(L, 1, love::filesystem::FileData::type))
@@ -297,7 +297,7 @@ int w_setDistanceModel(lua_State *L)
 	const char *modelStr = luaL_checkstring(L, 1);
 	Audio::DistanceModel distanceModel;
 	if (!Audio::getConstant(modelStr, distanceModel))
-		return luaL_error(L, "Invalid distance model: %s", modelStr);
+		return luax_enumerror(L, "distance model", Audio::getConstants(distanceModel), modelStr);
 	instance()->setDistanceModel(distanceModel);
 	return 0;
 }
@@ -351,7 +351,7 @@ int w_setEffect(lua_State *L)
 	Effect::Type type = Effect::TYPE_MAX_ENUM;
 	const char *typestr = luaL_checkstring(L, -1);
 	if (!Effect::getConstant(typestr, type))
-		return luaL_error(L, "Invalid Effect type: %s", typestr);
+		return luax_enumerror(L, "effect type", Effect::getConstants(type), typestr);
 
 	lua_pop(L, 1);
 	std::map<Effect::Parameter, float> params;
@@ -386,7 +386,7 @@ int w_setEffect(lua_State *L)
 				paramstr = lua_tostring(L, -1);
 				Effect::Waveform waveform;
 				if (!Effect::getConstant(paramstr, waveform))
-					return luaL_error(L, "Invalid waveform type: %s", paramstr);
+					return luax_enumerror(L, "waveform type", paramstr);
 				params[param] = static_cast<int>(waveform);
 				break;
 			}

+ 4 - 4
src/modules/audio/wrap_Source.cpp

@@ -111,7 +111,7 @@ int w_Source_seek(lua_State *L)
 	Source::Unit u = Source::UNIT_SECONDS;
 	const char *unit = lua_isnoneornil(L, 3) ? 0 : lua_tostring(L, 3);
 	if (unit && !t->getConstant(unit, u))
-		return luaL_error(L, "Invalid Source time unit: %s", unit);
+		return luax_enumerror(L, "time unit", Source::getConstants(u), unit);
 
 	t->seek(offset, u);
 	return 0;
@@ -124,7 +124,7 @@ int w_Source_tell(lua_State *L)
 	Source::Unit u = Source::UNIT_SECONDS;
 	const char *unit = lua_isnoneornil(L, 2) ? 0 : lua_tostring(L, 2);
 	if (unit && !t->getConstant(unit, u))
-		return luaL_error(L, "Invalid Source time unit: %s", unit);
+		return luax_enumerror(L, "time unit", Source::getConstants(u), unit);
 
 	lua_pushnumber(L, t->tell(u));
 	return 1;
@@ -137,7 +137,7 @@ int w_Source_getDuration(lua_State *L)
 	Source::Unit u = Source::UNIT_SECONDS;
 	const char *unit = lua_isnoneornil(L, 2) ? 0 : lua_tostring(L, 2);
 	if (unit && !t->getConstant(unit, u))
-		return luaL_error(L, "Invalid Source time unit: %s", unit);
+		return luax_enumerror(L, "time unit", Source::getConstants(u), unit);
 
 	lua_pushnumber(L, t->getDuration(u));
 	return 1;
@@ -363,7 +363,7 @@ int setFilterReadFilter(lua_State *L, int idx, std::map<Filter::Parameter, float
 	Filter::Type type = Filter::TYPE_MAX_ENUM;
 	const char *typestr = luaL_checkstring(L, -1);
 	if (!Filter::getConstant(typestr, type))
-		return luaL_error(L, "Invalid Filter type: %s", typestr);
+		return luax_enumerror(L, "filter type", Filter::getConstants(type), typestr);
 
 	lua_pop(L, 1);
 	params[Filter::FILTER_TYPE] = static_cast<int>(type);

+ 5 - 0
src/modules/data/Compressor.cpp

@@ -379,6 +379,11 @@ bool Compressor::getConstant(Format in, const char *&out)
 	return formatNames.find(in, out);
 }
 
+std::vector<std::string> Compressor::getConstants(Format)
+{
+	return formatNames.getNames();
+}
+
 StringMap<Compressor::Format, Compressor::FORMAT_MAX_ENUM>::Entry Compressor::formatEntries[] =
 {
 	{ "lz4",  FORMAT_LZ4  },

+ 1 - 0
src/modules/data/Compressor.h

@@ -87,6 +87,7 @@ public:
 
 	static bool getConstant(const char *in, Format &out);
 	static bool getConstant(Format in, const char *&out);
+	static std::vector<std::string> getConstants(Format);
 
 protected:
 

+ 5 - 0
src/modules/data/DataModule.cpp

@@ -263,5 +263,10 @@ bool getConstant(EncodeFormat in, const char *&out)
 	return encoders.find(in, out);
 }
 
+std::vector<std::string> getConstants(EncodeFormat)
+{
+	return encoders.getNames();
+}
+
 } // data
 } // love

+ 1 - 0
src/modules/data/DataModule.h

@@ -96,6 +96,7 @@ void hash(HashFunction::Function function, const char *input, uint64_t size, Has
 
 bool getConstant(const char *in, EncodeFormat &out);
 bool getConstant(EncodeFormat in, const char *&out);
+std::vector<std::string> getConstants(EncodeFormat);
 
 
 class DataModule : public Module

+ 4 - 0
src/modules/data/HashFunction.cpp

@@ -619,6 +619,10 @@ bool HashFunction::getConstant(const Function &in, const char *&out)
 {
 	return functionNames.find(in, out);
 }
+std::vector<std::string> HashFunction::getConstants(Function)
+{
+	return functionNames.getNames();
+}
 
 StringMap<HashFunction::Function, HashFunction::FUNCTION_MAX_ENUM>::Entry HashFunction::functionEntries[] =
 {

+ 1 - 0
src/modules/data/HashFunction.h

@@ -80,6 +80,7 @@ public:
 
 	static bool getConstant(const char *in, Function &out);
 	static bool getConstant(const Function &in, const char *&out);
+	static std::vector<std::string> getConstants(Function);
 
 protected:
 

+ 1 - 1
src/modules/data/wrap_CompressedData.cpp

@@ -47,7 +47,7 @@ int w_CompressedData_getFormat(lua_State *L)
 
 	const char *fname = nullptr;
 	if (!Compressor::getConstant(t->getFormat(), fname))
-		return luaL_error(L, "Unknown compressed data format.");
+		return luax_enumerror(L, "compressed data format", Compressor::getConstants(Compressor::FORMAT_MAX_ENUM), fname);
 
 	lua_pushstring(L, fname);
 	return 1;

+ 5 - 5
src/modules/data/wrap_DataModule.cpp

@@ -107,7 +107,7 @@ int w_compress(lua_State *L)
 	Compressor::Format format = Compressor::FORMAT_LZ4;
 
 	if (!Compressor::getConstant(fstr, format))
-		return luaL_error(L, "Invalid compressed data format: %s", fstr);
+		return luax_enumerror(L, "compressed data format", Compressor::getConstants(format), fstr);
 
 	int level = (int) luaL_optinteger(L, 3, -1);
 
@@ -146,7 +146,7 @@ int w_decompress(lua_State *L)
 		const char *fstr = luaL_checkstring(L, 1);
 
 		if (!Compressor::getConstant(fstr, format))
-			return luaL_error(L, "Invalid compressed data format: %s", fstr);
+			return luax_enumerror(L, "compressed data format", Compressor::getConstants(format), fstr);
 
 		size_t compressedsize = 0;
 		const char *cbytes = nullptr;
@@ -174,7 +174,7 @@ int w_encode(lua_State *L)
 	const char *formatstr = luaL_checkstring(L, 1);
 	EncodeFormat format;
 	if (!getConstant(formatstr, format))
-		return luaL_error(L, "Invalid encode format: %s", formatstr);
+		return luax_enumerror(L, "encode format", getConstants(format), formatstr);
 
 	size_t srclen = 0;
 	const char *src = nullptr;
@@ -208,7 +208,7 @@ int w_decode(lua_State *L)
 	const char *formatstr = luaL_checkstring(L, 1);
 	EncodeFormat format;
 	if (!getConstant(formatstr, format))
-		return luaL_error(L, "Invalid decode format: %s", formatstr);
+		return luax_enumerror(L, "decode format", getConstants(format), formatstr);
 
 	size_t srclen = 0;
 	const char *src = nullptr;
@@ -240,7 +240,7 @@ int w_hash(lua_State *L)
 	const char *fstr = luaL_checkstring(L, 1);
 	HashFunction::Function function;
 	if (!HashFunction::getConstant(fstr, function))
-		return luaL_error(L, "Invalid hash function: %s", fstr);
+		return luax_enumerror(L, "hash function", HashFunction::getConstants(function), fstr);
 
 	HashFunction::Value hashvalue;
 	if (lua_isstring(L, 2))

+ 10 - 0
src/modules/filesystem/File.cpp

@@ -103,6 +103,11 @@ bool File::getConstant(Mode in, const char *&out)
 	return modes.find(in, out);
 }
 
+std::vector<std::string> File::getConstants(Mode)
+{
+	return modes.getNames();
+}
+
 bool File::getConstant(const char *in, BufferMode &out)
 {
 	return bufferModes.find(in, out);
@@ -113,6 +118,11 @@ bool File::getConstant(BufferMode in, const char *&out)
 	return bufferModes.find(in, out);
 }
 
+std::vector<std::string> File::getConstants(BufferMode)
+{
+	return bufferModes.getNames();
+}
+
 StringMap<File::Mode, File::MODE_MAX_ENUM>::Entry File::modeEntries[] =
 {
 	{ "c", MODE_CLOSED },

+ 2 - 0
src/modules/filesystem/File.h

@@ -204,9 +204,11 @@ public:
 
 	static bool getConstant(const char *in, Mode &out);
 	static bool getConstant(Mode in, const char *&out);
+	static std::vector<std::string> getConstants(Mode);
 
 	static bool getConstant(const char *in, BufferMode &out);
 	static bool getConstant(BufferMode in, const char *&out);
+	static std::vector<std::string> getConstants(BufferMode);
 
 private:
 

+ 2 - 2
src/modules/filesystem/wrap_File.cpp

@@ -77,7 +77,7 @@ int w_File_open(lua_State *L)
 	File::Mode mode;
 
 	if (!File::getConstant(str, mode))
-		return luaL_error(L, "Incorrect file open mode: %s", str);
+		return luax_enumerror(L, "file open mode", File::getConstants(mode), str);
 
 	try
 	{
@@ -353,7 +353,7 @@ int w_File_setBuffer(lua_State *L)
 
 	File::BufferMode bufmode;
 	if (!File::getConstant(str, bufmode))
-		return luaL_error(L, "Incorrect file buffer mode: %s", str);
+		return luax_enumerror(L, "file buffer mode", File::getConstants(bufmode), str);
 
 	bool success = false;
 	try

+ 1 - 1
src/modules/filesystem/wrap_Filesystem.cpp

@@ -150,7 +150,7 @@ int w_newFile(lua_State *L)
 	{
 		str = luaL_checkstring(L, 2);
 		if (!File::getConstant(str, mode))
-			return luaL_error(L, "Incorrect file open mode: %s", str);
+			return luax_enumerror(L, "file open mode", File::getConstants(mode), str);
 	}
 
 	File *t = instance()->newFile(filename);

+ 5 - 0
src/modules/font/TrueTypeRasterizer.cpp

@@ -35,6 +35,11 @@ bool TrueTypeRasterizer::getConstant(Hinting in, const char *&out)
 	return hintings.find(in, out);
 }
 
+std::vector<std::string> TrueTypeRasterizer::getConstants(Hinting)
+{
+	return hintings.getNames();
+}
+
 StringMap<TrueTypeRasterizer::Hinting, TrueTypeRasterizer::HINTING_MAX_ENUM>::Entry TrueTypeRasterizer::hintingEntries[] =
 {
 	{"normal", HINTING_NORMAL},

+ 1 - 0
src/modules/font/TrueTypeRasterizer.h

@@ -48,6 +48,7 @@ public:
 
 	static bool getConstant(const char *in, Hinting &out);
 	static bool getConstant(Hinting in, const char *&out);
+	static std::vector<std::string> getConstants(Hinting);
 
 private:
 

+ 2 - 2
src/modules/font/wrap_Font.cpp

@@ -76,7 +76,7 @@ int w_newTrueTypeRasterizer(lua_State *L)
 
 		const char *hintstr = lua_isnoneornil(L, 2) ? nullptr : luaL_checkstring(L, 2);
 		if (hintstr && !TrueTypeRasterizer::getConstant(hintstr, hinting))
-			return luaL_error(L, "Invalid TrueType font hinting mode: %s", hintstr);
+			return luax_enumerror(L, "TrueType font hinting mode", TrueTypeRasterizer::getConstants(hinting), hintstr);
 
 		if (lua_isnoneornil(L, 3))
 			luax_catchexcept(L, [&](){ t = instance()->newTrueTypeRasterizer(size, hinting); });
@@ -102,7 +102,7 @@ int w_newTrueTypeRasterizer(lua_State *L)
 
 		const char *hintstr = lua_isnoneornil(L, 3) ? nullptr : luaL_checkstring(L, 3);
 		if (hintstr && !TrueTypeRasterizer::getConstant(hintstr, hinting))
-			return luaL_error(L, "Invalid TrueType font hinting mode: %s", hintstr);
+			return luax_enumerror(L, "TrueType font hinting mode", TrueTypeRasterizer::getConstants(hinting), hintstr);
 
 		if (lua_isnoneornil(L, 4))
 		{

+ 1 - 1
src/modules/font/wrap_GlyphData.cpp

@@ -118,7 +118,7 @@ int w_GlyphData_getFormat(lua_State *L)
 
 	const char *str;
 	if (!getConstant(t->getFormat(), str))
-		return luaL_error(L, "unknown GlyphData format.");
+		return luax_enumerror(L, "pixel format", str);
 
 	lua_pushstring(L, str);
 	return 1;

+ 5 - 0
src/modules/graphics/Canvas.cpp

@@ -190,6 +190,11 @@ bool Canvas::getConstant(MipmapMode in, const char *&out)
 	return mipmapModes.find(in, out);
 }
 
+std::vector<std::string> Canvas::getConstants(MipmapMode)
+{
+	return mipmapModes.getNames();
+}
+
 StringMap<Canvas::MipmapMode, Canvas::MIPMAPS_MAX_ENUM>::Entry Canvas::mipmapEntries[] =
 {
 	{ "none",   MIPMAPS_NONE   },

+ 1 - 0
src/modules/graphics/Canvas.h

@@ -79,6 +79,7 @@ public:
 
 	static bool getConstant(const char *in, MipmapMode &out);
 	static bool getConstant(MipmapMode in, const char *&out);
+	static std::vector<std::string> getConstants(MipmapMode);
 
 protected:
 

+ 5 - 0
src/modules/graphics/Font.cpp

@@ -1012,6 +1012,11 @@ bool Font::getConstant(AlignMode in, const char  *&out)
 	return alignModes.find(in, out);
 }
 
+std::vector<std::string> Font::getConstants(AlignMode)
+{
+	return alignModes.getNames();
+}
+
 StringMap<Font::AlignMode, Font::ALIGN_MAX_ENUM>::Entry Font::alignModeEntries[] =
 {
 	{ "left", ALIGN_LEFT },

+ 1 - 0
src/modules/graphics/Font.h

@@ -181,6 +181,7 @@ public:
 
 	static bool getConstant(const char *in, AlignMode &out);
 	static bool getConstant(AlignMode in, const char *&out);
+	static std::vector<std::string> getConstants(AlignMode);
 
 	static int fontCount;
 

+ 35 - 0
src/modules/graphics/Graphics.cpp

@@ -1444,6 +1444,11 @@ bool Graphics::getConstant(DrawMode in, const char *&out)
 	return drawModes.find(in, out);
 }
 
+std::vector<std::string> Graphics::getConstants(DrawMode)
+{
+	return drawModes.getNames();
+}
+
 bool Graphics::getConstant(const char *in, ArcMode &out)
 {
 	return arcModes.find(in, out);
@@ -1454,6 +1459,11 @@ bool Graphics::getConstant(ArcMode in, const char *&out)
 	return arcModes.find(in, out);
 }
 
+std::vector<std::string> Graphics::getConstants(ArcMode)
+{
+	return arcModes.getNames();
+}
+
 bool Graphics::getConstant(const char *in, BlendMode &out)
 {
 	return blendModes.find(in, out);
@@ -1464,6 +1474,11 @@ bool Graphics::getConstant(BlendMode in, const char *&out)
 	return blendModes.find(in, out);
 }
 
+std::vector<std::string> Graphics::getConstants(BlendMode)
+{
+	return blendModes.getNames();
+}
+
 bool Graphics::getConstant(const char *in, BlendAlpha &out)
 {
 	return blendAlphaModes.find(in, out);
@@ -1474,6 +1489,11 @@ bool Graphics::getConstant(BlendAlpha in, const char *&out)
 	return blendAlphaModes.find(in, out);
 }
 
+std::vector<std::string> Graphics::getConstants(BlendAlpha)
+{
+	return blendAlphaModes.getNames();
+}
+
 bool Graphics::getConstant(const char *in, LineStyle &out)
 {
 	return lineStyles.find(in, out);
@@ -1484,6 +1504,11 @@ bool Graphics::getConstant(LineStyle in, const char *&out)
 	return lineStyles.find(in, out);
 }
 
+std::vector<std::string> Graphics::getConstants(LineStyle)
+{
+	return lineStyles.getNames();
+}
+
 bool Graphics::getConstant(const char *in, LineJoin &out)
 {
 	return lineJoins.find(in, out);
@@ -1494,6 +1519,11 @@ bool Graphics::getConstant(LineJoin in, const char *&out)
 	return lineJoins.find(in, out);
 }
 
+std::vector<std::string> Graphics::getConstants(LineJoin)
+{
+	return lineJoins.getNames();
+}
+
 bool Graphics::getConstant(const char *in, Feature &out)
 {
 	return features.find(in, out);
@@ -1524,6 +1554,11 @@ bool Graphics::getConstant(StackType in, const char *&out)
 	return stackTypes.find(in, out);
 }
 
+std::vector<std::string> Graphics::getConstants(StackType)
+{
+	return stackTypes.getNames();
+}
+
 StringMap<Graphics::DrawMode, Graphics::DRAW_MAX_ENUM>::Entry Graphics::drawModeEntries[] =
 {
 	{ "line", DRAW_LINE },

+ 7 - 0
src/modules/graphics/Graphics.h

@@ -773,21 +773,27 @@ public:
 
 	static bool getConstant(const char *in, DrawMode &out);
 	static bool getConstant(DrawMode in, const char *&out);
+	static std::vector<std::string> getConstants(DrawMode);
 
 	static bool getConstant(const char *in, ArcMode &out);
 	static bool getConstant(ArcMode in, const char *&out);
+	static std::vector<std::string> getConstants(ArcMode);
 
 	static bool getConstant(const char *in, BlendMode &out);
 	static bool getConstant(BlendMode in, const char *&out);
+	static std::vector<std::string> getConstants(BlendMode);
 
 	static bool getConstant(const char *in, BlendAlpha &out);
 	static bool getConstant(BlendAlpha in, const char *&out);
+	static std::vector<std::string> getConstants(BlendAlpha);
 
 	static bool getConstant(const char *in, LineStyle &out);
 	static bool getConstant(LineStyle in, const char *&out);
+	static std::vector<std::string> getConstants(LineStyle);
 
 	static bool getConstant(const char *in, LineJoin &out);
 	static bool getConstant(LineJoin in, const char *&out);
+	static std::vector<std::string> getConstants(LineJoin);
 
 	static bool getConstant(const char *in, Feature &out);
 	static bool getConstant(Feature in, const char *&out);
@@ -797,6 +803,7 @@ public:
 
 	static bool getConstant(const char *in, StackType &out);
 	static bool getConstant(StackType in, const char *&out);
+	static std::vector<std::string> getConstants(StackType);
 
 	// Default shader code (a shader is always required internally.)
 	static Shader::ShaderSource defaultShaderCode[Shader::STANDARD_MAX_ENUM][Shader::LANGUAGE_MAX_ENUM][2];

+ 17 - 2
src/modules/graphics/Mesh.cpp

@@ -659,16 +659,21 @@ size_t Mesh::getAttribFormatSize(const AttribFormat &format)
 	}
 }
 
-bool Mesh::getConstant(const char *in, Mesh::DrawMode &out)
+bool Mesh::getConstant(const char *in, DrawMode &out)
 {
 	return drawModes.find(in, out);
 }
 
-bool Mesh::getConstant(Mesh::DrawMode in, const char *&out)
+bool Mesh::getConstant(DrawMode in, const char *&out)
 {
 	return drawModes.find(in, out);
 }
 
+std::vector<std::string> Mesh::getConstants(DrawMode)
+{
+	return drawModes.getNames();
+}
+
 bool Mesh::getConstant(const char *in, DataType &out)
 {
 	return dataTypes.find(in, out);
@@ -679,6 +684,11 @@ bool Mesh::getConstant(DataType in, const char *&out)
 	return dataTypes.find(in, out);
 }
 
+std::vector<std::string> Mesh::getConstants(DataType)
+{
+	return dataTypes.getNames();
+}
+
 bool Mesh::getConstant(const char *in, AttributeStep &out)
 {
 	return attributeSteps.find(in, out);
@@ -689,6 +699,11 @@ bool Mesh::getConstant(AttributeStep in, const char *&out)
 	return attributeSteps.find(in, out);
 }
 
+std::vector<std::string> Mesh::getConstants(AttributeStep)
+{
+	return attributeSteps.getNames();
+}
+
 StringMap<Mesh::DrawMode, Mesh::DRAWMODE_MAX_ENUM>::Entry Mesh::drawModeEntries[] =
 {
 	{ "fan",       DRAWMODE_FAN       },

+ 3 - 0
src/modules/graphics/Mesh.h

@@ -202,12 +202,15 @@ public:
 
 	static bool getConstant(const char *in, DrawMode &out);
 	static bool getConstant(DrawMode in, const char *&out);
+	static std::vector<std::string> getConstants(DrawMode);
 
 	static bool getConstant(const char *in, DataType &out);
 	static bool getConstant(DataType in, const char *&out);
+	static std::vector<std::string> getConstants(DataType);
 
 	static bool getConstant(const char *in, AttributeStep &out);
 	static bool getConstant(AttributeStep in, const char *&out);
+	static std::vector<std::string> getConstants(AttributeStep);
 
 protected:
 

+ 10 - 0
src/modules/graphics/ParticleSystem.cpp

@@ -1103,6 +1103,11 @@ bool ParticleSystem::getConstant(AreaSpreadDistribution in, const char *&out)
 	return distributions.find(in, out);
 }
 
+std::vector<std::string> ParticleSystem::getConstants(AreaSpreadDistribution)
+{
+	return distributions.getNames();
+}
+
 bool ParticleSystem::getConstant(const char *in, InsertMode &out)
 {
 	return insertModes.find(in, out);
@@ -1113,6 +1118,11 @@ bool ParticleSystem::getConstant(InsertMode in, const char *&out)
 	return insertModes.find(in, out);
 }
 
+std::vector<std::string> ParticleSystem::getConstants(InsertMode)
+{
+	return insertModes.getNames();
+}
+
 StringMap<ParticleSystem::AreaSpreadDistribution, ParticleSystem::DISTRIBUTION_MAX_ENUM>::Entry ParticleSystem::distributionsEntries[] =
 {
 	{ "none",    DISTRIBUTION_NONE },

+ 2 - 0
src/modules/graphics/ParticleSystem.h

@@ -541,9 +541,11 @@ public:
 
 	static bool getConstant(const char *in, AreaSpreadDistribution &out);
 	static bool getConstant(AreaSpreadDistribution in, const char *&out);
+	static std::vector<std::string> getConstants(AreaSpreadDistribution);
 
 	static bool getConstant(const char *in, InsertMode &out);
 	static bool getConstant(InsertMode in, const char *&out);
+	static std::vector<std::string> getConstants(InsertMode);
 
 protected:
 

+ 15 - 0
src/modules/graphics/Texture.cpp

@@ -384,6 +384,11 @@ bool Texture::getConstant(TextureType in, const char *&out)
 	return texTypes.find(in, out);
 }
 
+std::vector<std::string> Texture::getConstants(TextureType)
+{
+	return texTypes.getNames();
+}
+
 bool Texture::getConstant(const char *in, FilterMode &out)
 {
 	return filterModes.find(in, out);
@@ -394,6 +399,11 @@ bool Texture::getConstant(FilterMode in, const char *&out)
 	return filterModes.find(in, out);
 }
 
+std::vector<std::string> Texture::getConstants(FilterMode)
+{
+	return filterModes.getNames();
+}
+
 bool Texture::getConstant(const char *in, WrapMode &out)
 {
 	return wrapModes.find(in, out);
@@ -404,6 +414,11 @@ bool Texture::getConstant(WrapMode in, const char *&out)
 	return wrapModes.find(in, out);
 }
 
+std::vector<std::string> Texture::getConstants(WrapMode)
+{
+	return wrapModes.getNames();
+}
+
 StringMap<TextureType, TEXTURE_MAX_ENUM>::Entry Texture::texTypeEntries[] =
 {
 	{ "2d", TEXTURE_2D },

+ 3 - 0
src/modules/graphics/Texture.h

@@ -162,12 +162,15 @@ public:
 
 	static bool getConstant(const char *in, TextureType &out);
 	static bool getConstant(TextureType in, const char *&out);
+	static std::vector<std::string> getConstants(TextureType);
 
 	static bool getConstant(const char *in, FilterMode &out);
 	static bool getConstant(FilterMode in, const char *&out);
+	static std::vector<std::string> getConstants(FilterMode);
 
 	static bool getConstant(const char *in, WrapMode &out);
 	static bool getConstant(WrapMode in, const char *&out);
+	static std::vector<std::string> getConstants(WrapMode);
 
 protected:
 

+ 10 - 0
src/modules/graphics/depthstencil.cpp

@@ -85,6 +85,11 @@ bool getConstant(StencilAction in, const char *&out)
 	return stencilActions.find(in, out);
 }
 
+std::vector<std::string> getConstants(StencilAction)
+{
+	return stencilActions.getNames();
+}
+
 bool getConstant(const char *in, CompareMode &out)
 {
 	return compareModes.find(in, out);
@@ -95,5 +100,10 @@ bool getConstant(CompareMode in, const char *&out)
 	return compareModes.find(in, out);
 }
 
+std::vector<std::string> getConstants(CompareMode)
+{
+	return compareModes.getNames();
+}
+
 } // graphics
 } // love

+ 5 - 0
src/modules/graphics/depthstencil.h

@@ -20,6 +20,9 @@
 
 #pragma once
 
+#include <vector>
+#include <string>
+
 namespace love
 {
 namespace graphics
@@ -60,9 +63,11 @@ CompareMode getReversedCompareMode(CompareMode mode);
 
 bool getConstant(const char *in, StencilAction &out);
 bool getConstant(StencilAction in, const char *&out);
+std::vector<std::string> getConstants(StencilAction);
 
 bool getConstant(const char *in, CompareMode &out);
 bool getConstant(CompareMode in, const char *&out);
+std::vector<std::string> getConstants(CompareMode);
 
 } // graphics
 } // love

+ 10 - 0
src/modules/graphics/vertex.cpp

@@ -254,6 +254,11 @@ bool getConstant(IndexDataType in, const char *&out)
 	return indexTypes.find(in, out);
 }
 
+std::vector<std::string> getConstants(IndexDataType)
+{
+	return indexTypes.getNames();
+}
+
 bool getConstant(const char *in, Usage &out)
 {
 	return usages.find(in, out);
@@ -264,6 +269,11 @@ bool getConstant(Usage in, const char *&out)
 	return usages.find(in, out);
 }
 
+std::vector<std::string> getConstants(Usage)
+{
+	return usages.getNames();
+}
+
 } // vertex
 } // graphics
 } // love

+ 4 - 0
src/modules/graphics/vertex.h

@@ -26,6 +26,8 @@
 
 // C
 #include <stddef.h>
+#include <vector>
+#include <string>
 
 namespace love
 {
@@ -176,9 +178,11 @@ bool getConstant(VertexAttribID in, const char *&out);
 
 bool getConstant(const char *in, IndexDataType &out);
 bool getConstant(IndexDataType in, const char *&out);
+std::vector<std::string> getConstants(IndexDataType);
 
 bool getConstant(const char *in, Usage &out);
 bool getConstant(Usage in, const char *&out);
+std::vector<std::string> getConstants(Usage);
 
 } // vertex
 

+ 1 - 1
src/modules/graphics/wrap_Canvas.cpp

@@ -130,7 +130,7 @@ int w_Canvas_getMipmapMode(lua_State *L)
 	Canvas *c = luax_checkcanvas(L, 1);
 	const char *str;
 	if (!Canvas::getConstant(c->getMipmapMode(), str))
-		return luaL_error(L, "Unknown mipmap mode.");
+		return luax_enumerror(L, "mipmap mode", Canvas::getConstants(Canvas::MIPMAPS_MAX_ENUM), str);
 
 	lua_pushstring(L, str);
 	return 1;

+ 2 - 2
src/modules/graphics/wrap_Font.cpp

@@ -145,9 +145,9 @@ int w_Font_setFilter(lua_State *L)
 	const char *magstr = luaL_optstring(L, 3, minstr);
 
 	if (!Texture::getConstant(minstr, f.min))
-		return luaL_error(L, "Invalid filter mode: %s", minstr);
+		return luax_enumerror(L, "filter mode", Texture::getConstants(f.min), minstr);
 	if (!Texture::getConstant(magstr, f.mag))
-		return luaL_error(L, "Invalid filter mode: %s", magstr);
+		return luax_enumerror(L, "filter mode", Texture::getConstants(f.mag), magstr);
 
 	f.anisotropy = (float) luaL_optnumber(L, 4, 1.0);
 

+ 25 - 25
src/modules/graphics/wrap_Graphics.cpp

@@ -519,7 +519,7 @@ int w_captureScreenshot(lua_State *L)
 
 		image::FormatHandler::EncodedFormat format;
 		if (!image::ImageData::getConstant(ext.c_str(), format))
-			return luaL_error(L, "Invalid encoded image format: %s", ext.c_str());
+			return luax_enumerror(L, "encoded image format", image::ImageData::getConstants(format), ext.c_str());
 
 		ScreenshotFileInfo *fileinfo = new ScreenshotFileInfo;
 		fileinfo->filename = filename;
@@ -609,7 +609,7 @@ int w_stencil(lua_State *L)
 	{
 		const char *actionstr = luaL_checkstring(L, 2);
 		if (!getConstant(actionstr, action))
-			return luaL_error(L, "Invalid stencil draw action: %s", actionstr);
+			return luax_enumerror(L, "stencil draw action", getConstants(action), actionstr);
 	}
 
 	int stencilvalue = (int) luaL_optinteger(L, 3, 1);
@@ -647,7 +647,7 @@ int w_setStencilTest(lua_State *L)
 	{
 		const char *comparestr = luaL_checkstring(L, 1);
 		if (!getConstant(comparestr, compare))
-			return luaL_error(L, "Invalid compare mode: %s", comparestr);
+			return luax_enumerror(L, "compare mode", getConstants(compare), comparestr);
 
 		comparevalue = (int) luaL_checkinteger(L, 2);
 	}
@@ -1129,7 +1129,7 @@ int w_newSpriteBatch(lua_State *L)
 	{
 		const char *usagestr = luaL_checkstring(L, 3);
 		if (!vertex::getConstant(usagestr, usage))
-			return luaL_error(L, "Invalid usage hint: %s", usagestr);
+			return luax_enumerror(L, "usage hint", vertex::getConstants(usage), usagestr);
 	}
 
 	SpriteBatch *t = nullptr;
@@ -1195,7 +1195,7 @@ int w_newCanvas(lua_State *L)
 		{
 			const char *str = luaL_checkstring(L, -1);
 			if (!getConstant(str, settings.format))
-				return luaL_error(L, "Invalid pixel format: %s", str);
+				return luax_enumerror(L, "pixel format", str);
 		}
 		lua_pop(L, 1);
 
@@ -1204,7 +1204,7 @@ int w_newCanvas(lua_State *L)
 		{
 			const char *str = luaL_checkstring(L, -1);
 			if (!Texture::getConstant(str, settings.type))
-				return luaL_error(L, "Invalid texture type: %s", str);
+				return luax_enumerror(L, "texture type", Texture::getConstants(settings.type), str);
 		}
 		lua_pop(L, 1);
 
@@ -1221,7 +1221,7 @@ int w_newCanvas(lua_State *L)
 		{
 			const char *str = luaL_checkstring(L, -1);
 			if (!Canvas::getConstant(str, settings.mipmaps))
-				return luaL_error(L, "Invalid Canvas mipmap mode: %s", str);
+				return luax_enumerror(L, "Canvas mipmap mode", Canvas::getConstants(settings.mipmaps), str);
 		}
 		lua_pop(L, 1);
 	}
@@ -1394,7 +1394,7 @@ static vertex::Usage luax_optmeshusage(lua_State *L, int idx, vertex::Usage def)
 	const char *usagestr = lua_isnoneornil(L, idx) ? nullptr : luaL_checkstring(L, idx);
 
 	if (usagestr && !vertex::getConstant(usagestr, def))
-		luaL_error(L, "Invalid usage hint: %s", usagestr);
+		luax_enumerror(L, "usage hint", vertex::getConstants(def), usagestr);
 
 	return def;
 }
@@ -1404,7 +1404,7 @@ static Mesh::DrawMode luax_optmeshdrawmode(lua_State *L, int idx, Mesh::DrawMode
 	const char *modestr = lua_isnoneornil(L, idx) ? nullptr : luaL_checkstring(L, idx);
 
 	if (modestr && !Mesh::getConstant(modestr, def))
-		luaL_error(L, "Invalid mesh draw mode: %s", modestr);
+		luax_enumerror(L, "mesh draw mode", Mesh::getConstants(def), modestr);
 
 	return def;
 }
@@ -1499,7 +1499,7 @@ static Mesh *newCustomMesh(lua_State *L)
 		const char *tname = luaL_checkstring(L, -2);
 		if (!Mesh::getConstant(tname, format.type))
 		{
-			luaL_error(L, "Invalid Mesh vertex data type name: %s", tname);
+			luax_enumerror(L, "Mesh vertex data type name", Mesh::getConstants(format.type), tname);
 			return nullptr;
 		}
 
@@ -1782,14 +1782,14 @@ int w_setBlendMode(lua_State *L)
 	Graphics::BlendMode mode;
 	const char *str = luaL_checkstring(L, 1);
 	if (!Graphics::getConstant(str, mode))
-		return luaL_error(L, "Invalid blend mode: %s", str);
+		return luax_enumerror(L, "blend mode", Graphics::getConstants(mode), str);
 
 	Graphics::BlendAlpha alphamode = Graphics::BLENDALPHA_MULTIPLY;
 	if (!lua_isnoneornil(L, 2))
 	{
 		const char *alphastr = luaL_checkstring(L, 2);
 		if (!Graphics::getConstant(alphastr, alphamode))
-			return luaL_error(L, "Invalid blend alpha mode: %s", alphastr);
+			return luax_enumerror(L, "blend alpha mode", Graphics::getConstants(alphamode), alphastr);
 	}
 
 	luax_catchexcept(L, [&](){ instance()->setBlendMode(mode, alphamode); });
@@ -1823,9 +1823,9 @@ int w_setDefaultFilter(lua_State *L)
 	const char *magstr = luaL_optstring(L, 2, minstr);
 
 	if (!Texture::getConstant(minstr, f.min))
-		return luaL_error(L, "Invalid filter mode: %s", minstr);
+		return luax_enumerror(L, "filter mode", Texture::getConstants(f.min), minstr);
 	if (!Texture::getConstant(magstr, f.mag))
-		return luaL_error(L, "Invalid filter mode: %s", magstr);
+		return luax_enumerror(L, "filter mode", Texture::getConstants(f.mag), magstr);
 
 	f.anisotropy = (float) luaL_optnumber(L, 3, 1.0);
 
@@ -1856,7 +1856,7 @@ int w_setDefaultMipmapFilter(lua_State *L)
 	{
 		const char *str = luaL_checkstring(L, 1);
 		if (!Texture::getConstant(str, filter))
-			return luaL_error(L, "Invalid filter mode: %s", str);
+			return luax_enumerror(L, "filter mode", Texture::getConstants(filter), str);
 	}
 
 	float sharpness = (float) luaL_optnumber(L, 2, 0);
@@ -1896,7 +1896,7 @@ int w_setLineStyle(lua_State *L)
 	Graphics::LineStyle style;
 	const char *str = luaL_checkstring(L, 1);
 	if (!Graphics::getConstant(str, style))
-		return luaL_error(L, "Invalid line style: %s", str);
+		return luax_enumerror(L, "line style", Graphics::getConstants(style), str);
 
 	instance()->setLineStyle(style);
 	return 0;
@@ -1907,7 +1907,7 @@ int w_setLineJoin(lua_State *L)
 	Graphics::LineJoin join;
 	const char *str = luaL_checkstring(L, 1);
 	if (!Graphics::getConstant(str, join))
-		return luaL_error(L, "Invalid line join mode: %s", str);
+		return luax_enumerror(L, "line join", Graphics::getConstants(join), str);
 
 	instance()->setLineJoin(join);
 	return 0;
@@ -2373,7 +2373,7 @@ int w_printf(lua_State *L)
 
 	const char *astr = lua_isnoneornil(L, formatidx + 1) ? nullptr : luaL_checkstring(L, formatidx + 1);
 	if (astr != nullptr && !Font::getConstant(astr, align))
-		return luaL_error(L, "Incorrect alignment: %s", astr);
+		return luax_enumerror(L, "alignment", Font::getConstants(align), astr);
 
 	if (font != nullptr)
 		luax_catchexcept(L, [&](){ instance()->printf(str, font, wrap, align, m); });
@@ -2526,7 +2526,7 @@ int w_rectangle(lua_State *L)
 	Graphics::DrawMode mode;
 	const char *str = luaL_checkstring(L, 1);
 	if (!Graphics::getConstant(str, mode))
-		return luaL_error(L, "Invalid draw mode: %s", str);
+		return luax_enumerror(L, "draw mode", Graphics::getConstants(mode), str);
 
 	float x = (float)luaL_checknumber(L, 2);
 	float y = (float)luaL_checknumber(L, 3);
@@ -2558,7 +2558,7 @@ int w_circle(lua_State *L)
 	Graphics::DrawMode mode;
 	const char *str = luaL_checkstring(L, 1);
 	if (!Graphics::getConstant(str, mode))
-		return luaL_error(L, "Invalid draw mode: %s", str);
+		return luax_enumerror(L, "draw mode", Graphics::getConstants(mode), str);
 
 	float x = (float)luaL_checknumber(L, 2);
 	float y = (float)luaL_checknumber(L, 3);
@@ -2580,7 +2580,7 @@ int w_ellipse(lua_State *L)
 	Graphics::DrawMode mode;
 	const char *str = luaL_checkstring(L, 1);
 	if (!Graphics::getConstant(str, mode))
-		return luaL_error(L, "Invalid draw mode: %s", str);
+		return luax_enumerror(L, "draw mode", Graphics::getConstants(mode), str);
 
 	float x = (float)luaL_checknumber(L, 2);
 	float y = (float)luaL_checknumber(L, 3);
@@ -2603,7 +2603,7 @@ int w_arc(lua_State *L)
 	Graphics::DrawMode drawmode;
 	const char *drawstr = luaL_checkstring(L, 1);
 	if (!Graphics::getConstant(drawstr, drawmode))
-		return luaL_error(L, "Invalid draw mode: %s", drawstr);
+		return luax_enumerror(L, "draw mode", Graphics::getConstants(drawmode), drawstr);
 
 	int startidx = 2;
 
@@ -2613,7 +2613,7 @@ int w_arc(lua_State *L)
 	{
 		const char *arcstr = luaL_checkstring(L, 2);
 		if (!Graphics::getConstant(arcstr, arcmode))
-			return luaL_error(L, "Invalid arc mode: %s", arcstr);
+			return luax_enumerror(L, "arc mode", Graphics::getConstants(arcmode), arcstr);
 
 		startidx = 3;
 	}
@@ -2642,7 +2642,7 @@ int w_polygon(lua_State *L)
 	Graphics::DrawMode mode;
 	const char *str = luaL_checkstring(L, 1);
 	if (!Graphics::getConstant(str, mode))
-		return luaL_error(L, "Invalid draw mode: %s", str);
+		return luax_enumerror(L, "draw mode", Graphics::getConstants(mode), str);
 
 	bool is_table = false;
 	if (args == 1 && lua_istable(L, 2))
@@ -2704,7 +2704,7 @@ int w_push(lua_State *L)
 	Graphics::StackType stype = Graphics::STACK_TRANSFORM;
 	const char *sname = lua_isnoneornil(L, 1) ? nullptr : luaL_checkstring(L, 1);
 	if (sname && !Graphics::getConstant(sname, stype))
-		return luaL_error(L, "Invalid graphics stack type: %s", sname);
+		return luax_enumerror(L, "graphics stack type", Graphics::getConstants(stype), sname);
 
 	luax_catchexcept(L, [&](){ instance()->push(stype); });
 

+ 4 - 4
src/modules/graphics/wrap_Mesh.cpp

@@ -291,7 +291,7 @@ int w_Mesh_getVertexFormat(lua_State *L)
 	for (size_t i = 0; i < vertexformat.size(); i++)
 	{
 		if (!Mesh::getConstant(vertexformat[i].type, tname))
-			return luaL_error(L, "Unknown vertex attribute data type.");
+			return luax_enumerror(L, "vertex attribute data type", Mesh::getConstants(vertexformat[i].type), tname);
 
 		lua_createtable(L, 3, 0);
 
@@ -339,7 +339,7 @@ int w_Mesh_attachAttribute(lua_State *L)
 	Mesh::AttributeStep step = Mesh::STEP_PER_VERTEX;
 	const char *stepstr = lua_isnoneornil(L, 4) ? nullptr : luaL_checkstring(L, 4);
 	if (stepstr != nullptr && !Mesh::getConstant(stepstr, step))
-		return luaL_error(L, "Invalid vertex attribute step: %s", stepstr);
+		return luax_enumerror(L, "vertex attribute step", Mesh::getConstants(step), stepstr);
 
 	const char *attachname = luaL_optstring(L, 5, name);
 
@@ -382,7 +382,7 @@ int w_Mesh_setVertexMap(lua_State *L)
 		const char *indextypestr = luaL_checkstring(L, 3);
 		IndexDataType indextype;
 		if (!vertex::getConstant(indextypestr, indextype))
-			return luaL_error(L, "Invalid index data type: %s", indextypestr);
+			return luax_enumerror(L, "index data type", vertex::getConstants(indextype), indextypestr);
 
 		size_t datatypesize = vertex::getIndexDataSize(indextype);
 
@@ -488,7 +488,7 @@ int w_Mesh_setDrawMode(lua_State *L)
 	Mesh::DrawMode mode;
 
 	if (!Mesh::getConstant(str, mode))
-		return luaL_error(L, "Invalid mesh draw mode: %s", str);
+		return luax_enumerror(L, "mesh draw mode", Mesh::getConstants(mode), str);
 
 	t->setDrawMode(mode);
 	return 0;

+ 2 - 2
src/modules/graphics/wrap_ParticleSystem.cpp

@@ -99,7 +99,7 @@ int w_ParticleSystem_setInsertMode(lua_State *L)
 	ParticleSystem::InsertMode mode;
 	const char *str = luaL_checkstring(L, 2);
 	if (!ParticleSystem::getConstant(str, mode))
-		return luaL_error(L, "Invalid insert mode: '%s'", str);
+		return luax_enumerror(L, "insert mode", ParticleSystem::getConstants(mode), str);
 	t->setInsertMode(mode);
 	return 0;
 }
@@ -205,7 +205,7 @@ int w_ParticleSystem_setAreaSpread(lua_State *L)
 
 	const char *str = lua_isnoneornil(L, 2) ? 0 : luaL_checkstring(L, 2);
 	if (str && !ParticleSystem::getConstant(str, distribution))
-		return luaL_error(L, "Invalid particle distribution: %s", str);
+		return luax_enumerror(L, "particle distribution", ParticleSystem::getConstants(distribution), str);
 
 	if (distribution != ParticleSystem::DISTRIBUTION_NONE)
 	{

+ 2 - 2
src/modules/graphics/wrap_Shader.cpp

@@ -163,7 +163,7 @@ int w_Shader_sendMatrices(lua_State *L, int startidx, Shader *shader, const Shad
 		const char *layoutstr = lua_tostring(L, startidx);
 		math::Transform::MatrixLayout layout;
 		if (!math::Transform::getConstant(layoutstr, layout))
-			return luaL_error(L, "Invalid matrix layout: %s", layoutstr);
+			return luax_enumerror(L, "matrix layout", math::Transform::getConstants(layout), layoutstr);
 
 		columnmajor = (layout == math::Transform::MATRIX_COLUMN_MAJOR);
 		startidx++;
@@ -316,7 +316,7 @@ static int w_Shader_sendData(lua_State *L, int startidx, Shader *shader, const S
 		const char *layoutstr = lua_tostring(L, startidx + 1);
 		math::Transform::MatrixLayout layout;
 		if (!math::Transform::getConstant(layoutstr, layout))
-			return luaL_error(L, "Invalid matrix layout: %s", layoutstr);
+			return luax_enumerror(L, "matrix layout", math::Transform::getConstants(layout), layoutstr);
 
 		columnmajor = (layout == math::Transform::MATRIX_COLUMN_MAJOR);
 		startidx++;

+ 2 - 2
src/modules/graphics/wrap_Text.cpp

@@ -52,7 +52,7 @@ int w_Text_setf(lua_State *L)
 	Font::AlignMode align;
 	const char *alignstr = luaL_checkstring(L, 4);
 	if (!Font::getConstant(alignstr, align))
-		return luaL_error(L, "Invalid align mode: %s", alignstr);
+		return luax_enumerror(L, "align mode", Font::getConstants(align), alignstr);
 
 	std::vector<Font::ColoredString> newtext;
 	luax_checkcoloredstring(L, 2, newtext);
@@ -111,7 +111,7 @@ int w_Text_addf(lua_State *L)
 	const char *alignstr = luaL_checkstring(L, 4);
 
 	if (!Font::getConstant(alignstr, align))
-		return luaL_error(L, "Invalid align mode: %s", alignstr);
+		return luax_enumerror(L, "align mode", Font::getConstants(align), alignstr);
 
 	if (luax_istype(L, 5, math::Transform::type))
 	{

+ 8 - 8
src/modules/graphics/wrap_Texture.cpp

@@ -35,7 +35,7 @@ int w_Texture_getTextureType(lua_State *L)
 	Texture *t = luax_checktexture(L, 1);
 	const char *tstr;
 	if (!Texture::getConstant(t->getTextureType(), tstr))
-		return luaL_error(L, "unknown texture type");
+		return luax_enumerror(L, "texture type", Texture::getConstants(TEXTURE_MAX_ENUM), tstr);
 	lua_pushstring(L, tstr);
 	return 1;
 }
@@ -138,9 +138,9 @@ int w_Texture_setFilter(lua_State *L)
 	const char *magstr = luaL_optstring(L, 3, minstr);
 
 	if (!Texture::getConstant(minstr, f.min))
-		return luaL_error(L, "Invalid filter mode: %s", minstr);
+		return luax_enumerror(L, "filter mode", Texture::getConstants(f.min), minstr);
 	if (!Texture::getConstant(magstr, f.mag))
-		return luaL_error(L, "Invalid filter mode: %s", magstr);
+		return luax_enumerror(L, "filter mode", Texture::getConstants(f.mag), magstr);
 
 	f.anisotropy = (float) luaL_optnumber(L, 4, 1.0);
 
@@ -178,7 +178,7 @@ int w_Texture_setMipmapFilter(lua_State *L)
 	{
 		const char *mipmapstr = luaL_checkstring(L, 2);
 		if (!Texture::getConstant(mipmapstr, f.mipmap))
-			return luaL_error(L, "Invalid filter mode: %s", mipmapstr);
+			return luax_enumerror(L, "filter mode", Texture::getConstants(f.mipmap), mipmapstr);
 	}
 
 	luax_catchexcept(L, [&](){ t->setFilter(f); });
@@ -213,11 +213,11 @@ int w_Texture_setWrap(lua_State *L)
 	const char *rstr = luaL_optstring(L, 4, sstr);
 
 	if (!Texture::getConstant(sstr, w.s))
-		return luaL_error(L, "Invalid wrap mode: %s", sstr);
+		return luax_enumerror(L, "wrap mode", Texture::getConstants(w.s), sstr);
 	if (!Texture::getConstant(tstr, w.t))
-		return luaL_error(L, "Invalid wrap mode, %s", tstr);
+		return luax_enumerror(L, "wrap mode", Texture::getConstants(w.t), tstr);
 	if (!Texture::getConstant(rstr, w.r))
-		return luaL_error(L, "Invalid wrap mode, %s", rstr);
+		return luax_enumerror(L, "wrap mode", Texture::getConstants(w.r), rstr);
 
 	luax_pushboolean(L, t->setWrap(w));
 	return 1;
@@ -275,7 +275,7 @@ int w_Texture_setDepthSampleMode(lua_State *L)
 
 		mode.hasValue = true;
 		if (!getConstant(str, mode.value))
-			return luaL_error(L, "Invalid compare mode: %s", str);
+			return luax_enumerror(L, "compare mode", getConstants(mode.value), str);
 	}
 
 	luax_catchexcept(L, [&]() { t->setDepthSampleMode(mode); });

+ 2 - 2
src/modules/graphics/wrap_Video.cpp

@@ -119,9 +119,9 @@ int w_Video_setFilter(lua_State *L)
 	const char *magstr = luaL_optstring(L, 3, minstr);
 
 	if (!Texture::getConstant(minstr, f.min))
-		return luaL_error(L, "Invalid filter mode: %s", minstr);
+		return luax_enumerror(L, "filter mode", Texture::getConstants(f.min), minstr);
 	if (!Texture::getConstant(magstr, f.mag))
-		return luaL_error(L, "Invalid filter mode: %s", magstr);
+		return luax_enumerror(L, "filter mode", Texture::getConstants(f.mag), magstr);
 
 	f.anisotropy = (float) luaL_optnumber(L, 4, 1.0);
 

+ 5 - 0
src/modules/image/ImageData.cpp

@@ -502,6 +502,11 @@ bool ImageData::getConstant(FormatHandler::EncodedFormat in, const char *&out)
 	return encodedFormats.find(in, out);
 }
 
+std::vector<std::string> ImageData::getConstants(FormatHandler::EncodedFormat)
+{
+	return encodedFormats.getNames();
+}
+
 StringMap<FormatHandler::EncodedFormat, FormatHandler::ENCODED_MAX_ENUM>::Entry ImageData::encodedFormatEntries[] =
 {
 	{"tga", FormatHandler::ENCODED_TGA},

+ 1 - 0
src/modules/image/ImageData.h

@@ -117,6 +117,7 @@ public:
 
 	static bool getConstant(const char *in, FormatHandler::EncodedFormat &out);
 	static bool getConstant(FormatHandler::EncodedFormat in, const char *&out);
+	static std::vector<std::string> getConstants(FormatHandler::EncodedFormat);
 
 private:
 

+ 1 - 1
src/modules/image/wrap_Image.cpp

@@ -50,7 +50,7 @@ int w_newImageData(lua_State *L)
 		{
 			const char *fstr = luaL_checkstring(L, 3);
 			if (!getConstant(fstr, format))
-				return luaL_error(L, "Invalid pixel format: %s", fstr);
+				return luax_enumerror(L, "pixel format", fstr);
 		}
 
 		size_t numbytes = 0;

+ 1 - 1
src/modules/image/wrap_ImageData.cpp

@@ -265,7 +265,7 @@ int w_ImageData_encode(lua_State *L)
 	FormatHandler::EncodedFormat format;
 	const char *fmt = luaL_checkstring(L, 2);
 	if (!ImageData::getConstant(fmt, format))
-		return luaL_error(L, "Invalid encoded image format '%s'.", fmt);
+		return luax_enumerror(L, "encoded image format", ImageData::getConstants(format), fmt);
 
 	bool hasfilename = false;
 

+ 4 - 4
src/modules/joystick/wrap_Joystick.cpp

@@ -172,7 +172,7 @@ int w_Joystick_getGamepadAxis(lua_State *L)
 	Joystick::GamepadAxis axis;
 
 	if (!joystick::Joystick::getConstant(str, axis))
-		return luaL_error(L, "Invalid gamepad axis: %s", str);
+		return luax_enumerror(L, "gamepad axis", str);
 
 	lua_pushnumber(L, j->getGamepadAxis(axis));
 	return 1;
@@ -201,7 +201,7 @@ int w_Joystick_isGamepadDown(lua_State *L)
 			const char *str = luaL_checkstring(L, -1);
 
 			if (!joystick::Joystick::getConstant(str, button))
-				return luaL_error(L, "Invalid gamepad button: %s", str);
+				return luax_enumerror(L, "gamepad button", str);
 
 			buttons.push_back(button);
 
@@ -215,7 +215,7 @@ int w_Joystick_isGamepadDown(lua_State *L)
 			const char *str = luaL_checkstring(L, i + 2);
 
 			if (!joystick::Joystick::getConstant(str, button))
-				return luaL_error(L, "Invalid gamepad button: %s", str);
+				return luax_enumerror(L, "gamepad button", str);
 
 			buttons.push_back(button);
 		}
@@ -237,7 +237,7 @@ int w_Joystick_getGamepadMapping(lua_State *L)
 	else if (Joystick::getConstant(gpbindstr, gpinput.button))
 		gpinput.type = Joystick::INPUT_TYPE_BUTTON;
 	else
-		return luaL_error(L, "Invalid gamepad axis/button: %s", gpbindstr);
+		return luax_enumerror(L, "gamepad axis/button", gpbindstr);
 
 	Joystick::JoystickInput jinput;
 	jinput.type = Joystick::INPUT_TYPE_MAX_ENUM;

+ 4 - 4
src/modules/joystick/wrap_JoystickModule.cpp

@@ -79,13 +79,13 @@ int w_setGamepadMapping(lua_State *L)
 	else if (Joystick::getConstant(gpbindstr, gpinput.button))
 		gpinput.type = Joystick::INPUT_TYPE_BUTTON;
 	else
-		return luaL_error(L, "Invalid gamepad axis/button: %s", gpbindstr);
+		return luax_enumerror(L, "gamepad axis/button", gpbindstr);
 
 	const char *jinputtypestr = luaL_checkstring(L, 3);
 	Joystick::JoystickInput jinput;
 
 	if (!Joystick::getConstant(jinputtypestr, jinput.type))
-		return luaL_error(L, "Invalid joystick input type: %s", jinputtypestr);
+		return luax_enumerror(L, "joystick input type", jinputtypestr);
 
 	const char *hatstr;
 	switch (jinput.type)
@@ -101,10 +101,10 @@ int w_setGamepadMapping(lua_State *L)
 		jinput.hat.index = (int) luaL_checkinteger(L, 4) - 1;
 		hatstr = luaL_checkstring(L, 5);
 		if (!Joystick::getConstant(hatstr, jinput.hat.value))
-			return luaL_error(L, "Invalid joystick hat: %s", hatstr);
+			return luax_enumerror(L, "joystick hat", hatstr);
 		break;
 	default:
-		return luaL_error(L, "Invalid joystick input type: %s", jinputtypestr);
+		return luax_enumerror(L, "joystick input type", jinputtypestr);
 	}
 
 	bool success = false;

+ 6 - 6
src/modules/keyboard/wrap_Keyboard.cpp

@@ -61,7 +61,7 @@ int w_isDown(lua_State *L)
 
 			const char *name = luaL_checkstring(L, -1);
 			if (!Keyboard::getConstant(name, k))
-				return luaL_error(L, "Invalid key constant: %s", name);
+				return luax_enumerror(L, "key constant", name);
 
 			keylist.push_back(k);
 			lua_pop(L, 1);
@@ -73,7 +73,7 @@ int w_isDown(lua_State *L)
 		{
 			const char *name = luaL_checkstring(L, i + 1);
 			if (!Keyboard::getConstant(name, k))
-				return luaL_error(L, "Invalid key constant: %s", name);
+				return luax_enumerror(L, "key constant", name);
 
 			keylist.push_back(k);
 		}
@@ -101,7 +101,7 @@ int w_isScancodeDown(lua_State *L)
 
 			const char *name = luaL_checkstring(L, -1);
 			if (!Keyboard::getConstant(name, scancode))
-				return luaL_error(L, "Invalid scancode: %s", name);
+				return luax_enumerror(L, "scancode", name);
 
 			scancodelist.push_back(scancode);
 			lua_pop(L, 1);
@@ -113,7 +113,7 @@ int w_isScancodeDown(lua_State *L)
 		{
 			const char *name = luaL_checkstring(L, i + 1);
 			if (!Keyboard::getConstant(name, scancode))
-				return luaL_error(L, "Invalid scancode: %s", name);
+				return luax_enumerror(L, "scancode", name);
 
 			scancodelist.push_back(scancode);
 		}
@@ -128,7 +128,7 @@ int w_getScancodeFromKey(lua_State *L)
 	const char *keystr = luaL_checkstring(L, 1);
 	Keyboard::Key key;
 	if (!Keyboard::getConstant(keystr, key))
-		return luaL_error(L, "Invalid key constant: %s", keystr);
+		return luax_enumerror(L, "key constant", keystr);
 
 	Keyboard::Scancode scancode = instance()->getScancodeFromKey(key);
 
@@ -145,7 +145,7 @@ int w_getKeyFromScancode(lua_State *L)
 	const char *scancodestr = luaL_checkstring(L, 1);
 	Keyboard::Scancode scancode;
 	if (!Keyboard::getConstant(scancodestr, scancode))
-		return luaL_error(L, "Invalid scancode: %s", scancodestr);
+		return luax_enumerror(L, "scancode", scancodestr);
 
 	Keyboard::Key key = instance()->getKeyFromScancode(scancode);
 

+ 5 - 0
src/modules/math/Transform.cpp

@@ -139,6 +139,11 @@ bool Transform::getConstant(MatrixLayout in, const char *&out)
 	return matrixLayouts.find(in, out);
 }
 
+std::vector<std::string> Transform::getConstants(MatrixLayout)
+{
+	return matrixLayouts.getNames();
+}
+
 StringMap<Transform::MatrixLayout, Transform::MATRIX_MAX_ENUM>::Entry Transform::matrixLayoutEntries[] =
 {
 	{ "row",    MATRIX_ROW_MAJOR    },

+ 1 - 0
src/modules/math/Transform.h

@@ -71,6 +71,7 @@ public:
 
 	static bool getConstant(const char *in, MatrixLayout &out);
 	static bool getConstant(MatrixLayout in, const char *&out);
+	static std::vector<std::string> getConstants(MatrixLayout);
 
 private:
 

+ 2 - 2
src/modules/math/wrap_Math.cpp

@@ -357,7 +357,7 @@ int w_compress(lua_State *L)
 	Compressor::Format format = Compressor::FORMAT_LZ4;
 
 	if (fstr && !Compressor::getConstant(fstr, format))
-		return luaL_error(L, "Invalid compressed data format: %s", fstr);
+		return luax_enumerror(L, "compressed data format", Compressor::getConstants(format), fstr);
 
 	int level = (int) luaL_optinteger(L, 3, -1);
 
@@ -399,7 +399,7 @@ int w_decompress(lua_State *L)
 		const char *fstr = luaL_checkstring(L, 2);
 
 		if (!Compressor::getConstant(fstr, format))
-			return luaL_error(L, "Invalid compressed data format: %s", fstr);
+			return luax_enumerror(L, "compressed data format", Compressor::getConstants(format), fstr);
 
 		size_t compressedsize = 0;
 		const char *cbytes = nullptr;

+ 1 - 1
src/modules/math/wrap_Transform.cpp

@@ -140,7 +140,7 @@ int w_Transform_setMatrix(lua_State *L)
 		const char *layoutstr = lua_tostring(L, idx);
 		Transform::MatrixLayout layout;
 		if (!Transform::getConstant(layoutstr, layout))
-			return luaL_error(L, "Invalid matrix layout: %s", layoutstr);
+			return luax_enumerror(L, "matrix layout", Transform::getConstants(layout), layoutstr);
 
 		columnmajor = (layout == Transform::MATRIX_COLUMN_MAJOR);
 		idx++;

+ 1 - 1
src/modules/mouse/wrap_Mouse.cpp

@@ -57,7 +57,7 @@ int w_getSystemCursor(lua_State *L)
 	Cursor::SystemCursor systemCursor;
 
 	if (!Cursor::getConstant(str, systemCursor))
-		return luaL_error(L, "Invalid system cursor type: %s", str);
+		return luax_enumerror(L, "system cursor type", str);
 
 	Cursor *cursor = 0;
 	luax_catchexcept(L, [&](){ cursor = instance()->getSystemCursor(systemCursor); });

+ 6 - 1
src/modules/physics/Body.cpp

@@ -36,11 +36,16 @@ bool Body::getConstant(const char *in, Type &out)
 	return types.find(in, out);
 }
 
-bool Body::getConstant(Type in, const char  *&out)
+bool Body::getConstant(Type in, const char *&out)
 {
 	return types.find(in, out);
 }
 
+std::vector<std::string> Body::getConstants(Type)
+{
+	return types.getNames();
+}
+
 StringMap<Body::Type, Body::BODY_MAX_ENUM>::Entry Body::typeEntries[] =
 {
 	{"static", Body::BODY_STATIC},

+ 1 - 0
src/modules/physics/Body.h

@@ -49,6 +49,7 @@ public:
 
 	static bool getConstant(const char *in, Type &out);
 	static bool getConstant(Type in, const char  *&out);
+	static std::vector<std::string> getConstants(Type);
 
 private:
 

+ 1 - 1
src/modules/physics/box2d/wrap_Physics.cpp

@@ -74,7 +74,7 @@ int w_newBody(lua_State *L)
 	Body::Type btype = Body::BODY_STATIC;
 	const char *typestr = lua_isnoneornil(L, 4) ? 0 : lua_tostring(L, 4);
 	if (typestr && !Body::getConstant(typestr, btype))
-		return luaL_error(L, "Invalid Body type: %s", typestr);
+		return luax_enumerror(L, "Body type", Body::getConstants(btype), typestr);
 
 	Body *body;
 	luax_catchexcept(L, [&](){ body = instance()->newBody(world, x, y, btype); });

+ 14 - 4
src/modules/window/Window.cpp

@@ -34,22 +34,27 @@ void Window::swapBuffers()
 {
 }
 
-bool Window::getConstant(const char *in, Window::FullscreenType &out)
+bool Window::getConstant(const char *in, FullscreenType &out)
 {
 	return fullscreenTypes.find(in, out);
 }
 
-bool Window::getConstant(Window::FullscreenType in, const char *&out)
+bool Window::getConstant(FullscreenType in, const char *&out)
 {
 	return fullscreenTypes.find(in, out);
 }
 
-bool Window::getConstant(const char *in, Window::Setting &out)
+std::vector<std::string> Window::getConstants(FullscreenType)
+{
+	return fullscreenTypes.getNames();
+}
+
+bool Window::getConstant(const char *in, Setting &out)
 {
 	return settings.find(in, out);
 }
 
-bool Window::getConstant(Window::Setting in, const char *&out)
+bool Window::getConstant(Setting in, const char *&out)
 {
 	return settings.find(in, out);
 }
@@ -64,6 +69,11 @@ bool Window::getConstant(MessageBoxType in, const char *&out)
 	return messageBoxTypes.find(in, out);
 }
 
+std::vector<std::string> Window::getConstants(MessageBoxType)
+{
+	return messageBoxTypes.getNames();
+}
+
 StringMap<Window::Setting, Window::SETTING_MAX_ENUM>::Entry Window::settingEntries[] =
 {
 	{"fullscreen", SETTING_FULLSCREEN},

+ 2 - 0
src/modules/window/Window.h

@@ -198,9 +198,11 @@ public:
 
 	static bool getConstant(const char *in, FullscreenType &out);
 	static bool getConstant(FullscreenType in, const char *&out);
+	static std::vector<std::string> getConstants(FullscreenType);
 
 	static bool getConstant(const char *in, MessageBoxType &out);
 	static bool getConstant(MessageBoxType in, const char *&out);
+	static std::vector<std::string> getConstants(MessageBoxType);
 
 private:
 

+ 5 - 5
src/modules/window/wrap_Window.cpp

@@ -67,7 +67,7 @@ static int readWindowSettings(lua_State *L, int idx, WindowSettings &settings)
 		Window::Setting setting;
 
 		if (!Window::getConstant(key, setting))
-			return luaL_error(L, "Invalid window setting: %s", key);
+			return luax_enumerror(L, "window setting", key);
 
 		lua_pop(L, 1);
 	}
@@ -77,7 +77,7 @@ static int readWindowSettings(lua_State *L, int idx, WindowSettings &settings)
 	{
 		const char *typestr = luaL_checkstring(L, -1);
 		if (!Window::getConstant(typestr, settings.fstype))
-			return luaL_error(L, "Invalid fullscreen type: %s", typestr);
+			return luax_enumerror(L, "fullscreen type", Window::getConstants(settings.fstype), typestr);
 	}
 	lua_pop(L, 1);
 
@@ -274,7 +274,7 @@ int w_setFullscreen(lua_State *L)
 
 	const char *typestr = lua_isnoneornil(L, 2) ? 0 : luaL_checkstring(L, 2);
 	if (typestr && !Window::getConstant(typestr, fstype))
-		return luaL_error(L, "Invalid fullscreen type: %s", typestr);
+		return luax_enumerror(L, "fullscreen type", Window::getConstants(fstype), typestr);
 
 	bool success = false;
 	luax_catchexcept(L, [&]() {
@@ -527,7 +527,7 @@ int w_showMessageBox(lua_State *L)
 
 		const char *typestr = lua_isnoneornil(L, 4) ? nullptr : luaL_checkstring(L, 4);
 		if (typestr && !Window::getConstant(typestr, data.type))
-			return luaL_error(L, "Invalid messagebox type: %s", typestr);
+			return luax_enumerror(L, "messagebox type", Window::getConstants(data.type), typestr);
 
 		data.attachToWindow = luax_optboolean(L, 5, true);
 
@@ -538,7 +538,7 @@ int w_showMessageBox(lua_State *L)
 	{
 		const char *typestr = lua_isnoneornil(L, 3) ? nullptr : luaL_checkstring(L, 3);
 		if (typestr && !Window::getConstant(typestr, data.type))
-			return luaL_error(L, "Invalid messagebox type: %s", typestr);
+			return luax_enumerror(L, "messagebox type", Window::getConstants(data.type), typestr);
 
 		data.attachToWindow = luax_optboolean(L, 4, true);