diff --git a/projects/openrct2.vcxproj b/projects/openrct2.vcxproj
index 688622c8a5..bca15bf71e 100644
--- a/projects/openrct2.vcxproj
+++ b/projects/openrct2.vcxproj
@@ -208,6 +208,9 @@
+
+
+
diff --git a/projects/openrct2.vcxproj.filters b/projects/openrct2.vcxproj.filters
index 9cd193edaf..dad1e96ac8 100644
--- a/projects/openrct2.vcxproj.filters
+++ b/projects/openrct2.vcxproj.filters
@@ -794,5 +794,14 @@
Source\Localisation
+
+ Source\Core
+
+
+ Source\Core
+
+
+ Source\Core
+
\ No newline at end of file
diff --git a/src/core/Exception.hpp b/src/core/Exception.hpp
index f3daf469e6..2940d578b0 100644
--- a/src/core/Exception.hpp
+++ b/src/core/Exception.hpp
@@ -7,4 +7,6 @@ class Exception : public std::exception {
public:
Exception() : std::exception() { }
Exception(const char *message) : std::exception(message) { }
+
+ const char *GetMessage() const { return what(); }
};
diff --git a/src/core/Memory.hpp b/src/core/Memory.hpp
new file mode 100644
index 0000000000..2e4d77c99a
--- /dev/null
+++ b/src/core/Memory.hpp
@@ -0,0 +1,64 @@
+#pragma once
+
+/**
+ * Utility methods for memory management. Typically helpers and wrappers around the C standard library.
+ */
+namespace Memory {
+ template
+ T *Allocate() {
+ return (T*)malloc(sizeof(T));
+ }
+
+ template
+ T *Allocate(size_t size) {
+ return (T*)malloc(size);
+ }
+
+ 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 *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
+ T *Copy(T *dst, const T *src, size_t size) {
+ return (T*)memcpy((void*)dst, (const void*)src, size);
+ }
+
+ template
+ T *CopyArray(T *dst, const T *src, size_t count) {
+ 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 *DuplicateArray(const T *src, size_t count) {
+ T *result = AllocateArray(count);
+ return CopyArray(result, src, count);
+ }
+}
diff --git a/src/core/StringBuilder.hpp b/src/core/StringBuilder.hpp
new file mode 100644
index 0000000000..251fd5ca5a
--- /dev/null
+++ b/src/core/StringBuilder.hpp
@@ -0,0 +1,82 @@
+#pragma once
+
+#include "../common.h"
+#include "../localisation/localisation.h"
+#include "Memory.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 {
+public:
+ StringBuilder() {
+ _buffer = NULL;
+ _capacity = 0;
+ _length = 0;
+ }
+
+ StringBuilder(int capacity) : StringBuilder() {
+ EnsureCapacity(capacity);
+ }
+
+ ~StringBuilder() {
+ if (_buffer != NULL) Memory::Free(_buffer);
+ }
+
+ utf8 *GetString() const {
+ utf8 *result = Memory::AllocateArray(_length + 1);
+ Memory::CopyArray(result, _buffer, _length);
+ result[_length] = 0;
+ return result;
+ }
+
+ 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(utf8 *text) {
+ int textLength = strlen(text);
+
+ EnsureCapacity(_length + textLength + 1);
+ Memory::Copy(_buffer + _length, text, textLength);
+ _length += textLength;
+ _buffer[_length] = 0;
+ }
+
+ void Clear() {
+ _length = 0;
+ if (_capacity >= 1) {
+ _buffer[_length] = 0;
+ }
+ }
+
+ /**
+ * 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() {
+ return _buffer;
+ }
+
+private:
+ utf8 *_buffer;
+ size_t _capacity;
+ size_t _length;
+
+ void EnsureCapacity(size_t capacity)
+ {
+ if (_capacity > capacity) return;
+
+ _capacity = max(8, _capacity);
+ while (_capacity < capacity) {
+ _capacity *= 2;
+ }
+
+ _buffer = Memory::ReallocateArray(_buffer, _capacity);
+ }
+};
diff --git a/src/core/StringReader.hpp b/src/core/StringReader.hpp
new file mode 100644
index 0000000000..095f7bd285
--- /dev/null
+++ b/src/core/StringReader.hpp
@@ -0,0 +1,57 @@
+#pragma once
+
+#include "../common.h"
+#include "../localisation/localisation.h"
+#include "../util/util.h"
+
+interface IStringReader abstract {
+ virtual bool TryPeek(int *outCodepoint) abstract;
+ virtual bool TryRead(int *outCodepoint) abstract;
+ virtual void Skip() abstract;
+};
+
+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;
+ }
+
+ _text = text;
+ _current = text;
+ }
+
+ bool TryPeek(int *outCodepoint) override
+ {
+ if (_current == NULL) return false;
+
+ int codepoint = utf8_get_next(_current, NULL);
+ *outCodepoint = codepoint;
+ return true;
+ }
+
+ bool TryRead(int *outCodepoint) override
+ {
+ if (_current == NULL) return false;
+
+ int codepoint = utf8_get_next(_current, &_current);
+ *outCodepoint = codepoint;
+ if (codepoint == 0) {
+ _current = NULL;
+ return false;
+ }
+ return true;
+ }
+
+ void Skip() override
+ {
+ int codepoint;
+ TryRead(&codepoint);
+ }
+
+private:
+ const utf8 *_text;
+ const utf8 *_current;
+};
diff --git a/src/localisation/LanguagePack.cpp b/src/localisation/LanguagePack.cpp
index a9deaa3781..ecfe0e934e 100644
--- a/src/localisation/LanguagePack.cpp
+++ b/src/localisation/LanguagePack.cpp
@@ -4,100 +4,37 @@ extern "C" {
#include "localisation.h"
}
+#include "../core/FileStream.hpp"
+#include "../core/Memory.hpp"
+#include "../core/StringBuilder.hpp"
#include "LanguagePack.h"
-
#include
-// TODO Move to separate file
-class StringBuilder final {
-public:
- StringBuilder()
- {
- _buffer = NULL;
- _capacity = 0;
- _length = 0;
- }
-
- StringBuilder(int capacity) : StringBuilder()
- {
- EnsureCapacity(capacity);
- }
-
- ~StringBuilder()
- {
- if (_buffer != NULL) free(_buffer);
- }
-
- utf8 *GetString() const
- {
- utf8 *result = (utf8*)malloc(_length + 1);
- memcpy(result, _buffer, _length);
- result[_length] = 0;
- return result;
- }
-
- void Append(int codepoint)
- {
- int codepointLength = utf8_get_codepoint_length(codepoint);
- EnsureCapacity(_length + codepointLength + 1);
- utf8_write_codepoint(_buffer + _length, codepoint);
- _length += codepointLength;
- }
-
- void Append(utf8 *text)
- {
- int textLength = strlen(text);
-
- EnsureCapacity(_length + textLength + 1);
- memcpy(_buffer + _length, text, textLength);
- _length += textLength;
- }
-
-private:
- utf8 *_buffer;
- size_t _capacity;
- size_t _length;
-
- void EnsureCapacity(size_t capacity)
- {
- if (_capacity > capacity) return;
-
- _capacity = max(8, _capacity);
- while (_capacity < capacity) {
- _capacity *= 2;
- }
-
- if (_buffer == NULL) {
- _buffer = (utf8*)malloc(_capacity);
- } else {
- _buffer = (utf8*)realloc(_buffer, _capacity);
- }
- }
-};
-
LanguagePack *LanguagePack::FromFile(int id, const utf8 *path)
{
assert(path != NULL);
- int fileLength;
+ uint32 fileLength;
utf8 *fileData;
// Load file directly into memory
- SDL_RWops *file = SDL_RWFromFile(path, "rb");
- if (file == NULL) {
- log_error("Unable to open %s", path);
+ try {
+ FileStream fs = FileStream(path, FILE_MODE_OPEN);
+
+ fileLength = (uint32)fs.GetLength();
+ fileData = Memory::Allocate(fileLength);
+ fs.Read(fileData, fileLength);
+
+ fs.Dispose();
+ } catch (Exception ex) {
+ log_error("Unable to open %s: %s", path, ex.GetMessage());
return NULL;
}
- fileLength = (int)SDL_RWsize(file);
- fileData = (utf8*)malloc(fileLength);
- SDL_RWread(file, fileData, fileLength, 1);
- SDL_RWclose(file);
-
// Parse the memory as text
LanguagePack *result = FromText(id, fileData);
- free(fileData);
+ Memory::Free(fileData);
return result;
}
diff --git a/src/localisation/LanguagePack.h b/src/localisation/LanguagePack.h
index a66bc02093..10b4c29d8b 100644
--- a/src/localisation/LanguagePack.h
+++ b/src/localisation/LanguagePack.h
@@ -8,58 +8,7 @@ extern "C" {
#include "localisation.h"
}
-struct IStringReader abstract {
- virtual bool TryPeek(int *outCodepoint) abstract;
- virtual bool TryRead(int *outCodepoint) abstract;
- virtual void Skip() abstract;
-};
-
-// TODO Move to separate file in Core
-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;
- }
-
- _text = text;
- _current = text;
- }
-
- bool TryPeek(int *outCodepoint) override
- {
- if (_current == NULL) return false;
-
- int codepoint = utf8_get_next(_current, NULL);
- *outCodepoint = codepoint;
- return true;
- }
-
- bool TryRead(int *outCodepoint) override
- {
- if (_current == NULL) return false;
-
- int codepoint = utf8_get_next(_current, &_current);
- *outCodepoint = codepoint;
- if (codepoint == 0) {
- _current = NULL;
- return false;
- }
- return true;
- }
-
- void Skip() override
- {
- int codepoint;
- TryRead(&codepoint);
- }
-
-private:
- const utf8 *_text;
- const utf8 *_current;
-};
+#include "../core/StringReader.hpp"
class LanguagePack final {
public: