Skip to content

Commit

Permalink
Saner data path resolution code
Browse files Browse the repository at this point in the history
  • Loading branch information
captainurist committed Nov 8, 2023
1 parent ebc36fa commit 1f32572
Show file tree
Hide file tree
Showing 8 changed files with 128 additions and 95 deletions.
10 changes: 3 additions & 7 deletions src/Application/Game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,31 +92,27 @@
#include "Media/MediaPlayer.h"

#include "Library/Platform/Application/PlatformApplication.h"
#include "Library/Environment/Interface/Environment.h"
#include "Library/Random/Random.h"
#include "Library/Logger/Logger.h"

#include "Utility/Format.h"
#include "Utility/DataPath.h"
#include "Utility/Exception.h"
#include "Utility/FileSystem.h"

void ShowMM7IntroVideo_and_LoadingScreen();

using Graphics::IRenderFactory;

void initDataPath(Environment *environment, Platform *platform, const std::string &dataPath) {
void initDataPath(Platform *platform, const std::string &dataPath) {
std::string missing_file;

if (validateDataPath(dataPath, missing_file)) {
setDataPath(expandUserPath(dataPath, environment->path(PATH_HOME)));
if (validateDataPath(dataPath, &missing_file)) {
setDataPath(dataPath);

std::string savesPath = makeDataPath("saves");
if (!std::filesystem::exists(savesPath)) {
std::filesystem::create_directory(savesPath);
}

logger->info("Using MM7 directory: {}", dataPath);
} else {
std::string message = fmt::format(
"Required file {} not found.\n"
Expand Down
3 changes: 1 addition & 2 deletions src/Application/Game.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ class PlatformApplication;
class GameTraceHandler;
class NuklearEventHandler;
class Platform;
class Environment;

class Game {
public:
Expand Down Expand Up @@ -50,6 +49,6 @@ class Game {
std::shared_ptr<Nuklear> _nuklear = nullptr;
};

void initDataPath(Environment *environment, Platform *platform, const std::string &dataPath);
void initDataPath(Platform *platform, const std::string &dataPath);

extern class GraphicsImage *gamma_preview_image; // 506E40
144 changes: 79 additions & 65 deletions src/Application/GamePathResolver.cpp
Original file line number Diff line number Diff line change
@@ -1,81 +1,95 @@
#include <vector>

#include "Application/GamePathResolver.h"
#include "GamePathResolver.h"

#include "Library/Logger/Logger.h"
#include "Library/Environment/Interface/Environment.h"

static std::string _resolvePath(Environment *environment, const char *envVarOverride, const std::vector<const char *> &registryKeys);
struct PathResolutionConfig {
const char *overrideEnvKey = nullptr;
std::initializer_list<const char *> registryKeys;
};

std::string resolveMm6Path(Environment *environment) {
return _resolvePath(
environment,
mm6PathOverrideKey,
{
"HKEY_LOCAL_MACHINE/SOFTWARE/GOG.com/Games/1207661253/PATH",
"HKEY_LOCAL_MACHINE/SOFTWARE/GOG.com/GOGMM6/PATH",
"HKEY_LOCAL_MACHINE/SOFTWARE/New World Computing/Might and Magic\xC2\xAE VI/1.0/AppPath", // \xC2\xAE is (R) in utf-8.
"HKEY_LOCAL_MACHINE/SOFTWARE/WOW6432Node/GOG.com/Games/1207661253/PATH",
"HKEY_LOCAL_MACHINE/SOFTWARE/WOW6432Node/GOG.com/GOGMM6/PATH",
"HKEY_LOCAL_MACHINE/SOFTWARE/WOW6432Node/New World Computing/Might and Magic\xC2\xAE VI/1.0/AppPath",
}
);
}
static constexpr PathResolutionConfig mm6Config = {
mm6PathOverrideKey,
{
"HKEY_LOCAL_MACHINE/SOFTWARE/GOG.com/Games/1207661253/PATH",
"HKEY_LOCAL_MACHINE/SOFTWARE/GOG.com/GOGMM6/PATH",
"HKEY_LOCAL_MACHINE/SOFTWARE/New World Computing/Might and Magic\xC2\xAE VI/1.0/AppPath", // \xC2\xAE is (R) in utf-8.
"HKEY_LOCAL_MACHINE/SOFTWARE/WOW6432Node/GOG.com/Games/1207661253/PATH",
"HKEY_LOCAL_MACHINE/SOFTWARE/WOW6432Node/GOG.com/GOGMM6/PATH",
"HKEY_LOCAL_MACHINE/SOFTWARE/WOW6432Node/New World Computing/Might and Magic\xC2\xAE VI/1.0/AppPath",
}
};

static constexpr PathResolutionConfig mm7Config = {
mm7PathOverrideKey,
{
"HKEY_LOCAL_MACHINE/SOFTWARE/GOG.com/Games/1207658916/Path",
"HKEY_LOCAL_MACHINE/SOFTWARE/GOG.com/GOGMM7/PATH",
"HKEY_LOCAL_MACHINE/SOFTWARE/New World Computing/Might and Magic VII/1.0/AppPath",
"HKEY_LOCAL_MACHINE/SOFTWARE/WOW6432Node/GOG.com/Games/1207658916/Path",
"HKEY_LOCAL_MACHINE/SOFTWARE/WOW6432Node/GOG.com/GOGMM7/PATH",
"HKEY_LOCAL_MACHINE/SOFTWARE/WOW6432Node/New World Computing/Might and Magic VII/1.0/AppPath",
}
};

std::string resolveMm7Path(Environment *environment) {
return _resolvePath(
environment,
mm7PathOverrideKey,
{
"HKEY_LOCAL_MACHINE/SOFTWARE/GOG.com/Games/1207658916/Path",
"HKEY_LOCAL_MACHINE/SOFTWARE/GOG.com/GOGMM7/PATH",
"HKEY_LOCAL_MACHINE/SOFTWARE/New World Computing/Might and Magic VII/1.0/AppPath",
"HKEY_LOCAL_MACHINE/SOFTWARE/WOW6432Node/GOG.com/Games/1207658916/Path",
"HKEY_LOCAL_MACHINE/SOFTWARE/WOW6432Node/GOG.com/GOGMM7/PATH",
"HKEY_LOCAL_MACHINE/SOFTWARE/WOW6432Node/New World Computing/Might and Magic VII/1.0/AppPath",
}
);
}
static constexpr PathResolutionConfig mm8Config = {
mm8PathOverrideKey,
{
"HKEY_LOCAL_MACHINE/SOFTWARE/GOG.com/GOGMM8/PATH",
"HKEY_LOCAL_MACHINE/SOFTWARE/New World Computing/Might and Magic Day of the Destroyer/1.0/AppPath",
"HKEY_LOCAL_MACHINE/SOFTWARE/WOW6432Node/GOG.com/GOGMM8/PATH",
"HKEY_LOCAL_MACHINE/SOFTWARE/WOW6432Node/New World Computing/Might and Magic Day of the Destroyer/1.0/AppPath",
}
};

static std::vector<std::string> resolvePaths(Environment *environment, const PathResolutionConfig &config) {
std::string envPath = environment->getenv(config.overrideEnvKey);
if (!envPath.empty()) {
logger->info("Path override provided, '{}={}'.", config.overrideEnvKey, envPath);
return {envPath}; // Have path override => this is the only one we'll try.
}

std::string resolveMm8Path(Environment *environment) {
return _resolvePath(
environment,
mm8PathOverrideKey,
{
"HKEY_LOCAL_MACHINE/SOFTWARE/GOG.com/GOGMM8/PATH",
"HKEY_LOCAL_MACHINE/SOFTWARE/New World Computing/Might and Magic Day of the Destroyer/1.0/AppPath",
"HKEY_LOCAL_MACHINE/SOFTWARE/WOW6432Node/GOG.com/GOGMM8/PATH",
"HKEY_LOCAL_MACHINE/SOFTWARE/WOW6432Node/New World Computing/Might and Magic Day of the Destroyer/1.0/AppPath",
}
);
}
std::vector<std::string> result;

static std::string _resolvePath(Environment *environment, const char *envVarOverride, const std::vector<const char *> &registryKeys) {
#ifdef __ANDROID__
// TODO: find a better way to deal with paths and remove this android specific block.
std::string result = environment->path(PATH_ANDROID_STORAGE_EXTERNAL);
if (result.empty())
result = environment->path(PATH_ANDROID_STORAGE_INTERNAL);
// Windows-specific.
for (const char *registryKey : config.registryKeys) {
std::string registryPath = environment->queryRegistry(registryKey);
if (!registryPath.empty())
result.push_back(registryPath);
}

// Android-specific.
std::string externalPath = environment->path(PATH_ANDROID_STORAGE_EXTERNAL);
if (!externalPath.empty())
result.push_back(externalPath);
std::string internalPath = environment->path(PATH_ANDROID_STORAGE_INTERNAL);
if (!internalPath.empty())
result.push_back(internalPath);
// TODO(captainurist): need a mechanism to show user-visible errors. Commenting out for now.
//if (result.empty())
//if (ANDROID && result.empty())
// platform->showMessageBox("Device currently unsupported", "Your device doesn't have any storage so it is unsupported!");

// Mac-specific.
#ifdef __APPLE__
std::string home = environment->path(PATH_HOME);
if (!home.empty())
result.push_back(home + "/Library/Application Support/OpenEnroth");
#endif

// PWD.
result.push_back(".");

return result;
#else
std::string envPath = environment->getenv(envVarOverride);
if (!envPath.empty()) {
logger->info("Path override provided: {}={}", envVarOverride, envPath);
return envPath;
}
}

for (auto key : registryKeys) {
envPath = environment->queryRegistry(key);
if (!envPath.empty()) {
return envPath;
}
}
std::vector<std::string> resolveMm6Paths(Environment *environment) {
return resolvePaths(environment, mm6Config);
}

return "";
#endif
std::vector<std::string> resolveMm7Paths(Environment *environment) {
return resolvePaths(environment, mm7Config);
}

std::vector<std::string> resolveMm8Paths(Environment *environment) {
return resolvePaths(environment, mm8Config);
}
9 changes: 6 additions & 3 deletions src/Application/GamePathResolver.h
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
#pragma once

#include <string>
#include <vector>

class Environment;

constexpr char mm6PathOverrideKey[] = "OPENENROTH_MM6_PATH";
constexpr char mm7PathOverrideKey[] = "OPENENROTH_MM7_PATH";
constexpr char mm8PathOverrideKey[] = "OPENENROTH_MM8_PATH";

std::string resolveMm6Path(Environment *environment);
std::string resolveMm7Path(Environment *environment);
std::string resolveMm8Path(Environment *environment);
std::vector<std::string> resolveMm6Paths(Environment *environment);
std::vector<std::string> resolveMm7Paths(Environment *environment);
std::vector<std::string> resolveMm8Paths(Environment *environment);
43 changes: 33 additions & 10 deletions src/Application/GameStarter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <utility>
#include <filesystem>
#include <string>
#include <vector>

#include "Engine/Engine.h"

Expand All @@ -15,6 +16,8 @@
#include "Library/Platform/Interface/Platform.h"
#include "Library/Platform/Null/NullPlatform.h"

#include "Utility/DataPath.h"

#include "GamePathResolver.h"
#include "GameConfig.h"
#include "Game.h"
Expand All @@ -37,7 +40,7 @@ GameStarter::GameStarter(GameStarterOptions options): _options(std::move(options
}

// Init paths.
resolveDefaults(_environment.get(), &_options);
resolvePaths(_environment.get(), &_options, _logger.get());

// Init config - needs data paths initialized.
_config = std::make_shared<GameConfig>();
Expand All @@ -62,8 +65,8 @@ GameStarter::GameStarter(GameStarterOptions options): _options(std::move(options
_logger->setSink(_defaultSink.get());
_bufferSink->flush(_logger.get());

// Validate data paths.
initDataPath(_environment.get(), _platform.get(), _options.dataPath);
// Init global data path.
initDataPath(_platform.get(), _options.dataPath);

// Create application & game.
_application = std::make_unique<PlatformApplication>(_platform.get());
Expand All @@ -72,15 +75,35 @@ GameStarter::GameStarter(GameStarterOptions options): _options(std::move(options

GameStarter::~GameStarter() = default;

void GameStarter::resolveDefaults(Environment *environment, GameStarterOptions* options) {
if (options->dataPath.empty())
options->dataPath = resolveMm7Path(environment);
void GameStarter::resolvePaths(Environment *environment, GameStarterOptions* options, Logger *logger) {
if (options->dataPath.empty()) {
std::vector<std::string> candidates = resolveMm7Paths(environment);
assert(!candidates.empty());

if (candidates.size() > 1) {
for (int i = 0; i < candidates.size(); i++) {
std::string missingFile;
if (!std::filesystem::exists(candidates[i])) {
logger->info("Data path candidate #{} ('{}') doesn't exist.", i + 1, candidates[i]);
} else if (!validateDataPath(candidates[i], &missingFile)) {
logger->info("Data path candidate #{} ('{}') is missing file '{}'.", i + 1, candidates[i], missingFile);
} else {
logger->info("Data path candidate #{} ('{}') is OK!", i + 1, candidates[i]);
options->dataPath = candidates[i];
break;
}
}
}

if (options->useConfig && options->configPath.empty()) {
options->configPath = "openenroth.ini";
if (!options->dataPath.empty())
options->configPath = options->dataPath + "/" + options->configPath;
if (options->dataPath.empty()) // Only one candidate, or no valid candidates? Use the last one & re-check it later.
options->dataPath = candidates.back();
}

assert(!options->dataPath.empty());
logger->info("Using data path '{}'.", options->dataPath);

if (options->useConfig && options->configPath.empty())
options->configPath = options->dataPath + "/openenroth.ini";
}

void GameStarter::run() {
Expand Down
2 changes: 1 addition & 1 deletion src/Application/GameStarter.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class GameStarter {
void run();

private:
static void resolveDefaults(Environment *environment, GameStarterOptions* options);
static void resolvePaths(Environment *environment, GameStarterOptions* options, Logger *logger);

private:
GameStarterOptions _options;
Expand Down
10 changes: 4 additions & 6 deletions src/Utility/DataPath.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,7 @@ std::string makeDataPath(std::initializer_list<std::string_view> paths) {
return makeCaseInsensitivePath(result).string();
}

bool validateDataPath(const std::string &data_path, std::string& missing_file) {
bool isGood = true;
bool validateDataPath(const std::string &data_path, std::string *missing_file) {
for (auto v : globalValidateList) {
std::filesystem::path path = data_path;
for (auto p : v) {
Expand All @@ -63,11 +62,10 @@ bool validateDataPath(const std::string &data_path, std::string& missing_file) {
}

if (!std::filesystem::exists(makeCaseInsensitivePath(path))) {
isGood = false;
missing_file = std::filesystem::absolute(path).string();
break;
*missing_file = std::filesystem::absolute(path).string();
return false;
}
}

return isGood;
return true;
}
2 changes: 1 addition & 1 deletion src/Utility/DataPath.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@ std::string makeDataPath(Ts&&... paths) {
return makeDataPath({paths...});
}

bool validateDataPath(const std::string &data_path, std::string& missing_file);
bool validateDataPath(const std::string &data_path, std::string *missing_file);

0 comments on commit 1f32572

Please sign in to comment.