From c1d235a59cd368efe3174bcc0ce0a9137c340ca1 Mon Sep 17 00:00:00 2001 From: Asher Baker Date: Thu, 11 Oct 2018 15:20:10 +0100 Subject: [PATCH] Use natural sorting for map lists (Fixes #892) (#907) This produces consistently better results, especially for games such as L4D(2). --- core/logic/smn_maplists.cpp | 102 +++++++++++++++++++++++++++++++++++- 1 file changed, 101 insertions(+), 1 deletion(-) diff --git a/core/logic/smn_maplists.cpp b/core/logic/smn_maplists.cpp index 2393a141..4361e632 100644 --- a/core/logic/smn_maplists.cpp +++ b/core/logic/smn_maplists.cpp @@ -321,9 +321,109 @@ public: delete m_pCurMapList; m_pCurMapList = NULL; } + static bool alphanum_isdigit(const char c) + { + return c >= '0' && c <= '9'; + } + static int alphanum_impl(const char *l, const char *r) + { + /** + * http://www.davekoelle.com/files/alphanum.hpp + * + * compare l and r with strcmp() semantics, but using + * the "Alphanum Algorithm". This function is designed to read + * through the l and r strings only one time, for + * maximum performance. It does not allocate memory for + * substrings. + * + * Released under the MIT License - https://opensource.org/licenses/MIT + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + enum mode_t { STRING, NUMBER } mode = STRING; + + while (*l && *r) + { + if (mode == STRING) + { + char l_char, r_char; + while ((l_char = *l) && (r_char = *r)) + { + // check if this are digit characters + const bool l_digit = alphanum_isdigit(l_char), r_digit = alphanum_isdigit(r_char); + // if both characters are digits, we continue in NUMBER mode + if (l_digit && r_digit) + { + mode = NUMBER; + break; + } + // if only the left character is a digit, we have a result + if (l_digit) return -1; + // if only the right character is a digit, we have a result + if (r_digit) return +1; + // compute the difference of both characters + const int diff = l_char - r_char; + // if they differ we have a result + if (diff != 0) return diff; + // otherwise process the next characters + ++l; + ++r; + } + } + else // mode == NUMBER + { + // get the left number + unsigned long l_int = 0; + while (*l && alphanum_isdigit(*l)) + { + // TODO: this can overflow + l_int = l_int * 10 + *l - '0'; + ++l; + } + + // get the right number + unsigned long r_int = 0; + while (*r && alphanum_isdigit(*r)) + { + // TODO: this can overflow + r_int = r_int * 10 + *r - '0'; + ++r; + } + + // if the difference is not equal to zero, we have a comparison result + const long diff = l_int - r_int; + if (diff != 0) + return diff; + + // otherwise we process the next substring in STRING mode + mode = STRING; + } + } + + if (*r) return -1; + if (*l) return +1; + return 0; + } static int sort_maps_in_adt_array(const void *str1, const void *str2) { - return strcmp((char *)str1, (char *)str2); + return alphanum_impl(static_cast(str1), static_cast(str2)); } ICellArray *UpdateMapList(ICellArray *pUseArray, const char *name, int *pSerial, unsigned int flags) {