diff --git a/doc/strings.md b/doc/strings.md index 193cbfe4f..6a3a98096 100644 --- a/doc/strings.md +++ b/doc/strings.md @@ -20,6 +20,29 @@ Some general guidelines to keep in mind when it comes to `cc_string` strings: - Strings are not garbage collected or reference counted
(i.e. you are responsible for managing the lifetime of strings) +## Usage examples + +Initialisating a string from readonly text: +```C +cc_string str = String_FromConst("ABC"); +``` + +Initialising a string from temporary memory on the stack: +```C +// str will be able to store at most 200 characters in it +char strBuffer[200]; +cc_string str = String_FromArray(strBuffer); +``` + +Initialising a string from persistent memory on the heap: +```C +// str will be able to store at most 200 characters in it +char* str = Mem_Alloc(1, 200, "String buffer"); +cc_string str = String_Init(str, 0, 200); +``` + +# Converting to/from other string representations + ## C String conversion ### C string -> cc_string @@ -88,7 +111,7 @@ void SetWorkingDir(cc_string* title) { Platform_EncodeUtf16(&str, title); SetCurrentDirectoryW(str.uni); - // it's recommended that you DON'T use the ansi format whenever possible + // it's recommended that you DON'T use the ansi format whenever possible //SetCurrentDirectoryA(str.ansi); } ``` @@ -106,13 +129,15 @@ void SetWorkingDir(cc_string* title) { } ``` -## API +# API I'm lazy so I will just link to [String.h](/src/String.h) If you'd rather I provided a more detailed reference here, please let me know. -# Extra details +TODO + +# Comparisons to other string implementations ## C comparison @@ -202,10 +227,7 @@ string::compare -> String_Compare std::sprintf -> String_Format1/2/3/4 ``` - - - -## Lifetime examples +# Detailed lifetime examples Managing the lifetime of strings is important, as not properly managing them can cause issues. diff --git a/src/Chat.c b/src/Chat.c index df092e7cf..ade6ebcd4 100644 --- a/src/Chat.c +++ b/src/Chat.c @@ -415,19 +415,18 @@ static struct ChatCommand GpuInfoCommand = { }; static void RenderTypeCommand_Execute(const cc_string* args, int argsCount) { - int flags; - if (!argsCount) { - Chat_AddRaw("&e/client: &cYou didn't specify a new render type."); return; - } + char strBuffer[256]; + cc_string safeStr = String_FromArray(strBuffer); + cc_string unsafeStr; - flags = EnvRenderer_CalcFlags(args); - if (flags >= 0) { - EnvRenderer_SetMode(flags); - Options_Set(OPT_RENDER_TYPE, args); - Chat_Add1("&e/client: &fRender type is now %s.", args); - } else { - Chat_Add1("&e/client: &cUnrecognised render type &f\"%s\"&c.", args); - } + Options_SetInt("xyz", 123456); + Options_Get("xyz", &safeStr, ""); + Options_UNSAFE_Get("xyz", &unsafeStr); + + Options_Reload(); + + Chat_Add1("Safe: %s", &safeStr); + Chat_Add1("Unsafe: %s", &unsafeStr); } static struct ChatCommand RenderTypeCommand = { diff --git a/src/Drawer2D.c b/src/Drawer2D.c index 28687d265..d3ca2b933 100644 --- a/src/Drawer2D.c +++ b/src/Drawer2D.c @@ -633,26 +633,39 @@ static void DefaultPngProcess(struct Stream* stream, const cc_string* name) { static struct TextureEntry default_entry = { "default.png", DefaultPngProcess }; -static void InitHexEncodedColor(int i, int hex, cc_uint8 lo, cc_uint8 hi) { - Drawer2D.Colors[i] = BitmapColor_RGB( - lo * ((hex >> 2) & 1) + hi * (hex >> 3), - lo * ((hex >> 1) & 1) + hi * (hex >> 3), - lo * ((hex >> 0) & 1) + hi * (hex >> 3)); -} +/* The default 16 colours are the CGA 16 color palette (without special brown colour) */ +/* See https://en.wikipedia.org/wiki/Color_Graphics_Adapter#With_an_RGBI_monitor for reference */ +/* The 16 hex colours below were produced from the following formula: */ +/* R = 191 * ((hex >> 2) & 1) + 64 * (hex >> 3) */ +/* G = 191 * ((hex >> 1) & 1) + 64 * (hex >> 3) */ +/* B = 191 * ((hex >> 0) & 1) + 64 * (hex >> 3) */ +static const BitmapCol defaults_0_9[] = { + BitmapColor_RGB( 0, 0, 0), /* 0 */ + BitmapColor_RGB( 0, 0, 191), /* 1 */ + BitmapColor_RGB( 0, 191, 0), /* 2 */ + BitmapColor_RGB( 0, 191, 191), /* 3 */ + BitmapColor_RGB(191, 0, 0), /* 4 */ + BitmapColor_RGB(191, 0, 191), /* 5 */ + BitmapColor_RGB(191, 191, 0), /* 6 */ + BitmapColor_RGB(191, 191, 191), /* 7 */ + BitmapColor_RGB( 64, 64, 64), /* 8 */ + BitmapColor_RGB( 64, 64, 255) /* 9 */ +}; +static const BitmapCol defaults_a_f[] = { + BitmapColor_RGB( 64, 255, 64), /* A */ + BitmapColor_RGB( 64, 255, 255), /* B */ + BitmapColor_RGB(255, 64, 64), /* C */ + BitmapColor_RGB(255, 64, 255), /* D */ + BitmapColor_RGB(255, 255, 64), /* E */ + BitmapColor_RGB(255, 255, 255), /* F */ +}; static void OnReset(void) { - int i; - for (i = 0; i < DRAWER2D_MAX_COLORS; i++) { - Drawer2D.Colors[i] = 0; - } + Mem_Set(Drawer2D.Colors, 0, sizeof(Drawer2D.Colors)); - for (i = 0; i <= 9; i++) { - InitHexEncodedColor('0' + i, i, 191, 64); - } - for (i = 10; i <= 15; i++) { - InitHexEncodedColor('a' + (i - 10), i, 191, 64); - InitHexEncodedColor('A' + (i - 10), i, 191, 64); - } + Mem_Copy(&Drawer2D.Colors['0'], defaults_0_9, sizeof(defaults_0_9)); + Mem_Copy(&Drawer2D.Colors['a'], defaults_a_f, sizeof(defaults_a_f)); + Mem_Copy(&Drawer2D.Colors['A'], defaults_a_f, sizeof(defaults_a_f)); } static void OnInit(void) { diff --git a/src/Launcher.c b/src/Launcher.c index 098b689d9..37759ddd1 100644 --- a/src/Launcher.c +++ b/src/Launcher.c @@ -328,9 +328,7 @@ const struct LauncherTheme Launcher_NordicTheme = { CC_NOINLINE static void ParseColor(const char* key, BitmapCol* color) { cc_uint8 rgb[3]; - cc_string value; - if (!Options_UNSAFE_Get(key, &value)) return; - if (!PackedCol_TryParseHex(&value, rgb)) return; + if (!Options_GetColor(key, rgb)) return; *color = BitmapColor_RGB(rgb[0], rgb[1], rgb[2]); } diff --git a/src/Options.c b/src/Options.c index f932eb7b8..baf96118f 100644 --- a/src/Options.c +++ b/src/Options.c @@ -6,6 +6,7 @@ #include "Errors.h" #include "Utils.h" #include "Logger.h" +#include "PackedCol.h" struct StringsBuffer Options; static struct StringsBuffer changedOpts; @@ -137,6 +138,19 @@ int Options_GetEnum(const char* key, int defValue, const char* const* names, int return Utils_ParseEnum(&str, defValue, names, namesCount); } +cc_bool Options_GetColor(const char* key, cc_uint8* rgb) { + cc_string value, parts[3]; + if (!Options_UNSAFE_Get(key, &value)) return false; + if (PackedCol_TryParseHex(&value, rgb)) return true; + + /* Try parsing as R,G,B instead */ + return String_UNSAFE_Split(&value, ',', parts, 3) + && Convert_ParseUInt8(&parts[0], &rgb[0]) + && Convert_ParseUInt8(&parts[1], &rgb[1]) + && Convert_ParseUInt8(&parts[2], &rgb[2]); +} + + void Options_SetBool(const char* keyRaw, cc_bool value) { static const cc_string str_true = String_FromConst("True"); static const cc_string str_false = String_FromConst("False"); diff --git a/src/Options.h b/src/Options.h index 46ae97dfc..724352821 100644 --- a/src/Options.h +++ b/src/Options.h @@ -123,6 +123,9 @@ CC_API float Options_GetFloat(const char* key, float min, float max, float defV /* Returns value of given option as an integer, or defalt value if could not be converted. */ /* NOTE: Conversion is done by going through all elements of names, returning index of a match. */ CC_API int Options_GetEnum(const char* key, int defValue, const char* const* names, int namesCount); +/* Attempts to parse the value of the given option into an RGB (3 byte) colour. */ +/* Returns whether the option was actually found and could be parsed into a colour. */ +cc_bool Options_GetColor(const char* key, cc_uint8* rgb); /* Sets value of given option to either "true" or "false". */ CC_API void Options_SetBool(const char* keyRaw, cc_bool value); diff --git a/src/String.c b/src/String.c index ee2483a1d..bd3a7d36b 100644 --- a/src/String.c +++ b/src/String.c @@ -146,6 +146,13 @@ int String_CaselessEqualsConst(const cc_string* a, const char* b) { void String_Append(cc_string* str, char c) { + /* MSVC in debug mode will initialise all variables on the stack with 0xCC by default */ + /* So if a string is being passed with CC in all its fields, then it's probably invalid */ +#if _MSC_VER && _DEBUG + if (str->length == 0xCCCC && str->capacity == 0xCCCC) + Logger_Abort("String must be initialised before calling String_Append"); +#endif + if (str->length == str->capacity) return; str->buffer[str->length++] = c; }