diff options
author | rubidium <rubidium@openttd.org> | 2007-10-20 14:51:09 +0000 |
---|---|---|
committer | rubidium <rubidium@openttd.org> | 2007-10-20 14:51:09 +0000 |
commit | eadd6765e139d452ab11a5785b1761901a1932aa (patch) | |
tree | 28fbe813d418b83e119aa615200f98b20ba520c8 /src | |
parent | 3a361422fa7fbddf7cd3480e216aae214e793743 (diff) | |
download | openttd-eadd6765e139d452ab11a5785b1761901a1932aa.tar.xz |
(svn r11312) -Codechange: implement a overflow safe integer and use that for money and don't misuses CommandCost to have a overflow safe integer. Based on a patch by Noldo.
Diffstat (limited to 'src')
-rw-r--r-- | src/ai/trolly/trolly.cpp | 4 | ||||
-rw-r--r-- | src/build_vehicle_gui.cpp | 4 | ||||
-rw-r--r-- | src/command.cpp | 16 | ||||
-rw-r--r-- | src/console_cmds.cpp | 2 | ||||
-rw-r--r-- | src/economy.cpp | 2 | ||||
-rw-r--r-- | src/engine_gui.cpp | 2 | ||||
-rw-r--r-- | src/graph_gui.cpp | 14 | ||||
-rw-r--r-- | src/helpers.hpp | 135 | ||||
-rw-r--r-- | src/openttd.h | 4 | ||||
-rw-r--r-- | src/rail_cmd.cpp | 2 | ||||
-rw-r--r-- | src/road_cmd.cpp | 2 | ||||
-rw-r--r-- | src/strings.cpp | 16 |
12 files changed, 164 insertions, 39 deletions
diff --git a/src/ai/trolly/trolly.cpp b/src/ai/trolly/trolly.cpp index 2c30b682e..a65371368 100644 --- a/src/ai/trolly/trolly.cpp +++ b/src/ai/trolly/trolly.cpp @@ -954,7 +954,7 @@ static void AiNew_State_VerifyRoute(Player *p) // Check if we have enough money for it! if (p->ainew.new_cost > p->player_money - AI_MINIMUM_MONEY) { // Too bad.. - DEBUG(ai, 1, "Insufficient funds to build route (%d)", p->ainew.new_cost); + DEBUG(ai, 1, "Insufficient funds to build route (%" OTTD_PRINTF64 "d)", (int64)p->ainew.new_cost); p->ainew.state = AI_STATE_NOTHING; return; } @@ -1086,7 +1086,7 @@ static void AiNew_State_BuildPath(Player *p) } } - DEBUG(ai, 1, "Finished building path, cost: %d", p->ainew.new_cost); + DEBUG(ai, 1, "Finished building path, cost: %" OTTD_PRINTF64 "d", (int64)p->ainew.new_cost); p->ainew.state = AI_STATE_BUILD_DEPOT; } } diff --git a/src/build_vehicle_gui.cpp b/src/build_vehicle_gui.cpp index bd4938111..38b74cbb8 100644 --- a/src/build_vehicle_gui.cpp +++ b/src/build_vehicle_gui.cpp @@ -226,8 +226,8 @@ static int CDECL TrainEnginePowerVsRunningCostSorter(const void *a, const void * * Because of this, the return value have to be reversed as well and we return b - a instead of a - b. * Another thing is that both power and running costs should be doubled for multiheaded engines. * Since it would be multipling with 2 in both numerator and denumerator, it will even themselves out and we skip checking for multiheaded. */ - Money va = (rvi_a->running_cost_base * _price.running_rail[rvi_a->running_cost_class]) / max((uint16)1, rvi_a->power); - Money vb = (rvi_b->running_cost_base * _price.running_rail[rvi_b->running_cost_class]) / max((uint16)1, rvi_b->power); + Money va = (rvi_a->running_cost_base * _price.running_rail[rvi_a->running_cost_class]) / max(1U, (uint)rvi_a->power); + Money vb = (rvi_b->running_cost_base * _price.running_rail[rvi_b->running_cost_class]) / max(1U, (uint)rvi_b->power); int r = ClampToI32(vb - va); return _internal_sort_order ? -r : r; diff --git a/src/command.cpp b/src/command.cpp index 4370f9b19..e2133fe4f 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -677,25 +677,13 @@ CommandCost CommandCost::AddCost(CommandCost ret) CommandCost CommandCost::AddCost(Money cost) { - /* Overflow protection */ - if (cost < 0 && (this->cost + cost) > this->cost) { - this->cost = INT64_MIN; - } else if (cost > 0 && (this->cost + cost) < this->cost) { - this->cost = INT64_MAX; - } else { - this->cost += cost; - } + this->cost += cost; return *this; } CommandCost CommandCost::MultiplyCost(int factor) { - /* Overflow protection */ - if (factor != 0 && (INT64_MAX / myabs(factor)) < myabs(this->cost)) { - this->cost = (this->cost < 0 == factor < 0) ? INT64_MAX : INT64_MIN; - } else { - this->cost *= factor; - } + this->cost *= factor; return *this; } diff --git a/src/console_cmds.cpp b/src/console_cmds.cpp index 22047aaa6..6002da263 100644 --- a/src/console_cmds.cpp +++ b/src/console_cmds.cpp @@ -1249,7 +1249,7 @@ DEF_CONSOLE_CMD(ConPlayers) GetString(buffer, STR_00D1_DARK_BLUE + _player_colors[p->index], lastof(buffer)); IConsolePrintF(8, "#:%d(%s) Company Name: '%s' Year Founded: %d Money: %" OTTD_PRINTF64 "d Loan: %" OTTD_PRINTF64 "d Value: %" OTTD_PRINTF64 "d (T:%d, R:%d, P:%d, S:%d) %sprotected", - p->index + 1, buffer, npi->company_name, p->inaugurated_year, p->player_money, p->current_loan, CalculateCompanyValue(p), + p->index + 1, buffer, npi->company_name, p->inaugurated_year, (int64)p->player_money, (int64)p->current_loan, (int64)CalculateCompanyValue(p), /* trains */ npi->num_vehicle[0], /* lorry + bus */ npi->num_vehicle[1] + npi->num_vehicle[2], /* planes */ npi->num_vehicle[3], diff --git a/src/economy.cpp b/src/economy.cpp index 188b803e6..084da0c05 100644 --- a/src/economy.cpp +++ b/src/economy.cpp @@ -95,7 +95,7 @@ Money CalculateCompanyValue(const Player* p) value.AddCost(-p->current_loan); value.AddCost(p->player_money); - return max(value.GetCost(), 1LL); + return max(value.GetCost(), (Money)1); } /** if update is set to true, the economy is updated with this score diff --git a/src/engine_gui.cpp b/src/engine_gui.cpp index efe3b1c8b..f164301fd 100644 --- a/src/engine_gui.cpp +++ b/src/engine_gui.cpp @@ -120,7 +120,7 @@ void ShowEnginePreviewWindow(EngineID engine) static void DrawTrainEngineInfo(EngineID engine, int x, int y, int maxw) { const RailVehicleInfo *rvi = RailVehInfo(engine); - uint multihead = (rvi->railveh_type == RAILVEH_MULTIHEAD) ? 1 : 0; + int multihead = (rvi->railveh_type == RAILVEH_MULTIHEAD) ? 1 : 0; SetDParam(0, (_price.build_railvehicle >> 3) * rvi->base_cost >> 5); SetDParam(2, rvi->max_speed * 10 / 16); diff --git a/src/graph_gui.cpp b/src/graph_gui.cpp index 7231ec6f3..31f51f578 100644 --- a/src/graph_gui.cpp +++ b/src/graph_gui.cpp @@ -41,8 +41,8 @@ enum { }; /* Apparently these don't play well with enums. */ -static const int64 INVALID_DATAPOINT = INT64_MAX; // Value used for a datapoint that shouldn't be drawn. -static const uint INVALID_DATAPOINT_POS = UINT_MAX; // Used to determine if the previous point was drawn. +static const OverflowSafeInt64 INVALID_DATAPOINT = INT64_MAX; // Value used for a datapoint that shouldn't be drawn. +static const uint INVALID_DATAPOINT_POS = UINT_MAX; // Used to determine if the previous point was drawn. struct GraphDrawer { uint excluded_data; ///< bitmask of the datasets that shouldn't be displayed. @@ -70,9 +70,9 @@ struct GraphDrawer { static void DrawGraph(const GraphDrawer *gw) { - uint x, y; ///< Reused whenever x and y coordinates are needed. - int64 highest_value; ///< Highest value to be drawn. - int x_axis_offset; ///< Distance from the top of the graph to the x axis. + uint x, y; ///< Reused whenever x and y coordinates are needed. + OverflowSafeInt64 highest_value; ///< Highest value to be drawn. + int x_axis_offset; ///< Distance from the top of the graph to the x axis. /* the colors and cost array of GraphDrawer must accomodate * both values for cargo and players. So if any are higher, quit */ @@ -515,7 +515,7 @@ static void DeliveredCargoGraphWndProc(Window *w, WindowEvent *e) if (p->is_active) { gd.colors[numd] = _colour_gradient[p->player_color][6]; for (int j = gd.num_on_x_axis, i = 0; --j >= 0;) { - gd.cost[numd][i] = (j >= p->num_valid_stat_ent) ? INVALID_DATAPOINT : p->old_economy[j].delivered_cargo; + gd.cost[numd][i] = (j >= p->num_valid_stat_ent) ? INVALID_DATAPOINT : (OverflowSafeInt64)p->old_economy[j].delivered_cargo; i++; } } @@ -582,7 +582,7 @@ static void PerformanceHistoryWndProc(Window *w, WindowEvent *e) if (p->is_active) { gd.colors[numd] = _colour_gradient[p->player_color][6]; for (int j = gd.num_on_x_axis, i = 0; --j >= 0;) { - gd.cost[numd][i] = (j >= p->num_valid_stat_ent) ? INVALID_DATAPOINT : p->old_economy[j].performance_history; + gd.cost[numd][i] = (j >= p->num_valid_stat_ent) ? INVALID_DATAPOINT : (OverflowSafeInt64)p->old_economy[j].performance_history; i++; } } diff --git a/src/helpers.hpp b/src/helpers.hpp index c3f7f7930..f185eecec 100644 --- a/src/helpers.hpp +++ b/src/helpers.hpp @@ -158,4 +158,139 @@ template <typename T> void ToggleBitT(T &t, int bit_index) t = (T)(t ^ ((T)1 << bit_index)); } +/** + * Overflow safe template for integers, i.e. integers that will never overflow + * you multiply the maximum value with 2, or add 2, or substract somethng from + * the minimum value, etc. + * @param T the type these integers are stored with. + * @param T_MAX the maximum value for the integers. + * @param T_MIN the minimum value for the integers. + */ +template <class T, T T_MAX, T T_MIN> +class OverflowSafeInt +{ +private: + /** The non-overflow safe backend to store the value in. */ + T m_value; +public: + OverflowSafeInt() : m_value(0) { } + + OverflowSafeInt(const OverflowSafeInt& other) { this->m_value = other.m_value; } + OverflowSafeInt(const int64 int_) { this->m_value = int_; } + + FORCEINLINE OverflowSafeInt& operator = (const OverflowSafeInt& other) { this->m_value = other.m_value; return *this; } + + FORCEINLINE OverflowSafeInt operator - () const { return OverflowSafeInt(-this->m_value); } + + /** + * Safe implementation of addition. + * @param other the amount to add + * @note when the addition would yield more than T_MAX (or less than T_MIN), + * it will be T_MAX (respectively T_MIN). + */ + FORCEINLINE OverflowSafeInt& operator += (const OverflowSafeInt& other) + { + if ((T_MAX - myabs(other.m_value)) < myabs(this->m_value) && + (this->m_value < 0) == (other.m_value < 0)) { + this->m_value = (this->m_value < 0) ? T_MIN : T_MAX ; + } else { + this->m_value += other.m_value; + } + return *this; + } + + /* Operators for addition and substraction */ + FORCEINLINE OverflowSafeInt operator + (const OverflowSafeInt& other) const { OverflowSafeInt result = *this; result += other; return result; } + FORCEINLINE OverflowSafeInt operator + (const int other) const { OverflowSafeInt result = *this; result += (int64)other; return result; } + FORCEINLINE OverflowSafeInt operator + (const uint other) const { OverflowSafeInt result = *this; result += (int64)other; return result; } + FORCEINLINE OverflowSafeInt& operator -= (const OverflowSafeInt& other) { return *this += (-other); } + FORCEINLINE OverflowSafeInt operator - (const OverflowSafeInt& other) const { OverflowSafeInt result = *this; result -= other; return result; } + FORCEINLINE OverflowSafeInt operator - (const int other) const { OverflowSafeInt result = *this; result -= (int64)other; return result; } + FORCEINLINE OverflowSafeInt operator - (const uint other) const { OverflowSafeInt result = *this; result -= (int64)other; return result; } + + FORCEINLINE OverflowSafeInt& operator ++ (int) { return *this += 1; } + FORCEINLINE OverflowSafeInt& operator -- (int) { return *this += -1; } + + /** + * Safe implementation of multiplication. + * @param factor the factor to multiply this with. + * @note when the multiplication would yield more than T_MAX (or less than T_MIN), + * it will be T_MAX (respectively T_MIN). + */ + FORCEINLINE OverflowSafeInt& operator *= (const int factor) + { + if (factor != 0 && (T_MAX / myabs(factor)) < myabs(this->m_value)) { + this->m_value = ((this->m_value < 0) == (factor < 0)) ? T_MAX : T_MIN ; + } else { + this->m_value *= factor ; + } + return *this; + } + + /* Operators for multiplication */ + FORCEINLINE OverflowSafeInt operator * (const int64 factor) const { OverflowSafeInt result = *this; result *= factor; return result; } + FORCEINLINE OverflowSafeInt operator * (const int factor) const { OverflowSafeInt result = *this; result *= (int64)factor; return result; } + FORCEINLINE OverflowSafeInt operator * (const uint factor) const { OverflowSafeInt result = *this; result *= (int64)factor; return result; } + FORCEINLINE OverflowSafeInt operator * (const uint16 factor) const { OverflowSafeInt result = *this; result *= (int64)factor; return result; } + FORCEINLINE OverflowSafeInt operator * (const byte factor) const { OverflowSafeInt result = *this; result *= (int64)factor; return result; } + + /* Operators for division */ + FORCEINLINE OverflowSafeInt& operator /= (const int divisor) { this->m_value /= divisor; return *this; } + FORCEINLINE OverflowSafeInt operator / (const OverflowSafeInt& divisor) const { OverflowSafeInt result = *this; result /= divisor.m_value; return result; } + FORCEINLINE OverflowSafeInt operator / (const int divisor) const { OverflowSafeInt result = *this; result /= divisor; return result; } + FORCEINLINE OverflowSafeInt operator / (const uint divisor) const { OverflowSafeInt result = *this; result /= (int)divisor; return result; } + + /* Operators for modulo */ + FORCEINLINE OverflowSafeInt& operator %= (const int divisor) { this->m_value %= divisor; return *this; } + FORCEINLINE OverflowSafeInt operator % (const int divisor) const { OverflowSafeInt result = *this; result %= divisor; return result; } + + /* Operators for shifting */ + FORCEINLINE OverflowSafeInt& operator <<= (const int shift) { this->m_value <<= shift; return *this; } + FORCEINLINE OverflowSafeInt operator << (const int shift) const { OverflowSafeInt result = *this; result <<= shift; return result; } + FORCEINLINE OverflowSafeInt& operator >>= (const int shift) { this->m_value >>= shift; return *this; } + FORCEINLINE OverflowSafeInt operator >> (const int shift) const { OverflowSafeInt result = *this; result >>= shift; return result; } + + /* Operators for (in)equality when comparing overflow safe ints */ + FORCEINLINE bool operator == (const OverflowSafeInt& other) const { return this->m_value == other.m_value; } + FORCEINLINE bool operator != (const OverflowSafeInt& other) const { return !(*this == other); } + FORCEINLINE bool operator > (const OverflowSafeInt& other) const { return this->m_value > other.m_value; } + FORCEINLINE bool operator >= (const OverflowSafeInt& other) const { return this->m_value >= other.m_value; } + FORCEINLINE bool operator < (const OverflowSafeInt& other) const { return !(*this >= other); } + FORCEINLINE bool operator <= (const OverflowSafeInt& other) const { return !(*this > other); } + + /* Operators for (in)equality when comparing non-overflow safe ints */ + FORCEINLINE bool operator == (const int other) const { return this->m_value == other; } + FORCEINLINE bool operator != (const int other) const { return !(*this == other); } + FORCEINLINE bool operator > (const int other) const { return this->m_value > other; } + FORCEINLINE bool operator >= (const int other) const { return this->m_value >= other; } + FORCEINLINE bool operator < (const int other) const { return !(*this >= other); } + FORCEINLINE bool operator <= (const int other) const { return !(*this > other); } + + FORCEINLINE operator int64 () const { return this->m_value; } +}; + +/* Sometimes we got int64 operator OverflowSafeInt instead of vice versa. Handle that properly */ +template <class T, int64 T_MAX, int64 T_MIN> FORCEINLINE OverflowSafeInt<T, T_MAX, T_MIN> operator + (int64 a, OverflowSafeInt<T, T_MAX, T_MIN> b) { return b + a; } +template <class T, int64 T_MAX, int64 T_MIN> FORCEINLINE OverflowSafeInt<T, T_MAX, T_MIN> operator - (int64 a, OverflowSafeInt<T, T_MAX, T_MIN> b) { return -b + a; } +template <class T, int64 T_MAX, int64 T_MIN> FORCEINLINE OverflowSafeInt<T, T_MAX, T_MIN> operator * (int64 a, OverflowSafeInt<T, T_MAX, T_MIN> b) { return b * a; } +template <class T, int64 T_MAX, int64 T_MIN> FORCEINLINE OverflowSafeInt<T, T_MAX, T_MIN> operator / (int64 a, OverflowSafeInt<T, T_MAX, T_MIN> b) { return (OverflowSafeInt<T, T_MAX, T_MIN>)a / (int)b; } + +/* Sometimes we got int operator OverflowSafeInt instead of vice versa. Handle that properly */ +template <class T, int64 T_MAX, int64 T_MIN> FORCEINLINE OverflowSafeInt<T, T_MAX, T_MIN> operator + (int a, OverflowSafeInt<T, T_MAX, T_MIN> b) { return b + a; } +template <class T, int64 T_MAX, int64 T_MIN> FORCEINLINE OverflowSafeInt<T, T_MAX, T_MIN> operator - (int a, OverflowSafeInt<T, T_MAX, T_MIN> b) { return -b + a; } +template <class T, int64 T_MAX, int64 T_MIN> FORCEINLINE OverflowSafeInt<T, T_MAX, T_MIN> operator * (int a, OverflowSafeInt<T, T_MAX, T_MIN> b) { return b * a; } +template <class T, int64 T_MAX, int64 T_MIN> FORCEINLINE OverflowSafeInt<T, T_MAX, T_MIN> operator / (int a, OverflowSafeInt<T, T_MAX, T_MIN> b) { return (OverflowSafeInt<T, T_MAX, T_MIN>)a / (int)b; } + +/* Sometimes we got uint operator OverflowSafeInt instead of vice versa. Handle that properly */ +template <class T, int64 T_MAX, int64 T_MIN> FORCEINLINE OverflowSafeInt<T, T_MAX, T_MIN> operator + (uint a, OverflowSafeInt<T, T_MAX, T_MIN> b) { return b + a; } +template <class T, int64 T_MAX, int64 T_MIN> FORCEINLINE OverflowSafeInt<T, T_MAX, T_MIN> operator - (uint a, OverflowSafeInt<T, T_MAX, T_MIN> b) { return -b + a; } +template <class T, int64 T_MAX, int64 T_MIN> FORCEINLINE OverflowSafeInt<T, T_MAX, T_MIN> operator * (uint a, OverflowSafeInt<T, T_MAX, T_MIN> b) { return b * a; } +template <class T, int64 T_MAX, int64 T_MIN> FORCEINLINE OverflowSafeInt<T, T_MAX, T_MIN> operator / (uint a, OverflowSafeInt<T, T_MAX, T_MIN> b) { return (OverflowSafeInt<T, T_MAX, T_MIN>)a / (int)b; } + +/* Sometimes we got byte operator OverflowSafeInt instead of vice versa. Handle that properly */ +template <class T, int64 T_MAX, int64 T_MIN> FORCEINLINE OverflowSafeInt<T, T_MAX, T_MIN> operator + (byte a, OverflowSafeInt<T, T_MAX, T_MIN> b) { return b + a; } +template <class T, int64 T_MAX, int64 T_MIN> FORCEINLINE OverflowSafeInt<T, T_MAX, T_MIN> operator - (byte a, OverflowSafeInt<T, T_MAX, T_MIN> b) { return -b + a; } +template <class T, int64 T_MAX, int64 T_MIN> FORCEINLINE OverflowSafeInt<T, T_MAX, T_MIN> operator * (byte a, OverflowSafeInt<T, T_MAX, T_MIN> b) { return b * a; } +template <class T, int64 T_MAX, int64 T_MIN> FORCEINLINE OverflowSafeInt<T, T_MAX, T_MIN> operator / (byte a, OverflowSafeInt<T, T_MAX, T_MIN> b) { return (OverflowSafeInt<T, T_MAX, T_MIN>)a / (int)b; } + #endif /* HELPERS_HPP */ diff --git a/src/openttd.h b/src/openttd.h index 5514c2165..2fdf8e0a9 100644 --- a/src/openttd.h +++ b/src/openttd.h @@ -69,7 +69,9 @@ typedef uint16 SignID; typedef uint16 GroupID; typedef uint16 EngineRenewID; typedef uint16 DestinationID; -typedef int64 Money; + +typedef OverflowSafeInt<int64, INT64_MAX, INT64_MIN> OverflowSafeInt64; +typedef OverflowSafeInt64 Money; /* DestinationID must be at least as large as every these below, because it can * be any of them diff --git a/src/rail_cmd.cpp b/src/rail_cmd.cpp index 973737db1..00f397453 100644 --- a/src/rail_cmd.cpp +++ b/src/rail_cmd.cpp @@ -267,7 +267,7 @@ static CommandCost CheckRailSlope(Slope tileh, TrackBits rail_bits, TrackBits ex ) return_cmd_error(STR_1000_LAND_SLOPED_IN_WRONG_DIRECTION); Foundation f_old = GetRailFoundation(tileh, existing); - return CommandCost(f_new != f_old ? _price.terraform : 0); + return CommandCost(f_new != f_old ? _price.terraform : (Money)0); } /* Validate functions for rail building */ diff --git a/src/road_cmd.cpp b/src/road_cmd.cpp index 6d3669925..1b404a97f 100644 --- a/src/road_cmd.cpp +++ b/src/road_cmd.cpp @@ -366,7 +366,7 @@ static CommandCost CheckRoadSlope(Slope tileh, RoadBits* pieces, RoadBits existi /* foundation is used. Whole tile is leveled up */ if ((~_valid_tileh_slopes_road[1][tileh] & road_bits) == ROAD_NONE) { - return CommandCost(existing != ROAD_NONE ? 0 : _price.terraform); + return CommandCost(existing != ROAD_NONE ? (Money)0 : _price.terraform); } /* Force straight roads. */ diff --git a/src/strings.cpp b/src/strings.cpp index a82bdd15a..393f45f11 100644 --- a/src/strings.cpp +++ b/src/strings.cpp @@ -331,15 +331,15 @@ static char *FormatTinyDate(char *buff, Date date, const char* last) static char *FormatGenericCurrency(char *buff, const CurrencySpec *spec, Money number, bool compact, const char* last) { - const char* multiplier = ""; + /* We are going to make number absolute for printing, so + * keep this piece of data as we need it later on */ + bool negative = number < 0; + const char *multiplier = ""; char buf[40]; - char* p; + char *p; int j; - /* Multiply by exchange rate, but do it safely. */ - CommandCost cs(number); - cs.MultiplyCost(spec->rate); - number = cs.GetCost(); + number *= spec->rate; /* convert from negative */ if (number < 0) { @@ -374,7 +374,7 @@ static char *FormatGenericCurrency(char *buff, const CurrencySpec *spec, Money n *--p = spec->separator; j = 3; } - *--p = '0' + number % 10; + *--p = '0' + (char)(number % 10); } while ((number /= 10) != 0); buff = strecpy(buff, p, last); @@ -385,7 +385,7 @@ static char *FormatGenericCurrency(char *buff, const CurrencySpec *spec, Money n * The only remaining value is 1 (prefix), so everything that is not 0 */ if (spec->symbol_pos != 0) buff = strecpy(buff, spec->suffix, last); - if (cs.GetCost() < 0) { + if (negative) { if (buff + Utf8CharLen(SCC_PREVIOUS_COLOUR) > last) return buff; buff += Utf8Encode(buff, SCC_PREVIOUS_COLOUR); *buff = '\0'; |