mirror of
https://github.com/love2d/love.git
synced 2026-08-16 00:02:12 +02:00
Merge branch 'main' into 12.0-development
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
LodePNG version 20200306
|
||||
LodePNG version 20210627
|
||||
|
||||
Copyright (c) 2005-2020 Lode Vandevenne
|
||||
Copyright (c) 2005-2021 Lode Vandevenne
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
@@ -44,7 +44,7 @@ Rename this file to lodepng.cpp to use it for C++, or to lodepng.c to use it for
|
||||
#pragma warning( disable : 4996 ) /*VS does not like fopen, but fopen_s is not standard C so unusable here*/
|
||||
#endif /*_MSC_VER */
|
||||
|
||||
const char* LODEPNG_VERSION_STRING = "20200306";
|
||||
const char* LODEPNG_VERSION_STRING = "20210627";
|
||||
|
||||
/*
|
||||
This source file is built up in the following large parts. The code sections
|
||||
@@ -299,6 +299,7 @@ static void string_cleanup(char** out) {
|
||||
*out = NULL;
|
||||
}
|
||||
|
||||
/*also appends null termination character*/
|
||||
static char* alloc_string_sized(const char* in, size_t insize) {
|
||||
char* out = (char*)lodepng_malloc(insize + 1);
|
||||
if(out) {
|
||||
@@ -1260,7 +1261,7 @@ static unsigned getTreeInflateDynamic(HuffmanTree* tree_ll, HuffmanTree* tree_d,
|
||||
|
||||
/*inflate a block with dynamic of fixed Huffman tree. btype must be 1 or 2.*/
|
||||
static unsigned inflateHuffmanBlock(ucvector* out, LodePNGBitReader* reader,
|
||||
unsigned btype) {
|
||||
unsigned btype, size_t max_output_size) {
|
||||
unsigned error = 0;
|
||||
HuffmanTree tree_ll; /*the huffman tree for literal and length codes*/
|
||||
HuffmanTree tree_d; /*the huffman tree for distance codes*/
|
||||
@@ -1341,6 +1342,9 @@ static unsigned inflateHuffmanBlock(ucvector* out, LodePNGBitReader* reader,
|
||||
/* TODO: revise error codes 10,11,50: the above comment is no longer valid */
|
||||
ERROR_BREAK(51); /*error, bit pointer jumps past memory*/
|
||||
}
|
||||
if(max_output_size && out->size > max_output_size) {
|
||||
ERROR_BREAK(109); /*error, larger than max size*/
|
||||
}
|
||||
}
|
||||
|
||||
HuffmanTree_cleanup(&tree_ll);
|
||||
@@ -1398,9 +1402,9 @@ static unsigned lodepng_inflatev(ucvector* out,
|
||||
|
||||
if(BTYPE == 3) return 20; /*error: invalid BTYPE*/
|
||||
else if(BTYPE == 0) error = inflateNoCompression(out, &reader, settings); /*no compression*/
|
||||
else error = inflateHuffmanBlock(out, &reader, BTYPE); /*compression, BTYPE 01 or 10*/
|
||||
|
||||
if(error) return error;
|
||||
else error = inflateHuffmanBlock(out, &reader, BTYPE, settings->max_output_size); /*compression, BTYPE 01 or 10*/
|
||||
if(!error && settings->max_output_size && out->size > settings->max_output_size) error = 109;
|
||||
if(error) break;
|
||||
}
|
||||
|
||||
return error;
|
||||
@@ -1421,6 +1425,12 @@ static unsigned inflatev(ucvector* out, const unsigned char* in, size_t insize,
|
||||
if(settings->custom_inflate) {
|
||||
unsigned error = settings->custom_inflate(&out->data, &out->size, in, insize, settings);
|
||||
out->allocsize = out->size;
|
||||
if(error) {
|
||||
/*the custom inflate is allowed to have its own error codes, however, we translate it to code 110*/
|
||||
error = 110;
|
||||
/*if there's a max output size, and the custom zlib returned error, then indicate that error instead*/
|
||||
if(settings->max_output_size && out->size > settings->max_output_size) error = 109;
|
||||
}
|
||||
return error;
|
||||
} else {
|
||||
return lodepng_inflatev(out, in, insize, settings);
|
||||
@@ -2116,7 +2126,9 @@ static unsigned deflate(unsigned char** out, size_t* outsize,
|
||||
const unsigned char* in, size_t insize,
|
||||
const LodePNGCompressSettings* settings) {
|
||||
if(settings->custom_deflate) {
|
||||
return settings->custom_deflate(out, outsize, in, insize, settings);
|
||||
unsigned error = settings->custom_deflate(out, outsize, in, insize, settings);
|
||||
/*the custom deflate is allowed to have its own error codes, however, we translate it to code 111*/
|
||||
return error ? 111 : 0;
|
||||
} else {
|
||||
return lodepng_deflate(out, outsize, in, insize, settings);
|
||||
}
|
||||
@@ -2213,10 +2225,16 @@ unsigned lodepng_zlib_decompress(unsigned char** out, size_t* outsize, const uns
|
||||
/*expected_size is expected output size, to avoid intermediate allocations. Set to 0 if not known. */
|
||||
static unsigned zlib_decompress(unsigned char** out, size_t* outsize, size_t expected_size,
|
||||
const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings) {
|
||||
unsigned error;
|
||||
if(settings->custom_zlib) {
|
||||
return settings->custom_zlib(out, outsize, in, insize, settings);
|
||||
error = settings->custom_zlib(out, outsize, in, insize, settings);
|
||||
if(error) {
|
||||
/*the custom zlib is allowed to have its own error codes, however, we translate it to code 110*/
|
||||
error = 110;
|
||||
/*if there's a max output size, and the custom zlib returned error, then indicate that error instead*/
|
||||
if(settings->max_output_size && *outsize > settings->max_output_size) error = 109;
|
||||
}
|
||||
} else {
|
||||
unsigned error;
|
||||
ucvector v = ucvector_init(*out, *outsize);
|
||||
if(expected_size) {
|
||||
/*reserve the memory to avoid intermediate reallocations*/
|
||||
@@ -2226,8 +2244,8 @@ static unsigned zlib_decompress(unsigned char** out, size_t* outsize, size_t exp
|
||||
error = lodepng_zlib_decompressv(&v, in, insize, settings);
|
||||
*out = v.data;
|
||||
*outsize = v.size;
|
||||
return error;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
#endif /*LODEPNG_COMPILE_DECODER*/
|
||||
@@ -2275,7 +2293,9 @@ unsigned lodepng_zlib_compress(unsigned char** out, size_t* outsize, const unsig
|
||||
static unsigned zlib_compress(unsigned char** out, size_t* outsize, const unsigned char* in,
|
||||
size_t insize, const LodePNGCompressSettings* settings) {
|
||||
if(settings->custom_zlib) {
|
||||
return settings->custom_zlib(out, outsize, in, insize, settings);
|
||||
unsigned error = settings->custom_zlib(out, outsize, in, insize, settings);
|
||||
/*the custom zlib is allowed to have its own error codes, however, we translate it to code 111*/
|
||||
return error ? 111 : 0;
|
||||
} else {
|
||||
return lodepng_zlib_compress(out, outsize, in, insize, settings);
|
||||
}
|
||||
@@ -2334,13 +2354,14 @@ const LodePNGCompressSettings lodepng_default_compress_settings = {2, 1, DEFAULT
|
||||
void lodepng_decompress_settings_init(LodePNGDecompressSettings* settings) {
|
||||
settings->ignore_adler32 = 0;
|
||||
settings->ignore_nlen = 0;
|
||||
settings->max_output_size = 0;
|
||||
|
||||
settings->custom_zlib = 0;
|
||||
settings->custom_inflate = 0;
|
||||
settings->custom_context = 0;
|
||||
}
|
||||
|
||||
const LodePNGDecompressSettings lodepng_default_decompress_settings = {0, 0, 0, 0, 0};
|
||||
const LodePNGDecompressSettings lodepng_default_decompress_settings = {0, 0, 0, 0, 0, 0};
|
||||
|
||||
#endif /*LODEPNG_COMPILE_DECODER*/
|
||||
|
||||
@@ -2872,8 +2893,8 @@ static void LodePNGText_cleanup(LodePNGInfo* info) {
|
||||
|
||||
static unsigned LodePNGText_copy(LodePNGInfo* dest, const LodePNGInfo* source) {
|
||||
size_t i = 0;
|
||||
dest->text_keys = 0;
|
||||
dest->text_strings = 0;
|
||||
dest->text_keys = NULL;
|
||||
dest->text_strings = NULL;
|
||||
dest->text_num = 0;
|
||||
for(i = 0; i != source->text_num; ++i) {
|
||||
CERROR_TRY_RETURN(lodepng_add_text(dest, source->text_keys[i], source->text_strings[i]));
|
||||
@@ -2932,10 +2953,10 @@ static void LodePNGIText_cleanup(LodePNGInfo* info) {
|
||||
|
||||
static unsigned LodePNGIText_copy(LodePNGInfo* dest, const LodePNGInfo* source) {
|
||||
size_t i = 0;
|
||||
dest->itext_keys = 0;
|
||||
dest->itext_langtags = 0;
|
||||
dest->itext_transkeys = 0;
|
||||
dest->itext_strings = 0;
|
||||
dest->itext_keys = NULL;
|
||||
dest->itext_langtags = NULL;
|
||||
dest->itext_transkeys = NULL;
|
||||
dest->itext_strings = NULL;
|
||||
dest->itext_num = 0;
|
||||
for(i = 0; i != source->itext_num; ++i) {
|
||||
CERROR_TRY_RETURN(lodepng_add_itext(dest, source->itext_keys[i], source->itext_langtags[i],
|
||||
@@ -4093,10 +4114,12 @@ static unsigned unfilterScanline(unsigned char* recon, const unsigned char* scan
|
||||
case 0:
|
||||
for(i = 0; i != length; ++i) recon[i] = scanline[i];
|
||||
break;
|
||||
case 1:
|
||||
case 1: {
|
||||
size_t j = 0;
|
||||
for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i];
|
||||
for(i = bytewidth; i < length; ++i) recon[i] = scanline[i] + recon[i - bytewidth];
|
||||
for(i = bytewidth; i != length; ++i, ++j) recon[i] = scanline[i] + recon[j];
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
if(precon) {
|
||||
for(i = 0; i != length; ++i) recon[i] = scanline[i] + precon[i];
|
||||
@@ -4106,24 +4129,56 @@ static unsigned unfilterScanline(unsigned char* recon, const unsigned char* scan
|
||||
break;
|
||||
case 3:
|
||||
if(precon) {
|
||||
size_t j = 0;
|
||||
for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i] + (precon[i] >> 1u);
|
||||
for(i = bytewidth; i < length; ++i) recon[i] = scanline[i] + ((recon[i - bytewidth] + precon[i]) >> 1u);
|
||||
/* Unroll independent paths of this predictor. A 6x and 8x version is also possible but that adds
|
||||
too much code. Whether this speeds up anything depends on compiler and settings. */
|
||||
if(bytewidth >= 4) {
|
||||
for(; i + 3 < length; i += 4, j += 4) {
|
||||
unsigned char s0 = scanline[i + 0], r0 = recon[j + 0], p0 = precon[i + 0];
|
||||
unsigned char s1 = scanline[i + 1], r1 = recon[j + 1], p1 = precon[i + 1];
|
||||
unsigned char s2 = scanline[i + 2], r2 = recon[j + 2], p2 = precon[i + 2];
|
||||
unsigned char s3 = scanline[i + 3], r3 = recon[j + 3], p3 = precon[i + 3];
|
||||
recon[i + 0] = s0 + ((r0 + p0) >> 1u);
|
||||
recon[i + 1] = s1 + ((r1 + p1) >> 1u);
|
||||
recon[i + 2] = s2 + ((r2 + p2) >> 1u);
|
||||
recon[i + 3] = s3 + ((r3 + p3) >> 1u);
|
||||
}
|
||||
} else if(bytewidth >= 3) {
|
||||
for(; i + 2 < length; i += 3, j += 3) {
|
||||
unsigned char s0 = scanline[i + 0], r0 = recon[j + 0], p0 = precon[i + 0];
|
||||
unsigned char s1 = scanline[i + 1], r1 = recon[j + 1], p1 = precon[i + 1];
|
||||
unsigned char s2 = scanline[i + 2], r2 = recon[j + 2], p2 = precon[i + 2];
|
||||
recon[i + 0] = s0 + ((r0 + p0) >> 1u);
|
||||
recon[i + 1] = s1 + ((r1 + p1) >> 1u);
|
||||
recon[i + 2] = s2 + ((r2 + p2) >> 1u);
|
||||
}
|
||||
} else if(bytewidth >= 2) {
|
||||
for(; i + 1 < length; i += 2, j += 2) {
|
||||
unsigned char s0 = scanline[i + 0], r0 = recon[j + 0], p0 = precon[i + 0];
|
||||
unsigned char s1 = scanline[i + 1], r1 = recon[j + 1], p1 = precon[i + 1];
|
||||
recon[i + 0] = s0 + ((r0 + p0) >> 1u);
|
||||
recon[i + 1] = s1 + ((r1 + p1) >> 1u);
|
||||
}
|
||||
}
|
||||
for(; i != length; ++i, ++j) recon[i] = scanline[i] + ((recon[j] + precon[i]) >> 1u);
|
||||
} else {
|
||||
size_t j = 0;
|
||||
for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i];
|
||||
for(i = bytewidth; i < length; ++i) recon[i] = scanline[i] + (recon[i - bytewidth] >> 1u);
|
||||
for(i = bytewidth; i != length; ++i, ++j) recon[i] = scanline[i] + (recon[j] >> 1u);
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
if(precon) {
|
||||
size_t j = 0;
|
||||
for(i = 0; i != bytewidth; ++i) {
|
||||
recon[i] = (scanline[i] + precon[i]); /*paethPredictor(0, precon[i], 0) is always precon[i]*/
|
||||
}
|
||||
|
||||
/* Unroll independent paths of the paeth predictor. A 6x and 8x version would also be possible but that
|
||||
adds too much code. Whether this actually speeds anything up at all depends on compiler and settings. */
|
||||
/* Unroll independent paths of the paeth predictor. A 6x and 8x version is also possible but that
|
||||
adds too much code. Whether this speeds up anything depends on compiler and settings. */
|
||||
if(bytewidth >= 4) {
|
||||
for(; i + 3 < length; i += 4) {
|
||||
size_t j = i - bytewidth;
|
||||
for(; i + 3 < length; i += 4, j += 4) {
|
||||
unsigned char s0 = scanline[i + 0], s1 = scanline[i + 1], s2 = scanline[i + 2], s3 = scanline[i + 3];
|
||||
unsigned char r0 = recon[j + 0], r1 = recon[j + 1], r2 = recon[j + 2], r3 = recon[j + 3];
|
||||
unsigned char p0 = precon[i + 0], p1 = precon[i + 1], p2 = precon[i + 2], p3 = precon[i + 3];
|
||||
@@ -4134,8 +4189,7 @@ static unsigned unfilterScanline(unsigned char* recon, const unsigned char* scan
|
||||
recon[i + 3] = s3 + paethPredictor(r3, p3, q3);
|
||||
}
|
||||
} else if(bytewidth >= 3) {
|
||||
for(; i + 2 < length; i += 3) {
|
||||
size_t j = i - bytewidth;
|
||||
for(; i + 2 < length; i += 3, j += 3) {
|
||||
unsigned char s0 = scanline[i + 0], s1 = scanline[i + 1], s2 = scanline[i + 2];
|
||||
unsigned char r0 = recon[j + 0], r1 = recon[j + 1], r2 = recon[j + 2];
|
||||
unsigned char p0 = precon[i + 0], p1 = precon[i + 1], p2 = precon[i + 2];
|
||||
@@ -4145,8 +4199,7 @@ static unsigned unfilterScanline(unsigned char* recon, const unsigned char* scan
|
||||
recon[i + 2] = s2 + paethPredictor(r2, p2, q2);
|
||||
}
|
||||
} else if(bytewidth >= 2) {
|
||||
for(; i + 1 < length; i += 2) {
|
||||
size_t j = i - bytewidth;
|
||||
for(; i + 1 < length; i += 2, j += 2) {
|
||||
unsigned char s0 = scanline[i + 0], s1 = scanline[i + 1];
|
||||
unsigned char r0 = recon[j + 0], r1 = recon[j + 1];
|
||||
unsigned char p0 = precon[i + 0], p1 = precon[i + 1];
|
||||
@@ -4156,16 +4209,17 @@ static unsigned unfilterScanline(unsigned char* recon, const unsigned char* scan
|
||||
}
|
||||
}
|
||||
|
||||
for(; i != length; ++i) {
|
||||
recon[i] = (scanline[i] + paethPredictor(recon[i - bytewidth], precon[i], precon[i - bytewidth]));
|
||||
for(; i != length; ++i, ++j) {
|
||||
recon[i] = (scanline[i] + paethPredictor(recon[i - bytewidth], precon[i], precon[j]));
|
||||
}
|
||||
} else {
|
||||
size_t j = 0;
|
||||
for(i = 0; i != bytewidth; ++i) {
|
||||
recon[i] = scanline[i];
|
||||
}
|
||||
for(i = bytewidth; i < length; ++i) {
|
||||
for(i = bytewidth; i != length; ++i, ++j) {
|
||||
/*paethPredictor(recon[i - bytewidth], 0, 0) is always recon[i - bytewidth]*/
|
||||
recon[i] = (scanline[i] + recon[i - bytewidth]);
|
||||
recon[i] = (scanline[i] + recon[j]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -4447,10 +4501,13 @@ static unsigned readChunk_tEXt(LodePNGInfo* info, const unsigned char* data, siz
|
||||
}
|
||||
|
||||
/*compressed text chunk (zTXt)*/
|
||||
static unsigned readChunk_zTXt(LodePNGInfo* info, const LodePNGDecompressSettings* zlibsettings,
|
||||
static unsigned readChunk_zTXt(LodePNGInfo* info, const LodePNGDecoderSettings* decoder,
|
||||
const unsigned char* data, size_t chunkLength) {
|
||||
unsigned error = 0;
|
||||
|
||||
/*copy the object to change parameters in it*/
|
||||
LodePNGDecompressSettings zlibsettings = decoder->zlibsettings;
|
||||
|
||||
unsigned length, string2_begin;
|
||||
char *key = 0;
|
||||
unsigned char* str = 0;
|
||||
@@ -4473,12 +4530,14 @@ static unsigned readChunk_zTXt(LodePNGInfo* info, const LodePNGDecompressSetting
|
||||
if(string2_begin > chunkLength) CERROR_BREAK(error, 75); /*no null termination, corrupt?*/
|
||||
|
||||
length = (unsigned)chunkLength - string2_begin;
|
||||
zlibsettings.max_output_size = decoder->max_text_size;
|
||||
/*will fail if zlib error, e.g. if length is too small*/
|
||||
error = zlib_decompress(&str, &size, 0, &data[string2_begin],
|
||||
length, zlibsettings);
|
||||
length, &zlibsettings);
|
||||
/*error: compressed text larger than decoder->max_text_size*/
|
||||
if(error && size > zlibsettings.max_output_size) error = 112;
|
||||
if(error) break;
|
||||
error = lodepng_add_text_sized(info, key, (char*)str, size);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -4489,11 +4548,14 @@ static unsigned readChunk_zTXt(LodePNGInfo* info, const LodePNGDecompressSetting
|
||||
}
|
||||
|
||||
/*international text chunk (iTXt)*/
|
||||
static unsigned readChunk_iTXt(LodePNGInfo* info, const LodePNGDecompressSettings* zlibsettings,
|
||||
static unsigned readChunk_iTXt(LodePNGInfo* info, const LodePNGDecoderSettings* decoder,
|
||||
const unsigned char* data, size_t chunkLength) {
|
||||
unsigned error = 0;
|
||||
unsigned i;
|
||||
|
||||
/*copy the object to change parameters in it*/
|
||||
LodePNGDecompressSettings zlibsettings = decoder->zlibsettings;
|
||||
|
||||
unsigned length, begin, compressed;
|
||||
char *key = 0, *langtag = 0, *transkey = 0;
|
||||
|
||||
@@ -4550,9 +4612,12 @@ static unsigned readChunk_iTXt(LodePNGInfo* info, const LodePNGDecompressSetting
|
||||
if(compressed) {
|
||||
unsigned char* str = 0;
|
||||
size_t size = 0;
|
||||
zlibsettings.max_output_size = decoder->max_text_size;
|
||||
/*will fail if zlib error, e.g. if length is too small*/
|
||||
error = zlib_decompress(&str, &size, 0, &data[begin],
|
||||
length, zlibsettings);
|
||||
length, &zlibsettings);
|
||||
/*error: compressed text larger than decoder->max_text_size*/
|
||||
if(error && size > zlibsettings.max_output_size) error = 112;
|
||||
if(!error) error = lodepng_add_itext_sized(info, key, langtag, transkey, (char*)str, size);
|
||||
lodepng_free(str);
|
||||
} else {
|
||||
@@ -4628,11 +4693,13 @@ static unsigned readChunk_sRGB(LodePNGInfo* info, const unsigned char* data, siz
|
||||
return 0; /* OK */
|
||||
}
|
||||
|
||||
static unsigned readChunk_iCCP(LodePNGInfo* info, const LodePNGDecompressSettings* zlibsettings,
|
||||
static unsigned readChunk_iCCP(LodePNGInfo* info, const LodePNGDecoderSettings* decoder,
|
||||
const unsigned char* data, size_t chunkLength) {
|
||||
unsigned error = 0;
|
||||
unsigned i;
|
||||
size_t size = 0;
|
||||
/*copy the object to change parameters in it*/
|
||||
LodePNGDecompressSettings zlibsettings = decoder->zlibsettings;
|
||||
|
||||
unsigned length, string2_begin;
|
||||
|
||||
@@ -4655,9 +4722,12 @@ static unsigned readChunk_iCCP(LodePNGInfo* info, const LodePNGDecompressSetting
|
||||
if(string2_begin > chunkLength) return 75; /*no null termination, corrupt?*/
|
||||
|
||||
length = (unsigned)chunkLength - string2_begin;
|
||||
zlibsettings.max_output_size = decoder->max_icc_size;
|
||||
error = zlib_decompress(&info->iccp_profile, &size, 0,
|
||||
&data[string2_begin],
|
||||
length, zlibsettings);
|
||||
length, &zlibsettings);
|
||||
/*error: ICC profile larger than decoder->max_icc_size*/
|
||||
if(error && size > zlibsettings.max_output_size) error = 113;
|
||||
info->iccp_profile_size = size;
|
||||
if(!error && !info->iccp_profile_size) error = 100; /*invalid ICC profile size*/
|
||||
return error;
|
||||
@@ -4688,9 +4758,9 @@ unsigned lodepng_inspect_chunk(LodePNGState* state, size_t pos,
|
||||
} else if(lodepng_chunk_type_equals(chunk, "tEXt")) {
|
||||
error = readChunk_tEXt(&state->info_png, data, chunkLength);
|
||||
} else if(lodepng_chunk_type_equals(chunk, "zTXt")) {
|
||||
error = readChunk_zTXt(&state->info_png, &state->decoder.zlibsettings, data, chunkLength);
|
||||
error = readChunk_zTXt(&state->info_png, &state->decoder, data, chunkLength);
|
||||
} else if(lodepng_chunk_type_equals(chunk, "iTXt")) {
|
||||
error = readChunk_iTXt(&state->info_png, &state->decoder.zlibsettings, data, chunkLength);
|
||||
error = readChunk_iTXt(&state->info_png, &state->decoder, data, chunkLength);
|
||||
} else if(lodepng_chunk_type_equals(chunk, "tIME")) {
|
||||
error = readChunk_tIME(&state->info_png, data, chunkLength);
|
||||
} else if(lodepng_chunk_type_equals(chunk, "pHYs")) {
|
||||
@@ -4702,7 +4772,7 @@ unsigned lodepng_inspect_chunk(LodePNGState* state, size_t pos,
|
||||
} else if(lodepng_chunk_type_equals(chunk, "sRGB")) {
|
||||
error = readChunk_sRGB(&state->info_png, data, chunkLength);
|
||||
} else if(lodepng_chunk_type_equals(chunk, "iCCP")) {
|
||||
error = readChunk_iCCP(&state->info_png, &state->decoder.zlibsettings, data, chunkLength);
|
||||
error = readChunk_iCCP(&state->info_png, &state->decoder, data, chunkLength);
|
||||
#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
|
||||
} else {
|
||||
/* unhandled chunk is ok (is not an error) */
|
||||
@@ -4820,13 +4890,13 @@ static void decodeGeneric(unsigned char** out, unsigned* w, unsigned* h,
|
||||
} else if(lodepng_chunk_type_equals(chunk, "zTXt")) {
|
||||
/*compressed text chunk (zTXt)*/
|
||||
if(state->decoder.read_text_chunks) {
|
||||
state->error = readChunk_zTXt(&state->info_png, &state->decoder.zlibsettings, data, chunkLength);
|
||||
state->error = readChunk_zTXt(&state->info_png, &state->decoder, data, chunkLength);
|
||||
if(state->error) break;
|
||||
}
|
||||
} else if(lodepng_chunk_type_equals(chunk, "iTXt")) {
|
||||
/*international text chunk (iTXt)*/
|
||||
if(state->decoder.read_text_chunks) {
|
||||
state->error = readChunk_iTXt(&state->info_png, &state->decoder.zlibsettings, data, chunkLength);
|
||||
state->error = readChunk_iTXt(&state->info_png, &state->decoder, data, chunkLength);
|
||||
if(state->error) break;
|
||||
}
|
||||
} else if(lodepng_chunk_type_equals(chunk, "tIME")) {
|
||||
@@ -4845,7 +4915,7 @@ static void decodeGeneric(unsigned char** out, unsigned* w, unsigned* h,
|
||||
state->error = readChunk_sRGB(&state->info_png, data, chunkLength);
|
||||
if(state->error) break;
|
||||
} else if(lodepng_chunk_type_equals(chunk, "iCCP")) {
|
||||
state->error = readChunk_iCCP(&state->info_png, &state->decoder.zlibsettings, data, chunkLength);
|
||||
state->error = readChunk_iCCP(&state->info_png, &state->decoder, data, chunkLength);
|
||||
if(state->error) break;
|
||||
#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
|
||||
} else /*it's not an implemented chunk type, so ignore it: skip over the data*/ {
|
||||
@@ -4871,7 +4941,7 @@ static void decodeGeneric(unsigned char** out, unsigned* w, unsigned* h,
|
||||
if(!IEND) chunk = lodepng_chunk_next_const(chunk, in + insize);
|
||||
}
|
||||
|
||||
if(state->info_png.color.colortype == LCT_PALETTE && !state->info_png.color.palette) {
|
||||
if(!state->error && state->info_png.color.colortype == LCT_PALETTE && !state->info_png.color.palette) {
|
||||
state->error = 106; /* error: PNG file must have PLTE chunk if color type is palette */
|
||||
}
|
||||
|
||||
@@ -4955,6 +5025,11 @@ unsigned lodepng_decode_memory(unsigned char** out, unsigned* w, unsigned* h, co
|
||||
lodepng_state_init(&state);
|
||||
state.info_raw.colortype = colortype;
|
||||
state.info_raw.bitdepth = bitdepth;
|
||||
#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
|
||||
/*disable reading things that this function doesn't output*/
|
||||
state.decoder.read_text_chunks = 0;
|
||||
state.decoder.remember_unknown_chunks = 0;
|
||||
#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
|
||||
error = lodepng_decode(out, w, h, &state, in, insize);
|
||||
lodepng_state_cleanup(&state);
|
||||
return error;
|
||||
@@ -4997,6 +5072,8 @@ void lodepng_decoder_settings_init(LodePNGDecoderSettings* settings) {
|
||||
#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
|
||||
settings->read_text_chunks = 1;
|
||||
settings->remember_unknown_chunks = 0;
|
||||
settings->max_text_size = 16777216;
|
||||
settings->max_icc_size = 16777216; /* 16MB is much more than enough for any reasonable ICC profile */
|
||||
#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
|
||||
settings->ignore_crc = 0;
|
||||
settings->ignore_critical = 0;
|
||||
@@ -6204,6 +6281,16 @@ const char* lodepng_error_text(unsigned code) {
|
||||
case 106: return "PNG file must have PLTE chunk if color type is palette";
|
||||
case 107: return "color convert from palette mode requested without setting the palette data in it";
|
||||
case 108: return "tried to add more than 256 values to a palette";
|
||||
/*this limit can be configured in LodePNGDecompressSettings*/
|
||||
case 109: return "tried to decompress zlib or deflate data larger than desired max_output_size";
|
||||
case 110: return "custom zlib or inflate decompression failed";
|
||||
case 111: return "custom zlib or deflate compression failed";
|
||||
/*max text size limit can be configured in LodePNGDecoderSettings. This error prevents
|
||||
unreasonable memory consumption when decoding due to impossibly large text sizes.*/
|
||||
case 112: return "compressed text unreasonably large";
|
||||
/*max ICC size limit can be configured in LodePNGDecoderSettings. This error prevents
|
||||
unreasonable memory consumption when decoding due to impossibly large ICC profile*/
|
||||
case 113: return "ICC profile unreasonably large";
|
||||
}
|
||||
return "unknown error code";
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
LodePNG version 20200306
|
||||
LodePNG version 20210627
|
||||
|
||||
Copyright (c) 2005-2020 Lode Vandevenne
|
||||
Copyright (c) 2005-2021 Lode Vandevenne
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
@@ -142,16 +142,24 @@ unsigned lodepng_decode24(unsigned char** out, unsigned* w, unsigned* h,
|
||||
/*
|
||||
Load PNG from disk, from file with given name.
|
||||
Same as the other decode functions, but instead takes a filename as input.
|
||||
*/
|
||||
|
||||
NOTE: Wide-character filenames are not supported, you can use an external method
|
||||
to handle such files and decode in-memory.*/
|
||||
unsigned lodepng_decode_file(unsigned char** out, unsigned* w, unsigned* h,
|
||||
const char* filename,
|
||||
LodePNGColorType colortype, unsigned bitdepth);
|
||||
|
||||
/*Same as lodepng_decode_file, but always decodes to 32-bit RGBA raw image.*/
|
||||
/*Same as lodepng_decode_file, but always decodes to 32-bit RGBA raw image.
|
||||
|
||||
NOTE: Wide-character filenames are not supported, you can use an external method
|
||||
to handle such files and decode in-memory.*/
|
||||
unsigned lodepng_decode32_file(unsigned char** out, unsigned* w, unsigned* h,
|
||||
const char* filename);
|
||||
|
||||
/*Same as lodepng_decode_file, but always decodes to 24-bit RGB raw image.*/
|
||||
/*Same as lodepng_decode_file, but always decodes to 24-bit RGB raw image.
|
||||
|
||||
NOTE: Wide-character filenames are not supported, you can use an external method
|
||||
to handle such files and decode in-memory.*/
|
||||
unsigned lodepng_decode24_file(unsigned char** out, unsigned* w, unsigned* h,
|
||||
const char* filename);
|
||||
#endif /*LODEPNG_COMPILE_DISK*/
|
||||
@@ -191,17 +199,26 @@ unsigned lodepng_encode24(unsigned char** out, size_t* outsize,
|
||||
/*
|
||||
Converts raw pixel data into a PNG file on disk.
|
||||
Same as the other encode functions, but instead takes a filename as output.
|
||||
|
||||
NOTE: This overwrites existing files without warning!
|
||||
*/
|
||||
|
||||
NOTE: Wide-character filenames are not supported, you can use an external method
|
||||
to handle such files and encode in-memory.*/
|
||||
unsigned lodepng_encode_file(const char* filename,
|
||||
const unsigned char* image, unsigned w, unsigned h,
|
||||
LodePNGColorType colortype, unsigned bitdepth);
|
||||
|
||||
/*Same as lodepng_encode_file, but always encodes from 32-bit RGBA raw image.*/
|
||||
/*Same as lodepng_encode_file, but always encodes from 32-bit RGBA raw image.
|
||||
|
||||
NOTE: Wide-character filenames are not supported, you can use an external method
|
||||
to handle such files and encode in-memory.*/
|
||||
unsigned lodepng_encode32_file(const char* filename,
|
||||
const unsigned char* image, unsigned w, unsigned h);
|
||||
|
||||
/*Same as lodepng_encode_file, but always encodes from 24-bit RGB raw image.*/
|
||||
/*Same as lodepng_encode_file, but always encodes from 24-bit RGB raw image.
|
||||
|
||||
NOTE: Wide-character filenames are not supported, you can use an external method
|
||||
to handle such files and encode in-memory.*/
|
||||
unsigned lodepng_encode24_file(const char* filename,
|
||||
const unsigned char* image, unsigned w, unsigned h);
|
||||
#endif /*LODEPNG_COMPILE_DISK*/
|
||||
@@ -223,6 +240,9 @@ unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h,
|
||||
/*
|
||||
Converts PNG file from disk to raw pixel data in memory.
|
||||
Same as the other decode functions, but instead takes a filename as input.
|
||||
|
||||
NOTE: Wide-character filenames are not supported, you can use an external method
|
||||
to handle such files and decode in-memory.
|
||||
*/
|
||||
unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h,
|
||||
const std::string& filename,
|
||||
@@ -243,7 +263,11 @@ unsigned encode(std::vector<unsigned char>& out,
|
||||
/*
|
||||
Converts 32-bit RGBA raw pixel data into a PNG file on disk.
|
||||
Same as the other encode functions, but instead takes a filename as output.
|
||||
|
||||
NOTE: This overwrites existing files without warning!
|
||||
|
||||
NOTE: Wide-character filenames are not supported, you can use an external method
|
||||
to handle such files and decode in-memory.
|
||||
*/
|
||||
unsigned encode(const std::string& filename,
|
||||
const unsigned char* in, unsigned w, unsigned h,
|
||||
@@ -270,12 +294,21 @@ struct LodePNGDecompressSettings {
|
||||
unsigned ignore_adler32; /*if 1, continue and don't give an error message if the Adler32 checksum is corrupted*/
|
||||
unsigned ignore_nlen; /*ignore complement of len checksum in uncompressed blocks*/
|
||||
|
||||
/*use custom zlib decoder instead of built in one (default: null)*/
|
||||
/*Maximum decompressed size, beyond this the decoder may (and is encouraged to) stop decoding,
|
||||
return an error, output a data size > max_output_size and all the data up to that point. This is
|
||||
not hard limit nor a guarantee, but can prevent excessive memory usage. This setting is
|
||||
ignored by the PNG decoder, but is used by the deflate/zlib decoder and can be used by custom ones.
|
||||
Set to 0 to impose no limit (the default).*/
|
||||
size_t max_output_size;
|
||||
|
||||
/*use custom zlib decoder instead of built in one (default: null).
|
||||
Should return 0 if success, any non-0 if error (numeric value not exposed).*/
|
||||
unsigned (*custom_zlib)(unsigned char**, size_t*,
|
||||
const unsigned char*, size_t,
|
||||
const LodePNGDecompressSettings*);
|
||||
/*use custom deflate decoder instead of built in one (default: null)
|
||||
if custom_zlib is not null, custom_inflate is ignored (the zlib format uses deflate)*/
|
||||
if custom_zlib is not null, custom_inflate is ignored (the zlib format uses deflate).
|
||||
Should return 0 if success, any non-0 if error (numeric value not exposed).*/
|
||||
unsigned (*custom_inflate)(unsigned char**, size_t*,
|
||||
const unsigned char*, size_t,
|
||||
const LodePNGDecompressSettings*);
|
||||
@@ -454,30 +487,36 @@ typedef struct LodePNGInfo {
|
||||
unsigned background_b; /*blue component of suggested background color*/
|
||||
|
||||
/*
|
||||
non-international text chunks (tEXt and zTXt)
|
||||
Non-international text chunks (tEXt and zTXt)
|
||||
|
||||
The char** arrays each contain num strings. The actual messages are in
|
||||
text_strings, while text_keys are keywords that give a short description what
|
||||
the actual text represents, e.g. Title, Author, Description, or anything else.
|
||||
|
||||
All the string fields below including keys, names and language tags are null terminated.
|
||||
All the string fields below including strings, keys, names and language tags are null terminated.
|
||||
The PNG specification uses null characters for the keys, names and tags, and forbids null
|
||||
characters to appear in the main text which is why we can use null termination everywhere here.
|
||||
|
||||
A keyword is minimum 1 character and maximum 79 characters long. It's
|
||||
discouraged to use a single line length longer than 79 characters for texts.
|
||||
A keyword is minimum 1 character and maximum 79 characters long (plus the
|
||||
additional null terminator). It's discouraged to use a single line length
|
||||
longer than 79 characters for texts.
|
||||
|
||||
Don't allocate these text buffers yourself. Use the init/cleanup functions
|
||||
correctly and use lodepng_add_text and lodepng_clear_text.
|
||||
|
||||
Standard text chunk keywords and strings are encoded using Latin-1.
|
||||
*/
|
||||
size_t text_num; /*the amount of texts in these char** buffers (there may be more texts in itext)*/
|
||||
char** text_keys; /*the keyword of a text chunk (e.g. "Comment")*/
|
||||
char** text_strings; /*the actual text*/
|
||||
|
||||
/*
|
||||
international text chunks (iTXt)
|
||||
International text chunks (iTXt)
|
||||
Similar to the non-international text chunks, but with additional strings
|
||||
"langtags" and "transkeys".
|
||||
"langtags" and "transkeys", and the following text encodings are used:
|
||||
keys: Latin-1, langtags: ASCII, transkeys and strings: UTF-8.
|
||||
keys must be 1-79 characters (plus the additional null terminator), the other
|
||||
strings are any length.
|
||||
*/
|
||||
size_t itext_num; /*the amount of international texts in this PNG*/
|
||||
char** itext_keys; /*the English keyword of the text chunk (e.g. "Comment")*/
|
||||
@@ -639,8 +678,19 @@ typedef struct LodePNGDecoderSettings {
|
||||
|
||||
#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
|
||||
unsigned read_text_chunks; /*if false but remember_unknown_chunks is true, they're stored in the unknown chunks*/
|
||||
|
||||
/*store all bytes from unknown chunks in the LodePNGInfo (off by default, useful for a png editor)*/
|
||||
unsigned remember_unknown_chunks;
|
||||
|
||||
/* maximum size for decompressed text chunks. If a text chunk's text is larger than this, an error is returned,
|
||||
unless reading text chunks is disabled or this limit is set higher or disabled. Set to 0 to allow any size.
|
||||
By default it is a value that prevents unreasonably large strings from hogging memory. */
|
||||
size_t max_text_size;
|
||||
|
||||
/* maximum size for compressed ICC chunks. If the ICC profile is larger than this, an error will be returned. Set to
|
||||
0 to allow any size. By default this is a value that prevents ICC profiles that would be much larger than any
|
||||
legitimate profile could be to hog memory. */
|
||||
size_t max_icc_size;
|
||||
#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
|
||||
} LodePNGDecoderSettings;
|
||||
|
||||
@@ -950,6 +1000,9 @@ out: output parameter, contains pointer to loaded buffer.
|
||||
outsize: output parameter, size of the allocated out buffer
|
||||
filename: the path to the file to load
|
||||
return value: error code (0 means ok)
|
||||
|
||||
NOTE: Wide-character filenames are not supported, you can use an external method
|
||||
to handle such files and decode in-memory.
|
||||
*/
|
||||
unsigned lodepng_load_file(unsigned char** out, size_t* outsize, const char* filename);
|
||||
|
||||
@@ -960,6 +1013,9 @@ buffer: the buffer to write
|
||||
buffersize: size of the buffer to write
|
||||
filename: the path to the file to save to
|
||||
return value: error code (0 means ok)
|
||||
|
||||
NOTE: Wide-character filenames are not supported, you can use an external method
|
||||
to handle such files and encode in-memory
|
||||
*/
|
||||
unsigned lodepng_save_file(const unsigned char* buffer, size_t buffersize, const char* filename);
|
||||
#endif /*LODEPNG_COMPILE_DISK*/
|
||||
@@ -1000,12 +1056,18 @@ unsigned encode(std::vector<unsigned char>& out,
|
||||
/*
|
||||
Load a file from disk into an std::vector.
|
||||
return value: error code (0 means ok)
|
||||
|
||||
NOTE: Wide-character filenames are not supported, you can use an external method
|
||||
to handle such files and decode in-memory
|
||||
*/
|
||||
unsigned load_file(std::vector<unsigned char>& buffer, const std::string& filename);
|
||||
|
||||
/*
|
||||
Save the binary data in an std::vector to a file on disk. The file is overwritten
|
||||
without warning.
|
||||
|
||||
NOTE: Wide-character filenames are not supported, you can use an external method
|
||||
to handle such files and encode in-memory
|
||||
*/
|
||||
unsigned save_file(const std::vector<unsigned char>& buffer, const std::string& filename);
|
||||
#endif /* LODEPNG_COMPILE_DISK */
|
||||
@@ -1505,6 +1567,11 @@ of the error in English as a string.
|
||||
|
||||
Check the implementation of lodepng_error_text to see the meaning of each code.
|
||||
|
||||
It is not recommended to use the numerical values to programmatically make
|
||||
different decisions based on error types as the numbers are not guaranteed to
|
||||
stay backwards compatible. They are for human consumption only. Programmatically
|
||||
only 0 or non-0 matter.
|
||||
|
||||
|
||||
8. chunks and PNG editing
|
||||
-------------------------
|
||||
@@ -1678,6 +1745,9 @@ try to fix it if the compiler is modern and standards compliant.
|
||||
This decoder example shows the most basic usage of LodePNG. More complex
|
||||
examples can be found on the LodePNG website.
|
||||
|
||||
NOTE: these examples do not support wide-character filenames, you can use an
|
||||
external method to handle such files and encode or decode in-memory
|
||||
|
||||
10.1. decoder C++ example
|
||||
-------------------------
|
||||
|
||||
@@ -1775,6 +1845,10 @@ symbol.
|
||||
Not all changes are listed here, the commit history in github lists more:
|
||||
https://github.com/lvandeve/lodepng
|
||||
|
||||
*) 27 jun 2021: added warnings that file reading/writing functions don't support
|
||||
wide-character filenames (support for this is not planned, opening files is
|
||||
not the core part of PNG decoding/decoding and is platform dependent).
|
||||
*) 17 okt 2020: prevent decoding too large text/icc chunks by default.
|
||||
*) 06 mar 2020: simplified some of the dynamic memory allocations.
|
||||
*) 12 jan 2020: (!) added 'end' argument to lodepng_chunk_next to allow correct
|
||||
overflow checks.
|
||||
@@ -1941,5 +2015,5 @@ Domain: gmail dot com.
|
||||
Account: lode dot vandevenne.
|
||||
|
||||
|
||||
Copyright (c) 2005-2020 Lode Vandevenne
|
||||
Copyright (c) 2005-2021 Lode Vandevenne
|
||||
*/
|
||||
|
||||
+208
-73
@@ -1,4 +1,4 @@
|
||||
/* stb_image - v2.26 - public domain image loader - http://nothings.org/stb
|
||||
/* stb_image - v2.27 - public domain image loader - http://nothings.org/stb
|
||||
no warranty implied; use at your own risk
|
||||
|
||||
Do this:
|
||||
@@ -48,6 +48,7 @@ LICENSE
|
||||
|
||||
RECENT REVISION HISTORY:
|
||||
|
||||
2.27 (2021-07-11) document stbi_info better, 16-bit PNM support, bug fixes
|
||||
2.26 (2020-07-13) many minor fixes
|
||||
2.25 (2020-02-02) fix warnings
|
||||
2.24 (2020-02-02) fix warnings; thread-local failure_reason and flip_vertically
|
||||
@@ -89,7 +90,7 @@ RECENT REVISION HISTORY:
|
||||
Jeremy Sawicki (handle all ImageNet JPGs)
|
||||
Optimizations & bugfixes Mikhail Morozov (1-bit BMP)
|
||||
Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query)
|
||||
Arseny Kapoulkine
|
||||
Arseny Kapoulkine Simon Breuss (16-bit PNM)
|
||||
John-Mark Allen
|
||||
Carmelo J Fdez-Aguera
|
||||
|
||||
@@ -102,7 +103,7 @@ RECENT REVISION HISTORY:
|
||||
Thomas Ruf Ronny Chevalier github:rlyeh
|
||||
Janez Zemva John Bartholomew Michal Cichon github:romigrou
|
||||
Jonathan Blow Ken Hamada Tero Hanninen github:svdijk
|
||||
Laurent Gomila Cort Stratton github:snagar
|
||||
Eugene Golushkov Laurent Gomila Cort Stratton github:snagar
|
||||
Aruelien Pocheville Sergio Gonzalez Thibault Reuille github:Zelex
|
||||
Cass Everitt Ryamond Barbiero github:grim210
|
||||
Paul Du Bois Engin Manap Aldo Culquicondor github:sammyhw
|
||||
@@ -110,11 +111,13 @@ RECENT REVISION HISTORY:
|
||||
Josh Tobin Matthew Gregan github:poppolopoppo
|
||||
Julian Raschke Gregory Mullen Christian Floisand github:darealshinji
|
||||
Baldur Karlsson Kevin Schmidt JR Smith github:Michaelangel007
|
||||
Brad Weinberger Matvey Cherevko [reserved]
|
||||
Brad Weinberger Matvey Cherevko github:mosra
|
||||
Luca Sas Alexander Veselov Zack Middleton [reserved]
|
||||
Ryan C. Gordon [reserved] [reserved]
|
||||
DO NOT ADD YOUR NAME HERE
|
||||
|
||||
Jacko Dirks
|
||||
|
||||
To add your name to the credits, pick a random blank space in the middle and fill it.
|
||||
80% of merge conflicts on stb PRs are due to people adding their name at the end
|
||||
of the credits.
|
||||
@@ -176,6 +179,32 @@ RECENT REVISION HISTORY:
|
||||
//
|
||||
// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized.
|
||||
//
|
||||
// To query the width, height and component count of an image without having to
|
||||
// decode the full file, you can use the stbi_info family of functions:
|
||||
//
|
||||
// int x,y,n,ok;
|
||||
// ok = stbi_info(filename, &x, &y, &n);
|
||||
// // returns ok=1 and sets x, y, n if image is a supported format,
|
||||
// // 0 otherwise.
|
||||
//
|
||||
// Note that stb_image pervasively uses ints in its public API for sizes,
|
||||
// including sizes of memory buffers. This is now part of the API and thus
|
||||
// hard to change without causing breakage. As a result, the various image
|
||||
// loaders all have certain limits on image size; these differ somewhat
|
||||
// by format but generally boil down to either just under 2GB or just under
|
||||
// 1GB. When the decoded image would be larger than this, stb_image decoding
|
||||
// will fail.
|
||||
//
|
||||
// Additionally, stb_image will reject image files that have any of their
|
||||
// dimensions set to a larger value than the configurable STBI_MAX_DIMENSIONS,
|
||||
// which defaults to 2**24 = 16777216 pixels. Due to the above memory limit,
|
||||
// the only way to have an image with such dimensions load correctly
|
||||
// is for it to have a rather extreme aspect ratio. Either way, the
|
||||
// assumption here is that such larger images are likely to be malformed
|
||||
// or malicious. If you do need to load an image with individual dimensions
|
||||
// larger than that, and it still fits in the overall size limit, you can
|
||||
// #define STBI_MAX_DIMENSIONS on your own to be something larger.
|
||||
//
|
||||
// ===========================================================================
|
||||
//
|
||||
// UNICODE:
|
||||
@@ -281,11 +310,10 @@ RECENT REVISION HISTORY:
|
||||
//
|
||||
// iPhone PNG support:
|
||||
//
|
||||
// By default we convert iphone-formatted PNGs back to RGB, even though
|
||||
// they are internally encoded differently. You can disable this conversion
|
||||
// by calling stbi_convert_iphone_png_to_rgb(0), in which case
|
||||
// you will always just get the native iphone "format" through (which
|
||||
// is BGR stored in RGB).
|
||||
// We optionally support converting iPhone-formatted PNGs (which store
|
||||
// premultiplied BGRA) back to RGB, even though they're internally encoded
|
||||
// differently. To enable this conversion, call
|
||||
// stbi_convert_iphone_png_to_rgb(1).
|
||||
//
|
||||
// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per
|
||||
// pixel to remove any premultiplied alpha *only* if the image file explicitly
|
||||
@@ -489,6 +517,8 @@ STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip);
|
||||
// as above, but only applies to images loaded on the thread that calls the function
|
||||
// this function is only available if your compiler supports thread-local variables;
|
||||
// calling it will fail to link if your compiler doesn't
|
||||
STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply);
|
||||
STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert);
|
||||
STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip);
|
||||
|
||||
// ZLIB client - used by PNG, available for other purposes
|
||||
@@ -634,7 +664,7 @@ typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1];
|
||||
#ifdef STBI_HAS_LROTL
|
||||
#define stbi_lrot(x,y) _lrotl(x,y)
|
||||
#else
|
||||
#define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y))))
|
||||
#define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (-(y) & 31)))
|
||||
#endif
|
||||
|
||||
#if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED))
|
||||
@@ -748,9 +778,12 @@ static int stbi__sse2_available(void)
|
||||
|
||||
#ifdef STBI_NEON
|
||||
#include <arm_neon.h>
|
||||
// assume GCC or Clang on ARM targets
|
||||
#ifdef _MSC_VER
|
||||
#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name
|
||||
#else
|
||||
#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16)))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef STBI_SIMD_ALIGN
|
||||
#define STBI_SIMD_ALIGN(type, name) type name
|
||||
@@ -924,6 +957,7 @@ static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp);
|
||||
static int stbi__pnm_test(stbi__context *s);
|
||||
static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri);
|
||||
static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp);
|
||||
static int stbi__pnm_is16(stbi__context *s);
|
||||
#endif
|
||||
|
||||
static
|
||||
@@ -998,7 +1032,7 @@ static int stbi__mad3sizes_valid(int a, int b, int c, int add)
|
||||
}
|
||||
|
||||
// returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow
|
||||
#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR)
|
||||
#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM)
|
||||
static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add)
|
||||
{
|
||||
return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) &&
|
||||
@@ -1021,7 +1055,7 @@ static void *stbi__malloc_mad3(int a, int b, int c, int add)
|
||||
return stbi__malloc(a*b*c + add);
|
||||
}
|
||||
|
||||
#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR)
|
||||
#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM)
|
||||
static void *stbi__malloc_mad4(int a, int b, int c, int d, int add)
|
||||
{
|
||||
if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL;
|
||||
@@ -1087,9 +1121,8 @@ static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int re
|
||||
ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order
|
||||
ri->num_channels = 0;
|
||||
|
||||
#ifndef STBI_NO_JPEG
|
||||
if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri);
|
||||
#endif
|
||||
// test the formats with a very explicit header first (at least a FOURCC
|
||||
// or distinctive magic number first)
|
||||
#ifndef STBI_NO_PNG
|
||||
if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp, ri);
|
||||
#endif
|
||||
@@ -1107,6 +1140,13 @@ static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int re
|
||||
#ifndef STBI_NO_PIC
|
||||
if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri);
|
||||
#endif
|
||||
|
||||
// then the formats that can end up attempting to load with just 1 or 2
|
||||
// bytes matching expectations; these are prone to false positives, so
|
||||
// try them later
|
||||
#ifndef STBI_NO_JPEG
|
||||
if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri);
|
||||
#endif
|
||||
#ifndef STBI_NO_PNM
|
||||
if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp, ri);
|
||||
#endif
|
||||
@@ -1262,12 +1302,12 @@ static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, in
|
||||
|
||||
#ifndef STBI_NO_STDIO
|
||||
|
||||
#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8)
|
||||
#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8)
|
||||
STBI_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide);
|
||||
STBI_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default);
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8)
|
||||
#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8)
|
||||
STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input)
|
||||
{
|
||||
return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL);
|
||||
@@ -1277,16 +1317,16 @@ STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wch
|
||||
static FILE *stbi__fopen(char const *filename, char const *mode)
|
||||
{
|
||||
FILE *f;
|
||||
#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8)
|
||||
#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8)
|
||||
wchar_t wMode[64];
|
||||
wchar_t wFilename[1024];
|
||||
if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)))
|
||||
if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)/sizeof(*wFilename)))
|
||||
return 0;
|
||||
|
||||
if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)))
|
||||
if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)/sizeof(*wMode)))
|
||||
return 0;
|
||||
|
||||
#if _MSC_VER >= 1400
|
||||
#if defined(_MSC_VER) && _MSC_VER >= 1400
|
||||
if (0 != _wfopen_s(&f, wFilename, wMode))
|
||||
f = 0;
|
||||
#else
|
||||
@@ -1662,7 +1702,8 @@ static int stbi__get16le(stbi__context *s)
|
||||
static stbi__uint32 stbi__get32le(stbi__context *s)
|
||||
{
|
||||
stbi__uint32 z = stbi__get16le(s);
|
||||
return z + (stbi__get16le(s) << 16);
|
||||
z += (stbi__uint32)stbi__get16le(s) << 16;
|
||||
return z;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -2090,13 +2131,12 @@ stbi_inline static int stbi__extend_receive(stbi__jpeg *j, int n)
|
||||
int sgn;
|
||||
if (j->code_bits < n) stbi__grow_buffer_unsafe(j);
|
||||
|
||||
sgn = (stbi__int32)j->code_buffer >> 31; // sign bit is always in MSB
|
||||
sgn = j->code_buffer >> 31; // sign bit always in MSB; 0 if MSB clear (positive), 1 if MSB set (negative)
|
||||
k = stbi_lrot(j->code_buffer, n);
|
||||
if (n < 0 || n >= (int) (sizeof(stbi__bmask)/sizeof(*stbi__bmask))) return 0;
|
||||
j->code_buffer = k & ~stbi__bmask[n];
|
||||
k &= stbi__bmask[n];
|
||||
j->code_bits -= n;
|
||||
return k + (stbi__jbias[n] & ~sgn);
|
||||
return k + (stbi__jbias[n] & (sgn - 1));
|
||||
}
|
||||
|
||||
// get some unsigned bits
|
||||
@@ -2146,7 +2186,7 @@ static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman
|
||||
|
||||
if (j->code_bits < 16) stbi__grow_buffer_unsafe(j);
|
||||
t = stbi__jpeg_huff_decode(j, hdc);
|
||||
if (t < 0) return stbi__err("bad huffman code","Corrupt JPEG");
|
||||
if (t < 0 || t > 15) return stbi__err("bad huffman code","Corrupt JPEG");
|
||||
|
||||
// 0 all the ac values now so we can do it 32-bits at a time
|
||||
memset(data,0,64*sizeof(data[0]));
|
||||
@@ -2203,12 +2243,12 @@ static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__
|
||||
// first scan for DC coefficient, must be first
|
||||
memset(data,0,64*sizeof(data[0])); // 0 all the ac values now
|
||||
t = stbi__jpeg_huff_decode(j, hdc);
|
||||
if (t == -1) return stbi__err("can't merge dc and ac", "Corrupt JPEG");
|
||||
if (t < 0 || t > 15) return stbi__err("can't merge dc and ac", "Corrupt JPEG");
|
||||
diff = t ? stbi__extend_receive(j, t) : 0;
|
||||
|
||||
dc = j->img_comp[b].dc_pred + diff;
|
||||
j->img_comp[b].dc_pred = dc;
|
||||
data[0] = (short) (dc << j->succ_low);
|
||||
data[0] = (short) (dc * (1 << j->succ_low));
|
||||
} else {
|
||||
// refinement scan for DC coefficient
|
||||
if (stbi__jpeg_get_bit(j))
|
||||
@@ -2245,7 +2285,7 @@ static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__
|
||||
j->code_buffer <<= s;
|
||||
j->code_bits -= s;
|
||||
zig = stbi__jpeg_dezigzag[k++];
|
||||
data[zig] = (short) ((r >> 8) << shift);
|
||||
data[zig] = (short) ((r >> 8) * (1 << shift));
|
||||
} else {
|
||||
int rs = stbi__jpeg_huff_decode(j, hac);
|
||||
if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG");
|
||||
@@ -2263,7 +2303,7 @@ static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__
|
||||
} else {
|
||||
k += r;
|
||||
zig = stbi__jpeg_dezigzag[k++];
|
||||
data[zig] = (short) (stbi__extend_receive(j,s) << shift);
|
||||
data[zig] = (short) (stbi__extend_receive(j,s) * (1 << shift));
|
||||
}
|
||||
}
|
||||
} while (k <= j->spec_end);
|
||||
@@ -3227,6 +3267,13 @@ static int stbi__process_frame_header(stbi__jpeg *z, int scan)
|
||||
if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v;
|
||||
}
|
||||
|
||||
// check that plane subsampling factors are integer ratios; our resamplers can't deal with fractional ratios
|
||||
// and I've never seen a non-corrupted JPEG file actually use them
|
||||
for (i=0; i < s->img_n; ++i) {
|
||||
if (h_max % z->img_comp[i].h != 0) return stbi__err("bad H","Corrupt JPEG");
|
||||
if (v_max % z->img_comp[i].v != 0) return stbi__err("bad V","Corrupt JPEG");
|
||||
}
|
||||
|
||||
// compute interleaved mcu info
|
||||
z->img_h_max = h_max;
|
||||
z->img_v_max = v_max;
|
||||
@@ -3782,6 +3829,10 @@ static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp
|
||||
else
|
||||
decode_n = z->s->img_n;
|
||||
|
||||
// nothing to do if no components requested; check this now to avoid
|
||||
// accessing uninitialized coutput[0] later
|
||||
if (decode_n <= 0) { stbi__cleanup_jpeg(z); return NULL; }
|
||||
|
||||
// resample and color-convert
|
||||
{
|
||||
int k;
|
||||
@@ -3924,6 +3975,7 @@ static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int re
|
||||
{
|
||||
unsigned char* result;
|
||||
stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg));
|
||||
if (!j) return stbi__errpuc("outofmem", "Out of memory");
|
||||
STBI_NOTUSED(ri);
|
||||
j->s = s;
|
||||
stbi__setup_jpeg(j);
|
||||
@@ -3936,6 +3988,7 @@ static int stbi__jpeg_test(stbi__context *s)
|
||||
{
|
||||
int r;
|
||||
stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg));
|
||||
if (!j) return stbi__err("outofmem", "Out of memory");
|
||||
j->s = s;
|
||||
stbi__setup_jpeg(j);
|
||||
r = stbi__decode_jpeg_header(j, STBI__SCAN_type);
|
||||
@@ -3960,6 +4013,7 @@ static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp)
|
||||
{
|
||||
int result;
|
||||
stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg)));
|
||||
if (!j) return stbi__err("outofmem", "Out of memory");
|
||||
j->s = s;
|
||||
result = stbi__jpeg_info_raw(j, x, y, comp);
|
||||
STBI_FREE(j);
|
||||
@@ -3979,6 +4033,7 @@ static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp)
|
||||
// fast-way is faster to check than jpeg huffman, but slow way is slower
|
||||
#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables
|
||||
#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1)
|
||||
#define STBI__ZNSYMS 288 // number of symbols in literal/length alphabet
|
||||
|
||||
// zlib-style huffman encoding
|
||||
// (jpegs packs from left, zlib from right, so can't share code)
|
||||
@@ -3988,8 +4043,8 @@ typedef struct
|
||||
stbi__uint16 firstcode[16];
|
||||
int maxcode[17];
|
||||
stbi__uint16 firstsymbol[16];
|
||||
stbi_uc size[288];
|
||||
stbi__uint16 value[288];
|
||||
stbi_uc size[STBI__ZNSYMS];
|
||||
stbi__uint16 value[STBI__ZNSYMS];
|
||||
} stbi__zhuffman;
|
||||
|
||||
stbi_inline static int stbi__bitreverse16(int n)
|
||||
@@ -4120,7 +4175,7 @@ static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z)
|
||||
if (s >= 16) return -1; // invalid code!
|
||||
// code size is s, so:
|
||||
b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s];
|
||||
if (b >= sizeof (z->size)) return -1; // some data was corrupt somewhere!
|
||||
if (b >= STBI__ZNSYMS) return -1; // some data was corrupt somewhere!
|
||||
if (z->size[b] != s) return -1; // was originally an assert, but report failure instead.
|
||||
a->code_buffer >>= s;
|
||||
a->num_bits -= s;
|
||||
@@ -4317,7 +4372,7 @@ static int stbi__parse_zlib_header(stbi__zbuf *a)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const stbi_uc stbi__zdefault_length[288] =
|
||||
static const stbi_uc stbi__zdefault_length[STBI__ZNSYMS] =
|
||||
{
|
||||
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
|
||||
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
|
||||
@@ -4363,7 +4418,7 @@ static int stbi__parse_zlib(stbi__zbuf *a, int parse_header)
|
||||
} else {
|
||||
if (type == 1) {
|
||||
// use fixed code lengths
|
||||
if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , 288)) return 0;
|
||||
if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , STBI__ZNSYMS)) return 0;
|
||||
if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0;
|
||||
} else {
|
||||
if (!stbi__compute_huffman_codes(a)) return 0;
|
||||
@@ -4759,6 +4814,7 @@ static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint3
|
||||
|
||||
// de-interlacing
|
||||
final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0);
|
||||
if (!final) return stbi__err("outofmem", "Out of memory");
|
||||
for (p=0; p < 7; ++p) {
|
||||
int xorig[] = { 0,4,0,2,0,1,0 };
|
||||
int yorig[] = { 0,0,4,0,2,0,1 };
|
||||
@@ -4879,19 +4935,46 @@ static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int stbi__unpremultiply_on_load = 0;
|
||||
static int stbi__de_iphone_flag = 0;
|
||||
static int stbi__unpremultiply_on_load_global = 0;
|
||||
static int stbi__de_iphone_flag_global = 0;
|
||||
|
||||
STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply)
|
||||
{
|
||||
stbi__unpremultiply_on_load = flag_true_if_should_unpremultiply;
|
||||
stbi__unpremultiply_on_load_global = flag_true_if_should_unpremultiply;
|
||||
}
|
||||
|
||||
STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert)
|
||||
{
|
||||
stbi__de_iphone_flag = flag_true_if_should_convert;
|
||||
stbi__de_iphone_flag_global = flag_true_if_should_convert;
|
||||
}
|
||||
|
||||
#ifndef STBI_THREAD_LOCAL
|
||||
#define stbi__unpremultiply_on_load stbi__unpremultiply_on_load_global
|
||||
#define stbi__de_iphone_flag stbi__de_iphone_flag_global
|
||||
#else
|
||||
static STBI_THREAD_LOCAL int stbi__unpremultiply_on_load_local, stbi__unpremultiply_on_load_set;
|
||||
static STBI_THREAD_LOCAL int stbi__de_iphone_flag_local, stbi__de_iphone_flag_set;
|
||||
|
||||
STBIDEF void stbi__unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply)
|
||||
{
|
||||
stbi__unpremultiply_on_load_local = flag_true_if_should_unpremultiply;
|
||||
stbi__unpremultiply_on_load_set = 1;
|
||||
}
|
||||
|
||||
STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert)
|
||||
{
|
||||
stbi__de_iphone_flag_local = flag_true_if_should_convert;
|
||||
stbi__de_iphone_flag_set = 1;
|
||||
}
|
||||
|
||||
#define stbi__unpremultiply_on_load (stbi__unpremultiply_on_load_set \
|
||||
? stbi__unpremultiply_on_load_local \
|
||||
: stbi__unpremultiply_on_load_global)
|
||||
#define stbi__de_iphone_flag (stbi__de_iphone_flag_set \
|
||||
? stbi__de_iphone_flag_local \
|
||||
: stbi__de_iphone_flag_global)
|
||||
#endif // STBI_THREAD_LOCAL
|
||||
|
||||
static void stbi__de_iphone(stbi__png *z)
|
||||
{
|
||||
stbi__context *s = z->s;
|
||||
@@ -5272,6 +5355,32 @@ typedef struct
|
||||
int extra_read;
|
||||
} stbi__bmp_data;
|
||||
|
||||
static int stbi__bmp_set_mask_defaults(stbi__bmp_data *info, int compress)
|
||||
{
|
||||
// BI_BITFIELDS specifies masks explicitly, don't override
|
||||
if (compress == 3)
|
||||
return 1;
|
||||
|
||||
if (compress == 0) {
|
||||
if (info->bpp == 16) {
|
||||
info->mr = 31u << 10;
|
||||
info->mg = 31u << 5;
|
||||
info->mb = 31u << 0;
|
||||
} else if (info->bpp == 32) {
|
||||
info->mr = 0xffu << 16;
|
||||
info->mg = 0xffu << 8;
|
||||
info->mb = 0xffu << 0;
|
||||
info->ma = 0xffu << 24;
|
||||
info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0
|
||||
} else {
|
||||
// otherwise, use defaults, which is all-0
|
||||
info->mr = info->mg = info->mb = info->ma = 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 0; // error
|
||||
}
|
||||
|
||||
static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info)
|
||||
{
|
||||
int hsz;
|
||||
@@ -5299,6 +5408,8 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info)
|
||||
if (hsz != 12) {
|
||||
int compress = stbi__get32le(s);
|
||||
if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE");
|
||||
if (compress >= 4) return stbi__errpuc("BMP JPEG/PNG", "BMP type not supported: unsupported compression"); // this includes PNG/JPEG modes
|
||||
if (compress == 3 && info->bpp != 16 && info->bpp != 32) return stbi__errpuc("bad BMP", "bad BMP"); // bitfields requires 16 or 32 bits/pixel
|
||||
stbi__get32le(s); // discard sizeof
|
||||
stbi__get32le(s); // discard hres
|
||||
stbi__get32le(s); // discard vres
|
||||
@@ -5313,17 +5424,7 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info)
|
||||
}
|
||||
if (info->bpp == 16 || info->bpp == 32) {
|
||||
if (compress == 0) {
|
||||
if (info->bpp == 32) {
|
||||
info->mr = 0xffu << 16;
|
||||
info->mg = 0xffu << 8;
|
||||
info->mb = 0xffu << 0;
|
||||
info->ma = 0xffu << 24;
|
||||
info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0
|
||||
} else {
|
||||
info->mr = 31u << 10;
|
||||
info->mg = 31u << 5;
|
||||
info->mb = 31u << 0;
|
||||
}
|
||||
stbi__bmp_set_mask_defaults(info, compress);
|
||||
} else if (compress == 3) {
|
||||
info->mr = stbi__get32le(s);
|
||||
info->mg = stbi__get32le(s);
|
||||
@@ -5338,6 +5439,7 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info)
|
||||
return stbi__errpuc("bad BMP", "bad BMP");
|
||||
}
|
||||
} else {
|
||||
// V4/V5 header
|
||||
int i;
|
||||
if (hsz != 108 && hsz != 124)
|
||||
return stbi__errpuc("bad BMP", "bad BMP");
|
||||
@@ -5345,6 +5447,8 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info)
|
||||
info->mg = stbi__get32le(s);
|
||||
info->mb = stbi__get32le(s);
|
||||
info->ma = stbi__get32le(s);
|
||||
if (compress != 3) // override mr/mg/mb unless in BI_BITFIELDS mode, as per docs
|
||||
stbi__bmp_set_mask_defaults(info, compress);
|
||||
stbi__get32le(s); // discard color space
|
||||
for (i=0; i < 12; ++i)
|
||||
stbi__get32le(s); // discard color space parameters
|
||||
@@ -5394,8 +5498,7 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req
|
||||
psize = (info.offset - info.extra_read - info.hsz) >> 2;
|
||||
}
|
||||
if (psize == 0) {
|
||||
STBI_ASSERT(info.offset == s->callback_already_read + (int) (s->img_buffer - s->img_buffer_original));
|
||||
if (info.offset != s->callback_already_read + (s->img_buffer - s->buffer_start)) {
|
||||
if (info.offset != s->callback_already_read + (s->img_buffer - s->img_buffer_original)) {
|
||||
return stbi__errpuc("bad offset", "Corrupt BMP");
|
||||
}
|
||||
}
|
||||
@@ -6342,6 +6445,7 @@ static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_c
|
||||
|
||||
// intermediate buffer is RGBA
|
||||
result = (stbi_uc *) stbi__malloc_mad3(x, y, 4, 0);
|
||||
if (!result) return stbi__errpuc("outofmem", "Out of memory");
|
||||
memset(result, 0xff, x*y*4);
|
||||
|
||||
if (!stbi__pic_load_core(s,x,y,comp, result)) {
|
||||
@@ -6457,6 +6561,7 @@ static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_in
|
||||
static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp)
|
||||
{
|
||||
stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif));
|
||||
if (!g) return stbi__err("outofmem", "Out of memory");
|
||||
if (!stbi__gif_header(s, g, comp, 1)) {
|
||||
STBI_FREE(g);
|
||||
stbi__rewind( s );
|
||||
@@ -6766,6 +6871,17 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i
|
||||
}
|
||||
}
|
||||
|
||||
static void *stbi__load_gif_main_outofmem(stbi__gif *g, stbi_uc *out, int **delays)
|
||||
{
|
||||
STBI_FREE(g->out);
|
||||
STBI_FREE(g->history);
|
||||
STBI_FREE(g->background);
|
||||
|
||||
if (out) STBI_FREE(out);
|
||||
if (delays && *delays) STBI_FREE(*delays);
|
||||
return stbi__errpuc("outofmem", "Out of memory");
|
||||
}
|
||||
|
||||
static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp)
|
||||
{
|
||||
if (stbi__gif_test(s)) {
|
||||
@@ -6777,6 +6893,10 @@ static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y,
|
||||
int stride;
|
||||
int out_size = 0;
|
||||
int delays_size = 0;
|
||||
|
||||
STBI_NOTUSED(out_size);
|
||||
STBI_NOTUSED(delays_size);
|
||||
|
||||
memset(&g, 0, sizeof(g));
|
||||
if (delays) {
|
||||
*delays = 0;
|
||||
@@ -6794,26 +6914,29 @@ static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y,
|
||||
|
||||
if (out) {
|
||||
void *tmp = (stbi_uc*) STBI_REALLOC_SIZED( out, out_size, layers * stride );
|
||||
if (NULL == tmp) {
|
||||
STBI_FREE(g.out);
|
||||
STBI_FREE(g.history);
|
||||
STBI_FREE(g.background);
|
||||
return stbi__errpuc("outofmem", "Out of memory");
|
||||
}
|
||||
if (!tmp)
|
||||
return stbi__load_gif_main_outofmem(&g, out, delays);
|
||||
else {
|
||||
out = (stbi_uc*) tmp;
|
||||
out_size = layers * stride;
|
||||
}
|
||||
|
||||
if (delays) {
|
||||
*delays = (int*) STBI_REALLOC_SIZED( *delays, delays_size, sizeof(int) * layers );
|
||||
int *new_delays = (int*) STBI_REALLOC_SIZED( *delays, delays_size, sizeof(int) * layers );
|
||||
if (!new_delays)
|
||||
return stbi__load_gif_main_outofmem(&g, out, delays);
|
||||
*delays = new_delays;
|
||||
delays_size = layers * sizeof(int);
|
||||
}
|
||||
} else {
|
||||
out = (stbi_uc*)stbi__malloc( layers * stride );
|
||||
if (!out)
|
||||
return stbi__load_gif_main_outofmem(&g, out, delays);
|
||||
out_size = layers * stride;
|
||||
if (delays) {
|
||||
*delays = (int*) stbi__malloc( layers * sizeof(int) );
|
||||
if (!*delays)
|
||||
return stbi__load_gif_main_outofmem(&g, out, delays);
|
||||
delays_size = layers * sizeof(int);
|
||||
}
|
||||
}
|
||||
@@ -7138,9 +7261,10 @@ static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp)
|
||||
|
||||
info.all_a = 255;
|
||||
p = stbi__bmp_parse_header(s, &info);
|
||||
stbi__rewind( s );
|
||||
if (p == NULL)
|
||||
if (p == NULL) {
|
||||
stbi__rewind( s );
|
||||
return 0;
|
||||
}
|
||||
if (x) *x = s->img_x;
|
||||
if (y) *y = s->img_y;
|
||||
if (comp) {
|
||||
@@ -7206,8 +7330,8 @@ static int stbi__psd_is16(stbi__context *s)
|
||||
stbi__rewind( s );
|
||||
return 0;
|
||||
}
|
||||
(void) stbi__get32be(s);
|
||||
(void) stbi__get32be(s);
|
||||
STBI_NOTUSED(stbi__get32be(s));
|
||||
STBI_NOTUSED(stbi__get32be(s));
|
||||
depth = stbi__get16be(s);
|
||||
if (depth != 16) {
|
||||
stbi__rewind( s );
|
||||
@@ -7286,7 +7410,6 @@ static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp)
|
||||
// Known limitations:
|
||||
// Does not support comments in the header section
|
||||
// Does not support ASCII image data (formats P2 and P3)
|
||||
// Does not support 16-bit-per-channel
|
||||
|
||||
#ifndef STBI_NO_PNM
|
||||
|
||||
@@ -7307,7 +7430,8 @@ static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req
|
||||
stbi_uc *out;
|
||||
STBI_NOTUSED(ri);
|
||||
|
||||
if (!stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n))
|
||||
ri->bits_per_channel = stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n);
|
||||
if (ri->bits_per_channel == 0)
|
||||
return 0;
|
||||
|
||||
if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)");
|
||||
@@ -7317,12 +7441,12 @@ static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req
|
||||
*y = s->img_y;
|
||||
if (comp) *comp = s->img_n;
|
||||
|
||||
if (!stbi__mad3sizes_valid(s->img_n, s->img_x, s->img_y, 0))
|
||||
if (!stbi__mad4sizes_valid(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0))
|
||||
return stbi__errpuc("too large", "PNM too large");
|
||||
|
||||
out = (stbi_uc *) stbi__malloc_mad3(s->img_n, s->img_x, s->img_y, 0);
|
||||
out = (stbi_uc *) stbi__malloc_mad4(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0);
|
||||
if (!out) return stbi__errpuc("outofmem", "Out of memory");
|
||||
stbi__getn(s, out, s->img_n * s->img_x * s->img_y);
|
||||
stbi__getn(s, out, s->img_n * s->img_x * s->img_y * (ri->bits_per_channel / 8));
|
||||
|
||||
if (req_comp && req_comp != s->img_n) {
|
||||
out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y);
|
||||
@@ -7398,11 +7522,19 @@ static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp)
|
||||
stbi__pnm_skip_whitespace(s, &c);
|
||||
|
||||
maxv = stbi__pnm_getinteger(s, &c); // read max value
|
||||
|
||||
if (maxv > 255)
|
||||
return stbi__err("max value > 255", "PPM image not 8-bit");
|
||||
if (maxv > 65535)
|
||||
return stbi__err("max value > 65535", "PPM image supports only 8-bit and 16-bit images");
|
||||
else if (maxv > 255)
|
||||
return 16;
|
||||
else
|
||||
return 1;
|
||||
return 8;
|
||||
}
|
||||
|
||||
static int stbi__pnm_is16(stbi__context *s)
|
||||
{
|
||||
if (stbi__pnm_info(s, NULL, NULL, NULL) == 16)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -7458,6 +7590,9 @@ static int stbi__is_16_main(stbi__context *s)
|
||||
if (stbi__psd_is16(s)) return 1;
|
||||
#endif
|
||||
|
||||
#ifndef STBI_NO_PNM
|
||||
if (stbi__pnm_is16(s)) return 1;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -338,7 +338,7 @@ float Font::getKerning(uint32 leftglyph, uint32 rightglyph)
|
||||
if (it != kerning.end())
|
||||
return it->second;
|
||||
|
||||
float k = rasterizers[0]->getKerning(leftglyph, rightglyph) / dpiScale + 0.5f;
|
||||
float k = floorf(rasterizers[0]->getKerning(leftglyph, rightglyph) / dpiScale + 0.5f);
|
||||
|
||||
for (const auto &r : rasterizers)
|
||||
{
|
||||
|
||||
@@ -1480,7 +1480,7 @@ void Graphics::rectangle(DrawMode mode, float x, float y, float w, float h)
|
||||
|
||||
void Graphics::rectangle(DrawMode mode, float x, float y, float w, float h, float rx, float ry, int points)
|
||||
{
|
||||
if (rx == 0 || ry == 0)
|
||||
if (rx <= 0 || ry <= 0)
|
||||
{
|
||||
rectangle(mode, x, y, w, h);
|
||||
return;
|
||||
@@ -1539,7 +1539,8 @@ void Graphics::rectangle(DrawMode mode, float x, float y, float w, float h, floa
|
||||
|
||||
void Graphics::rectangle(DrawMode mode, float x, float y, float w, float h, float rx, float ry)
|
||||
{
|
||||
rectangle(mode, x, y, w, h, rx, ry, calculateEllipsePoints(rx, ry));
|
||||
int points = calculateEllipsePoints(std::min(rx, std::abs(w/2)), std::min(ry, std::abs(h/2)));
|
||||
rectangle(mode, x, y, w, h, rx, ry, points);
|
||||
}
|
||||
|
||||
void Graphics::circle(DrawMode mode, float x, float y, float radius, int points)
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
R"luastring"--(
|
||||
-- DO NOT REMOVE THE ABOVE LINE. It is used to load this file as a C++ string.
|
||||
-- There is a matching delimiter at the bottom of the file.
|
||||
|
||||
--[[
|
||||
Copyright (c) 2006-2021 LOVE Development Team
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
--]]
|
||||
|
||||
local love = require("love")
|
||||
|
||||
-- Used for setup:
|
||||
love.path = {}
|
||||
love.arg = {}
|
||||
|
||||
-- Replace any \ with /.
|
||||
function love.path.normalslashes(p)
|
||||
return p:gsub("\\", "/")
|
||||
end
|
||||
|
||||
-- Makes sure there is a slash at the end
|
||||
-- of a path.
|
||||
function love.path.endslash(p)
|
||||
if p:sub(-1) ~= "/" then
|
||||
return p .. "/"
|
||||
else
|
||||
return p
|
||||
end
|
||||
end
|
||||
|
||||
-- Checks whether a path is absolute or not.
|
||||
function love.path.abs(p)
|
||||
|
||||
local tmp = love.path.normalslashes(p)
|
||||
|
||||
-- Path is absolute if it starts with a "/".
|
||||
if tmp:find("/") == 1 then
|
||||
return true
|
||||
end
|
||||
|
||||
-- Path is absolute if it starts with a
|
||||
-- letter followed by a colon.
|
||||
if tmp:find("%a:") == 1 then
|
||||
return true
|
||||
end
|
||||
|
||||
-- Relative.
|
||||
return false
|
||||
|
||||
end
|
||||
|
||||
-- Converts any path into a full path.
|
||||
function love.path.getFull(p)
|
||||
|
||||
if love.path.abs(p) then
|
||||
return love.path.normalslashes(p)
|
||||
end
|
||||
|
||||
local cwd = love.filesystem.getWorkingDirectory()
|
||||
cwd = love.path.normalslashes(cwd)
|
||||
cwd = love.path.endslash(cwd)
|
||||
|
||||
-- Construct a full path.
|
||||
local full = cwd .. love.path.normalslashes(p)
|
||||
|
||||
-- Remove trailing /., if applicable
|
||||
return full:match("(.-)/%.$") or full
|
||||
end
|
||||
|
||||
-- Returns the leaf of a full path.
|
||||
function love.path.leaf(p)
|
||||
p = love.path.normalslashes(p)
|
||||
|
||||
local a = 1
|
||||
local last = p
|
||||
|
||||
while a do
|
||||
a = p:find("/", a+1)
|
||||
|
||||
if a then
|
||||
last = p:sub(a+1)
|
||||
end
|
||||
end
|
||||
|
||||
return last
|
||||
end
|
||||
|
||||
-- Finds the key in the table with the lowest integral index. The lowest
|
||||
-- will typically the executable, for instance "lua5.1.exe".
|
||||
function love.arg.getLow(a)
|
||||
local m = math.huge
|
||||
for k,v in pairs(a) do
|
||||
if k < m then
|
||||
m = k
|
||||
end
|
||||
end
|
||||
return a[m], m
|
||||
end
|
||||
|
||||
love.arg.options = {
|
||||
console = { a = 0 },
|
||||
fused = {a = 0 },
|
||||
game = { a = 1 }
|
||||
}
|
||||
|
||||
love.arg.optionIndices = {}
|
||||
|
||||
function love.arg.parseOption(m, i)
|
||||
m.set = true
|
||||
|
||||
if m.a > 0 then
|
||||
m.arg = {}
|
||||
for j=i,i+m.a-1 do
|
||||
love.arg.optionIndices[j] = true
|
||||
table.insert(m.arg, arg[j])
|
||||
end
|
||||
end
|
||||
|
||||
return m.a
|
||||
end
|
||||
|
||||
function love.arg.parseOptions(arg)
|
||||
|
||||
local game
|
||||
local argc = #arg
|
||||
|
||||
local i = 1
|
||||
while i <= argc do
|
||||
-- Look for options.
|
||||
local m = arg[i]:match("^%-%-(.*)")
|
||||
|
||||
if m and m ~= "" and love.arg.options[m] and not love.arg.options[m].set then
|
||||
love.arg.optionIndices[i] = true
|
||||
i = i + love.arg.parseOption(love.arg.options[m], i+1)
|
||||
elseif m == "" then -- handle '--' as an option
|
||||
love.arg.optionIndices[i] = true
|
||||
if not game then -- handle '--' followed by game name
|
||||
game = i + 1
|
||||
end
|
||||
break
|
||||
elseif not game then
|
||||
game = i
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
if not love.arg.options.game.set then
|
||||
love.arg.parseOption(love.arg.options.game, game or 0)
|
||||
end
|
||||
end
|
||||
|
||||
-- Returns the arguments that are passed to your game via love.load()
|
||||
-- arguments that were parsed as options are skipped.
|
||||
function love.arg.parseGameArguments(a)
|
||||
local out = {}
|
||||
|
||||
local _, lowindex = love.arg.getLow(a)
|
||||
|
||||
local o = lowindex
|
||||
for i=lowindex, #a do
|
||||
if not love.arg.optionIndices[i] then
|
||||
out[o] = a[i]
|
||||
o = o + 1
|
||||
end
|
||||
end
|
||||
|
||||
return out
|
||||
end
|
||||
|
||||
-- DO NOT REMOVE THE NEXT LINE. It is used to load this file as a C++ string.
|
||||
--)luastring"--"
|
||||
@@ -0,0 +1,388 @@
|
||||
R"luastring"--(
|
||||
-- DO NOT REMOVE THE ABOVE LINE. It is used to load this file as a C++ string.
|
||||
-- There is a matching delimiter at the bottom of the file.
|
||||
|
||||
--[[
|
||||
Copyright (c) 2006-2021 LOVE Development Team
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
--]]
|
||||
|
||||
-- Make sure love exists.
|
||||
local love = require("love")
|
||||
|
||||
-- Essential code boot/init.
|
||||
require("love.arg")
|
||||
require("love.callbacks")
|
||||
|
||||
local function uridecode(s)
|
||||
return s:gsub("%%%x%x", function(str)
|
||||
return string.char(tonumber(str:sub(2), 16))
|
||||
end)
|
||||
end
|
||||
|
||||
local no_game_code = false
|
||||
local invalid_game_path = nil
|
||||
|
||||
-- This can't be overridden.
|
||||
function love.boot()
|
||||
|
||||
-- This is absolutely needed.
|
||||
require("love.filesystem")
|
||||
|
||||
love.rawGameArguments = arg
|
||||
|
||||
local arg0 = love.arg.getLow(love.rawGameArguments)
|
||||
love.filesystem.init(arg0)
|
||||
|
||||
local exepath = love.filesystem.getExecutablePath()
|
||||
if #exepath == 0 then
|
||||
-- This shouldn't happen, but just in case we'll fall back to arg0.
|
||||
exepath = arg0
|
||||
end
|
||||
|
||||
no_game_code = false
|
||||
invalid_game_path = nil
|
||||
|
||||
-- Is this one of those fancy "fused" games?
|
||||
local can_has_game = pcall(love.filesystem.setSource, exepath)
|
||||
|
||||
-- It's a fused game, don't parse --game argument
|
||||
if can_has_game then
|
||||
love.arg.options.game.set = true
|
||||
end
|
||||
|
||||
-- Parse options now that we know which options we're looking for.
|
||||
love.arg.parseOptions(love.rawGameArguments)
|
||||
|
||||
-- parseGameArguments can only be called after parseOptions.
|
||||
love.parsedGameArguments = love.arg.parseGameArguments(love.rawGameArguments)
|
||||
|
||||
local o = love.arg.options
|
||||
|
||||
local is_fused_game = can_has_game or love.arg.options.fused.set
|
||||
|
||||
love.filesystem.setFused(is_fused_game)
|
||||
|
||||
love.setDeprecationOutput(not love.filesystem.isFused())
|
||||
|
||||
local identity = ""
|
||||
if not can_has_game and o.game.set and o.game.arg[1] then
|
||||
local nouri = o.game.arg[1]
|
||||
|
||||
if nouri:sub(1, 7) == "file://" then
|
||||
nouri = uridecode(nouri:sub(8))
|
||||
end
|
||||
|
||||
local full_source = love.path.getFull(nouri)
|
||||
can_has_game = pcall(love.filesystem.setSource, full_source)
|
||||
|
||||
if not can_has_game then
|
||||
invalid_game_path = full_source
|
||||
end
|
||||
|
||||
-- Use the name of the source .love as the identity for now.
|
||||
identity = love.path.leaf(full_source)
|
||||
else
|
||||
-- Use the name of the exe as the identity for now.
|
||||
identity = love.path.leaf(exepath)
|
||||
end
|
||||
|
||||
-- Try to use the archive containing main.lua as the identity name. It
|
||||
-- might not be available, in which case the fallbacks above are used.
|
||||
local realdir = love.filesystem.getRealDirectory("main.lua")
|
||||
if realdir then
|
||||
identity = love.path.leaf(realdir)
|
||||
end
|
||||
|
||||
identity = identity:gsub("^([%.]+)", "") -- strip leading "."'s
|
||||
identity = identity:gsub("%.([^%.]+)$", "") -- strip extension
|
||||
identity = identity:gsub("%.", "_") -- replace remaining "."'s with "_"
|
||||
identity = #identity > 0 and identity or "lovegame"
|
||||
|
||||
-- When conf.lua is initially loaded, the main source should be checked
|
||||
-- before the save directory (the identity should be appended.)
|
||||
pcall(love.filesystem.setIdentity, identity, true)
|
||||
|
||||
if can_has_game and not (love.filesystem.getInfo("main.lua") or love.filesystem.getInfo("conf.lua")) then
|
||||
no_game_code = true
|
||||
end
|
||||
|
||||
if not can_has_game then
|
||||
local nogame = require("love.nogame")
|
||||
nogame()
|
||||
end
|
||||
end
|
||||
|
||||
function love.init()
|
||||
|
||||
-- Create default configuration settings.
|
||||
-- NOTE: Adding a new module to the modules list
|
||||
-- will NOT make it load, see below.
|
||||
local c = {
|
||||
title = "Untitled",
|
||||
version = love._version,
|
||||
window = {
|
||||
width = 800,
|
||||
height = 600,
|
||||
x = nil,
|
||||
y = nil,
|
||||
minwidth = 1,
|
||||
minheight = 1,
|
||||
fullscreen = false,
|
||||
fullscreentype = "desktop",
|
||||
display = 1,
|
||||
vsync = 1,
|
||||
msaa = 0,
|
||||
borderless = false,
|
||||
resizable = false,
|
||||
centered = true,
|
||||
usedpiscale = true,
|
||||
},
|
||||
modules = {
|
||||
data = true,
|
||||
event = true,
|
||||
keyboard = true,
|
||||
mouse = true,
|
||||
timer = true,
|
||||
joystick = true,
|
||||
touch = true,
|
||||
image = true,
|
||||
graphics = true,
|
||||
audio = true,
|
||||
math = true,
|
||||
physics = true,
|
||||
sound = true,
|
||||
system = true,
|
||||
font = true,
|
||||
thread = true,
|
||||
window = true,
|
||||
video = true,
|
||||
},
|
||||
audio = {
|
||||
mixwithsystem = true, -- Only relevant for Android / iOS.
|
||||
mic = false, -- Only relevant for Android.
|
||||
},
|
||||
console = false, -- Only relevant for windows.
|
||||
identity = false,
|
||||
appendidentity = false,
|
||||
externalstorage = false, -- Only relevant for Android.
|
||||
accelerometerjoystick = true, -- Only relevant for Android / iOS.
|
||||
gammacorrect = false,
|
||||
highdpi = false,
|
||||
}
|
||||
|
||||
-- Console hack, part 1.
|
||||
local openedconsole = false
|
||||
if love.arg.options.console.set and love._openConsole then
|
||||
love._openConsole()
|
||||
openedconsole = true
|
||||
end
|
||||
|
||||
-- If config file exists, load it and allow it to update config table.
|
||||
local confok, conferr
|
||||
if (not love.conf) and love.filesystem and love.filesystem.getInfo("conf.lua") then
|
||||
confok, conferr = pcall(require, "conf")
|
||||
end
|
||||
|
||||
-- Yes, conf.lua might not exist, but there are other ways of making
|
||||
-- love.conf appear, so we should check for it anyway.
|
||||
if love.conf then
|
||||
confok, conferr = pcall(love.conf, c)
|
||||
-- If love.conf errors, we'll trigger the error after loading modules so
|
||||
-- the error message can be displayed in the window.
|
||||
end
|
||||
|
||||
-- Console hack, part 2.
|
||||
if c.console and love._openConsole and not openedconsole then
|
||||
love._openConsole()
|
||||
end
|
||||
|
||||
-- Hack for disabling accelerometer-as-joystick on Android / iOS.
|
||||
if love._setAccelerometerAsJoystick then
|
||||
love._setAccelerometerAsJoystick(c.accelerometerjoystick)
|
||||
end
|
||||
|
||||
if love._setGammaCorrect then
|
||||
love._setGammaCorrect(c.gammacorrect)
|
||||
end
|
||||
|
||||
if love._setHighDPIAllowed then
|
||||
love._setHighDPIAllowed(c.highdpi)
|
||||
end
|
||||
|
||||
if love._setAudioMixWithSystem then
|
||||
if c.audio and c.audio.mixwithsystem ~= nil then
|
||||
love._setAudioMixWithSystem(c.audio.mixwithsystem)
|
||||
end
|
||||
end
|
||||
|
||||
if love._requestRecordingPermission then
|
||||
love._requestRecordingPermission(c.audio and c.audio.mic)
|
||||
end
|
||||
|
||||
-- Gets desired modules.
|
||||
for k,v in ipairs{
|
||||
"data",
|
||||
"thread",
|
||||
"timer",
|
||||
"event",
|
||||
"keyboard",
|
||||
"joystick",
|
||||
"mouse",
|
||||
"touch",
|
||||
"sound",
|
||||
"system",
|
||||
"audio",
|
||||
"image",
|
||||
"video",
|
||||
"font",
|
||||
"window",
|
||||
"graphics",
|
||||
"math",
|
||||
"physics",
|
||||
} do
|
||||
if c.modules[v] then
|
||||
require("love." .. v)
|
||||
end
|
||||
end
|
||||
|
||||
if love.event then
|
||||
love.createhandlers()
|
||||
end
|
||||
|
||||
-- Check the version
|
||||
c.version = tostring(c.version)
|
||||
if not love.isVersionCompatible(c.version) then
|
||||
local major, minor, revision = c.version:match("^(%d+)%.(%d+)%.(%d+)$")
|
||||
if (not major or not minor or not revision) or (major ~= love._version_major and minor ~= love._version_minor) then
|
||||
local msg = ("This game indicates it was made for version '%s' of LOVE.\n"..
|
||||
"It may not be compatible with the running version (%s)."):format(c.version, love._version)
|
||||
|
||||
print(msg)
|
||||
|
||||
if love.window then
|
||||
love.window.showMessageBox("Compatibility Warning", msg, "warning")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not confok and conferr then
|
||||
error(conferr)
|
||||
end
|
||||
|
||||
-- Setup window here.
|
||||
if c.window and c.modules.window then
|
||||
love.window.setTitle(c.window.title or c.title)
|
||||
assert(love.window.setMode(c.window.width, c.window.height,
|
||||
{
|
||||
fullscreen = c.window.fullscreen,
|
||||
fullscreentype = c.window.fullscreentype,
|
||||
vsync = c.window.vsync,
|
||||
msaa = c.window.msaa,
|
||||
stencil = c.window.stencil,
|
||||
depth = c.window.depth,
|
||||
resizable = c.window.resizable,
|
||||
minwidth = c.window.minwidth,
|
||||
minheight = c.window.minheight,
|
||||
borderless = c.window.borderless,
|
||||
centered = c.window.centered,
|
||||
display = c.window.display,
|
||||
highdpi = c.window.highdpi, -- deprecated
|
||||
usedpiscale = c.window.usedpiscale,
|
||||
x = c.window.x,
|
||||
y = c.window.y,
|
||||
}), "Could not set window mode")
|
||||
if c.window.icon then
|
||||
assert(love.image, "If an icon is set in love.conf, love.image must be loaded!")
|
||||
love.window.setIcon(love.image.newImageData(c.window.icon))
|
||||
end
|
||||
end
|
||||
|
||||
-- Our first timestep, because window creation can take some time
|
||||
if love.timer then
|
||||
love.timer.step()
|
||||
end
|
||||
|
||||
if love.filesystem then
|
||||
love.filesystem._setAndroidSaveExternal(c.externalstorage)
|
||||
love.filesystem.setIdentity(c.identity or love.filesystem.getIdentity(), c.appendidentity)
|
||||
if love.filesystem.getInfo("main.lua") then
|
||||
require("main")
|
||||
end
|
||||
end
|
||||
|
||||
if no_game_code then
|
||||
error("No code to run\nYour game might be packaged incorrectly.\nMake sure main.lua is at the top level of the zip.")
|
||||
elseif invalid_game_path then
|
||||
error("Cannot load game at path '" .. invalid_game_path .. "'.\nMake sure a folder exists at the specified path.")
|
||||
end
|
||||
end
|
||||
|
||||
local print, debug, tostring = print, debug, tostring
|
||||
|
||||
local function error_printer(msg, layer)
|
||||
print((debug.traceback("Error: " .. tostring(msg), 1+(layer or 1)):gsub("\n[^\n]+$", "")))
|
||||
end
|
||||
|
||||
-----------------------------------------------------------
|
||||
-- The root of all calls.
|
||||
-----------------------------------------------------------
|
||||
|
||||
return function()
|
||||
local func
|
||||
local inerror = false
|
||||
|
||||
local function deferErrhand(...)
|
||||
local errhand = love.errorhandler or love.errhand
|
||||
local handler = (not inerror and errhand) or error_printer
|
||||
inerror = true
|
||||
func = handler(...)
|
||||
end
|
||||
|
||||
local function earlyinit()
|
||||
-- If love.boot fails, return 1 and finish immediately
|
||||
local result = xpcall(love.boot, error_printer)
|
||||
if not result then return 1 end
|
||||
|
||||
-- If love.init or love.run fails, don't return a value,
|
||||
-- as we want the error handler to take over
|
||||
result = xpcall(love.init, deferErrhand)
|
||||
if not result then return end
|
||||
|
||||
-- NOTE: We can't assign to func directly, as we'd
|
||||
-- overwrite the result of deferErrhand with nil on error
|
||||
local main
|
||||
result, main = xpcall(love.run, deferErrhand)
|
||||
if result then
|
||||
func = main
|
||||
end
|
||||
end
|
||||
|
||||
func = earlyinit
|
||||
|
||||
while func do
|
||||
local _, retval, restartvalue = xpcall(func, deferErrhand)
|
||||
if retval then return retval, restartvalue end
|
||||
coroutine.yield()
|
||||
end
|
||||
|
||||
return 1
|
||||
end
|
||||
|
||||
-- DO NOT REMOVE THE NEXT LINE. It is used to load this file as a C++ string.
|
||||
--)luastring"--"
|
||||
@@ -0,0 +1,313 @@
|
||||
R"luastring"--(
|
||||
-- DO NOT REMOVE THE ABOVE LINE. It is used to load this file as a C++ string.
|
||||
-- There is a matching delimiter at the bottom of the file.
|
||||
|
||||
--[[
|
||||
Copyright (c) 2006-2021 LOVE Development Team
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
--]]
|
||||
|
||||
local love = require("love")
|
||||
|
||||
function love.createhandlers()
|
||||
|
||||
-- Standard callback handlers.
|
||||
love.handlers = setmetatable({
|
||||
keypressed = function (b,s,r)
|
||||
if love.keypressed then return love.keypressed(b,s,r) end
|
||||
end,
|
||||
keyreleased = function (b,s)
|
||||
if love.keyreleased then return love.keyreleased(b,s) end
|
||||
end,
|
||||
textinput = function (t)
|
||||
if love.textinput then return love.textinput(t) end
|
||||
end,
|
||||
textedited = function (t,s,l)
|
||||
if love.textedited then return love.textedited(t,s,l) end
|
||||
end,
|
||||
mousemoved = function (x,y,dx,dy,t)
|
||||
if love.mousemoved then return love.mousemoved(x,y,dx,dy,t) end
|
||||
end,
|
||||
mousepressed = function (x,y,b,t,c)
|
||||
if love.mousepressed then return love.mousepressed(x,y,b,t,c) end
|
||||
end,
|
||||
mousereleased = function (x,y,b,t,c)
|
||||
if love.mousereleased then return love.mousereleased(x,y,b,t,c) end
|
||||
end,
|
||||
wheelmoved = function (x,y)
|
||||
if love.wheelmoved then return love.wheelmoved(x,y) end
|
||||
end,
|
||||
touchpressed = function (id,x,y,dx,dy,p)
|
||||
if love.touchpressed then return love.touchpressed(id,x,y,dx,dy,p) end
|
||||
end,
|
||||
touchreleased = function (id,x,y,dx,dy,p)
|
||||
if love.touchreleased then return love.touchreleased(id,x,y,dx,dy,p) end
|
||||
end,
|
||||
touchmoved = function (id,x,y,dx,dy,p)
|
||||
if love.touchmoved then return love.touchmoved(id,x,y,dx,dy,p) end
|
||||
end,
|
||||
joystickpressed = function (j,b)
|
||||
if love.joystickpressed then return love.joystickpressed(j,b) end
|
||||
end,
|
||||
joystickreleased = function (j,b)
|
||||
if love.joystickreleased then return love.joystickreleased(j,b) end
|
||||
end,
|
||||
joystickaxis = function (j,a,v)
|
||||
if love.joystickaxis then return love.joystickaxis(j,a,v) end
|
||||
end,
|
||||
joystickhat = function (j,h,v)
|
||||
if love.joystickhat then return love.joystickhat(j,h,v) end
|
||||
end,
|
||||
gamepadpressed = function (j,b)
|
||||
if love.gamepadpressed then return love.gamepadpressed(j,b) end
|
||||
end,
|
||||
gamepadreleased = function (j,b)
|
||||
if love.gamepadreleased then return love.gamepadreleased(j,b) end
|
||||
end,
|
||||
gamepadaxis = function (j,a,v)
|
||||
if love.gamepadaxis then return love.gamepadaxis(j,a,v) end
|
||||
end,
|
||||
joystickadded = function (j)
|
||||
if love.joystickadded then return love.joystickadded(j) end
|
||||
end,
|
||||
joystickremoved = function (j)
|
||||
if love.joystickremoved then return love.joystickremoved(j) end
|
||||
end,
|
||||
focus = function (f)
|
||||
if love.focus then return love.focus(f) end
|
||||
end,
|
||||
mousefocus = function (f)
|
||||
if love.mousefocus then return love.mousefocus(f) end
|
||||
end,
|
||||
visible = function (v)
|
||||
if love.visible then return love.visible(v) end
|
||||
end,
|
||||
quit = function ()
|
||||
return
|
||||
end,
|
||||
threaderror = function (t, err)
|
||||
if love.threaderror then return love.threaderror(t, err) end
|
||||
end,
|
||||
resize = function (w, h)
|
||||
if love.resize then return love.resize(w, h) end
|
||||
end,
|
||||
filedropped = function (f)
|
||||
if love.filedropped then return love.filedropped(f) end
|
||||
end,
|
||||
directorydropped = function (dir)
|
||||
if love.directorydropped then return love.directorydropped(dir) end
|
||||
end,
|
||||
lowmemory = function ()
|
||||
if love.lowmemory then love.lowmemory() end
|
||||
collectgarbage()
|
||||
collectgarbage()
|
||||
end,
|
||||
displayrotated = function (display, orient)
|
||||
if love.displayrotated then return love.displayrotated(display, orient) end
|
||||
end,
|
||||
}, {
|
||||
__index = function(self, name)
|
||||
error("Unknown event: " .. name)
|
||||
end,
|
||||
})
|
||||
|
||||
end
|
||||
|
||||
-----------------------------------------------------------
|
||||
-- Default callbacks.
|
||||
-----------------------------------------------------------
|
||||
|
||||
function love.run()
|
||||
if love.load then love.load(love.parsedGameArguments, love.rawGameArguments) end
|
||||
|
||||
-- We don't want the first frame's dt to include time taken by love.load.
|
||||
if love.timer then love.timer.step() end
|
||||
|
||||
-- Main loop time.
|
||||
return function()
|
||||
-- Process events.
|
||||
if love.event then
|
||||
love.event.pump()
|
||||
for name, a,b,c,d,e,f in love.event.poll() do
|
||||
if name == "quit" then
|
||||
if not love.quit or not love.quit() then
|
||||
return a or 0, b
|
||||
end
|
||||
end
|
||||
love.handlers[name](a,b,c,d,e,f)
|
||||
end
|
||||
end
|
||||
|
||||
-- Update dt, as we'll be passing it to update
|
||||
local dt = love.timer and love.timer.step() or 0
|
||||
|
||||
-- Call update and draw
|
||||
if love.update then love.update(dt) end -- will pass 0 if love.timer is disabled
|
||||
|
||||
if love.graphics and love.graphics.isActive() then
|
||||
love.graphics.origin()
|
||||
love.graphics.clear(love.graphics.getBackgroundColor())
|
||||
|
||||
if love.draw then love.draw() end
|
||||
|
||||
love.graphics.present()
|
||||
end
|
||||
|
||||
if love.timer then love.timer.sleep(0.001) end
|
||||
end
|
||||
end
|
||||
|
||||
local debug, print, tostring, error = debug, print, tostring, error
|
||||
|
||||
function love.threaderror(t, err)
|
||||
error("Thread error ("..tostring(t)..")\n\n"..err, 0)
|
||||
end
|
||||
|
||||
local utf8 = require("utf8")
|
||||
|
||||
local function error_printer(msg, layer)
|
||||
print((debug.traceback("Error: " .. tostring(msg), 1+(layer or 1)):gsub("\n[^\n]+$", "")))
|
||||
end
|
||||
|
||||
function love.errhand(msg)
|
||||
msg = tostring(msg)
|
||||
|
||||
error_printer(msg, 2)
|
||||
|
||||
if not love.window or not love.graphics or not love.event then
|
||||
return
|
||||
end
|
||||
|
||||
if not love.graphics.isCreated() or not love.window.isOpen() then
|
||||
local success, status = pcall(love.window.setMode, 800, 600)
|
||||
if not success or not status then
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
-- Reset state.
|
||||
if love.mouse then
|
||||
love.mouse.setVisible(true)
|
||||
love.mouse.setGrabbed(false)
|
||||
love.mouse.setRelativeMode(false)
|
||||
if love.mouse.isCursorSupported() then
|
||||
love.mouse.setCursor()
|
||||
end
|
||||
end
|
||||
if love.joystick then
|
||||
-- Stop all joystick vibrations.
|
||||
for i,v in ipairs(love.joystick.getJoysticks()) do
|
||||
v:setVibration()
|
||||
end
|
||||
end
|
||||
if love.audio then love.audio.stop() end
|
||||
|
||||
love.graphics.reset()
|
||||
local font = love.graphics.setNewFont(14)
|
||||
|
||||
love.graphics.setColor(1, 1, 1)
|
||||
|
||||
local trace = debug.traceback()
|
||||
|
||||
love.graphics.origin()
|
||||
|
||||
local sanitizedmsg = {}
|
||||
for char in msg:gmatch(utf8.charpattern) do
|
||||
table.insert(sanitizedmsg, char)
|
||||
end
|
||||
sanitizedmsg = table.concat(sanitizedmsg)
|
||||
|
||||
local err = {}
|
||||
|
||||
table.insert(err, "Error\n")
|
||||
table.insert(err, sanitizedmsg)
|
||||
|
||||
if #sanitizedmsg ~= #msg then
|
||||
table.insert(err, "Invalid UTF-8 string in error message.")
|
||||
end
|
||||
|
||||
table.insert(err, "\n")
|
||||
|
||||
for l in trace:gmatch("(.-)\n") do
|
||||
if not l:match("boot.lua") then
|
||||
l = l:gsub("stack traceback:", "Traceback\n")
|
||||
table.insert(err, l)
|
||||
end
|
||||
end
|
||||
|
||||
local p = table.concat(err, "\n")
|
||||
|
||||
p = p:gsub("\t", "")
|
||||
p = p:gsub("%[string \"(.-)\"%]", "%1")
|
||||
|
||||
local function draw()
|
||||
local pos = 70
|
||||
love.graphics.clear(89/255, 157/255, 220/255)
|
||||
love.graphics.printf(p, pos, pos, love.graphics.getWidth() - pos)
|
||||
love.graphics.present()
|
||||
end
|
||||
|
||||
local fullErrorText = p
|
||||
local function copyToClipboard()
|
||||
if not love.system then return end
|
||||
love.system.setClipboardText(fullErrorText)
|
||||
p = p .. "\nCopied to clipboard!"
|
||||
draw()
|
||||
end
|
||||
|
||||
if love.system then
|
||||
p = p .. "\n\nPress Ctrl+C or tap to copy this error"
|
||||
end
|
||||
|
||||
return function()
|
||||
love.event.pump()
|
||||
|
||||
for e, a, b, c in love.event.poll() do
|
||||
if e == "quit" then
|
||||
return 1
|
||||
elseif e == "keypressed" and a == "escape" then
|
||||
return 1
|
||||
elseif e == "keypressed" and a == "c" and love.keyboard.isDown("lctrl", "rctrl") then
|
||||
copyToClipboard()
|
||||
elseif e == "touchpressed" then
|
||||
local name = love.window.getTitle()
|
||||
if #name == 0 or name == "Untitled" then name = "Game" end
|
||||
local buttons = {"OK", "Cancel"}
|
||||
if love.system then
|
||||
buttons[3] = "Copy to clipboard"
|
||||
end
|
||||
local pressed = love.window.showMessageBox("Quit "..name.."?", "", buttons)
|
||||
if pressed == 1 then
|
||||
return 1
|
||||
elseif pressed == 3 then
|
||||
copyToClipboard()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
draw()
|
||||
|
||||
if love.timer then
|
||||
love.timer.sleep(0.1)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- DO NOT REMOVE THE NEXT LINE. It is used to load this file as a C++ string.
|
||||
--)luastring"--"
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "common/version.h"
|
||||
#include "common/deprecation.h"
|
||||
#include "common/runtime.h"
|
||||
#include "modules/window/Window.h"
|
||||
|
||||
#include "love.h"
|
||||
|
||||
@@ -32,6 +33,12 @@
|
||||
|
||||
#ifdef LOVE_WINDOWS
|
||||
#include <windows.h>
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER < 1900)
|
||||
// VS 2013 and earlier doesn't have snprintf
|
||||
#define snprintf sprintf_s
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER < 1900)
|
||||
|
||||
#endif // LOVE_WINDOWS
|
||||
|
||||
#ifdef LOVE_ANDROID
|
||||
@@ -79,9 +86,21 @@ extern "C"
|
||||
# include "audio/Audio.h"
|
||||
#endif
|
||||
|
||||
// Scripts
|
||||
// Scripts.
|
||||
#include "scripts/nogame.lua.h"
|
||||
#include "scripts/boot.lua.h"
|
||||
|
||||
// Put the Lua code directly into a raw string literal.
|
||||
static const char arg_lua[] =
|
||||
#include "arg.lua"
|
||||
;
|
||||
|
||||
static const char callbacks_lua[] =
|
||||
#include "callbacks.lua"
|
||||
;
|
||||
|
||||
static const char boot_lua[] =
|
||||
#include "boot.lua"
|
||||
;
|
||||
|
||||
// All modules define a c-accessible luaopen
|
||||
// so let's make use of those, instead
|
||||
@@ -146,6 +165,8 @@ extern "C"
|
||||
extern int luaopen_love_window(lua_State*);
|
||||
#endif
|
||||
extern int luaopen_love_nogame(lua_State*);
|
||||
extern int luaopen_love_arg(lua_State*);
|
||||
extern int luaopen_love_callbacks(lua_State*);
|
||||
extern int luaopen_love_boot(lua_State*);
|
||||
}
|
||||
|
||||
@@ -208,6 +229,8 @@ static const luaL_Reg modules[] = {
|
||||
{ "love.window", luaopen_love_window },
|
||||
#endif
|
||||
{ "love.nogame", luaopen_love_nogame },
|
||||
{ "love.arg", luaopen_love_arg },
|
||||
{ "love.callbacks", luaopen_love_callbacks },
|
||||
{ "love.boot", luaopen_love_boot },
|
||||
{ 0, 0 }
|
||||
};
|
||||
@@ -545,6 +568,33 @@ int luaopen_love(lua_State *L)
|
||||
love::luax_preload(L, luaopen_luautf8, "utf8");
|
||||
#endif
|
||||
|
||||
#ifdef LOVE_ENABLE_WINDOW
|
||||
// In some environments, LuaJIT is limited to 2GB and LuaJIT sometimes panic when it
|
||||
// reaches OOM and closes the whole program, leaving the user confused about what's
|
||||
// going on.
|
||||
// We can't recover the state at this point, but it's better to inform user that
|
||||
// something very bad happening instead of silently exiting.
|
||||
// Note that this is not foolproof. In some cases, the whole process crashes by
|
||||
// uncaught exception that LuaJIT throws or simply exit as if calling
|
||||
// love.event.quit("not enough memory")
|
||||
lua_atpanic(L, [](lua_State *L)
|
||||
{
|
||||
using namespace love;
|
||||
using namespace love::window;
|
||||
|
||||
char message[128];
|
||||
Window* windowModule = Module::getInstance<Window>(Module::M_WINDOW);
|
||||
|
||||
snprintf(message, sizeof(message), "PANIC: unprotected error in call to Lua API (%s)", lua_tostring(L, -1));
|
||||
|
||||
if (windowModule)
|
||||
windowModule->showMessageBox("Lua Fatal Error", message, Window::MESSAGEBOX_ERROR, windowModule->isOpen());
|
||||
|
||||
fprintf(stderr, "%s\n", message);
|
||||
return 0;
|
||||
});
|
||||
#endif
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -649,13 +699,26 @@ int luaopen_love_nogame(lua_State *L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int luaopen_love_boot(lua_State *L)
|
||||
int luaopen_love_arg(lua_State *L)
|
||||
{
|
||||
if (luaL_loadbuffer(L, (const char *)love::boot_lua, sizeof(love::boot_lua), "boot.lua") == 0)
|
||||
if (luaL_loadbuffer(L, arg_lua, sizeof(arg_lua), "arg.lua") == 0)
|
||||
lua_call(L, 0, 1);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int luaopen_love_callbacks(lua_State *L)
|
||||
{
|
||||
if (luaL_loadbuffer(L, callbacks_lua, sizeof(callbacks_lua), "callbacks.lua") == 0)
|
||||
lua_call(L, 0, 1);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int luaopen_love_boot(lua_State *L)
|
||||
{
|
||||
if (luaL_loadbuffer(L, boot_lua, sizeof(boot_lua), "boot.lua") == 0)
|
||||
lua_call(L, 0, 1);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -1,814 +0,0 @@
|
||||
--[[
|
||||
Copyright (c) 2006-2021 LOVE Development Team
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
--]]
|
||||
|
||||
-- Make sure love exists.
|
||||
local love = require("love")
|
||||
|
||||
-- Used for setup:
|
||||
love.path = {}
|
||||
love.arg = {}
|
||||
|
||||
-- Replace any \ with /.
|
||||
function love.path.normalslashes(p)
|
||||
return p:gsub("\\", "/")
|
||||
end
|
||||
|
||||
-- Makes sure there is a slash at the end
|
||||
-- of a path.
|
||||
function love.path.endslash(p)
|
||||
if p:sub(-1) ~= "/" then
|
||||
return p .. "/"
|
||||
else
|
||||
return p
|
||||
end
|
||||
end
|
||||
|
||||
-- Checks whether a path is absolute or not.
|
||||
function love.path.abs(p)
|
||||
|
||||
local tmp = love.path.normalslashes(p)
|
||||
|
||||
-- Path is absolute if it starts with a "/".
|
||||
if tmp:find("/") == 1 then
|
||||
return true
|
||||
end
|
||||
|
||||
-- Path is absolute if it starts with a
|
||||
-- letter followed by a colon.
|
||||
if tmp:find("%a:") == 1 then
|
||||
return true
|
||||
end
|
||||
|
||||
-- Relative.
|
||||
return false
|
||||
|
||||
end
|
||||
|
||||
-- Converts any path into a full path.
|
||||
function love.path.getFull(p)
|
||||
|
||||
if love.path.abs(p) then
|
||||
return love.path.normalslashes(p)
|
||||
end
|
||||
|
||||
local cwd = love.filesystem.getWorkingDirectory()
|
||||
cwd = love.path.normalslashes(cwd)
|
||||
cwd = love.path.endslash(cwd)
|
||||
|
||||
-- Construct a full path.
|
||||
local full = cwd .. love.path.normalslashes(p)
|
||||
|
||||
-- Remove trailing /., if applicable
|
||||
return full:match("(.-)/%.$") or full
|
||||
end
|
||||
|
||||
-- Returns the leaf of a full path.
|
||||
function love.path.leaf(p)
|
||||
p = love.path.normalslashes(p)
|
||||
|
||||
local a = 1
|
||||
local last = p
|
||||
|
||||
while a do
|
||||
a = p:find("/", a+1)
|
||||
|
||||
if a then
|
||||
last = p:sub(a+1)
|
||||
end
|
||||
end
|
||||
|
||||
return last
|
||||
end
|
||||
|
||||
-- Finds the key in the table with the lowest integral index. The lowest
|
||||
-- will typically the executable, for instance "lua5.1.exe".
|
||||
function love.arg.getLow(a)
|
||||
local m = math.huge
|
||||
for k,v in pairs(a) do
|
||||
if k < m then
|
||||
m = k
|
||||
end
|
||||
end
|
||||
return a[m], m
|
||||
end
|
||||
|
||||
love.arg.options = {
|
||||
console = { a = 0 },
|
||||
fused = {a = 0 },
|
||||
game = { a = 1 }
|
||||
}
|
||||
|
||||
love.arg.optionIndices = {}
|
||||
|
||||
function love.arg.parseOption(m, i)
|
||||
m.set = true
|
||||
|
||||
if m.a > 0 then
|
||||
m.arg = {}
|
||||
for j=i,i+m.a-1 do
|
||||
love.arg.optionIndices[j] = true
|
||||
table.insert(m.arg, arg[j])
|
||||
end
|
||||
end
|
||||
|
||||
return m.a
|
||||
end
|
||||
|
||||
function love.arg.parseOptions(arg)
|
||||
|
||||
local game
|
||||
local argc = #arg
|
||||
|
||||
local i = 1
|
||||
while i <= argc do
|
||||
-- Look for options.
|
||||
local m = arg[i]:match("^%-%-(.*)")
|
||||
|
||||
if m and m ~= "" and love.arg.options[m] and not love.arg.options[m].set then
|
||||
love.arg.optionIndices[i] = true
|
||||
i = i + love.arg.parseOption(love.arg.options[m], i+1)
|
||||
elseif m == "" then -- handle '--' as an option
|
||||
love.arg.optionIndices[i] = true
|
||||
if not game then -- handle '--' followed by game name
|
||||
game = i + 1
|
||||
end
|
||||
break
|
||||
elseif not game then
|
||||
game = i
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
if not love.arg.options.game.set then
|
||||
love.arg.parseOption(love.arg.options.game, game or 0)
|
||||
end
|
||||
end
|
||||
|
||||
-- Returns the arguments that are passed to your game via love.load()
|
||||
-- arguments that were parsed as options are skipped.
|
||||
function love.arg.parseGameArguments(a)
|
||||
local out = {}
|
||||
|
||||
local _, lowindex = love.arg.getLow(a)
|
||||
|
||||
local o = lowindex
|
||||
for i=lowindex, #a do
|
||||
if not love.arg.optionIndices[i] then
|
||||
out[o] = a[i]
|
||||
o = o + 1
|
||||
end
|
||||
end
|
||||
|
||||
return out
|
||||
end
|
||||
|
||||
function love.createhandlers()
|
||||
|
||||
-- Standard callback handlers.
|
||||
love.handlers = setmetatable({
|
||||
keypressed = function (b,s,r)
|
||||
if love.keypressed then return love.keypressed(b,s,r) end
|
||||
end,
|
||||
keyreleased = function (b,s)
|
||||
if love.keyreleased then return love.keyreleased(b,s) end
|
||||
end,
|
||||
textinput = function (t)
|
||||
if love.textinput then return love.textinput(t) end
|
||||
end,
|
||||
textedited = function (t,s,l)
|
||||
if love.textedited then return love.textedited(t,s,l) end
|
||||
end,
|
||||
mousemoved = function (x,y,dx,dy,t)
|
||||
if love.mousemoved then return love.mousemoved(x,y,dx,dy,t) end
|
||||
end,
|
||||
mousepressed = function (x,y,b,t,c)
|
||||
if love.mousepressed then return love.mousepressed(x,y,b,t,c) end
|
||||
end,
|
||||
mousereleased = function (x,y,b,t,c)
|
||||
if love.mousereleased then return love.mousereleased(x,y,b,t,c) end
|
||||
end,
|
||||
wheelmoved = function (x,y)
|
||||
if love.wheelmoved then return love.wheelmoved(x,y) end
|
||||
end,
|
||||
touchpressed = function (id,x,y,dx,dy,p)
|
||||
if love.touchpressed then return love.touchpressed(id,x,y,dx,dy,p) end
|
||||
end,
|
||||
touchreleased = function (id,x,y,dx,dy,p)
|
||||
if love.touchreleased then return love.touchreleased(id,x,y,dx,dy,p) end
|
||||
end,
|
||||
touchmoved = function (id,x,y,dx,dy,p)
|
||||
if love.touchmoved then return love.touchmoved(id,x,y,dx,dy,p) end
|
||||
end,
|
||||
joystickpressed = function (j,b)
|
||||
if love.joystickpressed then return love.joystickpressed(j,b) end
|
||||
end,
|
||||
joystickreleased = function (j,b)
|
||||
if love.joystickreleased then return love.joystickreleased(j,b) end
|
||||
end,
|
||||
joystickaxis = function (j,a,v)
|
||||
if love.joystickaxis then return love.joystickaxis(j,a,v) end
|
||||
end,
|
||||
joystickhat = function (j,h,v)
|
||||
if love.joystickhat then return love.joystickhat(j,h,v) end
|
||||
end,
|
||||
gamepadpressed = function (j,b)
|
||||
if love.gamepadpressed then return love.gamepadpressed(j,b) end
|
||||
end,
|
||||
gamepadreleased = function (j,b)
|
||||
if love.gamepadreleased then return love.gamepadreleased(j,b) end
|
||||
end,
|
||||
gamepadaxis = function (j,a,v)
|
||||
if love.gamepadaxis then return love.gamepadaxis(j,a,v) end
|
||||
end,
|
||||
joystickadded = function (j)
|
||||
if love.joystickadded then return love.joystickadded(j) end
|
||||
end,
|
||||
joystickremoved = function (j)
|
||||
if love.joystickremoved then return love.joystickremoved(j) end
|
||||
end,
|
||||
focus = function (f)
|
||||
if love.focus then return love.focus(f) end
|
||||
end,
|
||||
mousefocus = function (f)
|
||||
if love.mousefocus then return love.mousefocus(f) end
|
||||
end,
|
||||
visible = function (v)
|
||||
if love.visible then return love.visible(v) end
|
||||
end,
|
||||
quit = function ()
|
||||
return
|
||||
end,
|
||||
threaderror = function (t, err)
|
||||
if love.threaderror then return love.threaderror(t, err) end
|
||||
end,
|
||||
resize = function (w, h)
|
||||
if love.resize then return love.resize(w, h) end
|
||||
end,
|
||||
filedropped = function (f)
|
||||
if love.filedropped then return love.filedropped(f) end
|
||||
end,
|
||||
directorydropped = function (dir)
|
||||
if love.directorydropped then return love.directorydropped(dir) end
|
||||
end,
|
||||
lowmemory = function ()
|
||||
if love.lowmemory then love.lowmemory() end
|
||||
collectgarbage()
|
||||
collectgarbage()
|
||||
end,
|
||||
displayrotated = function (display, orient)
|
||||
if love.displayrotated then return love.displayrotated(display, orient) end
|
||||
end,
|
||||
}, {
|
||||
__index = function(self, name)
|
||||
error("Unknown event: " .. name)
|
||||
end,
|
||||
})
|
||||
|
||||
end
|
||||
|
||||
local function uridecode(s)
|
||||
return s:gsub("%%%x%x", function(str)
|
||||
return string.char(tonumber(str:sub(2), 16))
|
||||
end)
|
||||
end
|
||||
|
||||
local no_game_code = false
|
||||
local invalid_game_path = nil
|
||||
|
||||
-- This can't be overridden.
|
||||
function love.boot()
|
||||
|
||||
-- This is absolutely needed.
|
||||
require("love.filesystem")
|
||||
|
||||
love.rawGameArguments = arg
|
||||
|
||||
local arg0 = love.arg.getLow(love.rawGameArguments)
|
||||
love.filesystem.init(arg0)
|
||||
|
||||
local exepath = love.filesystem.getExecutablePath()
|
||||
if #exepath == 0 then
|
||||
-- This shouldn't happen, but just in case we'll fall back to arg0.
|
||||
exepath = arg0
|
||||
end
|
||||
|
||||
no_game_code = false
|
||||
invalid_game_path = nil
|
||||
|
||||
-- Is this one of those fancy "fused" games?
|
||||
local can_has_game = pcall(love.filesystem.setSource, exepath)
|
||||
|
||||
-- It's a fused game, don't parse --game argument
|
||||
if can_has_game then
|
||||
love.arg.options.game.set = true
|
||||
end
|
||||
|
||||
-- Parse options now that we know which options we're looking for.
|
||||
love.arg.parseOptions(love.rawGameArguments)
|
||||
|
||||
-- parseGameArguments can only be called after parseOptions.
|
||||
love.parsedGameArguments = love.arg.parseGameArguments(love.rawGameArguments)
|
||||
|
||||
local o = love.arg.options
|
||||
|
||||
local is_fused_game = can_has_game or love.arg.options.fused.set
|
||||
|
||||
love.filesystem.setFused(is_fused_game)
|
||||
|
||||
love.setDeprecationOutput(not love.filesystem.isFused())
|
||||
|
||||
local identity = ""
|
||||
if not can_has_game and o.game.set and o.game.arg[1] then
|
||||
local nouri = o.game.arg[1]
|
||||
|
||||
if nouri:sub(1, 7) == "file://" then
|
||||
nouri = uridecode(nouri:sub(8))
|
||||
end
|
||||
|
||||
local full_source = love.path.getFull(nouri)
|
||||
can_has_game = pcall(love.filesystem.setSource, full_source)
|
||||
|
||||
if not can_has_game then
|
||||
invalid_game_path = full_source
|
||||
end
|
||||
|
||||
-- Use the name of the source .love as the identity for now.
|
||||
identity = love.path.leaf(full_source)
|
||||
else
|
||||
-- Use the name of the exe as the identity for now.
|
||||
identity = love.path.leaf(exepath)
|
||||
end
|
||||
|
||||
-- Try to use the archive containing main.lua as the identity name. It
|
||||
-- might not be available, in which case the fallbacks above are used.
|
||||
local realdir = love.filesystem.getRealDirectory("main.lua")
|
||||
if realdir then
|
||||
identity = love.path.leaf(realdir)
|
||||
end
|
||||
|
||||
identity = identity:gsub("^([%.]+)", "") -- strip leading "."'s
|
||||
identity = identity:gsub("%.([^%.]+)$", "") -- strip extension
|
||||
identity = identity:gsub("%.", "_") -- replace remaining "."'s with "_"
|
||||
identity = #identity > 0 and identity or "lovegame"
|
||||
|
||||
-- When conf.lua is initially loaded, the main source should be checked
|
||||
-- before the save directory (the identity should be appended.)
|
||||
pcall(love.filesystem.setIdentity, identity, true)
|
||||
|
||||
if can_has_game and not (love.filesystem.getInfo("main.lua") or love.filesystem.getInfo("conf.lua")) then
|
||||
no_game_code = true
|
||||
end
|
||||
|
||||
if not can_has_game then
|
||||
local nogame = require("love.nogame")
|
||||
nogame()
|
||||
end
|
||||
end
|
||||
|
||||
function love.init()
|
||||
|
||||
-- Create default configuration settings.
|
||||
-- NOTE: Adding a new module to the modules list
|
||||
-- will NOT make it load, see below.
|
||||
local c = {
|
||||
title = "Untitled",
|
||||
version = love._version,
|
||||
window = {
|
||||
width = 800,
|
||||
height = 600,
|
||||
x = nil,
|
||||
y = nil,
|
||||
minwidth = 1,
|
||||
minheight = 1,
|
||||
fullscreen = false,
|
||||
fullscreentype = "desktop",
|
||||
display = 1,
|
||||
vsync = 1,
|
||||
msaa = 0,
|
||||
borderless = false,
|
||||
resizable = false,
|
||||
centered = true,
|
||||
usedpiscale = true,
|
||||
},
|
||||
modules = {
|
||||
data = true,
|
||||
event = true,
|
||||
keyboard = true,
|
||||
mouse = true,
|
||||
timer = true,
|
||||
joystick = true,
|
||||
touch = true,
|
||||
image = true,
|
||||
graphics = true,
|
||||
audio = true,
|
||||
math = true,
|
||||
physics = true,
|
||||
sound = true,
|
||||
system = true,
|
||||
font = true,
|
||||
thread = true,
|
||||
window = true,
|
||||
video = true,
|
||||
},
|
||||
audio = {
|
||||
mixwithsystem = true, -- Only relevant for Android / iOS.
|
||||
mic = false, -- Only relevant for Android.
|
||||
},
|
||||
console = false, -- Only relevant for windows.
|
||||
identity = false,
|
||||
appendidentity = false,
|
||||
externalstorage = false, -- Only relevant for Android.
|
||||
accelerometerjoystick = true, -- Only relevant for Android / iOS.
|
||||
gammacorrect = false,
|
||||
highdpi = false,
|
||||
}
|
||||
|
||||
-- Console hack, part 1.
|
||||
local openedconsole = false
|
||||
if love.arg.options.console.set and love._openConsole then
|
||||
love._openConsole()
|
||||
openedconsole = true
|
||||
end
|
||||
|
||||
-- If config file exists, load it and allow it to update config table.
|
||||
local confok, conferr
|
||||
if (not love.conf) and love.filesystem and love.filesystem.getInfo("conf.lua") then
|
||||
confok, conferr = pcall(require, "conf")
|
||||
end
|
||||
|
||||
-- Yes, conf.lua might not exist, but there are other ways of making
|
||||
-- love.conf appear, so we should check for it anyway.
|
||||
if love.conf then
|
||||
confok, conferr = pcall(love.conf, c)
|
||||
-- If love.conf errors, we'll trigger the error after loading modules so
|
||||
-- the error message can be displayed in the window.
|
||||
end
|
||||
|
||||
-- Console hack, part 2.
|
||||
if c.console and love._openConsole and not openedconsole then
|
||||
love._openConsole()
|
||||
end
|
||||
|
||||
-- Hack for disabling accelerometer-as-joystick on Android / iOS.
|
||||
if love._setAccelerometerAsJoystick then
|
||||
love._setAccelerometerAsJoystick(c.accelerometerjoystick)
|
||||
end
|
||||
|
||||
if love._setGammaCorrect then
|
||||
love._setGammaCorrect(c.gammacorrect)
|
||||
end
|
||||
|
||||
if love._setHighDPIAllowed then
|
||||
love._setHighDPIAllowed(c.highdpi)
|
||||
end
|
||||
|
||||
if love._setAudioMixWithSystem then
|
||||
if c.audio and c.audio.mixwithsystem ~= nil then
|
||||
love._setAudioMixWithSystem(c.audio.mixwithsystem)
|
||||
end
|
||||
end
|
||||
|
||||
if love._requestRecordingPermission then
|
||||
love._requestRecordingPermission(c.audio and c.audio.mic)
|
||||
end
|
||||
|
||||
-- Gets desired modules.
|
||||
for k,v in ipairs{
|
||||
"data",
|
||||
"thread",
|
||||
"timer",
|
||||
"event",
|
||||
"keyboard",
|
||||
"joystick",
|
||||
"mouse",
|
||||
"touch",
|
||||
"sound",
|
||||
"system",
|
||||
"audio",
|
||||
"image",
|
||||
"video",
|
||||
"font",
|
||||
"window",
|
||||
"graphics",
|
||||
"math",
|
||||
"physics",
|
||||
} do
|
||||
if c.modules[v] then
|
||||
require("love." .. v)
|
||||
end
|
||||
end
|
||||
|
||||
if love.event then
|
||||
love.createhandlers()
|
||||
end
|
||||
|
||||
-- Check the version
|
||||
c.version = tostring(c.version)
|
||||
if not love.isVersionCompatible(c.version) then
|
||||
local major, minor, revision = c.version:match("^(%d+)%.(%d+)%.(%d+)$")
|
||||
if (not major or not minor or not revision) or (major ~= love._version_major and minor ~= love._version_minor) then
|
||||
local msg = ("This game indicates it was made for version '%s' of LOVE.\n"..
|
||||
"It may not be compatible with the running version (%s)."):format(c.version, love._version)
|
||||
|
||||
print(msg)
|
||||
|
||||
if love.window then
|
||||
love.window.showMessageBox("Compatibility Warning", msg, "warning")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not confok and conferr then
|
||||
error(conferr)
|
||||
end
|
||||
|
||||
-- Setup window here.
|
||||
if c.window and c.modules.window then
|
||||
love.window.setTitle(c.window.title or c.title)
|
||||
assert(love.window.setMode(c.window.width, c.window.height,
|
||||
{
|
||||
fullscreen = c.window.fullscreen,
|
||||
fullscreentype = c.window.fullscreentype,
|
||||
vsync = c.window.vsync,
|
||||
msaa = c.window.msaa,
|
||||
stencil = c.window.stencil,
|
||||
depth = c.window.depth,
|
||||
resizable = c.window.resizable,
|
||||
minwidth = c.window.minwidth,
|
||||
minheight = c.window.minheight,
|
||||
borderless = c.window.borderless,
|
||||
centered = c.window.centered,
|
||||
display = c.window.display,
|
||||
highdpi = c.window.highdpi, -- deprecated
|
||||
usedpiscale = c.window.usedpiscale,
|
||||
x = c.window.x,
|
||||
y = c.window.y,
|
||||
}), "Could not set window mode")
|
||||
if c.window.icon then
|
||||
assert(love.image, "If an icon is set in love.conf, love.image must be loaded!")
|
||||
love.window.setIcon(love.image.newImageData(c.window.icon))
|
||||
end
|
||||
end
|
||||
|
||||
-- Our first timestep, because window creation can take some time
|
||||
if love.timer then
|
||||
love.timer.step()
|
||||
end
|
||||
|
||||
if love.filesystem then
|
||||
love.filesystem._setAndroidSaveExternal(c.externalstorage)
|
||||
love.filesystem.setIdentity(c.identity or love.filesystem.getIdentity(), c.appendidentity)
|
||||
if love.filesystem.getInfo("main.lua") then
|
||||
require("main")
|
||||
end
|
||||
end
|
||||
|
||||
if no_game_code then
|
||||
error("No code to run\nYour game might be packaged incorrectly.\nMake sure main.lua is at the top level of the zip.")
|
||||
elseif invalid_game_path then
|
||||
error("Cannot load game at path '" .. invalid_game_path .. "'.\nMake sure a folder exists at the specified path.")
|
||||
end
|
||||
end
|
||||
|
||||
-----------------------------------------------------------
|
||||
-- Default callbacks.
|
||||
-----------------------------------------------------------
|
||||
|
||||
function love.run()
|
||||
if love.load then love.load(love.parsedGameArguments, love.rawGameArguments) end
|
||||
|
||||
-- We don't want the first frame's dt to include time taken by love.load.
|
||||
if love.timer then love.timer.step() end
|
||||
|
||||
-- Main loop time.
|
||||
return function()
|
||||
-- Process events.
|
||||
if love.event then
|
||||
love.event.pump()
|
||||
for name, a,b,c,d,e,f in love.event.poll() do
|
||||
if name == "quit" then
|
||||
if not love.quit or not love.quit() then
|
||||
return a or 0, b
|
||||
end
|
||||
end
|
||||
love.handlers[name](a,b,c,d,e,f)
|
||||
end
|
||||
end
|
||||
|
||||
-- Update dt, as we'll be passing it to update
|
||||
local dt = love.timer and love.timer.step() or 0
|
||||
|
||||
-- Call update and draw
|
||||
if love.update then love.update(dt) end -- will pass 0 if love.timer is disabled
|
||||
|
||||
if love.graphics and love.graphics.isActive() then
|
||||
love.graphics.origin()
|
||||
love.graphics.clear(love.graphics.getBackgroundColor())
|
||||
|
||||
if love.draw then love.draw() end
|
||||
|
||||
love.graphics.present()
|
||||
end
|
||||
|
||||
if love.timer then love.timer.sleep(0.001) end
|
||||
end
|
||||
end
|
||||
|
||||
local debug, print, error = debug, print, error
|
||||
|
||||
function love.threaderror(t, err)
|
||||
error("Thread error ("..tostring(t)..")\n\n"..err, 0)
|
||||
end
|
||||
|
||||
local utf8 = require("utf8")
|
||||
|
||||
local function error_printer(msg, layer)
|
||||
print((debug.traceback("Error: " .. tostring(msg), 1+(layer or 1)):gsub("\n[^\n]+$", "")))
|
||||
end
|
||||
|
||||
function love.errhand(msg)
|
||||
msg = tostring(msg)
|
||||
|
||||
error_printer(msg, 2)
|
||||
|
||||
if not love.window or not love.graphics or not love.event then
|
||||
return
|
||||
end
|
||||
|
||||
if not love.graphics.isCreated() or not love.window.isOpen() then
|
||||
local success, status = pcall(love.window.setMode, 800, 600)
|
||||
if not success or not status then
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
-- Reset state.
|
||||
if love.mouse then
|
||||
love.mouse.setVisible(true)
|
||||
love.mouse.setGrabbed(false)
|
||||
love.mouse.setRelativeMode(false)
|
||||
if love.mouse.isCursorSupported() then
|
||||
love.mouse.setCursor()
|
||||
end
|
||||
end
|
||||
if love.joystick then
|
||||
-- Stop all joystick vibrations.
|
||||
for i,v in ipairs(love.joystick.getJoysticks()) do
|
||||
v:setVibration()
|
||||
end
|
||||
end
|
||||
if love.audio then love.audio.stop() end
|
||||
|
||||
love.graphics.reset()
|
||||
local font = love.graphics.setNewFont(14)
|
||||
|
||||
love.graphics.setColor(1, 1, 1)
|
||||
|
||||
local trace = debug.traceback()
|
||||
|
||||
love.graphics.origin()
|
||||
|
||||
local sanitizedmsg = {}
|
||||
for char in msg:gmatch(utf8.charpattern) do
|
||||
table.insert(sanitizedmsg, char)
|
||||
end
|
||||
sanitizedmsg = table.concat(sanitizedmsg)
|
||||
|
||||
local err = {}
|
||||
|
||||
table.insert(err, "Error\n")
|
||||
table.insert(err, sanitizedmsg)
|
||||
|
||||
if #sanitizedmsg ~= #msg then
|
||||
table.insert(err, "Invalid UTF-8 string in error message.")
|
||||
end
|
||||
|
||||
table.insert(err, "\n")
|
||||
|
||||
for l in trace:gmatch("(.-)\n") do
|
||||
if not l:match("boot.lua") then
|
||||
l = l:gsub("stack traceback:", "Traceback\n")
|
||||
table.insert(err, l)
|
||||
end
|
||||
end
|
||||
|
||||
local p = table.concat(err, "\n")
|
||||
|
||||
p = p:gsub("\t", "")
|
||||
p = p:gsub("%[string \"(.-)\"%]", "%1")
|
||||
|
||||
local function draw()
|
||||
local pos = 70
|
||||
love.graphics.clear(89/255, 157/255, 220/255)
|
||||
love.graphics.printf(p, pos, pos, love.graphics.getWidth() - pos)
|
||||
love.graphics.present()
|
||||
end
|
||||
|
||||
local fullErrorText = p
|
||||
local function copyToClipboard()
|
||||
if not love.system then return end
|
||||
love.system.setClipboardText(fullErrorText)
|
||||
p = p .. "\nCopied to clipboard!"
|
||||
draw()
|
||||
end
|
||||
|
||||
if love.system then
|
||||
p = p .. "\n\nPress Ctrl+C or tap to copy this error"
|
||||
end
|
||||
|
||||
return function()
|
||||
love.event.pump()
|
||||
|
||||
for e, a, b, c in love.event.poll() do
|
||||
if e == "quit" then
|
||||
return 1
|
||||
elseif e == "keypressed" and a == "escape" then
|
||||
return 1
|
||||
elseif e == "keypressed" and a == "c" and love.keyboard.isDown("lctrl", "rctrl") then
|
||||
copyToClipboard()
|
||||
elseif e == "touchpressed" then
|
||||
local name = love.window.getTitle()
|
||||
if #name == 0 or name == "Untitled" then name = "Game" end
|
||||
local buttons = {"OK", "Cancel"}
|
||||
if love.system then
|
||||
buttons[3] = "Copy to clipboard"
|
||||
end
|
||||
local pressed = love.window.showMessageBox("Quit "..name.."?", "", buttons)
|
||||
if pressed == 1 then
|
||||
return 1
|
||||
elseif pressed == 3 then
|
||||
copyToClipboard()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
draw()
|
||||
|
||||
if love.timer then
|
||||
love.timer.sleep(0.1)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-----------------------------------------------------------
|
||||
-- The root of all calls.
|
||||
-----------------------------------------------------------
|
||||
|
||||
return function()
|
||||
local func
|
||||
local inerror = false
|
||||
|
||||
local function deferErrhand(...)
|
||||
local errhand = love.errorhandler or love.errhand
|
||||
local handler = (not inerror and errhand) or error_printer
|
||||
inerror = true
|
||||
func = handler(...)
|
||||
end
|
||||
|
||||
local function earlyinit()
|
||||
-- If love.boot fails, return 1 and finish immediately
|
||||
local result = xpcall(love.boot, error_printer)
|
||||
if not result then return 1 end
|
||||
|
||||
-- If love.init or love.run fails, don't return a value,
|
||||
-- as we want the error handler to take over
|
||||
result = xpcall(love.init, deferErrhand)
|
||||
if not result then return end
|
||||
|
||||
-- NOTE: We can't assign to func directly, as we'd
|
||||
-- overwrite the result of deferErrhand with nil on error
|
||||
local main
|
||||
result, main = xpcall(love.run, deferErrhand)
|
||||
if result then
|
||||
func = main
|
||||
end
|
||||
end
|
||||
|
||||
func = earlyinit
|
||||
|
||||
while func do
|
||||
local _, retval, restartvalue = xpcall(func, deferErrhand)
|
||||
if retval then return retval, restartvalue end
|
||||
coroutine.yield()
|
||||
end
|
||||
|
||||
return 1
|
||||
end
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user