Skip to content

Commit

Permalink
Merge pull request OpenEnroth#1885 from captainurist/macos_fixes_611
Browse files Browse the repository at this point in the history
Write out game config nicely
  • Loading branch information
pskelton authored Nov 19, 2024
2 parents d9d2e67 + 60a0de3 commit a4bb5d1
Show file tree
Hide file tree
Showing 9 changed files with 458 additions and 232 deletions.
5 changes: 2 additions & 3 deletions src/Application/GameConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ GameConfig::CheatCommands::CheatCommands(GameConfig *config) : ConfigSection(con

void GameConfig::CheatCommands::_addCommand(int commandIndex, const std::string& defaultValue) {
std::string name = fmt::format("command{:02}", commandIndex + 1);
auto item = std::make_unique<String>(this, name, defaultValue,
"Cheat Command. Example: 'xp add 1000|Give 1000 xp to current Character'");
CommandList.push_back(std::move(item));
auto item = std::make_unique<String>(this, name, defaultValue, fmt::format("Cheat command #{}.", commandIndex + 1));
_commandList.push_back(std::move(item));
}
448 changes: 243 additions & 205 deletions src/Application/GameConfig.h

Large diffs are not rendered by default.

11 changes: 1 addition & 10 deletions src/Application/Startup/GameStarter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -262,15 +262,6 @@ void GameStarter::migrateUserData() {
}
}
}

if (ufs->exists(configName)) {
logger->info(" Target config exists, skipping config migration.");
} else if (!dfs->exists(configName)) {
logger->info(" No config file to migrate.");
} else {
ufs->write(configName, dfs->read(configName));
logger->info(" Copied '{}'.", configName);
}
}

void GameStarter::run() {
Expand All @@ -279,7 +270,7 @@ void GameStarter::run() {

_application->component<GameWindowHandler>()->UpdateConfigFromWindow(_config.get());
_config->save(ufs->openForWriting(configName).get());
logger->info("Configuration file '{}' saved!", configName);
logger->info("Configuration file '{}' saved!", ufs->displayPath(configName));
} catch (const std::exception &e) {
// Log the exception so that it goes to all registered loggers.
logger->critical("Terminated with exception: {}", e.what());
Expand Down
29 changes: 18 additions & 11 deletions src/Library/Config/Config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
#include "Utility/Streams/FileInputStream.h"
#include "Utility/Streams/FileOutputStream.h"
#include "Utility/MapAccess.h"
#include "Utility/String/Format.h"
#include "Utility/String/Wrap.h"

void Config::load(std::string_view path) {
FileInputStream stream(path); // Will throw if file doesn't exist.
Expand All @@ -22,10 +24,10 @@ void Config::save(std::string_view path) const {
}

void Config::load(InputStream *stream) {
// We'd rather handle FS errors on our side.
std::istringstream stdStream(stream->readAll());

ini::IniFile ini;
ini.setCommentChar(';'); // Use ini comment char, not '#'.
ini.decode(stdStream); // This can throw.

for (const auto &[sectionName, iniSection] : ini)
Expand All @@ -36,16 +38,21 @@ void Config::load(InputStream *stream) {
}

void Config::save(OutputStream *stream) const {
ini::IniFile ini;
for (ConfigSection *section : sections())
for (AnyConfigEntry *entry : section->entries())
ini[section->name()][entry->name()] = entry->string();

std::ostringstream stdStream;
ini.encode(stdStream); // This can throw.

// Same here - we'd rather handle FS errors on our side.
stream->write(stdStream.str());
// ini::IniFile doesn't support comments so we just write things out ourselves.
for (ConfigSection *section : sections()) {
stream->write(fmt::format("[{}]\n", section->name()));

for (AnyConfigEntry *entry : section->entries()) {
if (!entry->description().empty())
for (const std::string &line : wrapText(entry->description(), 78))
stream->write(fmt::format("; {}\n", line));
stream->write(fmt::format("; Default is '{}'.\n", entry->defaultString()));
stream->write(fmt::format("{} = {}\n", entry->name(), entry->string()));
stream->write("\n");
}

stream->write("\n");
}
}

void Config::reset() {
Expand Down
9 changes: 6 additions & 3 deletions src/Utility/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ set(UTILITY_SOURCES
String/Ascii.cpp
String/Split.cpp
String/Transformations.cpp
UnicodeCrt.cpp)
UnicodeCrt.cpp
String/Wrap.cpp)

set(UTILITY_HEADERS
Embedded.h
Expand Down Expand Up @@ -46,7 +47,8 @@ set(UTILITY_HEADERS
Unaligned.h
UnicodeCrt.h
SmallVector.h
ScopedRollback.h)
ScopedRollback.h
String/Wrap.h)

if(OE_BUILD_PLATFORM STREQUAL "windows")
list(APPEND UTILITY_HEADERS Win/Unicode.h)
Expand Down Expand Up @@ -78,7 +80,8 @@ if(OE_BUILD_TESTS)
String/Tests/TransparentFunctors_ut.cpp
String/Tests/Ascii_ut.cpp
String/Tests/Split_ut.cpp
String/Tests/Join_ut.cpp)
String/Tests/Join_ut.cpp
String/Tests/Wrap_ut.cpp)

if(OE_BUILD_PLATFORM STREQUAL "windows")
list(APPEND TEST_UTILITY_SOURCES Win/Tests/Unicode_ut.cpp)
Expand Down
4 changes: 4 additions & 0 deletions src/Utility/String/Ascii.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ inline char toUpper(char c) {
return isLower(c) ? c - 'a' + 'A' : c;
}

inline bool isSpace(char c) {
return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v';
}

std::string toLower(std::string_view text);
std::string toUpper(std::string_view text);

Expand Down
132 changes: 132 additions & 0 deletions src/Utility/String/Tests/Wrap_ut.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
#include <vector>
#include <string>

#include "Testing/Unit/UnitTest.h"

#include "Utility/String/Wrap.h"

UNIT_TEST(StringWrap, BasicSentence) {
std::string text = "This is a simple test";
size_t width = 10;
std::vector<std::string> expected = {"This is a", "simple", "test"};
EXPECT_EQ(wrapText(text, width), expected);
}

UNIT_TEST(StringWrap, EmptyString) {
std::string text = "";
size_t width = 10;
std::vector<std::string> expected = {""};
EXPECT_EQ(wrapText(text, width), expected);
}

UNIT_TEST(StringWrap, SingleShortWord) {
std::string text = "Hello";
size_t width = 10;
std::vector<std::string> expected = {"Hello"};
EXPECT_EQ(wrapText(text, width), expected);
}

UNIT_TEST(StringWrap, SingleLongWord) {
std::string text = "Supercalifragilisticexpialidocious";
size_t width = 10;
std::vector<std::string> expected = {"Supercalif", "ragilistic", "expialidoc", "ious"};
EXPECT_EQ(wrapText(text, width), expected);
}

UNIT_TEST(StringWrap, WordsLongerThanWidth) {
std::string text = "Hello Supercalifragilisticexpialidocious World";
size_t width = 10;
std::vector<std::string> expected = {"Hello", "Supercalif", "ragilistic", "expialidoc", "ious World"};
EXPECT_EQ(wrapText(text, width), expected);
}

UNIT_TEST(StringWrap, ExactFitWidth) {
std::string text = "1234567890 12345";
size_t width = 10;
std::vector<std::string> expected = {"1234567890", "12345"};
EXPECT_EQ(wrapText(text, width), expected);
}

UNIT_TEST(StringWrap, LeadingTrailingSpaces) {
std::string text = " Hello world ";
size_t width = 10;
std::vector<std::string> expected = {" Hello", "world "};
EXPECT_EQ(wrapText(text, width), expected);
}

UNIT_TEST(StringWrap, WidthGreaterThanTextLength) {
std::string text = "Hello world";
size_t width = 20;
std::vector<std::string> expected = {"Hello world"};
EXPECT_EQ(wrapText(text, width), expected);
}

UNIT_TEST(StringWrap, WidthEqualToTextLength) {
std::string text = "Hello world";
size_t width = 11;
std::vector<std::string> expected = {"Hello world"};
EXPECT_EQ(wrapText(text, width), expected);
}

UNIT_TEST(StringWrap, WidthOne) {
std::string text = "AB CD EFGH";
size_t width = 1;
std::vector<std::string> expected = {"A", "B", "C", "D", "E", "F", "G", "H"};
EXPECT_EQ(wrapText(text, width), expected);
}

UNIT_TEST(StringWrap, AllSpaces) {
std::string text = " ";
size_t width = 10;
std::vector<std::string> expected = {" "};
EXPECT_EQ(wrapText(text, width), expected);
}

UNIT_TEST(StringWrap, ForcedLineBreak) {
std::string text = "Hello\nWorld";
size_t width = 10;
std::vector<std::string> expected = {"Hello", "World"};
EXPECT_EQ(wrapText(text, width), expected);
}

UNIT_TEST(StringWrap, ForcedLineBreakWrap) {
std::string text = "This is a\ntest with forced\nline breaks.";
size_t width = 15;
std::vector<std::string> expected = {"This is a", "test with", "forced", "line breaks."};
EXPECT_EQ(wrapText(text, width), expected);
}

UNIT_TEST(StringWrap, ForcedLineBreakRepeated) {
std::string text = "Hello\n\nWorld";
size_t width = 10;
std::vector<std::string> expected = {"Hello", "", "World"};
EXPECT_EQ(wrapText(text, width), expected);
}

UNIT_TEST(StringWrap, NewlineWithLongWordExceedingWidth) {
std::string text = "Hello\nSupercalifragilisticexpialidocious\nWorld";
size_t width = 10;
std::vector<std::string> expected = {"Hello", "Supercalif", "ragilistic", "expialidoc", "ious", "World"};
EXPECT_EQ(wrapText(text, width), expected);
}

UNIT_TEST(StringWrap, NewlineAtEndOfText) {
std::string text = "This is a test\n";
size_t width = 15;
std::vector<std::string> expected = {"This is a test", ""};
EXPECT_EQ(wrapText(text, width), expected);
}

UNIT_TEST(StringWrap, NewlineAtStartOfText) {
std::string text = "\nThis is a test";
size_t width = 10;
std::vector<std::string> expected = {"", "This is a", "test"};
EXPECT_EQ(wrapText(text, width), expected);
}

UNIT_TEST(StringWrap, MultipleNewlinesOnly) {
std::string text = "\n\n\n";
size_t width = 10;
std::vector<std::string> expected = {"", "", "", ""};
EXPECT_EQ(wrapText(text, width), expected);
}
46 changes: 46 additions & 0 deletions src/Utility/String/Wrap.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#include "Wrap.h"

#include <string>
#include <vector>

#include "Ascii.h"

std::vector<std::string> wrapText(std::string_view text, size_t size) {
std::vector<std::string> result;

size_t breakablePos = 0;
size_t lineLength = 0;
size_t lineStart = 0;
for (size_t i = 0; i <= text.size(); ++i) {
lineLength++;

char c = i == text.size() ? '\n' : text[i]; // Simple hack so that we don't have to do weird ifs after the loop.
if (ascii::isSpace(c))
breakablePos = i;

size_t breakPos = static_cast<size_t>(-1);
size_t startPos = 0;
if (lineLength > size && breakablePos != 0) {
/* OK to break! */
breakPos = breakablePos;
startPos = breakablePos + 1;
} else if (lineLength > size && breakablePos == 0) {
/* Nowhere to break, so break mid-word. */
breakPos = i;
startPos = i;
} else if (c == '\n') {
/* Forced break. */
breakPos = i;
startPos = i + 1;
}

if (breakPos != static_cast<size_t>(-1)) {
result.emplace_back(text.substr(lineStart, breakPos - lineStart));
breakablePos = 0;
lineLength = i - startPos + 1;
lineStart = startPos;
}
}

return result;
}
6 changes: 6 additions & 0 deletions src/Utility/String/Wrap.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#pragma once

#include <string>
#include <vector>

std::vector<std::string> wrapText(std::string_view text, size_t size);

0 comments on commit a4bb5d1

Please sign in to comment.