refactor and format old C++ code

This commit is contained in:
IntelOrca 2016-01-23 18:58:31 +00:00
parent 36a4cd2fa0
commit 4c1605f268
15 changed files with 1107 additions and 864 deletions

View File

@ -1,19 +1,24 @@
#pragma once
#include <exception>
#include "../common.h"
class Exception : public std::exception {
public:
Exception() : std::exception() { }
Exception(const char *message) : std::exception() {
_message = message;
}
virtual ~Exception() { }
#include <exception>
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;
};

View File

@ -1,94 +1,108 @@
#pragma once
#include <SDL.h>
#include "../common.h"
#include <SDL.h>
#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.");
}
}
};

View File

@ -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;
};

View File

@ -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<typename T>
void Read(T *value) {
Read(value, sizeof(T));
}
/**
* Reads the size of the given type from the stream directly into the given address.
*/
template<typename T>
void Read(T * value)
{
Read(value, sizeof(T));
}
/**
* Writes the size of the given type to the stream directly from the given address.
*/
template<typename T>
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<typename T>
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<typename T>
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<typename T>
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<typename T>
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<typename T>
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) { }
};

View File

@ -5,21 +5,23 @@
/**
* Common mathematical functions.
*/
namespace Math {
namespace Math
{
template<typename T>
T Min(T a, T b)
{
return (std::min)(a, b);
}
template<typename T>
T Min(T a, T b) {
return (std::min)(a, b);
}
template<typename T>
T Max(T a, T b) {
return (std::max)(a, b);
}
template<typename T>
T Clamp(T low, T x, T high) {
return (std::min)((std::max)(low, x), high);
}
template<typename T>
T Max(T a, T b)
{
return (std::max)(a, b);
}
template<typename T>
T Clamp(T low, T x, T high)
{
return (std::min)((std::max)(low, x), high);
}
}

View File

@ -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<typename T>
T *Allocate() {
return (T*)malloc(sizeof(T));
}
namespace Memory
{
template<typename T>
T * Allocate()
{
return (T*)malloc(sizeof(T));
}
template<typename T>
T *Allocate(size_t size) {
return (T*)malloc(size);
}
template<typename T>
T * Allocate(size_t size)
{
return (T*)malloc(size);
}
template<typename T>
T *AllocateArray(size_t count) {
return (T*)malloc(count * sizeof(T));
}
template<typename T>
T * AllocateArray(size_t count)
{
return (T*)malloc(count * sizeof(T));
}
template<typename T>
T *Reallocate(T *ptr, size_t size) {
if (ptr == NULL)
return (T*)malloc(size);
else
return (T*)realloc((void*)ptr, size);
}
template<typename T>
T * Reallocate(T * ptr, size_t size)
{
if (ptr == NULL)
{
return (T*)malloc(size);
}
else
{
return (T*)realloc((void*)ptr, size);
}
}
template<typename T>
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<typename T>
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<typename T>
void Free(T *ptr) {
free((void*)ptr);
}
template<typename T>
void Free(T * ptr)
{
free((void*)ptr);
}
template<typename T>
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<typename T>
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<typename T>
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<typename T>
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<typename T>
T *Duplicate(const T *src, size_t size) {
T *result = Allocate<T>(size);
return Copy(result, src, size);
}
template<typename T>
T * Duplicate(const T * src, size_t size)
{
T *result = Allocate<T>(size);
return Copy(result, src, size);
}
template<typename T>
T *DuplicateArray(const T *src, size_t count) {
T *result = AllocateArray<T>(count);
return CopyArray(result, src, count);
}
template<typename T>
T * DuplicateArray(const T * src, size_t count)
{
T *result = AllocateArray<T>(count);
return CopyArray(result, src, count);
}
template<typename T>
T * Set(T * dst, uint8 value, size_t size)
{
return (T*)memset((void*)dst, (int)value, size);
}
}

View File

@ -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();
}
}

View File

@ -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();
};

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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<utf8>(_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<utf8>(_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<utf8>(_buffer, _capacity);
}
_buffer = Memory::ReallocateArray<utf8>(_buffer, _capacity);
}
};

View File

@ -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;
};

View File

@ -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 <SDL.h>
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<utf8>(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<utf8>(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;
}

View File

@ -2,10 +2,11 @@
#include <vector>
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<const utf8*> _strings;
std::vector<ObjectOverride> _objectOverrides;
std::vector<ScenarioOverride> _scenarioOverrides;
uint16 _id;
utf8 * _stringData;
LanguagePack(int id, const utf8 *text);
ObjectOverride *GetObjectOverride(const char *objectIdentifier);
ScenarioOverride *GetScenarioOverride(const utf8 *scenarioFilename);
std::vector<const utf8*> _strings;
std::vector<ObjectOverride> _objectOverrides;
std::vector<ScenarioOverride> _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);
};

View File

@ -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))))