Not All is Fine in the Morrowind Universe
OpenMW is evaluated for code issues by PVS-Studio
I have checked the OpenMW project by PVS-Studio and written this tiny article. Very few bugs were found, so OpenMW team can be proud of their code.
OpenMW
OpenMW is an attempt to reconstruct the popular RPG Morrowind, a full-blown implementation of all of the game's specifics with open source code. To run OpenMW, you will need an original Morrowind disk. The source code can be downloaded from https://code.google.com/p/openmw/Suspicious fragments found
Fragment No. 1std::string getUtf8(unsigned char c, ToUTF8::Utf8Encoder& encoder, ToUTF8::FromType encoding) { .... conv[0xa2] = 0xf3; conv[0xa3] = 0xbf; conv[0xa4] = 0x0; conv[0xe1] = 0x8c; conv[0xe1] = 0x8c; <<<<==== conv[0xe3] = 0x0; .... } PVS-Studio diagnostic message: V519 The 'conv[0xe1]' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 103, 104. openmw fontloader.cpp 104 I guess it is a typo. It is the 0xe2 index that should be probably used in the marked line. Fragment No. 2 enum Flags { .... NoDuration = 0x4, .... } bool CastSpell::cast (const ESM::Ingredient* ingredient) { .... if (!magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration) .... } PVS-Studio diagnostic message: V564 The '&' operator is applied to bool type value. You've probably forgotten to include parentheses or intended to use the '&&' operator. openmw spellcasting.cpp 717 Here we deal with a mistake related to operation precedence. At first, the (!magicEffect->mData.mFlags) statement is executed which evaluates either to 0 or 1. Then the statement 0 & 4 or 1 & 4 is executed. But it doesn't make any sense, and the code should most likely look as follows: if ( ! (magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration) ) Fragment No. 3 void Clothing::blank() { mData.mType = 0; mData.mWeight = 0; mData.mValue = 0; mData.mEnchant = 0; mParts.mParts.clear(); mName.clear(); mModel.clear(); mIcon.clear(); mIcon.clear(); mEnchant.clear(); mScript.clear(); } PVS-Studio diagnostic message: V586 The 'clear' function is called twice for deallocation of the same resource. Check lines: 48, 49. components loadclot.cpp 49 The mIcon object is cleared twice. The second clearing is redundant or something else should have been cleared instead. Fragment No. 4 void Storage::loadDataFromStream( ContainerType& container, std::istream& stream) { std::string line; while (!stream.eof()) { std::getline( stream, line ); .... } .... } PVS-Studio diagnostic message: V663 Infinite loop is possible. The 'cin.eof()' condition is insufficient to break from the loop. Consider adding the 'cin.fail()' function call to the conditional expression. components translation.cpp 45 When working with the std::istream class, calling the eof() function to terminate the loop is not enough. If a failure occurs when reading data, the call of the eof() function will return false all the time. To terminate the loop in this case, you need an additional check of the value returned by fail(). Fragment No. 5 class Factory { .... bool getReadSourceCache() { return mReadSourceCache; } bool getWriteSourceCache() { return mReadSourceCache; } .... bool mReadSourceCache; bool mWriteSourceCache; .... }; PVS-Studio diagnostic message: V524 It is odd that the body of 'getWriteSourceCache' function is fully equivalent to the body of 'getReadSourceCache' function. components factory.hpp 209 I guess the getWriteSourceCache() function should look like this: bool getWriteSourceCache() { return mWriteSourceCache; } Fragments No. 6, 7, 8 std::string rangeTypeLabel(int idx) { const char* rangeTypeLabels [] = { "Self", "Touch", "Target" }; if (idx >= 0 && idx <= 3) return rangeTypeLabels[idx]; else return "Invalid"; } PVS-Studio diagnostic message: V557 Array overrun is possible. The value of 'idx' index could reach 3. esmtool labels.cpp 502 Here we see an incorrect check of an array index. If the idx variable equals 3, an array overrun will occur. The correct code: if (idx >= 0 && idx < 3) A similar defect was found in two other fragments:- V557 Array overrun is possible. The value of 'idx' index could reach 143. esmtool labels.cpp 391
- V557 Array overrun is possible. The value of 'idx' index could reach 27. esmtool labels.cpp 475
enum UpperBodyCharacterState { UpperCharState_Nothing, UpperCharState_EquipingWeap, UpperCharState_UnEquipingWeap, .... }; bool CharacterController::updateWeaponState() { .... if((weaptype != WeapType_None || UpperCharState_UnEquipingWeap) && animPlaying) .... } PVS-Studio diagnostic message: V560 A part of conditional expression is always true: UpperCharState_UnEquipingWeap. openmw character.cpp 949 This condition is very strange. In its current form, it can be reduced to if (animPlaying). Something is obviously wrong with it. Fragments No. 10, 11 void World::clear() { mLocalScripts.clear(); mPlayer->clear(); .... if (mPlayer) .... } PVS-Studio diagnostic message: V595 The 'mPlayer' pointer was utilized before it was verified against nullptr. Check lines: 234, 245. openmw worldimp.cpp 234 Similar defect: V595 The mBody pointer was utilized before it was verified against nullptr. Check lines: 95, 99. openmw physic.cpp 95 Fragment No. 12 void ExprParser::replaceBinaryOperands() { .... if (t1==t2) mOperands.push_back (t1); else if (t1=='f' || t2=='f') mOperands.push_back ('f'); else std::logic_error ("failed to determine result operand type"); } PVS-Studio diagnostic message: V596 The object was created but it is not being used. The 'throw' keyword could be missing: throw logic_error(FOO); components exprparser.cpp 101 The keyword throw is missing. The fixed code should look like this: throw std::logic_error ("failed to determine result operand type"); Conclusion
Purchase a PVS-Studio for in your team, and you will save huge amount of time usually spent on eliminating typos and diverse bugs.Related Tutorials
Balancing Game Development and Creative Direction in Indie Production
A practical look at how indie developers can balance creative direction with hands-on game development. This article co…
My Unreal Engine Development Process: From Core Idea to Playable Build
A practical overview of my Unreal Engine development process, covering how I move from a core game idea to a playable b…
Introducing LaneGraph: The Ultimate Road Network Solution for Unity
Discover the power of LaneGraph, a lightweight and flexible lane-based navigation system for Unity. LaneGraph makes it…
Retargeting Mixamo Characters with Root Motion In Unreal Engine 5.4.
I have always found Retargeting Mixamo Characters To have Root Motion is a serious lengthy Tast, Recently I stumbled up…
How To Make A SIMPLE Main Menu In Unity
In this tutorial for unity, i go over how to make a simple main menu for unity, it's an unlisted video because i do not…
Guide to Gameplay Balance
A perspective on competitive gameplay balance, from a background of "shooter" sandbox design.
Discussion
More from Andrey Karpov
Checking the Unity C# Source Code
Recently a long-awaited event has happen - Unity Technologies uploaded the C# source code of the game engine, available…
Checking the World of Warcraft CMaNGOS open source server
In this article, I would like to share the results of our analysis of the open source implementation of the World of Wa…
Critical errors in CryEngine V code
In May 2016, German game-development company Crytek made the, decision to upload the source code of their game engine, …
Discussion