|
@@ -35,7 +35,6 @@
|
|
#include "ucaps.h"
|
|
#include "ucaps.h"
|
|
#include "color.h"
|
|
#include "color.h"
|
|
#include "variant.h"
|
|
#include "variant.h"
|
|
-#include <stdio.h>
|
|
|
|
#define MAX_DIGITS 6
|
|
#define MAX_DIGITS 6
|
|
#define UPPERCASE(m_c) (((m_c)>='a' && (m_c)<='z')?((m_c)-('a'-'A')):(m_c))
|
|
#define UPPERCASE(m_c) (((m_c)>='a' && (m_c)<='z')?((m_c)-('a'-'A')):(m_c))
|
|
#define LOWERCASE(m_c) (((m_c)>='A' && (m_c)<='Z')?((m_c)+('a'-'A')):(m_c))
|
|
#define LOWERCASE(m_c) (((m_c)>='A' && (m_c)<='Z')?((m_c)+('a'-'A')):(m_c))
|
|
@@ -983,7 +982,7 @@ String String::num(double p_num,int p_decimals) {
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
-String String::num_int64(int64_t p_num) {
|
|
|
|
|
|
+String String::num_int64(int64_t p_num, int base, bool capitalize_hex) {
|
|
|
|
|
|
bool sign=p_num<0;
|
|
bool sign=p_num<0;
|
|
int64_t num=ABS(p_num);
|
|
int64_t num=ABS(p_num);
|
|
@@ -992,7 +991,7 @@ String String::num_int64(int64_t p_num) {
|
|
|
|
|
|
int chars=0;
|
|
int chars=0;
|
|
do {
|
|
do {
|
|
- n/=10;
|
|
|
|
|
|
+ n/=base;
|
|
chars++;
|
|
chars++;
|
|
} while(n);
|
|
} while(n);
|
|
|
|
|
|
@@ -1004,8 +1003,15 @@ String String::num_int64(int64_t p_num) {
|
|
c[chars]=0;
|
|
c[chars]=0;
|
|
n=num;
|
|
n=num;
|
|
do {
|
|
do {
|
|
- c[--chars]='0'+(n%10);
|
|
|
|
- n/=10;
|
|
|
|
|
|
+ int mod = n%base;
|
|
|
|
+ if (mod >= 10) {
|
|
|
|
+ char a = (capitalize_hex ? 'A' : 'a');
|
|
|
|
+ c[--chars]=a+(mod - 10);
|
|
|
|
+ } else {
|
|
|
|
+ c[--chars]='0'+mod;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ n/=base;
|
|
} while(n);
|
|
} while(n);
|
|
|
|
|
|
if (sign)
|
|
if (sign)
|
|
@@ -3520,41 +3526,84 @@ String rtoss(double p_val) {
|
|
return String::num_scientific(p_val);
|
|
return String::num_scientific(p_val);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+// Right-pad with a character.
|
|
|
|
+String String::rpad(int min_length, const String& character) const {
|
|
|
|
+ String s = *this;
|
|
|
|
+ int padding = min_length - s.length();
|
|
|
|
+ if (padding > 0) {
|
|
|
|
+ for (int i = 0; i < padding; i++) s = s + character;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return s;
|
|
|
|
+}
|
|
|
|
+// Left-pad with a character.
|
|
|
|
+String String::lpad(int min_length, const String& character) const {
|
|
|
|
+ String s = *this;
|
|
|
|
+ int padding = min_length - s.length();
|
|
|
|
+ if (padding > 0) {
|
|
|
|
+ for (int i = 0; i < padding; i++) s = character + s;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return s;
|
|
|
|
+}
|
|
|
|
+
|
|
// sprintf is implemented in GDScript via:
|
|
// sprintf is implemented in GDScript via:
|
|
// "fish %s pie" % "frog"
|
|
// "fish %s pie" % "frog"
|
|
// "fish %s %d pie" % ["frog", 12]
|
|
// "fish %s %d pie" % ["frog", 12]
|
|
-const int FORMAT_BUFFER_SIZE = 1024;
|
|
|
|
-const int OUTPUT_BUFFER_SIZE = 1024 * 100;
|
|
|
|
String String::sprintf(const Array& values) const {
|
|
String String::sprintf(const Array& values) const {
|
|
|
|
|
|
String formatted;
|
|
String formatted;
|
|
CharType* self = (CharType*)c_str();
|
|
CharType* self = (CharType*)c_str();
|
|
bool in_format = false;
|
|
bool in_format = false;
|
|
int value_index = 0;
|
|
int value_index = 0;
|
|
- char format_format[FORMAT_BUFFER_SIZE] = "%d";
|
|
|
|
|
|
+ int min_chars;
|
|
|
|
+ int num_decimals;
|
|
|
|
+ bool in_decimals;
|
|
|
|
+ bool pad_with_zeroes;
|
|
|
|
+ bool left_justified;
|
|
|
|
+ bool show_sign;
|
|
|
|
|
|
for (; *self; self++) {
|
|
for (; *self; self++) {
|
|
const CharType c = *self;
|
|
const CharType c = *self;
|
|
|
|
|
|
if (in_format) { // We have % - lets see what else we get.
|
|
if (in_format) { // We have % - lets see what else we get.
|
|
switch (c) {
|
|
switch (c) {
|
|
- case '%': // Manage %% as %
|
|
|
|
|
|
+ case '%': { // Replace %% with %
|
|
formatted += chr(c);
|
|
formatted += chr(c);
|
|
in_format = false;
|
|
in_format = false;
|
|
break;
|
|
break;
|
|
-
|
|
|
|
|
|
+ }
|
|
case 'd': // Integer (signed)
|
|
case 'd': // Integer (signed)
|
|
case 'o': // Octal
|
|
case 'o': // Octal
|
|
case 'x': // Hexadecimal (lowercase)
|
|
case 'x': // Hexadecimal (lowercase)
|
|
- case 'X': // Hexadecimal (uppercase)
|
|
|
|
|
|
+ case 'X': { // Hexadecimal (uppercase)
|
|
if (values[value_index].is_num()) {
|
|
if (values[value_index].is_num()) {
|
|
- char buffer[OUTPUT_BUFFER_SIZE];
|
|
|
|
- int value = values[value_index];
|
|
|
|
- format_format[1] = c;
|
|
|
|
- format_format[2] = 0;
|
|
|
|
- ::sprintf(buffer, format_format, value);
|
|
|
|
-
|
|
|
|
- formatted += String(buffer);
|
|
|
|
|
|
+ int64_t value = values[value_index];
|
|
|
|
+ int base;
|
|
|
|
+ bool capitalize = false;
|
|
|
|
+ switch (c) {
|
|
|
|
+ case 'd': base = 10; break;
|
|
|
|
+ case 'o': base = 8; break;
|
|
|
|
+ case 'x': base = 16; break;
|
|
|
|
+ case 'X': base = 16; capitalize = true; break;
|
|
|
|
+ }
|
|
|
|
+ // Get basic number.
|
|
|
|
+ String str = String::num_int64(value, base, capitalize);
|
|
|
|
+
|
|
|
|
+ // Sign.
|
|
|
|
+ if (show_sign && value >= 0) {
|
|
|
|
+ str = str.insert(0, "+");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Padding.
|
|
|
|
+ String pad_char = pad_with_zeroes ? String("0") : String(" ");
|
|
|
|
+ if (left_justified) {
|
|
|
|
+ str = str.rpad(min_chars, pad_char);
|
|
|
|
+ } else {
|
|
|
|
+ str = str.lpad(min_chars, pad_char);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ formatted += str;
|
|
++value_index;
|
|
++value_index;
|
|
in_format = false;
|
|
in_format = false;
|
|
} else {
|
|
} else {
|
|
@@ -3562,14 +3611,28 @@ String String::sprintf(const Array& values) const {
|
|
}
|
|
}
|
|
|
|
|
|
break;
|
|
break;
|
|
-
|
|
|
|
- case 'f': // Float
|
|
|
|
|
|
+ }
|
|
|
|
+ case 'f': { // Float
|
|
if (values[value_index].is_num()) {
|
|
if (values[value_index].is_num()) {
|
|
- char buffer[OUTPUT_BUFFER_SIZE];
|
|
|
|
double value = values[value_index];
|
|
double value = values[value_index];
|
|
- ::sprintf(buffer, "%f", value);
|
|
|
|
|
|
+ String str = String::num(value, num_decimals);
|
|
|
|
+
|
|
|
|
+ // Pad decimals out.
|
|
|
|
+ str = str.pad_decimals(num_decimals);
|
|
|
|
+
|
|
|
|
+ // Show sign
|
|
|
|
+ if (show_sign && value >= 0) {
|
|
|
|
+ str = str.insert(0, "+");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Padding
|
|
|
|
+ if (left_justified) {
|
|
|
|
+ str = str.rpad(min_chars);
|
|
|
|
+ } else {
|
|
|
|
+ str = str.lpad(min_chars);
|
|
|
|
+ }
|
|
|
|
|
|
- formatted += String(buffer);
|
|
|
|
|
|
+ formatted += str;
|
|
++value_index;
|
|
++value_index;
|
|
in_format = false;
|
|
in_format = false;
|
|
} else {
|
|
} else {
|
|
@@ -3577,26 +3640,49 @@ String String::sprintf(const Array& values) const {
|
|
}
|
|
}
|
|
|
|
|
|
break;
|
|
break;
|
|
|
|
+ }
|
|
|
|
+ case 's': { // String
|
|
|
|
+ String str = values[value_index];
|
|
|
|
+ // Padding.
|
|
|
|
+ if (left_justified) {
|
|
|
|
+ str = str.rpad(min_chars);
|
|
|
|
+ } else {
|
|
|
|
+ str = str.lpad(min_chars);
|
|
|
|
+ }
|
|
|
|
|
|
- case 's': // String
|
|
|
|
- String value = values[value_index];
|
|
|
|
- formatted += value;
|
|
|
|
|
|
+ formatted += str;
|
|
++value_index;
|
|
++value_index;
|
|
in_format = false;
|
|
in_format = false;
|
|
break;
|
|
break;
|
|
-
|
|
|
|
- // case '-': // Left justify
|
|
|
|
- // break;
|
|
|
|
-
|
|
|
|
- // case '+': // Show + if positive.
|
|
|
|
- // break;
|
|
|
|
-
|
|
|
|
- // case '0': case '1': case '2': case '3': case '4':
|
|
|
|
- // case '5': case '6': case '7': case '8': case '9':
|
|
|
|
- // break;
|
|
|
|
-
|
|
|
|
- // case '.': // Float separtor.
|
|
|
|
- // break;
|
|
|
|
|
|
+ }
|
|
|
|
+ case '-': { // Left justify
|
|
|
|
+ left_justified = true;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ case '+': { // Show + if positive.
|
|
|
|
+ show_sign = true;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ case '0': case '1': case '2': case '3': case '4':
|
|
|
|
+ case '5': case '6': case '7': case '8': case '9': {
|
|
|
|
+ int n = c - '0';
|
|
|
|
+ if (in_decimals) {
|
|
|
|
+ num_decimals *= 10;
|
|
|
|
+ num_decimals += n;
|
|
|
|
+ } else {
|
|
|
|
+ if (c == '0' && min_chars == 0) {
|
|
|
|
+ pad_with_zeroes = true;
|
|
|
|
+ } else {
|
|
|
|
+ min_chars *= 10;
|
|
|
|
+ min_chars += n;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ case '.': // Float separtor.
|
|
|
|
+ in_decimals = true;
|
|
|
|
+ num_decimals = 0; // We want to add the value manually.
|
|
|
|
+ break;
|
|
|
|
|
|
// case '*': // Dyanmic width, based on value.
|
|
// case '*': // Dyanmic width, based on value.
|
|
// break;
|
|
// break;
|
|
@@ -3608,6 +3694,13 @@ String String::sprintf(const Array& values) const {
|
|
switch (c) {
|
|
switch (c) {
|
|
case '%':
|
|
case '%':
|
|
in_format = true;
|
|
in_format = true;
|
|
|
|
+ // Back to defaults:
|
|
|
|
+ min_chars = 0;
|
|
|
|
+ num_decimals = 6;
|
|
|
|
+ pad_with_zeroes = false;
|
|
|
|
+ left_justified = false;
|
|
|
|
+ show_sign = false;
|
|
|
|
+ in_decimals = false;
|
|
break;
|
|
break;
|
|
default:
|
|
default:
|
|
formatted += chr(c);
|
|
formatted += chr(c);
|