Browse Source

More dependency untangling

1. Moves more assets-related stuff from `init` to `engine/assets`.
2. Removes `SDL_audiolib` dependency from `soundsample.h`.
3. Cleans up some unused/missing includes.
pull/7632/head
Gleb Mazovetskiy 1 year ago
parent
commit
d7647d6c63
  1. 1
      Source/DiabloUI/diabloui.cpp
  2. 2
      Source/DiabloUI/dialogs.cpp
  3. 7
      Source/DiabloUI/hero/selhero.cpp
  4. 1
      Source/DiabloUI/mainmenu.cpp
  5. 2
      Source/DiabloUI/settingsmenu.cpp
  6. 6
      Source/appfat.cpp
  7. 7
      Source/appfat.h
  8. 1
      Source/control.cpp
  9. 3
      Source/diablo.cpp
  10. 8
      Source/diablo.h
  11. 2
      Source/discord/discord.cpp
  12. 1
      Source/dvlnet/packet.h
  13. 2
      Source/effects.cpp
  14. 209
      Source/engine/assets.cpp
  15. 22
      Source/engine/assets.hpp
  16. 1
      Source/engine/dx.cpp
  17. 2
      Source/engine/palette.h
  18. 5
      Source/engine/path.cpp
  19. 4
      Source/engine/path.h
  20. 2
      Source/engine/render/clx_render.cpp
  21. 2
      Source/engine/sound.cpp
  22. 2
      Source/gamemenu.cpp
  23. 2
      Source/help.cpp
  24. 216
      Source/init.cpp
  25. 39
      Source/init.h
  26. 3
      Source/interfac.cpp
  27. 2
      Source/items.cpp
  28. 7
      Source/levels/gendung.cpp
  29. 1
      Source/levels/gendung.h
  30. 4
      Source/levels/town.cpp
  31. 2
      Source/levels/trigs.cpp
  32. 4
      Source/lighting.h
  33. 2
      Source/loadsave.cpp
  34. 2
      Source/missiles.cpp
  35. 3
      Source/monster.cpp
  36. 1
      Source/monster.h
  37. 2
      Source/mpq/mpq_writer.cpp
  38. 2
      Source/objects.cpp
  39. 1
      Source/options.cpp
  40. 1
      Source/pack.cpp
  41. 2
      Source/panels/spell_book.cpp
  42. 1
      Source/panels/spell_icons.cpp
  43. 1
      Source/pfile.cpp
  44. 5
      Source/player.cpp
  45. 1
      Source/qol/chatlog.cpp
  46. 1
      Source/quests.cpp
  47. 4
      Source/stores.cpp
  48. 1
      Source/utils/log.hpp
  49. 22
      Source/utils/soundsample.cpp
  50. 31
      Source/utils/soundsample.h
  51. 22
      test/app_fatal_for_testing.cpp
  52. 2
      test/dun_render_benchmark.cpp
  53. 1
      test/inv_test.cpp
  54. 2
      test/language_for_testing.cpp
  55. 1
      test/pack_test.cpp
  56. 3
      test/timedemo_test.cpp

1
Source/DiabloUI/diabloui.cpp

@ -23,6 +23,7 @@
#include "engine/render/clx_render.hpp"
#include "engine/ticks.hpp"
#include "hwcursor.hpp"
#include "init.h"
#include "utils/algorithm/container.hpp"
#include "utils/display.h"
#include "utils/is_of.hpp"

2
Source/DiabloUI/dialogs.cpp

@ -14,7 +14,9 @@
#include "engine/load_clx.hpp"
#include "engine/load_pcx.hpp"
#include "engine/palette.h"
#include "headless_mode.hpp"
#include "hwcursor.hpp"
#include "init.h"
#include "utils/display.h"
#include "utils/is_of.hpp"
#include "utils/language.h"

7
Source/DiabloUI/hero/selhero.cpp

@ -15,6 +15,7 @@
#include "DiabloUI/selyesno.h"
#include "control.h"
#include "controls/plrctrls.h"
#include "engine/assets.hpp"
#include "game_mode.hpp"
#include "menu.h"
#include "options.h"
@ -161,10 +162,10 @@ void SelheroListSelect(size_t value)
vecSelHeroDlgItems.push_back(std::make_unique<UiListItem>(_("Sorcerer"), static_cast<int>(HeroClass::Sorcerer)));
if (gbIsHellfire) {
vecSelHeroDlgItems.push_back(std::make_unique<UiListItem>(_("Monk"), static_cast<int>(HeroClass::Monk)));
if (gbBard || *sgOptions.Gameplay.testBard) {
if (HaveBardAssets() || *sgOptions.Gameplay.testBard) {
vecSelHeroDlgItems.push_back(std::make_unique<UiListItem>(_("Bard"), static_cast<int>(HeroClass::Bard)));
}
if (gbBarbarian || *sgOptions.Gameplay.testBarbarian) {
if (HaveBarbarianAssets() || *sgOptions.Gameplay.testBarbarian) {
vecSelHeroDlgItems.push_back(std::make_unique<UiListItem>(_("Barbarian"), static_cast<int>(HeroClass::Barbarian)));
}
}
@ -263,7 +264,7 @@ void AddSelHeroBackground()
void SelheroClassSelectorSelect(size_t value)
{
auto hClass = static_cast<HeroClass>(vecSelHeroDlgItems[value]->m_value);
if (gbIsSpawn && (hClass == HeroClass::Rogue || hClass == HeroClass::Sorcerer || (hClass == HeroClass::Bard && !gbBard))) {
if (gbIsSpawn && (hClass == HeroClass::Rogue || hClass == HeroClass::Sorcerer || (hClass == HeroClass::Bard && !HaveBardAssets()))) {
RemoveSelHeroBackground();
UiSelOkDialog(nullptr, _("The Rogue and Sorcerer are only available in the full retail version of Diablo. Visit https://www.gog.com/game/diablo to purchase.").data(), false);
AddSelHeroBackground();

1
Source/DiabloUI/mainmenu.cpp

@ -3,6 +3,7 @@
#include "DiabloUI/diabloui.h"
#include "DiabloUI/selok.h"
#include "control.h"
#include "engine/assets.hpp"
#include "engine/load_clx.hpp"
#include "game_mode.hpp"
#include "utils/language.h"

2
Source/DiabloUI/settingsmenu.cpp

@ -2,6 +2,7 @@
#include <cstdint>
#include <optional>
#include <vector>
#include <function_ref.hpp>
@ -11,6 +12,7 @@
#include "controls/controller_motion.h"
#include "controls/plrctrls.h"
#include "controls/remap_keyboard.h"
#include "engine/assets.hpp"
#include "engine/render/text_render.hpp"
#include "hwcursor.hpp"
#include "options.h"

6
Source/appfat.cpp

@ -49,15 +49,15 @@ void FreeDlg()
SNetDestroy();
}
[[noreturn]] void DisplayFatalErrorAndExit(std::string_view title, std::string_view body)
} // namespace
void DisplayFatalErrorAndExit(std::string_view title, std::string_view body)
{
FreeDlg();
UiErrorOkDialog(title, body);
diablo_quit(1);
}
} // namespace
void app_fatal(std::string_view str)
{
DisplayFatalErrorAndExit(_("Error"), str);

7
Source/appfat.h

@ -21,6 +21,13 @@ namespace devilution {
#define assert(exp) (void)((exp) || (assert_fail(__LINE__, __FILE__, #exp), 0))
#endif
/**
* @brief Terminates the game and displays an error message box.
* @param str Message box title.
* @param str Error message.
*/
[[noreturn]] void DisplayFatalErrorAndExit(std::string_view title, std::string_view body);
/**
* @brief Terminates the game and displays an error message box.
* @param str Error message.

1
Source/control.cpp

@ -29,7 +29,6 @@
#include "engine/trn.hpp"
#include "gamemenu.h"
#include "headless_mode.hpp"
#include "init.h"
#include "inv.h"
#include "inv_iterators.hpp"
#include "levels/setmaps.h"

3
Source/diablo.cpp

@ -12,6 +12,7 @@
#include <config.h>
#include "DiabloUI/selstart.h"
#include "appfat.h"
#include "automap.h"
#include "capture.h"
#include "control.h"
@ -124,8 +125,6 @@ bool gbProcessPlayers;
bool gbLoadGame;
bool cineflag;
int PauseMode;
bool gbBard;
bool gbBarbarian;
clicktype sgbMouseDown;
uint16_t gnTickDelay = 50;
char gszProductName[64] = "DevilutionX vUnknown";

8
Source/diablo.h

@ -7,6 +7,12 @@
#include <cstdint>
#include <SDL.h>
#ifdef USE_SDL1
#include "utils/sdl2_to_1_2_backports.h"
#endif
#ifdef _DEBUG
#include "monstdat.h"
#endif
@ -69,8 +75,6 @@ extern bool cineflag;
/* These are defined in fonts.h */
extern void FontsCleanup();
extern DVL_API_FOR_TEST int PauseMode;
extern bool gbBard;
extern bool gbBarbarian;
extern clicktype sgbMouseDown;
extern uint16_t gnTickDelay;
extern char gszProductName[64];

2
Source/discord/discord.cpp

@ -17,7 +17,7 @@
#include <fmt/format.h>
#include "config.h"
#include "init.h"
#include "game_mode.hpp"
#include "levels/gendung.h"
#include "levels/setmaps.h"
#include "multi.h"

1
Source/dvlnet/packet.h

@ -16,6 +16,7 @@
#include "appfat.h"
#include "dvlnet/abstract_net.h"
#include "utils/attributes.h"
#include "utils/endian_read.hpp"
#include "utils/endian_write.hpp"
#include "utils/str_cat.hpp"
#include "utils/stubs.h"

2
Source/effects.cpp

@ -17,7 +17,7 @@
#include "engine/sound.h"
#include "engine/sound_defs.hpp"
#include "engine/sound_position.hpp"
#include "init.h"
#include "game_mode.hpp"
#include "player.h"
#include "utils/is_of.hpp"

209
Source/engine/assets.cpp

@ -5,11 +5,17 @@
#include <cstring>
#include <string_view>
#include "appfat.h"
#include "game_mode.hpp"
#include "utils/file_util.h"
#include "utils/log.hpp"
#include "utils/paths.h"
#include "utils/str_cat.hpp"
#include "utils/str_split.hpp"
#if defined(_WIN32) && !defined(__UWP__) && !defined(DEVILUTIONX_WINDOWS_NO_WCHAR)
#include <find_steam_game.h>
#endif
#ifndef UNPACKED_MPQS
#include "mpq/mpq_sdl_rwops.hpp"
@ -258,4 +264,207 @@ std::string FailedToOpenFileErrorMessage(std::string_view path, std::string_view
return fmt::format(fmt::runtime(_("Failed to open file:\n{:s}\n\n{:s}\n\nThe MPQ file(s) might be damaged. Please check the file integrity.")), path, error);
}
namespace {
#ifdef UNPACKED_MPQS
std::optional<std::string> FindUnpackedMpqData(const std::vector<std::string> &paths, std::string_view mpqName)
{
std::string targetPath;
for (const std::string &path : paths) {
targetPath.clear();
targetPath.reserve(path.size() + mpqName.size() + 1);
targetPath.append(path).append(mpqName) += DirectorySeparator;
if (FileExists(targetPath)) {
LogVerbose(" Found unpacked MPQ directory: {}", targetPath);
return targetPath;
}
}
return std::nullopt;
}
#else
std::optional<MpqArchive> LoadMPQ(const std::vector<std::string> &paths, std::string_view mpqName)
{
std::optional<MpqArchive> archive;
std::string mpqAbsPath;
std::int32_t error = 0;
for (const auto &path : paths) {
mpqAbsPath = path + mpqName.data();
if ((archive = MpqArchive::Open(mpqAbsPath.c_str(), error))) {
LogVerbose(" Found: {} in {}", mpqName, path);
return archive;
}
if (error != 0) {
LogError("Error {}: {}", MpqArchive::ErrorMessage(error), mpqAbsPath);
}
}
if (error == 0) {
LogVerbose("Missing: {}", mpqName);
}
return std::nullopt;
}
#endif
std::vector<std::string> GetMPQSearchPaths()
{
std::vector<std::string> paths;
paths.push_back(paths::BasePath());
paths.push_back(paths::PrefPath());
if (paths[0] == paths[1])
paths.pop_back();
paths.push_back(paths::ConfigPath());
if (paths[0] == paths[1] || (paths.size() == 3 && (paths[0] == paths[2] || paths[1] == paths[2])))
paths.pop_back();
#if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__)
// `XDG_DATA_HOME` is usually the root path of `paths::PrefPath()`, so we only
// add `XDG_DATA_DIRS`.
const char *xdgDataDirs = std::getenv("XDG_DATA_DIRS");
if (xdgDataDirs != nullptr) {
for (const std::string_view path : SplitByChar(xdgDataDirs, ':')) {
std::string fullPath(path);
if (!path.empty() && path.back() != '/')
fullPath += '/';
fullPath.append("diasurgical/devilutionx/");
paths.push_back(std::move(fullPath));
}
} else {
paths.emplace_back("/usr/local/share/diasurgical/devilutionx/");
paths.emplace_back("/usr/share/diasurgical/devilutionx/");
}
#elif defined(NXDK)
paths.emplace_back("D:\\");
#elif defined(_WIN32) && !defined(__UWP__) && !defined(DEVILUTIONX_WINDOWS_NO_WCHAR)
char gogpath[_FSG_PATH_MAX];
fsg_get_gog_game_path(gogpath, "1412601690");
if (strlen(gogpath) > 0) {
paths.emplace_back(std::string(gogpath) + "/");
paths.emplace_back(std::string(gogpath) + "/hellfire/");
}
#endif
if (paths.empty() || !paths.back().empty()) {
paths.emplace_back(); // PWD
}
if (SDL_LOG_PRIORITY_VERBOSE >= SDL_LogGetPriority(SDL_LOG_CATEGORY_APPLICATION)) {
LogVerbose("Paths:\n base: {}\n pref: {}\n config: {}\n assets: {}",
paths::BasePath(), paths::PrefPath(), paths::ConfigPath(), paths::AssetsPath());
std::string message;
for (std::size_t i = 0; i < paths.size(); ++i) {
message.append(fmt::format("\n{:6d}. '{}'", i + 1, paths[i]));
}
LogVerbose("MPQ search paths:{}", message);
}
return paths;
}
} // namespace
void LoadCoreArchives()
{
auto paths = GetMPQSearchPaths();
#ifdef UNPACKED_MPQS
font_data_path = FindUnpackedMpqData(paths, "fonts");
#else // !UNPACKED_MPQS
#if !defined(__ANDROID__) && !defined(__APPLE__) && !defined(__3DS__) && !defined(__SWITCH__)
// Load devilutionx.mpq first to get the font file for error messages
devilutionx_mpq = LoadMPQ(paths, "devilutionx.mpq");
#endif
font_mpq = LoadMPQ(paths, "fonts.mpq"); // Extra fonts
#endif
}
void LoadLanguageArchive()
{
#ifdef UNPACKED_MPQS
lang_data_path = std::nullopt;
#else
lang_mpq = std::nullopt;
#endif
std::string_view code = GetLanguageCode();
if (code != "en") {
std::string langMpqName { code };
#ifdef UNPACKED_MPQS
lang_data_path = FindUnpackedMpqData(GetMPQSearchPaths(), langMpqName);
#else
langMpqName.append(".mpq");
lang_mpq = LoadMPQ(GetMPQSearchPaths(), langMpqName);
#endif
}
}
void LoadGameArchives()
{
auto paths = GetMPQSearchPaths();
#ifdef UNPACKED_MPQS
diabdat_data_path = FindUnpackedMpqData(paths, "diabdat");
if (!diabdat_data_path) {
spawn_data_path = FindUnpackedMpqData(paths, "spawn");
if (spawn_data_path)
gbIsSpawn = true;
}
if (!HeadlessMode) {
AssetRef ref = FindAsset("ui_art\\title.clx");
if (!ref.ok()) {
LogError("{}", SDL_GetError());
InsertCDDlg(_("diabdat.mpq or spawn.mpq"));
}
}
hellfire_data_path = FindUnpackedMpqData(paths, "hellfire");
if (hellfire_data_path)
gbIsHellfire = true;
if (forceHellfire && !hellfire_data_path)
InsertCDDlg("hellfire");
const bool hasMonk = FileExists(*hellfire_data_path + "plrgfx/monk/mha/mhaas.clx");
const bool hasMusic = FileExists(*hellfire_data_path + "music/dlvlf.wav")
|| FileExists(*hellfire_data_path + "music/dlvlf.mp3");
const bool hasVoice = FileExists(*hellfire_data_path + "sfx/hellfire/cowsut1.wav")
|| FileExists(*hellfire_data_path + "sfx/hellfire/cowsut1.mp3");
if (gbIsHellfire && (!hasMonk || !hasMusic || !hasVoice)) {
DisplayFatalErrorAndExit(_("Some Hellfire MPQs are missing"), _("Not all Hellfire MPQs were found.\nPlease copy all the hf*.mpq files."));
}
#else // !UNPACKED_MPQS
diabdat_mpq = LoadMPQ(paths, "DIABDAT.MPQ");
if (!diabdat_mpq) {
// DIABDAT.MPQ is uppercase on the original CD and the GOG version.
diabdat_mpq = LoadMPQ(paths, "diabdat.mpq");
}
if (!diabdat_mpq) {
spawn_mpq = LoadMPQ(paths, "spawn.mpq");
if (spawn_mpq)
gbIsSpawn = true;
}
if (!HeadlessMode) {
AssetRef ref = FindAsset("ui_art\\title.pcx");
if (!ref.ok()) {
LogError("{}", SDL_GetError());
InsertCDDlg(_("diabdat.mpq or spawn.mpq"));
}
}
hellfire_mpq = LoadMPQ(paths, "hellfire.mpq");
if (hellfire_mpq)
gbIsHellfire = true;
if (forceHellfire && !hellfire_mpq)
InsertCDDlg("hellfire.mpq");
hfmonk_mpq = LoadMPQ(paths, "hfmonk.mpq");
hfbard_mpq = LoadMPQ(paths, "hfbard.mpq");
hfbarb_mpq = LoadMPQ(paths, "hfbarb.mpq");
hfmusic_mpq = LoadMPQ(paths, "hfmusic.mpq");
hfvoice_mpq = LoadMPQ(paths, "hfvoice.mpq");
if (gbIsHellfire && (!hfmonk_mpq || !hfmusic_mpq || !hfvoice_mpq)) {
DisplayFatalErrorAndExit(_("Some Hellfire MPQs are missing"), _("Not all Hellfire MPQs were found.\nPlease copy all the hf*.mpq files."));
}
#endif
}
} // namespace devilution

22
Source/engine/assets.hpp

@ -300,4 +300,26 @@ extern std::optional<MpqArchive> lang_mpq;
extern std::optional<MpqArchive> devilutionx_mpq;
#endif
void LoadCoreArchives();
void LoadLanguageArchive();
void LoadGameArchives();
#ifdef UNPACKED_MPQS
[[nodiscard]] inline bool HaveSpawn() { return spawn_data_path.has_value(); }
[[nodiscard]] inline bool HaveDiabdat() { return diabdat_data_path.has_value(); }
[[nodiscard]] inline bool HaveHellfire() { return hellfire_data_path.has_value(); }
[[nodiscard]] inline bool HaveExtraFonts() { return font_data_path.has_value(); }
// Bard and barbarian are not currently supported in unpacked mode.
[[nodiscard]] inline bool HaveBardAssets() { return false; }
[[nodiscard]] inline bool HaveBarbarianAssets() { return false; }
#else
[[nodiscard]] inline bool HaveSpawn() { return spawn_mpq.has_value(); }
[[nodiscard]] inline bool HaveDiabdat() { return diabdat_mpq.has_value(); }
[[nodiscard]] inline bool HaveHellfire() { return hellfire_mpq.has_value(); }
[[nodiscard]] inline bool HaveExtraFonts() { return font_mpq.has_value(); }
[[nodiscard]] inline bool HaveBardAssets() { return hfbard_mpq.has_value(); }
[[nodiscard]] inline bool HaveBarbarianAssets() { return hfbarb_mpq.has_value(); }
#endif
} // namespace devilution

1
Source/engine/dx.cpp

@ -11,6 +11,7 @@
#include "controls/plrctrls.h"
#include "engine/render/primitive_render.hpp"
#include "headless_mode.hpp"
#include "init.h"
#include "options.h"
#include "utils/display.h"
#include "utils/log.hpp"

2
Source/engine/palette.h

@ -8,6 +8,8 @@
#include <array>
#include <cstdint>
#include <SDL.h>
#include "levels/gendung.h"
namespace devilution {

5
Source/engine/path.cpp

@ -5,14 +5,15 @@
*/
#include "engine/path.h"
#include <array>
#include <cstdint>
#include <limits>
#include <function_ref.hpp>
#include "appfat.h"
#include "crawl.hpp"
#include "engine/direction.hpp"
#include "levels/gendung.h"
#include "lighting.h"
#include "objects.h"
namespace devilution {

4
Source/engine/path.h

@ -6,14 +6,12 @@
#pragma once
#include <cstdint>
#include <limits>
#include <optional>
#include <SDL.h>
#include <function_ref.hpp>
#include "engine/direction.hpp"
#include "engine/point.hpp"
#include "utils/attributes.h"
namespace devilution {

2
Source/engine/render/clx_render.cpp

@ -6,9 +6,11 @@
#include "clx_render.hpp"
#include <algorithm>
#include <cstdint>
#include "engine/point.hpp"
#include "engine/render/blit_impl.hpp"
#include "engine/surface.hpp"
#include "utils/attributes.h"
#include "utils/clx_decode.hpp"
#include "utils/static_vector.hpp"

2
Source/engine/sound.cpp

@ -13,11 +13,11 @@
#include <optional>
#include <string>
#include <Aulib/Stream.h>
#include <SDL.h>
#include <expected.hpp>
#include "engine/assets.hpp"
#include "init.h"
#include "options.h"
#include "utils/log.hpp"
#include "utils/math.h"

2
Source/gamemenu.cpp

@ -13,8 +13,8 @@
#include "engine/sound_defs.hpp"
#include "gmenu.h"
#include "headless_mode.hpp"
#include "init.h"
#include "loadsave.h"
#include "multi.h"
#include "options.h"
#include "pfile.h"
#include "qol/floatingnumbers.h"

2
Source/help.cpp

@ -10,7 +10,7 @@
#include "DiabloUI/ui_flags.hpp"
#include "engine/render/clx_render.hpp"
#include "engine/render/text_render.hpp"
#include "init.h"
#include "game_mode.hpp"
#include "minitext.h"
#include "qol/chatlog.h"
#include "stores.h"

216
Source/init.cpp

@ -12,10 +12,6 @@
#include <SDL.h>
#include <config.h>
#if defined(_WIN32) && !defined(__UWP__) && !defined(DEVILUTIONX_WINDOWS_NO_WCHAR)
#include <find_steam_game.h>
#endif
#include "DiabloUI/diabloui.h"
#include "engine/assets.hpp"
#include "engine/backbuffer_state.hpp"
@ -46,7 +42,6 @@ int _newlib_heap_size_user = 100 * 1024 * 1024;
namespace devilution {
/** True if the game is the current active window */
bool gbActive;
namespace {
@ -54,101 +49,6 @@ namespace {
constexpr char DevilutionXMpqVersion[] = "1\n";
constexpr char ExtraFontsVersion[] = "1\n";
#ifdef UNPACKED_MPQS
std::optional<std::string> FindUnpackedMpqData(const std::vector<std::string> &paths, std::string_view mpqName)
{
std::string targetPath;
for (const std::string &path : paths) {
targetPath.clear();
targetPath.reserve(path.size() + mpqName.size() + 1);
targetPath.append(path).append(mpqName) += DirectorySeparator;
if (FileExists(targetPath)) {
LogVerbose(" Found unpacked MPQ directory: {}", targetPath);
return targetPath;
}
}
return std::nullopt;
}
#else
std::optional<MpqArchive> LoadMPQ(const std::vector<std::string> &paths, std::string_view mpqName)
{
std::optional<MpqArchive> archive;
std::string mpqAbsPath;
std::int32_t error = 0;
for (const auto &path : paths) {
mpqAbsPath = path + mpqName.data();
if ((archive = MpqArchive::Open(mpqAbsPath.c_str(), error))) {
LogVerbose(" Found: {} in {}", mpqName, path);
return archive;
}
if (error != 0) {
LogError("Error {}: {}", MpqArchive::ErrorMessage(error), mpqAbsPath);
}
}
if (error == 0) {
LogVerbose("Missing: {}", mpqName);
}
return std::nullopt;
}
#endif
std::vector<std::string> GetMPQSearchPaths()
{
std::vector<std::string> paths;
paths.push_back(paths::BasePath());
paths.push_back(paths::PrefPath());
if (paths[0] == paths[1])
paths.pop_back();
paths.push_back(paths::ConfigPath());
if (paths[0] == paths[1] || (paths.size() == 3 && (paths[0] == paths[2] || paths[1] == paths[2])))
paths.pop_back();
#if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__)
// `XDG_DATA_HOME` is usually the root path of `paths::PrefPath()`, so we only
// add `XDG_DATA_DIRS`.
const char *xdgDataDirs = std::getenv("XDG_DATA_DIRS");
if (xdgDataDirs != nullptr) {
for (const std::string_view path : SplitByChar(xdgDataDirs, ':')) {
std::string fullPath(path);
if (!path.empty() && path.back() != '/')
fullPath += '/';
fullPath.append("diasurgical/devilutionx/");
paths.push_back(std::move(fullPath));
}
} else {
paths.emplace_back("/usr/local/share/diasurgical/devilutionx/");
paths.emplace_back("/usr/share/diasurgical/devilutionx/");
}
#elif defined(NXDK)
paths.emplace_back("D:\\");
#elif defined(_WIN32) && !defined(__UWP__) && !defined(DEVILUTIONX_WINDOWS_NO_WCHAR)
char gogpath[_FSG_PATH_MAX];
fsg_get_gog_game_path(gogpath, "1412601690");
if (strlen(gogpath) > 0) {
paths.emplace_back(std::string(gogpath) + "/");
paths.emplace_back(std::string(gogpath) + "/hellfire/");
}
#endif
if (paths.empty() || !paths.back().empty()) {
paths.emplace_back(); // PWD
}
if (SDL_LOG_PRIORITY_VERBOSE >= SDL_LogGetPriority(SDL_LOG_CATEGORY_APPLICATION)) {
LogVerbose("Paths:\n base: {}\n pref: {}\n config: {}\n assets: {}",
paths::BasePath(), paths::PrefPath(), paths::ConfigPath(), paths::AssetsPath());
std::string message;
for (std::size_t i = 0; i < paths.size(); ++i) {
message.append(fmt::format("\n{:6d}. '{}'", i + 1, paths[i]));
}
LogVerbose("MPQ search paths:{}", message);
}
return paths;
}
bool AssetContentsEq(AssetRef &&ref, std::string_view expected)
{
const size_t size = ref.size();
@ -243,122 +143,6 @@ void init_cleanup()
NetClose();
}
void LoadCoreArchives()
{
auto paths = GetMPQSearchPaths();
#ifdef UNPACKED_MPQS
font_data_path = FindUnpackedMpqData(paths, "fonts");
#else // !UNPACKED_MPQS
#if !defined(__ANDROID__) && !defined(__APPLE__) && !defined(__3DS__) && !defined(__SWITCH__)
// Load devilutionx.mpq first to get the font file for error messages
devilutionx_mpq = LoadMPQ(paths, "devilutionx.mpq");
#endif
font_mpq = LoadMPQ(paths, "fonts.mpq"); // Extra fonts
#endif
}
void LoadLanguageArchive()
{
#ifdef UNPACKED_MPQS
lang_data_path = std::nullopt;
#else
lang_mpq = std::nullopt;
#endif
std::string_view code = GetLanguageCode();
if (code != "en") {
std::string langMpqName { code };
#ifdef UNPACKED_MPQS
lang_data_path = FindUnpackedMpqData(GetMPQSearchPaths(), langMpqName);
#else
langMpqName.append(".mpq");
lang_mpq = LoadMPQ(GetMPQSearchPaths(), langMpqName);
#endif
}
}
void LoadGameArchives()
{
auto paths = GetMPQSearchPaths();
#ifdef UNPACKED_MPQS
diabdat_data_path = FindUnpackedMpqData(paths, "diabdat");
if (!diabdat_data_path) {
spawn_data_path = FindUnpackedMpqData(paths, "spawn");
if (spawn_data_path)
gbIsSpawn = true;
}
if (!HeadlessMode) {
AssetRef ref = FindAsset("ui_art\\title.clx");
if (!ref.ok()) {
LogError("{}", SDL_GetError());
InsertCDDlg(_("diabdat.mpq or spawn.mpq"));
}
}
hellfire_data_path = FindUnpackedMpqData(paths, "hellfire");
if (hellfire_data_path)
gbIsHellfire = true;
if (forceHellfire && !hellfire_data_path)
InsertCDDlg("hellfire");
const bool hasMonk = FileExists(*hellfire_data_path + "plrgfx/monk/mha/mhaas.clx");
const bool hasMusic = FileExists(*hellfire_data_path + "music/dlvlf.wav")
|| FileExists(*hellfire_data_path + "music/dlvlf.mp3");
const bool hasVoice = FileExists(*hellfire_data_path + "sfx/hellfire/cowsut1.wav")
|| FileExists(*hellfire_data_path + "sfx/hellfire/cowsut1.mp3");
// Bard and barbarian are not currently supported in unpacked mode
// because they use the same paths as rogue and warrior.
gbBard = false;
gbBarbarian = false;
if (gbIsHellfire && (!hasMonk || !hasMusic || !hasVoice)) {
UiErrorOkDialog(_("Some Hellfire MPQs are missing"), _("Not all Hellfire MPQs were found.\nPlease copy all the hf*.mpq files."));
diablo_quit(1);
}
#else // !UNPACKED_MPQS
diabdat_mpq = LoadMPQ(paths, "DIABDAT.MPQ");
if (!diabdat_mpq) {
// DIABDAT.MPQ is uppercase on the original CD and the GOG version.
diabdat_mpq = LoadMPQ(paths, "diabdat.mpq");
}
if (!diabdat_mpq) {
spawn_mpq = LoadMPQ(paths, "spawn.mpq");
if (spawn_mpq)
gbIsSpawn = true;
}
if (!HeadlessMode) {
AssetRef ref = FindAsset("ui_art\\title.pcx");
if (!ref.ok()) {
LogError("{}", SDL_GetError());
InsertCDDlg(_("diabdat.mpq or spawn.mpq"));
}
}
hellfire_mpq = LoadMPQ(paths, "hellfire.mpq");
if (hellfire_mpq)
gbIsHellfire = true;
if (forceHellfire && !hellfire_mpq)
InsertCDDlg("hellfire.mpq");
hfmonk_mpq = LoadMPQ(paths, "hfmonk.mpq");
hfbard_mpq = LoadMPQ(paths, "hfbard.mpq");
if (hfbard_mpq)
gbBard = true;
hfbarb_mpq = LoadMPQ(paths, "hfbarb.mpq");
if (hfbarb_mpq)
gbBarbarian = true;
hfmusic_mpq = LoadMPQ(paths, "hfmusic.mpq");
hfvoice_mpq = LoadMPQ(paths, "hfvoice.mpq");
if (gbIsHellfire && (!hfmonk_mpq || !hfmusic_mpq || !hfvoice_mpq)) {
UiErrorOkDialog(_("Some Hellfire MPQs are missing"), _("Not all Hellfire MPQs were found.\nPlease copy all the hf*.mpq files."));
diablo_quit(1);
}
#endif
}
void init_create_window()
{
if (!SpawnWindow(PROJECT_NAME))

39
Source/init.h

@ -21,42 +21,6 @@ namespace devilution {
/** True if the game is the current active window */
extern bool gbActive;
inline bool HaveSpawn()
{
#ifdef UNPACKED_MPQS
return bool(spawn_data_path);
#else
return bool(spawn_mpq);
#endif
}
inline bool HaveDiabdat()
{
#ifdef UNPACKED_MPQS
return bool(diabdat_data_path);
#else
return bool(diabdat_mpq);
#endif
}
inline bool HaveHellfire()
{
#ifdef UNPACKED_MPQS
return bool(hellfire_data_path);
#else
return bool(hellfire_mpq);
#endif
}
inline bool HaveExtraFonts()
{
#ifdef UNPACKED_MPQS
return bool(font_data_path);
#else
return bool(font_mpq);
#endif
}
#ifdef UNPACKED_MPQS
bool AreExtraFontsOutOfDate(const std::string &path);
#else
@ -86,9 +50,6 @@ inline bool IsDevilutionXMpqOutOfDate()
}
void init_cleanup();
void LoadCoreArchives();
void LoadLanguageArchive();
void LoadGameArchives();
void init_create_window();
void MainWndProc(const SDL_Event &event);

3
Source/interfac.cpp

@ -20,10 +20,11 @@
#include "engine/palette.h"
#include "engine/render/clx_render.hpp"
#include "engine/render/primitive_render.hpp"
#include "game_mode.hpp"
#include "headless_mode.hpp"
#include "hwcursor.hpp"
#include "init.h"
#include "loadsave.h"
#include "multi.h"
#include "pfile.h"
#include "plrmsg.h"
#include "utils/log.hpp"

2
Source/items.cpp

@ -27,8 +27,8 @@
#include "engine/render/clx_render.hpp"
#include "engine/render/primitive_render.hpp"
#include "engine/render/text_render.hpp"
#include "game_mode.hpp"
#include "headless_mode.hpp"
#include "init.h"
#include "inv_iterators.hpp"
#include "levels/town.h"
#include "lighting.h"

7
Source/levels/gendung.cpp

@ -15,7 +15,8 @@
#include "engine/load_file.hpp"
#include "engine/random.hpp"
#include "engine/world_tile.hpp"
#include "init.h"
#include "game_mode.hpp"
#include "items.h"
#include "levels/drlg_l1.h"
#include "levels/drlg_l2.h"
#include "levels/drlg_l3.h"
@ -23,7 +24,9 @@
#include "levels/reencode_dun_cels.hpp"
#include "levels/town.h"
#include "lighting.h"
#include "options.h"
#include "monster.h"
#include "objects.h"
#include "utils/algorithm/container.hpp"
#include "utils/bitset2d.hpp"
#include "utils/is_of.hpp"
#include "utils/log.hpp"

1
Source/levels/gendung.h

@ -11,6 +11,7 @@
#include <string>
#include <string_view>
#include <SDL_endian.h>
#include <expected.hpp>
#include "engine/clx_sprite.hpp"

4
Source/levels/town.cpp

@ -4,9 +4,11 @@
#include "engine/load_file.hpp"
#include "engine/random.hpp"
#include "init.h"
#include "engine/world_tile.hpp"
#include "game_mode.hpp"
#include "levels/drlg_l1.h"
#include "levels/trigs.h"
#include "multi.h"
#include "player.h"
#include "quests.h"

2
Source/levels/trigs.cpp

@ -15,7 +15,7 @@
#include "cursor.h"
#include "diablo_msg.hpp"
#include "game_mode.hpp"
#include "init.h"
#include "multi.h"
#include "utils/algorithm/container.hpp"
#include "utils/is_of.hpp"
#include "utils/language.h"

4
Source/lighting.h

@ -7,12 +7,8 @@
#include <array>
#include <cstdint>
#include <optional>
#include <type_traits>
#include <vector>
#include <expected.hpp>
#include <function_ref.hpp>
#include "automap.h"
#include "engine/displacement.hpp"

2
Source/loadsave.cpp

@ -24,7 +24,7 @@
#include "doom.h"
#include "engine/point.hpp"
#include "engine/random.hpp"
#include "init.h"
#include "game_mode.hpp"
#include "inv.h"
#include "levels/dun_tile.hpp"
#include "lighting.h"

2
Source/missiles.cpp

@ -22,8 +22,8 @@
#include "engine/points_in_rectangle_range.hpp"
#include "engine/random.hpp"
#include "engine/render/primitive_render.hpp"
#include "game_mode.hpp"
#include "headless_mode.hpp"
#include "init.h"
#include "inv.h"
#include "levels/dun_tile.hpp"
#include "levels/trigs.h"

3
Source/monster.cpp

@ -15,6 +15,7 @@
#include <string>
#include <string_view>
#include <SDL_endian.h>
#include <expected.hpp>
#include <fmt/core.h>
#include <fmt/format.h>
@ -32,7 +33,6 @@
#include "engine/world_tile.hpp"
#include "game_mode.hpp"
#include "headless_mode.hpp"
#include "init.h"
#include "levels/crypt.h"
#include "levels/drlg_l4.h"
#include "levels/themes.h"
@ -55,7 +55,6 @@
#include "utils/static_vector.hpp"
#include "utils/status_macros.hpp"
#include "utils/str_cat.hpp"
#include "utils/utf8.hpp"
#ifdef _DEBUG
#include "debug.h"

1
Source/monster.h

@ -22,7 +22,6 @@
#include "engine/sound.h"
#include "engine/world_tile.hpp"
#include "game_mode.hpp"
#include "init.h"
#include "levels/dun_tile.hpp"
#include "misdat.h"
#include "monstdat.h"

2
Source/mpq/mpq_writer.cpp

@ -6,11 +6,11 @@
#include <memory>
#include <type_traits>
#include <SDL_endian.h>
#include <libmpq/mpq.h>
#include "appfat.h"
#include "encrypt.h"
#include "utils/endian_read.hpp"
#include "utils/file_util.h"
#include "utils/language.h"
#include "utils/log.hpp"

2
Source/objects.cpp

@ -27,7 +27,6 @@
#include "engine/points_in_rectangle_range.hpp"
#include "engine/random.hpp"
#include "headless_mode.hpp"
#include "init.h"
#include "inv.h"
#include "inv_iterators.hpp"
#include "levels/crypt.h"
@ -49,7 +48,6 @@
#include "utils/language.h"
#include "utils/log.hpp"
#include "utils/str_cat.hpp"
#include "utils/utf8.hpp"
namespace devilution {

1
Source/options.cpp

@ -19,6 +19,7 @@
#include "controls/game_controls.h"
#include "controls/plrctrls.h"
#include "discord/discord.h"
#include "engine/assets.hpp"
#include "engine/demomode.h"
#include "engine/sound_defs.hpp"
#include "game_mode.hpp"

1
Source/pack.cpp

@ -9,7 +9,6 @@
#include "engine/random.hpp"
#include "game_mode.hpp"
#include "init.h"
#include "items/validation.h"
#include "loadsave.h"
#include "playerdat.hpp"

2
Source/panels/spell_book.cpp

@ -15,7 +15,7 @@
#include "engine/rectangle.hpp"
#include "engine/render/clx_render.hpp"
#include "engine/render/text_render.hpp"
#include "init.h"
#include "game_mode.hpp"
#include "missiles.h"
#include "panels/spell_icons.hpp"
#include "panels/ui_panels.hpp"

1
Source/panels/spell_icons.cpp

@ -9,7 +9,6 @@
#include "engine/render/clx_render.hpp"
#include "engine/render/primitive_render.hpp"
#include "game_mode.hpp"
#include "init.h"
namespace devilution {

1
Source/pfile.cpp

@ -17,7 +17,6 @@
#include "engine/load_file.hpp"
#include "engine/render/primitive_render.hpp"
#include "game_mode.hpp"
#include "init.h"
#include "loadsave.h"
#include "menu.h"
#include "mpq/mpq_common.hpp"

5
Source/player.cpp

@ -28,7 +28,6 @@
#include "gamemenu.h"
#include "headless_mode.hpp"
#include "help.h"
#include "init.h"
#include "inv_iterators.hpp"
#include "levels/trigs.h"
#include "lighting.h"
@ -1463,9 +1462,9 @@ void ValidatePlayer()
HeroClass GetPlayerSpriteClass(HeroClass cls)
{
if (cls == HeroClass::Bard && !gbBard)
if (cls == HeroClass::Bard && !HaveBardAssets())
return HeroClass::Rogue;
if (cls == HeroClass::Barbarian && !gbBarbarian)
if (cls == HeroClass::Barbarian && !HaveBarbarianAssets())
return HeroClass::Warrior;
return cls;
}

1
Source/qol/chatlog.cpp

@ -20,7 +20,6 @@
#include "engine/render/text_render.hpp"
#include "gamemenu.h"
#include "help.h"
#include "init.h"
#include "inv.h"
#include "minitext.h"
#include "stores.h"

1
Source/quests.cpp

@ -18,7 +18,6 @@
#include "engine/render/text_render.hpp"
#include "engine/world_tile.hpp"
#include "game_mode.hpp"
#include "init.h"
#include "levels/gendung.h"
#include "levels/town.h"
#include "levels/trigs.h"

4
Source/stores.cpp

@ -14,14 +14,14 @@
#include "controls/plrctrls.h"
#include "cursor.h"
#include "engine/backbuffer_state.hpp"
#include "engine/load_cel.hpp"
#include "engine/random.hpp"
#include "engine/render/clx_render.hpp"
#include "engine/render/primitive_render.hpp"
#include "engine/render/text_render.hpp"
#include "engine/trn.hpp"
#include "init.h"
#include "game_mode.hpp"
#include "minitext.h"
#include "multi.h"
#include "options.h"
#include "panels/info_box.hpp"
#include "qol/stash.h"

1
Source/utils/log.hpp

@ -4,6 +4,7 @@
#include <SDL.h>
#include <fmt/core.h>
#include <fmt/format.h>
#include <fmt/ranges.h>
#include "utils/str_cat.hpp"

22
Source/utils/soundsample.cpp

@ -7,6 +7,8 @@
#include <Aulib/DecoderDrmp3.h>
#include <Aulib/DecoderDrwav.h>
#include <Aulib/Stream.h>
#include <SDL.h>
#ifdef USE_SDL1
#include "utils/sdl2_to_1_2_backports.h"
@ -91,6 +93,26 @@ float VolumeLogToLinear(int logVolume, int logMin, int logMax)
///// SoundSample /////
void SoundSample::SetFinishCallback(Aulib::Stream::Callback &&callback)
{
stream_->setFinishCallback(std::forward<Aulib::Stream::Callback>(callback));
}
void SoundSample::Stop()
{
stream_->stop();
}
void SoundSample::Mute()
{
stream_->mute();
}
void SoundSample::Unmute()
{
stream_->unmute();
}
void SoundSample::Release()
{
stream_ = nullptr;

31
Source/utils/soundsample.h

@ -2,13 +2,19 @@
#include <cstddef>
#include <cstdint>
#include <functional>
#include <memory>
#include <Aulib/Stream.h>
#include <string>
#include "engine/sound_defs.hpp"
#include "utils/stdcompat/shared_ptr_array.hpp"
// Forward-declares Aulib::Stream to avoid adding dependencies
// on SDL_audiolib to every user of this header.
namespace Aulib {
class Stream;
} // namespace Aulib
namespace devilution {
class SoundSample final {
@ -28,10 +34,7 @@ public:
// Returns 0 on success.
int SetChunkStream(std::string filePath, bool isMp3, bool logErrors = true);
void SetFinishCallback(Aulib::Stream::Callback &&callback)
{
stream_->setFinishCallback(std::forward<Aulib::Stream::Callback>(callback));
}
void SetFinishCallback(std::function<void(Aulib::Stream &)> &&callback);
/**
* @brief Sets the sample's WAV, FLAC, or Ogg/Vorbis data.
@ -72,23 +75,13 @@ public:
/**
* @brief Stop playing the sound
*/
void Stop()
{
stream_->stop();
}
void Stop();
void SetVolume(int logVolume, int logMin, int logMax);
void SetStereoPosition(int logPan);
void Mute()
{
stream_->mute();
}
void Unmute()
{
stream_->unmute();
}
void Mute();
void Unmute();
/**
* @return Audio duration in ms

22
test/app_fatal_for_testing.cpp

@ -3,10 +3,32 @@
namespace devilution {
[[noreturn]] void DisplayFatalErrorAndExit(std::string_view title, std::string_view body)
{
std::cerr << "fatal error: " << title << "\n"
<< body << std::endl;
std::abort();
}
[[noreturn]] void app_fatal(std::string_view str)
{
std::cerr << "app_fatal: " << str << std::endl;
std::abort();
}
[[noreturn]] void ErrDlg(const char *title, std::string_view error, std::string_view logFilePath, int logLineNr)
{
std::cerr << "ErrDlg: " << title << "\n"
<< error << "\n"
<< logFilePath << ":" << logLineNr << std::endl;
std::abort();
}
[[noreturn]] void assert_fail(int nLineNo, const char *pszFile, const char *pszFail)
{
std::cerr << "assert_fail: " << pszFile << ":" << nLineNo << "\n"
<< pszFail << std::endl;
std::abort();
}
} // namespace devilution

2
test/dun_render_benchmark.cpp

@ -5,12 +5,12 @@
#include <benchmark/benchmark.h>
#include "diablo.h"
#include "engine/assets.hpp"
#include "engine/clx_sprite.hpp"
#include "engine/displacement.hpp"
#include "engine/load_file.hpp"
#include "engine/render/dun_render.hpp"
#include "engine/surface.hpp"
#include "init.h"
#include "levels/dun_tile.hpp"
#include "levels/gendung.h"
#include "lighting.h"

1
test/inv_test.cpp

@ -1,6 +1,7 @@
#include <gtest/gtest.h>
#include "cursor.h"
#include "engine/assets.hpp"
#include "inv.h"
#include "player.h"
#include "storm/storm_net.hpp"

2
test/language_for_testing.cpp

@ -1,5 +1,6 @@
#include <string>
#include <string_view>
#include <vector>
std::string_view GetLanguageCode() { return "en"; }
bool HasTranslation(const std::string &locale) { return true; }
@ -7,3 +8,4 @@ void LanguageInitialize() { }
std::string_view LanguageTranslate(const char *key) { return key; }
std::string_view LanguagePluralTranslate(const char *singular, std::string_view plural, int count) { return count == 1 ? singular : plural; }
std::string_view LanguageParticularTranslate(std::string_view context, std::string_view message) { return message; }
std::vector<std::string> GetLocales() { return {}; }

1
test/pack_test.cpp

@ -3,6 +3,7 @@
#include <gtest/gtest.h>
#include "cursor.h"
#include "engine/assets.hpp"
#include "game_mode.hpp"
#include "monstdat.h"
#include "pack.h"

3
test/timedemo_test.cpp

@ -2,10 +2,11 @@
#include <gtest/gtest.h>
#include <iostream>
#include "diablo.h"
#include "engine/assets.hpp"
#include "engine/demomode.h"
#include "game_mode.hpp"
#include "headless_mode.hpp"
#include "init.h"
#include "lua/lua.hpp"
#include "monstdat.h"
#include "options.h"

Loading…
Cancel
Save