Help: C++ interop of BIG C++ code base (HoMM3) with lots of dependencies

I'm a huge Heroes of Might and Magic III (HoMM3) fan and my dream is to rewrite the game from scratch in Swift. I started doing just this 2 years ago, resulting in pure Swift backend package Makt and frontend package Tritium. To say that this is an ambitious idea to rewrite the game by myself is the understatement of the week. Even though I've managed to parse H3M map files and render them, I've only progressed like 0.03% (pure guestimate, might be less).

Thus I was very happy to hear about the C++ interop, because there already exists an open source code base for HoMM3, called vcmi, which is written in C++. I then had the idea that instead of writing HoMM3 from scratch in Swift, I might incrementally port vcmi to Swift! For now I only care about developing vcmi on macOS and only running vcmi on macOS. And I only care about the "vcmiclient" (not server, nor "launcher", for now.)

The challenge is that vcmi has sooooo many dependencies and an incredibly difficult build process. Here is the build instructions for macOS, to sum up the dependencies/build requirements:

Two git submodules ("fuzzylite" and "googletest")

Other non git submodule dependencies:
boost, minizip, sdl2, sdl2_image, sdl2_mixer, sdl2_ttf tbb ffmpeg qt

The dependencies can be installed either using brew or conan. Setting up conan seemed... very hard. So I went with brew.

Finally I can prepare the project for building

cmake -S . -B BUILD_DIR -G Xcode -D ENABLE_LAUNCHER=OFF -D "CMAKE_PREFIX_PATH=$(brew --prefix ffmpeg@4);$(brew --prefix qt@5)" 

This creates a folder BUILD_DIR with this contents:

drwxr-xr-x@ 11 sajjon  staff    352 Jul  1 19:31 AI
-rw-r--r--@  1 sajjon  staff  37741 Jul  1 10:32 CMakeCache.txt
drwxr-xr-x@ 11 sajjon  staff    352 Jul  1 19:31 CMakeFiles
drwxr-xr-x@  3 sajjon  staff     96 Jul  1 10:32 CMakeScripts
-rw-r--r--@  1 sajjon  staff   3801 Jul  1 10:32 CPackConfig.cmake
-rw-r--r--@  1 sajjon  staff   4254 Jul  1 10:32 CPackSourceConfig.cmake
-rw-r--r--@  1 sajjon  staff    942 Jul  1 10:32 Info.plist
drwxr-xr-x@  5 sajjon  staff    160 Jul  1 10:42 VCMI.xcodeproj
-rw-r--r--@  1 sajjon  staff    396 Jul  1 10:38 Version.cpp
drwxr-xr-x@  4 sajjon  staff    128 Jul  1 19:32 bin
drwxr-xr-x@ 17 sajjon  staff    544 Jul  1 19:31 build
drwxr-xr-x@  4 sajjon  staff    128 Jul  1 10:32 client
-rw-r--r--@  1 sajjon  staff   4020 Jul  1 10:32 cmake_install.cmake
-rw-r--r--@  1 sajjon  staff    231 Jul  1 10:32 fuzzylite.pc
drwxr-xr-x@  4 sajjon  staff    128 Jul  1 10:32 lib
drwxr-xr-x@ 19 sajjon  staff    608 Jul  1 19:31 mapeditor
drwxr-xr-x@  4 sajjon  staff    128 Jul  1 10:32 osx
drwxr-xr-x@  4 sajjon  staff    128 Jul  1 10:32 server

I open the VCMI.xcodeproj and it has 16 targets, of which I think I mostly (only?) care about vcmiclient:

Inside the file vcmiclient/CMT.cpp there is a "main" function, defined as:

#if defined(VCMI_WINDOWS) && !defined(__GNUC__) && defined(VCMI_WITH_DEBUG_CONSOLE)
int wmain(int argc, wchar_t* argv[])
#elif defined(VCMI_MOBILE)
int SDL_main(int argc, char *argv[])
#else
int main(int argc, char * argv[])
#endif
{

Which seem to be the entry point of the vcmiclient target.

My questions:

  1. Is it possible for me to port vcmi to Swift using C++ interop?
  2. Is it feasible? I'm really bad at C++... and I'm bad at cmake and cmakelists and I have no clue what conan is...
  3. Where would I even start..? I guess the problem is that I first have to create BUILD_DIR to even get the VCMI.xcodeproj, it feels like that cmake step which created VCMI.xcodeproj has to... be replaced by a SPM package, with a cxxVCMI target?? But does SPM support conan? or otherwise support dependencies? Or is it OK to require to first install dependencies via brew? The Mixing Swift and C++ guide did not bring up C++ code having dependencies I think.

Any help whatsoever would be greatly appreciated!

8 Likes

I don’t have any advice on interop but I just wanted to say this is a neat project and I hope it works out :slight_smile:

8 Likes

Continuing to think out loud, maybe clueless, inaccurate rambling...

I guess I would not really need the git submodule dependencies (try to comment out the code referencing them), so maybe that complexity can be scaled away.

I think (and might be entirely wrong) that I would like to start with changing the whole process of cmake to get BUILD_DIR which contains VCMI.xcodeproj, into rather:
in project root have an SPM package which contains the source code. Not sure if it would be one single target? or just the a vcmiclient target.. (files in vcmiclient folder "includes" code in lib folder... so maybe I need lib target as well etc.)?

Anyway I think the first main goal is to get rid of having to run the command:

cmake -S . -B BUILD_DIR -G Xcode -D ENABLE_LAUNCHER=OFF -D "CMAKE_PREFIX_PATH=$(brew --prefix ffmpeg@4);$(brew --prefix qt@5)"

And rather have SPM do that part of building the SPM target (either vcmiclient or the whole thing vcmi)??? But this step, is very hard for me to untangle. I think that would entail translating the CMakeLists.txt which is 800 lines into SPM config??

1 Like

+1 to this. I knew there were other reimplementations of the HoMM3 engine, but now I definitely root for this one (incrementally re-)written in Swift :clap:

3 Likes

Another possibly bad idea which might not work: since I dont really care about the project being a great an well structured project on C++ level (since the end goal is to have 0% C++), perhaps I can "solve" the difficulties with C++ dependencies (e.g. "boost" ) by manually copying in the source of these dependencies? :thinking: That might have worked if these dependencies in turn did not have their own (sub)dependencies, but maybe they do, and then maybe it would be infeasible... But you get my idea... I don't care at all about "degrading" the C++ code setup if it means I can get the code base to work with C++ and Swift interop, and doing everything to reduce project complexity with dependencies seems to be a good idea? Of course this would be that my fork of vcmi would be a "snapshot" frozen in time and I would no longer in a feasible manner be able to git pull upstream main, that is not an issue for me. Just being able to run it in whatever buggy form it has now, using e.g. Swift as the entrypoint of the game would be a huge win and a great start for me!

Yep, it might be painful for big projects but it could work. Basically you undo all the "packaging" first and make the app fully operable in the form of sources only with no dependences (having tests and verifying individual steps is a good idea). The next step is to convert to swift one file/function/etc at a time. Still lots of work for big projects.

Something else to think about, what do you do with #if ANDROID / Windows, etc branches. If you don't need those at all you can significantly minimise the scope by removing those, OTOH, if you'd need them later on it would be more work later on to reintroduce them.

For the intermediate phase when you still have some C++ code along with Swift: how well C++ interops with Swift is another big area of attention. I anticipate potential problems with boost/templates/exceptions/coroutines/auto-ptr-flavours/multiple inheritance and other C++ shenanigans.

Good luck with your journey!