Wednesday, 14 January 2015

Building TRViewer in VS2013 Community Edition

TRViewer is a program used in the Tomb Raider Level Editor (TRLE) community.

TRViewer screenshot

To compile and build TRViewer Microsoft Visual Studio must be used because TRViewer is a Multi Document Interface (MDI) program that uses the Microsoft Foundation Classes (MFC).

MFC is only included with the professional versions of Visual Studio and to use MFC with the free Express versions you had to complete a complicated process outlined at the CodeProject site. [link]

Microsoft has released a free version of Visual Studio that includes MFC called Visual Studio 2013 Community Edition so we will see if it can be used to build TRViewer.

TRViewer was originally built in Visual C++ 6.0 and Visual Studio.Net 2003 and if you have a copy of those programs you should use them.

Because there have been lots of changes in the C++ language and Visual Studio since 2003 we will get lots of errors in Visual Studio 2013 building TRViewer and there is no guarantee we will be successful.

I only know the basics of C/C++ and am a novice with Visual Studio and MFC but let’s give it a try.

Download and install Visual Studio 2013 Community version with update 4.  [link]

You can download the DVD ISO Image (6GB) to install offline or download the web installer and install while connected to the internet.

Download-Visual-Studio-2013-Communit

While on the same web page scroll down to Additional Software and download MultiByte MFC Library for Visual Studio 2013.

Download-Multibyte-MFC-Library-for-V[2]

Install the Multibyte MFC Library after installing Visual Studio.

Without the Multibyte MFC Library TRViewer will need to be converted to an application that uses Unicode strings and this would be a painstaking job even if you know how.

You will have to register Visual Studio 2013 Community Edition to keep using it after 30 days.
You register it by signing in with your Microsoft account while connected to the internet.

Register-Visual-Studio_thumb2

Install the NuGet Package Manager extension [link] as outlined in this post [link].

Search-for-a-package-at-nuget.org_th

Download the TRViewer source from E. V. Popov’s (TRViewer author) site and extract the zip. [link]

There are several versions available but I will download the newest, TRViewer V1.083 sources.

TRViewer source code downloads at evpopov.com


Open the solution "\TRViewerSrc_1083\TRViewerSrc\TRViewer\TRViewer.sln" in Visual Studio.

Because TRViewer was created in an earlier version of Visual Studio it will need to be upgraded for Visual Studio 2013. Click “OK” to the perform the upgrade.

Upgrade TRViewer project and solution

A migration report will appear in your browser, hopefully with zero errors, and the TRViewer project will be displayed in Visual Studio’s Solution Explorer.

By default Visual Studio doesn’t show line numbers in the text editor which will be needed for this discussion.

Go to TOOLS>Options>Text Editor>All Languages>General and check the Line numbers box to turn on line numbering in the text editor.

Display Line Numbers

Click BUILD>Build Solution to build TRViewer and see what happens.

Build the solution

We get 1 error but Visual Studio stopped before it started processing any of the TRViewer source code files.

The way we will proceed is to fix the first error and then rebuild, because fixing the first error often fixes many of the following errors.

Copying and pasting the error into an internet search engine is how I find fixes for errors I don’t understand.

WINVER error

This error means that MFC does not support Windows versions older than Windows XP and tells us to change the definition of WINVER in the project properties or precompiled header.

Precompiled header means the file StdAfx.h and since the output shows StdAfx.cpp we will check there first before checking the project properties.

Expand the TRViewer>Source Files>MFC (cpp) folders in the Solution Explorer and double click on StdAfx.cpp to open it in the text editor.

Expand the TRViewer project

There is only the line, #include “stdafx.h”, in the file so we need to look at stdafx.h.

Right click on the text “stdafx.h” and select Open Document “stdafx.h” in the menu to open the file stdafx.h in the editor.

On line 23 change #define WINVER 0x0400 to #define WINVER 0x0501 and click BUILD>Rebuild Solution.

This time the first error is about the definition of _WIN32_WINNT in the precompiled header so once again check for its definition in the file stdafx.h.

Change line 27, #define _WIN32_WINNT 0x0400 to #define _WIN32_WINNT 0x0501 and rebuild the solution.

We get another error but we can see from the output that this time Visual Studio started processing a TRViewer source file, cvectmat.h.

The error says SBYTE has been redefined so we will search all the TRViewer source files to see where SBYTE is defined.

Double click on the error in the error list and Visual Studio will open cvectmat.h and go to the line where the error is.

Double click on the text “SBYTE” in cvectmat.h in the editor to select it and then click EDIT>Find and Replace>Find in Files. Click the Find All button to search all the files in the solution.

SBYTE is found three times but only once in a typedef definition so we don’t know where it has been previously defined.

So we will comment out line 33 in cvectmat.h by placing double slashes at the beginning of the line, //typedef unsigned char SBYTE;

Now in the editor if we hover the mouse pointer over SBYTE in one of the other lines we found containing SBYTE we see that SBYTE is an alias for a signed char.

Right clicking on the text “SBYTE” and selecting Go to Definition” shows us that SBYTE is defined in the file oledb.h which is a file included with Visual Studio so should not be modified.

We will assume that SBYTE now being defined as an signed char won’t cause a problem for the functions in cvectmat.h that use SBYTE parameters and rebuild TRViewer.

The build failed with at least 299 errors but this time Visual Studio attempted to compile all of the TRViewer source files which can be seen by inspecting the output.

299 errors

Double click on the first error in the error list which is about an undeclared identifier in debug.cpp to go to the error line in debug.cpp.

In line 233 the identifier “i” is used as a counter in a for loop but we see in line 223 that “i” is defined as a bit16 type in another for loop.

In VS2003 the “i” in each for loop would have been the same identifier but modern C++ compilers see each “i” as separate identifiers.

Fortunately there is an easy fix for this error.

Go to PROJECT>Properties to open the property pages for TRViewer.
Change the Configuration to All Configurations.

Set All Configurations in Property Pages

Expand the C/C++ node and select Language.
Change “Force Conformance in For Loop Scope” to “No” and click OK to save the change.

Set For Loop Conformance to No

Rebuild TRViewer.

The build fails but we are down to 41 errors.

41 errors

Clicking on the first error in the error list again takes us to a line in debug.cpp with an undeclared identifier.

The identifier “it” is an iterator used in a for loop so it seems our for loop scope fix doesn’t work for iterators used in for loops.

Scroll up to line 3403 were “it” is declared as type, my_type::iterator in a previous for loop.
What we need to do is copy this type and paste it before “it” in the for loop on line 3414.

Line 3414 of debug.cpp becomes:
for (my_type::iterator it = m_mapValue2List.begin(); it != m_mapValue2List.end(); ++it)

Scrolling down to line 3421 we find the identifier used in another for loop without a type declared before it so we will copy the type into that for loop as well.

Line 3421 of debug.cpp becomes:
for (my_type::iterator it = m_mapValue2List.begin(); it != m_mapValue2List.end(); ++it)

Rebuilding TRViewer fails again but the error count is down to 31.

31 errors

Double clicking on the first error in the error list takes us to line 3987 of debug.cpp where there is a missing type specifier.

We will add the missing type specifier.

Change line 3987 of debug.cpp to const int val = 123; .

Rebuilding TRViewer fails with 29 errors.

29 errors

When we look at line 254 in fragment.cpp we see it has an undeclared iterator “theIter” in a for loop.

Scrolling up to line 235 we see that “theIter” is first declared in a for loop with the type MATNAME2INFO::iterator.

Copy and paste this type in front of “theIter” in the for loop on line 254.

Line 254 of fragment.cpp becomes:
for (MATNAME2INFO::iterator theIter = lpMapMat->begin(); theIter != lpMapMat->end(); theIter++)

Scrolling down we see that “theIter is not used in any more for loops in this function. The end of the function is indicated by the line with just  “}”  not indented at all.

Rebuilding TRViewer fails with 26 errors.

26 errors

Looking at the first seven errors in the error list we can guess what we have to do.

Scroll up from the error line, find the first declaration of each iterator identifier in a for loop and copy the type specifier to the for loops after the initial for loop.

Line 354 of fragment.cpp changes to:
for (TRListMoveable::iterator theIterator = lpListMvb->begin(); theIterator != lpListMvb->end(); theIterator++)

Line 362 of fragment.cpp changes to:
for (TRListStaticMesh::iterator theIterator2 = lpListStatMsh->begin(); theIterator2 != lpListStatMsh->end(); theIterator2++)

Line 370 of fragment.cpp changes to:
for (TRListSpriteSeq::iterator theIterator3 = lpListSpriteSeq->begin(); theIterator3 != lpListSpriteSeq->end(); theIterator3++)

In line 582 of fragment.cpp the undeclared identifier “theIter” is not in a for loop.

It is initially declared in a for loop on line 568 so we know what type it should be but if we examine the for loop we see that it contains a break statement.

This means that the for loop can stop anytime and the value of the identifier “theIter” when the loop stops is used on line 582.

This means we cannot just copy the type in front of the identifier on line 582 because this would create a separate identifier to the one in the for loop.

What we will do is declare the identifier before the for loop and not inside the for loop so then the same identifier is used inside and outside the for loop.

In fragment.cpp insert a blank line before line 568 and add the code:
ListSlot::iterator theIter;

In fragment.cpp change line 569 (old line number 568) to:
for(theIter = SlotList.begin(); theIter != SlotList.end(); theIter++)

Rebuilding TRViewer fails with 19 errors.

19 errors

The first error is one we haven’t seen before but the next twelve are undeclared iterators in for loops to which we just have to add type specifiers.

The error at line 1033 in markupstl.cpp says that one side of the “=” sign we have type char * and on the other side we have const char *.

We cannot change the return type from the function on the right hand side of “=” but we can change the type of the identifier on the left hand side.

Change line 1030 of markupstl.cpp to:
const char* pFound;

Change line 312 of trfile_convert.cpp to:
for (TRListSpriteSeq::iterator theIterator3 = lpListSpriteSeq->begin(); theIterator3 != lpListSpriteSeq->end(); theIterator3++, dwSpriteSeq++)

Change line 440 of trfile_convert.cpp to:
for (TRListMoveable::iterator theIterator = lpListMoveable->begin(); theIterator != lpListMoveable->end(); theIterator++)

Change line 510 of trfile_convert.cpp to:
for (TRListMoveable::iterator theIterator = lpListMoveable->begin(); theIterator != lpListMoveable->end(); theIterator++)

Change line 746 of trfile_convert.cpp to:
for (TRListMoveable::iterator theIterator = lpListMoveable->begin(); theIterator != lpListMoveable->end(); theIterator++, lpMoveable++)

Change line 783 of trfile_convert.cpp to:
for (TRListStaticMesh::iterator theIterator2 = lpListStatMsh->begin(); theIterator2 != lpListStatMsh->end(); theIterator2++)

Change line 194 of trfile_mesh_3ds.cpp to:
for (MATNAME2INFO::iterator theIter = MapMat.begin(); theIter != MapMat.end(); theIter++)

Rebuilding TRViewer fails with 5 errors.

5 errors

The first error in the error list is the familiar undeclared iterator in a for loop.

Change line 283 in trsndmgr.cpp to:
for (MapIdx2Snd::iterator theIter = m_SoundMap.begin(); theIter != m_SoundMap.end(); theIter++, dwSoundDetail++)

Rebuilding TRViewer fails with more errors but these errors are linking errors and not compilation errors.

Linker errors

Notice the that the error numbers are prefixed with “LNK” and that no source files or line numbers are shown.

This means we have successfully compiled TRViewer and now need to adjust only the properties of the project and add or change libraries to have the linker link our compiled code into an executable file.

The errors about unsafe for SAFESEH image are all caused by the very old versions of the third party libraries included in TRViewer and could probably be fixed by using newer versions.

However a quick way to fix those errors is to change a setting in the property pages.

Open the project’s property pages via PROJECT>TRViewer Properties.
Change the Configuration to All Configurations, select Configuration Properties> >Linker>Advanced>Image Has Safe Exception Handlers and change the setting to “No”.

Set Image Has Safe Exception Handlers to No

Click Apply or OK to save the change and build TRViewer. Use build and not rebuild to avoid compiling everything again.

Building fails with an error – “cannot open file LIBC.lib”.

LIBC.lib is a library included with old versions of Visual Studio and so probably doesn’t exist in the 2013 version.

We will try and find which library needs to use functions from LIBC.lib.

Open TRViewer’s property pages, select the Debug configuration which should be also the Active configuration, Type the text “/verbose” into the Configuration Properties>Linker>Command Line>Additional Options box and click apply or OK to save the change.

Set linker verbose option

Build TRViewer and inspect the Output window.

verbose output

Scrolling through the output shows that it found LIBC while searching ftkvc40.lib for the ftkvc40.lib functions referenced elsewhere in the code.

If you have a dependency (library) that uses an old default library apparently you are supposed to rebuild that library with the same Visual Studio version you are building your program.

I will try that another day [link] and instead use the quick fix which is to set the linker to ignore a default library. This may cause runtime errors in TRViewer.

Go to TRViewer’s property pages and set All Configurations.
Type “LIBC” in Configuration properties>Linker>Input>Ignore Specific Default Libraries and click Apply or OK to save the change.

Ignore default library LIBC

TRViewer builds successfully.

Build successful

The output displays the folder where the TRViewer.exe executable is located. (Which is wrong for some reason - see below.)

In some earlier versions of the TRViewer source distributions the GL/glut32.lib library is missing and TRViewer will not build.

Use the NuGet Package Manager and install freeglut into the TRViewer project. Freeglut is a modern replacement for glut.

We have built our executable but will it operate correctly?

Open the folder where the executable is located in File Explorer.

To run TRViewer you need the following *.dlls need to be in the same folder or installed on your PC.
  • GLUT32.dll
  • ReadPic.dll
  • zlib.dll
These *.dll files are included in the debug output folder of the TRViewer_1083 source.

Required DLLs

A subdirectory named “resources” in the same folder as TRViewer.exe is also required and must contain the *.xml and *.txt files that TRViewer uses for slot names and level names etc.

The files in the resources file are shown below and are included in the debug output folder of the TRViewer_1083 source.

Other required files

To run TRViewer, double click on TRViewer.exe.

TRViewer running

Note that clicking the Open menu item or toolbar button crashes the Debug version! Only files in the recent files list can be opened.

Because we built the Debug version of TRViewer we should be able to run it in the Visual Studio debugger using DEBUG>Start Debugging or clicking the Local Windows Debugger toolbar button with the green coloured arrow.

When we do Visual Studio complains that it cannot find the file.

I didn’t notice before but the path in the successful build output shows the incorrect path to TRViewer.exe.

In the TRViewer Property Pages for the Debug configuration, the Configuration Properties>Linker>General>Output File setting includes path information.

Linker Output File setting

In TRViewer’s Property Pages for the Debug configuration, change the Configuration Settings>General>Output Directory to “..\bin\Debug\” and click OK.

Change the Output Directory setting

When you click DEBUG>Start Debugging Visual Studio may ask to rebuild the project because you changed a project property so click OK to rebuild.

Running TRViewer in the debugger allows you to locate which source code line the program was executing if it crashes and inspect the values of the variables at that point to see why.

If the program crashes in the debugger, click Retry and then click Break to return to the source code in Visual Studio.

To stop the debugger click the red coloured square toolbar button or click DEBUG>Stop Debugging.



next

No comments:

Post a Comment