Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,10 @@ IF (MINGW)
SET(OD_SOURCEFILES ${OD_SOURCEFILES} ${SRC}/utils/StackTraceWinMinGW.cpp)
ELSEIF(MSVC)
SET(OD_SOURCEFILES ${OD_SOURCEFILES} ${SRC}/utils/StackTraceWinMSVC.cpp)
ELSEIF(APPLE)
# StackTraceUnix relies on struct sigcontext and the deprecated ucontext
# routines, neither of which exists on macOS.
SET(OD_SOURCEFILES ${OD_SOURCEFILES} ${SRC}/utils/StackTraceStub.cpp)
ELSEIF(UNIX)
SET(OD_SOURCEFILES ${OD_SOURCEFILES} ${SRC}/utils/StackTraceUnix.cpp)
ELSE()
Expand Down
6 changes: 5 additions & 1 deletion source/ODApplication.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,11 @@ void ODApplication::startClient()
HWND hwnd;
renderWindow->getCustomAttribute("WINDOW", static_cast<void*>(&hwnd));
HINSTANCE hInst = static_cast<HINSTANCE>(GetModuleHandle(nullptr));
SetClassLong(hwnd, GCL_HICON, reinterpret_cast<LONG>(LoadIcon(hInst, MAKEINTRESOURCE(IDI_ICON1))));
// SetClassLong takes a 32 bit value, so casting the icon handle to one only works
// while a handle is 32 bits wide: a 64 bit build does not compile. The Ptr form is
// the same call on a 32 bit build and the right one on a 64 bit build.
SetClassLongPtr(hwnd, GCLP_HICON,
reinterpret_cast<LONG_PTR>(LoadIcon(hInst, MAKEINTRESOURCE(IDI_ICON1))));
#endif

//Initialise RTshader system
Expand Down
39 changes: 38 additions & 1 deletion source/modes/AdvertMode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,43 @@
#include <cstdlib>
#include <CEGUI/CEGUI.h>

#include <OgrePrerequisites.h>

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
// MinGW's libstdc++ predefines NOMINMAX, and the game builds with -Werror:
// an unconditional redefinition is fatal there.
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <windows.h>
#include <shellapi.h>
#endif

namespace
{
//! \brief Hands a link to whatever the system uses to open one. Each platform has its
//! own way: xdg-open is the freedesktop one and does not exist on the other two.
void openInBrowser(const std::string& url)
{
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
ShellExecuteA(nullptr, "open", url.c_str(), nullptr, nullptr, SW_SHOWNORMAL);
#else
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
const std::string command = "open '" + url + "'";
#else
const std::string command = "xdg-open '" + url + "'";
#endif
// The result is worth looking at only to say so: there is nothing to fall back on,
// and the game is on its way out by the time this runs.
if(std::system(command.c_str()) != 0)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I am not too smart, but this branch will not compile on WINDOWS OS, because std::string command would end up being not declared anywhere .......

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude reported that compiling on Windows would require more extensive code changes. I told him to stop. Do you want to go on?

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm I have my local code augmentation for my Windows installment in here, otherwise I wouldn't ship my binary version of ODP for Windows. Just for the sake of sanity , move the declaration of std::string command above the preprocessor '#if' ... I will cope with rest. BTW: are you aware of this little site : https://opendungeons.org/ ? ( with binary for W included ) .

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, I wasn't aware of that site. I've asked Claude to fix building for Windows and test building for Mac. After he finishes, I will push the changes here.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude has now verified this directly: it compile-checked every translation unit of the game for Windows with MinGW-w64 (GCC 16, both x86_64 and i686) against real Windows headers plus Ogre 13.6.5 / CEGUI / SFML 2.6 headers. AdvertMode.cpp compiles cleanly as it is: command is only declared and used inside the non-Windows #else branch — on Windows the call goes through ShellExecuteA instead, so nothing ends up undeclared. If you would still prefer std::string command hoisted above the #if for readability, happy to move it.

Two commits are now pushed to this branch:

  • f876104 — the two remaining fixes needed to compile on macOS (verified: the game now compiles and links on a Mac, against Ogre 13.6.5 / CEGUI / SFML 2.6.2 / OIS 1.5.1 built from the versions pinned in snap/snapcraft.yaml);
  • 9ccaa13 — one real Windows issue the sweep did find: #define NOMINMAX collides with MinGW's libstdc++, which predefines it, and that warning is fatal under the project's default -Werror. Both defines are now guarded with #ifndef.

Caveats: this was per-file compilation with MinGW, not a full Windows link, and the two StackTraceWin* files could not be checked here (they need bfd.h / MSVC).

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

YEah I usually build my W binary bundle with MSVC ...

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have Windows. Cannot test it.

OD_LOG_WRN("Couldn't open " + url);
#endif
}
}


AdvertMode::AdvertMode(ModeManager* modeManager):
AbstractApplicationMode(modeManager, ModeManager::ADVERTISMENT)
Expand Down Expand Up @@ -85,7 +122,7 @@ void AdvertMode::activate()
bool AdvertMode::showWWW()
{
ODFrameListener::getSingletonPtr()->requestExit();
system("xdg-open 'https://discord.gg/K2JPXuchZV'");
openInBrowser("https://discord.gg/K2JPXuchZV");
return true;

}
Expand Down
62 changes: 31 additions & 31 deletions source/modes/EditorMode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,15 @@


#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
#include <fileapi.h>
// MinGW's libstdc++ predefines NOMINMAX, and the game builds with -Werror:
// an unconditional redefinition is fatal there.
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <windows.h>
#endif

#include <OgreEntity.h>
Expand Down Expand Up @@ -470,16 +478,21 @@ void EditorMode::activate()
// Hide also the Replay check-box as it doesn't make sense for the editor
guiSheet->getChild("ConfirmExit/SaveReplayCheckbox")->hide();
guiSheet->getChild("GameChatWindow/GameChatEditBox")->hide();
// Start the file dialogs in the folder the player's own levels are saved to. That used
// to be $HOME, which is only set on Windows if someone has set it: there the dialogs
// opened on nothing at all. ResourceManager resolves this one per platform, and it is
// where a level being loaded or saved from the editor belongs anyway.
const std::string userLevelPath = ResourceManager::getSingleton().getUserLevelPathSkirmish();
guiSheet->getChild("MenuEditorLoad")->hide();
guiSheet->getChild("MenuEditorLoad")->getChild("LevelWindowFrame")
->getChild("FilePath")->setText(getEnv("HOME"));
->getChild("FilePath")->setText(userLevelPath);
guiSheet->getChild("MenuEditorLoad")->getChild("LevelWindowFrame")
->getChild("FilePath")->fireEvent(CEGUI::Editbox::EventTextAccepted,args);
->getChild("FilePath")->fireEvent(CEGUI::Editbox::EventTextAccepted,args);
guiSheet->getChild("MenuEditorSave")->hide();
guiSheet->getChild("MenuEditorSave")->getChild("LevelWindowFrame")
->getChild("FilePath")->setText(getEnv("HOME"));
->getChild("FilePath")->setText(userLevelPath);
guiSheet->getChild("MenuEditorSave")->getChild("LevelWindowFrame")
->getChild("FilePath")->fireEvent(CEGUI::Editbox::EventTextAccepted,args);
->getChild("FilePath")->fireEvent(CEGUI::Editbox::EventTextAccepted,args);
CEGUI::Combobox* levelTypeCb = static_cast<CEGUI::Combobox*>
(mRootWindow->getChild("LevelWindowFrame/LevelTypeSelect"));
levelTypeCb->setItemSelectState(static_cast<size_t>(0), true);
Expand Down Expand Up @@ -1565,7 +1578,7 @@ bool EditorMode::loadMenuFilePathTextChanged( const CEGUI::EventArgs& /*arg*/)
int nn = 1;
for (directory_entry& xx : directory_iterator(pp))
{
if(!(isFileHidden(xx.path().filename().generic_string())
if(!(isFileHidden(xx.path())
&& !isCheckboxSelected("MenuEditorLoad/LevelWindowFrame/HiddenFiles")))
{
if(xx.path().has_extension() && xx.path().extension().compare(L".level") == 0)
Expand Down Expand Up @@ -1719,7 +1732,7 @@ bool EditorMode::saveMenuFilePathTextChanged(const CEGUI::EventArgs& /*arg*/)
int nn = 1;
for (directory_entry& xx : directory_iterator(pp))
{
if(!(isFileHidden(xx.path().filename().generic_string()) && !isCheckboxSelected("MenuEditorSave/LevelWindowFrame/HiddenFiles")))
if(!(isFileHidden(xx.path()) && !isCheckboxSelected("MenuEditorSave/LevelWindowFrame/HiddenFiles")))
{
if(xx.path().has_extension() && xx.path().extension().compare(std::string(".level")) == 0)
{
Expand Down Expand Up @@ -2026,23 +2039,6 @@ bool EditorMode::updateDescription(const CEGUI::EventArgs&)
return true;
}

std::string EditorMode::getEnv( const std::string & var )
{
// WINDOWS:
// "you could look at HOMEDRIVE, HOMEPATH or USERPROFILE
// env variables on windows. or i guess SHGetFolderPathA()"
const char * val = std::getenv( var.c_str() );
if ( val == nullptr )
{ // invalid to assign nullptr to std::string
return "";
}
else
{
return val;
}
}


void EditorMode::uninstallRecentlyUsedFilesButtons()
{
CEGUI::Window *pm = mRootWindow->getChild("Menubar")->getChild("File")->getChild("PopupMenu1")->getChild("RecentlyUsed")->getChild("PopupMenu2");
Expand Down Expand Up @@ -2122,16 +2118,20 @@ bool EditorMode::isCheckboxSelected(const CEGUI::String& checkbox)
}


bool EditorMode::isFileHidden(std::string path)
bool EditorMode::isFileHidden(const boost::filesystem::path& path)
{
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32

//DWORD attributes = GetFileAttributes(path);
//return (attributes & FILE_ATTRIBUTE_HIDDEN);
return false;
// Windows keeps it as an attribute of the file rather than in its name, so the whole
// path is needed to ask for it. A file we cannot read the attributes of is not hidden.
const DWORD attributes = GetFileAttributesA(path.string().c_str());
if(attributes == INVALID_FILE_ATTRIBUTES)
return false;

return (attributes & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)) != 0;
#else
return (path[0] == '.');
#endif
const std::string filename = path.filename().string();
return !filename.empty() && (filename[0] == '.');
#endif
}


Expand Down
6 changes: 4 additions & 2 deletions source/modes/EditorMode.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,6 @@ friend class ODClient;

void displayText(const Ogre::ColourValue& txtColour, const std::string& txt) override;
bool updateDescription(const CEGUI::EventArgs& e = {});
std::string getEnv( const std::string & var );
bool isCheckboxSelected(const CEGUI::String& checkbox);
private:

Expand Down Expand Up @@ -187,7 +186,10 @@ friend class ODClient;

bool loadLevelFromFile(const std::string&);
//! \brief file path to currently choosen file via load / save menu
bool isFileHidden(std::string path);
//! \brief Whether the file should be kept out of the level lists unless the player
//! asked for hidden ones. Takes the whole path: on Windows being hidden is an attribute
//! of the file, not a dot in front of its name.
bool isFileHidden(const boost::filesystem::path& path);
void addPathNameToList(boost::filesystem::directory_entry& xx, CEGUI::Listbox* levelSelectList, CEGUI::Colour cc, int& nn );
std::string dialogFullPath;

Expand Down
12 changes: 11 additions & 1 deletion source/utils/Helper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,16 @@ namespace Helper
while (baseLevelFile.good())
{
std::getline(baseLevelFile, nextParam);

// A file written on Windows ends its lines with a carriage return before the
// line feed, and only Windows strips it back off when reading. Everywhere else
// it stays at the end of the line, where it makes the last value on it, or a
// section marker standing alone, something nothing here recognises: a level
// saved on Windows would not load anywhere at all. Take it off ourselves so
// that it makes no difference where a file was written.
if(!nextParam.empty() && (*nextParam.rbegin() == '\r'))
nextParam.erase(nextParam.size() - 1);

/* Find the first occurrence of the comment symbol on the
* line and return everything before that character.
*/
Expand Down Expand Up @@ -284,7 +294,7 @@ namespace Helper
{
return TTostring(d);
}
#if defined(__OpenBSD__) && defined(__LP64__)
#if defined(__APPLE__) || (defined(__OpenBSD__) && defined(__LP64__))
std::string toString(size_t d)
{
return TTostring(d);
Expand Down
5 changes: 4 additions & 1 deletion source/utils/Helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,10 @@ namespace Helper
std::string toString(uint32_t d);
std::string toString(int64_t d);
std::string toString(uint64_t d);
#if defined(__OpenBSD__) && defined(__LP64__)
// On LP64 systems whose size_t is unsigned long while uint64_t is
// unsigned long long (macOS, OpenBSD), size_t matches none of the fixed
// width overloads and calls become ambiguous.
#if defined(__APPLE__) || (defined(__OpenBSD__) && defined(__LP64__))
std::string toString(size_t d);
#endif
std::string toString(const Ogre::Vector2& v);
Expand Down
35 changes: 27 additions & 8 deletions source/utils/ResourceManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,20 @@

#include <boost/program_options.hpp>

namespace
{
//! \brief Whether the character ends a folder name. Windows takes both, and the paths
//! here are a mix: some are built with '/', some come from the system or the player.
bool isDirectorySeparator(char c)
{
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
return (c == '/') || (c == '\\');
#else
return c == '/';
#endif
}
}

template<> ResourceManager* Ogre::Singleton<ResourceManager>::msSingleton = nullptr;
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32 && defined(OD_DEBUG)
//On windows, if the application is compiled in debug mode, use the plugins with debug prefix.
Expand Down Expand Up @@ -98,6 +112,7 @@ ResourceManager::ResourceManager(boost::program_options::variables_map& options)

void ResourceManager::setupDataPath(boost::program_options::variables_map& options)
{
std::string path;
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
//TODO - Test osx support
char applePath[1024];
Expand All @@ -115,12 +130,12 @@ void ResourceManager::setupDataPath(boost::program_options::variables_map& optio
CFRelease(mainBundleURL);
CFRelease(cfStringRef);

mMacBundlePath = std::string(applePath + "/");
// Not applePath + "/": that is a pointer plus a pointer, which does not compile.
mMacBundlePath = std::string(applePath) + "/";

mGameDataPath = mMacBundlePath + "Contents/Resources/";
#else // Windows and linux

std::string path;
#ifdef OD_DATA_PATH
path = std::string(OD_DATA_PATH);
#else
Expand All @@ -130,7 +145,7 @@ void ResourceManager::setupDataPath(boost::program_options::variables_map& optio
if(!path.empty())
{
mGameDataPath = path;
if (*mGameDataPath.rbegin() != '/')
if (!isDirectorySeparator(*mGameDataPath.rbegin()))
{
mGameDataPath.append("/");
}
Expand All @@ -139,7 +154,12 @@ void ResourceManager::setupDataPath(boost::program_options::variables_map& optio
mGameDataPath = Ogre::FileSystemLayer::resolveBundlePath(mGameDataPath);
#endif
}
#endif // Windows and Linux

// From here on the logic is the same on every platform: data or a plugins.cfg
// in the current folder win over the installed ones. This is also what lets a
// macOS build run at all outside an .app bundle: the Apple branch above knows
// only the bundle layout, and nothing used to set the plugins path there.
// Test whether there is data in "./" and remove the system path in that case.
// Useful for developers.
std::string resourceCfg = "./" + RESOURCECFG;
Expand Down Expand Up @@ -179,8 +199,6 @@ void ResourceManager::setupDataPath(boost::program_options::variables_map& optio
mPluginsPath = pluginsCfg;
}

#endif // Windows and Linux

OD_LOG_INF( PLUGINSCFG + " path is: " + mPluginsPath + '\n');

mScriptPath = mGameDataPath + SCRIPTSUBPATH;
Expand All @@ -202,8 +220,7 @@ void ResourceManager::setupUserDataFolders(boost::program_options::variables_map
mUserDataPath = itOption->second.as<std::string>();
if(!mUserDataPath.empty())
{
uint32_t len = mUserDataPath.length();
if((mUserDataPath.at(len - 1) != '/') && (mUserDataPath.at(len - 1) != '\\'))
if(!isDirectorySeparator(*mUserDataPath.rbegin()))
mUserDataPath += '/';

mUserConfigPath = mUserDataPath + "cfg/";
Expand Down Expand Up @@ -495,7 +512,9 @@ void ResourceManager::setupOgreResources(uint16_t shaderLanguageVersion)
const Ogre::String& typeName = setting.first;
Ogre::String archName = setting.second;

if(!archName.empty() && archName.front() != '/') // do not modify absolute paths
// Do not modify absolute paths. A leading '/' is not what makes one on Windows,
// where they start with a drive letter, so let boost decide.
if(!archName.empty() && !boost::filesystem::path(archName).is_absolute())
archName = mGameDataPath + archName;
else
archName = Ogre::FileSystemLayer::resolveBundlePath(archName);
Expand Down