mirror of
git://git.tartarus.org/simon/puzzles.git
synced 2025-04-21 08:01:30 -07:00
Limit maximum grid size in Loopy
Every grid shape has its own limit, so this involved adding a new interface between loopy.c and grid.c. The limits are based on ensuring that the co-ordinate system of the grid doesn't overflow INT_MAX and neither do the lengths of the face and dot arrays. Though now I come to look at it I think the actual limits of grid.c are much lower. Hmm.
This commit is contained in:
206
grid.c
206
grid.c
@ -11,8 +11,9 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <math.h>
|
|
||||||
#include <float.h>
|
#include <float.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
#include "puzzles.h"
|
#include "puzzles.h"
|
||||||
#include "tree234.h"
|
#include "tree234.h"
|
||||||
@ -1388,6 +1389,15 @@ void grid_find_incentre(grid_face *f)
|
|||||||
|
|
||||||
#define SQUARE_TILESIZE 20
|
#define SQUARE_TILESIZE 20
|
||||||
|
|
||||||
|
static const char *grid_validate_params_square(int width, int height)
|
||||||
|
{
|
||||||
|
if (width > INT_MAX / SQUARE_TILESIZE || /* xextent */
|
||||||
|
height > INT_MAX / SQUARE_TILESIZE || /* yextent */
|
||||||
|
width + 1 > INT_MAX / (height + 1)) /* max_dots */
|
||||||
|
return "Grid size must not be unreasonably large";
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static void grid_size_square(int width, int height,
|
static void grid_size_square(int width, int height,
|
||||||
int *tilesize, int *xextent, int *yextent)
|
int *tilesize, int *xextent, int *yextent)
|
||||||
{
|
{
|
||||||
@ -1450,6 +1460,18 @@ static grid *grid_new_square(int width, int height, const char *desc)
|
|||||||
#define HONEY_A 15
|
#define HONEY_A 15
|
||||||
#define HONEY_B 26
|
#define HONEY_B 26
|
||||||
|
|
||||||
|
static const char *grid_validate_params_honeycomb(int width, int height)
|
||||||
|
{
|
||||||
|
int a = HONEY_A;
|
||||||
|
int b = HONEY_B;
|
||||||
|
|
||||||
|
if (width - 1 > (INT_MAX - 4*a) / (3 * a) || /* xextent */
|
||||||
|
height - 1 > (INT_MAX - 3*b) / (2 * b) || /* yextent */
|
||||||
|
width + 1 > INT_MAX / 2 / (height + 1)) /* max_dots */
|
||||||
|
return "Grid size must not be unreasonably large";
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static void grid_size_honeycomb(int width, int height,
|
static void grid_size_honeycomb(int width, int height,
|
||||||
int *tilesize, int *xextent, int *yextent)
|
int *tilesize, int *xextent, int *yextent)
|
||||||
{
|
{
|
||||||
@ -1519,6 +1541,18 @@ static grid *grid_new_honeycomb(int width, int height, const char *desc)
|
|||||||
#define TRIANGLE_VEC_X 15
|
#define TRIANGLE_VEC_X 15
|
||||||
#define TRIANGLE_VEC_Y 26
|
#define TRIANGLE_VEC_Y 26
|
||||||
|
|
||||||
|
static const char *grid_validate_params_triangular(int width, int height)
|
||||||
|
{
|
||||||
|
int vec_x = TRIANGLE_VEC_X;
|
||||||
|
int vec_y = TRIANGLE_VEC_Y;
|
||||||
|
|
||||||
|
if (width > INT_MAX / (2 * vec_x) - 1 || /* xextent */
|
||||||
|
height > INT_MAX / vec_y || /* yextent */
|
||||||
|
width + 1 > INT_MAX / 4 / (height + 1)) /* max_dots */
|
||||||
|
return "Grid size must not be unreasonably large";
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static void grid_size_triangular(int width, int height,
|
static void grid_size_triangular(int width, int height,
|
||||||
int *tilesize, int *xextent, int *yextent)
|
int *tilesize, int *xextent, int *yextent)
|
||||||
{
|
{
|
||||||
@ -1716,6 +1750,19 @@ static grid *grid_new_triangular(int width, int height, const char *desc)
|
|||||||
#define SNUBSQUARE_A 15
|
#define SNUBSQUARE_A 15
|
||||||
#define SNUBSQUARE_B 26
|
#define SNUBSQUARE_B 26
|
||||||
|
|
||||||
|
static const char *grid_validate_params_snubsquare(int width, int height)
|
||||||
|
{
|
||||||
|
int a = SNUBSQUARE_A;
|
||||||
|
int b = SNUBSQUARE_B;
|
||||||
|
|
||||||
|
if (width-1 > (INT_MAX - (a + b)) / (a+b) || /* xextent */
|
||||||
|
height > (INT_MAX - (a + b)) / (a+b) || /* yextent */
|
||||||
|
width > INT_MAX / 3 / height || /* max_faces */
|
||||||
|
width + 1 > INT_MAX / 2 / (height + 1)) /* max_dots */
|
||||||
|
return "Grid size must not be unreasonably large";
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static void grid_size_snubsquare(int width, int height,
|
static void grid_size_snubsquare(int width, int height,
|
||||||
int *tilesize, int *xextent, int *yextent)
|
int *tilesize, int *xextent, int *yextent)
|
||||||
{
|
{
|
||||||
@ -1830,6 +1877,18 @@ static grid *grid_new_snubsquare(int width, int height, const char *desc)
|
|||||||
#define CAIRO_A 14
|
#define CAIRO_A 14
|
||||||
#define CAIRO_B 31
|
#define CAIRO_B 31
|
||||||
|
|
||||||
|
static const char *grid_validate_params_cairo(int width, int height)
|
||||||
|
{
|
||||||
|
int b = CAIRO_B; /* a unused in determining grid size. */
|
||||||
|
|
||||||
|
if (width - 1 > (INT_MAX - 2*b) / (2*b) || /* xextent */
|
||||||
|
height - 1 > (INT_MAX - 2*b) / (2*b) || /* yextent */
|
||||||
|
width > INT_MAX / 2 / height || /* max_faces */
|
||||||
|
width + 1 > INT_MAX / 3 / (height + 1)) /* max_dots */
|
||||||
|
return "Grid size must not be unreasonably large";
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static void grid_size_cairo(int width, int height,
|
static void grid_size_cairo(int width, int height,
|
||||||
int *tilesize, int *xextent, int *yextent)
|
int *tilesize, int *xextent, int *yextent)
|
||||||
{
|
{
|
||||||
@ -1936,6 +1995,18 @@ static grid *grid_new_cairo(int width, int height, const char *desc)
|
|||||||
#define GREATHEX_A 15
|
#define GREATHEX_A 15
|
||||||
#define GREATHEX_B 26
|
#define GREATHEX_B 26
|
||||||
|
|
||||||
|
static const char *grid_validate_params_greathexagonal(int width, int height)
|
||||||
|
{
|
||||||
|
int a = GREATHEX_A;
|
||||||
|
int b = GREATHEX_B;
|
||||||
|
|
||||||
|
if (width-1 > (INT_MAX - 4*a) / (3*a + b) || /* xextent */
|
||||||
|
height-1 > (INT_MAX - (3*b + a)) / (2*a + 2*b) || /* yextent */
|
||||||
|
width + 1 > INT_MAX / 6 / (height + 1)) /* max_faces */
|
||||||
|
return "Grid size must not be unreasonably large";
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static void grid_size_greathexagonal(int width, int height,
|
static void grid_size_greathexagonal(int width, int height,
|
||||||
int *tilesize, int *xextent, int *yextent)
|
int *tilesize, int *xextent, int *yextent)
|
||||||
{
|
{
|
||||||
@ -2066,6 +2137,18 @@ static grid *grid_new_greathexagonal(int width, int height, const char *desc)
|
|||||||
#define KAGOME_A 15
|
#define KAGOME_A 15
|
||||||
#define KAGOME_B 26
|
#define KAGOME_B 26
|
||||||
|
|
||||||
|
static const char *grid_validate_params_kagome(int width, int height)
|
||||||
|
{
|
||||||
|
int a = KAGOME_A;
|
||||||
|
int b = KAGOME_B;
|
||||||
|
|
||||||
|
if (width-1 > (INT_MAX - 6*a) / (4*a) || /* xextent */
|
||||||
|
height-1 > (INT_MAX - 2*b) / (2*b) || /* yextent */
|
||||||
|
width + 1 > INT_MAX / 6 / (height + 1)) /* max_faces */
|
||||||
|
return "Grid size must not be unreasonably large";
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static void grid_size_kagome(int width, int height,
|
static void grid_size_kagome(int width, int height,
|
||||||
int *tilesize, int *xextent, int *yextent)
|
int *tilesize, int *xextent, int *yextent)
|
||||||
{
|
{
|
||||||
@ -2162,6 +2245,18 @@ static grid *grid_new_kagome(int width, int height, const char *desc)
|
|||||||
#define OCTAGONAL_A 29
|
#define OCTAGONAL_A 29
|
||||||
#define OCTAGONAL_B 41
|
#define OCTAGONAL_B 41
|
||||||
|
|
||||||
|
static const char *grid_validate_params_octagonal(int width, int height)
|
||||||
|
{
|
||||||
|
int a = OCTAGONAL_A;
|
||||||
|
int b = OCTAGONAL_B;
|
||||||
|
|
||||||
|
if (width > INT_MAX / (2*a + b) || /* xextent */
|
||||||
|
height > INT_MAX / (2*a + b) || /* yextent */
|
||||||
|
height + 1 > INT_MAX / 4 / (width + 1)) /* max_dots */
|
||||||
|
return "Grid size must not be unreasonably large";
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static void grid_size_octagonal(int width, int height,
|
static void grid_size_octagonal(int width, int height,
|
||||||
int *tilesize, int *xextent, int *yextent)
|
int *tilesize, int *xextent, int *yextent)
|
||||||
{
|
{
|
||||||
@ -2245,6 +2340,18 @@ static grid *grid_new_octagonal(int width, int height, const char *desc)
|
|||||||
#define KITE_A 15
|
#define KITE_A 15
|
||||||
#define KITE_B 26
|
#define KITE_B 26
|
||||||
|
|
||||||
|
static const char *grid_validate_params_kites(int width, int height)
|
||||||
|
{
|
||||||
|
int a = KITE_A;
|
||||||
|
int b = KITE_B;
|
||||||
|
|
||||||
|
if (width > (INT_MAX - 2*b) / (4*b) || /* xextent */
|
||||||
|
height - 1 > (INT_MAX - 8*a) / (6*a) || /* yextent */
|
||||||
|
width + 1 > INT_MAX / 6 / (height + 1)) /* max_dots */
|
||||||
|
return "Grid size must not be unreasonably large";
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static void grid_size_kites(int width, int height,
|
static void grid_size_kites(int width, int height,
|
||||||
int *tilesize, int *xextent, int *yextent)
|
int *tilesize, int *xextent, int *yextent)
|
||||||
{
|
{
|
||||||
@ -2367,6 +2474,20 @@ static grid *grid_new_kites(int width, int height, const char *desc)
|
|||||||
#define FLORET_PX 75
|
#define FLORET_PX 75
|
||||||
#define FLORET_PY -26
|
#define FLORET_PY -26
|
||||||
|
|
||||||
|
static const char *grid_validate_params_floret(int width, int height)
|
||||||
|
{
|
||||||
|
int px = FLORET_PX, py = FLORET_PY; /* |( 75, -26)| = 79.43 */
|
||||||
|
int qx = 4*px/5, qy = -py*2; /* |( 60, 52)| = 79.40 */
|
||||||
|
int ry = qy-py;
|
||||||
|
/* rx unused in determining grid size. */
|
||||||
|
|
||||||
|
if (width - 1 > (INT_MAX - (4*qx + 2*px)) / ((6*px+3*qx)/2) ||/* xextent */
|
||||||
|
height - 1 > (INT_MAX - (4*qy + 2*ry)) / (5*qy-4*py) || /* yextent */
|
||||||
|
width + 1 > INT_MAX / 9 / (height + 1)) /* max_dots */
|
||||||
|
return "Grid size must not be unreasonably large";
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static void grid_size_floret(int width, int height,
|
static void grid_size_floret(int width, int height,
|
||||||
int *tilesize, int *xextent, int *yextent)
|
int *tilesize, int *xextent, int *yextent)
|
||||||
{
|
{
|
||||||
@ -2476,6 +2597,18 @@ static grid *grid_new_floret(int width, int height, const char *desc)
|
|||||||
#define DODEC_A 15
|
#define DODEC_A 15
|
||||||
#define DODEC_B 26
|
#define DODEC_B 26
|
||||||
|
|
||||||
|
static const char *grid_validate_params_dodecagonal(int width, int height)
|
||||||
|
{
|
||||||
|
int a = DODEC_A;
|
||||||
|
int b = DODEC_B;
|
||||||
|
|
||||||
|
if (width - 1 > (INT_MAX - 3*(2*a + b)) / (4*a + 2*b) || /* xextent */
|
||||||
|
height - 1 > (INT_MAX - 2*(2*a + b)) / (3*a + 2*b) || /* yextent */
|
||||||
|
width > INT_MAX / 14 / height) /* max_dots */
|
||||||
|
return "Grid size must not be unreasonably large";
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static void grid_size_dodecagonal(int width, int height,
|
static void grid_size_dodecagonal(int width, int height,
|
||||||
int *tilesize, int *xextent, int *yextent)
|
int *tilesize, int *xextent, int *yextent)
|
||||||
{
|
{
|
||||||
@ -2556,6 +2689,18 @@ static grid *grid_new_dodecagonal(int width, int height, const char *desc)
|
|||||||
return g;
|
return g;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *grid_validate_params_greatdodecagonal(int width, int height)
|
||||||
|
{
|
||||||
|
int a = DODEC_A;
|
||||||
|
int b = DODEC_B;
|
||||||
|
|
||||||
|
if (width - 1 > (INT_MAX - (2*(2*a + b) + 3*a + b)) / (6*a + 2*b) ||
|
||||||
|
height - 1 > (INT_MAX - 2*(2*a + b)) / (3*a + 3*b) || /* yextent */
|
||||||
|
width > INT_MAX / 200 / height) /* max_dots */
|
||||||
|
return "Grid size must not be unreasonably large";
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static void grid_size_greatdodecagonal(int width, int height,
|
static void grid_size_greatdodecagonal(int width, int height,
|
||||||
int *tilesize, int *xextent, int *yextent)
|
int *tilesize, int *xextent, int *yextent)
|
||||||
{
|
{
|
||||||
@ -2670,6 +2815,19 @@ static grid *grid_new_greatdodecagonal(int width, int height, const char *desc)
|
|||||||
return g;
|
return g;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *grid_validate_params_greatgreatdodecagonal(
|
||||||
|
int width, int height)
|
||||||
|
{
|
||||||
|
int a = DODEC_A;
|
||||||
|
int b = DODEC_B;
|
||||||
|
|
||||||
|
if (width-1 > (INT_MAX - (2*(2*a + b) + 2*a + 2*b)) / (4*a + 4*b) ||
|
||||||
|
height-1 > (INT_MAX - 2*(2*a + b)) / (6*a + 2*b) || /* yextent */
|
||||||
|
width > INT_MAX / 300 / height) /* max_dots */
|
||||||
|
return "Grid size must not be unreasonably large";
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static void grid_size_greatgreatdodecagonal(int width, int height,
|
static void grid_size_greatgreatdodecagonal(int width, int height,
|
||||||
int *tilesize, int *xextent, int *yextent)
|
int *tilesize, int *xextent, int *yextent)
|
||||||
{
|
{
|
||||||
@ -2839,6 +2997,19 @@ static grid *grid_new_greatgreatdodecagonal(int width, int height, const char *d
|
|||||||
return g;
|
return g;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *grid_validate_params_compassdodecagonal(
|
||||||
|
int width, int height)
|
||||||
|
{
|
||||||
|
int a = DODEC_A;
|
||||||
|
int b = DODEC_B;
|
||||||
|
|
||||||
|
if (width > INT_MAX / (4*a + 2*b) || /* xextent */
|
||||||
|
height > INT_MAX / (4*a + 2*b) || /* yextent */
|
||||||
|
width > INT_MAX / 18 / height) /* max_dots */
|
||||||
|
return "Grid must not be unreasonably large";
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static void grid_size_compassdodecagonal(int width, int height,
|
static void grid_size_compassdodecagonal(int width, int height,
|
||||||
int *tilesize, int *xextent, int *yextent)
|
int *tilesize, int *xextent, int *yextent)
|
||||||
{
|
{
|
||||||
@ -2985,6 +3156,17 @@ static int set_faces(penrose_state *state, vector *vs, int n, int depth)
|
|||||||
|
|
||||||
#define PENROSE_TILESIZE 100
|
#define PENROSE_TILESIZE 100
|
||||||
|
|
||||||
|
static const char *grid_validate_params_penrose(int width, int height)
|
||||||
|
{
|
||||||
|
int l = PENROSE_TILESIZE;
|
||||||
|
|
||||||
|
if (width > INT_MAX / l || /* xextent */
|
||||||
|
height > INT_MAX / l || /* yextent */
|
||||||
|
width > INT_MAX / (3 * 3 * 4 * height)) /* max_dots */
|
||||||
|
return "Grid must not be unreasonably large";
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static void grid_size_penrose(int width, int height,
|
static void grid_size_penrose(int width, int height,
|
||||||
int *tilesize, int *xextent, int *yextent)
|
int *tilesize, int *xextent, int *yextent)
|
||||||
{
|
{
|
||||||
@ -3187,6 +3369,16 @@ static grid *grid_new_penrose(int width, int height, int which, const char *desc
|
|||||||
return g;
|
return g;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *grid_validate_params_penrose_p2_kite(int width, int height)
|
||||||
|
{
|
||||||
|
return grid_validate_params_penrose(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *grid_validate_params_penrose_p3_thick(int width, int height)
|
||||||
|
{
|
||||||
|
return grid_validate_params_penrose(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
static void grid_size_penrose_p2_kite(int width, int height,
|
static void grid_size_penrose_p2_kite(int width, int height,
|
||||||
int *tilesize, int *xextent, int *yextent)
|
int *tilesize, int *xextent, int *yextent)
|
||||||
{
|
{
|
||||||
@ -3211,12 +3403,24 @@ static grid *grid_new_penrose_p3_thick(int width, int height, const char *desc)
|
|||||||
|
|
||||||
/* ----------- End of grid generators ------------- */
|
/* ----------- End of grid generators ------------- */
|
||||||
|
|
||||||
|
#define FNVAL(upper,lower) &grid_validate_params_ ## lower,
|
||||||
#define FNNEW(upper,lower) &grid_new_ ## lower,
|
#define FNNEW(upper,lower) &grid_new_ ## lower,
|
||||||
#define FNSZ(upper,lower) &grid_size_ ## lower,
|
#define FNSZ(upper,lower) &grid_size_ ## lower,
|
||||||
|
|
||||||
|
static const char *(*(grid_validate_paramses[]))(int, int) =
|
||||||
|
{ GRIDGEN_LIST(FNVAL) };
|
||||||
static grid *(*(grid_news[]))(int, int, const char*) = { GRIDGEN_LIST(FNNEW) };
|
static grid *(*(grid_news[]))(int, int, const char*) = { GRIDGEN_LIST(FNNEW) };
|
||||||
static void(*(grid_sizes[]))(int, int, int*, int*, int*) = { GRIDGEN_LIST(FNSZ) };
|
static void(*(grid_sizes[]))(int, int, int*, int*, int*) = { GRIDGEN_LIST(FNSZ) };
|
||||||
|
|
||||||
|
/* Work out if a grid can be made, and complain if not. */
|
||||||
|
|
||||||
|
const char *grid_validate_params(grid_type type, int width, int height)
|
||||||
|
{
|
||||||
|
if (width <= 0 || height <= 0)
|
||||||
|
return "Width and height must both be positive";
|
||||||
|
return grid_validate_paramses[type](width, height);
|
||||||
|
}
|
||||||
|
|
||||||
char *grid_new_desc(grid_type type, int width, int height, random_state *rs)
|
char *grid_new_desc(grid_type type, int width, int height, random_state *rs)
|
||||||
{
|
{
|
||||||
if (type == GRID_PENROSE_P2 || type == GRID_PENROSE_P3) {
|
if (type == GRID_PENROSE_P2 || type == GRID_PENROSE_P3) {
|
||||||
|
2
grid.h
2
grid.h
@ -115,6 +115,8 @@ typedef struct grid {
|
|||||||
typedef enum grid_type { GRIDGEN_LIST(ENUM) GRID_TYPE_MAX } grid_type;
|
typedef enum grid_type { GRIDGEN_LIST(ENUM) GRID_TYPE_MAX } grid_type;
|
||||||
#undef ENUM
|
#undef ENUM
|
||||||
|
|
||||||
|
const char *grid_validate_params(grid_type type, int width, int height);
|
||||||
|
|
||||||
/* Free directly after use if non-NULL. Will never contain an underscore
|
/* Free directly after use if non-NULL. Will never contain an underscore
|
||||||
* (so clients can safely use that as a separator). */
|
* (so clients can safely use that as a separator). */
|
||||||
char *grid_new_desc(grid_type type, int width, int height, random_state *rs);
|
char *grid_new_desc(grid_type type, int width, int height, random_state *rs);
|
||||||
|
3
loopy.c
3
loopy.c
@ -684,6 +684,7 @@ static game_params *custom_params(const config_item *cfg)
|
|||||||
|
|
||||||
static const char *validate_params(const game_params *params, bool full)
|
static const char *validate_params(const game_params *params, bool full)
|
||||||
{
|
{
|
||||||
|
const char *err;
|
||||||
if (params->type < 0 || params->type >= NUM_GRID_TYPES)
|
if (params->type < 0 || params->type >= NUM_GRID_TYPES)
|
||||||
return "Illegal grid type";
|
return "Illegal grid type";
|
||||||
if (params->w < grid_size_limits[params->type].amin ||
|
if (params->w < grid_size_limits[params->type].amin ||
|
||||||
@ -692,6 +693,8 @@ static const char *validate_params(const game_params *params, bool full)
|
|||||||
if (params->w < grid_size_limits[params->type].omin &&
|
if (params->w < grid_size_limits[params->type].omin &&
|
||||||
params->h < grid_size_limits[params->type].omin)
|
params->h < grid_size_limits[params->type].omin)
|
||||||
return grid_size_limits[params->type].oerr;
|
return grid_size_limits[params->type].oerr;
|
||||||
|
err = grid_validate_params(grid_types[params->type], params->w, params->h);
|
||||||
|
if (err != NULL) return err;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This shouldn't be able to happen at all, since decode_params
|
* This shouldn't be able to happen at all, since decode_params
|
||||||
|
Reference in New Issue
Block a user