Some time ago, at job interview I was given a task of porting "PliyySattano 2" game to PC/Windows. It was pretty cool thing to do from pure technical point of view, although it wasn't my type of game.
I've received PS2 binary which worked atop of PC emulation layer ("PS2 player" or "reference application" as you can call it), all the PS2 source code (in unknown state, for me at last) , alot of assets and other weird things . The goal was to compile it somehow, put everything together, make display preferably on DirectX, add sound (optionally) and incorporate motion blur effect (also optionally).
I had to estimate how long it will take too. I had no clue, so the first bet was one week, which was suggested during the interview. Finally, I've made an agreement that I will tell after a week how it goes and tell if I need some more time. But making it for more than two weeks was no-go.
Of course I couldn't say, just before deadline, that I cannot send the final binary, because dog has eaten my floppy too ;).
The game is quite simple you navigate console of choice and you have to gain whole market share (this is the footage from actual PC port):
I am quite pleased with the end result. For sound I've used FMOD library. I've started with BASS (which was used in original sources), but it gave me weird linking errors on some functions, like play simple sound, very weird, so I had to quickly replace it. Graphics is hardware accelerated DirectX (programmable pipeline), for windowing and input I've used SDL. Whole thing took ca. 2 intensive weeks. I was afraid a little of DirectX part, because I've never used it directly actually (I'm only OpenGL lad with experience with several 3D api agnostic engines ;)), but in the end it wasn't that hard. I've also got no prior experience with PS2 at all, so I've searched for some information about it on the web and this one was quite useful. It allowed me to filter out PS2 specific parts of the sources and wipe them out. It also gave me a rough idea what specific part of PS2 code is doing.
For a port I had to rewrite file i/o, replace time measurement functions with native Win32 ones, make DirectX 9.0c renderer, add loading textures, feed various parameters to shaders, adjust data files (some textures refused to load, some paths weren't pointing to right assets) - it also meant editing some 3d model files with hex/binary editor. I've added also logger which can output everything to console/file and all the logs can be removed from the final build with one define. It, among other things, saved my ass ;), helped figure out how the overall system works during runtime, which in turn gave me information where to fill the missing gaps.
During development I've used Mercurial SCM to make rollback quickly when something goes wrong (fortunately it didn't happen). I've also used Cpp Unit for static code analysis, which helped me track down some issues with original code. For shaders HLSL code validation and testing NVIDIA's FX Composer was very useful.
I've managed to make working game system pretty quickly (with sound) in a week, the renderer part took longer than I've anticipated.
What I think should be improved to make this port better:
- startup, configuration dialog - to enable/disable fullscreen mode, choose preferred screen resolution and graphics details (this would also mean adding special execution paths for renderer). For this project the only requirement was support for Vertex/Pixel shaders 2.0 or better . If card had no support for vertex/pixel shaders then appropriate message dialog is displayed and application quits gracefully.
- Handle the device lost, device reset states. This has to be implemented, especially for full screen applications. This would also mean adding some sort of mechanism for reloading the assets during runtime (if it is needed). The truth is other applications can interfere with game, users can pres alt-tab any time. End of story.
- Better configuration for input devices - users like to configure input settings to their liking, want to use various controllers. Hardcoding controls isn’t good idea.
- Put files into one archive, this would mean making Virtual File System of some sort.
- Compress/convert assets like sound and textures. Compressed textures save CPU <-> graphics card bandwith. Making one big texture atlas wouldn’t be stupid thing too.
- Use of precompiled shaders.
- Rework of GUI to be resolution independent. In original code there was hardcoded values which were tied to one resolution. To make it resolution independent all screen coordinates had to be in 0.0f-1.0f range.
- Write an installer - self explanatory. No one wants to add manually various runtimes to launch the application, especially the end user.
Here are some tips I want to share for everyone starting to develop win32 applications with DirectX:
1) Debug CRT
Enable debug CRT. When quiting you will have some additional info about leaked memory.
1 2 3 4 5 6 7 #ifdef _MSC_VER // Enable run-time memory check for debug builds. #if defined(DEBUG) | defined(_DEBUG) #include <crtdbg.h> _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); #endif
2) DirectX settings in control panel
Quite handy when tracking DirectX bugs. Beware of "_Break on memory leaks_" and "_Break on DX9 error_" tick boxes. Those settings caused Visual Studio 2010 crash during launch(or after clicking the upper window bar) and this in turn activated "Choose the debugger dialog". After choosing default debugger, Visual Studio was relaunched once again to crash once more - vicious circle ;). I've made several reinstalls/repairs of the whole development environment, before I've figured out that it's the fault of those damn settings.
3) PIX for Windows
This tool is for tracking DirectX bugs in display, it comes with DirectX SDK. You can trace all the graphics operations and see the process of drawing frame after frame, look up the geometry buffers (index, vertices) and frame buffers, step through state changes and many more. I wish that there were similar, free tool for OpenGL development.
And this will lead to displaying geometry part. As I was quite new to DirectX functionalities, I've created separate testbed to try/test drawing out of main application. I had also several issues.
First one was with specification of custom Vertex format. Firstly I've used FVF defines (because it looked quicker to type), but it lead to "vertex format sizzling' errors during drawing operations. It's very likely that I've overlooked something, but I've tried many times adjusting the custom vertex size ( _sizeof(myCustomVertex3D)_ for vertex size in **SetStreamSource()** didn't work). In the end I've eliminated the problem by using standard DirectX vertex declarations (described with **D3DVERTEXELEMENT9** array). So, stay away from FVF.
Second, I've lost alot of time with **D3DXMESH** stuff. It had vertex and index buffers builtin which I though would be cool to feed with my data. Figuring out attribute buffer also took awhile. Unfortunately I had crashes during subset drawing (I don't know really why). In the end I've created triangle vertex and index buffers separately, which worked.
Third, setting orthogonal view and mapping triangles to screen space. There were two issues here, first one was to selecting the proper vertex format. At first I've experimented with **D3DFVF_XYZRHW** flexible format, but it meant that i had to use two kinds of vertex declarations - for 2D elements with vertices in screen space and another one for traditional 3D models. In the end I've used one format for 2D/3D vertices and left behind **D3DFVF_XYZRHW** idea. For vertices that should be left alone and untransformed by the pipeline, w component in position vector had to be set to 1.0f. Second one was setting orthogonal view with origin at (0,0) in upper left corner and (screenWidth, screenHeight) for a lower right one. **D3DXMatrixOrthoOffCenterLH()** function made it easy.
Fourth, everything is rendering, 3D models doesn't display at all, real show stopper. I've tried everything, from changing camera view to tinkering with world, view, projection matrices to force the drawing of the models somewhere the camera is pointing at. In the end it turned out that objects were drawn very far from the camera and setting far plane clipping to insane 10000 fixed the problem. So always check out your zNear/zFar values :).
And here is one of many precious WTF moments:
One final thought, porting code to PC is alot easier than PC to console - we have alot more resources at our disposal, this eases things alot and is really liberating. On the other hand there is so many possible users configurations, so you have to program everything in flexible/scalable way as you can. Additionally the code that I've received was easier to work with, because whole game logic was separated from the platform dependent code, this might not happen often in real-life projects.
Getting things done is also hard sometimes, I had several moments when I had no clue what to do due to serious bugs or crashes. The best thing to do in those grim moments is to take a break and return to the issue later or switch to other task that you can manage somehow, read something or make some improvements that will allow you better track problems, be it logs or error checking.
Now, after all of this I want to get PS2 black box and hack it to pieces 😛 .