The Mega Change Of Doom - or, fixing Stop. Almost. At least it's better than before. However, the OpenGL plugin seems to lose textures a lot between game restarts :P I think the GL plugin needs to do a lot more cleanup.

This change also includes tons of minor code formatting cleanup. Yeah, should've separated it ... sorry :(

Kills the old CPUCompare support. I'll resurrect it if I need it again, right now it mostly clutters the code.

git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@2321 8ced0084-cf51-0410-be5f-012b33b47a6e
This commit is contained in:
hrydgard
2009-02-20 22:04:52 +00:00
parent f992dae50d
commit 6cd34b318f
47 changed files with 685 additions and 1088 deletions

View File

@ -15,10 +15,8 @@
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
/*
All plugins from Core > Plugins are loaded and unloaded with this class when
Dolpin is started and stopped.
*/
// All plugins from Core > Plugins are loaded and unloaded with this class when
// Dolphin is started and stopped.
#include <string.h> // System
#ifdef _WIN32
@ -34,7 +32,6 @@
#include "DynamicLibrary.h"
#include "ConsoleWindow.h"
DynamicLibrary::DynamicLibrary()
{
library = 0;
@ -42,33 +39,33 @@ DynamicLibrary::DynamicLibrary()
std::string GetLastErrorAsString()
{
#ifdef _WIN32
LPVOID lpMsgBuf = 0;
DWORD error = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
error,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPTSTR) &lpMsgBuf,
0, NULL);
std::string s;
if (lpMsgBuf)
{
s = ((char *)lpMsgBuf);
LocalFree(lpMsgBuf);
} else {
s = StringFromFormat("(unknown error %08x)", error);
}
return s;
#else
static std::string errstr;
char *tmp = dlerror();
if (tmp)
errstr = tmp;
return errstr;
#endif
#ifdef _WIN32
LPVOID lpMsgBuf = 0;
DWORD error = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
error,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPTSTR) &lpMsgBuf,
0, NULL);
std::string s;
if (lpMsgBuf)
{
s = ((char *)lpMsgBuf);
LocalFree(lpMsgBuf);
} else {
s = StringFromFormat("(unknown error %08x)", error);
}
return s;
#else
static std::string errstr;
char *tmp = dlerror();
if (tmp)
errstr = tmp;
return errstr;
#endif
}
/* Function: Loading means loading the dll with LoadLibrary() to get an
@ -81,15 +78,16 @@ std::string GetLastErrorAsString()
Called from: The Dolphin Core */
int DynamicLibrary::Load(const char* filename)
{
if (!filename || strlen(filename) == 0) {
LOG(MASTER_LOG, "Missing filename of dynamic library to load");
PanicAlert("Missing filename of dynamic library to load");
return 0;
if (!filename || strlen(filename) == 0)
{
LOG(MASTER_LOG, "Missing filename of dynamic library to load");
PanicAlert("Missing filename of dynamic library to load");
return 0;
}
LOG(MASTER_LOG, "Trying to load library %s", filename);
if (IsLoaded()) {
LOG(MASTER_LOG, "Trying to load already loaded library %s", filename);
return 2;
if (IsLoaded())
{
LOG(MASTER_LOG, "Trying to load already loaded library %s", filename);
return 2;
}
Console::Print("LoadLibrary: %s", filename);
@ -99,10 +97,11 @@ int DynamicLibrary::Load(const char* filename)
library = dlopen(filename, RTLD_NOW | RTLD_LOCAL);
#endif
Console::Print(" %p\n", library);
if (!library) {
LOG(MASTER_LOG, "Error loading DLL %s: %s", filename, GetLastErrorAsString().c_str());
PanicAlert("Error loading DLL %s: %s\n", filename, GetLastErrorAsString().c_str());
return 0;
if (!library)
{
LOG(MASTER_LOG, "Error loading DLL %s: %s", filename, GetLastErrorAsString().c_str());
PanicAlert("Error loading DLL %s: %s\n", filename, GetLastErrorAsString().c_str());
return 0;
}
library_file = filename;
@ -111,28 +110,29 @@ int DynamicLibrary::Load(const char* filename)
int DynamicLibrary::Unload()
{
int retval;
if (!IsLoaded()) {
PanicAlert("Error unloading DLL %s: not loaded", library_file.c_str());
return 0;
}
int retval;
if (!IsLoaded())
{
PanicAlert("Error unloading DLL %s: not loaded", library_file.c_str());
return 0;
}
Console::Print("FreeLibrary: %s %p\n", library_file.c_str(), library);
Console::Print("FreeLibrary: %s %p\n", library_file.c_str(), library);
#ifdef _WIN32
retval = FreeLibrary(library);
retval = FreeLibrary(library);
#else
retval = dlclose(library)?0:1;
retval = dlclose(library)?0:1;
#endif
if (! retval) {
PanicAlert("Error unloading DLL %s: %s", library_file.c_str(),
GetLastErrorAsString().c_str());
}
library = 0;
return retval;
if (!retval)
{
PanicAlert("Error unloading DLL %s: %s", library_file.c_str(),
GetLastErrorAsString().c_str());
}
library = 0;
return retval;
}
void* DynamicLibrary::Get(const char* funcname) const
{
void* retval;
@ -156,5 +156,3 @@ void* DynamicLibrary::Get(const char* funcname) const
return retval;
}

View File

@ -14,17 +14,12 @@
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
//////////////////////////////////////////////////////////////////////////////////////////
// Include
/* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
File description: This is the common Plugin class that links to the functions that are
common to all plugins. This class is inherited by all plugin classes. But it's only created
directly in PluginManager.cpp when we check if a plugin is valid or not.
///////////////////////////////////////////////*/
////////////////////////////////////////////////
// This is the common Plugin class that links to the functions that are
// common to all plugins. This class is inherited by all plugin classes. But it's only created
// directly in PluginManager.cpp when we check if a plugin is valid or not.
///////////////////////////////////////////////
#include "Plugin.h"
@ -33,21 +28,20 @@ namespace Common
CPlugin::~CPlugin()
{
m_hInstLib.Unload();
m_hInstLib.Unload();
}
CPlugin::CPlugin(const char* _szName) : valid(false)
{
m_GetDllInfo = NULL;
m_DllConfig = NULL;
m_DllDebugger = NULL;
m_SetDllGlobals = NULL;
m_Initialize = NULL;
m_Shutdown = NULL;
m_DoState = NULL;
if (m_hInstLib.Load(_szName))
m_GetDllInfo = NULL;
m_DllConfig = NULL;
m_DllDebugger = NULL;
m_SetDllGlobals = NULL;
m_Initialize = NULL;
m_Shutdown = NULL;
m_DoState = NULL;
if (m_hInstLib.Load(_szName))
{
m_GetDllInfo = reinterpret_cast<TGetDllInfo>
(m_hInstLib.Get("GetDllInfo"));
@ -66,7 +60,7 @@ CPlugin::CPlugin(const char* _szName) : valid(false)
}
// Check if the plugin has all the functions it shold have
if (m_GetDllInfo != 0 &&
if (m_GetDllInfo != 0 &&
m_DllConfig != 0 &&
m_DllDebugger != 0 &&
m_SetDllGlobals != 0 &&
@ -81,63 +75,57 @@ CPlugin::CPlugin(const char* _szName) : valid(false)
void *CPlugin::LoadSymbol(const char *sym)
{
return m_hInstLib.Get(sym);
return m_hInstLib.Get(sym);
}
// GetInfo: Get DLL info
bool CPlugin::GetInfo(PLUGIN_INFO& _pluginInfo)
{
if (m_GetDllInfo != NULL) {
m_GetDllInfo(&_pluginInfo);
return(true);
}
return(false);
if (m_GetDllInfo != NULL) {
m_GetDllInfo(&_pluginInfo);
return(true);
}
return(false);
}
// Config: Open the Config window
void CPlugin::Config(HWND _hwnd)
{
if (m_DllConfig != NULL)
m_DllConfig(_hwnd);
if (m_DllConfig != NULL)
m_DllConfig(_hwnd);
}
// Debug: Open the Debugging window
void CPlugin::Debug(HWND _hwnd, bool Show)
{
if (m_DllDebugger != NULL)
m_DllDebugger(_hwnd, Show);
if (m_DllDebugger != NULL)
m_DllDebugger(_hwnd, Show);
}
void CPlugin::SetGlobals(PLUGIN_GLOBALS* _pluginGlobals) {
if (m_SetDllGlobals != NULL)
m_SetDllGlobals(_pluginGlobals);
if (m_SetDllGlobals != NULL)
m_SetDllGlobals(_pluginGlobals);
}
void CPlugin::DoState(unsigned char **ptr, int mode) {
if (m_DoState != NULL)
m_DoState(ptr, mode);
if (m_DoState != NULL)
m_DoState(ptr, mode);
}
// Run Initialize() in the plugin
void CPlugin::Initialize(void *init)
{
/* We first check that we have found the Initialize() function, but there
is no restriction on running this several times */
if (m_Initialize != NULL)
m_Initialize(init);
/* We first check that we have found the Initialize() function, but there
is no restriction on running this several times */
// Uh, the above comment sounds extremely dubious.
if (m_Initialize != NULL)
m_Initialize(init);
}
void CPlugin::Shutdown()
{
if (m_Shutdown != NULL)
m_Shutdown();
if (m_Shutdown != NULL)
m_Shutdown();
}
} // end of namespace Common

View File

@ -1,36 +1,41 @@
#include "PluginDSP.h"
namespace Common {
PluginDSP::PluginDSP(const char *_Filename) : CPlugin(_Filename), validDSP(false) {
PluginDSP::PluginDSP(const char *_Filename) : CPlugin(_Filename), validDSP(false) {
DSP_ReadMailboxHigh = reinterpret_cast<TDSP_ReadMailBox>
(LoadSymbol("DSP_ReadMailboxHigh"));
DSP_ReadMailboxLow = reinterpret_cast<TDSP_ReadMailBox>
(LoadSymbol("DSP_ReadMailboxLow"));
DSP_WriteMailboxHigh = reinterpret_cast<TDSP_WriteMailBox>
(LoadSymbol("DSP_WriteMailboxHigh"));
DSP_WriteMailboxLow = reinterpret_cast<TDSP_WriteMailBox>
(LoadSymbol("DSP_WriteMailboxLow"));
DSP_ReadControlRegister = reinterpret_cast<TDSP_ReadControlRegister>
(LoadSymbol("DSP_ReadControlRegister"));
DSP_WriteControlRegister = reinterpret_cast<TDSP_WriteControlRegister>
(LoadSymbol("DSP_WriteControlRegister"));
DSP_Update = reinterpret_cast<TDSP_Update>
(LoadSymbol("DSP_Update"));
DSP_SendAIBuffer = reinterpret_cast<TDSP_SendAIBuffer>
(LoadSymbol("DSP_SendAIBuffer"));
(LoadSymbol("DSP_ReadMailboxHigh"));
DSP_ReadMailboxLow = reinterpret_cast<TDSP_ReadMailBox>
(LoadSymbol("DSP_ReadMailboxLow"));
DSP_WriteMailboxHigh = reinterpret_cast<TDSP_WriteMailBox>
(LoadSymbol("DSP_WriteMailboxHigh"));
DSP_WriteMailboxLow = reinterpret_cast<TDSP_WriteMailBox>
(LoadSymbol("DSP_WriteMailboxLow"));
DSP_ReadControlRegister = reinterpret_cast<TDSP_ReadControlRegister>
(LoadSymbol("DSP_ReadControlRegister"));
DSP_WriteControlRegister = reinterpret_cast<TDSP_WriteControlRegister>
(LoadSymbol("DSP_WriteControlRegister"));
DSP_Update = reinterpret_cast<TDSP_Update>
(LoadSymbol("DSP_Update"));
DSP_SendAIBuffer = reinterpret_cast<TDSP_SendAIBuffer>
(LoadSymbol("DSP_SendAIBuffer"));
DSP_StopSoundStream = reinterpret_cast<TDSP_StopSoundStream>
(LoadSymbol("DSP_StopSoundStream"));
if ((DSP_ReadMailboxHigh != 0) &&
(DSP_ReadMailboxLow != 0) &&
(DSP_WriteMailboxHigh != 0) &&
(DSP_WriteMailboxLow != 0) &&
(DSP_ReadControlRegister != 0) &&
(DSP_WriteControlRegister != 0) &&
(DSP_SendAIBuffer != 0) &&
(DSP_Update != 0))
validDSP = true;
}
PluginDSP::~PluginDSP() {
}
(DSP_ReadMailboxLow != 0) &&
(DSP_WriteMailboxHigh != 0) &&
(DSP_WriteMailboxLow != 0) &&
(DSP_ReadControlRegister != 0) &&
(DSP_WriteControlRegister != 0) &&
(DSP_SendAIBuffer != 0) &&
(DSP_Update != 0) &&
(DSP_StopSoundStream != 0))
validDSP = true;
}
PluginDSP::~PluginDSP() {
}
} // namespace

View File

@ -5,33 +5,37 @@
#include "Plugin.h"
namespace Common {
typedef void (__cdecl* TDSP_WriteMailBox)(bool _CPUMailbox, unsigned short);
typedef unsigned short (__cdecl* TDSP_ReadMailBox)(bool _CPUMailbox);
typedef unsigned short (__cdecl* TDSP_ReadControlRegister)();
typedef unsigned short (__cdecl* TDSP_WriteControlRegister)(unsigned short);
typedef void (__cdecl* TDSP_SendAIBuffer)(unsigned int address, int sample_rate);
typedef void (__cdecl* TDSP_Update)(int cycles);
class PluginDSP : public CPlugin
{
public:
typedef void (__cdecl* TDSP_WriteMailBox)(bool _CPUMailbox, unsigned short);
typedef unsigned short (__cdecl* TDSP_ReadMailBox)(bool _CPUMailbox);
typedef unsigned short (__cdecl* TDSP_ReadControlRegister)();
typedef unsigned short (__cdecl* TDSP_WriteControlRegister)(unsigned short);
typedef void (__cdecl* TDSP_SendAIBuffer)(unsigned int address, int sample_rate);
typedef void (__cdecl* TDSP_Update)(int cycles);
typedef void (__cdecl* TDSP_StopSoundStream)();
class PluginDSP : public CPlugin
{
public:
PluginDSP(const char *_Filename);
~PluginDSP();
virtual bool IsValid() {return validDSP;};
TDSP_ReadMailBox DSP_ReadMailboxHigh;
TDSP_ReadMailBox DSP_ReadMailboxLow;
TDSP_WriteMailBox DSP_WriteMailboxHigh;
TDSP_ReadMailBox DSP_ReadMailboxHigh;
TDSP_ReadMailBox DSP_ReadMailboxLow;
TDSP_WriteMailBox DSP_WriteMailboxHigh;
TDSP_WriteMailBox DSP_WriteMailboxLow;
TDSP_ReadControlRegister DSP_ReadControlRegister;
TDSP_WriteControlRegister DSP_WriteControlRegister;
TDSP_SendAIBuffer DSP_SendAIBuffer;
TDSP_SendAIBuffer DSP_SendAIBuffer;
TDSP_Update DSP_Update;
TDSP_StopSoundStream DSP_StopSoundStream;
private:
private:
bool validDSP;
};
}
};
} // namespace
#endif

View File

@ -2,41 +2,42 @@
namespace Common
{
PluginVideo::PluginVideo(const char *_Filename) : CPlugin(_Filename), validVideo(false)
{
PluginVideo::PluginVideo(const char *_Filename) : CPlugin(_Filename), validVideo(false)
{
Video_Prepare = 0;
Video_SendFifoData = 0;
Video_UpdateXFB = 0;
Video_EnterLoop = 0;
Video_ExitLoop = 0;
Video_Screenshot = 0;
Video_AddMessage = 0;
Video_Stop = 0;
Video_Prepare = reinterpret_cast<TVideo_Prepare>
(LoadSymbol("Video_Prepare"));
Video_Prepare = reinterpret_cast<TVideo_Prepare>
(LoadSymbol("Video_Prepare"));
Video_SendFifoData = reinterpret_cast<TVideo_SendFifoData>
(LoadSymbol("Video_SendFifoData"));
Video_UpdateXFB = reinterpret_cast<TVideo_UpdateXFB>
(LoadSymbol("Video_UpdateXFB"));
Video_Screenshot = reinterpret_cast<TVideo_Screenshot>
(LoadSymbol("Video_Screenshot"));
Video_EnterLoop = reinterpret_cast<TVideo_EnterLoop>
(LoadSymbol("Video_EnterLoop"));
Video_AddMessage = reinterpret_cast<TVideo_AddMessage>
(LoadSymbol("Video_AddMessage"));
Video_Stop = reinterpret_cast<TVideo_Stop>
(LoadSymbol("Video_Stop"));
(LoadSymbol("Video_SendFifoData"));
Video_UpdateXFB = reinterpret_cast<TVideo_UpdateXFB>
(LoadSymbol("Video_UpdateXFB"));
Video_Screenshot = reinterpret_cast<TVideo_Screenshot>
(LoadSymbol("Video_Screenshot"));
Video_EnterLoop = reinterpret_cast<TVideo_EnterLoop>
(LoadSymbol("Video_EnterLoop"));
Video_ExitLoop = reinterpret_cast<TVideo_ExitLoop>
(LoadSymbol("Video_ExitLoop"));
Video_AddMessage = reinterpret_cast<TVideo_AddMessage>
(LoadSymbol("Video_AddMessage"));
if ((Video_Prepare != 0) &&
(Video_SendFifoData != 0) &&
(Video_UpdateXFB != 0) &&
(Video_EnterLoop != 0) &&
(Video_Screenshot != 0) &&
(Video_AddMessage != 0) &&
(Video_Stop != 0))
validVideo = true;
}
PluginVideo::~PluginVideo() {}
if ((Video_Prepare != 0) &&
(Video_SendFifoData != 0) &&
(Video_UpdateXFB != 0) &&
(Video_EnterLoop != 0) &&
(Video_ExitLoop != 0) &&
(Video_Screenshot != 0) &&
(Video_AddMessage != 0))
validVideo = true;
}
PluginVideo::~PluginVideo() {}
} // namespace

View File

@ -10,8 +10,8 @@ namespace Common {
typedef void (__cdecl* TVideo_UpdateXFB)(u8*, u32, u32, s32, bool);
typedef bool (__cdecl* TVideo_Screenshot)(const char* filename);
typedef void (__cdecl* TVideo_EnterLoop)();
typedef void (__cdecl* TVideo_ExitLoop)();
typedef void (__cdecl* TVideo_AddMessage)(const char* pstr, unsigned int milliseconds);
typedef void (__cdecl* TVideo_Stop)();
class PluginVideo : public CPlugin
{
@ -22,15 +22,15 @@ namespace Common {
TVideo_Prepare Video_Prepare;
TVideo_SendFifoData Video_SendFifoData;
TVideo_UpdateXFB Video_UpdateXFB;
TVideo_Screenshot Video_Screenshot;
TVideo_EnterLoop Video_EnterLoop;
TVideo_ExitLoop Video_ExitLoop;
TVideo_UpdateXFB Video_UpdateXFB;
TVideo_AddMessage Video_AddMessage;
TVideo_Stop Video_Stop;
TVideo_Screenshot Video_Screenshot;
private:
bool validVideo;
};
}

View File

@ -15,11 +15,9 @@
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#ifndef __SETUP_h__
#define __SETUP_h__
////////////////////////////////////////////////////////////////////////////////////////
// File description
/* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
@ -31,18 +29,10 @@
////////////////////////*/
////////////////////////////////////////////////////////////////////////////////////////
// Settings
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
// This may fix a problem with Stop and Start that I described in the comments to revision 2,139
//#define SETUP_FREE_PLUGIN_ON_BOOT
// This may fix a semi-frequent hanging that occured when I used single core and render to main frame
//#define SETUP_AVOID_SINGLE_CORE_HANG_ON_STOP
// This may remove sound artifacts in Wario Land Shake It and perhaps other games
//#define SETUP_AVOID_SOUND_ARTIFACTS
@ -54,5 +44,4 @@
///////////////////////////
#endif // __SETUP_h__

View File

@ -22,6 +22,12 @@
namespace Common
{
#ifdef _WIN32
void InitThreading()
{
// Nothing to do in Win32 build.
}
CriticalSection::CriticalSection(int spincount)
{
if (spincount)
@ -34,25 +40,21 @@ CriticalSection::CriticalSection(int spincount)
}
}
CriticalSection::~CriticalSection()
{
DeleteCriticalSection(&section);
}
void CriticalSection::Enter()
{
EnterCriticalSection(&section);
}
bool CriticalSection::TryEnter()
{
return TryEnterCriticalSection(&section) ? true : false;
}
void CriticalSection::Leave()
{
LeaveCriticalSection(&section);
@ -71,13 +73,11 @@ Thread::Thread(ThreadFunc function, void* arg)
&m_threadId);
}
Thread::~Thread()
{
WaitForDeath();
}
void Thread::WaitForDeath()
{
if (m_hThread)
@ -88,13 +88,11 @@ void Thread::WaitForDeath()
}
}
void Thread::SetAffinity(int mask)
{
SetThreadAffinityMask(m_hThread, mask);
}
void Thread::SetCurrentThreadAffinity(int mask)
{
SetThreadAffinityMask(GetCurrentThread(), mask);
@ -106,32 +104,27 @@ Event::Event()
m_hEvent = 0;
}
void Event::Init()
{
m_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
}
void Event::Shutdown()
{
CloseHandle(m_hEvent);
m_hEvent = 0;
}
void Event::Set()
{
SetEvent(m_hEvent);
}
void Event::Wait()
{
WaitForSingleObject(m_hEvent, INFINITE);
}
void SleepCurrentThread(int ms)
{
Sleep(ms);
@ -304,12 +297,13 @@ void Thread::SetCurrentThreadAffinity(int mask)
void InitThreading() {
static int thread_init_done = 0;
if (thread_init_done) return;
thread_init_done++;
if (thread_init_done)
return;
if (pthread_key_create(&threadname_key, NULL/*free*/) != 0)
perror("Unable to create thread name key: ");
thread_init_done++;
}
void SleepCurrentThread(int ms)

View File

@ -63,12 +63,11 @@ public:
Thread(ThreadFunc entry, void* arg);
~Thread();
void WaitForDeath();
void SetAffinity(int mask);
static void SetCurrentThreadAffinity(int mask);
private:
void WaitForDeath();
#ifdef _WIN32
HANDLE m_hThread;
@ -81,29 +80,26 @@ private:
class Event
{
public:
public:
Event();
Event();
void Init();
void Shutdown();
void Init();
void Shutdown();
void Set();
void Wait();
private:
void Set();
void Wait();
private:
#ifdef _WIN32
HANDLE m_hEvent;
HANDLE m_hEvent;
#else
bool is_set_;
pthread_cond_t event_;
pthread_mutex_t mutex_;
bool is_set_;
pthread_cond_t event_;
pthread_mutex_t mutex_;
#endif
};
void InitThreading(void);
void InitThreading();
void SleepCurrentThread(int ms);
void SetCurrentThreadName(const char *name);
@ -114,5 +110,4 @@ LONG SyncInterlockedIncrement(LONG *Dest);
} // end of namespace Common
#endif

View File

@ -120,11 +120,12 @@ u64 Timer::GetTimeElapsed(void)
std::string Timer::GetTimeElapsedFormatted(void)
{
// If we have not started yet, return zero
if(m_StartTime == 0) return "00:00:00:000";
if (m_StartTime == 0)
return "00:00:00:000";
// The number of milliseconds since the start, use a different value if the timer is stopped
u32 Milliseconds;
if(m_Running)
if (m_Running)
Milliseconds = timeGetTime() - m_StartTime;
else
Milliseconds = m_LastTime - m_StartTime;