Browse Source

Added love.math.encode(format, data | string [, linelength]) and love.math.decode(format, data | string). Current formats are "base64" and "hex". The optional line length only applies to base64 and specifies the maximum number of characters per line in the encoded string.

Removed the variant of love.filesystem.newFileData which decoded a base64-encoded string since it's redundant.

--HG--
branch : minor
Alex Szpakowski 9 years ago
parent
commit
734789eb93

+ 110 - 13
src/common/b64.cpp

@@ -19,12 +19,98 @@
  **/
  **/
 
 
 #include "b64.h"
 #include "b64.h"
+#include "Exception.h"
+
+#include <limits>
+#include <stdio.h>
 
 
 namespace love
 namespace love
 {
 {
 
 
+// Translation table as described in RFC1113
+static const char cb64[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+// Translation table to decode (created by Bob Trower)
 static const char cd64[]="|$$$}rstuvwxyz{$$$$$$$>?@ABCDEFGHIJKLMNOPQRSTUVW$$$$$$XYZ[\\]^_`abcdefghijklmnopq";
 static const char cd64[]="|$$$}rstuvwxyz{$$$$$$$>?@ABCDEFGHIJKLMNOPQRSTUVW$$$$$$XYZ[\\]^_`abcdefghijklmnopq";
 
 
+/**
+ * encode 3 8-bit binary bytes as 4 '6-bit' characters
+ **/
+static void b64_encode_block(char in[3], char out[4], int len)
+{
+	out[0] = (char) cb64[(int)(in[0] >> 2)];
+	out[1] = (char) cb64[(int)(((in[0] & 0x03) << 4) | ((in[1] & 0xf0) >> 4))];
+	out[2] = (char) (len > 1 ? cb64[(int)(((in[1] & 0x0f) << 2) | ((in[2] & 0xc0) >> 6))] : '=');
+	out[3] = (char) (len > 2 ? cb64[(int)(in[2] & 0x3f)] : '=');
+}
+
+char *b64_encode(const char *src, size_t srclen, size_t linelen, size_t &dstlen)
+{
+	if (linelen == 0)
+		linelen = std::numeric_limits<size_t>::max();
+
+	size_t blocksout = 0;
+	size_t srcpos = 0;
+
+	size_t adjustment = (srclen % 3) ? (3 - (srclen % 3)) : 0;
+	size_t paddedlen = ((srclen + adjustment) / 3) * 4;
+
+	dstlen = paddedlen + paddedlen / linelen;
+
+	if (dstlen == 0)
+		return nullptr;
+
+	char *dst = nullptr;
+	try
+	{
+		dst = new char[dstlen + 1];
+	}
+	catch (std::exception &)
+	{
+		throw love::Exception("Out of memory.");
+	}
+
+	size_t dstpos = 0;
+
+	while (srcpos < srclen)
+	{
+		char in[3]  = {0};
+		char out[4] = {0};
+
+		int len = 0;
+
+		for (int i = 0; i < 3; i++)
+		{
+			if (srcpos >= srclen)
+				break;
+
+			in[i] = src[srcpos++];
+			len++;
+		}
+
+		if (len > 0)
+		{
+			b64_encode_block(in, out, len);
+
+			for (int i = 0; i < 4 && dstpos < dstlen; i++, dstpos++)
+				dst[dstpos] = out[i];
+
+			blocksout++;
+		}
+
+		if (blocksout >= linelen / 4 || srcpos >= srclen)
+		{
+			if (blocksout > 0 && dstpos < dstlen)
+				dst[dstpos++] = '\n';
+
+			blocksout = 0;
+		}
+	}
+
+	dst[dstpos] = '\0';
+	return dst;
+}
+
 static void b64_decode_block(char in[4], char out[3])
 static void b64_decode_block(char in[4], char out[3])
 {
 {
 	out[0] = (char)(in[0] << 2 | in[1] >> 4);
 	out[0] = (char)(in[0] << 2 | in[1] >> 4);
@@ -32,34 +118,44 @@ static void b64_decode_block(char in[4], char out[3])
 	out[2] = (char)(((in[2] << 6) & 0xc0) | in[3]);
 	out[2] = (char)(((in[2] << 6) & 0xc0) | in[3]);
 }
 }
 
 
-char *b64_decode(const char *src, int slen, int &size)
+char *b64_decode(const char *src, size_t srclen, size_t &size)
 {
 {
-	size = (slen / 4) * 3;
+	size_t paddedsize = (srclen / 4) * 3;
+
+	char *dst = nullptr;
+	try
+	{
+		dst = new char[paddedsize];
+	}
+	catch (std::exception &)
+	{
+		throw love::Exception("Out of memory.");
+	}
 
 
-	char *dst = new char[size];
 	char *d = dst;
 	char *d = dst;
 
 
-	char in[4] = {0}, out[3], v;
-	int i, len, pos = 0;
+	char in[4]  = {0};
+	char out[3] = {0};
+	size_t i, len, srcpos = 0;
 
 
-	while (pos <= slen)
+	while (srcpos <= srclen)
 	{
 	{
-		for (len = 0, i = 0; i < 4 && pos <= slen; i++)
+		for (len = 0, i = 0; i < 4 && srcpos <= srclen; i++)
 		{
 		{
-			v = 0;
+			char v = 0;
 
 
-			while (pos <= slen && v == 0)
+			while (srcpos <= srclen && v == 0)
 			{
 			{
-				v = src[pos++];
+				v = src[srcpos++];
 				v = (char)((v < 43 || v > 122) ? 0 : cd64[v - 43]);
 				v = (char)((v < 43 || v > 122) ? 0 : cd64[v - 43]);
-				if (v)
+				if (v != 0)
 					v = (char)((v == '$') ? 0 : v - 61);
 					v = (char)((v == '$') ? 0 : v - 61);
 			}
 			}
 
 
-			if (pos <= slen)
+			if (srcpos <= srclen)
 			{
 			{
 				len++;
 				len++;
-				if (v)
+				if (v != 0)
 					in[i] = (char)(v - 1);
 					in[i] = (char)(v - 1);
 			}
 			}
 			else
 			else
@@ -74,6 +170,7 @@ char *b64_decode(const char *src, int slen, int &size)
 		}
 		}
 	}
 	}
 
 
+	size = (size_t)(ptrdiff_t) (d - dst);
 	return dst;
 	return dst;
 }
 }
 
 

+ 17 - 3
src/common/b64.h

@@ -20,21 +20,35 @@
 
 
 #include "config.h"
 #include "config.h"
 
 
+#include <stddef.h>
+
 #ifndef LOVE_B64_H
 #ifndef LOVE_B64_H
 #define LOVE_B64_H
 #define LOVE_B64_H
 
 
 namespace love
 namespace love
 {
 {
 
 
+/**
+ * Base64-encode data.
+ *
+ * @param src The data to encode.
+ * @param srclen The size in bytes of the data.
+ * @param linelen The maximum length of each line in the encoded string.
+ *        0 indicates no maximum length.
+ * @param dstlen The length of the encoded string is stored here.
+ * @return A string containing the base64-encoded data (allocated with new[]).
+ */
+char *b64_encode(const char *src, size_t srclen, size_t linelen, size_t &dstlen);
+
 /**
 /**
  * Decode base64 encoded data.
  * Decode base64 encoded data.
  *
  *
  * @param src The string containing the base64 data.
  * @param src The string containing the base64 data.
- * @param slen The length of the string.
- * @param size The size of the binary data is stored here.
+ * @param srclen The length of the string.
+ * @param dstlen The size of the binary data is stored here.
  * @return A chunk of memory containing the binary data (allocated with new[]).
  * @return A chunk of memory containing the binary data (allocated with new[]).
  */
  */
-char *b64_decode(const char *src, int slen, int &size);
+char *b64_decode(const char *src, size_t srclen, size_t &dstlen);
 
 
 } // love
 } // love
 
 

+ 0 - 18
src/modules/filesystem/FileData.cpp

@@ -73,23 +73,5 @@ const std::string &FileData::getExtension() const
 	return extension;
 	return extension;
 }
 }
 
 
-bool FileData::getConstant(const char *in, Decoder &out)
-{
-	return decoders.find(in, out);
-}
-
-bool FileData::getConstant(Decoder in, const char *&out)
-{
-	return decoders.find(in, out);
-}
-
-StringMap<FileData::Decoder, FileData::DECODE_MAX_ENUM>::Entry FileData::decoderEntries[] =
-{
-	{"file", FileData::FILE},
-	{"base64", FileData::BASE64},
-};
-
-StringMap<FileData::Decoder, FileData::DECODE_MAX_ENUM> FileData::decoders(FileData::decoderEntries, sizeof(FileData::decoderEntries));
-
 } // filesystem
 } // filesystem
 } // love
 } // love

+ 3 - 15
src/modules/filesystem/FileData.h

@@ -22,10 +22,11 @@
 #define LOVE_FILESYSTEM_FILE_DATA_H
 #define LOVE_FILESYSTEM_FILE_DATA_H
 
 
 // LOVE
 // LOVE
-#include <string>
 #include "common/Data.h"
 #include "common/Data.h"
-#include "common/StringMap.h"
 #include "common/int.h"
 #include "common/int.h"
+#include "common/Exception.h"
+
+#include <string>
 
 
 namespace love
 namespace love
 {
 {
@@ -36,13 +37,6 @@ class FileData : public Data
 {
 {
 public:
 public:
 
 
-	enum Decoder
-	{
-		FILE,
-		BASE64,
-		DECODE_MAX_ENUM
-	}; // Decoder
-
 	FileData(uint64 size, const std::string &filename);
 	FileData(uint64 size, const std::string &filename);
 
 
 	virtual ~FileData();
 	virtual ~FileData();
@@ -54,9 +48,6 @@ public:
 	const std::string &getFilename() const;
 	const std::string &getFilename() const;
 	const std::string &getExtension() const;
 	const std::string &getExtension() const;
 
 
-	static bool getConstant(const char *in, Decoder &out);
-	static bool getConstant(Decoder in, const char *&out);
-
 private:
 private:
 
 
 	// The actual data.
 	// The actual data.
@@ -71,9 +62,6 @@ private:
 	// The extension (without dot). Used to identify file type.
 	// The extension (without dot). Used to identify file type.
 	std::string extension;
 	std::string extension;
 
 
-	static StringMap<Decoder, DECODE_MAX_ENUM>::Entry decoderEntries[];
-	static StringMap<Decoder, DECODE_MAX_ENUM> decoders;
-
 }; // FileData
 }; // FileData
 
 
 } // filesystem
 } // filesystem

+ 7 - 0
src/modules/filesystem/Filesystem.cpp

@@ -60,6 +60,13 @@ bool Filesystem::isAndroidSaveExternal() const
 	return useExternal;
 	return useExternal;
 }
 }
 
 
+FileData *Filesystem::newFileData(const void *data, size_t size, const char *filename) const
+{
+	FileData *fd = new FileData(size, std::string(filename));
+	memcpy(fd->getData(), data, size);
+	return fd;
+}
+
 bool Filesystem::isRealDirectory(const std::string &path) const
 bool Filesystem::isRealDirectory(const std::string &path) const
 {
 {
 #ifdef LOVE_WINDOWS
 #ifdef LOVE_WINDOWS

+ 1 - 7
src/modules/filesystem/Filesystem.h

@@ -128,13 +128,7 @@ public:
 	 * @param size The size of the data.
 	 * @param size The size of the data.
 	 * @param filename The full filename used to file type identification.
 	 * @param filename The full filename used to file type identification.
 	 **/
 	 **/
-	virtual FileData *newFileData(void *data, unsigned int size, const char *filename) const = 0;
-
-	/**
-	 * Creates a new FileData object from base64 data.
-	 * @param b64 The base64 data.
-	 **/
-	virtual FileData *newFileData(const char *b64, const char *filename) const = 0;
+	virtual FileData *newFileData(const void *data, size_t size, const char *filename) const;
 
 
 	/**
 	/**
 	 * Gets the current working directory.
 	 * Gets the current working directory.

+ 0 - 24
src/modules/filesystem/physfs/Filesystem.cpp

@@ -457,30 +457,6 @@ love::filesystem::File *Filesystem::newFile(const char *filename) const
 	return new File(filename);
 	return new File(filename);
 }
 }
 
 
-FileData *Filesystem::newFileData(void *data, unsigned int size, const char *filename) const
-{
-	FileData *fd = new FileData(size, std::string(filename));
-
-	// Copy the data into FileData.
-	memcpy(fd->getData(), data, size);
-
-	return fd;
-}
-
-FileData *Filesystem::newFileData(const char *b64, const char *filename) const
-{
-	int size = (int) strlen(b64);
-	int outsize = 0;
-	char *dst = b64_decode(b64, size, outsize);
-	FileData *fd = new FileData(outsize, std::string(filename));
-
-	// Copy the data into FileData.
-	memcpy(fd->getData(), dst, outsize);
-	delete [] dst;
-
-	return fd;
-}
-
 const char *Filesystem::getWorkingDirectory()
 const char *Filesystem::getWorkingDirectory()
 {
 {
 	if (cwd.empty())
 	if (cwd.empty())

+ 0 - 3
src/modules/filesystem/physfs/Filesystem.h

@@ -64,9 +64,6 @@ public:
 
 
 	File *newFile(const char *filename) const;
 	File *newFile(const char *filename) const;
 
 
-	FileData *newFileData(void *data, unsigned int size, const char *filename) const;
-	FileData *newFileData(const char *b64, const char *filename) const;
-
 	const char *getWorkingDirectory();
 	const char *getWorkingDirectory();
 	std::string getUserDirectory();
 	std::string getUserDirectory();
 	std::string getAppdataDirectory();
 	std::string getAppdataDirectory();

+ 2 - 19
src/modules/filesystem/wrap_Filesystem.cpp

@@ -242,26 +242,9 @@ int w_newFileData(lua_State *L)
 	size_t length = 0;
 	size_t length = 0;
 	const char *str = luaL_checklstring(L, 1, &length);
 	const char *str = luaL_checklstring(L, 1, &length);
 	const char *filename = luaL_checkstring(L, 2);
 	const char *filename = luaL_checkstring(L, 2);
-	const char *decstr = lua_isstring(L, 3) ? lua_tostring(L, 3) : 0;
 
 
-	FileData::Decoder decoder = FileData::FILE;
-
-	if (decstr && !FileData::getConstant(decstr, decoder))
-		return luaL_error(L, "Invalid FileData decoder: %s", decstr);
-
-	FileData *t = 0;
-
-	switch (decoder)
-	{
-	case FileData::FILE:
-		t = instance()->newFileData((void *)str, (int)length, filename);
-		break;
-	case FileData::BASE64:
-		t = instance()->newFileData(str, filename);
-		break;
-	default:
-		return luaL_error(L, "Invalid FileData decoder: %s", decstr);
-	}
+	FileData *t = nullptr;
+	luax_catchexcept(L, [&](){ t = instance()->newFileData(str, length, filename); });
 
 
 	luax_pushtype(L, FILESYSTEM_FILE_DATA_ID, t);
 	luax_pushtype(L, FILESYSTEM_FILE_DATA_ID, t);
 	t->release();
 	t->release();

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

@@ -23,6 +23,7 @@
 
 
 // LOVE
 // LOVE
 #include "common/Data.h"
 #include "common/Data.h"
+#include "common/StringMap.h"
 #include "filesystem/FileData.h"
 #include "filesystem/FileData.h"
 #include "thread/threads.h"
 #include "thread/threads.h"
 
 

+ 150 - 26
src/modules/math/MathModule.cpp

@@ -21,6 +21,8 @@
 // LOVE
 // LOVE
 #include "MathModule.h"
 #include "MathModule.h"
 #include "common/Vector.h"
 #include "common/Vector.h"
+#include "common/b64.h"
+#include "common/int.h"
 #include "BezierCurve.h"
 #include "BezierCurve.h"
 
 
 // STL
 // STL
@@ -34,49 +36,129 @@ using love::Vertex;
 
 
 namespace
 namespace
 {
 {
-	// check if an angle is oriented counter clockwise
-	inline bool is_oriented_ccw(const Vertex &a, const Vertex &b, const Vertex &c)
+
+static const char hexchars[] = "0123456789abcdef";
+
+char *bytesToHex(const love::uint8 *src, size_t srclen, size_t &dstlen)
+{
+	dstlen = srclen * 2;
+
+	if (dstlen == 0)
+		return nullptr;
+
+	char *dst = nullptr;
+	try
+	{
+		dst = new char[dstlen + 1];
+	}
+	catch (std::exception &)
 	{
 	{
-		// return det(b-a, c-a) >= 0
-		return ((b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x)) >= 0;
+		throw love::Exception("Out of memory.");
 	}
 	}
 
 
-	// check if a and b are on the same side of the line c->d
-	bool on_same_side(const Vertex &a, const Vertex &b, const Vertex &c, const Vertex &d)
+	for (size_t i = 0; i < srclen; i++)
+	{
+		love::uint8 b = src[i];
+		dst[i * 2 + 0] = hexchars[b >> 4];
+		dst[i * 2 + 1] = hexchars[b & 0xF];
+	}
+
+	dst[dstlen] = '\0';
+	return dst;
+}
+
+love::uint8 nibble(char c)
+{
+	if (c >= '0' && c <= '9')
+		return (love::uint8) (c - '0');
+
+	if (c >= 'A' && c <= 'F')
+		return (love::uint8) (c - 'A' + 0x0a);
+
+	if (c >= 'a' && c <= 'f')
+		return (love::uint8) (c - 'a' + 0x0a);
+
+	return 0;
+}
+
+love::uint8 *hexToBytes(const char *src, size_t srclen, size_t &dstlen)
+{
+	if (srclen >= 2 && src[0] == '0' && (src[1] == 'x' || src[1] == 'X'))
 	{
 	{
-		float px = d.x - c.x, py = d.y - c.y;
-		// return det(p, a-c) * det(p, b-c) >= 0
-		float l = px * (a.y - c.y) - py * (a.x - c.x);
-		float m = px * (b.y - c.y) - py * (b.x - c.x);
-		return l * m >= 0;
+		src += 2;
+		srclen -= 2;
 	}
 	}
 
 
-	// checks is p is contained in the triangle abc
-	inline bool point_in_triangle(const Vertex &p, const Vertex &a, const Vertex &b, const Vertex &c)
+	dstlen = (srclen + 1) / 2;
+
+	if (dstlen == 0)
+		return nullptr;
+
+	love::uint8 *dst = nullptr;
+	try
+	{
+		dst = new love::uint8[dstlen];
+	}
+	catch (std::exception &)
 	{
 	{
-		return on_same_side(p,a, b,c) && on_same_side(p,b, a,c) && on_same_side(p,c, a,b);
+		throw love::Exception("Out of memory.");
 	}
 	}
 
 
-	// checks if any vertex in `vertices' is in the triangle abc.
-	bool any_point_in_triangle(const list<const Vertex *> &vertices, const Vertex &a, const Vertex &b, const Vertex &c)
+	for (size_t i = 0; i < dstlen; i++)
 	{
 	{
-		list<const Vertex *>::const_iterator it, end = vertices.end();
-		for (it = vertices.begin(); it != end; ++it)
-		{
-			const Vertex *p = *it;
-			if ((p != &a) && (p != &b) && (p != &c) && point_in_triangle(*p, a,b,c)) // oh god...
-				return true;
-		}
+		dst[i] = nibble(src[i * 2]) << 4;
 
 
-		return false;
+		if (i * 2 + 1 < srclen)
+			dst[i] |= nibble(src[i * 2 + 1]);
 	}
 	}
 
 
-	inline bool is_ear(const Vertex &a, const Vertex &b, const Vertex &c, const list<const Vertex *> &vertices)
+	return dst;
+}
+
+// check if an angle is oriented counter clockwise
+inline bool is_oriented_ccw(const Vertex &a, const Vertex &b, const Vertex &c)
+{
+	// return det(b-a, c-a) >= 0
+	return ((b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x)) >= 0;
+}
+
+// check if a and b are on the same side of the line c->d
+bool on_same_side(const Vertex &a, const Vertex &b, const Vertex &c, const Vertex &d)
+{
+	float px = d.x - c.x, py = d.y - c.y;
+	// return det(p, a-c) * det(p, b-c) >= 0
+	float l = px * (a.y - c.y) - py * (a.x - c.x);
+	float m = px * (b.y - c.y) - py * (b.x - c.x);
+	return l * m >= 0;
+}
+
+// checks is p is contained in the triangle abc
+inline bool point_in_triangle(const Vertex &p, const Vertex &a, const Vertex &b, const Vertex &c)
+{
+	return on_same_side(p,a, b,c) && on_same_side(p,b, a,c) && on_same_side(p,c, a,b);
+}
+
+// checks if any vertex in `vertices' is in the triangle abc.
+bool any_point_in_triangle(const list<const Vertex *> &vertices, const Vertex &a, const Vertex &b, const Vertex &c)
+{
+	list<const Vertex *>::const_iterator it, end = vertices.end();
+	for (it = vertices.begin(); it != end; ++it)
 	{
 	{
-		return is_oriented_ccw(a,b,c) && !any_point_in_triangle(vertices, a,b,c);
+		const Vertex *p = *it;
+		if ((p != &a) && (p != &b) && (p != &c) && point_in_triangle(*p, a,b,c)) // oh god...
+			return true;
 	}
 	}
+
+	return false;
+}
+
+inline bool is_ear(const Vertex &a, const Vertex &b, const Vertex &c, const list<const Vertex *> &vertices)
+{
+	return is_oriented_ccw(a,b,c) && !any_point_in_triangle(vertices, a,b,c);
 }
 }
 
 
+} // anonymous namespace
+
 namespace love
 namespace love
 {
 {
 namespace math
 namespace math
@@ -270,5 +352,47 @@ char *Math::decompress(Compressor::Format format, const char *cbytes, size_t com
 	return compressor->decompress(format, cbytes, compressedsize, rawsize);
 	return compressor->decompress(format, cbytes, compressedsize, rawsize);
 }
 }
 
 
+char *Math::encode(EncodeFormat format, const char *src, size_t srclen, size_t &dstlen, size_t linelen)
+{
+	switch (format)
+	{
+	case ENCODE_BASE64:
+	default:
+		return b64_encode(src, srclen, linelen, dstlen);
+	case ENCODE_HEX:
+		return bytesToHex((const uint8 *) src, srclen, dstlen);
+	}
+}
+
+char *Math::decode(EncodeFormat format, const char *src, size_t srclen, size_t &dstlen)
+{
+	switch (format)
+	{
+	case ENCODE_BASE64:
+	default:
+		return b64_decode(src, srclen, dstlen);
+	case ENCODE_HEX:
+		return (char *) hexToBytes(src, srclen, dstlen);
+	}
+}
+
+bool Math::getConstant(const char *in, EncodeFormat &out)
+{
+	return encoders.find(in, out);
+}
+
+bool Math::getConstant(EncodeFormat in, const char *&out)
+{
+	return encoders.find(in, out);
+}
+
+StringMap<Math::EncodeFormat, Math::ENCODE_MAX_ENUM>::Entry Math::encoderEntries[] =
+{
+	{ "base64", ENCODE_BASE64 },
+	{ "hex",    ENCODE_HEX },
+};
+
+StringMap<Math::EncodeFormat, Math::ENCODE_MAX_ENUM> Math::encoders(Math::encoderEntries, sizeof(Math::encoderEntries));
+
 } // math
 } // math
 } // love
 } // love

+ 17 - 0
src/modules/math/MathModule.h

@@ -30,6 +30,7 @@
 #include "common/math.h"
 #include "common/math.h"
 #include "common/Vector.h"
 #include "common/Vector.h"
 #include "common/int.h"
 #include "common/int.h"
+#include "common/StringMap.h"
 
 
 // Noise
 // Noise
 #include "libraries/noise1234/noise1234.h"
 #include "libraries/noise1234/noise1234.h"
@@ -53,6 +54,13 @@ private:
 
 
 public:
 public:
 
 
+	enum EncodeFormat
+	{
+		ENCODE_BASE64,
+		ENCODE_HEX,
+		ENCODE_MAX_ENUM
+	};
+
 	virtual ~Math();
 	virtual ~Math();
 
 
 	RandomGenerator *getRandomGenerator()
 	RandomGenerator *getRandomGenerator()
@@ -152,12 +160,21 @@ public:
 	 **/
 	 **/
 	char *decompress(Compressor::Format format, const char *cbytes, size_t compressedsize, size_t &rawsize);
 	char *decompress(Compressor::Format format, const char *cbytes, size_t compressedsize, size_t &rawsize);
 
 
+	char *encode(EncodeFormat format, const char *src, size_t srclen, size_t &dstlen, size_t linelen = 0);
+	char *decode(EncodeFormat format, const char *src, size_t srclen, size_t &dstlen);
+
+	static bool getConstant(const char *in, EncodeFormat &out);
+	static bool getConstant(EncodeFormat in, const char *&out);
+
 	static Math instance;
 	static Math instance;
 
 
 private:
 private:
 
 
 	Math();
 	Math();
 
 
+	static StringMap<EncodeFormat, ENCODE_MAX_ENUM>::Entry encoderEntries[];
+	static StringMap<EncodeFormat, ENCODE_MAX_ENUM> encoders;
+
 }; // Math
 }; // Math
 
 
 inline float Math::noise(float x) const
 inline float Math::noise(float x) const

+ 69 - 0
src/modules/math/wrap_Math.cpp

@@ -24,6 +24,7 @@
 #include "wrap_CompressedData.h"
 #include "wrap_CompressedData.h"
 #include "MathModule.h"
 #include "MathModule.h"
 #include "BezierCurve.h"
 #include "BezierCurve.h"
+#include "common/b64.h"
 
 
 #include <cmath>
 #include <cmath>
 #include <iostream>
 #include <iostream>
@@ -385,6 +386,72 @@ int w_decompress(lua_State *L)
 	return 1;
 	return 1;
 }
 }
 
 
+int w_encode(lua_State *L)
+{
+	const char *formatstr = luaL_checkstring(L, 1);
+	Math::EncodeFormat format;
+	if (!Math::getConstant(formatstr, format))
+		return luaL_error(L, "Invalid encode format: %s", formatstr);
+
+	size_t srclen = 0;
+	const char *src = nullptr;
+
+	if (luax_istype(L, 2, DATA_ID))
+	{
+		Data *data = luax_totype<Data>(L, 2, DATA_ID);
+		src = (const char *) data->getData();
+		srclen = data->getSize();
+	}
+	else
+		src = luaL_checklstring(L, 2, &srclen);
+
+	size_t linelen = (size_t) luaL_optinteger(L, 3, 0);
+
+	size_t dstlen = 0;
+	char *dst = nullptr;
+	luax_catchexcept(L, [&](){ dst = Math::instance.encode(format, src, srclen, dstlen, linelen); });
+
+	if (dst != nullptr)
+		lua_pushlstring(L, dst, dstlen);
+	else
+		lua_pushstring(L, "");
+
+	delete[] dst;
+	return 1;
+}
+
+int w_decode(lua_State *L)
+{
+	const char *formatstr = luaL_checkstring(L, 1);
+	Math::EncodeFormat format;
+	if (!Math::getConstant(formatstr, format))
+		return luaL_error(L, "Invalid decode format: %s", formatstr);
+
+	size_t srclen = 0;
+	const char *src = nullptr;
+
+	if (luax_istype(L, 2, DATA_ID))
+	{
+		Data *data = luax_totype<Data>(L, 2, DATA_ID);
+		src = (const char *) data->getData();
+		srclen = data->getSize();
+	}
+	else
+		src = luaL_checklstring(L, 2, &srclen);
+
+	size_t dstlen = 0;
+	char *dst = nullptr;
+	luax_catchexcept(L, [&](){ dst = Math::instance.decode(format, src, srclen, dstlen); });
+
+	if (dst != nullptr)
+		lua_pushlstring(L, dst, dstlen);
+	else
+		lua_pushstring(L, "");
+
+	delete[] dst;
+	return 1;
+}
+
 // C functions in a struct, necessary for the FFI versions of math functions.
 // C functions in a struct, necessary for the FFI versions of math functions.
 struct FFI_Math
 struct FFI_Math
 {
 {
@@ -441,6 +508,8 @@ static const luaL_Reg functions[] =
 	{ "noise", w_noise },
 	{ "noise", w_noise },
 	{ "compress", w_compress },
 	{ "compress", w_compress },
 	{ "decompress", w_decompress },
 	{ "decompress", w_decompress },
+	{ "encode", w_encode },
+	{ "decode", w_decode },
 	{ 0, 0 }
 	{ 0, 0 }
 };
 };
 
 

+ 2 - 1
src/scripts/nogame.lua

@@ -936,7 +936,8 @@ function love.nogame()
 		love.graphics.setBackgroundColor(136/255, 193/255, 206/255)
 		love.graphics.setBackgroundColor(136/255, 193/255, 206/255)
 
 
 		local function load_image(file, name)
 		local function load_image(file, name)
-			return love.graphics.newImage(love.filesystem.newFileData(file, name:gsub("_", "."), "base64"))
+			local decoded = love.math.decode("base64", file)
+			return love.graphics.newImage(love.filesystem.newFileData(decoded, name:gsub("_", ".")))
 		end
 		end
 
 
 		g_images = {}
 		g_images = {}

+ 6 - 3
src/scripts/nogame.lua.h

@@ -3572,12 +3572,15 @@ const unsigned char nogame_lua[] =
 	0x09, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6c, 
 	0x09, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6c, 
 	0x6f, 0x61, 0x64, 0x5f, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x28, 0x66, 0x69, 0x6c, 0x65, 0x2c, 0x20, 0x6e, 0x61, 
 	0x6f, 0x61, 0x64, 0x5f, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x28, 0x66, 0x69, 0x6c, 0x65, 0x2c, 0x20, 0x6e, 0x61, 
 	0x6d, 0x65, 0x29, 0x0a,
 	0x6d, 0x65, 0x29, 0x0a,
+	0x09, 0x09, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x20, 0x3d, 
+	0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x6d, 0x61, 0x74, 0x68, 0x2e, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x28, 
+	0x22, 0x62, 0x61, 0x73, 0x65, 0x36, 0x34, 0x22, 0x2c, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x29, 0x0a,
 	0x09, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 
 	0x09, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 
 	0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x6e, 0x65, 0x77, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x28, 0x6c, 0x6f, 0x76, 
 	0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x6e, 0x65, 0x77, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x28, 0x6c, 0x6f, 0x76, 
 	0x65, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2e, 0x6e, 0x65, 0x77, 0x46, 0x69, 
 	0x65, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2e, 0x6e, 0x65, 0x77, 0x46, 0x69, 
-	0x6c, 0x65, 0x44, 0x61, 0x74, 0x61, 0x28, 0x66, 0x69, 0x6c, 0x65, 0x2c, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x3a, 
-	0x67, 0x73, 0x75, 0x62, 0x28, 0x22, 0x5f, 0x22, 0x2c, 0x20, 0x22, 0x2e, 0x22, 0x29, 0x2c, 0x20, 0x22, 0x62, 
-	0x61, 0x73, 0x65, 0x36, 0x34, 0x22, 0x29, 0x29, 0x0a,
+	0x6c, 0x65, 0x44, 0x61, 0x74, 0x61, 0x28, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x2c, 0x20, 0x6e, 0x61, 
+	0x6d, 0x65, 0x3a, 0x67, 0x73, 0x75, 0x62, 0x28, 0x22, 0x5f, 0x22, 0x2c, 0x20, 0x22, 0x2e, 0x22, 0x29, 0x29, 
+	0x29, 0x0a,
 	0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a,
 	0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a,
 	0x09, 0x09, 0x67, 0x5f, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x73, 0x20, 0x3d, 0x20, 0x7b, 0x7d, 0x0a,
 	0x09, 0x09, 0x67, 0x5f, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x73, 0x20, 0x3d, 0x20, 0x7b, 0x7d, 0x0a,
 	0x09, 0x09, 0x67, 0x5f, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x74, 0x6f, 0x61, 0x73, 0x74, 0x20, 0x3d, 
 	0x09, 0x09, 0x67, 0x5f, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x74, 0x6f, 0x61, 0x73, 0x74, 0x20, 0x3d,