Load SGX SymbOS graphics
This commit is contained in:
		
							parent
							
								
									951f58445c
								
							
						
					
					
						commit
						713fd9ff71
					
				@ -129,6 +129,7 @@ enum FILE_FORMATS
 | 
				
			|||||||
  FORMAT_CM5,  ///< Amstrad CPC Mode 5
 | 
					  FORMAT_CM5,  ///< Amstrad CPC Mode 5
 | 
				
			||||||
  FORMAT_PPH,  ///< Amstrad CPC Perfect Pix
 | 
					  FORMAT_PPH,  ///< Amstrad CPC Perfect Pix
 | 
				
			||||||
  FORMAT_GOS,  ///< Amstrad Plus Graphos
 | 
					  FORMAT_GOS,  ///< Amstrad Plus Graphos
 | 
				
			||||||
 | 
					  FORMAT_SGX,  ///< SymbOS graphic files
 | 
				
			||||||
  FORMAT_XPM,  ///< X PixMap
 | 
					  FORMAT_XPM,  ///< X PixMap
 | 
				
			||||||
  FORMAT_ICO,  ///< Windows icons
 | 
					  FORMAT_ICO,  ///< Windows icons
 | 
				
			||||||
  FORMAT_INFO, ///< Amiga OS icons
 | 
					  FORMAT_INFO, ///< Amiga OS icons
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										223
									
								
								src/cpcformats.c
									
									
									
									
									
								
							
							
						
						
									
										223
									
								
								src/cpcformats.c
									
									
									
									
									
								
							@ -33,6 +33,10 @@
 | 
				
			|||||||
#include "gfx2mem.h"
 | 
					#include "gfx2mem.h"
 | 
				
			||||||
#include "gfx2log.h"
 | 
					#include "gfx2log.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef MAX
 | 
				
			||||||
 | 
					#define MAX(a,b) (((a)>(b)) ? (a) : (b))
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * @defgroup cpcformats Amstrad CPC/CPC+ picture formats
 | 
					 * @defgroup cpcformats Amstrad CPC/CPC+ picture formats
 | 
				
			||||||
 * @ingroup loadsaveformats
 | 
					 * @ingroup loadsaveformats
 | 
				
			||||||
@ -48,10 +52,229 @@
 | 
				
			|||||||
 * - SCR : OCP Art Studio / iMPdraw v2 / etc.
 | 
					 * - SCR : OCP Art Studio / iMPdraw v2 / etc.
 | 
				
			||||||
 * - CM5 : Mode 5 Viewer
 | 
					 * - CM5 : Mode 5 Viewer
 | 
				
			||||||
 * - PPH : Perfect Pix
 | 
					 * - PPH : Perfect Pix
 | 
				
			||||||
 | 
					 * - SGX : SymbOS graphic files
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @{
 | 
					 * @{
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Test for SGX file (SymbOS)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * http://www.cpcwiki.eu/index.php/Format:SGX_(SymbOS_graphic_files)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void Test_SGX(T_IO_Context * context, FILE * file)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  byte header[8];
 | 
				
			||||||
 | 
					  (void)context;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (Read_bytes(file, header, 8))
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    unsigned long file_size = File_length_file(file);
 | 
				
			||||||
 | 
					    if (header[0] > 0 && header[0] < 64
 | 
				
			||||||
 | 
					        && ((header[1] + 3) >> 2) == header[0]
 | 
				
			||||||
 | 
					        && file_size >= (3 + (unsigned long)header[0] * (unsigned long)header[2]))
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      // Simple 4 colour graphic
 | 
				
			||||||
 | 
					      File_error = 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else if (header[0] == 64 && (header[1] == 0 || header[1] == 5))
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      // extended
 | 
				
			||||||
 | 
					      word byte_width = header[2] | ((word)header[3] << 8);
 | 
				
			||||||
 | 
					      word pixel_width = header[4] | ((word)header[5] << 8);
 | 
				
			||||||
 | 
					      word height = header[6] | ((word)header[7] << 8);
 | 
				
			||||||
 | 
					      if (height == 0 || byte_width == 0 || byte_width > 255) return;
 | 
				
			||||||
 | 
					      if (header[1] == 0 && byte_width != ((pixel_width + 3) >> 2)) return;
 | 
				
			||||||
 | 
					      if (header[1] == 5 && byte_width != ((pixel_width + 1) >> 1)) return;
 | 
				
			||||||
 | 
					      File_error = 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void Set_SGX_Palette(T_IO_Context * context)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  static const byte sgx_palette[] = {
 | 
				
			||||||
 | 
					    0xff, 0xff, 0x80, // 0
 | 
				
			||||||
 | 
					    0x00, 0x00, 0x00,
 | 
				
			||||||
 | 
					    0xff, 0x80, 0x00,
 | 
				
			||||||
 | 
					    0x80, 0x00, 0x00,
 | 
				
			||||||
 | 
					    0x00, 0xff, 0xff, // 4
 | 
				
			||||||
 | 
					    0x00, 0x00, 0x80,
 | 
				
			||||||
 | 
					    0x80, 0x80, 0xff,
 | 
				
			||||||
 | 
					    0x80, 0x00, 0xff,
 | 
				
			||||||
 | 
					    0xff, 0xff, 0xff, // 8
 | 
				
			||||||
 | 
					    0x00, 0x80, 0x00,
 | 
				
			||||||
 | 
					    0x00, 0xff, 0x00,
 | 
				
			||||||
 | 
					    0xff, 0x00, 0xff,
 | 
				
			||||||
 | 
					    0xff, 0xff, 0x00, // 12
 | 
				
			||||||
 | 
					    0x80, 0x80, 0x80,
 | 
				
			||||||
 | 
					    0xff, 0x80, 0x80,
 | 
				
			||||||
 | 
					    0xff, 0x00, 0x00
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					  if (Config.Clear_palette)
 | 
				
			||||||
 | 
					    memset(context->Palette,0,sizeof(T_Palette));
 | 
				
			||||||
 | 
					  memcpy(context->Palette, sgx_palette, sizeof(sgx_palette));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Structure for current information about loading SGX file
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					struct sgx_data {
 | 
				
			||||||
 | 
					  T_IO_Context * context;
 | 
				
			||||||
 | 
					  FILE * file;
 | 
				
			||||||
 | 
					  unsigned long file_size;
 | 
				
			||||||
 | 
					  word width, height;
 | 
				
			||||||
 | 
					  byte ncolors;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Callback function for Read_SGX()
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					typedef int (*Read_SGX_Callback)(struct sgx_data *, word, word, word, word, word, byte);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Read a SGX file structure.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @return 0 for error
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static int Read_SGX(struct sgx_data * data, Read_SGX_Callback cb)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  word posx = 0;
 | 
				
			||||||
 | 
					  word posy = 0;
 | 
				
			||||||
 | 
					  byte b;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  data->width = 0;
 | 
				
			||||||
 | 
					  data->height = 0;
 | 
				
			||||||
 | 
					  while (Read_byte(data->file, &b))
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    if (b == 0)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      // EOF
 | 
				
			||||||
 | 
					      GFX2_Log(GFX2_DEBUG, "SGX EOF\n");
 | 
				
			||||||
 | 
					      return 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else if (b == 255)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      GFX2_Log(GFX2_DEBUG, "SGX LF\n");
 | 
				
			||||||
 | 
					      // skip 2 bytes
 | 
				
			||||||
 | 
					      if (!(Read_byte(data->file, &b) && Read_byte(data->file, &b)))
 | 
				
			||||||
 | 
					        return 0;
 | 
				
			||||||
 | 
					      posx = 0;
 | 
				
			||||||
 | 
					      posy = data->height;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else if (b < 64)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      byte byte_width, width, height;
 | 
				
			||||||
 | 
					      byte_width = b;
 | 
				
			||||||
 | 
					      if (!(Read_byte(data->file, &width) && Read_byte(data->file, &height)))
 | 
				
			||||||
 | 
					        return 0;
 | 
				
			||||||
 | 
					      GFX2_Log(GFX2_DEBUG, "SGX Simple 4c : %dx%d\n", (int)width, (int)height);
 | 
				
			||||||
 | 
					      if (!cb(data, posx, posy, width, height, byte_width, 4))
 | 
				
			||||||
 | 
					        return 0;
 | 
				
			||||||
 | 
					      posx += width;
 | 
				
			||||||
 | 
					      data->width = MAX(data->width, posx);
 | 
				
			||||||
 | 
					      data->height = MAX(data->height, posy + height);
 | 
				
			||||||
 | 
					      if (data->ncolors == 0)
 | 
				
			||||||
 | 
					        data->ncolors = 4;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else if (b == 64)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      byte type;
 | 
				
			||||||
 | 
					      word byte_width, width, height;
 | 
				
			||||||
 | 
					      if (!(Read_byte(data->file, &type) && Read_word_le(data->file, &byte_width)
 | 
				
			||||||
 | 
					            && Read_word_le(data->file, &width) && Read_word_le(data->file, &height)))
 | 
				
			||||||
 | 
					        return 0;
 | 
				
			||||||
 | 
					      GFX2_Log(GFX2_DEBUG, "SGX Extended %dc : %dx%d\n", type == 0 ? 4 : 16, (int)width, (int)height);
 | 
				
			||||||
 | 
					      if (!cb(data, posx, posy, width, height, byte_width, type == 0 ? 4 : 16))
 | 
				
			||||||
 | 
					        return 0;
 | 
				
			||||||
 | 
					      posx += width;
 | 
				
			||||||
 | 
					      data->width = MAX(data->width, posx);
 | 
				
			||||||
 | 
					      data->height = MAX(data->height, posy + height);
 | 
				
			||||||
 | 
					      data->ncolors = MAX(data->ncolors, type == 0 ? 4 : 16);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      GFX2_Log(GFX2_WARNING, "SGX : unrecognized chunk. Byte 0x%02x at offset 0x%06x\n", b, ftell(data->file) - 1);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return 1; // it is OK if the file does finish with a EOF mark
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Only skip bytes
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int SGX_Get_dimensions(struct sgx_data * data, word posx, word posy, word width, word height, word byte_width, byte ncolors)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  (void)posx;
 | 
				
			||||||
 | 
					  (void)posy;
 | 
				
			||||||
 | 
					  (void)width;
 | 
				
			||||||
 | 
					  (void)ncolors;
 | 
				
			||||||
 | 
					  if (fseek(data->file, (long)byte_width * height, SEEK_CUR) < 0)
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					  return 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Set the pixels
 | 
				
			||||||
 | 
					 * @return 0 in case of error
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int SGX_Load_Picture(struct sgx_data * data, word posx, word posy, word width, word height, word byte_width, byte ncolors)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  word y;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for (y = posy; y < (posy + height); y++)
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    word i, x;
 | 
				
			||||||
 | 
					    for (i = 0, x = posx; i < byte_width; i++)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      byte b;
 | 
				
			||||||
 | 
					      if (!Read_byte(data->file, &b))
 | 
				
			||||||
 | 
					        return 0;
 | 
				
			||||||
 | 
					      if (ncolors == 4)
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        do {
 | 
				
			||||||
 | 
					          // upper nibble is 4 lower color bits, lower nibble is 4 upper color bits
 | 
				
			||||||
 | 
					          Set_pixel(data->context, x++, y, (b & 0x80) >> 7 | (b & 0x08) >> 2);
 | 
				
			||||||
 | 
					          b <<= 1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        while (((x ^ posx) & 3) && (x < (posx + width)));
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      else
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        Set_pixel(data->context, x++, y, b >> 4);
 | 
				
			||||||
 | 
					        if (x < (posx + width))
 | 
				
			||||||
 | 
					          Set_pixel(data->context, x++, y, b & 0x0f);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Load_SGX(T_IO_Context * context)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  struct sgx_data data = { context, NULL, 0, 0, 0, 0 };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  File_error = 1;
 | 
				
			||||||
 | 
					  data.file = Open_file_read(context);
 | 
				
			||||||
 | 
					  if (data.file == NULL) return;
 | 
				
			||||||
 | 
					  data.file_size = File_length_file(data.file);
 | 
				
			||||||
 | 
					  if (Read_SGX(&data, SGX_Get_dimensions))
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    GFX2_Log(GFX2_DEBUG, "SGX total dimensions : %ux%u, %d colors\n",
 | 
				
			||||||
 | 
					             (unsigned)data.width, (unsigned)data.height, (int)data.ncolors);
 | 
				
			||||||
 | 
					    File_error = 0;
 | 
				
			||||||
 | 
					    Pre_load(context, data.width, data.height, data.file_size, FORMAT_SGX, PIXEL_SIMPLE, data.ncolors == 16 ? 4 : 2);
 | 
				
			||||||
 | 
					    if (File_error == 0)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      if (fseek(data.file, 0, SEEK_SET) < 0 || !Read_SGX(&data, SGX_Load_Picture))
 | 
				
			||||||
 | 
					        File_error = 1;
 | 
				
			||||||
 | 
					      else
 | 
				
			||||||
 | 
					        Set_SGX_Palette(context);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  fclose(data.file);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Test for SCR file (Amstrad CPC)
 | 
					 * Test for SCR file (Amstrad CPC)
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 | 
				
			|||||||
@ -148,6 +148,10 @@ void Test_GOS(T_IO_Context *, FILE *);
 | 
				
			|||||||
void Load_GOS(T_IO_Context *);
 | 
					void Load_GOS(T_IO_Context *);
 | 
				
			||||||
void Save_GOS(T_IO_Context *);
 | 
					void Save_GOS(T_IO_Context *);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// -- SGX (SymbOS)
 | 
				
			||||||
 | 
					void Test_SGX(T_IO_Context *, FILE *);
 | 
				
			||||||
 | 
					void Load_SGX(T_IO_Context *);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// -- XPM (X PixMap)
 | 
					// -- XPM (X PixMap)
 | 
				
			||||||
// Loading is done through SDL_Image
 | 
					// Loading is done through SDL_Image
 | 
				
			||||||
void Save_XPM(T_IO_Context*);
 | 
					void Save_XPM(T_IO_Context*);
 | 
				
			||||||
 | 
				
			|||||||
@ -541,6 +541,7 @@ static const T_Help_table helptable_credits[] =
 | 
				
			|||||||
  HELP_TEXT ("  PPH : Rhino / Batman Group")
 | 
					  HELP_TEXT ("  PPH : Rhino / Batman Group")
 | 
				
			||||||
  HELP_TEXT ("  SCx : Colorix (?)")
 | 
					  HELP_TEXT ("  SCx : Colorix (?)")
 | 
				
			||||||
  HELP_TEXT ("  SCR : Advanced OCP Art Studio + iMPdraw")
 | 
					  HELP_TEXT ("  SCR : Advanced OCP Art Studio + iMPdraw")
 | 
				
			||||||
 | 
					  HELP_TEXT ("  SGX : SymbOS")
 | 
				
			||||||
  HELP_TEXT ("  TIFF : Aldus (now Adobe)")
 | 
					  HELP_TEXT ("  TIFF : Aldus (now Adobe)")
 | 
				
			||||||
  HELP_TEXT ("  TNY : David Mumper")
 | 
					  HELP_TEXT ("  TNY : David Mumper")
 | 
				
			||||||
  HELP_TEXT ("  XPM : C.Nahaboo and D.Dardailler / Bull")
 | 
					  HELP_TEXT ("  XPM : C.Nahaboo and D.Dardailler / Bull")
 | 
				
			||||||
 | 
				
			|||||||
@ -112,7 +112,7 @@ const T_Format File_formats[] = {
 | 
				
			|||||||
    "pi1;pc1;pi2;pc2;pi3;pc3;neo;tny;tn1;tn2;tn3;tn4;ca1;ca2;ca3;"
 | 
					    "pi1;pc1;pi2;pc2;pi3;pc3;neo;tny;tn1;tn2;tn3;tn4;ca1;ca2;ca3;"
 | 
				
			||||||
    "c64;p64;a64;pi;rp;aas;art;dd;iph;ipt;hpc;ocp;koa;koala;fli;bml;cdu;prg;pmg;rpm;"
 | 
					    "c64;p64;a64;pi;rp;aas;art;dd;iph;ipt;hpc;ocp;koa;koala;fli;bml;cdu;prg;pmg;rpm;"
 | 
				
			||||||
    "gpx;"
 | 
					    "gpx;"
 | 
				
			||||||
    "cpc;scr;win;pph;cm5;go1;"
 | 
					    "cpc;scr;win;pph;cm5;go1;sgx;"
 | 
				
			||||||
    "hgr;dhgr;"
 | 
					    "hgr;dhgr;"
 | 
				
			||||||
    "grb;grob;"
 | 
					    "grb;grob;"
 | 
				
			||||||
    "sc2;"
 | 
					    "sc2;"
 | 
				
			||||||
@ -148,6 +148,7 @@ const T_Format File_formats[] = {
 | 
				
			|||||||
  {FORMAT_CM5, " cm5", Test_CM5, Load_CM5, Save_CM5, 0, 0, 1, "cm5", "cm5"},
 | 
					  {FORMAT_CM5, " cm5", Test_CM5, Load_CM5, Save_CM5, 0, 0, 1, "cm5", "cm5"},
 | 
				
			||||||
  {FORMAT_PPH, " pph", Test_PPH, Load_PPH, Save_PPH, 0, 0, 1, "pph", "pph"},
 | 
					  {FORMAT_PPH, " pph", Test_PPH, Load_PPH, Save_PPH, 0, 0, 1, "pph", "pph"},
 | 
				
			||||||
  {FORMAT_GOS, " go1", Test_GOS, Load_GOS, Save_GOS, 0, 0, 0, "go1", "go1"},
 | 
					  {FORMAT_GOS, " go1", Test_GOS, Load_GOS, Save_GOS, 0, 0, 0, "go1", "go1"},
 | 
				
			||||||
 | 
					  {FORMAT_SGX, " sgx", Test_SGX, Load_SGX, NULL,     0, 0, 1, "sgx", "sgx"},
 | 
				
			||||||
  {FORMAT_XPM, " xpm", NULL,     NULL,     Save_XPM, 0, 0, 0, "xpm", "xpm"},
 | 
					  {FORMAT_XPM, " xpm", NULL,     NULL,     Save_XPM, 0, 0, 0, "xpm", "xpm"},
 | 
				
			||||||
  {FORMAT_ICO, " ico", Test_ICO, Load_ICO, Save_ICO, 0, 0, 0, "ico", "ico;ic2;cur"},
 | 
					  {FORMAT_ICO, " ico", Test_ICO, Load_ICO, Save_ICO, 0, 0, 0, "ico", "ico;ic2;cur"},
 | 
				
			||||||
  {FORMAT_INFO," info",Test_INFO,Load_INFO,NULL,     0, 0, 0, "info", "info"},
 | 
					  {FORMAT_INFO," info",Test_INFO,Load_INFO,NULL,     0, 0, 0, "info", "info"},
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user