diff options
author | Haru <haru@dotalux.com> | 2013-11-17 04:07:27 +0100 |
---|---|---|
committer | Haru <haru@dotalux.com> | 2013-11-17 04:19:03 +0100 |
commit | e095650d437c81e6ae90a99a3cd1bb0c8c6eba38 (patch) | |
tree | 594bf0d9a9f17612fb587e4be276dfe26a8862aa | |
parent | be6440dfb609b4ce28ca973a8987ab434774c8ed (diff) | |
download | hercules-e095650d437c81e6ae90a99a3cd1bb0c8c6eba38.tar.gz hercules-e095650d437c81e6ae90a99a3cd1bb0c8c6eba38.tar.bz2 hercules-e095650d437c81e6ae90a99a3cd1bb0c8c6eba38.tar.xz hercules-e095650d437c81e6ae90a99a3cd1bb0c8c6eba38.zip |
Added emblem blank pixels check
- Made possible thanks to (and using base code by) Ai4rei - eAthena
r15263 and add-on patch
http://hercules.ws/board/topic/2974-add-on-patch-for-ea-r15263-commit/
- Modified to allow checking for a percentage of transparent pixels
rather than full image transparency.
- Tweaked to accept some other bmp formats that are currently allowed
by the client.
- The performance hit from enabling this check is negligible (benchmarks
show that scanning an emblem requires about half the time required to
decompress it after it's received), but it does exist, as noted in the
configuration file.
- Special thanks to Ind.
Signed-off-by: Haru <haru@dotalux.com>
-rw-r--r-- | conf/battle/client.conf | 8 | ||||
-rw-r--r-- | src/map/battle.c | 1 | ||||
-rw-r--r-- | src/map/battle.h | 1 | ||||
-rw-r--r-- | src/map/clif.c | 122 |
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; } |