mirror of
https://github.com/SWG-Source/src.git
synced 2026-08-01 01:16:03 -04:00
472 lines
11 KiB
C++
Executable File
472 lines
11 KiB
C++
Executable File
// ======================================================================
|
|
//
|
|
// Os.cpp
|
|
// jeff grills
|
|
//
|
|
// copyright 1998 Bootprint Entertainment
|
|
//
|
|
// ======================================================================
|
|
|
|
#include "sharedFoundation/FirstSharedFoundation.h"
|
|
#include "sharedFoundation/Os.h"
|
|
|
|
#include "sharedFoundation/Clock.h"
|
|
#include "sharedFoundation/ConfigSharedFoundation.h"
|
|
#include "sharedDebug/DebugMonitor.h"
|
|
#include "sharedFoundation/ExitChain.h"
|
|
#include "sharedFoundation/FloatingPointUnit.h"
|
|
#include "sharedIoWin/IoWinManager.h"
|
|
|
|
#include <stack>
|
|
#include <string>
|
|
|
|
// ======================================================================
|
|
|
|
bool Os::installed;
|
|
bool Os::runInBackground;
|
|
int Os::numberOfUpdates;
|
|
int Os::menuValue;
|
|
bool Os::paused;
|
|
bool Os::wasPaused;
|
|
bool Os::gameOver;
|
|
bool Os::shouldReturnFromAbort;
|
|
char Os::programName[PROGRAM_NAME_SIZE];
|
|
char *Os::shortProgramName;
|
|
pthread_t Os::mainThreadId;
|
|
bool Os::threadDied;
|
|
|
|
bool Os::isMp;
|
|
int Os::processorCount;
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
namespace OsNamespace
|
|
{
|
|
class UncatchableException
|
|
{
|
|
};
|
|
}
|
|
|
|
|
|
// ----------------------------------------------------------------------
|
|
/**
|
|
* Install the Os subsystem for non-games.
|
|
*
|
|
* This routine is supported for all platforms, although different platforms may
|
|
* require different arguments to this routine.
|
|
*
|
|
* This routine will add Os::remove to the ExitChain.
|
|
*
|
|
* @see Os::remove()
|
|
*/
|
|
|
|
void Os::install(void)
|
|
{
|
|
installCommon();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
/**
|
|
* This routine will remove the Os subsystem.
|
|
*
|
|
* This routine should not be called directly. It will be called from the ExitChain.
|
|
*
|
|
* @see Os::install()
|
|
*/
|
|
|
|
void Os::remove(void)
|
|
{
|
|
DEBUG_FATAL(!installed, ("not installed"));
|
|
installed = false;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void Os::installCommon(void)
|
|
{
|
|
DEBUG_FATAL(installed, ("already installed"));
|
|
|
|
ExitChain::add(Os::remove, "Os::remove", 0, true);
|
|
|
|
#if 0 //TODO For now we won't screw with the priority of the process
|
|
HANDLE threadHandle = GetCurrentThread();
|
|
DEBUG_FATAL(!SetThreadPriority(threadHandle, THREAD_PRIORITY_ABOVE_NORMAL), ("Failed to set game thread priority"));
|
|
#endif
|
|
|
|
numberOfUpdates = 0;
|
|
mainThreadId = pthread_self();
|
|
|
|
// get the name of the executable
|
|
//Can't find UNIX call for this: DWORD result = GetModuleFileName(nullptr, programName, sizeof(programName));
|
|
strcpy(programName, "TempName");
|
|
DWORD result = 1;
|
|
|
|
|
|
FATAL(result == 0, ("GetModuleFileName failed"));
|
|
|
|
// get the file name without the path
|
|
shortProgramName = strrchr(programName, '\\');
|
|
if (shortProgramName)
|
|
++shortProgramName;
|
|
else
|
|
shortProgramName = programName;
|
|
|
|
// determine the number of processors by parsing /proc/cpuinfo
|
|
processorCount = 1;
|
|
FILE * f = fopen("/proc/cpuinfo", "r");
|
|
if (f)
|
|
{
|
|
char buffer[512];
|
|
while (!feof(f))
|
|
{
|
|
if (fgets(buffer, 512, f) != nullptr) {
|
|
if (strncmp(buffer, "processor\t: ", 12)==0)
|
|
{
|
|
processorCount = atoi(buffer+12)+1;
|
|
}
|
|
}
|
|
}
|
|
fclose(f);
|
|
}
|
|
isMp = processorCount > 1;
|
|
|
|
// switch into single-precision floating point mode
|
|
FloatingPointUnit::install();
|
|
|
|
installed = true;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
bool Os::isMainThread(void)
|
|
{
|
|
// if the Os class hasn't been installed, then assume we are the main thread.
|
|
// otherwise, check to see if our thread id is the main thread id
|
|
return !installed || (pthread_self() == mainThreadId);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
/**
|
|
* Terminate the application because of an error condition.
|
|
*
|
|
* This routine is supported for all platforms.
|
|
*
|
|
* This routine should not be called directly. The engine and game should use the
|
|
* FATAL macro to terminate the application because of an error.
|
|
*
|
|
* Calling Os::returnFromAbort() will cause the routine to do nothing but return
|
|
* immediately.
|
|
*
|
|
* @see Os::returnFromAbort(), FATAL()
|
|
*/
|
|
#include <signal.h>
|
|
void Os::abort(void)
|
|
{
|
|
if (!isMainThread())
|
|
{
|
|
threadDied = true;
|
|
pthread_exit(nullptr);
|
|
}
|
|
|
|
if (!shouldReturnFromAbort)
|
|
{
|
|
// let the C runtime deal with the abnormal termination
|
|
int * dummy = nullptr;
|
|
int forceCrash = *dummy;
|
|
UNREF(forceCrash);
|
|
for (;;)
|
|
{
|
|
// One of these should work:
|
|
pthread_kill(pthread_self(), SIGSEGV);
|
|
::kill(0,SIGSEGV);
|
|
::abort();
|
|
OsNamespace::UncatchableException ex;
|
|
throw ex;
|
|
sleep(10);
|
|
}
|
|
}
|
|
}
|
|
//-----------------------------------------------------------------
|
|
/**
|
|
* Create the specified directory and all of it's parents.
|
|
* @param directory the path to a directory
|
|
* @return always true currently
|
|
*/
|
|
|
|
bool Os::createDirectories (const char *directory)
|
|
{
|
|
//-- construct list of subdirectories all the way down to root
|
|
std::stack<std::string> directoryStack;
|
|
|
|
std::string currentDirectory = directory;
|
|
|
|
static const char path_seps [] = { '\\', '/', 0 };
|
|
|
|
// build the stack
|
|
while (!currentDirectory.empty())
|
|
{
|
|
// remove trailing backslash
|
|
if (currentDirectory[currentDirectory.size()-1] == '\\' || currentDirectory[currentDirectory.size()-1] == '/')
|
|
IGNORE_RETURN(currentDirectory.erase(currentDirectory.size()-1));
|
|
|
|
if (currentDirectory[currentDirectory.size()-1] == ':')
|
|
{
|
|
// we've hit something like c:
|
|
break;
|
|
}
|
|
|
|
if (!currentDirectory.empty())
|
|
directoryStack.push(currentDirectory);
|
|
|
|
// now strip off current directory
|
|
size_t previousDirIndex = currentDirectory.find_last_of (path_seps);
|
|
if (previousDirIndex == currentDirectory.npos)
|
|
break;
|
|
else
|
|
IGNORE_RETURN(currentDirectory.erase(previousDirIndex));
|
|
}
|
|
|
|
//-- build all directories specified by the initial directory
|
|
while (!directoryStack.empty())
|
|
{
|
|
// get the directory
|
|
currentDirectory = directoryStack.top();
|
|
directoryStack.pop();
|
|
|
|
// try to create it (don't pass any security attributes)
|
|
IGNORE_RETURN (mkdir (currentDirectory.c_str(), 0));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
// ----------------------------------------------------------------------
|
|
/**
|
|
* Write out a file.
|
|
*
|
|
* The file name and where the file is written is system-dependent.
|
|
*
|
|
* @param fileName Name of the file to write
|
|
* @param data Data buffer to write to the file
|
|
*/
|
|
|
|
bool Os::writeFile(const char *fileName, const void *data, int length) // Length of the data bufferto write
|
|
{
|
|
BOOL result;
|
|
HANDLE handle;
|
|
DWORD written;
|
|
|
|
// open the file for writing
|
|
handle = CreateFile(fileName, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
|
|
|
|
// check if it was opened
|
|
if (handle == INVALID_HANDLE_VALUE)
|
|
return false;
|
|
|
|
// attempt to write the data
|
|
result = WriteFile(handle, data, static_cast<DWORD>(length), &written, nullptr);
|
|
|
|
// make sure the data was written okay
|
|
if (!result || written != static_cast<DWORD>(length))
|
|
{
|
|
static_cast<void>(CloseHandle(handle));
|
|
return false;
|
|
}
|
|
|
|
// close the file
|
|
result = CloseHandle(handle);
|
|
|
|
// make sure the close was sucessful
|
|
if (!result)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
/**
|
|
* Update the Os subsystem.
|
|
*
|
|
* This routine is supported for all platforms.
|
|
*
|
|
* For the Win* platforms, this routine will process the windows message pump.
|
|
*/
|
|
|
|
bool Os::update(void)
|
|
{
|
|
static int ppid = getppid();
|
|
|
|
FloatingPointUnit::update();
|
|
|
|
++numberOfUpdates;
|
|
|
|
#if 0
|
|
#ifdef _DEBUG
|
|
|
|
if (DEBUG_FLAG_PLATFORM(validateHeap))
|
|
{
|
|
PROFILER_START("validate heap");
|
|
MemoryManager::validate();
|
|
PROFILER_STOP("validate heap");
|
|
}
|
|
|
|
#endif
|
|
#endif
|
|
|
|
Clock::update();
|
|
|
|
wasPaused = false;
|
|
|
|
// if our parent's pid has changed, we tell our caller it should exit
|
|
if (getppid() != ppid)
|
|
{
|
|
WARNING(true, ("Parent process exited!"));
|
|
// reset the parent process id, so we don't keep spamming warnings for processes that don't exit because of this
|
|
ppid = getppid();
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
/**
|
|
* Formats a message error using GetLastError() and FormatMessge().
|
|
*
|
|
* The buffer returned from this function is dynamically allocated to prevent
|
|
* issues with this routine being called from multiple threads. The caller
|
|
* must delete the buffer when it is done.
|
|
*
|
|
* @return A dynamically allocated buffer containing the error message
|
|
*/
|
|
|
|
char *Os::getLastError(void)
|
|
{
|
|
return DuplicateString(strerror(errno));
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
bool Os::getAbsolutePath(const char *relativePath, char *absolutePath, int absolutePathBufferSize)
|
|
{
|
|
// realpath sucks and could cause a buffer overrun. however, it's better than writing it ourselves for now.
|
|
char *result = realpath(relativePath, absolutePath);
|
|
if (!result){
|
|
free(result);
|
|
return false;
|
|
}
|
|
|
|
FATAL(istrlen(absolutePath)+1 > absolutePathBufferSize, ("buffer overrun"));
|
|
return true;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
/**
|
|
* Get the actual system time, in seconds since the epoch.
|
|
*
|
|
* Do not use this for most game systems, since it does not take into account
|
|
* clock sku, game loop times, etc.
|
|
*/
|
|
time_t Os::getRealSystemTime(void)
|
|
{
|
|
return time(0);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
/**
|
|
* Convert a time in seconds since the epoch to GMT.
|
|
*
|
|
*/
|
|
|
|
void Os::convertTimeToGMT(const time_t &time, tm &zulu)
|
|
{
|
|
zulu=*gmtime(&time); // gmtime uses a single static tm structure. Yuck!
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
/**
|
|
* Convert a tm structure to the time in seconds since the epoch.
|
|
*
|
|
*/
|
|
|
|
time_t Os::convertGMTToTime(const tm &zulu)
|
|
{
|
|
return mktime(const_cast<tm*>(&zulu));
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
/**
|
|
* Get a unique identifier for this thread.
|
|
*
|
|
* @return A unique identifier for this thread.
|
|
*/
|
|
|
|
Os::ThreadId Os::getThreadId()
|
|
{
|
|
return pthread_self();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
/**
|
|
* Assign the given thread a reasonable name (only works for MSDev 6.0 debugger)
|
|
* Since this is Linux-specific, make sure this fuction does nothing
|
|
*
|
|
*/
|
|
|
|
void Os::setThreadName(DWORD threadID, const char* threadName)
|
|
{
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
/**
|
|
* Cause the current thread to sleep for a period of time. If the period is
|
|
* zero, the current thread yields it's timeslice.
|
|
*/
|
|
|
|
void Os::sleep(int ms)
|
|
{
|
|
if (ms == 0)
|
|
{
|
|
sched_yield();
|
|
}
|
|
else
|
|
{
|
|
usleep(ms * 1000);
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
/**
|
|
* Copy text to the system clipboard.
|
|
*
|
|
* This routine can be used if the application wants to make some data easily available for pasting (like crash call stacks).
|
|
*/
|
|
|
|
bool Os::copyTextToClipboard(const char *text)
|
|
{
|
|
UNREF(text);
|
|
return false;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
Os::OsPID_t Os::getProcessId()
|
|
{
|
|
return getpid();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void Os::setProgramName(const char * name)
|
|
{
|
|
strncpy(programName, name, Os::PROGRAM_NAME_SIZE);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
bool Os::isFocused()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|