Hi all, this week I worked on replacing STL(Standard Library) with the alternatives provided by ScummVM. I will be talking about the same today.
Why do we need to replace STL
The answer to this is that ScummVM needs to support many platforms, and every platform may not provide a modern enough compiler for the codebase to compile. For example, an engine may require features introduced in c++17, but some platforms’ compilers might only support c++11 at max. Therefore, to maintain compatibility ScummVM provides its own replacements for STL.
How I replaced STL in CRAB
During the writing of my proposal, I had already gone through the codebase of CRAB and made a note of what STL containers CRAB used and the replacements that existed for them in ScummVM.
Alternatives for almost all the STL containers/algorithms needed for CRAB were already available, thus, the process was basically effortless. The only thing which did not exist already in ScummVM and that I added was Common::remove()
.
With commit #03b75da the engine became fully independent of any STL headers which were not permitted by ScummVM(Remember, we had previously used FORBIDDEN_SYMBOL_ALLOW_ALL
to get past this requirement.)
Bonus: A troublesome bug
After I had made a significant number of commits replacing STL with their alternatives, I switched my OS back to Windows and tried compiling with mingw64 and guess what, the engine would fail on an assert statement. Now, this is where things got interesting. When the engine was compiled using any other compiler(gcc, msvc, clang++), the assertion never failed and it continued as normal. 🙂
Mingw64 did not support ubsan as of writing and the trace produced by gdb looked.. valid and okay. This is where I took the good old debugging-via-printf
route and generated a log file which I compared to the logs produced by the original game and Viola! I noticed a difference.
The GameEvent class has an enum member state which decides what animation frame for a particular character is drawn. In the logs produced by the original game, the value for state
was suspiciously large for the first few frames whereas the other log showed that the value was pretty small always (1 – 3). The question that immediately arose in my mind was why would the value be so large in the original game as this was an index number and it is supposed to be small. I added some more printfs and soon I discovered the core issue – state variable was never initialized as the parametrized constructor did not delegate to the default constructor and thus it contained garbage values. 😉
And with this single-line fix, the engine started to work again when compiled using mingw64!
My primary purpose in mentioning this bug was to make future Engine-Porters aware that:
- Do not assume that the original engine does not contain any bugs.
- Small irrelevant changes can have a larger cascade effect and make engines crash at seemingly unintuitive places.
and probably the most important:
- Always test your changes with different compilers.
That is all for this blog. Thanks for reading!