Added some utility files

Tested to be working although INIFile is currently a windows-only utility and so is currently removed from the project (but still in the repo as it may be re-written to be cross-platform)
Gui_Panel_Refactor
Joeyrp 4 years ago
parent c0c1a48e22
commit fd424abd16

@ -12,6 +12,8 @@ configure_file(LunariumConfig.h.in LunariumConfig.h)
# Source Files # Source Files
set(LUNARIUM_SRC set(LUNARIUM_SRC
"src/test_main.cpp" "src/test_main.cpp"
"src/utils/Logger.cpp"
"src/utils/HighResTimer.cpp"
) )
# add the executable # add the executable

@ -5,6 +5,8 @@
#include <imgui_impl_glfw.h> #include <imgui_impl_glfw.h>
#include <imgui_impl_opengl3.h> #include <imgui_impl_opengl3.h>
#include <pugixml.hpp> #include <pugixml.hpp>
#include "utils/INIFile.h"
#include "utils/Logger.h"
extern "C" extern "C"
{ {
@ -67,6 +69,13 @@ int main(int argc, char** argv)
std::cout << "\nPugiXML worked! -- x is: " << x; std::cout << "\nPugiXML worked! -- x is: " << x;
} }
// UTILS TEST
// NOTE: this works but is windows only so it won't be used in the engine
// unless INIFile is re-written to be cross-platform
// Lunarium::INIFile file("../test_data/test.ini");
// int meaning = file.ReadInt("section", "var");
// std::cout << "\nvar from test.ini is: " << meaning;
// GLFW DEAR IMGUI TEST // GLFW DEAR IMGUI TEST
GLFWwindow* window; GLFWwindow* window;
int width, height; int width, height;

@ -0,0 +1,65 @@
/******************************************************************************
* Filename: HighResTimer.cpp
* Date: 12/06/2010
* Mod. Date: 03/21/2018
* Author: Joseph R. Pollack
* Purpose: A high resolution timer suitable for profiling code. This new
* version has stripped out the game specific functionality and
* does not include windows.h in the header.
******************************************************************************/
#include "HighResTimer.h"
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
namespace Lunarium
{
HighResTimer::HighResTimer()
{
Initialize();
}
void HighResTimer::Initialize()
{
mStartTime = 0;
LARGE_INTEGER tickFreq;
QueryPerformanceFrequency(&tickFreq);
mTickFrequency = tickFreq.QuadPart;
}
// Starts or re-starts the timer.
void HighResTimer::Reset()
{
LARGE_INTEGER tickStart;
QueryPerformanceCounter(&tickStart);
mStartTime = tickStart.QuadPart;
}
// Returns the amount of time in microseconds since the last Reset() call.
int64_t HighResTimer::GetElapsedTime()
{
LARGE_INTEGER ticks;
QueryPerformanceCounter(&ticks);
int64_t elapsedMicro = ticks.QuadPart - mStartTime;
elapsedMicro *= mcMils;
elapsedMicro /= mTickFrequency;
return elapsedMicro;
}
double HighResTimer::GetElapsedSeconds()
{
int64_t emicro = GetElapsedTime();
double dmicro = ((double)emicro);
double dmcMils = ((double)mcMils);
double seconds = dmicro / dmcMils;
return seconds;
}
}

@ -0,0 +1,60 @@
/******************************************************************************
* Filename: HighResTimer.h
* Date: 12/06/2010
* Mod. Date: 03/21/2018
* Author: Joseph R. Pollack
* Purpose: A high resolution timer suitable for profiling code. This new
* version has stripped out the game specific functionality and
* does not include windows.h in the header.
******************************************************************************/
#ifndef HIGH_RES_TIMER_H_
#define HIGH_RES_TIMER_H_
#include <stdint.h>
namespace Lunarium
{
class HighResTimer
{
public:
// NOTE: Currently unused
enum eUnits { SECONDS, MILLISECONDS, MICROSECONDS, NANOSECONDS, PICOSECONDS };
public:
HighResTimer();
void Initialize();
// Starts or re starts the timer.
void Reset();
// Returns the amount of time in microseconds since the last Reset() call.
int64_t GetElapsedTime();
// Returns the elapsed time in terms of seconds
double GetElapsedSeconds();
private:
int64_t mStartTime;
int64_t mTickFrequency;
const int mcMils = 1000000;
const int mcBils = 1000000000;
const long long mcTrils = 1000000000000;
/* // OLD MEMBERS
long long m_TickFreqQuadPart;
double m_StartTime;
*/
};
}
#endif // HIGH_RES_TIMER_H_

@ -0,0 +1,109 @@
/******************************************************************************
* File - INIFile.h
* Author - Joey Pollack
* Date - 2017/11/28 (y/m/d)
* Mod Date - 2017/11/28 (y/m/d)
* Description - Read/Write from a .ini file
******************************************************************************/
#include "INIFile.h"
// TODO: This needs to be re-written without the windows-specific functions
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <algorithm>
#include <stdexcept>
namespace Lunarium
{
INIFile::INIFile(const char * filename)
{
mFullPath = filename;
}
INIFile::~INIFile()
{
}
std::string INIFile::Read(const char* section, const char* key)
{
char buffer[256];
GetPrivateProfileStringA(section, key, nullptr, buffer, 256, mFullPath.c_str());
return std::string(buffer);
}
int INIFile::ReadInt(const char* section, const char* key)
{
int val = GetPrivateProfileIntA(section, key, -1, mFullPath.c_str());
return val;
}
bool INIFile::ReadBool(const char* section, const char* key)
{
std::string buffer = Read(section, key);
if (buffer.size() < 1)
throw std::runtime_error("ReadBool() Failed: buffer was empty!");
std::transform(buffer.begin(), buffer.end(), buffer.begin(), ::tolower);
if (buffer == std::string("false"))
return false;
if (buffer == std::string("true"))
return true;
throw std::runtime_error("ReadBool() Failed: buffer not a boolean value!");
return false;
}
double INIFile::ReadDouble(const char* section, const char* key)
{
throw "Not implemented!";
return 0.0;
}
void INIFile::Write(const char * section, const char * key, const char * value)
{
WritePrivateProfileStringA(section, key, value, mFullPath.c_str());
}
void INIFile::WriteInt(const char * section, const char * key, int value)
{
std::string strVal = std::to_string(value);
WritePrivateProfileStringA(section, key, strVal.c_str(), mFullPath.c_str());
}
void INIFile::WriteBool(const char * section, const char * key, bool value)
{
std::string strVal = "true";
if (!value)
{
strVal = "false";
}
WritePrivateProfileStringA(section, key, strVal.c_str(), mFullPath.c_str());
}
void INIFile::WriteDouble(const char * section, const char * key, double value)
{
throw "INIFile::WriteDouble Not implemented!";
}
bool INIFile::KeyExists(const char* section, const char* key)
{
return Read(section, key).length() > 0;
}
void INIFile::DeleteKey(const char* section, const char* key)
{
Write(section, key, nullptr);
}
void INIFile::DeleteSection(const char * section)
{
Write(section, nullptr, nullptr);
}
}

@ -0,0 +1,54 @@
/******************************************************************************
* File - INIFile.h
* Author - Joey Pollack
* Date - 2017/11/28 (y/m/d)
* Mod Date - 2017/11/28 (y/m/d)
* Description - Read/Write from a .ini file
* Based on the C# class writen by Danny Beckett and released
* to public domain:
* https://stackoverflow.com/questions/217902/reading-writing-an-ini-file
******************************************************************************/
#ifndef _INI_FILE_H_
#define _INI_FILE_H_
#include <string>
namespace Lunarium
{
class INIFile
{
public:
// filename must be a full, absolute path to the .ini file.
INIFile(const char* filename);
~INIFile();
// Read methods
std::string Read(const char* section, const char* key);
int ReadInt(const char* section, const char* key);
bool ReadBool(const char* section, const char* key);
double ReadDouble(const char* section, const char* key);
// Write methods
void Write(const char * section, const char* key, const char* value);
void WriteInt(const char * section, const char* key, int value);
void WriteBool(const char * section, const char* key, bool value);
void WriteDouble(const char * section, const char* key, double value);
// Management
bool KeyExists(const char* section, const char* key);
void DeleteKey(const char* section, const char* key);
void DeleteSection(const char* section);
private:
bool mbFileOpen;
std::string mFullPath;
};
}
#endif // _INI_FILE_H_

@ -0,0 +1,414 @@
/******************************************************************************
* File - Logger.h
* Author - Joey Pollack
* Date - 2019/06/24 (y/m/d)
* Mod Date - 2019/06/26 (y/m/d)
* Description - Main interface to the logging system. Manages a linked
* list of built-in and user-defined listerers.
******************************************************************************/
#include "Logger.h"
#include <iostream>
#include <sstream>
#include <iomanip>
#include <time.h>
#include <stdarg.h>
namespace Lunarium
{
//namespace LogLevel
//{
// bool LevelIsSet(uint16_t buf, uint16_t mask)
// {
// return buf & mask;
// }
// std::string FormatLevel(uint16_t level)
// {
// if (LevelIsSet(level, FATAL_ERROR))
// {
// return "[FATAL ERROR] ";
// }
// else if (LevelIsSet(level, ERROR))
// {
// return "[ERROR] ";
// }
// else if (LevelIsSet(level, WARNING))
// {
// return "[WARNING] ";
// }
// else if (LevelIsSet(level, INFO))
// {
// return "[INFO] ";
// }
// else if (LevelIsSet(level, INFO_VERBOSE))
// {
// return "[INFO_VERBOSE] ";
// }
// else if (LevelIsSet(level, OGL_DEBUG))
// {
// return "[OGL_DEBUG] ";
// }
// else
// {
// return "[UNKNOWN] ";
// }
// }
//}
//namespace LogCategory
//{
// bool CategoryIsSet(uint16_t buf, uint16_t mask)
// {
// return buf & mask;
// }
// std::string FormatCategory(uint16_t level)
// {
// if (CategoryIsSet(level, CORE))
// {
// return "[CORE] ";
// }
// else if (CategoryIsSet(level, WINDOW_CONTROLLER))
// {
// return "[WINDOW CONTROLLER] ";
// }
// else if (CategoryIsSet(level, GRAPHICS))
// {
// return "[GRAPHICS] ";
// }
// else if (CategoryIsSet(level, UTILITIES))
// {
// return "[UTILITIES] ";
// }
// else if (CategoryIsSet(level, USER_PROJECT))
// {
// return "[USER PROJECT] ";
// }
// else
// {
// return "[UNKNOWN] ";
// }
// }
//}
LogListener::LogListener(uint32_t acceptedLogLevels, uint32_t acceptedLogCategories, const char* myName)
: mAcceptedLogLevels(acceptedLogLevels), mAcceptedLogCategories(acceptedLogCategories), mMyName(myName)
{
}
const std::string & LogListener::GetName() const
{
return mMyName;
}
bool LogListener::CategoryIsSet(uint32_t id) const
{
return mAcceptedLogCategories & id;
}
bool LogListener::LevelIsSet(uint32_t id) const
{
return mAcceptedLogLevels & id;
}
// ////////////////////////////////////////////////////////////////////////
// ========================================================================
// Standard Listener
// ========================================================================
// ////////////////////////////////////////////////////////////////////////
StandardListener::StandardListener(uint32_t acceptedLogLevels, uint32_t acceptedLogCategories, const char* myName)
: LogListener(acceptedLogLevels, acceptedLogCategories, myName)
{
}
bool StandardListener::Log(LogMessage& message)
{
if (!LevelIsSet(message.LogLevel) ||
!CategoryIsSet(message.LogCategory))
return false;
std::cout << std::endl << Logger::TimeStamp() << Logger::GetCategoryName(message.LogCategory)
<< Logger::GetLevelName(message.LogLevel) << message.Message;
return true;
}
// ////////////////////////////////////////////////////////////////////////
// ========================================================================
// Error Listener
// ========================================================================
// ////////////////////////////////////////////////////////////////////////
ErrorListener::ErrorListener(uint32_t acceptedLogLevels, uint32_t acceptedLogCategories, const char* myName)
: LogListener(acceptedLogLevels, acceptedLogCategories, myName)
{
}
bool ErrorListener::Log(LogMessage& message)
{
if (!LevelIsSet(message.LogLevel) ||
!CategoryIsSet(message.LogCategory))
return false;
std::cerr << std::endl << Logger::TimeStamp() << Logger::GetCategoryName(message.LogCategory)
<< Logger::GetLevelName(message.LogLevel) << message.Message;
return true;
}
// ////////////////////////////////////////////////////////////////////////
// ========================================================================
// File Listener
// ========================================================================
// ////////////////////////////////////////////////////////////////////////
FileListener::FileListener(std::ofstream & of, uint32_t acceptedLogLevels, uint32_t acceptedLogCategories, const char* myName)
: mOF(of), LogListener(acceptedLogLevels, acceptedLogCategories, myName)
{
}
bool FileListener::Log(LogMessage& message)
{
if (!LevelIsSet(message.LogLevel) ||
!CategoryIsSet(message.LogCategory))
return false;
mOF << std::endl << Logger::TimeStamp() << Logger::GetCategoryName(message.LogCategory)
<< Logger::GetLevelName(message.LogLevel) << message.Message;
return true;
}
// ////////////////////////////////////////////////////////////////////////
// ========================================================================
// Main Logger
// ========================================================================
// ////////////////////////////////////////////////////////////////////////
Logger* Logger::mInstance = nullptr;
uint32_t Logger::mNextCategoryID = 0x01;
uint32_t Logger::mNextLevelID = 0x01;
std::map<uint32_t, std::string> Logger::mCategoryNames;
std::map<uint32_t, std::string> Logger::mLevelNames;
std::string Logger::TimeStamp()
{
time_t t = time(0);
struct tm now;
localtime_s(&now, &t);
std::ostringstream oss("");
oss << "[" << (now.tm_year + 1900) << "/";
oss << std::setfill('0') << std::setw(2);
oss << (now.tm_mon + 1) << "/"
<< std::setw(2) << now.tm_mday << " -- "
<< std::setw(2) << now.tm_hour << ":"
<< std::setw(2) << now.tm_min << ":"
<< std::setw(2) << now.tm_sec << "] ";
return oss.str();
}
Logger::Logger()
{
// Register built-in levels
RegisterLevel("INFO");
RegisterLevel("WARNING");
RegisterLevel("ERROR");
RegisterLevel("FATAL_ERROR");
RegisterLevel("INFO_VERBOSE");
RegisterLevel("OGL_DEBUG");
RegisterCategory("CORE");
RegisterCategory("WINDOW_CONTROLLER");
RegisterCategory("GRAPHICS");
RegisterCategory("UTILITIES");
}
Logger * Logger::GetInstance()
{
if (!Logger::mInstance)
{
Logger::mInstance = new Logger;
Logger::mInstance->mTimer.Reset();
}
return Logger::mInstance;
}
void Logger::FreeInstance()
{
delete mInstance;
mInstance = nullptr;
Logger::mNextCategoryID = 0x01;
Logger::mNextLevelID = 0x01;
Logger::mCategoryNames.clear();
Logger::mLevelNames.clear();
}
uint32_t Logger::RegisterCategory(const char * name)
{
Logger::mCategoryNames[mNextCategoryID] = name;
uint32_t id = mNextCategoryID;
mNextCategoryID = mNextCategoryID << 1;
return id;
}
uint32_t Logger::RegisterLevel(const char * name)
{
Logger::mLevelNames[mNextLevelID] = name;
uint32_t id = mNextLevelID;
mNextLevelID = mNextLevelID << 1;
return id;
}
std::string Logger::GetCategoryName(uint32_t id)
{
if (Logger::mCategoryNames.find(id) == Logger::mCategoryNames.end())
return "[]";
std::string name = "[";
name += Logger::mCategoryNames[id];
name += "] ";
return name;
}
std::string Logger::GetLevelName(uint32_t id)
{
if (Logger::mLevelNames.find(id) == Logger::mLevelNames.end())
return "[]";
std::string name = "[";
name += Logger::mLevelNames[id];
name += "] ";
return name;
}
LogListener* Logger::AddListener(LogListener * pl)
{
mListeners.push_back(pl);
return pl;
}
LogListener* Logger::RemoveListener(LogListener * pl)
{
mListeners.remove(pl);
return pl;
}
LogListener* Logger::RemoveListener(const char * withName)
{
LogListener* pl = nullptr;
for (auto iter = mListeners.begin(); iter != mListeners.end(); iter++)
{
if ((*iter)->GetName() == withName)
{
pl = (*iter);
break;
}
}
mListeners.remove(pl);
return pl;
}
void Logger::FreeAllListeners()
{
for (auto iter = mListeners.begin(); iter != mListeners.end(); iter++)
{
delete (*iter);
}
mListeners.clear();
}
void Logger::ClearBacklog()
{
mBacklog.clear();
}
void Logger::Log(uint32_t logCategory, uint32_t logLevel, const char * message, ...)
{
// clear the buffer
memset(mBuffer, 0, 1024);
// Fill the buffer with the formatted message
va_list args;
va_start(args, message);
vsprintf_s(mBuffer, BUFFER_SIZE, message, args);
va_end(args);
double timeStamp = mTimer.GetElapsedSeconds();
LogMessage lm;
lm.LogCategory = logCategory;
lm.LogLevel = logLevel;
lm.Message = mBuffer;
lm.Repeats = 0;
lm.RepeatAlertSent = false;
lm.SentTimeStamp = timeStamp;
bool send = true;
bool track = true;
// Check for repeats
for (int i = 0; i < mBacklog.size(); i++)
{
LogMessage& prevMsg = mBacklog[i];
if (prevMsg.Message == lm.Message)
{
if (prevMsg.RepeatAlertSent)
{
send = false;
break;
}
// only suppress the messsage if it has been less than
// 1 second since it was last sent
double timeSinceLastSend = timeStamp - prevMsg.SentTimeStamp;
if (timeSinceLastSend <= 1.0)
{
prevMsg.Repeats += 1;
if (prevMsg.Repeats > 100 && !prevMsg.RepeatAlertSent)
{
lm.Message = lm.Message + " [This message has rapidly repeated over 100 times! No more repeats will be sent out!]";
prevMsg.RepeatAlertSent = true;
track = false;
}
else
{
send = false;
}
}
prevMsg.SentTimeStamp = timeStamp;
break;
}
}
if (!send)
return;
// Send to all listeners
for (auto iter = mListeners.begin(); iter != mListeners.end(); iter++)
{
if ((*iter)->Log(lm))
{
// Track which listeners handle the message
lm.HandledBy.push_back((*iter)->GetName());
}
}
if (track)
mBacklog.push_back(lm);
}
const std::vector<LogMessage>& Logger::GetBacklog() const
{
return mBacklog;
}
}

@ -0,0 +1,182 @@
/******************************************************************************
* File - Logger.h
* Author - Joey Pollack
* Date - 2019/06/24 (y/m/d)
* Mod Date - 2019/06/26 (y/m/d)
* Description - Main interface to the logging system. Manages a linked
* list of built-in and user-defined listerers.
******************************************************************************/
#ifndef LOGGER_H_
#define LOGGER_H_
#include <cstdint>
#include <string>
#include <list>
#include <vector>
#include <map>
#include <fstream>
// TODO: Remove the dependence on HighResTimer.h
// OR re-write HighResTimer with modern timer code
#include "HighResTimer.h"
// Conflict with a region flag in wingdi.h
#undef ERROR
namespace Lunarium
{
namespace LogLevel
{
const uint16_t ANY = 0xFFFF;
const uint16_t INFO = 0x0001;
const uint16_t WARNING = 0x0002;
const uint16_t ERROR = 0x0004;
const uint16_t FATAL_ERROR = 0x0008;
const uint16_t INFO_VERBOSE = 0x0010;
const uint16_t OGL_DEBUG = 0x0020;
}
namespace LogCategory
{
const uint16_t ANY = 0xFFFF;
const uint16_t CORE = 0x0001;
const uint16_t WINDOW_CONTROLLER = 0x0002;
const uint16_t GRAPHICS = 0x0004;
const uint16_t UTILITIES = 0x0008;
//const uint16_t USER_PROJECT = 0x0010;
}
struct LogMessage
{
// The ID of the category
uint32_t LogCategory;
// The ID of the Level
uint32_t LogLevel;
std::string Message;
std::vector<std::string> HandledBy;
uint32_t Repeats;
bool RepeatAlertSent;
double SentTimeStamp;
};
// ////////////////////////////////////////////////////////////////////////
// ========================================================================
// LISTENER CLASSES
// ========================================================================
// ////////////////////////////////////////////////////////////////////////
// Log Listener interface class
class LogListener
{
public:
LogListener(uint32_t acceptedLogLevels = LogLevel::ANY, uint32_t acceptedLogCategories = LogLevel::ANY, const char* myName = "UNKNOWN");
virtual bool Log(LogMessage& message) = 0;
const std::string& GetName() const;
bool CategoryIsSet(uint32_t id) const;
bool LevelIsSet(uint32_t id) const;
protected:
uint32_t mAcceptedLogCategories;
uint32_t mAcceptedLogLevels;
std::string mMyName;
};
// Outputs all (by default) messages to std::cout
// Adds a time stamp and the log level to the message.
class StandardListener : public LogListener
{
public:
StandardListener(uint32_t acceptedLogLevels = LogLevel::ANY,
uint32_t acceptedLogCategories = LogCategory::ANY, const char* myName = "StandardListener");
bool Log(LogMessage& message);
};
// Outputs Errors and Fatal Errors to std::err
// Adds a time stamp and the log level to the message.
class ErrorListener : public LogListener
{
public:
ErrorListener(uint32_t acceptedLogLevels = LogLevel::ERROR | LogLevel::FATAL_ERROR,
uint32_t acceptedLogCategories = LogCategory::ANY, const char* myName = "ErrorListener");
bool Log(LogMessage& message);
};
// Outputs all (by default) messages to the given std::ofstream.
// Adds a time stamp and the log level to the message.
class FileListener : public LogListener
{
public:
FileListener(std::ofstream& of, uint32_t acceptedLogLevels = LogLevel::ANY,
uint32_t acceptedLogCategories = LogCategory::ANY, const char* myName = "FileListener");
bool Log(LogMessage& message);
private:
std::ofstream& mOF;
};
#define BUFFER_SIZE 1024
// ////////////////////////////////////////////////////////////////////////
// ========================================================================
// MAIN LOGGERCLASS
// ========================================================================
// ////////////////////////////////////////////////////////////////////////
class Logger
{
public:
static std::string TimeStamp();
static Logger* GetInstance();
static void FreeInstance();
static uint32_t RegisterCategory(const char* name);
static uint32_t RegisterLevel(const char* name);
static std::string GetCategoryName(uint32_t id);
static std::string GetLevelName(uint32_t id);
LogListener* AddListener(LogListener* pl);
// This method does NOT delete the listener's memory!
LogListener* RemoveListener(LogListener* pl);
// This method does NOT delete the listener's memory!
LogListener* RemoveListener(const char* withName);
// Calls delete on all listeners and clears the list
void FreeAllListeners();
void ClearBacklog();
// The message argument and the variable argument list work just like
// printf. Use the same formatters as printf when calling this function.
// Messages must be shorter than 1024 bytes.
void Log(uint32_t logCategory, uint32_t logLevel, const char* message, ...);
const std::vector<LogMessage>& GetBacklog() const;
private:
static Logger* mInstance;
static uint32_t mNextCategoryID;
static uint32_t mNextLevelID;
static std::map<uint32_t, std::string> mLevelNames;
static std::map<uint32_t, std::string> mCategoryNames;
std::list<LogListener*> mListeners;
std::vector<LogMessage> mBacklog;
char mBuffer[BUFFER_SIZE];
HighResTimer mTimer;
private:
Logger();
Logger(const Logger&) = delete;
const Logger& operator=(const Logger&) = delete;
};
}
#endif // LOGGER_H_

@ -0,0 +1,2 @@
[section]
var=42
Loading…
Cancel
Save