summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--conf/battle/client.conf8
-rw-r--r--src/map/battle.c1
-rw-r--r--src/map/battle.h1
-rw-r--r--src/map/clif.c122
4 files changed, 122 insertions, 10 deletions
diff --git a/conf/battle/client.conf b/conf/battle/client.conf
index eae8aad3b..069dea1a5 100644
--- a/conf/battle/client.conf
+++ b/conf/battle/client.conf
@@ -118,3 +118,11 @@ client_sort_storage: no
// because the client sees multiple commands in succession as spam.
// Default: 0 (means disabled)
client_accept_chatdori: 0
+
+// Limits use of blank (transparent) pixels in guild emblems to a set
+// percentage of the total.
+// Official servers do not enforce this technically to date, but some disallow
+// use of blank emblems in their rules. (Note 2)
+// A value of 100 (allowing 100% blank pixels) disables this check.
+// NOTE: Enabling this option slightly degrades performance.
+client_emblem_max_blank_percent: 100
diff --git a/src/map/battle.c b/src/map/battle.c
index e1dffb796..b44bc353e 100644
--- a/src/map/battle.c
+++ b/src/map/battle.c
@@ -6442,6 +6442,7 @@ static const struct _battle_data {
{ "cashshop_show_points", &battle_config.cashshop_show_points, 0, 0, 1, },
{ "mail_show_status", &battle_config.mail_show_status, 0, 0, 2, },
{ "client_limit_unit_lv", &battle_config.client_limit_unit_lv, 0, 0, BL_ALL, },
+ { "client_emblem_max_blank_percent", &battle_config.client_emblem_max_blank_percent, 100, 0, 100, },
// BattleGround Settings
{ "bg_update_interval", &battle_config.bg_update_interval, 1000, 100, INT_MAX, },
{ "bg_flee_penalty", &battle_config.bg_flee_penalty, 20, 0, INT_MAX, },
diff --git a/src/map/battle.h b/src/map/battle.h
index 0f3a22c4b..fd6699f4d 100644
--- a/src/map/battle.h
+++ b/src/map/battle.h
@@ -426,6 +426,7 @@ struct Battle_Config {
int cashshop_show_points;
int mail_show_status;
int client_limit_unit_lv;
+ int client_emblem_max_blank_percent;
int hom_max_level;
int hom_S_max_level;
diff --git a/src/map/clif.c b/src/map/clif.c
index 6740c7a74..c509da7dd 100644
--- a/src/map/clif.c
+++ b/src/map/clif.c
@@ -12967,20 +12967,122 @@ void clif_parse_GuildRequestEmblem(int fd,struct map_session_data *sd)
clif->guild_emblem(sd,g);
}
-
/// Validates data of a guild emblem (compressed bitmap)
-bool clif_validate_emblem(const uint8* emblem, unsigned long emblem_len) {
- bool success;
- uint8 buf[1800]; // no well-formed emblem bitmap is larger than 1782 (24 bit) / 1654 (8 bit) bytes
+bool clif_validate_emblem(const uint8 *emblem, unsigned long emblem_len) {
+ enum e_bitmapconst {
+ RGBTRIPLE_SIZE = 3, // sizeof(RGBTRIPLE)
+ RGBQUAD_SIZE = 4, // sizeof(RGBQUAD)
+ BITMAPFILEHEADER_SIZE = 14, // sizeof(BITMAPFILEHEADER)
+ BITMAPINFOHEADER_SIZE = 40, // sizeof(BITMAPINFOHEADER)
+ BITMAP_WIDTH = 24,
+ BITMAP_HEIGHT = 24,
+ };
+#pragma pack(push, 1)
+ struct s_bitmaptripple {
+ //uint8 b;
+ //uint8 g;
+ //uint8 r;
+ unsigned int rgb:24;
+ } __attribute__((packed));
+#pragma pack(pop)
+ uint8 buf[1800]; // no well-formed emblem bitmap is larger than 1782 (24 bit) / 1654 (8 bit) bytes
unsigned long buf_len = sizeof(buf);
+ int header = 0, bitmap = 0, offbits = 0, palettesize = 0, i = 0;
+
+ if( decode_zip(buf, &buf_len, emblem, emblem_len) != 0 || buf_len < BITMAPFILEHEADER_SIZE + BITMAPINFOHEADER_SIZE
+ || RBUFW(buf,0) != 0x4d42 // BITMAPFILEHEADER.bfType (signature)
+ || RBUFL(buf,2) != buf_len // BITMAPFILEHEADER.bfSize (file size)
+ || RBUFL(buf,14) != BITMAPINFOHEADER_SIZE // BITMAPINFOHEADER.biSize (other headers are not supported)
+ || RBUFL(buf,18) != BITMAP_WIDTH // BITMAPINFOHEADER.biWidth
+ || RBUFL(buf,22) != BITMAP_HEIGHT // BITMAPINFOHEADER.biHeight (top-down bitmaps (-24) are not supported)
+ || RBUFL(buf,30) != 0 // BITMAPINFOHEADER.biCompression == BI_RGB (compression not supported)
+ ) {
+ // Invalid data
+ return false;
+ }
- success = ( decode_zip(buf, &buf_len, emblem, emblem_len) == 0 && buf_len >= 18 ) // sizeof(BITMAPFILEHEADER) + sizeof(biSize) of the following info header struct
- && RBUFW(buf,0) == 0x4d42 // BITMAPFILEHEADER.bfType (signature)
- && RBUFL(buf,2) == buf_len // BITMAPFILEHEADER.bfSize (file size)
- && RBUFL(buf,10) < buf_len // BITMAPFILEHEADER.bfOffBits (offset to bitmap bits)
- ;
+ offbits = RBUFL(buf,10); // BITMAPFILEHEADER.bfOffBits (offset to bitmap bits)
+
+ switch( RBUFW(buf,28) ) { // BITMAPINFOHEADER.biBitCount
+ case 8:
+ palettesize = RBUFL(buf,46); // BITMAPINFOHEADER.biClrUsed (number of colors in the palette)
+ if( palettesize == 0 )
+ palettesize = 256; // Defaults to 2^n if set to zero
+ else if( palettesize > 256 )
+ return false;
+ header = BITMAPFILEHEADER_SIZE + BITMAPINFOHEADER_SIZE + RGBQUAD_SIZE * palettesize; // headers + palette
+ bitmap = BITMAP_WIDTH * BITMAP_HEIGHT;
+ break;
+ case 24:
+ header = BITMAPFILEHEADER_SIZE + BITMAPINFOHEADER_SIZE;
+ bitmap = BITMAP_WIDTH * BITMAP_HEIGHT * RGBTRIPLE_SIZE;
+ break;
+ default:
+ return false;
+ }
+
+ // NOTE: This check gives a little freedom for bitmap-producing implementations,
+ // that align the start of bitmap data, which is harmless but unnecessary.
+ // If you want it paranoidly strict, change the first condition from < to !=.
+ // This also allows files with trailing garbage at the end of the file.
+ // If you want to avoid that, change the last condition to !=.
+ if( offbits < header || buf_len <= bitmap || offbits > buf_len - bitmap ) {
+ return false;
+ }
- return success;
+ if( battle_config.client_emblem_max_blank_percent < 100 ) {
+ int required_pixels = BITMAP_WIDTH * BITMAP_HEIGHT * (100 - battle_config.client_emblem_max_blank_percent) / 100;
+ int found_pixels = 0;
+ /// Checks what percentage of a guild emblem is blank. A blank emblem
+ /// consists solely of magenta pixels. Since the client uses 16-bit
+ /// colors, any magenta shade that reduces to #ff00ff passes off as
+ /// transparent color as well (down to #f807f8).
+ ///
+ /// Unlike real magenta, reduced magenta causes the guild window to
+ /// become see-through in the transparent parts of the emblem
+ /// background (glitch).
+ switch( RBUFW(buf,28) ) {
+ case 8: // palette indexes
+ {
+ const uint8 *indexes = (const uint8 *)RBUFP(buf,offbits);
+ const uint32 *palette = (const uint32 *)RBUFP(buf,BITMAPFILEHEADER_SIZE + BITMAPINFOHEADER_SIZE);
+
+ for( i = 0; i < BITMAP_WIDTH * BITMAP_HEIGHT; i++ ) {
+ if( indexes[i] >= palettesize ) // Invalid color
+ return false;
+
+ // if( color->r < 0xF8 || color->g > 0x07 || color->b < 0xF8 )
+ if( ( palette[indexes[i]]&0x00F8F8F8 ) != 0x00F800F8 ) {
+ if( ++found_pixels >= required_pixels ) {
+ // Enough valid pixels were found
+ return true;
+ }
+ }
+ }
+ break;
+ }
+ case 24: // full colors
+ {
+ const struct s_bitmaptripple *pixels = (const struct s_bitmaptripple*)RBUFP(buf,offbits);
+
+ for( i = 0; i < BITMAP_WIDTH * BITMAP_HEIGHT; i++ ) {
+ // if( pixels[i].r < 0xF8 || pixels[i].g > 0x07 || pixels[i].b < 0xF8 )
+ if( ( pixels[i].rgb&0xF8F8F8 ) != 0xF800F8 ) {
+ if( ++found_pixels >= required_pixels ) {
+ // Enough valid pixels were found
+ return true;
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ // Not enough non-blank pixels found
+ return false;
+ }
+
+ return true;
}