/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see .
*/
/////////////////////////////////////////////////////////////////////////EA-V1
// $File: //depot/GeneralsMD/Staging/code/Libraries/Source/debug/debug_debug.h $
// $Author: mhoffe $
// $Revision: #1 $
// $DateTime: 2003/07/03 11:55:26 $
//
// ©2003 Electronic Arts
//
// main Debug object (singleton)
//////////////////////////////////////////////////////////////////////////////
#ifdef _MSC_VER
# pragma once
#endif
#ifndef DEBUG_DEBUG_H // Include guard
#define DEBUG_DEBUG_H
// this makes sure that whenever this header is included
// the accompanying OBJ file is linked in as well
#pragma comment(linker,"/include:___DebugIncludeInLink1")
/**
\class Debug debug.h
\brief Debug module main class (singleton).
*/
class Debug
{
// necessary because all debug commands operate directly on this class
friend class DebugCmdInterfaceDebug;
// necessary because exception handler needs direct access
friend class DebugExceptionhandler;
public:
enum
{
/// maximum number of times a check can be hit before it is turned off
MAX_CHECK_HITS = 20
};
/**
\brief HRESULT translator callback function type.
See \ref Debug::AddHResultTranslator for more information.
\param debug Debug instance where info can be written to
\param hresult current HRESULT value
\param user user value passed into AddHResultTranslator
\return true if value translated and no more translators should be called,
false if next translator should be tried
*/
typedef bool (*HResultTranslator)(Debug &debug, long hresult, void *user);
/**
\class MemDump debug.h
\brief Helper class for performing a raw memory dump.
Can be used directly in the output stream, e.g.
\code
DLOG( "This is 16 bytes of memory:\n" << Debug::MemDump::Raw(&somePointer,16) );
\endcode
*/
class MemDump
{
// necessary because Debug needs access to the following private members
friend Debug;
const unsigned char *m_startPtr; ///< start dumping with this address
unsigned m_numItems; ///< dump the given number of items
unsigned m_bytePerItem; ///< determines the number of bytes per item (1. 2 or 4)
bool m_absAddr; ///< show absolute addresses (true) or relative addresses (false)
bool m_withChars; ///< show printable characters on right side of dump (true) or not (false)
// constructor is private on purpose so that nobody can
// create instances of this class except the static functions
// provided herein
MemDump(const void *ptr, unsigned num, unsigned bpi, bool absAddr, bool withChars):
m_startPtr((const unsigned char *)ptr), m_numItems(num),
m_bytePerItem(bpi), m_absAddr(absAddr), m_withChars(withChars) {}
public:
/**
Creates a memory dump descriptor.
\param startPtr address to start dump from
\param numItems number of items (usually bytes) to dump
\param bytePerItem number of bytes per item (usually 1)
*/
static MemDump Raw(const void *startPtr, unsigned numItems, unsigned bytePerItem=1)
{
return MemDump(startPtr,numItems,bytePerItem,true,false);
}
/**
Creates a memory dump descriptor with relative addresses.
\param startPtr address to start dump from
\param numItems number of items (usually bytes) to dump
\param bytePerItem number of bytes per item (usually 1)
*/
static MemDump RawRel(const void *startPtr, unsigned numItems, unsigned bytePerItem=1)
{
return MemDump(startPtr,numItems,bytePerItem,false,false);
}
/**
Creates a memory dump descriptor that dumps out ASCII chars as well.
\param startPtr address to start dump from
\param numItems number of items (usually bytes) to dump
\param bytePerItem number of bytes per item (usually 1)
*/
static MemDump Char(const void *startPtr, unsigned numItems, unsigned bytePerItem=1)
{
return MemDump(startPtr,numItems,bytePerItem,true,true);
}
/**
Creates a memory dump descriptor with relative addresses that dumps out ASCII chars as well.
\param startPtr address to start dump from
\param numItems number of items (usually bytes) to dump
\param bytePerItem number of bytes per item (usually 1)
*/
static MemDump CharRel(const void *startPtr, unsigned numItems, unsigned bytePerItem=1)
{
return MemDump(startPtr,numItems,bytePerItem,false,true);
}
};
/**
\class HResult debug.h
\brief Helper class for writing HRESULTs to the debug stream.
Can be used directly in the output stream, e.g.
\code
DLOG( "My HResult is: " << Debug::HResult(SomeHRESULTValue) << "\n" );
\endcode
The most interesting thing about HRESULTs is that custom HRESULT translators
can be added or removed during runtime, see \ref Debug::AddHResultTranslator for more
information.
*/
class HResult
{
// necessary because Debug needs access to the following private members
friend Debug;
long m_hresult; ///< HRESULT value
public:
/**
Creates a HRESULT descriptor.
\param hresult HRESULT value (checked, Windows declares HRESULT as typedef long)
*/
explicit HResult(long hresult): m_hresult(hresult) {}
};
/** \internal
\brief Helper class for adding log group descriptions.
*/
class LogDescription
{
// sorry, no copies or assignments
LogDescription(const LogDescription&);
LogDescription& operator=(const LogDescription&);
public:
/** \internal
Adds a description for a logging file/group.
\param fileOrGroup filename or logging group (see \ref Debug::LogBegin)
\param description descriptive text for logging file/group
*/
LogDescription(const char *fileOrGroup, const char *description);
};
/**
\brief Switches integer output to hexadecimal format.
*/
class Hex {};
/// \internal Performs actual switch to hexadecimal format.
Debug& operator<<(Hex &)
{
SetPrefixAndRadix("0x",16);
return *this;
}
/**
\brief Switches integer output to decimal format.
*/
class Dec {};
/// \internal Performs actuals switch to decimal format
Debug& operator<<(Dec &)
{
SetPrefixAndRadix("",10);
return *this;
}
/**
\brief Switches integer output to binary format.
*/
class Bin {};
/// \internal Performs actuals switch to binary format
Debug& operator<<(Bin &)
{
SetPrefixAndRadix("%",2);
return *this;
}
/**
\brief Sets output width for the next insertion.
*/
class Width
{
// necessary because Debug needs access to the following private members
friend Debug;
int m_width; ///< output width
public:
/// \brief Sets new output width (next insertion only).
explicit Width(int width): m_width(width) {}
};
/// \internal Performs actuals width switch
Debug& operator<<(Width &w)
{
m_width=w.m_width;
return *this;
}
/**
\brief Sets new fill character.
*/
class FillChar
{
// necessary because Debug needs access to the following private members
friend Debug;
char m_fill; ///< fill character
public:
/// \brief Sets new fill character.
explicit FillChar(char ch=' '): m_fill(ch) {}
};
/// \internal Performs actuals setting of fill char
Debug& operator<<(FillChar &c)
{
m_fillChar=c.m_fill;
return *this;
}
/**
\brief Repeats a given character N times.
*/
class RepeatChar
{
// necessary because Debug needs access to the following private members
friend Debug;
char m_char; ///< character
int m_count; ///< repeat count
public:
/// \brief Repeats a given character N times
explicit RepeatChar(char ch, int count): m_char(ch), m_count(count) {}
};
/// \internal Performs actuals repeating of char
Debug& operator<<(RepeatChar &c);
/**
\brief Old printf style formatting.
\note Do not use this helper class for new code. It is mainly here
to get the old code base adapted to the new debug module more quickly.
*/
class Format
{
// necessary because Debug needs access to the following private members
friend Debug;
// no CC, AOp
Format(const Format &);
Format& operator=(const Format&);
char m_buffer[512]; ///< this contains the string to write \note Fixed size buffer!
public:
/// \brief Old printf style formatting.
explicit Format(const char *format, ...);
};
/// \internal Writes printf style formatted string to debug log.
Debug& operator<<(const Format &f)
{
operator<<(f.m_buffer);
return *this;
}
// this is necessary because LogDescription needs to call AddLogGroup
friend class LogDescription;
/** \internal
\brief Performs logical cleanup.
*/
~Debug();
/**
\brief Installs exception handler for current thread.
For the main thread this is already done, but for any additional
threads being created this function must be called.
*/
static void InstallExceptionHandler(void);
/** \internal
\brief Helper function for skipping over disabled asserts and logs.
This function simply records the address it has been called from. If
an assert or other function is disabled this function returns true,
false otherwise.
@todo_opt Change so that instead of returning true the call to this
function is directly removed from the calling code
\return true if next assert/log should be skipped, false otherwise
*/
static bool SkipNext(void);
/** \internal
\brief Helper function which gets called if an assertion fails.
Starts building the assert string which will then be send to the active
output destinations. SkipNext must be called before calling this function
since this function also associates the most recent SkipNext call with
the current assertion.
\param file file that contains DASSERT or DASSERT_MSG macro
\param line line where assert macro can be found
\param expr expression that triggered the assertion, NULL for 'general failure' (\ref DFAIL)
\return reference to Debug instance
*/
static Debug &AssertBegin(const char *file, int line, const char *expr);
/** \internal
\brief Displays assertion window.
Depending on the user feedback (abort, retry, ignore) the program then
either aborts or continues. This function is to be used as the final
call in a debug message stream.
\return false (always)
*/
bool AssertDone(void);
/** \internal
\brief Helper function which gets called if a check fails.
Starts building the assert string which will then be send to the active
output destinations. SkipNext must be called before calling this function
since this function also associates the most recent SkipNext call with
the current check.
\param file file that contains DCHECK or DCHECK_MSG macro
\param line line where check macro can be found
\param expr expression that triggered the assertion
\return reference to Debug instance
*/
static Debug &CheckBegin(const char *file, int line, const char *expr);
/** \internal
\brief Flushes current 'check' message.
This function is to be used as the final call in a debug message stream.
\return false (always)
*/
bool CheckDone(void);
/** \internal
\brief Helper function which gets when writing data to the output log.
Starts building the log string which will then be send to the active
output destinations. SkipNext must be called before calling this function
since this function also associates the most recent SkipNext call with
the current log group.
\param fileOrGroup current file or group the following log data is for
\return reference to Debug instance
*/
static Debug &LogBegin(const char *fileOrGroup);
/** \internal
\brief Flushes current 'log' message.
This function is to be used as the final call in a debug message stream.
\return false (always)
*/
bool LogDone(void);
/** \internal
\brief Helper function which gets called on crash.
Starts building the crash string which will then be send to the active
output destinations.
\param file file that contains DCRASH or DCRASH_RELEASE macro, if NULL
then no file info is given (used by DCRASH_RELEASE in release
builds)
\param line line where crash macro can be found, 0 if no line info should
be given
\return reference to Debug instance
*/
static Debug &CrashBegin(const char *file, int line);
/** \internal
\brief Exits program with a 'crash' message.
This function is to be used as the final call in a debug message stream.
\param die true if module should exit after displaying message, false if
the user should have the choice
\return false (always)
*/
bool CrashDone(bool die);
/** \internal
\brief Write string to output log.
\param str string to write
\return *this
*/
Debug& operator<<(const char *str);
/** \internal
\brief Define prefix and radix for integer output.
\param prefix prefix to use (typically "" or "0x")
\param radix radix to use (typically 10 or 16)
*/
void SetPrefixAndRadix(const char *prefix, int radix);
/** \internal
\brief Write signed integer to output log.
\param val signed integer
\return *this
*/
Debug& operator<<(int val);
/** \internal
\brief Write unsigned integer to output log.
\param val unsigned integer
\return *this
*/
Debug& operator<<(unsigned val);
/** \internal
\brief Write signed long to output log.
\param val signed long
\return *this
*/
Debug& operator<<(long val);
/** \internal
\brief Write unsigned long to output log.
\param val unsigned long
\return *this
*/
Debug& operator<<(unsigned long val);
/** \internal
\brief Write boolean value to output log.
\param val bool
\return *this
*/
Debug& operator<<(bool val);
/** \internal
\brief Write float value to output log.
\param val float
\return *this
*/
Debug& operator<<(float val);
/** \internal
\brief Write double precision float to output log.
\param val double
\return *this
*/
Debug& operator<<(double val);
/** \internal
\brief Write signed short integer to output log.
\param val signed short integer
\return *this
*/
Debug& operator<<(short val);
/** \internal
\brief Write unsigned short integer to output log.
\param val unsigned short integer
\return *this
*/
Debug& operator<<(unsigned short val);
/** \internal
\brief Write signed 64 bit integer to output log.
\param val signed 64 bit integer
\return *this
*/
Debug& operator<<(__int64 val);
/** \internal
\brief Write unsigned 64 bit integer to output log.
\param val unsigned 64 bit integer
\return *this
*/
Debug& operator<<(unsigned __int64 val);
/** \internal
\brief Write pointer address to output log.
\param ptr pointer address
\return *this
*/
Debug& operator<<(const void *ptr);
/** \internal
\brief Write memory dump to output log.
\param dump MemDump descriptor, defines what range of memory to dump
\return *this
*/
Debug& operator<<(const MemDump &dump);
/** \internal
\brief Write HRESULT value to output log.
\param hres HResult descriptor
\return *this
*/
Debug& operator<<(HResult hres);
/** \internal
\brief Determines if a log file/group is active or not.
\param fileOrGroup Name of source file or group to check for. If the
string contains any forward or backslashes then anything
before them is ignored. If the then remaining string
contains any dots anything beyond the first dot is ignored
as well.
\return true if logging is enabled, false if not
*/
static bool IsLogEnabled(const char *fileOrGroup);
/**
\brief Adds a HRESULT translator.
A HRESULT translator is called whenever a HResult descriptor is passed into
the Debug log stream. Such a translator can translate the numeric value
into something more meaningful.
Translators are differentiated by both function address and user pointer. There
is however no harm in adding the same translator/user pointer pair twice.
\param prio priority, translators with a higher priority get called first
\param func translator address
\param user optional user pointer which will be passed to the given translator
\see RemoveHResultTranslator
*/
static void AddHResultTranslator(unsigned prio, HResultTranslator func, void *user=0);
/**
\brief Removes a HRESULT translator.
If the translator/user pointer pair does not exist nothing is done.
\param func translator address
\param user optional user pointer
\see AddHResultTranslator
*/
static void RemoveHResultTranslator(HResultTranslator func, void *user=0);
/**
\brief Registers a new I/O class factory function.
This is typically used internally by the DEBUG_IMPLEMENT_IO_INTERFACE
macro. If there is already an I/O class with the same name registered
the new one overwrites the old.
\param io_id name of I/O class as it should be registered with Debug module
\param descr short class description
\param func factory function
\return true (so function can be used in static initializers)
*/
static bool AddIOFactory(const char *io_id, const char *descr,
DebugIOInterface* (*func)(void));
/**
\brief Adds a new command group.
If the command group already exists the new command interface is added at
the end of the list. A command issued for that group is first passed
to the 'old' command interface. If that DebugCmdInterface::Execute call
fails the command is passed on to the next interface within the same
command group.
The only exception is the 'help' command. Here all Execute functions are
always called.
Ownership of the interface pointer is passed to the Debug module i.e. the
interface is destroyed when the Debug module shuts down (unless the
interface is removed using RemoveCommands).
\param cmdgroup command group this interface implements
\param cmdif command group interface instance
\return true (so function can be used in static initializers)
*/
static bool AddCommands(const char *cmdgroup, DebugCmdInterface *cmdif);
/**
\brief Removes a command group.
\param cmdif command group interface that will be removed
*/
static void RemoveCommands(DebugCmdInterface *cmdif);
/**
\brief Issues a debug command.
\param cmd command to execute
*/
static void Command(const char *cmd);
/**
\brief Update method, must be called on a regular basis.
Scans I/O classes for new command input and processes it.
*/
static void Update(void);
/** \internal
\brief Simple recursive pattern matcher.
\param str string to match
\param pattern pattern, only wildcard valid is '*'
\return true if string matches pattern, false if not
*/
static bool SimpleMatch(const char *str, const char *pattern);
/**
\brief Tell debug module about build info.
All these strings are free form and can be up to 63 chars long.
\param version official version
\param internalVersion internal version
\param buildDate build date & time
*/
static void SetBuildInfo(const char *version,
const char *internalVersion,
const char *buildDate);
/**
\brief Write build information into log.
*/
void WriteBuildInfo(void);
private:
// no assignment, no copy constructor
Debug(const Debug&);
Debug& operator=(const Debug&);
/** \internal
Undocumented default constructor. Initializes debugging library.
We can make this private as well so nobody accidently tries to create
a Debug instance. Actually this function does not do anything -
initialization is rather performed by PreStaticInit() and
PostStaticInit().
*/
Debug(void);
/** \internal
This function gets called before any static C++ symbols are
initialized. Code herein must be extremely careful because all
global C++ instances are not initialized yet.
*/
static void PreStaticInit(void);
/** \internal
This function gets called after all static C++ symbols have
been initialized.
*/
static void PostStaticInit(void);
/** \internal
This function gets called if the program exists. Use this
function for any cleanup purposes (not the destructor, it
might get called too early).
*/
static void StaticExit(void);
/** \internal
The only debug instance. Actually not used for anything
except as magic first parameter for overloaded stream operators.
*/
static Debug Instance;
/** \internal
Helper variable for putting PreStaticInit() into the MSVC
startup list.
*/
static void *PreStatic;
/** \internal
Helper variable for putting PostStaticInit() into the MSVC
startup list.
*/
static void *PostStatic;
/// \internal HResult translator vector entry
struct HResultTranslatorEntry
{
/// priority
unsigned prio;
/// translator function
HResultTranslator func;
/// user pointer
void *user;
};
/// \internal HResult translator vector
HResultTranslatorEntry *hrTranslators;
/// \internal number of HResult translators
unsigned numHrTranslators;
/// \internal I/O class/factory list entry
struct IOFactoryListEntry
{
/// pointer to next entry in list
IOFactoryListEntry *next;
/// I/O ID
const char *ioID;
/// I/O description
const char *descr;
/// factory function
DebugIOInterface* (*factory)(void);
/// I/O interface (may be NULL)
DebugIOInterface *io;
/// input buffer
char *input;
/// used size of input buffer
unsigned inputUsed;
/// allocated size of input buffer
unsigned inputAlloc;
};
/** \internal
First I/O factory list entry. A singly linked list is
okay for this because looking up I/O IDs is not
time critical.
*/
IOFactoryListEntry *firstIOFactory;
/// \internal command interface list entry
struct CmdInterfaceListEntry
{
/// pointer to next entry in list
CmdInterfaceListEntry *next;
/// command group
const char *group;
/// interface pointer
DebugCmdInterface *cmdif;
};
/** \internal
First command interface list entry. A singly linked list is
okay for this because looking up command groups is not
time critical.
*/
CmdInterfaceListEntry *firstCmdGroup;
/// \internal current stack frame (used by SkipNext)
static unsigned curStackFrame;
/** \internal
Bit mask for frame entry types. Implemented as a bit
mask since it is also used for the inclusion/exclusion
list where a single pattern can apply to more than
one type.
*/
enum
{
/// assert
FrameTypeAssert = 0x00000001,
/// check
FrameTypeCheck = 0x00000002,
/// log
FrameTypeLog = 0x00000004
};
/** \internal
List of possible statuses for a frame hash entry.
*/
enum FrameStatus
{
/// unknown, must check
Unknown = 0,
/// skip this frame
Skip,
/// do not skip this frame
NoSkip
};
/** \internal
\brief Hash table entry for mapping stack frame addresses to
asserts/checks/logs.
*/
struct FrameHashEntry
{
/// pointer to next entry with same hash
FrameHashEntry *next;
/// frame address
unsigned frameAddr;
/// frame type (FrameTypeAssert, FrameTypeCheck, or FrameTypeLog)
unsigned frameType;
/// file (or group if log)
const char *fileOrGroup;
/// line number (undefined for logs)
int line;
/// number of times this frame has been hit
int hits;
/// frame status
FrameStatus status;
};
/// \internal initial frame hash size (prime number)
enum { FRAME_HASH_SIZE = 10007 };
/// \internal frame hash pointers
FrameHashEntry *frameHash[FRAME_HASH_SIZE];
/// \internal number of FrameHashEntry structures to allocate at one time
enum { FRAME_HASH_ALLOC_COUNT = 100 };
/// \internal next unused FrameHashEntry structure
FrameHashEntry *nextUnusedFrameHash;
/// \internal number of available FrameHashEntry structures
unsigned numAvailableFrameHash;
/** \internal
Look up given frame address.
\param addr frame address
\return FrameHashEntry found or 0 if nothing found
*/
__forceinline FrameHashEntry *LookupFrame(unsigned addr)
{
for (FrameHashEntry *e=frameHash[addr%FRAME_HASH_SIZE];e;e=e->next)
if (e->frameAddr==addr)
return e;
return 0;
}
/** \internal
Add frame address entry (entry must not exist yet). If
a log frame is added then a corresponding entry is added
to the list of known log groups as well.
\param addr frame address
\param type frame type
\param fileOrGroup file (or group name)
\param line line number
\return the entry just added
*/
FrameHashEntry *AddFrameEntry(unsigned addr, unsigned type,
const char *fileOrGroup, int line);
/** \internal
Updates the frames status variable depending on the current
pattern list.
\param entry entry that needs to be updated
*/
void UpdateFrameStatus(FrameHashEntry &entry);
/** \internal
Combines LookupFrame, AddFrameEntry and UpdateFrameStatus.
\param addr frame address
\param type frame type
\param fileOrGroup file (or group name)
\param line line number
\return the entry just added (or the already existing entry)
*/
FrameHashEntry *GetFrameEntry(unsigned addr, unsigned type,
const char *fileOrGroup, int line)
{
FrameHashEntry *e=LookupFrame(addr);
if (!e)
e=AddFrameEntry(addr,type,fileOrGroup,line);
if (e->status==Unknown)
UpdateFrameStatus(*e);
return e;
}
/** \internal
\brief List of known logs.
A singly linked list is
okay for this because looking up log groups happens only
if a new stack frame entry is added (which happens only
once for each log command).
*/
struct KnownLogGroupList
{
/// next entry
KnownLogGroupList *next;
/// name of log group (dynamically allocated memory)
char *nameGroup;
/// log description (if any)
const char *descr;
};
/// \internal first log group list entry
KnownLogGroupList *firstLogGroup;
/** \internal
Adds a log group (and optionally description) to the list
of known groups. If a group with that name already exists
nothing is done. Takes care of stripping path and extension
of the passed in group name (if it's a file name).
Returns translated group name.
\param fileOrGroup file or log group
\param descr description, may be NULL
\return translated log group name
*/
const char *AddLogGroup(const char *fileOrGroup, const char *descr);
/// \internal I/O buffers for all I/O string types
struct
{
/// buffer
char *buffer;
/// used buffer size
unsigned used;
/// allocated buffer size
unsigned alloc;
/// has last character been CR?
bool lastWasCR;
} ioBuffer[DebugIOInterface::StringType::MAX];
/// \internal current I/O string type we're writing
DebugIOInterface::StringType curType;
/// \internal current I/O string source (fixed size, careful!)
char curSource[256];
/// \internal used to enable/disable all asserts/checks/logs
int disableAssertsEtc;
/** \internal
Starts new output stream with the given type and source.
\param type string type
\param fmt wsprintf format string
*/
void StartOutput(DebugIOInterface::StringType type, const char *fmt, ...);
/** \internal
Adds the given string to the currently active output buffer.
\param str string
\param len string length
*/
void AddOutput(const char *str, unsigned len);
/** \internal
Flushes current I/O buffer to all output handlers.
\param defaultLog if true and no I/O class is active then data
is written to default.log
*/
void FlushOutput(bool defaultLog=true);
/// \internal pointer to currently active frame
FrameHashEntry *curFrameEntry;
/// \internal pattern list entry
struct PatternListEntry
{
/// next entry
PatternListEntry *next;
/// frame type(s)
unsigned frameTypes;
/// active (true) or inactive (false)?
bool isActive;
/// pattern itself (dynamic allocated memory)
char *pattern;
};
/** \internal
First pattern list list entry. A singly linked list is
okay for this because checking patterns is a costly
operation anyway and is therefore cached.
*/
PatternListEntry *firstPatternEntry;
/// \internal last pattern list entry for fast additions to list at end
PatternListEntry *lastPatternEntry;
/** \internal
Adds a new pattern list entry. This function does not check if
a matching pattern entry exists already.
\param types frame type(s)
\param isActive active (true) or inactive (false)?
\param pattern pattern itself
*/
void AddPatternEntry(unsigned types, bool isActive, const char *pattern);
/** \internal
Executes the given command.
\param cmdstart start of command
\param cmdend end of command (not including this character)
*/
void ExecCommand(const char *cmdstart, const char *cmdend);
/** \internal
\brief Checks if main program is running windowed or not.
If the decisison can not be made an windowed program is assumed.
\return true if windowed, false if full screen
*/
bool IsWindowed(void);
/// \internal name of current command group
char curCommandGroup[100];
/// \internal if true then always flush after each line written
bool alwaysFlush;
/// \internal if true then put timestamps before each new line
bool timeStamp;
/// \internal the one and only stack walker
DebugStackwalk m_stackWalk;
/// \internal current integer prefix to use
char m_prefix[16];
/// \internal current integer radix to use
int m_radix;
/// \internal official version
char m_version[64];
/// \internal internal version
char m_intVersion[64];
/// \internal build date/time
char m_buildDate[64];
/// \internal output width
int m_width;
/// \internal fill char
char m_fillChar;
/// \internal <0 if fullscreen, >0 if windowed, ==0 if not checked yet
char m_isWindowed;
};
/// \addtogroup debug_fn Debugging functions
///@{
/**
\brief Determines default commands to be executed at startup.
This function returns a list of default commands which are executed on
startup if no .dbgcmd file is read.
Currently this function returns:
\code
"debug.io flat add"
\endcode
In order to provide a different set of commands simply put another
version of DebugGetDefaultCommands in a CPP file. This is basically
identical to having a .dbgcmd file in the current directory. For some
situations (e.g. passing EXEs off to QA) it is however safer to have
these startup commands build into the EXE itself rather than relying
on QA having the right .dbgcmd file in place.
As an example a new version of this function might return:
\code
"debug.io flat add\nio flat copy q:\logfiles\n"
\endcode
\return list of commands, separated by \\n
\note This function is executed after all static variables have been initialized.
*/
const char *DebugGetDefaultCommands(void);
///@} end of debug_fn group
#endif // DEBUG_DEBUG_H