Skip to content

Commit a3695fb

Browse files
Fix #1258: Add mixed hex and RGB(A) parsing to XMLColorToInt (#1261)
* Fix #1258: Add mixed hex and RGBA parsing to XMLColorToInt `XMLColorToInt` is used when parsing i.e. `color` attribute from map files on various elements such as vehicles, blips, radarareas, teams and so on. Before it only supported a single format for multi-value attributes. So now it is possible to specify separate colors like so: `#ff0, 255, 0, 0, #ff0000, #ffffff55` Which corresponds: yellow, red, red, transparent white * Use unsigned int * Handle empty strings as RGB skip
1 parent 7d3aef8 commit a3695fb

File tree

3 files changed

+125
-26
lines changed

3 files changed

+125
-26
lines changed

Server/mods/deathmatch/logic/CVehicle.cpp

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -304,28 +304,20 @@ bool CVehicle::ReadSpecialData(const int iLine)
304304
char szTemp[256];
305305
if (GetCustomDataString("color", szTemp, 256, true))
306306
{
307-
uchar ucValues[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
308-
char* sz1 = strtok(szTemp, ", ");
309-
if (sz1)
310-
ucValues[0] = atoi(sz1);
307+
std::vector<SColorRGBA> vecColors;
308+
unsigned char ucCount;
311309

312-
int i;
313-
for (i = 1; i < 12; i++)
310+
if (ColorStringToRGB(szTemp, SColorRGBA(0, 0, 0, 0), vecColors, ucCount))
314311
{
315-
char* szn = strtok(NULL, ", ");
316-
if (!szn)
317-
break;
318-
ucValues[i] = atoi(szn);
319-
}
320-
321-
if (i == 3 || i == 6 || i == 9 || i == 12)
322-
{
323-
m_Color.SetRGBColors(SColorRGBA(ucValues[0], ucValues[1], ucValues[2], 0), SColorRGBA(ucValues[3], ucValues[4], ucValues[5], 0),
324-
SColorRGBA(ucValues[6], ucValues[7], ucValues[8], 0), SColorRGBA(ucValues[9], ucValues[10], ucValues[11], 0));
312+
if (ucCount % 3 == 0)
313+
m_Color.SetRGBColors(vecColors[0], vecColors[1], vecColors[2], vecColors[3]);
314+
else
315+
m_Color.SetPaletteColors(vecColors[0].R, vecColors[0].G, vecColors[0].B, vecColors[1].R);
325316
}
326317
else
327318
{
328-
m_Color.SetPaletteColors(ucValues[0], ucValues[1], ucValues[2], ucValues[3]);
319+
CLogger::ErrorPrintf("Bad 'color' value specified in <vehicle> (line %u)\n", iLine);
320+
return false;
329321
}
330322
}
331323

Server/mods/deathmatch/logic/Utils.cpp

Lines changed: 111 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -535,18 +535,120 @@ bool XMLColorToInt(const char* szColor, unsigned long& ulColor)
535535

536536
bool XMLColorToInt(const char* szColor, unsigned char& ucRed, unsigned char& ucGreen, unsigned char& ucBlue, unsigned char& ucAlpha)
537537
{
538-
// Convert it to an integer first
539-
unsigned long ulColor;
540-
if (!XMLColorToInt(szColor, ulColor))
541-
{
538+
// If we're empty, let's just stop right away
539+
if (!szColor || strlen(szColor) == 0)
542540
return false;
541+
542+
std::vector<SColorRGBA> vecColors;
543+
unsigned char ucCount;
544+
545+
if (ColorStringToRGBA(szColor, SColorRGBA(ucRed, ucGreen, ucBlue, ucAlpha), vecColors, ucCount))
546+
{
547+
ucRed = vecColors[0].R;
548+
ucGreen = vecColors[0].G;
549+
ucBlue = vecColors[0].B;
550+
ucAlpha = vecColors[0].A;
551+
552+
return true;
553+
}
554+
555+
return false;
556+
}
557+
558+
bool ColorStringToRGBA(const char* szColor, SColorRGBA defaultColor, std::vector<SColorRGBA>& vecColors, unsigned char& ucCount, bool bIgnoreAlpha)
559+
{
560+
std::stringstream ss(szColor);
561+
SColorRGBA color = defaultColor;
562+
bool bPreviousWasHex = false;
563+
unsigned int uiRGBAIndex = 0;
564+
unsigned long ulColor;
565+
unsigned char ucValue;
566+
unsigned char ucLength = bIgnoreAlpha ? 3 : 4;
567+
568+
while (ss.good())
569+
{
570+
// Ambiguous value before a comma
571+
SString strValue;
572+
getline(ss, strValue, ',');
573+
574+
// Remove spaces
575+
ReplaceOccurrencesInString(strValue, " ", "");
576+
577+
// Is the value looking like a hexadecimal?
578+
if (strValue[0] == '#')
579+
{
580+
// Try converting it to an integer
581+
if (XMLColorToInt(strValue.c_str(), ulColor))
582+
{
583+
// If a previous RGBA wasn't finished, let's finish it now
584+
if (!bPreviousWasHex && uiRGBAIndex != 0)
585+
{
586+
vecColors.push_back(color);
587+
ucCount += ucLength - uiRGBAIndex;
588+
color = defaultColor;
589+
}
590+
591+
color.R = static_cast<unsigned char>(ulColor);
592+
color.G = static_cast<unsigned char>(ulColor >> 8);
593+
color.B = static_cast<unsigned char>(ulColor >> 16);
594+
595+
if (!bIgnoreAlpha)
596+
color.A = static_cast<unsigned char>(ulColor >> 24);
597+
598+
bPreviousWasHex = true;
599+
ucCount += ucLength;
600+
}
601+
else
602+
return false;
603+
}
604+
// It looks like we have an empty value, let's skip the value but treat it as RGB
605+
else if (strValue.empty())
606+
{
607+
if (bPreviousWasHex || uiRGBAIndex % ucLength == 0)
608+
{
609+
bPreviousWasHex = false;
610+
uiRGBAIndex = 0;
611+
}
612+
613+
uiRGBAIndex++;
614+
ucCount++;
615+
616+
if (uiRGBAIndex % ucLength != 0 && ss.good())
617+
continue;
618+
}
619+
// It looks like a plain number so let's treat it as a RGBA value
620+
else if (strValue.find_first_not_of("0123456789") == std::string::npos)
621+
{
622+
ucValue = atoi(strValue.c_str());
623+
624+
if (bPreviousWasHex || uiRGBAIndex % ucLength == 0)
625+
{
626+
color.R = ucValue;
627+
bPreviousWasHex = false;
628+
uiRGBAIndex = 0;
629+
}
630+
else if (uiRGBAIndex % ucLength == 1)
631+
color.G = ucValue;
632+
else if (uiRGBAIndex % ucLength == 2)
633+
color.B = ucValue;
634+
else if (uiRGBAIndex % ucLength == 3)
635+
color.A = ucValue;
636+
637+
uiRGBAIndex++;
638+
ucCount++;
639+
640+
if (uiRGBAIndex % ucLength != 0 && ss.good())
641+
continue;
642+
}
643+
else
644+
return false;
645+
646+
// We have a color, so let's push it
647+
vecColors.push_back(color);
648+
color = defaultColor;
649+
uiRGBAIndex = 0;
543650
}
544651

545-
// Convert it to red, green, blue and alpha
546-
ucRed = static_cast<unsigned char>(ulColor);
547-
ucGreen = static_cast<unsigned char>(ulColor >> 8);
548-
ucBlue = static_cast<unsigned char>(ulColor >> 16);
549-
ucAlpha = static_cast<unsigned char>(ulColor >> 24);
550652
return true;
551653
}
552654

Server/mods/deathmatch/logic/Utils.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ bool IsValidOrganizationPath(const char* szPath);
5757
unsigned int HexToInt(const char* szHex);
5858
bool XMLColorToInt(const char* szColor, unsigned long& ulColor);
5959
bool XMLColorToInt(const char* szColor, unsigned char& ucRed, unsigned char& ucGreen, unsigned char& ucBlue, unsigned char& ucAlpha);
60+
bool ColorStringToRGBA(const char* szColor, SColorRGBA defaultColor, std::vector<SColorRGBA>& vecColors, unsigned char& ucCount, bool bIgnoreAlpha = false);
61+
inline bool ColorStringToRGB(const char* szColor, SColorRGBA defaultColor, std::vector<SColorRGBA>& vecColors, unsigned char& ucCount)
62+
{
63+
return ColorStringToRGBA(szColor, defaultColor, vecColors, ucCount, true);
64+
}
6065

6166
inline float WrapAround(float fValue, float fHigh)
6267
{

0 commit comments

Comments
 (0)