mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-09 04:49:39 -10:00
332 lines
7.8 KiB
C++
332 lines
7.8 KiB
C++
/*
|
|
* Copyright (C) Volition, Inc. 1999. All rights reserved.
|
|
*
|
|
* All source code herein is the property of Volition, Inc. You may not sell
|
|
* or otherwise commercially exploit the source or things you created based on the
|
|
* source.
|
|
*
|
|
*/
|
|
|
|
#include <cstdio>
|
|
#include <cstdarg>
|
|
#include <cstring>
|
|
#include <algorithm>
|
|
|
|
#ifdef WIN32
|
|
#include <direct.h>
|
|
#endif
|
|
|
|
#include "osapi/DebugWindow.h"
|
|
#include "osapi/osapi.h"
|
|
#include "osapi/outwnd.h"
|
|
#include "graphics/2d.h"
|
|
#include "freespaceresource.h"
|
|
#include "globalincs/systemvars.h"
|
|
#include "cfile/cfilesystem.h"
|
|
#include "parse/parselo.h"
|
|
|
|
static const char *FILTERS_ENABLED_BY_DEFAULT[] =
|
|
{
|
|
"error",
|
|
"warning",
|
|
"general",
|
|
"scripting"
|
|
};
|
|
|
|
struct outwnd_filter_struct {
|
|
char name[NAME_LENGTH];
|
|
bool enabled;
|
|
};
|
|
|
|
// This technique is necessary to avoid a crash in FRED. See commit e624f6c1.
|
|
static SCP_vector<outwnd_filter_struct>& filter_vector()
|
|
{
|
|
static SCP_vector<outwnd_filter_struct> vec;
|
|
return vec;
|
|
}
|
|
|
|
// used for file logging
|
|
bool Log_debug_output_to_file = true;
|
|
|
|
void outwnd_print(const char* id = nullptr, const char* temp = nullptr);
|
|
|
|
// 0 = .cfg file found, 1 = not found and warning not printed yet, 2 = not found and warning printed
|
|
static ubyte Outwnd_no_filter_file = 0;
|
|
|
|
static ubyte outwnd_filter_loaded = 0;
|
|
static bool outwnd_inited = false;
|
|
|
|
static FILE* Log_fp = nullptr;
|
|
static bool Log_close_fp = false;
|
|
static const char* FreeSpace_logfilename = nullptr;
|
|
|
|
static std::unique_ptr<osapi::DebugWindow> debugWindow;
|
|
|
|
void load_filter_info()
|
|
{
|
|
FILE* fp;
|
|
char pathname[MAX_PATH_LEN];
|
|
char inbuf[NAME_LENGTH + 4];
|
|
outwnd_filter_struct* filter_ptr;
|
|
|
|
// add default-enabled filters
|
|
for (const char *name : FILTERS_ENABLED_BY_DEFAULT) {
|
|
filter_vector().emplace_back();
|
|
filter_ptr = &filter_vector().back();
|
|
|
|
strcpy_s(filter_ptr->name, name);
|
|
filter_ptr->enabled = true;
|
|
}
|
|
|
|
outwnd_filter_loaded = 1;
|
|
|
|
memset(pathname, 0, sizeof(pathname));
|
|
snprintf(pathname, MAX_PATH_LEN, "%s/%s", Pathtypes[CF_TYPE_DATA].path, NOX("debug_filter.cfg"));
|
|
|
|
fp = fopen(os_get_config_path(pathname).c_str(), "rt");
|
|
|
|
if (!fp) {
|
|
Outwnd_no_filter_file = 1;
|
|
return;
|
|
}
|
|
|
|
Outwnd_no_filter_file = 0;
|
|
|
|
while ( fgets(inbuf, NAME_LENGTH+3, fp) ) {
|
|
bool enabled;
|
|
|
|
if (*inbuf == '+')
|
|
enabled = true;
|
|
else if (*inbuf == '-')
|
|
enabled = false;
|
|
else
|
|
continue; // skip everything else
|
|
|
|
auto z = strlen(inbuf) - 1;
|
|
if (inbuf[z] == '\n')
|
|
inbuf[z] = 0;
|
|
|
|
Assert( strlen(inbuf+1) < NAME_LENGTH );
|
|
const char *name = &inbuf[1];
|
|
|
|
// find it, or create it
|
|
filter_ptr = nullptr;
|
|
for (auto &filter : filter_vector()) {
|
|
if (!stricmp(filter.name, name)) {
|
|
filter_ptr = &filter;
|
|
break;
|
|
}
|
|
}
|
|
if (filter_ptr == nullptr) {
|
|
filter_vector().emplace_back();
|
|
filter_ptr = &filter_vector().back();
|
|
strcpy_s(filter_ptr->name, name);
|
|
}
|
|
|
|
// set it as enabled or not, depending on config
|
|
filter_ptr->enabled = enabled;
|
|
}
|
|
|
|
if ( ferror(fp) && !feof(fp) )
|
|
nprintf(("Error", "Error reading \"%s\"\n", pathname));
|
|
|
|
fclose(fp);
|
|
}
|
|
|
|
void save_filter_info()
|
|
{
|
|
FILE* fp;
|
|
char pathname[MAX_PATH_LEN];
|
|
|
|
if (!outwnd_filter_loaded)
|
|
return;
|
|
|
|
if (Outwnd_no_filter_file)
|
|
return; // No file, don't save
|
|
|
|
memset(pathname, 0, sizeof(pathname));
|
|
snprintf(pathname, MAX_PATH_LEN, "%s/%s", Pathtypes[CF_TYPE_DATA].path, NOX("debug_filter.cfg"));
|
|
|
|
fp = fopen(os_get_config_path(pathname).c_str(), "wt");
|
|
|
|
if (fp) {
|
|
for (auto& i : filter_vector()) {
|
|
fprintf(fp, "%c%s\n", i.enabled ? '+' : '-', i.name);
|
|
}
|
|
|
|
fclose(fp);
|
|
}
|
|
}
|
|
|
|
void outwnd_printf2(const char *format, ...)
|
|
{
|
|
SCP_string temp;
|
|
va_list args;
|
|
|
|
if (format == nullptr)
|
|
return;
|
|
|
|
va_start(args, format);
|
|
vsprintf(temp, format, args);
|
|
va_end(args);
|
|
|
|
outwnd_print("General", temp.c_str());
|
|
}
|
|
|
|
void outwnd_printf(const char *id, const char *format, ...)
|
|
{
|
|
SCP_string temp;
|
|
va_list args;
|
|
|
|
if ((id == nullptr) || (format == nullptr))
|
|
return;
|
|
|
|
va_start(args, format);
|
|
vsprintf(temp, format, args);
|
|
va_end(args);
|
|
|
|
outwnd_print(id, temp.c_str());
|
|
}
|
|
|
|
void outwnd_print(const char *id, const char *tmp)
|
|
{
|
|
if (running_unittests) {
|
|
// Ignore all messages when running unit tests
|
|
return;
|
|
}
|
|
|
|
if ((id == nullptr) || (tmp == nullptr))
|
|
return;
|
|
|
|
if (!outwnd_inited)
|
|
return;
|
|
|
|
if (Outwnd_no_filter_file == 1) {
|
|
Outwnd_no_filter_file = 2;
|
|
|
|
outwnd_print("general", "==========================================================================\n");
|
|
outwnd_print("general", "DEBUG SPEW: No debug_filter.cfg found, so only general, error, and warning\n");
|
|
outwnd_print("general", "categories can be shown and no debug_filter.cfg info will be saved.\n");
|
|
outwnd_print("general", "==========================================================================\n");
|
|
}
|
|
|
|
auto filter = std::find_if(filter_vector().begin(), filter_vector().end(), [&id] (const outwnd_filter_struct& f) { return stricmp(f.name, id) == 0; });
|
|
|
|
// id found that isn't in the filter list yet
|
|
if ( filter == filter_vector().end() ) {
|
|
// Only create new filters if there was a filter file
|
|
if (Outwnd_no_filter_file)
|
|
return;
|
|
|
|
Assert( strlen(id)+1 < NAME_LENGTH );
|
|
outwnd_filter_struct new_filter;
|
|
|
|
strcpy_s(new_filter.name, id);
|
|
new_filter.enabled = stricmp(new_filter.name, "general") == 0 || stricmp(new_filter.name, "error") == 0 || stricmp(new_filter.name, "warning") == 0;
|
|
|
|
filter_vector().push_back( new_filter );
|
|
save_filter_info();
|
|
}
|
|
else if (!filter->enabled)
|
|
return;
|
|
|
|
if (Log_debug_output_to_file) {
|
|
if (Log_fp != nullptr) {
|
|
fputs(tmp, Log_fp);
|
|
fflush(Log_fp);
|
|
}
|
|
}
|
|
|
|
if (debugWindow) {
|
|
debugWindow->addDebugMessage(id, tmp);
|
|
}
|
|
}
|
|
|
|
extern const char* Osapi_legacy_mode_reason; // This was not exported in a header since it's not intended for general use
|
|
|
|
void outwnd_init()
|
|
{
|
|
if (outwnd_inited)
|
|
return;
|
|
|
|
if (!running_unittests && Log_fp == nullptr) {
|
|
SCP_string logpath;
|
|
if (!Cmdline_log_to_stdout) {
|
|
char pathname[MAX_PATH_LEN];
|
|
|
|
/* Set where the log file is going to go */
|
|
// Zacam: Set various conditions based on what type of log to generate.
|
|
if (Fred_running) {
|
|
FreeSpace_logfilename = "fred2_open.log";
|
|
} else if (Is_standalone) {
|
|
FreeSpace_logfilename = "fs2_standalone.log";
|
|
} else {
|
|
FreeSpace_logfilename = "fs2_open.log";
|
|
}
|
|
|
|
// create data file path if it does not exist
|
|
_mkdir(os_get_config_path(Pathtypes[CF_TYPE_DATA].path).c_str());
|
|
|
|
memset(pathname, 0, sizeof(pathname));
|
|
snprintf(pathname, MAX_PATH_LEN, "%s/%s", Pathtypes[CF_TYPE_DATA].path, FreeSpace_logfilename);
|
|
|
|
logpath = os_get_config_path(pathname);
|
|
|
|
Log_fp = fopen(logpath.c_str(), "wb");
|
|
Log_close_fp = true;
|
|
} else {
|
|
Log_fp = stdout;
|
|
Log_close_fp = false;
|
|
|
|
logpath = "<stdout>";
|
|
}
|
|
|
|
outwnd_inited = Log_fp != nullptr;
|
|
|
|
if (Log_fp == nullptr) {
|
|
fprintf(stderr, "Error opening %s\n", logpath.c_str());
|
|
} else {
|
|
time_t timedate = time(nullptr);
|
|
char datestr[50];
|
|
|
|
memset(datestr, 0, sizeof(datestr));
|
|
strftime(datestr, sizeof(datestr) - 1, "%a %b %d %H:%M:%S %Y", localtime(&timedate));
|
|
|
|
outwnd_printf("General", "Opened log '%s', %s ...\n", logpath.c_str(), datestr);
|
|
mprintf(("Legacy config mode is %s.\nReason: %s\n", os_is_legacy_mode() ? "ENABLED" : "DISABLED",
|
|
Osapi_legacy_mode_reason));
|
|
}
|
|
}
|
|
}
|
|
|
|
void outwnd_close()
|
|
{
|
|
if (!running_unittests && Log_fp != nullptr) {
|
|
time_t timedate = time(nullptr);
|
|
char datestr[50];
|
|
|
|
memset(datestr, 0, sizeof(datestr));
|
|
strftime(datestr, sizeof(datestr) - 1, "%a %b %d %H:%M:%S %Y", localtime(&timedate));
|
|
|
|
outwnd_printf("General", "... Log closed, %s\n", datestr);
|
|
|
|
if (Log_close_fp) {
|
|
fclose(Log_fp);
|
|
}
|
|
Log_fp = nullptr;
|
|
Log_close_fp = false;
|
|
}
|
|
|
|
outwnd_inited = false;
|
|
}
|
|
|
|
void outwnd_debug_window_init() {
|
|
debugWindow.reset(new osapi::DebugWindow());
|
|
}
|
|
void outwnd_debug_window_do_frame(float frametime) {
|
|
debugWindow->doFrame(frametime);
|
|
}
|
|
void outwnd_debug_window_deinit() {
|
|
debugWindow.reset();
|
|
}
|