Upcoming Events
Unite 2010
11/10 - 11/12 @ Montréal, Canada

GDC China
12/5 - 12/7 @ Shanghai, China

Asia Game Show 2010
12/24 - 12/27  

GDC 2011
2/28 - 3/4 @ San Francisco, CA

More events...
Quick Stats
69 people currently visiting GDNet.
2406 articles in the reference section.

Help us fight cancer!
Join SETI Team GDNet!
Link to us Events 4 Gamers
Intel sponsors gamedev.net search:

Contents
 Organizing Projects
 Creating Libraries
 Runtime Log Files
 Protecting
 Image Data

 Introduction
 to Scripting

 The String Table

 Printable version
 Discuss this article
 in the forums



The Series
 Beginning Windows
 Programming

 Using Resources
 in Win32 Programs

 Tracking Your
 Window/Using GDI

 Introduction
 to DirectX

 Palettes and Pixels
 in DirectDraw

 Bitmapped Graphics
 in DirectDraw

 Developing the
 Game Structure

 Basic Tile Engines
 Adding Characters
 Tips and Tricks

Runtime Log Files

How many times have you fired up your game program, anxious to see how your newest additions are working out, only to see a black screen or have the program crash on you? If you said "never," you're either a god or a liar. And since gods have no need to read these articles, that makes you a liar. :) Tracking down logic errors is no fun, so you need anything you can get that will help you locate problems. The Visual C++ debugger is a great tool for this, and log files are another. A log file is simply a text file generated by a program while it's running that tells the results of various function calls, the states of variables at certain points, or anything else you care to throw in there. Here's an example, a little snippet of Terran's log file:

DIRECTDRAW
DirectDraw interface created.
Fullscreen cooperation level set.
    --Message Received: WM_DISPLAYCHANGE
Resolution 640x480x16 set.
Surfaces created successfully in system memory.
Pixel format is 5.6.5 (16-bit).
DirectDraw clipper created.
Clipper attached to back buffer.

This, obviously, is one of the blocks of text generated during initialization of the game. Something like this can help you see exactly which function call is failing, or what setting is coming out not as you'd expect. When I first released a demo of Terran, some people said that the fading wasn't working properly. Thankfully they send back this log file, and I was able to see immediately that it would always fail when the pixel format was 5.5.5, so I knew right where to look for the bug. Another thing you can use log files for is outputting information about the user's computer. If things are running slowly for someone, it's possible that their machine doesn't accelerate some feature of DirectDraw that your machine does. Things like that are easy to spot when you can look at a log file for reference.

There are two basic ways to create a log file. One is to just open a file at the beginning of the program, write all your information with sprintf() statements during the game's run, and close the file at the end. The other way is to write a function that does that every time you want to log something. I prefer the latter, because if the program crashes, it's better not to have left the file open. That approach also makes it possible to easily enable or disable logging while the program is running, or add extra features instead of just a straight write. Here's an example of the logging function that I use:

BOOL ADXL_LogText(char *lpszText, ...)
{
  // only do this if logging is enabled
  if (bLogEnabled)
  {
    va_list argList;
    FILE *pFile;

    // initialize variable argument list
    va_start(argList, lpszText);

    // open the log file for append
    if ((pFile = fopen("log.txt", "a+")) == NULL)
    return(FALSE);

    // write the text and a newline
    vfprintf(pFile, lpszText, argList);
    putc('\n', pFile);

    // close the file
    fclose(pFile);
    va_end(argList);
  }

  // return success
  return(TRUE);
}

Chances are you haven't written a function with an arbitrary number of parameters before, so I'll explain briefly. The elipsis (...) in the function header says that after lpszText, any number of additional arguments may follow, of any data type. To handle something like that, you need the va_list data type, and the va_start and va_end macros. The "va" presumably stands for "variable arguments." Anyway, the va_list type is just a pointer to a list of arguments. You need to create one in any function that receives a variable number of arguments. The va_start macro initializes a list of type va_list, and takes the list, and the previous argument as parameters. The va_end macro simply resets the argument list pointer to NULL.

Finally, the other bit of syntax to notice in the log function is the use of the vfprintf() function, which is the version of fprintf() that is designed for use with variable argument lists. There are also vsprintf() and vprintf() functions that function similarly if you ever need them.

The variable bLogEnabled that you see near the top is set by the other logging function I use, which simply enables the log file. It's a pretty straightforward one:

int ADXL_EnableLog()
{
  FILE* pFile;

  // enable log
  bLogEnabled = TRUE;

  // clear the file contents
  if ((pFile = fopen("log.txt", "wb")) == NULL)
    return(FALSE);

  // close it up and return success
  fclose(pFile);
  return(TRUE);
}

The bLogEnabled variable is just a global variable within my ADXL library, where these functions are coming from. The variable initially is set to FALSE, so that nothing gets logged until after you call ADXL_EnableLog(). It's nice to have it set up this way, because then if you want to distribute a version of your program that doesn't generate a huge log file, you can just remove one line, instead of searching through your code to remove all the calls to the logging function.





Next : Protecting Image Data