diff --git a/Sources/Plasma/CoreLib/plFileSystem.cpp b/Sources/Plasma/CoreLib/plFileSystem.cpp index 8d223d11..9a318cde 100644 --- a/Sources/Plasma/CoreLib/plFileSystem.cpp +++ b/Sources/Plasma/CoreLib/plFileSystem.cpp @@ -556,9 +556,9 @@ plString plFileSystem::ConvertFileSize(uint64_t size) float decimal = static_cast(last_div) / 1024.f; // Kilobytes are so small that we only care about whole numbers if (i < 1) - return plString::Format("%.0f %s", decimal, labels[i]); + return plFormat("{.0f} {}", decimal, labels[i]); else - return plString::Format("%.2f %s", decimal, labels[i]); + return plFormat("{.2f} {}", decimal, labels[i]); } last_div = my_div; } diff --git a/Sources/Plasma/CoreLib/plFormat.cpp b/Sources/Plasma/CoreLib/plFormat.cpp index 01ce94df..b7554598 100644 --- a/Sources/Plasma/CoreLib/plFormat.cpp +++ b/Sources/Plasma/CoreLib/plFormat.cpp @@ -147,7 +147,7 @@ namespace plFormat_Private case '5': case '6': case '7': case '8': case '9': { char *end = nullptr; - spec.fPrecisionLeft = strtol(ptr, &end, 10); + spec.fMinimumLength = strtol(ptr, &end, 10); ptr = end - 1; } break; @@ -155,7 +155,7 @@ namespace plFormat_Private { hsAssert(*(ptr + 1), "Unterminated format specifier"); char *end = nullptr; - spec.fPrecisionRight = strtol(ptr + 1, &end, 10); + spec.fPrecision = strtol(ptr + 1, &end, 10); ptr = end - 1; } break; @@ -227,16 +227,16 @@ static plStringBuffer _formatNumeric(const plFormat_Private::FormatSpec &f max = 1; plStringBuffer buffer; - if (format.fPrecisionLeft > max) { - char *output = buffer.CreateWritableBuffer(format.fPrecisionLeft); - memset(output, pad, format.fPrecisionLeft); + if (format.fMinimumLength > max) { + char *output = buffer.CreateWritableBuffer(format.fMinimumLength); + memset(output, pad, format.fMinimumLength); if (format.fAlignment == plFormat_Private::kAlignLeft) { _IFormatNumeric_Impl<_IType>(output + max, value, radix, upperCase); } else { - _IFormatNumeric_Impl<_IType>(output + format.fPrecisionLeft, + _IFormatNumeric_Impl<_IType>(output + format.fMinimumLength, value, radix, upperCase); } - output[format.fPrecisionLeft] = 0; + output[format.fMinimumLength] = 0; } else { char *output = buffer.CreateWritableBuffer(max); _IFormatNumeric_Impl<_IType>(output + max, value, radix, upperCase); @@ -268,21 +268,21 @@ static plStringBuffer _formatDecimal(const plFormat_Private::FormatSpec &f plStringBuffer buffer; char *output; - if (format.fPrecisionLeft > max) { - output = buffer.CreateWritableBuffer(format.fPrecisionLeft); - memset(output, pad, format.fPrecisionLeft); + if (format.fMinimumLength > max) { + output = buffer.CreateWritableBuffer(format.fMinimumLength); + memset(output, pad, format.fMinimumLength); if (format.fAlignment == plFormat_Private::kAlignLeft) _IFormatNumeric_Impl<_IType>(output + max, abs, 10); else - _IFormatNumeric_Impl<_IType>(output + format.fPrecisionLeft, abs, 10); - output[format.fPrecisionLeft] = 0; + _IFormatNumeric_Impl<_IType>(output + format.fMinimumLength, abs, 10); + output[format.fMinimumLength] = 0; } else { output = buffer.CreateWritableBuffer(max); _IFormatNumeric_Impl<_IType>(output + max, abs, 10); output[max] = 0; } - int signPos = format.fPrecisionLeft - static_cast(max); + int signPos = format.fPrecision - static_cast(max); if (signPos < 0) signPos = 0; @@ -296,7 +296,7 @@ static plStringBuffer _formatDecimal(const plFormat_Private::FormatSpec &f static plStringBuffer _formatChar(const plFormat_Private::FormatSpec &format, int ch) { - hsAssert(format.fPrecisionLeft == 0 && format.fPadChar == 0, + hsAssert(format.fMinimumLength == 0 && format.fPadChar == 0, "Char formatting does not currently support padding"); // Don't need to nul-terminate this, since plStringBuffer's constructor fixes it @@ -392,6 +392,60 @@ _PL_FORMAT_IMPL_INT_TYPE(long, unsigned long) _PL_FORMAT_IMPL_INT_TYPE(int64_t, uint64_t) #endif +PL_FORMAT_IMPL(float) +{ + return PL_FORMAT_FORWARD(format, double(value)); +} + +PL_FORMAT_IMPL(double) +{ + char pad = format.fPadChar ? format.fPadChar : ' '; + + // Cheating a bit here -- just pass it along to cstdio + char format_buffer[32]; + size_t end = 0; + + format_buffer[end++] = '%'; + if (format.fPrecision) { + int count = snprintf(format_buffer + end, arrsize(format_buffer) - end, + ".%d", format.fPrecision); + + // Ensure one more space (excluding \0) is available for the format specifier + hsAssert(count > 0 && count + end + 2 < arrsize(format_buffer), + "Not enough space for format string"); + end += count; + } + + format_buffer[end++] = + (format.fFloatClass == plFormat_Private::kFloatExp) ? 'e' : + (format.fFloatClass == plFormat_Private::kFloatExpUpper) ? 'E' : + (format.fFloatClass == plFormat_Private::kFloatFixed) ? 'f' : 'g'; + format_buffer[end] = 0; + + int format_size = snprintf(nullptr, 0, format_buffer, value); + hsAssert(format_size > 0, "Your libc doesn't support reporting format size"); + plStringBuffer out_buffer; + char *output; + + if (format.fMinimumLength > format_size) { + output = out_buffer.CreateWritableBuffer(format.fMinimumLength); + memset(output, pad, format.fMinimumLength); + if (format.fAlignment == plFormat_Private::kAlignLeft) { + snprintf(output, format_size + 1, format_buffer, value); + output[format_size] = pad; // snprintf overwrites this + output[format.fMinimumLength] = 0; + } else { + snprintf(output + (format.fMinimumLength - format_size), format_size + 1, + format_buffer, value); + } + } else { + output = out_buffer.CreateWritableBuffer(format_size); + snprintf(output, format_size + 1, format_buffer, value); + } + + return out_buffer; +} + PL_FORMAT_IMPL(char) { /* Note: The use of unsigned here is not a typo -- we only format decimal @@ -445,18 +499,18 @@ static plStringBuffer _formatString(const plFormat_Private::FormatSpec &fo { char pad = format.fPadChar ? format.fPadChar : ' '; - if (format.fPrecisionLeft > value.GetSize()) { + if (format.fMinimumLength > value.GetSize()) { plStringBuffer buf; - char *output = buf.CreateWritableBuffer(format.fPrecisionLeft); - memset(output, pad, format.fPrecisionLeft); + char *output = buf.CreateWritableBuffer(format.fMinimumLength); + memset(output, pad, format.fMinimumLength); if (format.fAlignment == plFormat_Private::kAlignRight) { - memcpy(output + (format.fPrecisionLeft - value.GetSize()), + memcpy(output + (format.fMinimumLength - value.GetSize()), value.GetData(), value.GetSize()); } else { memcpy(output, value.GetData(), value.GetSize()); } - output[format.fPrecisionLeft] = 0; + output[format.fMinimumLength] = 0; return buf; } diff --git a/Sources/Plasma/CoreLib/plFormat.h b/Sources/Plasma/CoreLib/plFormat.h index f2940799..a3901b01 100644 --- a/Sources/Plasma/CoreLib/plFormat.h +++ b/Sources/Plasma/CoreLib/plFormat.h @@ -73,7 +73,7 @@ Mead, WA 99021 * `b` | Binary * `d` | Decimal (default) -- when used with char types, outputs a number instead of the UTF representation of the char * `c` | UTF character (default for character types) - * `FFF.EEE` | Use FFF.EEE floating point precision + * `.EEE` | Use EEE digits of floating point precision * `f` | Fixed floating point format (ddd.ddd) * `e` | Exponent notation for floating point (d.ddde[+/-]dd) * `E` | Same as 'e' format, but with upper case E (d.dddE[+/-]dd) @@ -114,8 +114,8 @@ namespace plFormat_Private /** Represents a parsed format tag, for use in formatter implementations. */ struct FormatSpec { - int fPrecisionLeft = 0; /**< Requested padding and/or precision */ - int fPrecisionRight = 0; /**< Requested precision after the . for floating-point */ + int fMinimumLength = 0; /**< Requested minimum padding length */ + int fPrecision = 0; /**< Requested precision for floating-point */ char fPadChar = 0; /**< Explicit padding char (default is space) */ Alignment fAlignment = kAlignDefault; /**< Requested pad alignment */ @@ -192,6 +192,10 @@ namespace plFormat_Private PL_FORMAT_TYPE(int64_t) PL_FORMAT_TYPE(uint64_t) #endif + + PL_FORMAT_TYPE(float) + PL_FORMAT_TYPE(double) + PL_FORMAT_TYPE(const char *) PL_FORMAT_TYPE(const wchar_t *) PL_FORMAT_TYPE(const plString &) @@ -203,9 +207,6 @@ namespace plFormat_Private PL_FORMAT_TYPE(const std::string &) PL_FORMAT_TYPE(const std::wstring &) - // TODO: Implement floating point types (float, double). They're harder - // than the others, so I'll get around to them later >.> - // Formats as "true" or "false", following normal string formatting rules. // To use other formats, don't pass us a bool directly... PL_FORMAT_TYPE(bool) diff --git a/Sources/Plasma/FeatureLib/pfConsole/pfDispatchLog.cpp b/Sources/Plasma/FeatureLib/pfConsole/pfDispatchLog.cpp index 16c21527..0517fe9c 100644 --- a/Sources/Plasma/FeatureLib/pfConsole/pfDispatchLog.cpp +++ b/Sources/Plasma/FeatureLib/pfConsole/pfDispatchLog.cpp @@ -257,10 +257,10 @@ static bool DumpSpecificMsgInfo(plMessage* msg, plString& info) PrintKIType(kGZFlashUpdate); // flash an update without saving (for animation of GZFill in) PrintKIType(kNoCommand); - info = plString::Format("Type: %s Str: %s User: %s(%d) Delay: %f Int: %d", + info = plFormat("Type: {} Str: {} User: {}({}) Delay: {} Int: {}", typeName, - kiMsg->GetString().c_str("(nil)"), - kiMsg->GetUser().c_str("(nil)"), + kiMsg->GetString(), + kiMsg->GetUser(), kiMsg->GetPlayerID(), kiMsg->GetDelay(), kiMsg->GetIntValue()); diff --git a/Sources/Plasma/FeatureLib/pfPython/cyMisc.cpp b/Sources/Plasma/FeatureLib/pfPython/cyMisc.cpp index 15c26c4d..021ed081 100644 --- a/Sources/Plasma/FeatureLib/pfPython/cyMisc.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/cyMisc.cpp @@ -1566,7 +1566,7 @@ void cyMisc::FogSetDefExp2(float end, float density) void cyMisc::SetClearColor(float red, float green, float blue) { // do this command via the console to keep the maxplugins from barfing - plString command = plString::Format("Graphics.Renderer.SetClearColor %f %f %f", red, green, blue); + plString command = plFormat("Graphics.Renderer.SetClearColor {f} {f} {f}", red, green, blue); // create message to send to the console plControlEventMsg* pMsg = new plControlEventMsg; diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plAvTaskSeek.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plAvTaskSeek.cpp index ed36367d..3f80b997 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAvTaskSeek.cpp +++ b/Sources/Plasma/PubUtilLib/plAvatar/plAvTaskSeek.cpp @@ -563,17 +563,17 @@ bool plAvTaskSeek::IUpdateObjective(plArmatureMod *avatar) // ---------- void plAvTaskSeek::DumpDebug(const char *name, int &x, int&y, int lineHeight, plDebugText &debugTxt) { - debugTxt.DrawString(x, y, plString::Format("duration: %.2f pos: (%.3f, %.3f, %.3f) goalPos: (%.3f, %.3f, %.3f) ", + debugTxt.DrawString(x, y, plFormat("duration: {.2f} pos: ({.3f}, {.3f}, {.3f}) goalPos: ({.3f}, {.3f}, {.3f}) ", hsTimer::GetSysSeconds() - fStartTime, fPosition.fX, fPosition.fY, fPosition.fZ, fSeekPos.fX, fSeekPos.fY, fSeekPos.fZ)); y += lineHeight; - debugTxt.DrawString(x, y, plString::Format("positioning: %d rotating %d goalVec: (%.3f, %.3f, %.3f) dist: %.3f angFwd: %.3f angRt: %.3f", + debugTxt.DrawString(x, y, plFormat("positioning: {} rotating {} goalVec: ({.3f}, {.3f}, {.3f}) dist: {.3f} angFwd: {.3f} angRt: {.3f}", fStillPositioning, fStillRotating, fGoalVec.fX, fGoalVec.fY, fGoalVec.fZ, fDistance, fAngForward, fAngRight)); y += lineHeight; - debugTxt.DrawString(x, y, plString::Format(" distFwd: %.3f distRt: %.3f shufRange: %.3f sidAngle: %.3f sidRange: %.3f, fMinWalk: %.3f", + debugTxt.DrawString(x, y, plFormat(" distFwd: {.3f} distRt: {.3f} shufRange: {.3f} sidAngle: {.3f} sidRange: {.3f}, fMinWalk: {.3f}", fDistForward, fDistRight, fShuffleRange, fMaxSidleAngle, fMaxSidleRange, fMinFwdAngle)); y += lineHeight; } @@ -583,17 +583,17 @@ void plAvTaskSeek::DumpToAvatarLog(plArmatureMod *avatar) plStatusLog *log = plAvatarMgr::GetInstance()->GetLog(); log->AddLine(avatar->GetMoveKeyString().c_str()); - log->AddLine(plString::Format(" duration: %.2f pos: (%.3f, %.3f, %.3f) goalPos: (%.3f, %.3f, %.3f) ", + log->AddLine(plFormat(" duration: {.2f} pos: ({.3f}, {.3f}, {.3f}) goalPos: ({.3f}, {.3f}, {.3f}) ", hsTimer::GetSysSeconds() - fStartTime, fPosition.fX, fPosition.fY, fPosition.fZ, fSeekPos.fX, fSeekPos.fY, fSeekPos.fZ).c_str()); - log->AddLine(plString::Format(" positioning: %d rotating %d goalVec: (%.3f, %.3f, %.3f) dist: %.3f angFwd: %.3f angRt: %.3f", + log->AddLine(plFormat(" positioning: {} rotating {} goalVec: ({.3f}, {.3f}, {.3f}) dist: {.3f} angFwd: {.3f} angRt: {.3f}", fStillPositioning, fStillRotating, fGoalVec.fX, fGoalVec.fY, fGoalVec.fZ, fDistance, fAngForward, fAngRight).c_str()); - log->AddLine(plString::Format(" distFwd: %.3f distRt: %.3f shufRange: %.3f sidAngle: %.3f sidRange: %.3f, fMinWalk: %.3f", + log->AddLine(plFormat(" distFwd: {.3f} distRt: {.3f} shufRange: {.3f} sidAngle: {.3f} sidRange: {.3f}, fMinWalk: {.3f}", fDistForward, fDistRight, fShuffleRange, fMaxSidleAngle, fMaxSidleRange, fMinFwdAngle).c_str()); }