diff --git a/src/core/Exception.hpp b/src/core/Exception.hpp index ac3d25d2ad..ed96db621e 100644 --- a/src/core/Exception.hpp +++ b/src/core/Exception.hpp @@ -1,19 +1,24 @@ #pragma once -#include #include "../common.h" -class Exception : public std::exception { -public: - Exception() : std::exception() { } - Exception(const char *message) : std::exception() { - _message = message; - } - virtual ~Exception() { } +#include - const char *what() const throw() override { return _message; } - const char *GetMessage() const { return _message; } +class Exception : public std::exception +{ +public: + Exception() : Exception(nullptr) { } + + Exception(const char * message) : std::exception() + { + _message = message; + } + + virtual ~Exception() { } + + const char * what() const throw() override { return _message; } + const char * GetMessage() const { return _message; } private: - const char *_message; + const char * _message; }; diff --git a/src/core/FileStream.hpp b/src/core/FileStream.hpp index e7b91d37d8..7224741ef1 100644 --- a/src/core/FileStream.hpp +++ b/src/core/FileStream.hpp @@ -1,94 +1,108 @@ #pragma once -#include #include "../common.h" + +#include #include "IStream.hpp" -enum { - FILE_MODE_OPEN, - FILE_MODE_WRITE +enum +{ + FILE_MODE_OPEN, + FILE_MODE_WRITE }; /** * A stream for reading and writing to files. Wraps an SDL_RWops, SDL2's cross platform file stream. */ -class FileStream : public IStream { +class FileStream : public IStream +{ private: - SDL_RWops *_file; - bool _canRead; - bool _canWrite; - bool _disposed; + SDL_RWops * _file; + bool _canRead; + bool _canWrite; + bool _disposed; public: - FileStream(const utf8 *path, int fileMode) { - const char *mode; - switch (fileMode) { - case FILE_MODE_OPEN: - mode = "rb"; - _canRead = true; - _canWrite = false; - break; - case FILE_MODE_WRITE: - mode = "wb"; - _canRead = false; - _canWrite = true; - break; - default: - throw; - } + FileStream(const utf8 * path, int fileMode) + { + const char * mode; + switch (fileMode) { + case FILE_MODE_OPEN: + mode = "rb"; + _canRead = true; + _canWrite = false; + break; + case FILE_MODE_WRITE: + mode = "wb"; + _canRead = false; + _canWrite = true; + break; + default: + throw; + } - _file = SDL_RWFromFile(path, mode); - if (_file == NULL) { - throw IOException(SDL_GetError()); - } + _file = SDL_RWFromFile(path, mode); + if (_file == nullptr) + { + throw IOException(SDL_GetError()); + } - _disposed = false; - } + _disposed = false; + } - ~FileStream() { - Dispose(); - } + ~FileStream() + { + Dispose(); + } - void Dispose() override { - if (!_disposed) { - _disposed = true; - SDL_RWclose(_file); - } - } + void Dispose() override + { + if (!_disposed) + { + _disposed = true; + SDL_RWclose(_file); + } + } - bool CanRead() const override { return _canRead; } - bool CanWrite() const override { return _canWrite; } + bool CanRead() const override { return _canRead; } + bool CanWrite() const override { return _canWrite; } - sint64 GetLength() const override { return SDL_RWsize(_file); } - sint64 GetPosition() const override { return SDL_RWtell(_file); } + uint64 GetLength() const override { return SDL_RWsize(_file); } + uint64 GetPosition() const override { return SDL_RWtell(_file); } - void SetPosition(sint64 position) override { - Seek(position, STREAM_SEEK_BEGIN); - } + void SetPosition(uint64 position) override + { + Seek(position, STREAM_SEEK_BEGIN); + } - void Seek(sint64 offset, int origin) override { - switch (origin) { - case STREAM_SEEK_BEGIN: - SDL_RWseek(_file, offset, RW_SEEK_SET); - break; - case STREAM_SEEK_CURRENT: - SDL_RWseek(_file, offset, RW_SEEK_CUR); - break; - case STREAM_SEEK_END: - SDL_RWseek(_file, offset, RW_SEEK_END); - break; - } - } + void Seek(sint64 offset, int origin) override + { + switch (origin) { + case STREAM_SEEK_BEGIN: + SDL_RWseek(_file, offset, RW_SEEK_SET); + break; + case STREAM_SEEK_CURRENT: + SDL_RWseek(_file, offset, RW_SEEK_CUR); + break; + case STREAM_SEEK_END: + SDL_RWseek(_file, offset, RW_SEEK_END); + break; + } + } - void Read(void *buffer, int length) override { - if (SDL_RWread(_file, buffer, length, 1) != 1) { - throw IOException("Attempted to read past end of file."); - } - } + void Read(void * buffer, uint64 length) override + { + if (SDL_RWread(_file, buffer, (size_t)length, 1) != 1) + { + throw IOException("Attempted to read past end of file."); + } + } - void Write(const void *buffer, int length) override { - if (SDL_RWwrite(_file, buffer, length, 1) != 1) { - throw IOException("Unable to write to file."); - } - } + void Write(const void * buffer, uint64 length) override + { + if (SDL_RWwrite(_file, buffer, (size_t)length, 1) != 1) + { + throw IOException("Unable to write to file."); + } + } }; diff --git a/src/core/IDisposable.hpp b/src/core/IDisposable.hpp index e0cfb58726..d6137daed3 100644 --- a/src/core/IDisposable.hpp +++ b/src/core/IDisposable.hpp @@ -5,6 +5,7 @@ /** * Represents an object that can be disposed. So things can explicitly close resources before the destructor kicks in. */ -interface IDisposable { - virtual void Dispose() abstract; +interface IDisposable +{ + virtual void Dispose() abstract; }; diff --git a/src/core/IStream.hpp b/src/core/IStream.hpp index 00911cc6aa..cf9a60a0e6 100644 --- a/src/core/IStream.hpp +++ b/src/core/IStream.hpp @@ -1,75 +1,82 @@ #pragma once #include "../common.h" + #include "Exception.hpp" #include "IDisposable.hpp" enum { - STREAM_SEEK_BEGIN, - STREAM_SEEK_CURRENT, - STREAM_SEEK_END + STREAM_SEEK_BEGIN, + STREAM_SEEK_CURRENT, + STREAM_SEEK_END }; /** * Represents a stream that can be read or written to. Implemented by types such as FileStream, NetworkStream or MemoryStream. */ -interface IStream : public IDisposable { - /////////////////////////////////////////////////////////////////////////// - // Interface methods - /////////////////////////////////////////////////////////////////////////// - // virtual ~IStream() abstract; +interface IStream : public IDisposable +{ + /////////////////////////////////////////////////////////////////////////// + // Interface methods + /////////////////////////////////////////////////////////////////////////// + // virtual ~IStream() abstract; - virtual bool CanRead() const abstract; - virtual bool CanWrite() const abstract; + virtual bool CanRead() const abstract; + virtual bool CanWrite() const abstract; - virtual sint64 GetLength() const abstract; - virtual sint64 GetPosition() const abstract; - virtual void SetPosition(sint64 position) abstract; - virtual void Seek(sint64 offset, int origin) abstract; + virtual uint64 GetLength() const abstract; + virtual uint64 GetPosition() const abstract; + virtual void SetPosition(uint64 position) abstract; + virtual void Seek(sint64 offset, int origin) abstract; - virtual void Read(void *buffer, int length) abstract; - virtual void Write(const void *buffer, int length) abstract; + virtual void Read(void * buffer, uint64 length) abstract; + virtual void Write(const void * buffer, uint64 length) abstract; - /////////////////////////////////////////////////////////////////////////// - // Helper methods - /////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// + // Helper methods + /////////////////////////////////////////////////////////////////////////// - /** - * Reads the size of the given type from the stream directly into the given address. - */ - template - void Read(T *value) { - Read(value, sizeof(T)); - } + /** + * Reads the size of the given type from the stream directly into the given address. + */ + template + void Read(T * value) + { + Read(value, sizeof(T)); + } - /** - * Writes the size of the given type to the stream directly from the given address. - */ - template - void Write(const T *value) { - Write(value, sizeof(T)); - } + /** + * Writes the size of the given type to the stream directly from the given address. + */ + template + void Write(const T * value) + { + Write(value, sizeof(T)); + } - /** - * Reads the given type from the stream. Use this only for small types (e.g. sint8, sint64, double) - */ - template - T ReadValue() { - T buffer; - Read(&buffer); - return buffer; - } + /** + * Reads the given type from the stream. Use this only for small types (e.g. sint8, sint64, double) + */ + template + T ReadValue() + { + T buffer; + Read(&buffer); + return buffer; + } - /** - * Writes the given type to the stream. Use this only for small types (e.g. sint8, sint64, double) - */ - template - void WriteValue(const T value) { - Write(&value); - } + /** + * Writes the given type to the stream. Use this only for small types (e.g. sint8, sint64, double) + */ + template + void WriteValue(const T value) + { + Write(&value); + } }; -class IOException : public Exception { +class IOException : public Exception +{ public: - IOException(const char *message) : Exception(message) { } + IOException(const char * message) : Exception(message) { } }; diff --git a/src/core/Math.hpp b/src/core/Math.hpp index 30bcd3143f..ae096ec2a8 100644 --- a/src/core/Math.hpp +++ b/src/core/Math.hpp @@ -5,21 +5,23 @@ /** * Common mathematical functions. */ -namespace Math { +namespace Math +{ + template + T Min(T a, T b) + { + return (std::min)(a, b); + } - template - T Min(T a, T b) { - return (std::min)(a, b); - } - - template - T Max(T a, T b) { - return (std::max)(a, b); - } - - template - T Clamp(T low, T x, T high) { - return (std::min)((std::max)(low, x), high); - } + template + T Max(T a, T b) + { + return (std::max)(a, b); + } + template + T Clamp(T low, T x, T high) + { + return (std::min)((std::max)(low, x), high); + } } diff --git a/src/core/Memory.hpp b/src/core/Memory.hpp index 913e98a7fa..fcab3ac554 100644 --- a/src/core/Memory.hpp +++ b/src/core/Memory.hpp @@ -1,70 +1,96 @@ #pragma once -extern "C" { -#include "../common.h" +extern "C" +{ + #include "../common.h" } /** * Utility methods for memory management. Typically helpers and wrappers around the C standard library. */ -namespace Memory { - template - T *Allocate() { - return (T*)malloc(sizeof(T)); - } +namespace Memory +{ + template + T * Allocate() + { + return (T*)malloc(sizeof(T)); + } - template - T *Allocate(size_t size) { - return (T*)malloc(size); - } + template + T * Allocate(size_t size) + { + return (T*)malloc(size); + } - template - T *AllocateArray(size_t count) { - return (T*)malloc(count * sizeof(T)); - } + template + T * AllocateArray(size_t count) + { + return (T*)malloc(count * sizeof(T)); + } - template - T *Reallocate(T *ptr, size_t size) { - if (ptr == NULL) - return (T*)malloc(size); - else - return (T*)realloc((void*)ptr, size); - } + template + T * Reallocate(T * ptr, size_t size) + { + if (ptr == NULL) + { + return (T*)malloc(size); + } + else + { + return (T*)realloc((void*)ptr, size); + } + } - template - T *ReallocateArray(T *ptr, size_t count) { - if (ptr == NULL) - return (T*)malloc(count * sizeof(T)); - else - return (T*)realloc((void*)ptr, count * sizeof(T)); - } + template + T * ReallocateArray(T * ptr, size_t count) + { + if (ptr == NULL) + { + return (T*)malloc(count * sizeof(T)); + } + else + { + return (T*)realloc((void*)ptr, count * sizeof(T)); + } + } - template - void Free(T *ptr) { - free((void*)ptr); - } + template + void Free(T * ptr) + { + free((void*)ptr); + } - template - T *Copy(T *dst, const T *src, size_t size) { - if (size == 0) return (T*)dst; - return (T*)memcpy((void*)dst, (const void*)src, size); - } + template + T * Copy(T * dst, const T * src, size_t size) + { + if (size == 0) return (T*)dst; + return (T*)memcpy((void*)dst, (const void*)src, size); + } - template - T *CopyArray(T *dst, const T *src, size_t count) { - if (count == 0) return (T*)dst; - return (T*)memcpy((void*)dst, (const void*)src, count * sizeof(T)); - } + template + T * CopyArray(T *dst, const T * src, size_t count) + { + if (count == 0) return (T*)dst; + return (T*)memcpy((void*)dst, (const void*)src, count * sizeof(T)); + } - template - T *Duplicate(const T *src, size_t size) { - T *result = Allocate(size); - return Copy(result, src, size); - } + template + T * Duplicate(const T * src, size_t size) + { + T *result = Allocate(size); + return Copy(result, src, size); + } - template - T *DuplicateArray(const T *src, size_t count) { - T *result = AllocateArray(count); - return CopyArray(result, src, count); - } + template + T * DuplicateArray(const T * src, size_t count) + { + T *result = AllocateArray(count); + return CopyArray(result, src, count); + } + + template + T * Set(T * dst, uint8 value, size_t size) + { + return (T*)memset((void*)dst, (int)value, size); + } } diff --git a/src/core/Stopwatch.cpp b/src/core/Stopwatch.cpp index cc875bb2d0..25a8b7d6f9 100644 --- a/src/core/Stopwatch.cpp +++ b/src/core/Stopwatch.cpp @@ -6,127 +6,129 @@ uint64 Stopwatch::Frequency = 0; Stopwatch::Stopwatch() { - Reset(); + Reset(); } -uint64 Stopwatch::GetElapsedTicks() +uint64 Stopwatch::GetElapsedTicks() const { - uint64 result = _total; - if (_isRunning) - { - uint64 ticks = QueryCurrentTicks(); - if (ticks != 0) { - result += QueryCurrentTicks() - _last; - } - } + uint64 result = _total; + if (_isRunning) + { + uint64 ticks = QueryCurrentTicks(); + if (ticks != 0) { + result += QueryCurrentTicks() - _last; + } + } - return _total; + return _total; } -uint64 Stopwatch::GetElapsedMilliseconds() +uint64 Stopwatch::GetElapsedMilliseconds() const { - if (Frequency == 0) - { - Frequency = QueryFrequency(); - if (Frequency == 0) - { - return 0; - } - } + if (Frequency == 0) + { + Frequency = QueryFrequency(); + if (Frequency == 0) + { + return 0; + } + } - return (GetElapsedTicks() * 1000) / Frequency; + return (GetElapsedTicks() * 1000) / Frequency; } void Stopwatch::Reset() { - _isRunning = false; - _total = 0; - _last = 0; + _isRunning = false; + _total = 0; + _last = 0; } void Stopwatch::Start() { - if (_isRunning) return; + if (_isRunning) return; - uint64 ticks = QueryCurrentTicks(); - if (ticks != 0) - { - _last = ticks; - _isRunning = true; - } + uint64 ticks = QueryCurrentTicks(); + if (ticks != 0) + { + _last = ticks; + _isRunning = true; + } } void Stopwatch::Restart() { - Reset(); - Start(); + Reset(); + Start(); } void Stopwatch::Stop() { - uint64 ticks = QueryCurrentTicks(); - if (ticks != 0) { - _total += QueryCurrentTicks() - _last; - } - _isRunning = false; + uint64 ticks = QueryCurrentTicks(); + if (ticks != 0) + { + _total += QueryCurrentTicks() - _last; + } + _isRunning = false; } uint64 Stopwatch::QueryFrequency() { - return SDL_GetPerformanceFrequency(); + return SDL_GetPerformanceFrequency(); } uint64 Stopwatch::QueryCurrentTicks() { - return SDL_GetPerformanceCounter(); + return SDL_GetPerformanceCounter(); } -extern "C" { - #include "stopwatch.h" +extern "C" +{ + #include "stopwatch.h" - void stopwatch_create(stopwatch *stopwatch) - { - stopwatch->context = new Stopwatch(); - } + void stopwatch_create(stopwatch * stopwatch) + { + stopwatch->context = new Stopwatch(); + } - void stopwatch_dispose(stopwatch *stopwatch) - { - delete ((Stopwatch*)stopwatch->context); - } + void stopwatch_dispose(stopwatch * stopwatch) + { + delete ((Stopwatch*)stopwatch->context); + } - uint64 stopwatch_GetElapsedTicks(stopwatch *stopwatch) - { - Stopwatch *ctx = (Stopwatch*)stopwatch->context; - return ctx->GetElapsedTicks(); - } + uint64 stopwatch_GetElapsedTicks(stopwatch * stopwatch) + { + Stopwatch * ctx = (Stopwatch*)stopwatch->context; + return ctx->GetElapsedTicks(); + } - uint64 stopwatch_GetElapsedMilliseconds(stopwatch *stopwatch) - { - Stopwatch *ctx = (Stopwatch*)stopwatch->context; - return ctx->GetElapsedMilliseconds(); - } + uint64 stopwatch_GetElapsedMilliseconds(stopwatch * stopwatch) + { + Stopwatch * ctx = (Stopwatch*)stopwatch->context; + return ctx->GetElapsedMilliseconds(); + } - void stopwatch_Reset(stopwatch *stopwatch) - { - Stopwatch *ctx = (Stopwatch*)stopwatch->context; - return ctx->Reset(); - } + void stopwatch_Reset(stopwatch * stopwatch) + { + Stopwatch * ctx = (Stopwatch*)stopwatch->context; + return ctx->Reset(); + } - void stopwatch_Start(stopwatch *stopwatch) - { - Stopwatch *ctx = (Stopwatch*)stopwatch->context; - return ctx->Start(); - } + void stopwatch_Start(stopwatch * stopwatch) + { + Stopwatch * ctx = (Stopwatch*)stopwatch->context; + return ctx->Start(); + } - void stopwatch_Restart(stopwatch *stopwatch) - { - Stopwatch *ctx = (Stopwatch*)stopwatch->context; - return ctx->Restart(); - } + void stopwatch_Restart(stopwatch * stopwatch) + { + Stopwatch * ctx = (Stopwatch*)stopwatch->context; + return ctx->Restart(); + } - void stopwatch_Stop(stopwatch *stopwatch) - { - Stopwatch *ctx = (Stopwatch*)stopwatch->context; - return ctx->Stop(); - } + void stopwatch_Stop(stopwatch * stopwatch) + { + Stopwatch * ctx = (Stopwatch*)stopwatch->context; + return ctx->Stop(); + } } diff --git a/src/core/Stopwatch.hpp b/src/core/Stopwatch.hpp index 1494373325..b08185c230 100644 --- a/src/core/Stopwatch.hpp +++ b/src/core/Stopwatch.hpp @@ -1,7 +1,8 @@ #pragma once -extern "C" { -#include "../common.h" +extern "C" +{ + #include "../common.h" } /** @@ -10,26 +11,26 @@ extern "C" { class Stopwatch { private: - /** Number of ticks in a second. */ - static uint64 Frequency; + /** Number of ticks in a second. */ + static uint64 Frequency; - uint64 _total; - uint64 _last; - bool _isRunning; + uint64 _total; + uint64 _last; + bool _isRunning; - static uint64 QueryFrequency(); - static uint64 QueryCurrentTicks(); + static uint64 QueryFrequency(); + static uint64 QueryCurrentTicks(); public: - bool IsRunning() { return _isRunning; } + bool IsRunning() const { return _isRunning; } - Stopwatch(); + Stopwatch(); - uint64 GetElapsedTicks(); - uint64 GetElapsedMilliseconds(); + uint64 GetElapsedTicks() const; + uint64 GetElapsedMilliseconds() const; - void Reset(); - void Start(); - void Restart(); - void Stop(); + void Reset(); + void Start(); + void Restart(); + void Stop(); }; diff --git a/src/core/String.cpp b/src/core/String.cpp index cb06861072..cf4600bf2a 100644 --- a/src/core/String.cpp +++ b/src/core/String.cpp @@ -141,4 +141,38 @@ namespace String { return DiscardUse(ptr, String::Duplicate(replacement)); } + + utf8 * SkipBOM(utf8 * buffer) + { + return (utf8*)SkipBOM(buffer); + } + + const utf8 * SkipBOM(const utf8 * buffer) + { + if (buffer[0] == 0xEF && buffer[1] == 0xBB && buffer[2] == 0xBF) + { + return buffer + 3; + } + return buffer; + } + + size_t GetCodepointLength(codepoint_t codepoint) + { + return utf8_get_codepoint_length(codepoint); + } + + codepoint_t GetNextCodepoint(utf8 * ptr, utf8 * * nextPtr) + { + return GetNextCodepoint((const utf8 *)ptr, (const utf8 * *)nextPtr); + } + + codepoint_t GetNextCodepoint(const utf8 * ptr, const utf8 * * nextPtr) + { + return utf8_get_next(ptr, nextPtr); + } + + utf8 * WriteCodepoint(utf8 * dst, codepoint_t codepoint) + { + return utf8_write_codepoint(dst, codepoint); + } } diff --git a/src/core/String.hpp b/src/core/String.hpp index b1dc24c720..f60998ce19 100644 --- a/src/core/String.hpp +++ b/src/core/String.hpp @@ -9,8 +9,17 @@ namespace String { bool Equals(const utf8 * a, const utf8 * b, bool ignoreCase = false); bool StartsWith(const utf8 * str, const utf8 * match, bool ignoreCase = false); + + /** + * Gets the length of the given string in codepoints. + */ size_t LengthOf(const utf8 * str); + + /** + * Gets the size of the given string in bytes excluding the null terminator. + */ size_t SizeOf(const utf8 * str); + utf8 * Set(utf8 * buffer, size_t bufferSize, const utf8 * src); utf8 * Set(utf8 * buffer, size_t bufferSize, const utf8 * src, size_t srcSize); utf8 * Append(utf8 * buffer, size_t bufferSize, const utf8 * src); @@ -27,4 +36,12 @@ namespace String * Helper method to free the string a string pointer points to and set it to a copy of a replacement string. */ utf8 * DiscardDuplicate(utf8 * * ptr, utf8 * replacement); + + utf8 * SkipBOM(utf8 * buffer); + const utf8 * SkipBOM(const utf8 * buffer); + + size_t GetCodepointLength(codepoint_t codepoint); + codepoint_t GetNextCodepoint(utf8 * ptr, utf8 * * nextPtr = nullptr); + codepoint_t GetNextCodepoint(const utf8 * ptr, const utf8 * * nextPtr = nullptr); + utf8 * WriteCodepoint(utf8 * dst, codepoint_t codepoint); } diff --git a/src/core/StringBuilder.hpp b/src/core/StringBuilder.hpp index b92f4001b0..b5828fcd51 100644 --- a/src/core/StringBuilder.hpp +++ b/src/core/StringBuilder.hpp @@ -1,135 +1,158 @@ #pragma once #include "../common.h" + #include "../localisation/localisation.h" #include "Math.hpp" #include "Memory.hpp" +#include "String.hpp" /** * Class for constructing strings efficiently. A buffer is automatically allocated and reallocated when characters or strings * are appended. Use GetString to copy the current state of the string builder to a new fire-and-forget string. */ -class StringBuilder final { +class StringBuilder final +{ public: - StringBuilder() { - _buffer = NULL; - _capacity = 0; - _length = 0; - } + StringBuilder() + { + _buffer = nullptr; + _capacity = 0; + _length = 0; + } - StringBuilder(int capacity) : StringBuilder() { - EnsureCapacity(capacity); - } + StringBuilder(size_t capacity) : StringBuilder() + { + EnsureCapacity(capacity); + } - ~StringBuilder() { - if (_buffer != NULL) Memory::Free(_buffer); - } + ~StringBuilder() + { + Memory::Free(_buffer); + } - /** - * Appends the given character to the current string. - */ - void Append(int codepoint) { - int codepointLength = utf8_get_codepoint_length(codepoint); - EnsureCapacity(_length + codepointLength + 1); - utf8_write_codepoint(_buffer + _length, codepoint); - _length += codepointLength; - _buffer[_length] = 0; - } + void Append(int codepoint) + { + Append((codepoint_t)codepoint); + } - /** - * Appends the given string to the current string. - */ - void Append(const utf8 *text) { - int textLength = strlen(text); - Append(text, textLength); - } + /** + * Appends the given character to the current string. + */ + void Append(codepoint_t codepoint) + { + codepoint_t codepointLength = String::GetCodepointLength(codepoint); + EnsureCapacity(_length + codepointLength + 1); + String::WriteCodepoint(_buffer + _length, codepoint); + _length += codepointLength; + _buffer[_length] = 0; + } - /** - * Appends the given string of the given length to the current string. Essentially used to ignore null terminators or copy - * the data faster as the length is already known. - * @param text Pointer to the UTF-8 text to append. - * @param textLength Number of bytes to copy. (Can be used to append single bytes rather than codepoints) - */ - void Append(const utf8 *text, int textLength) { - EnsureCapacity(_length + textLength + 1); - Memory::Copy(_buffer + _length, text, textLength); - _length += textLength; - _buffer[_length] = 0; - } + /** + * Appends the given string to the current string. + */ + void Append(const utf8 * text) + { + size_t textLength = String::SizeOf(text); + Append(text, textLength); + } - /** - * Appends the string of a given StringBuilder to the current string. - */ - void Append(const StringBuilder *sb) { - Append(sb->GetBuffer(), sb->GetLength()); - } + /** + * Appends the given string of the given length to the current string. Essentially used to ignore null terminators or copy + * the data faster as the length is already known. + * @param text Pointer to the UTF-8 text to append. + * @param textLength Number of bytes to copy. (Can be used to append single bytes rather than codepoints) + */ + void Append(const utf8 * text, size_t textLength) + { + EnsureCapacity(_length + textLength + 1); + Memory::Copy(_buffer + _length, text, textLength); + _length += textLength; + _buffer[_length] = 0; + } - /** - * Clears the current string, but preserves the allocated memory for another string. - */ - void Clear() { - _length = 0; - if (_capacity >= 1) { - _buffer[_length] = 0; - } - } + /** + * Appends the string of a given StringBuilder to the current string. + */ + void Append(const StringBuilder * sb) + { + Append(sb->GetBuffer(), sb->GetLength()); + } - /** - * Like Clear, only will guarantee freeing of the underlying buffer. - */ - void Reset() { - _length = 0; - _capacity = 0; - if (_buffer != NULL) { - Memory::Free(_buffer); - } - } + /** + * Clears the current string, but preserves the allocated memory for another string. + */ + void Clear() + { + _length = 0; + if (_capacity >= 1) + { + _buffer[_length] = 0; + } + } - /** - * Returns the current string buffer as a new fire-and-forget string. - */ - utf8 *GetString() const { - // If buffer is null, length should be 0 which will create a new one byte memory block containing a null terminator - utf8 *result = Memory::AllocateArray(_length + 1); - Memory::CopyArray(result, _buffer, _length); - result[_length] = 0; - return result; - } + /** + * Like Clear, only will guarantee freeing of the underlying buffer. + */ + void Reset() + { + _length = 0; + _capacity = 0; + if (_buffer != nullptr) + { + Memory::Free(_buffer); + _buffer = nullptr; + } + } - /** - * Gets the current state of the StringBuilder. Warning: this represents the StringBuilder's current working buffer and will - * be deallocated when the StringBuilder is destructed. - */ - const utf8 *GetBuffer() const { - // buffer may be null, so return an immutable empty string - if (_buffer == NULL) return ""; - return _buffer; - } + /** + * Returns the current string buffer as a new fire-and-forget string. + */ + utf8 * GetString() const + { + // If buffer is null, length should be 0 which will create a new one byte memory block containing a null terminator + utf8 * result = Memory::AllocateArray(_length + 1); + Memory::CopyArray(result, _buffer, _length); + result[_length] = 0; + return result; + } - /** - * Gets the amount of allocated memory for the string buffer. - */ - size_t GetCapacity() const { return _capacity; } + /** + * Gets the current state of the StringBuilder. Warning: this represents the StringBuilder's current working buffer and will + * be deallocated when the StringBuilder is destructed. + */ + const utf8 * GetBuffer() const + { + // buffer may be null, so return an immutable empty string + if (_buffer == nullptr) return ""; + return _buffer; + } - /** - * Gets the length of the current string. - */ - size_t GetLength() const { return _length; } + /** + * Gets the amount of allocated memory for the string buffer. + */ + size_t GetCapacity() const { return _capacity; } + + /** + * Gets the length of the current string. + */ + size_t GetLength() const { return _length; } private: - utf8 *_buffer; - size_t _capacity; - size_t _length; + utf8 * _buffer; + size_t _capacity; + size_t _length; - void EnsureCapacity(size_t capacity) - { - if (_capacity > capacity) return; + void EnsureCapacity(size_t capacity) + { + if (_capacity > capacity) return; - _capacity = Math::Max((size_t)8, _capacity); - while (_capacity < capacity) { - _capacity *= 2; - } + _capacity = Math::Max((size_t)8, _capacity); + while (_capacity < capacity) + { + _capacity *= 2; + } - _buffer = Memory::ReallocateArray(_buffer, _capacity); - } + _buffer = Memory::ReallocateArray(_buffer, _capacity); + } }; diff --git a/src/core/StringReader.hpp b/src/core/StringReader.hpp index 196470cc87..b2abd5926f 100644 --- a/src/core/StringReader.hpp +++ b/src/core/StringReader.hpp @@ -3,56 +3,62 @@ #include "../common.h" #include "../localisation/localisation.h" #include "../util/util.h" +#include "String.hpp" -interface IStringReader { - virtual bool TryPeek(int *outCodepoint) abstract; - virtual bool TryRead(int *outCodepoint) abstract; - virtual void Skip() abstract; - virtual bool CanRead() const abstract; +interface IStringReader +{ + virtual bool TryPeek(codepoint_t * outCodepoint) abstract; + virtual bool TryRead(codepoint_t * outCodepoint) abstract; + virtual void Skip() abstract; + virtual bool CanRead() const abstract; }; -class UTF8StringReader final : public IStringReader { +class UTF8StringReader final : public IStringReader +{ public: - UTF8StringReader(const utf8 *text) { - // Skip UTF-8 byte order mark - if (strlen(text) >= 3 && utf8_is_bom(text)) { - text += 3; - } + UTF8StringReader(const utf8 * text) + { + text = String::SkipBOM(text); - _text = text; - _current = text; - } + _text = text; + _current = text; + } - bool TryPeek(int *outCodepoint) override { - if (_current == NULL) return false; + bool TryPeek(codepoint_t * outCodepoint) override + { + if (_current == nullptr) return false; - int codepoint = utf8_get_next(_current, NULL); - *outCodepoint = codepoint; - return true; - } + codepoint_t codepoint = String::GetNextCodepoint(_current); + *outCodepoint = codepoint; + return true; + } - bool TryRead(int *outCodepoint) override { - if (_current == NULL) return false; + bool TryRead(codepoint_t * outCodepoint) override + { + if (_current == nullptr) return false; - int codepoint = utf8_get_next(_current, &_current); - *outCodepoint = codepoint; - if (codepoint == 0) { - _current = NULL; - return false; - } - return true; - } + codepoint_t codepoint = String::GetNextCodepoint(_current, &_current); + *outCodepoint = codepoint; + if (codepoint == 0) + { + _current = nullptr; + return false; + } + return true; + } - void Skip() override { - int codepoint; - TryRead(&codepoint); - } + void Skip() override + { + codepoint_t codepoint; + TryRead(&codepoint); + } - bool CanRead() const override { - return _current != NULL; - } + bool CanRead() const override + { + return _current != nullptr; + } private: - const utf8 *_text; - const utf8 *_current; + const utf8 *_text; + const utf8 *_current; }; diff --git a/src/localisation/LanguagePack.cpp b/src/localisation/LanguagePack.cpp index 7a07260128..cccce403db 100644 --- a/src/localisation/LanguagePack.cpp +++ b/src/localisation/LanguagePack.cpp @@ -1,8 +1,9 @@ -extern "C" { - #include "../common.h" - #include "../util/util.h" - #include "localisation.h" - #include "../platform/platform.h" +extern "C" +{ + #include "../common.h" + #include "../platform/platform.h" + #include "../util/util.h" + #include "localisation.h" } #include "../core/FileStream.hpp" @@ -12,191 +13,233 @@ extern "C" { #include "LanguagePack.h" #include -constexpr rct_string_id ObjectOverrideBase = 0x6000; -constexpr int ObjectOverrideMaxStringCount = 4; +// Don't try to load more than language files that exceed 64 MiB +constexpr uint64 MAX_LANGUAGE_SIZE = 64 * 1024 * 1024; -constexpr rct_string_id ScenarioOverrideBase = 0x7000; -constexpr int ScenarioOverrideMaxStringCount = 3; +constexpr rct_string_id ObjectOverrideBase = 0x6000; +constexpr int ObjectOverrideMaxStringCount = 4; -LanguagePack *LanguagePack::FromFile(int id, const utf8 *path) +constexpr rct_string_id ScenarioOverrideBase = 0x7000; +constexpr int ScenarioOverrideMaxStringCount = 3; + +LanguagePack * LanguagePack::FromFile(uint16 id, const utf8 * path) { - assert(path != nullptr); + assert(path != nullptr); - uint32 fileLength; - utf8 *fileData = nullptr; + // Load file directly into memory + utf8 * fileData = nullptr; + try + { + FileStream fs = FileStream(path, FILE_MODE_OPEN); + + size_t fileLength = (size_t)fs.GetLength(); + if (fileLength > MAX_LANGUAGE_SIZE) + { + throw IOException("Language file too large."); + } - // Load file directly into memory - try { - FileStream fs = FileStream(path, FILE_MODE_OPEN); + fileData = Memory::Allocate(fileLength + 1); + fs.Read(fileData, fileLength); + fileData[fileLength] = '\0'; + } + catch (Exception ex) + { + Memory::Free(fileData); + log_error("Unable to open %s: %s", path, ex.GetMessage()); + return nullptr; + } - fileLength = (uint32)fs.GetLength(); - fileData = Memory::Allocate(fileLength + 1); - fs.Read(fileData, fileLength); - fileData[fileLength] = '\0'; + // Parse the memory as text + LanguagePack * result = FromText(id, fileData); - fs.Dispose(); - } catch (Exception ex) { - Memory::Free(fileData); - log_error("Unable to open %s: %s", path, ex.GetMessage()); - return nullptr; - } - - // Parse the memory as text - LanguagePack *result = FromText(id, fileData); - - Memory::Free(fileData); - return result; + Memory::Free(fileData); + return result; } -LanguagePack *LanguagePack::FromText(int id, const utf8 *text) +LanguagePack * LanguagePack::FromText(uint16 id, const utf8 * text) { - return new LanguagePack(id, text); + return new LanguagePack(id, text); } -LanguagePack::LanguagePack(int id, const utf8 *text) +LanguagePack::LanguagePack(uint16 id, const utf8 * text) { - assert(text != nullptr); + assert(text != nullptr); - _id = id; - _stringData = nullptr; - _currentGroup = nullptr; - _currentObjectOverride = nullptr; - _currentScenarioOverride = nullptr; + _id = id; + _stringData = nullptr; + _currentGroup = nullptr; + _currentObjectOverride = nullptr; + _currentScenarioOverride = nullptr; - auto reader = UTF8StringReader(text); - while (reader.CanRead()) { - ParseLine(&reader); - } + auto reader = UTF8StringReader(text); + while (reader.CanRead()) + { + ParseLine(&reader); + } - _stringData = _stringDataSB.GetString(); + _stringData = _stringDataSB.GetString(); - size_t stringDataBaseAddress = (size_t)_stringData; - for (size_t i = 0; i < _strings.size(); i++) { - if (_strings[i] != nullptr) - _strings[i] = (utf8*)(stringDataBaseAddress + (size_t)_strings[i]); - } - for (size_t i = 0; i < _objectOverrides.size(); i++) { - for (int j = 0; j < ObjectOverrideMaxStringCount; j++) { - const utf8 **strPtr = &(_objectOverrides[i].strings[j]); - if (*strPtr != nullptr) { - *strPtr = (utf8*)(stringDataBaseAddress + (size_t)*strPtr); - } - } - } - for (size_t i = 0; i < _scenarioOverrides.size(); i++) { - for (int j = 0; j < ScenarioOverrideMaxStringCount; j++) { - const utf8 **strPtr = &(_scenarioOverrides[i].strings[j]); - if (*strPtr != nullptr) { - *strPtr = (utf8*)(stringDataBaseAddress + (size_t)*strPtr); - } - } - } + size_t stringDataBaseAddress = (size_t)_stringData; + for (size_t i = 0; i < _strings.size(); i++) + { + if (_strings[i] != nullptr) + { + _strings[i] = (utf8*)(stringDataBaseAddress + (size_t)_strings[i]); + } + } + for (size_t i = 0; i < _objectOverrides.size(); i++) + { + for (int j = 0; j < ObjectOverrideMaxStringCount; j++) + { + const utf8 * * strPtr = &(_objectOverrides[i].strings[j]); + if (*strPtr != nullptr) + { + *strPtr = (utf8*)(stringDataBaseAddress + (size_t)*strPtr); + } + } + } + for (size_t i = 0; i < _scenarioOverrides.size(); i++) + { + for (int j = 0; j < ScenarioOverrideMaxStringCount; j++) + { + const utf8 **strPtr = &(_scenarioOverrides[i].strings[j]); + if (*strPtr != nullptr) + { + *strPtr = (utf8*)(stringDataBaseAddress + (size_t)*strPtr); + } + } + } - // Destruct the string builder to free memory - _stringDataSB = StringBuilder(); + // Destruct the string builder to free memory + _stringDataSB = StringBuilder(); } LanguagePack::~LanguagePack() { - SafeFree(_stringData); - SafeFree(_currentGroup); + Memory::Free(_stringData); + Memory::Free(_currentGroup); } -const utf8 *LanguagePack::GetString(int stringId) const { - if (stringId >= ScenarioOverrideBase) { - int offset = stringId - ScenarioOverrideBase; - int ooIndex = offset / ScenarioOverrideMaxStringCount; - int ooStringIndex = offset % ScenarioOverrideMaxStringCount; - - if (_scenarioOverrides.size() > (size_t)ooIndex) { - return _scenarioOverrides[ooIndex].strings[ooStringIndex]; - } else { - return nullptr; - } - }else if (stringId >= ObjectOverrideBase) { - int offset = stringId - ObjectOverrideBase; - int ooIndex = offset / ObjectOverrideMaxStringCount; - int ooStringIndex = offset % ObjectOverrideMaxStringCount; - - if (_objectOverrides.size() > (size_t)ooIndex) { - return _objectOverrides[ooIndex].strings[ooStringIndex]; - } else { - return nullptr; - } - } else { - if (_strings.size() > (size_t)stringId) { - return _strings[stringId]; - } else { - return nullptr; - } - } -} - -rct_string_id LanguagePack::GetObjectOverrideStringId(const char *objectIdentifier, int index) +const utf8 * LanguagePack::GetString(rct_string_id stringId) const { - assert(objectIdentifier != nullptr); - assert(index < ObjectOverrideMaxStringCount); + if (stringId >= ScenarioOverrideBase) + { + int offset = stringId - ScenarioOverrideBase; + int ooIndex = offset / ScenarioOverrideMaxStringCount; + int ooStringIndex = offset % ScenarioOverrideMaxStringCount; - int ooIndex = 0; - for (const ObjectOverride &objectOverride : _objectOverrides) { - if (strncmp(objectOverride.name, objectIdentifier, 8) == 0) { - if (objectOverride.strings[index] == nullptr) { - return STR_NONE; - } - return ObjectOverrideBase + (ooIndex * ObjectOverrideMaxStringCount) + index; - } - ooIndex++; - } + if (_scenarioOverrides.size() > (size_t)ooIndex) + { + return _scenarioOverrides[ooIndex].strings[ooStringIndex]; + } + else + { + return nullptr; + } + } + else if (stringId >= ObjectOverrideBase) + { + int offset = stringId - ObjectOverrideBase; + int ooIndex = offset / ObjectOverrideMaxStringCount; + int ooStringIndex = offset % ObjectOverrideMaxStringCount; - return STR_NONE; + if (_objectOverrides.size() > (size_t)ooIndex) + { + return _objectOverrides[ooIndex].strings[ooStringIndex]; + } + else + { + return nullptr; + } + } + else + { + if (_strings.size() > (size_t)stringId) + { + return _strings[stringId]; + } + else + { + return nullptr; + } + } } -rct_string_id LanguagePack::GetScenarioOverrideStringId(const utf8 *scenarioFilename, int index) +rct_string_id LanguagePack::GetObjectOverrideStringId(const char * objectIdentifier, uint8 index) { - assert(scenarioFilename != nullptr); - assert(index < ScenarioOverrideMaxStringCount); + assert(objectIdentifier != nullptr); + assert(index < ObjectOverrideMaxStringCount); - int ooIndex = 0; - for (const ScenarioOverride &scenarioOverride : _scenarioOverrides) { - if (_stricmp(scenarioOverride.filename, scenarioFilename) == 0) { - if (scenarioOverride.strings[index] == nullptr) { - return STR_NONE; - } - return ScenarioOverrideBase + (ooIndex * ScenarioOverrideMaxStringCount) + index; - } - ooIndex++; - } + int ooIndex = 0; + for (const ObjectOverride &objectOverride : _objectOverrides) + { + if (strncmp(objectOverride.name, objectIdentifier, 8) == 0) + { + if (objectOverride.strings[index] == nullptr) + { + return STR_NONE; + } + return ObjectOverrideBase + (ooIndex * ObjectOverrideMaxStringCount) + index; + } + ooIndex++; + } - return STR_NONE; + return STR_NONE; } -LanguagePack::ObjectOverride *LanguagePack::GetObjectOverride(const char *objectIdentifier) +rct_string_id LanguagePack::GetScenarioOverrideStringId(const utf8 * scenarioFilename, uint8 index) { - assert(objectIdentifier != nullptr); + assert(scenarioFilename != nullptr); + assert(index < ScenarioOverrideMaxStringCount); - for (size_t i = 0; i < _objectOverrides.size(); i++) { - ObjectOverride *oo = &_objectOverrides[i]; - if (strncmp(oo->name, objectIdentifier, 8) == 0) { - return oo; - } - } - return nullptr; + int ooIndex = 0; + for (const ScenarioOverride &scenarioOverride : _scenarioOverrides) + { + if (_stricmp(scenarioOverride.filename, scenarioFilename) == 0) + { + if (scenarioOverride.strings[index] == nullptr) + { + return STR_NONE; + } + return ScenarioOverrideBase + (ooIndex * ScenarioOverrideMaxStringCount) + index; + } + ooIndex++; + } + + return STR_NONE; } -LanguagePack::ScenarioOverride *LanguagePack::GetScenarioOverride(const utf8 *scenarioIdentifier) +LanguagePack::ObjectOverride * LanguagePack::GetObjectOverride(const char * objectIdentifier) { - assert(scenarioIdentifier != nullptr); + assert(objectIdentifier != nullptr); - for (size_t i = 0; i < _scenarioOverrides.size(); i++) { - ScenarioOverride *so = &_scenarioOverrides[i]; - // At this point ScenarioOverrides were not yet rewritten to point at - // strings, but rather still hold offsets from base. - const utf8 *name = _stringDataSB.GetBuffer() + (size_t)so->name; - if (_stricmp(name, scenarioIdentifier) == 0) { - return so; - } - } - return nullptr; + for (size_t i = 0; i < _objectOverrides.size(); i++) + { + ObjectOverride *oo = &_objectOverrides[i]; + if (strncmp(oo->name, objectIdentifier, 8) == 0) + { + return oo; + } + } + return nullptr; +} + +LanguagePack::ScenarioOverride * LanguagePack::GetScenarioOverride(const utf8 * scenarioIdentifier) +{ + assert(scenarioIdentifier != nullptr); + + for (size_t i = 0; i < _scenarioOverrides.size(); i++) + { + ScenarioOverride *so = &_scenarioOverrides[i]; + // At this point ScenarioOverrides were not yet rewritten to point at + // strings, but rather still hold offsets from base. + const utf8 *name = _stringDataSB.GetBuffer() + (size_t)so->name; + if (_stricmp(name, scenarioIdentifier) == 0) + { + return so; + } + } + return nullptr; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -214,284 +257,337 @@ LanguagePack::ScenarioOverride *LanguagePack::GetScenarioOverride(const utf8 *sc // Use # at the beginning of a line to leave a comment. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -static bool IsWhitespace(int codepoint) +static bool IsWhitespace(codepoint_t codepoint) { - return codepoint == '\t' || codepoint == ' ' || codepoint == '\r' || codepoint == '\n'; + return codepoint == '\t' || codepoint == ' ' || codepoint == '\r' || codepoint == '\n'; } -static bool IsNewLine(int codepoint) +static bool IsNewLine(codepoint_t codepoint) { - return codepoint == '\r' || codepoint == '\n'; + return codepoint == '\r' || codepoint == '\n'; } -static void SkipWhitespace(IStringReader *reader) +static void SkipWhitespace(IStringReader * reader) { - int codepoint; - while (reader->TryPeek(&codepoint)) { - if (IsWhitespace(codepoint)) { - reader->Skip(); - } else { - break; - } - } + codepoint_t codepoint; + while (reader->TryPeek(&codepoint)) + { + if (IsWhitespace(codepoint)) + { + reader->Skip(); + } + else + { + break; + } + } } -static void SkipNewLine(IStringReader *reader) +static void SkipNewLine(IStringReader * reader) { - int codepoint; - while (reader->TryPeek(&codepoint)) { - if (IsNewLine(codepoint)) { - reader->Skip(); - } else { - break; - } - } + codepoint_t codepoint; + while (reader->TryPeek(&codepoint)) + { + if (IsNewLine(codepoint)) + { + reader->Skip(); + } + else + { + break; + } + } } -static void SkipToEndOfLine(IStringReader *reader) +static void SkipToEndOfLine(IStringReader * reader) { - int codepoint; - while (reader->TryPeek(&codepoint)) { - if (codepoint != '\r' && codepoint != '\n') { - reader->Skip(); - } else { - break; - } - } + codepoint_t codepoint; + while (reader->TryPeek(&codepoint)) { + if (codepoint != '\r' && codepoint != '\n') + { + reader->Skip(); + } + else + { + break; + } + } } void LanguagePack::ParseLine(IStringReader *reader) { - SkipWhitespace(reader); + SkipWhitespace(reader); - int codepoint; - if (reader->TryPeek(&codepoint)) { - switch (codepoint) { - case '#': - SkipToEndOfLine(reader); - break; - case '[': - ParseGroupObject(reader); - break; - case '<': - ParseGroupScenario(reader); - break; - case '\r': - case '\n': - break; - default: - ParseString(reader); - break; - } - SkipToEndOfLine(reader); - SkipNewLine(reader); - } + codepoint_t codepoint; + if (reader->TryPeek(&codepoint)) + { + switch (codepoint) { + case '#': + SkipToEndOfLine(reader); + break; + case '[': + ParseGroupObject(reader); + break; + case '<': + ParseGroupScenario(reader); + break; + case '\r': + case '\n': + break; + default: + ParseString(reader); + break; + } + SkipToEndOfLine(reader); + SkipNewLine(reader); + } } -void LanguagePack::ParseGroupObject(IStringReader *reader) +void LanguagePack::ParseGroupObject(IStringReader * reader) { - auto sb = StringBuilder(); - int codepoint; + auto sb = StringBuilder(); + codepoint_t codepoint; - // Should have already deduced that the next codepoint is a [ - reader->Skip(); + // Should have already deduced that the next codepoint is a [ + reader->Skip(); - // Read string up to ] or line end - bool closedCorrectly = false; - while (reader->TryPeek(&codepoint)) { - if (IsNewLine(codepoint)) break; + // Read string up to ] or line end + bool closedCorrectly = false; + while (reader->TryPeek(&codepoint)) + { + if (IsNewLine(codepoint)) break; - reader->Skip(); - if (codepoint == ']') { - closedCorrectly = true; - break; - } - sb.Append(codepoint); - } + reader->Skip(); + if (codepoint == ']') + { + closedCorrectly = true; + break; + } + sb.Append(codepoint); + } - if (closedCorrectly) { - SafeFree(_currentGroup); + if (closedCorrectly) + { + SafeFree(_currentGroup); - while (sb.GetLength() < 8) { - sb.Append(' '); - } - if (sb.GetLength() == 8) { - _currentGroup = sb.GetString(); - _currentObjectOverride = GetObjectOverride(_currentGroup); - _currentScenarioOverride = nullptr; - if (_currentObjectOverride == nullptr) { - _objectOverrides.push_back(ObjectOverride()); - _currentObjectOverride = &_objectOverrides[_objectOverrides.size() - 1]; - memset(_currentObjectOverride, 0, sizeof(ObjectOverride)); - memcpy(_currentObjectOverride->name, _currentGroup, 8); - } - } - } + while (sb.GetLength() < 8) + { + sb.Append(' '); + } + if (sb.GetLength() == 8) + { + _currentGroup = sb.GetString(); + _currentObjectOverride = GetObjectOverride(_currentGroup); + _currentScenarioOverride = nullptr; + if (_currentObjectOverride == nullptr) + { + _objectOverrides.push_back(ObjectOverride()); + _currentObjectOverride = &_objectOverrides[_objectOverrides.size() - 1]; + memset(_currentObjectOverride, 0, sizeof(ObjectOverride)); + memcpy(_currentObjectOverride->name, _currentGroup, 8); + } + } + } } -void LanguagePack::ParseGroupScenario(IStringReader *reader) +void LanguagePack::ParseGroupScenario(IStringReader * reader) { - auto sb = StringBuilder(); - int codepoint; + auto sb = StringBuilder(); + codepoint_t codepoint; - // Should have already deduced that the next codepoint is a < - reader->Skip(); + // Should have already deduced that the next codepoint is a < + reader->Skip(); - // Read string up to > or line end - bool closedCorrectly = false; - while (reader->TryPeek(&codepoint)) { - if (IsNewLine(codepoint)) break; + // Read string up to > or line end + bool closedCorrectly = false; + while (reader->TryPeek(&codepoint)) + { + if (IsNewLine(codepoint)) break; - reader->Skip(); - if (codepoint == '>') { - closedCorrectly = true; - break; - } - sb.Append(codepoint); - } + reader->Skip(); + if (codepoint == '>') + { + closedCorrectly = true; + break; + } + sb.Append(codepoint); + } - if (closedCorrectly) { - SafeFree(_currentGroup); + if (closedCorrectly) + { + SafeFree(_currentGroup); - _currentGroup = sb.GetString(); - _currentObjectOverride = nullptr; - _currentScenarioOverride = GetScenarioOverride(_currentGroup); - if (_currentScenarioOverride == nullptr) { - _scenarioOverrides.push_back(ScenarioOverride()); - _currentScenarioOverride = &_scenarioOverrides[_scenarioOverrides.size() - 1]; - memset(_currentScenarioOverride, 0, sizeof(ScenarioOverride)); - _currentScenarioOverride->filename = sb.GetString(); - } - } + _currentGroup = sb.GetString(); + _currentObjectOverride = nullptr; + _currentScenarioOverride = GetScenarioOverride(_currentGroup); + if (_currentScenarioOverride == nullptr) + { + _scenarioOverrides.push_back(ScenarioOverride()); + _currentScenarioOverride = &_scenarioOverrides[_scenarioOverrides.size() - 1]; + Memory::Set(_currentScenarioOverride, 0, sizeof(ScenarioOverride)); + _currentScenarioOverride->filename = sb.GetString(); + } + } } void LanguagePack::ParseString(IStringReader *reader) { - auto sb = StringBuilder(); - int codepoint; + auto sb = StringBuilder(); + codepoint_t codepoint; - // Parse string identifier - while (reader->TryPeek(&codepoint)) { - if (IsNewLine(codepoint)) { - // Unexpected new line, ignore line entirely - return; - } else if (!IsWhitespace(codepoint) && codepoint != ':') { - reader->Skip(); - sb.Append(codepoint); - } else { - break; - } - } + // Parse string identifier + while (reader->TryPeek(&codepoint)) + { + if (IsNewLine(codepoint)) + { + // Unexpected new line, ignore line entirely + return; + } + else if (!IsWhitespace(codepoint) && codepoint != ':') + { + reader->Skip(); + sb.Append(codepoint); + } + else + { + break; + } + } - SkipWhitespace(reader); + SkipWhitespace(reader); - // Parse a colon - if (!reader->TryPeek(&codepoint) || codepoint != ':') { - // Expected a colon, ignore line entirely - return; - } - reader->Skip(); + // Parse a colon + if (!reader->TryPeek(&codepoint) || codepoint != ':') + { + // Expected a colon, ignore line entirely + return; + } + reader->Skip(); - // Validate identifier - const utf8 *identifier = sb.GetBuffer(); + // Validate identifier + const utf8 *identifier = sb.GetBuffer(); - int stringId; - if (_currentGroup == nullptr) { - if (sscanf(identifier, "STR_%4d", &stringId) != 1) { - // Ignore line entirely - return; - } - } else { - if (strcmp(identifier, "STR_NAME") == 0) { stringId = 0; } - else if (strcmp(identifier, "STR_DESC") == 0) { stringId = 1; } - else if (strcmp(identifier, "STR_CPTY") == 0) { stringId = 2; } + int stringId; + if (_currentGroup == nullptr) + { + if (sscanf(identifier, "STR_%4d", &stringId) != 1) + { + // Ignore line entirely + return; + } + } + else + { + if (strcmp(identifier, "STR_NAME") == 0) { stringId = 0; } + else if (strcmp(identifier, "STR_DESC") == 0) { stringId = 1; } + else if (strcmp(identifier, "STR_CPTY") == 0) { stringId = 2; } - else if (strcmp(identifier, "STR_SCNR") == 0) { stringId = 0; } - else if (strcmp(identifier, "STR_PARK") == 0) { stringId = 1; } - else if (strcmp(identifier, "STR_DTLS") == 0) { stringId = 2; } - else { - // Ignore line entirely - return; - } - } + else if (strcmp(identifier, "STR_SCNR") == 0) { stringId = 0; } + else if (strcmp(identifier, "STR_PARK") == 0) { stringId = 1; } + else if (strcmp(identifier, "STR_DTLS") == 0) { stringId = 2; } + else { + // Ignore line entirely + return; + } + } - // Rest of the line is the actual string - sb.Clear(); - while (reader->TryPeek(&codepoint) && !IsNewLine(codepoint)) { - if (codepoint == '{') { - uint32 token; - bool isByte; - if (ParseToken(reader, &token, &isByte)) { - if (isByte) { - sb.Append((const utf8*)&token, 1); - } else { - sb.Append((int)token); - } - } else { - // Syntax error or unknown token, ignore line entirely - return; - } - } else { - reader->Skip(); - sb.Append(codepoint); - } - } + // Rest of the line is the actual string + sb.Clear(); + while (reader->TryPeek(&codepoint) && !IsNewLine(codepoint)) + { + if (codepoint == '{') + { + uint32 token; + bool isByte; + if (ParseToken(reader, &token, &isByte)) + { + if (isByte) + { + sb.Append((const utf8*)&token, 1); + } + else + { + sb.Append((int)token); + } + } + else + { + // Syntax error or unknown token, ignore line entirely + return; + } + } + else + { + reader->Skip(); + sb.Append(codepoint); + } + } - // Append a null terminator for the benefit of the last string - _stringDataSB.Append(0); + // Append a null terminator for the benefit of the last string + _stringDataSB.Append('\0'); - // Get the relative offset to the string (add the base offset when we extract the string properly) - utf8 *relativeOffset = (utf8*)_stringDataSB.GetLength(); + // Get the relative offset to the string (add the base offset when we extract the string properly) + utf8 * relativeOffset = (utf8*)_stringDataSB.GetLength(); - if (_currentGroup == nullptr) { - // Make sure the list is big enough to contain this string id - while (_strings.size() <= (size_t)stringId) { - _strings.push_back(nullptr); - } + if (_currentGroup == nullptr) + { + // Make sure the list is big enough to contain this string id + while (_strings.size() <= (size_t)stringId) + { + _strings.push_back(nullptr); + } - _strings[stringId] = relativeOffset; - } else { - if (_currentObjectOverride != nullptr) { - _currentObjectOverride->strings[stringId] = relativeOffset; - } else { - _currentScenarioOverride->strings[stringId] = relativeOffset; - } - } + _strings[stringId] = relativeOffset; + } + else + { + if (_currentObjectOverride != nullptr) + { + _currentObjectOverride->strings[stringId] = relativeOffset; + } + else + { + _currentScenarioOverride->strings[stringId] = relativeOffset; + } + } - _stringDataSB.Append(&sb); + _stringDataSB.Append(&sb); } -bool LanguagePack::ParseToken(IStringReader *reader, uint32 *token, bool *isByte) +bool LanguagePack::ParseToken(IStringReader * reader, uint32 * token, bool * isByte) { - auto sb = StringBuilder(); - int codepoint; + auto sb = StringBuilder(); + codepoint_t codepoint; - // Skip open brace - reader->Skip(); + // Skip open brace + reader->Skip(); - while (reader->TryPeek(&codepoint)) { - if (IsNewLine(codepoint)) return false; - if (IsWhitespace(codepoint)) return false; + while (reader->TryPeek(&codepoint)) + { + if (IsNewLine(codepoint)) return false; + if (IsWhitespace(codepoint)) return false; - reader->Skip(); + reader->Skip(); - if (codepoint == '}') break; + if (codepoint == '}') break; - sb.Append(codepoint); - } + sb.Append(codepoint); + } - const utf8 *tokenName = sb.GetBuffer(); - *token = format_get_code(tokenName); - *isByte = false; + const utf8 * tokenName = sb.GetBuffer(); + *token = format_get_code(tokenName); + *isByte = false; - // Handle explicit byte values - if (*token == 0) { - int number; - if (sscanf(tokenName, "%d", &number) == 1) { - *token = Math::Clamp(0, number, 255); - *isByte = true; - } - } + // Handle explicit byte values + if (*token == 0) + { + int number; + if (sscanf(tokenName, "%d", &number) == 1) + { + *token = Math::Clamp(0, number, 255); + *isByte = true; + } + } - return true; + return true; } diff --git a/src/localisation/LanguagePack.h b/src/localisation/LanguagePack.h index 871b689401..db5f6a5fbb 100644 --- a/src/localisation/LanguagePack.h +++ b/src/localisation/LanguagePack.h @@ -2,10 +2,11 @@ #include -extern "C" { - #include "../common.h" - #include "../util/util.h" - #include "localisation.h" +extern "C" +{ + #include "../common.h" + #include "../util/util.h" + #include "localisation.h" } #include "../core/StringBuilder.hpp" @@ -13,65 +14,71 @@ extern "C" { class LanguagePack final { public: - static LanguagePack *FromFile(int id, const utf8 *path); - static LanguagePack *FromText(int id, const utf8 *text); + static LanguagePack * FromFile(uint16 id, const utf8 * path); + static LanguagePack * FromText(uint16 id, const utf8 * text); - ~LanguagePack(); + ~LanguagePack(); - int GetId() const { return _id; } - int GetCount() const { return _strings.size(); } + uint16 GetId() const { return _id; } + uint32 GetCount() const { return _strings.size(); } - const utf8 *GetString(int stringId) const; + const utf8 * GetString(rct_string_id stringId) const; - void SetString(int stringId, const utf8 *str) { - if (_strings.size() >= (size_t)stringId) { - _strings[stringId] = str; - } - } + void SetString(rct_string_id stringId, const utf8 * str) + { + if (_strings.size() >= (size_t)stringId) + { + _strings[stringId] = str; + } + } - rct_string_id GetObjectOverrideStringId(const char *objectIdentifier, int index); - rct_string_id GetScenarioOverrideStringId(const utf8 *scenarioFilename, int index); + rct_string_id GetObjectOverrideStringId(const char * objectIdentifier, uint8 index); + rct_string_id GetScenarioOverrideStringId(const utf8 * scenarioFilename, uint8 index); private: - struct ObjectOverride { - char name[8]; - const utf8 *strings[4]; - }; + struct ObjectOverride + { + char name[8]; + const utf8 * strings[4]; + }; - struct ScenarioOverride { - const utf8 *filename; - union { - const utf8 *strings[3]; - struct { - const utf8 *name; - const utf8 *park; - const utf8 *details; - }; - }; - }; + struct ScenarioOverride + { + const utf8 * filename; + union { + const utf8 * strings[3]; + struct { + const utf8 * name; + const utf8 * park; + const utf8 * details; + }; + }; + }; - int _id; - utf8 *_stringData; - std::vector _strings; - std::vector _objectOverrides; - std::vector _scenarioOverrides; + uint16 _id; + utf8 * _stringData; - LanguagePack(int id, const utf8 *text); - ObjectOverride *GetObjectOverride(const char *objectIdentifier); - ScenarioOverride *GetScenarioOverride(const utf8 *scenarioFilename); + std::vector _strings; + std::vector _objectOverrides; + std::vector _scenarioOverrides; - /////////////////////////////////////////////////////////////////////////// - // Parsing - /////////////////////////////////////////////////////////////////////////// - StringBuilder _stringDataSB; - utf8 *_currentGroup; - ObjectOverride *_currentObjectOverride; - ScenarioOverride *_currentScenarioOverride; + LanguagePack(uint16 id, const utf8 * text); - void ParseLine(IStringReader *reader); - void ParseGroupObject(IStringReader *reader); - void ParseGroupScenario(IStringReader *reader); - void ParseString(IStringReader *reader); + ObjectOverride * GetObjectOverride(const char * objectIdentifier); + ScenarioOverride * GetScenarioOverride(const utf8 * scenarioFilename); - bool ParseToken(IStringReader *reader, uint32 *token, bool *isByte); + /////////////////////////////////////////////////////////////////////////// + // Parsing + /////////////////////////////////////////////////////////////////////////// + StringBuilder _stringDataSB; + utf8 * _currentGroup; + ObjectOverride * _currentObjectOverride; + ScenarioOverride * _currentScenarioOverride; + + void ParseLine(IStringReader * reader); + void ParseGroupObject(IStringReader * reader); + void ParseGroupScenario(IStringReader * reader); + void ParseString(IStringReader * reader); + + bool ParseToken(IStringReader * reader, uint32 * token, bool * isByte); }; diff --git a/src/rct2.h b/src/rct2.h index 2d5ce10850..e3a3af6300 100644 --- a/src/rct2.h +++ b/src/rct2.h @@ -58,6 +58,8 @@ typedef const utf8* const_utf8string; typedef wchar_t utf16; typedef utf16* utf16string; +typedef uint32 codepoint_t; + #define rol8(x, shift) (((uint8)(x) << (shift)) | ((uint8)(x) >> (8 - (shift)))) #define ror8(x, shift) (((uint8)(x) >> (shift)) | ((uint8)(x) << (8 - (shift)))) #define rol16(x, shift) (((uint16)(x) << (shift)) | ((uint16)(x) >> (16 - (shift))))