Logging to with a ringbuffer. Much faster.

This commit is contained in:
Ben Vanik 2015-08-29 18:06:30 -07:00
parent 8dd59d07ac
commit b7203c2989
25 changed files with 387 additions and 312 deletions

1
.gitignore vendored
View File

@ -63,6 +63,7 @@ npm-debug.log
private/ private/
*.trace *.trace
imgui.ini imgui.ini
*.log
# ============================================================================== # ==============================================================================
# Build system output # Build system output

View File

@ -77,6 +77,8 @@ Have some spare time, know advanced C++, and want to write an emulator?
Contribute! There's a ton of work that needs to be done, a lot of which Contribute! There's a ton of work that needs to be done, a lot of which
is wide open greenfield fun. is wide open greenfield fun.
For general rules and guidelines please see [CONTRIBUTING.md](CONTRIBUTING.md).
Fixes and optimizations are always welcome (please!), but in addition to Fixes and optimizations are always welcome (please!), but in addition to
that there are some major work areas still untouched: that there are some major work areas still untouched:

View File

@ -25,8 +25,8 @@ and set the 'Command' to `$(SolutionDir)$(TargetPath)` and the
'Working Directory' to `$(SolutionDir)..\..`. You can specify flags and 'Working Directory' to `$(SolutionDir)..\..`. You can specify flags and
the file to run in the 'Command Arguments' field (or use `--flagfile=flags.txt`). the file to run in the 'Command Arguments' field (or use `--flagfile=flags.txt`).
To redirect output, use the following command flags: By default logs are written to a file with the name of the executable. You can
`--flagfile=$(SolutionDir)scratch\flags.txt 2>&1 1>$(SolutionDir)scratch\stdout.txt` override this with `--log_file=log.txt`.
### Linux ### Linux

View File

@ -44,8 +44,8 @@ std::unique_ptr<EmulatorWindow> EmulatorWindow::Create(Emulator* emulator) {
xe::Profiler::ThreadEnter("Win32 Loop"); xe::Profiler::ThreadEnter("Win32 Loop");
if (!emulator_window->Initialize()) { if (!emulator_window->Initialize()) {
XEFATAL("Failed to initialize main window"); xe::FatalError("Failed to initialize main window");
exit(1); return;
} }
}); });

View File

@ -20,7 +20,7 @@
extern "C" { extern "C" {
#include "libavutil/log.h" #include "libavutil/log.h"
} } // extern "C"
// As with normal Microsoft, there are like twelve different ways to access // As with normal Microsoft, there are like twelve different ways to access
// the audio APIs. Early games use XMA*() methods almost exclusively to touch // the audio APIs. Early games use XMA*() methods almost exclusively to touch
@ -55,9 +55,7 @@ XmaDecoder::XmaDecoder(cpu::Processor* processor)
XmaDecoder::~XmaDecoder() = default; XmaDecoder::~XmaDecoder() = default;
void av_log_callback(void* avcl, int level, const char* fmt, va_list va) { void av_log_callback(void* avcl, int level, const char* fmt, va_list va) {
StringBuffer buff; xe::LogLineVarargs('A', fmt, va);
buff.AppendVarargs(fmt, va);
xe::log_line('i', "libav: %s", buff.GetString());
} }
X_STATUS XmaDecoder::Setup(kernel::KernelState* kernel_state) { X_STATUS XmaDecoder::Setup(kernel::KernelState* kernel_state) {

View File

@ -34,12 +34,12 @@ std::string CanonicalizePath(const std::string& original_path) {
pos_n = std::string::npos; pos_n = std::string::npos;
break; break;
case 1: case 1:
// Duplicate separators // Duplicate separators.
path.erase(pos, 1); path.erase(pos, 1);
pos_n -= 1; pos_n -= 1;
break; break;
case 2: case 2:
// Potential marker for current directory // Potential marker for current directory.
if (path[pos + 1] == '.') { if (path[pos + 1] == '.') {
path.erase(pos, 2); path.erase(pos, 2);
pos_n -= 2; pos_n -= 2;
@ -48,10 +48,10 @@ std::string CanonicalizePath(const std::string& original_path) {
} }
break; break;
case 3: case 3:
// Potential marker for parent directory // Potential marker for parent directory.
if (path[pos + 1] == '.' && path[pos + 2] == '.') { if (path[pos + 1] == '.' && path[pos + 2] == '.') {
if (path_breaks.empty()) { if (path_breaks.empty()) {
// Ensure we don't override the device name // Ensure we don't override the device name.
std::string::size_type loc(path.find_first_of(':')); std::string::size_type loc(path.find_first_of(':'));
auto req(pos + 3); auto req(pos + 3);
if (loc == std::string::npos || loc > req) { if (loc == std::string::npos || loc > req) {
@ -66,7 +66,7 @@ std::string CanonicalizePath(const std::string& original_path) {
auto last_diff((pos + 3) - last); auto last_diff((pos + 3) - last);
path.erase(last, last_diff); path.erase(last, last_diff);
pos_n = last; pos_n = last;
// Also remove path reference // Also remove path reference.
path_breaks.erase(path_breaks.end() - 1); path_breaks.erase(path_breaks.end() - 1);
} }
} else { } else {
@ -82,12 +82,12 @@ std::string CanonicalizePath(const std::string& original_path) {
pos = pos_n; pos = pos_n;
} }
// Remove trailing seperator // Remove trailing seperator.
if (!path.empty() && path.back() == path_sep) { if (!path.empty() && path.back() == path_sep) {
path.erase(path.size() - 1); path.erase(path.size() - 1);
} }
// Final sanity check for dead paths // Final sanity check for dead paths.
if ((path.size() == 1 && (path[0] == '.' || path[0] == path_sep)) || if ((path.size() == 1 && (path[0] == '.' || path[0] == path_sep)) ||
(path.size() == 2 && path[0] == '.' && path[1] == '.')) { (path.size() == 2 && path[0] == '.' && path[1] == '.')) {
return ""; return "";
@ -96,6 +96,16 @@ std::string CanonicalizePath(const std::string& original_path) {
return path; return path;
} }
bool CreateParentFolder(const std::wstring& path) {
auto fixed_path = xe::fix_path_separators(path, '/');
auto base_path = xe::find_base_path(fixed_path, '/');
if (!PathExists(base_path)) {
return CreateFolder(base_path);
} else {
return true;
}
}
WildcardFlags WildcardFlags::FIRST(true, false); WildcardFlags WildcardFlags::FIRST(true, false);
WildcardFlags WildcardFlags::LAST(false, true); WildcardFlags WildcardFlags::LAST(false, true);

View File

@ -24,6 +24,7 @@ std::string CanonicalizePath(const std::string& original_path);
bool PathExists(const std::wstring& path); bool PathExists(const std::wstring& path);
bool CreateParentFolder(const std::wstring& path);
bool CreateFolder(const std::wstring& path); bool CreateFolder(const std::wstring& path);
bool DeleteFolder(const std::wstring& path); bool DeleteFolder(const std::wstring& path);
bool IsFolder(const std::wstring& path); bool IsFolder(const std::wstring& path);

View File

@ -11,12 +11,15 @@
#include <gflags/gflags.h> #include <gflags/gflags.h>
#include <atomic>
#include <cstdarg> #include <cstdarg>
#include <mutex> #include <mutex>
#include <vector> #include <vector>
#include "xenia/base/filesystem.h"
#include "xenia/base/main.h" #include "xenia/base/main.h"
#include "xenia/base/math.h" #include "xenia/base/math.h"
#include "xenia/base/ring_buffer.h"
#include "xenia/base/threading.h" #include "xenia/base/threading.h"
// For MessageBox: // For MessageBox:
@ -25,91 +28,150 @@
#include "xenia/base/platform_win.h" #include "xenia/base/platform_win.h"
#endif // XE_PLATFORM_WIN32 #endif // XE_PLATFORM_WIN32
DEFINE_bool(fast_stdout, false, DEFINE_string(log_file, "",
"Don't lock around stdout/stderr. May introduce weirdness."); "Logs are written to the given file instead of the default.");
DEFINE_bool(flush_stdout, true, "Flush stdout after each log line."); DEFINE_bool(flush_log, true, "Flush log file after each log line batch.");
namespace xe { namespace xe {
std::mutex log_lock; thread_local std::vector<char> log_format_buffer_(64 * 1024);
thread_local std::vector<char> log_buffer(16 * 1024); class Logger {
public:
void format_log_line(char* buffer, size_t buffer_capacity, Logger() : ring_buffer_(buffer_, kBufferSize), running_(true) {
const char level_char, const char* fmt, va_list args) { flush_event_ = xe::threading::Event::CreateAutoResetEvent(false);
char* buffer_ptr; write_thread_ =
buffer_ptr = buffer; xe::threading::Thread::Create({}, [this]() { WriteThread(); });
*(buffer_ptr++) = level_char; write_thread_->set_name("xe::FileLogSink Writer");
*(buffer_ptr++) = '>';
*(buffer_ptr++) = ' ';
buffer_ptr +=
std::snprintf(buffer_ptr, buffer_capacity - (buffer_ptr - buffer), "%.4X",
xe::threading::current_thread_id());
*(buffer_ptr++) = ' ';
// Scribble args into the print buffer.
size_t remaining_capacity = buffer_capacity - (buffer_ptr - buffer) - 3;
size_t chars_written = vsnprintf(buffer_ptr, remaining_capacity, fmt, args);
if (chars_written >= remaining_capacity) {
buffer_ptr += remaining_capacity - 1;
} else {
buffer_ptr += chars_written;
} }
// Add a trailing newline. ~Logger() {
if (buffer_ptr[-1] != '\n') { running_ = false;
buffer_ptr[0] = '\n'; flush_event_->Set();
buffer_ptr[1] = 0; xe::threading::Wait(write_thread_.get(), true);
fflush(file_);
fclose(file_);
} }
void Initialize(const std::wstring& app_name) {
if (!FLAGS_log_file.empty()) {
auto file_path = xe::to_wstring(FLAGS_log_file.c_str());
xe::filesystem::CreateParentFolder(file_path);
file_ = xe::filesystem::OpenFile(file_path, "wt");
} else {
auto file_path = app_name + L".log";
file_ = xe::filesystem::OpenFile(file_path, "wt");
}
}
void AppendLine(uint32_t thread_id, const char level_char, const char* buffer,
size_t buffer_length) {
LogLine line;
line.thread_id = thread_id;
line.level_char = level_char;
line.buffer_length = buffer_length;
while (true) {
mutex_.lock();
if (ring_buffer_.write_count() < sizeof(line) + buffer_length) {
// Buffer is full. Stall.
mutex_.unlock();
xe::threading::MaybeYield();
continue;
}
ring_buffer_.Write(&line, sizeof(LogLine));
ring_buffer_.Write(buffer, buffer_length);
mutex_.unlock();
break;
}
flush_event_->Set();
}
private:
static const size_t kBufferSize = 32 * 1024 * 1024;
struct LogLine {
uint32_t thread_id;
char level_char;
size_t buffer_length;
};
void WriteThread() {
while (running_) {
mutex_.lock();
bool did_write = false;
while (!ring_buffer_.empty()) {
did_write = true;
LogLine line;
ring_buffer_.Read(&line, sizeof(line));
ring_buffer_.Read(log_format_buffer_.data(), line.buffer_length);
const char prefix[3] = {line.level_char, '>', ' '};
fwrite(prefix, 1, sizeof(prefix), file_);
fwrite(log_format_buffer_.data(), 1, line.buffer_length, file_);
if (log_format_buffer_[line.buffer_length - 1] != '\n') {
const char suffix[1] = {'\n'};
fwrite(suffix, 1, sizeof(suffix), file_);
}
}
mutex_.unlock();
if (did_write) {
if (FLAGS_flush_log) {
fflush(file_);
}
}
xe::threading::Wait(flush_event_.get(), true);
}
}
FILE* file_ = nullptr;
uint8_t buffer_[kBufferSize];
RingBuffer ring_buffer_;
std::mutex mutex_;
std::atomic<bool> running_;
std::unique_ptr<xe::threading::Event> flush_event_;
std::unique_ptr<xe::threading::Thread> write_thread_;
};
Logger logger_;
void InitializeLogging(const std::wstring& app_name) {
logger_.Initialize(app_name);
} }
void log_line(const char level_char, const char* fmt, ...) { void LogLineFormat(const char level_char, const char* fmt, ...) {
// SCOPE_profile_cpu_i("emu", "log_line");
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
format_log_line(log_buffer.data(), log_buffer.capacity(), level_char, fmt, size_t chars_written = vsnprintf(log_format_buffer_.data(),
args); log_format_buffer_.capacity(), fmt, args);
va_end(args); va_end(args);
logger_.AppendLine(xe::threading::current_thread_id(), level_char,
if (!FLAGS_fast_stdout) { log_format_buffer_.data(), chars_written);
log_lock.lock();
}
#if 0 // defined(OutputDebugString)
OutputDebugStringA(log_buffer.data());
#else
fprintf(stdout, "%s", log_buffer.data());
if (FLAGS_flush_stdout) {
fflush(stdout);
}
#endif // OutputDebugString
if (!FLAGS_fast_stdout) {
log_lock.unlock();
}
} }
void handle_fatal(const char* fmt, ...) { void LogLineVarargs(const char level_char, const char* fmt, va_list args) {
size_t chars_written = vsnprintf(log_format_buffer_.data(),
log_format_buffer_.capacity(), fmt, args);
logger_.AppendLine(xe::threading::current_thread_id(), level_char,
log_format_buffer_.data(), chars_written);
}
void LogLine(const char level_char, const std::string& str) {
logger_.AppendLine(xe::threading::current_thread_id(), level_char,
str.c_str(), str.length());
}
void FatalError(const char* fmt, ...) {
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
format_log_line(log_buffer.data(), log_buffer.capacity(), 'X', fmt, args); LogLineVarargs('X', fmt, args);
va_end(args); va_end(args);
if (!FLAGS_fast_stdout) {
log_lock.lock();
}
#if defined(OutputDebugString)
OutputDebugStringA(log_buffer.data());
#else
fprintf(stderr, "%s", log_buffer.data());
fflush(stderr);
#endif // OutputDebugString
if (!FLAGS_fast_stdout) {
log_lock.unlock();
}
#if XE_PLATFORM_WIN32 #if XE_PLATFORM_WIN32
if (!xe::has_console_attached()) { if (!xe::has_console_attached()) {
MessageBoxA(NULL, log_buffer.data(), "Xenia Error", va_start(args, fmt);
vsnprintf(log_format_buffer_.data(), log_format_buffer_.capacity(), fmt,
args);
va_end(args);
MessageBoxA(NULL, log_format_buffer_.data(), "Xenia Error",
MB_OK | MB_ICONERROR | MB_APPLMODAL | MB_SETFOREGROUND); MB_OK | MB_ICONERROR | MB_APPLMODAL | MB_SETFOREGROUND);
} }
#endif // WIN32 #endif // WIN32
@ -117,4 +179,6 @@ void handle_fatal(const char* fmt, ...) {
exit(1); exit(1);
} }
void FatalError(const std::string& str) { FatalError(str.c_str()); }
} // namespace xe } // namespace xe

View File

@ -11,86 +11,47 @@
#define XENIA_BASE_LOGGING_H_ #define XENIA_BASE_LOGGING_H_
#include <cstdint> #include <cstdint>
#include <string>
#include "xenia/base/string.h" #include "xenia/base/string.h"
namespace xe { namespace xe {
#define XE_OPTION_ENABLE_LOGGING 1 #define XE_OPTION_ENABLE_LOGGING 1
#define XE_OPTION_LOG_ERROR 1
#define XE_OPTION_LOG_WARNING 1
#define XE_OPTION_LOG_INFO 1
#define XE_OPTION_LOG_DEBUG 1
#define XE_OPTION_LOG_CPU 1
#define XE_OPTION_LOG_APU 1
#define XE_OPTION_LOG_GPU 1
#define XE_OPTION_LOG_KERNEL 1
#define XE_OPTION_LOG_FS 1
#define XE_EMPTY_MACRO \ // Initializes the logging system and any outputs requested.
do { \ // Must be called on startup.
} while (false) void InitializeLogging(const std::wstring& app_name);
void log_line(const char level_char, const char* fmt, ...); // Appends a line to the log with printf-style formatting.
void handle_fatal(const char* fmt, ...); void LogLineFormat(const char level_char, const char* fmt, ...);
void LogLineVarargs(const char level_char, const char* fmt, va_list args);
// Appends a line to the log.
void LogLine(const char level_char, const std::string& str);
// Logs a fatal error with printf-style formatting and aborts the program.
void FatalError(const char* fmt, ...);
// Logs a fatal error and aborts the program.
void FatalError(const std::string& str);
#if XE_OPTION_ENABLE_LOGGING #if XE_OPTION_ENABLE_LOGGING
#define XELOGCORE(level, fmt, ...) xe::log_line(level, fmt, ##__VA_ARGS__) #define XELOGCORE(level, fmt, ...) xe::LogLineFormat(level, fmt, ##__VA_ARGS__)
#else #else
#define XELOGCORE(level, fmt, ...) XE_EMPTY_MACRO #define XELOGCORE(level, fmt, ...) \
do { \
} while (false)
#endif // ENABLE_LOGGING #endif // ENABLE_LOGGING
#define XEFATAL(fmt, ...) \
do { \
xe::handle_fatal(fmt, ##__VA_ARGS__); \
} while (false)
#if XE_OPTION_LOG_ERROR
#define XELOGE(fmt, ...) XELOGCORE('!', fmt, ##__VA_ARGS__) #define XELOGE(fmt, ...) XELOGCORE('!', fmt, ##__VA_ARGS__)
#else
#define XELOGE(fmt, ...) XE_EMPTY_MACRO
#endif
#if XE_OPTION_LOG_WARNING
#define XELOGW(fmt, ...) XELOGCORE('w', fmt, ##__VA_ARGS__) #define XELOGW(fmt, ...) XELOGCORE('w', fmt, ##__VA_ARGS__)
#else
#define XELOGW(fmt, ...) XE_EMPTY_MACRO
#endif
#if XE_OPTION_LOG_INFO
#define XELOGI(fmt, ...) XELOGCORE('i', fmt, ##__VA_ARGS__) #define XELOGI(fmt, ...) XELOGCORE('i', fmt, ##__VA_ARGS__)
#else
#define XELOGI(fmt, ...) XE_EMPTY_MACRO
#endif
#if XE_OPTION_LOG_DEBUG
#define XELOGD(fmt, ...) XELOGCORE('d', fmt, ##__VA_ARGS__) #define XELOGD(fmt, ...) XELOGCORE('d', fmt, ##__VA_ARGS__)
#else
#define XELOGD(fmt, ...) XE_EMPTY_MACRO
#endif
#if XE_OPTION_LOG_CPU
#define XELOGCPU(fmt, ...) XELOGCORE('C', fmt, ##__VA_ARGS__) #define XELOGCPU(fmt, ...) XELOGCORE('C', fmt, ##__VA_ARGS__)
#else
#define XELOGCPU(fmt, ...) XE_EMPTY_MACRO
#endif
#if XE_OPTION_LOG_APU
#define XELOGAPU(fmt, ...) XELOGCORE('A', fmt, ##__VA_ARGS__) #define XELOGAPU(fmt, ...) XELOGCORE('A', fmt, ##__VA_ARGS__)
#else
#define XELOGAPU(fmt, ...) XE_EMPTY_MACRO
#endif
#if XE_OPTION_LOG_GPU
#define XELOGGPU(fmt, ...) XELOGCORE('G', fmt, ##__VA_ARGS__) #define XELOGGPU(fmt, ...) XELOGCORE('G', fmt, ##__VA_ARGS__)
#else
#define XELOGGPU(fmt, ...) XE_EMPTY_MACRO
#endif
#if XE_OPTION_LOG_KERNEL
#define XELOGKERNEL(fmt, ...) XELOGCORE('K', fmt, ##__VA_ARGS__) #define XELOGKERNEL(fmt, ...) XELOGCORE('K', fmt, ##__VA_ARGS__)
#else
#define XELOGKERNEL(fmt, ...) XE_EMPTY_MACRO
#endif
#if XE_OPTION_LOG_FS
#define XELOGFS(fmt, ...) XELOGCORE('F', fmt, ##__VA_ARGS__) #define XELOGFS(fmt, ...) XELOGCORE('F', fmt, ##__VA_ARGS__)
#else
#define XELOGFS(fmt, ...) XE_EMPTY_MACRO
#endif
} // namespace xe } // namespace xe

View File

@ -11,6 +11,7 @@
#include <gflags/gflags.h> #include <gflags/gflags.h>
#include "xenia/base/logging.h"
#include "xenia/base/string.h" #include "xenia/base/string.h"
namespace xe { namespace xe {
@ -32,6 +33,9 @@ extern "C" int main(int argc, char** argv) {
args.push_back(xe::to_wstring(argv[n])); args.push_back(xe::to_wstring(argv[n]));
} }
// Initialize logging. Needs parsed FLAGS.
xe::InitializeLogging(entry_info.name);
// Call app-provided entry point. // Call app-provided entry point.
int result = entry_info.entry_point(args); int result = entry_info.entry_point(args);

View File

@ -13,6 +13,7 @@
#include <gflags/gflags.h> #include <gflags/gflags.h>
#include <io.h> #include <io.h>
#include "xenia/base/logging.h"
#include "xenia/base/platform_win.h" #include "xenia/base/platform_win.h"
#include "xenia/base/string.h" #include "xenia/base/string.h"
@ -84,6 +85,9 @@ int Main() {
// NOTE: this may fail if COM has already been initialized - that's OK. // NOTE: this may fail if COM has already been initialized - that's OK.
CoInitializeEx(nullptr, COINIT_MULTITHREADED); CoInitializeEx(nullptr, COINIT_MULTITHREADED);
// Initialize logging. Needs parsed FLAGS.
xe::InitializeLogging(entry_info.name);
// Call app-provided entry point. // Call app-provided entry point.
int result = entry_info.entry_point(args); int result = entry_info.entry_point(args);

View File

@ -15,7 +15,7 @@
namespace xe { namespace xe {
RingBuffer::RingBuffer(uint8_t* buffer, size_t capacity) RingBuffer::RingBuffer(uint8_t* buffer, size_t capacity)
: buffer_(buffer), capacity_(capacity), read_offset_(0), write_offset_(0) {} : buffer_(buffer), capacity_(capacity) {}
size_t RingBuffer::Read(uint8_t* buffer, size_t count) { size_t RingBuffer::Read(uint8_t* buffer, size_t count) {
count = std::min(count, capacity_); count = std::min(count, capacity_);
@ -37,7 +37,7 @@ size_t RingBuffer::Read(uint8_t* buffer, size_t count) {
return count; return count;
} }
size_t RingBuffer::Write(uint8_t* buffer, size_t count) { size_t RingBuffer::Write(const uint8_t* buffer, size_t count) {
count = std::min(count, capacity_); count = std::min(count, capacity_);
if (!count) { if (!count) {
return 0; return 0;

View File

@ -20,14 +20,13 @@ class RingBuffer {
public: public:
RingBuffer(uint8_t* buffer, size_t capacity); RingBuffer(uint8_t* buffer, size_t capacity);
size_t Read(uint8_t* buffer, size_t count); uint8_t* buffer() const { return buffer_; }
size_t Write(uint8_t* buffer, size_t count); size_t capacity() const { return capacity_; }
bool empty() const { return read_offset_ == write_offset_; }
uint8_t* buffer() { return buffer_; } size_t read_offset() const { return read_offset_; }
size_t capacity() { return capacity_; } void set_read_offset(size_t offset) { read_offset_ = offset % capacity_; }
size_t read_count() const {
size_t read_offset() { return read_offset_; }
size_t read_count() {
if (read_offset_ == write_offset_) { if (read_offset_ == write_offset_) {
return 0; return 0;
} else if (read_offset_ < write_offset_) { } else if (read_offset_ < write_offset_) {
@ -37,8 +36,9 @@ class RingBuffer {
} }
} }
size_t write_offset() { return write_offset_; } size_t write_offset() const { return write_offset_; }
size_t write_count() { void set_write_offset(size_t offset) { write_offset_ = offset % capacity_; }
size_t write_count() const {
if (read_offset_ == write_offset_) { if (read_offset_ == write_offset_) {
return capacity_; return capacity_;
} else if (write_offset_ < read_offset_) { } else if (write_offset_ < read_offset_) {
@ -48,15 +48,23 @@ class RingBuffer {
} }
} }
void set_read_offset(size_t offset) { read_offset_ = offset % capacity_; } size_t Read(uint8_t* buffer, size_t count);
template <typename T>
size_t Read(T* buffer, size_t count) {
return Read(reinterpret_cast<uint8_t*>(buffer), count);
}
void set_write_offset(size_t offset) { write_offset_ = offset % capacity_; } size_t Write(const uint8_t* buffer, size_t count);
template <typename T>
size_t Write(const T* buffer, size_t count) {
return Write(reinterpret_cast<const uint8_t*>(buffer), count);
}
private: private:
uint8_t* buffer_; uint8_t* buffer_ = nullptr;
size_t capacity_; size_t capacity_ = 0;
size_t read_offset_; size_t read_offset_ = 0;
size_t write_offset_; size_t write_offset_ = 0;
}; };
} // namespace xe } // namespace xe

View File

@ -155,14 +155,14 @@ std::string fix_path_separators(const std::string& source, char new_sep) {
return dest; return dest;
} }
std::string find_name_from_path(const std::string& path) { std::string find_name_from_path(const std::string& path, char sep) {
std::string name(path); std::string name(path);
if (!path.empty()) { if (!path.empty()) {
std::string::size_type from(std::string::npos); std::string::size_type from(std::string::npos);
if (path.back() == '\\') { if (path.back() == sep) {
from = path.size() - 2; from = path.size() - 2;
} }
auto pos(path.find_last_of('\\', from)); auto pos(path.find_last_of(sep, from));
if (pos != std::string::npos) { if (pos != std::string::npos) {
if (from == std::string::npos) { if (from == std::string::npos) {
name = path.substr(pos + 1); name = path.substr(pos + 1);
@ -175,14 +175,14 @@ std::string find_name_from_path(const std::string& path) {
return name; return name;
} }
std::wstring find_name_from_path(const std::wstring& path) { std::wstring find_name_from_path(const std::wstring& path, wchar_t sep) {
std::wstring name(path); std::wstring name(path);
if (!path.empty()) { if (!path.empty()) {
std::wstring::size_type from(std::wstring::npos); std::wstring::size_type from(std::wstring::npos);
if (path.back() == '\\') { if (path.back() == sep) {
from = path.size() - 2; from = path.size() - 2;
} }
auto pos(path.find_last_of('\\', from)); auto pos(path.find_last_of(sep, from));
if (pos != std::wstring::npos) { if (pos != std::wstring::npos) {
if (from == std::wstring::npos) { if (from == std::wstring::npos) {
name = path.substr(pos + 1); name = path.substr(pos + 1);
@ -195,12 +195,12 @@ std::wstring find_name_from_path(const std::wstring& path) {
return name; return name;
} }
std::string find_base_path(const std::string& path) { std::string find_base_path(const std::string& path, char sep) {
auto last_slash = path.find_last_of('\\'); auto last_slash = path.find_last_of(sep);
if (last_slash == std::string::npos) { if (last_slash == std::string::npos) {
return path; return path;
} else if (last_slash == path.length() - 1) { } else if (last_slash == path.length() - 1) {
auto prev_slash = path.find_last_of('\\', last_slash - 1); auto prev_slash = path.find_last_of(sep, last_slash - 1);
if (prev_slash == std::string::npos) { if (prev_slash == std::string::npos) {
return ""; return "";
} else { } else {
@ -211,12 +211,12 @@ std::string find_base_path(const std::string& path) {
} }
} }
std::wstring find_base_path(const std::wstring& path) { std::wstring find_base_path(const std::wstring& path, wchar_t sep) {
auto last_slash = path.find_last_of('\\'); auto last_slash = path.find_last_of(sep);
if (last_slash == std::wstring::npos) { if (last_slash == std::wstring::npos) {
return path; return path;
} else if (last_slash == path.length() - 1) { } else if (last_slash == path.length() - 1) {
auto prev_slash = path.find_last_of('\\', last_slash - 1); auto prev_slash = path.find_last_of(sep, last_slash - 1);
if (prev_slash == std::wstring::npos) { if (prev_slash == std::wstring::npos) {
return L""; return L"";
} else { } else {

View File

@ -45,12 +45,16 @@ std::string fix_path_separators(const std::string& source,
char new_sep = xe::kPathSeparator); char new_sep = xe::kPathSeparator);
// Find the top directory name or filename from a path. // Find the top directory name or filename from a path.
std::string find_name_from_path(const std::string& path); std::string find_name_from_path(const std::string& path,
std::wstring find_name_from_path(const std::wstring& path); char sep = xe::kPathSeparator);
std::wstring find_name_from_path(const std::wstring& path,
wchar_t sep = xe::kPathSeparator);
// Get parent path of the given directory or filename. // Get parent path of the given directory or filename.
std::string find_base_path(const std::string& path); std::string find_base_path(const std::string& path,
std::wstring find_base_path(const std::wstring& path); char sep = xe::kPathSeparator);
std::wstring find_base_path(const std::wstring& path,
wchar_t sep = xe::kPathSeparator);
} // namespace xe } // namespace xe

View File

@ -75,7 +75,7 @@ X64Emitter::X64Emitter(X64Backend* backend, XbyakAllocator* allocator)
} }
if (!cpu_.has(Xbyak::util::Cpu::tAVX)) { if (!cpu_.has(Xbyak::util::Cpu::tAVX)) {
XEFATAL( xe::FatalError(
"Your CPU is too old to support Xenia. See the FAQ for system " "Your CPU is too old to support Xenia. See the FAQ for system "
"requirements at http://xenia.jp"); "requirements at http://xenia.jp");
return; return;

View File

@ -22,7 +22,9 @@
#if 0 #if 0
#define LOGPPC(fmt, ...) XELOGCORE('p', fmt, ##__VA_ARGS__) #define LOGPPC(fmt, ...) XELOGCORE('p', fmt, ##__VA_ARGS__)
#else #else
#define LOGPPC(fmt, ...) XE_EMPTY_MACRO #define LOGPPC(fmt, ...) \
do { \
} while (false)
#endif #endif
namespace xe { namespace xe {

View File

@ -46,8 +46,8 @@ std::unique_ptr<Application> Application::Create() {
xe::Profiler::ThreadEnter("Win32 Loop"); xe::Profiler::ThreadEnter("Win32 Loop");
if (!app->Initialize()) { if (!app->Initialize()) {
XEFATAL("Failed to initialize application"); xe::FatalError("Failed to initialize application");
exit(1); return;
} }
fence.Signal(); fence.Signal();

View File

@ -228,11 +228,11 @@ X_STATUS Emulator::LaunchDiscImage(std::wstring path) {
// Register the disc image in the virtual filesystem. // Register the disc image in the virtual filesystem.
auto device = std::make_unique<vfs::DiscImageDevice>(mount_path, path); auto device = std::make_unique<vfs::DiscImageDevice>(mount_path, path);
if (!device->Initialize()) { if (!device->Initialize()) {
XELOGE("Unable to mount disc image"); xe::FatalError("Unable to mount disc image; file not found or corrupt.");
return X_STATUS_NO_SUCH_FILE; return X_STATUS_NO_SUCH_FILE;
} }
if (!file_system_->RegisterDevice(std::move(device))) { if (!file_system_->RegisterDevice(std::move(device))) {
XELOGE("Unable to register disc image"); xe::FatalError("Unable to register disc image.");
return X_STATUS_NO_SUCH_FILE; return X_STATUS_NO_SUCH_FILE;
} }
@ -250,11 +250,12 @@ X_STATUS Emulator::LaunchStfsContainer(std::wstring path) {
// Register the container in the virtual filesystem. // Register the container in the virtual filesystem.
auto device = std::make_unique<vfs::StfsContainerDevice>(mount_path, path); auto device = std::make_unique<vfs::StfsContainerDevice>(mount_path, path);
if (!device->Initialize()) { if (!device->Initialize()) {
XELOGE("Unable to mount STFS container"); xe::FatalError(
"Unable to mount STFS container; file not found or corrupt.");
return X_STATUS_NO_SUCH_FILE; return X_STATUS_NO_SUCH_FILE;
} }
if (!file_system_->RegisterDevice(std::move(device))) { if (!file_system_->RegisterDevice(std::move(device))) {
XELOGE("Unable to register STFS container"); xe::FatalError("Unable to register STFS container.");
return X_STATUS_NO_SUCH_FILE; return X_STATUS_NO_SUCH_FILE;
} }

View File

@ -172,7 +172,7 @@ void CommandProcessor::ClearCaches() {
void CommandProcessor::WorkerThreadMain() { void CommandProcessor::WorkerThreadMain() {
context_->MakeCurrent(); context_->MakeCurrent();
if (!SetupGL()) { if (!SetupGL()) {
XEFATAL("Unable to setup command processor GL state"); xe::FatalError("Unable to setup command processor GL state");
return; return;
} }

View File

@ -88,7 +88,7 @@ X_STATUS GL4GraphicsSystem::Setup(cpu::Processor* processor,
processor_context->ClearCurrent(); processor_context->ClearCurrent();
}); });
if (!processor_context) { if (!processor_context) {
XEFATAL( xe::FatalError(
"Unable to initialize GL context. Xenia requires OpenGL 4.5. Ensure " "Unable to initialize GL context. Xenia requires OpenGL 4.5. Ensure "
"you have the latest drivers for your GPU and that it supports OpenGL " "you have the latest drivers for your GPU and that it supports OpenGL "
"4.5. See http://xenia.jp/faq/ for more information."); "4.5. See http://xenia.jp/faq/ for more information.");

View File

@ -2197,8 +2197,8 @@ int trace_viewer_main(const std::vector<std::wstring>& args) {
loop->PostSynchronous([&window]() { loop->PostSynchronous([&window]() {
xe::threading::set_name("Win32 Loop"); xe::threading::set_name("Win32 Loop");
if (!window->Initialize()) { if (!window->Initialize()) {
XEFATAL("Failed to initialize main window"); FatalError("Failed to initialize main window");
exit(1); return;
} }
}); });
window->on_closed.AddListener([&loop](xe::ui::UIEvent* e) { window->on_closed.AddListener([&loop](xe::ui::UIEvent* e) {

View File

@ -76,8 +76,8 @@ void GraphicsSystem::DispatchInterruptCallback(uint32_t source, uint32_t cpu) {
} }
thread->SetActiveCpu(cpu); thread->SetActiveCpu(cpu);
// XELOGGPU("Dispatching GPU interrupt at %.8X w/ mode %d on cpu %d", XELOGGPU("Dispatching GPU interrupt at %.8X w/ mode %d on cpu %d",
// interrupt_callback_, source, cpu); interrupt_callback_, source, cpu);
uint64_t args[] = {source, interrupt_callback_data_}; uint64_t args[] = {source, interrupt_callback_data_};
processor_->Execute(thread->thread_state(), interrupt_callback_, args, processor_->Execute(thread->thread_state(), interrupt_callback_, args,

View File

@ -276,8 +276,8 @@ X_STATUS XUserModule::Launch(uint32_t flags) {
// We know this is the 'main thread'. // We know this is the 'main thread'.
char thread_name[32]; char thread_name[32];
snprintf(thread_name, xe::countof(thread_name), "Main XThread %04X", std::snprintf(thread_name, xe::countof(thread_name), "Main XThread %04X",
thread->handle()); thread->handle());
thread->set_name(thread_name); thread->set_name(thread_name);
X_STATUS result = thread->Create(); X_STATUS result = thread->Create();
@ -308,24 +308,31 @@ void XUserModule::Dump() {
return; return;
} }
StringBuffer sb;
xe::cpu::ExportResolver* export_resolver = xe::cpu::ExportResolver* export_resolver =
kernel_state_->emulator()->export_resolver(); kernel_state_->emulator()->export_resolver();
auto header = xex_header(); auto header = xex_header();
// XEX header. // XEX header.
printf("Module %s:\n", path_.c_str()); sb.AppendFormat("Module %s:\n", path_.c_str());
printf(" Module Flags: %.8X\n", (uint32_t)header->module_flags); sb.AppendFormat(" Module Flags: %.8X\n", (uint32_t)header->module_flags);
// Security header // Security header
auto security_info = xex_module()->xex_security_info(); auto security_info = xex_module()->xex_security_info();
printf("Security Header:\n"); sb.AppendFormat("Security Header:\n");
printf(" Image Flags: %.8X\n", (uint32_t)security_info->image_flags); sb.AppendFormat(" Image Flags: %.8X\n",
printf(" Load Address: %.8X\n", (uint32_t)security_info->load_address); (uint32_t)security_info->image_flags);
printf(" Image Size: %.8X\n", (uint32_t)security_info->image_size); sb.AppendFormat(" Load Address: %.8X\n",
printf(" Export Table: %.8X\n", (uint32_t)security_info->export_table); (uint32_t)security_info->load_address);
sb.AppendFormat(" Image Size: %.8X\n",
(uint32_t)security_info->image_size);
sb.AppendFormat(" Export Table: %.8X\n",
(uint32_t)security_info->export_table);
// Optional headers // Optional headers
printf("Optional Header Count: %d\n", (uint32_t)header->header_count); sb.AppendFormat("Optional Header Count: %d\n",
(uint32_t)header->header_count);
for (uint32_t i = 0; i < header->header_count; i++) { for (uint32_t i = 0; i < header->header_count; i++) {
auto& opt_header = header->headers[i]; auto& opt_header = header->headers[i];
@ -335,7 +342,7 @@ void XUserModule::Dump() {
reinterpret_cast<const uint8_t*>(header) + opt_header.offset; reinterpret_cast<const uint8_t*>(header) + opt_header.offset;
switch (opt_header.key) { switch (opt_header.key) {
case XEX_HEADER_RESOURCE_INFO: { case XEX_HEADER_RESOURCE_INFO: {
printf(" XEX_HEADER_RESOURCE_INFO:\n"); sb.AppendFormat(" XEX_HEADER_RESOURCE_INFO:\n");
auto opt_resource_info = auto opt_resource_info =
reinterpret_cast<const xex2_opt_resource_info*>(opt_header_ptr); reinterpret_cast<const xex2_opt_resource_info*>(opt_header_ptr);
@ -348,35 +355,37 @@ void XUserModule::Dump() {
std::memcpy(name, res.name, sizeof(res.name)); std::memcpy(name, res.name, sizeof(res.name));
name[8] = 0; name[8] = 0;
printf(" %-8s %.8X-%.8X, %db\n", name, (uint32_t)res.address, sb.AppendFormat(
(uint32_t)res.address + (uint32_t)res.size, " %-8s %.8X-%.8X, %db\n", name, (uint32_t)res.address,
(uint32_t)res.size); (uint32_t)res.address + (uint32_t)res.size, (uint32_t)res.size);
} }
} break; } break;
case XEX_HEADER_FILE_FORMAT_INFO: { case XEX_HEADER_FILE_FORMAT_INFO: {
printf(" XEX_HEADER_FILE_FORMAT_INFO (TODO):\n"); sb.AppendFormat(" XEX_HEADER_FILE_FORMAT_INFO (TODO):\n");
} break; } break;
case XEX_HEADER_DELTA_PATCH_DESCRIPTOR: { case XEX_HEADER_DELTA_PATCH_DESCRIPTOR: {
printf(" XEX_HEADER_DELTA_PATCH_DESCRIPTOR (TODO):\n"); sb.AppendFormat(" XEX_HEADER_DELTA_PATCH_DESCRIPTOR (TODO):\n");
} break; } break;
case XEX_HEADER_BOUNDING_PATH: { case XEX_HEADER_BOUNDING_PATH: {
auto opt_bound_path = auto opt_bound_path =
reinterpret_cast<const xex2_opt_bound_path*>(opt_header_ptr); reinterpret_cast<const xex2_opt_bound_path*>(opt_header_ptr);
printf(" XEX_HEADER_BOUNDING_PATH: %s\n", opt_bound_path->path); sb.AppendFormat(" XEX_HEADER_BOUNDING_PATH: %s\n",
opt_bound_path->path);
} break; } break;
case XEX_HEADER_ORIGINAL_BASE_ADDRESS: { case XEX_HEADER_ORIGINAL_BASE_ADDRESS: {
printf(" XEX_HEADER_ORIGINAL_BASE_ADDRESS: %.8X\n", sb.AppendFormat(" XEX_HEADER_ORIGINAL_BASE_ADDRESS: %.8X\n",
(uint32_t)opt_header.value); (uint32_t)opt_header.value);
} break; } break;
case XEX_HEADER_ENTRY_POINT: { case XEX_HEADER_ENTRY_POINT: {
printf(" XEX_HEADER_ENTRY_POINT: %.8X\n", (uint32_t)opt_header.value); sb.AppendFormat(" XEX_HEADER_ENTRY_POINT: %.8X\n",
(uint32_t)opt_header.value);
} break; } break;
case XEX_HEADER_IMAGE_BASE_ADDRESS: { case XEX_HEADER_IMAGE_BASE_ADDRESS: {
printf(" XEX_HEADER_IMAGE_BASE_ADDRESS: %.8X\n", sb.AppendFormat(" XEX_HEADER_IMAGE_BASE_ADDRESS: %.8X\n",
(uint32_t)opt_header.value); (uint32_t)opt_header.value);
} break; } break;
case XEX_HEADER_IMPORT_LIBRARIES: { case XEX_HEADER_IMPORT_LIBRARIES: {
printf(" XEX_HEADER_IMPORT_LIBRARIES:\n"); sb.AppendFormat(" XEX_HEADER_IMPORT_LIBRARIES:\n");
auto opt_import_libraries = auto opt_import_libraries =
reinterpret_cast<const xex2_opt_import_libraries*>(opt_header_ptr); reinterpret_cast<const xex2_opt_import_libraries*>(opt_header_ptr);
@ -408,116 +417,119 @@ void XUserModule::Dump() {
auto library = reinterpret_cast<const xex2_import_library*>( auto library = reinterpret_cast<const xex2_import_library*>(
libraries + library_offset); libraries + library_offset);
auto name = string_table[library->name_index]; auto name = string_table[library->name_index];
printf(" %s - %d imports\n", name, (uint16_t)library->count); sb.AppendFormat(" %s - %d imports\n", name,
(uint16_t)library->count);
// Manually byteswap these because of the bitfields. // Manually byteswap these because of the bitfields.
xex2_version version, version_min; xex2_version version, version_min;
version.value = xe::byte_swap<uint32_t>(library->version.value); version.value = xe::byte_swap<uint32_t>(library->version.value);
version_min.value = version_min.value =
xe::byte_swap<uint32_t>(library->version_min.value); xe::byte_swap<uint32_t>(library->version_min.value);
printf(" Version: %d.%d.%d.%d\n", version.major, version.minor, sb.AppendFormat(" Version: %d.%d.%d.%d\n", version.major,
version.build, version.qfe); version.minor, version.build, version.qfe);
printf(" Min Version: %d.%d.%d.%d\n", version_min.major, sb.AppendFormat(" Min Version: %d.%d.%d.%d\n", version_min.major,
version_min.minor, version_min.build, version_min.qfe); version_min.minor, version_min.build,
version_min.qfe);
library_offset += library->size; library_offset += library->size;
} }
} break; } break;
case XEX_HEADER_CHECKSUM_TIMESTAMP: { case XEX_HEADER_CHECKSUM_TIMESTAMP: {
printf(" XEX_HEADER_CHECKSUM_TIMESTAMP (TODO):\n"); sb.AppendFormat(" XEX_HEADER_CHECKSUM_TIMESTAMP (TODO):\n");
} break; } break;
case XEX_HEADER_ORIGINAL_PE_NAME: { case XEX_HEADER_ORIGINAL_PE_NAME: {
auto opt_pe_name = auto opt_pe_name =
reinterpret_cast<const xex2_opt_original_pe_name*>(opt_header_ptr); reinterpret_cast<const xex2_opt_original_pe_name*>(opt_header_ptr);
printf(" XEX_HEADER_ORIGINAL_PE_NAME: %s\n", opt_pe_name->name); sb.AppendFormat(" XEX_HEADER_ORIGINAL_PE_NAME: %s\n",
opt_pe_name->name);
} break; } break;
case XEX_HEADER_STATIC_LIBRARIES: { case XEX_HEADER_STATIC_LIBRARIES: {
printf(" XEX_HEADER_STATIC_LIBRARIES:\n"); sb.AppendFormat(" XEX_HEADER_STATIC_LIBRARIES:\n");
auto opt_static_libraries = auto opt_static_libraries =
reinterpret_cast<const xex2_opt_static_libraries*>(opt_header_ptr); reinterpret_cast<const xex2_opt_static_libraries*>(opt_header_ptr);
uint32_t count = (opt_static_libraries->size - 4) / 0x10; uint32_t count = (opt_static_libraries->size - 4) / 0x10;
for (uint32_t l = 0; l < count; l++) { for (uint32_t l = 0; l < count; l++) {
auto& library = opt_static_libraries->libraries[l]; auto& library = opt_static_libraries->libraries[l];
printf(" %-8s : %d.%d.%d.%d\n", library.name, sb.AppendFormat(" %-8s : %d.%d.%d.%d\n", library.name,
static_cast<uint16_t>(library.version_major), static_cast<uint16_t>(library.version_major),
static_cast<uint16_t>(library.version_minor), static_cast<uint16_t>(library.version_minor),
static_cast<uint16_t>(library.version_build), static_cast<uint16_t>(library.version_build),
static_cast<uint16_t>(library.version_qfe)); static_cast<uint16_t>(library.version_qfe));
} }
} break; } break;
case XEX_HEADER_TLS_INFO: { case XEX_HEADER_TLS_INFO: {
printf(" XEX_HEADER_TLS_INFO:\n"); sb.AppendFormat(" XEX_HEADER_TLS_INFO:\n");
auto opt_tls_info = auto opt_tls_info =
reinterpret_cast<const xex2_opt_tls_info*>(opt_header_ptr); reinterpret_cast<const xex2_opt_tls_info*>(opt_header_ptr);
printf(" Slot Count: %d\n", sb.AppendFormat(" Slot Count: %d\n",
static_cast<uint32_t>(opt_tls_info->slot_count)); static_cast<uint32_t>(opt_tls_info->slot_count));
printf(" Raw Data Address: %.8X\n", sb.AppendFormat(" Raw Data Address: %.8X\n",
static_cast<uint32_t>(opt_tls_info->raw_data_address)); static_cast<uint32_t>(opt_tls_info->raw_data_address));
printf(" Data Size: %d\n", sb.AppendFormat(" Data Size: %d\n",
static_cast<uint32_t>(opt_tls_info->data_size)); static_cast<uint32_t>(opt_tls_info->data_size));
printf(" Raw Data Size: %d\n", sb.AppendFormat(" Raw Data Size: %d\n",
static_cast<uint32_t>(opt_tls_info->raw_data_size)); static_cast<uint32_t>(opt_tls_info->raw_data_size));
} break; } break;
case XEX_HEADER_DEFAULT_STACK_SIZE: { case XEX_HEADER_DEFAULT_STACK_SIZE: {
printf(" XEX_HEADER_DEFAULT_STACK_SIZE: %d\n", sb.AppendFormat(" XEX_HEADER_DEFAULT_STACK_SIZE: %d\n",
static_cast<uint32_t>(opt_header.value)); static_cast<uint32_t>(opt_header.value));
} break; } break;
case XEX_HEADER_DEFAULT_FILESYSTEM_CACHE_SIZE: { case XEX_HEADER_DEFAULT_FILESYSTEM_CACHE_SIZE: {
printf(" XEX_HEADER_DEFAULT_FILESYSTEM_CACHE_SIZE: %d\n", sb.AppendFormat(" XEX_HEADER_DEFAULT_FILESYSTEM_CACHE_SIZE: %d\n",
static_cast<uint32_t>(opt_header.value)); static_cast<uint32_t>(opt_header.value));
} break; } break;
case XEX_HEADER_DEFAULT_HEAP_SIZE: { case XEX_HEADER_DEFAULT_HEAP_SIZE: {
printf(" XEX_HEADER_DEFAULT_HEAP_SIZE: %d\n", sb.AppendFormat(" XEX_HEADER_DEFAULT_HEAP_SIZE: %d\n",
static_cast<uint32_t>(opt_header.value)); static_cast<uint32_t>(opt_header.value));
} break; } break;
case XEX_HEADER_PAGE_HEAP_SIZE_AND_FLAGS: { case XEX_HEADER_PAGE_HEAP_SIZE_AND_FLAGS: {
printf(" XEX_HEADER_PAGE_HEAP_SIZE_AND_FLAGS (TODO):\n"); sb.AppendFormat(" XEX_HEADER_PAGE_HEAP_SIZE_AND_FLAGS (TODO):\n");
} break; } break;
case XEX_HEADER_SYSTEM_FLAGS: { case XEX_HEADER_SYSTEM_FLAGS: {
printf(" XEX_HEADER_SYSTEM_FLAGS: %.8X\n", sb.AppendFormat(" XEX_HEADER_SYSTEM_FLAGS: %.8X\n",
static_cast<uint32_t>(opt_header.value)); static_cast<uint32_t>(opt_header.value));
} break; } break;
case XEX_HEADER_EXECUTION_INFO: { case XEX_HEADER_EXECUTION_INFO: {
printf(" XEX_HEADER_EXECUTION_INFO:\n"); sb.AppendFormat(" XEX_HEADER_EXECUTION_INFO:\n");
auto opt_exec_info = auto opt_exec_info =
reinterpret_cast<const xex2_opt_execution_info*>(opt_header_ptr); reinterpret_cast<const xex2_opt_execution_info*>(opt_header_ptr);
printf(" Media ID: %.8X\n", sb.AppendFormat(" Media ID: %.8X\n",
static_cast<uint32_t>(opt_exec_info->media_id)); static_cast<uint32_t>(opt_exec_info->media_id));
printf(" Title ID: %.8X\n", sb.AppendFormat(" Title ID: %.8X\n",
static_cast<uint32_t>(opt_exec_info->title_id)); static_cast<uint32_t>(opt_exec_info->title_id));
printf(" Savegame ID: %.8X\n", sb.AppendFormat(" Savegame ID: %.8X\n",
static_cast<uint32_t>(opt_exec_info->title_id)); static_cast<uint32_t>(opt_exec_info->title_id));
printf(" Disc Number / Total: %d / %d\n", opt_exec_info->disc_number, sb.AppendFormat(" Disc Number / Total: %d / %d\n",
opt_exec_info->disc_count); opt_exec_info->disc_number, opt_exec_info->disc_count);
} break; } break;
case XEX_HEADER_TITLE_WORKSPACE_SIZE: { case XEX_HEADER_TITLE_WORKSPACE_SIZE: {
printf(" XEX_HEADER_TITLE_WORKSPACE_SIZE: %d\n", sb.AppendFormat(" XEX_HEADER_TITLE_WORKSPACE_SIZE: %d\n",
uint32_t(opt_header.value)); uint32_t(opt_header.value));
} break; } break;
case XEX_HEADER_GAME_RATINGS: { case XEX_HEADER_GAME_RATINGS: {
printf(" XEX_HEADER_GAME_RATINGS (TODO):\n"); sb.AppendFormat(" XEX_HEADER_GAME_RATINGS (TODO):\n");
} break; } break;
case XEX_HEADER_LAN_KEY: { case XEX_HEADER_LAN_KEY: {
printf(" XEX_HEADER_LAN_KEY (TODO):\n"); sb.AppendFormat(" XEX_HEADER_LAN_KEY (TODO):\n");
} break; } break;
case XEX_HEADER_XBOX360_LOGO: { case XEX_HEADER_XBOX360_LOGO: {
printf(" XEX_HEADER_XBOX360_LOGO (TODO):\n"); sb.AppendFormat(" XEX_HEADER_XBOX360_LOGO (TODO):\n");
} break; } break;
case XEX_HEADER_MULTIDISC_MEDIA_IDS: { case XEX_HEADER_MULTIDISC_MEDIA_IDS: {
printf(" XEX_HEADER_MULTIDISC_MEDIA_IDS (TODO):\n"); sb.AppendFormat(" XEX_HEADER_MULTIDISC_MEDIA_IDS (TODO):\n");
} break; } break;
case XEX_HEADER_ALTERNATE_TITLE_IDS: { case XEX_HEADER_ALTERNATE_TITLE_IDS: {
printf(" XEX_HEADER_ALTERNATE_TITLE_IDS (TODO):\n"); sb.AppendFormat(" XEX_HEADER_ALTERNATE_TITLE_IDS (TODO):\n");
} break; } break;
case XEX_HEADER_ADDITIONAL_TITLE_MEMORY: { case XEX_HEADER_ADDITIONAL_TITLE_MEMORY: {
printf(" XEX_HEADER_ADDITIONAL_TITLE_MEMORY: %d\n", sb.AppendFormat(" XEX_HEADER_ADDITIONAL_TITLE_MEMORY: %d\n",
uint32_t(opt_header.value)); uint32_t(opt_header.value));
} break; } break;
case XEX_HEADER_EXPORTS_BY_NAME: { case XEX_HEADER_EXPORTS_BY_NAME: {
printf(" XEX_HEADER_EXPORTS_BY_NAME:\n"); sb.AppendFormat(" XEX_HEADER_EXPORTS_BY_NAME:\n");
auto dir = auto dir =
reinterpret_cast<const xex2_opt_data_directory*>(opt_header_ptr); reinterpret_cast<const xex2_opt_data_directory*>(opt_header_ptr);
@ -539,16 +551,16 @@ void XUserModule::Dump() {
auto name = reinterpret_cast<const char*>(e_base + name_table[n]); auto name = reinterpret_cast<const char*>(e_base + name_table[n]);
uint16_t ordinal = ordinal_table[n]; uint16_t ordinal = ordinal_table[n];
uint32_t addr = exe_address + function_table[ordinal]; uint32_t addr = exe_address + function_table[ordinal];
printf(" %-28s - %.3X - %.8X\n", name, ordinal, addr); sb.AppendFormat(" %-28s - %.3X - %.8X\n", name, ordinal, addr);
} }
} break; } break;
default: { default: {
printf(" Unknown Header %.8X\n", (uint32_t)opt_header.key); sb.AppendFormat(" Unknown Header %.8X\n", (uint32_t)opt_header.key);
} break; } break;
} }
} }
printf("Sections:\n"); sb.AppendFormat("Sections:\n");
for (uint32_t i = 0, page = 0; i < security_info->page_descriptor_count; for (uint32_t i = 0, page = 0; i < security_info->page_descriptor_count;
i++) { i++) {
// Manually byteswap the bitfield data. // Manually byteswap the bitfield data.
@ -574,9 +586,9 @@ void XUserModule::Dump() {
uint32_t start_address = security_info->load_address + (page * page_size); uint32_t start_address = security_info->load_address + (page * page_size);
uint32_t end_address = start_address + (page_descriptor.size * page_size); uint32_t end_address = start_address + (page_descriptor.size * page_size);
printf(" %3u %s %3u pages %.8X - %.8X (%d bytes)\n", page, type, sb.AppendFormat(" %3u %s %3u pages %.8X - %.8X (%d bytes)\n", page,
page_descriptor.size, start_address, end_address, type, page_descriptor.size, start_address, end_address,
page_descriptor.size * page_size); page_descriptor.size * page_size);
page += page_descriptor.size; page += page_descriptor.size;
} }
@ -584,7 +596,7 @@ void XUserModule::Dump() {
// TODO(benvanik): figure out a way to remove dependency on old xex header. // TODO(benvanik): figure out a way to remove dependency on old xex header.
auto old_header = xe_xex2_get_header(xex_module()->xex()); auto old_header = xe_xex2_get_header(xex_module()->xex());
printf("Imports:\n"); sb.AppendFormat("Imports:\n");
for (size_t n = 0; n < old_header->import_library_count; n++) { for (size_t n = 0; n < old_header->import_library_count; n++) {
const xe_xex2_import_library_t* library = &old_header->import_libraries[n]; const xe_xex2_import_library_t* library = &old_header->import_libraries[n];
@ -592,14 +604,14 @@ void XUserModule::Dump() {
size_t import_info_count; size_t import_info_count;
if (!xe_xex2_get_import_infos(xex_module()->xex(), library, &import_infos, if (!xe_xex2_get_import_infos(xex_module()->xex(), library, &import_infos,
&import_info_count)) { &import_info_count)) {
printf(" %s - %lld imports\n", library->name, import_info_count); sb.AppendFormat(" %s - %lld imports\n", library->name, import_info_count);
printf(" Version: %d.%d.%d.%d\n", library->version.major, sb.AppendFormat(" Version: %d.%d.%d.%d\n", library->version.major,
library->version.minor, library->version.build, library->version.minor, library->version.build,
library->version.qfe); library->version.qfe);
printf(" Min Version: %d.%d.%d.%d\n", library->min_version.major, sb.AppendFormat(" Min Version: %d.%d.%d.%d\n",
library->min_version.minor, library->min_version.build, library->min_version.major, library->min_version.minor,
library->min_version.qfe); library->min_version.build, library->min_version.qfe);
printf("\n"); sb.AppendFormat("\n");
// Counts. // Counts.
int known_count = 0; int known_count = 0;
@ -642,14 +654,14 @@ void XUserModule::Dump() {
} }
} }
float total_count = static_cast<float>(import_info_count) / 100.0f; float total_count = static_cast<float>(import_info_count) / 100.0f;
printf(" Total: %4llu\n", import_info_count); sb.AppendFormat(" Total: %4llu\n", import_info_count);
printf(" Known: %3d%% (%d known, %d unknown)\n", sb.AppendFormat(" Known: %3d%% (%d known, %d unknown)\n",
static_cast<int>(known_count / total_count), known_count, static_cast<int>(known_count / total_count), known_count,
unknown_count); unknown_count);
printf(" Implemented: %3d%% (%d implemented, %d unimplemented)\n", sb.AppendFormat(
static_cast<int>(impl_count / total_count), impl_count, " Implemented: %3d%% (%d implemented, %d unimplemented)\n",
unimpl_count); static_cast<int>(impl_count / total_count), impl_count, unimpl_count);
printf("\n"); sb.AppendFormat("\n");
// Listing. // Listing.
for (size_t m = 0; m < import_info_count; m++) { for (size_t m = 0; m < import_info_count; m++) {
@ -674,18 +686,22 @@ void XUserModule::Dump() {
} }
if (kernel_export && if (kernel_export &&
kernel_export->type == cpu::Export::Type::kVariable) { kernel_export->type == cpu::Export::Type::kVariable) {
printf(" V %.8X %.3X (%3d) %s %s\n", info->value_address, sb.AppendFormat(" V %.8X %.3X (%3d) %s %s\n",
info->ordinal, info->ordinal, implemented ? " " : "!!", name); info->value_address, info->ordinal, info->ordinal,
implemented ? " " : "!!", name);
} else if (info->thunk_address) { } else if (info->thunk_address) {
printf(" F %.8X %.8X %.3X (%3d) %s %s\n", info->value_address, sb.AppendFormat(" F %.8X %.8X %.3X (%3d) %s %s\n",
info->thunk_address, info->ordinal, info->ordinal, info->value_address, info->thunk_address,
implemented ? " " : "!!", name); info->ordinal, info->ordinal,
implemented ? " " : "!!", name);
} }
} }
} }
printf("\n"); sb.AppendFormat("\n");
} }
XELOGI(sb.GetString());
} }
} // namespace kernel } // namespace kernel

View File

@ -51,6 +51,14 @@ thread_local WGLEWContext* tls_wglew_context_ = nullptr;
extern "C" GLEWContext* glewGetContext() { return tls_glew_context_; } extern "C" GLEWContext* glewGetContext() { return tls_glew_context_; }
extern "C" WGLEWContext* wglewGetContext() { return tls_wglew_context_; } extern "C" WGLEWContext* wglewGetContext() { return tls_wglew_context_; }
void FatalGLError(std::string error) {
xe::FatalError(
error +
"\nEnsure you have the latest drivers for your GPU and that it supports "
"OpenGL 4.5. See http://xenia.jp/faq/ for more information and a list"
"of supported GPUs.");
}
std::unique_ptr<GLContext> GLContext::Create(Window* target_window) { std::unique_ptr<GLContext> GLContext::Create(Window* target_window) {
auto context = std::unique_ptr<GLContext>(new GLContext(target_window)); auto context = std::unique_ptr<GLContext>(new GLContext(target_window));
if (!context->Initialize(target_window)) { if (!context->Initialize(target_window)) {
@ -98,17 +106,17 @@ bool GLContext::Initialize(Window* target_window) {
pfd.iLayerType = PFD_MAIN_PLANE; pfd.iLayerType = PFD_MAIN_PLANE;
int pixel_format = ChoosePixelFormat(dc_, &pfd); int pixel_format = ChoosePixelFormat(dc_, &pfd);
if (!pixel_format) { if (!pixel_format) {
XELOGE("Unable to choose pixel format"); FatalGLError("Unable to choose pixel format.");
return false; return false;
} }
if (!SetPixelFormat(dc_, pixel_format, &pfd)) { if (!SetPixelFormat(dc_, pixel_format, &pfd)) {
XELOGE("Unable to set pixel format"); FatalGLError("Unable to set pixel format.");
return false; return false;
} }
HGLRC temp_context = wglCreateContext(dc_); HGLRC temp_context = wglCreateContext(dc_);
if (!temp_context) { if (!temp_context) {
XELOGE("Unable to create temporary GL context"); FatalGLError("Unable to create temporary GL context.");
return false; return false;
} }
wglMakeCurrent(dc_, temp_context); wglMakeCurrent(dc_, temp_context);
@ -116,16 +124,16 @@ bool GLContext::Initialize(Window* target_window) {
tls_glew_context_ = glew_context_.get(); tls_glew_context_ = glew_context_.get();
tls_wglew_context_ = wglew_context_.get(); tls_wglew_context_ = wglew_context_.get();
if (glewInit() != GLEW_OK) { if (glewInit() != GLEW_OK) {
XELOGE("Unable to initialize GLEW"); FatalGLError("Unable to initialize GLEW.");
return false; return false;
} }
if (wglewInit() != GLEW_OK) { if (wglewInit() != GLEW_OK) {
XELOGE("Unable to initialize WGLEW"); FatalGLError("Unable to initialize WGLEW.");
return false; return false;
} }
if (!WGLEW_ARB_create_context) { if (!WGLEW_ARB_create_context) {
XELOGE("WGL_ARG_create_context not supported by GL ICD"); FatalGLError("WGL_ARG_create_context not supported by GL ICD.");
return false; return false;
} }
@ -148,12 +156,12 @@ bool GLContext::Initialize(Window* target_window) {
wglMakeCurrent(nullptr, nullptr); wglMakeCurrent(nullptr, nullptr);
wglDeleteContext(temp_context); wglDeleteContext(temp_context);
if (!glrc_) { if (!glrc_) {
XELOGE("Unable to create real GL context"); FatalGLError("Unable to create real GL context.");
return false; return false;
} }
if (!MakeCurrent()) { if (!MakeCurrent()) {
XELOGE("Could not make real GL context current"); FatalGLError("Could not make real GL context current.");
return false; return false;
} }
@ -171,7 +179,7 @@ bool GLContext::Initialize(Window* target_window) {
SetupDebugging(); SetupDebugging();
if (!blitter_.Initialize()) { if (!blitter_.Initialize()) {
XELOGE("Unable to initialize blitter"); FatalGLError("Unable to initialize blitter.");
ClearCurrent(); ClearCurrent();
return false; return false;
} }
@ -204,7 +212,7 @@ std::unique_ptr<GraphicsContext> GLContext::CreateShared() {
0}; 0};
new_glrc = wglCreateContextAttribsARB(dc_, glrc_, attrib_list); new_glrc = wglCreateContextAttribsARB(dc_, glrc_, attrib_list);
if (!new_glrc) { if (!new_glrc) {
XELOGE("Could not create shared context"); FatalGLError("Could not create shared context.");
return nullptr; return nullptr;
} }
} }
@ -212,30 +220,30 @@ std::unique_ptr<GraphicsContext> GLContext::CreateShared() {
auto new_context = auto new_context =
std::unique_ptr<GLContext>(new GLContext(target_window_, new_glrc)); std::unique_ptr<GLContext>(new GLContext(target_window_, new_glrc));
if (!new_context->MakeCurrent()) { if (!new_context->MakeCurrent()) {
XELOGE("Could not make new GL context current"); FatalGLError("Could not make new GL context current.");
return nullptr; return nullptr;
} }
if (!glGetString(GL_EXTENSIONS)) { if (!glGetString(GL_EXTENSIONS)) {
new_context->ClearCurrent(); new_context->ClearCurrent();
XELOGE("New GL context did not have extensions"); FatalGLError("New GL context did not have extensions.");
return nullptr; return nullptr;
} }
if (glewInit() != GLEW_OK) { if (glewInit() != GLEW_OK) {
new_context->ClearCurrent(); new_context->ClearCurrent();
XELOGE("Unable to initialize GLEW"); FatalGLError("Unable to initialize GLEW on shared context.");
return nullptr; return nullptr;
} }
if (wglewInit() != GLEW_OK) { if (wglewInit() != GLEW_OK) {
new_context->ClearCurrent(); new_context->ClearCurrent();
XELOGE("Unable to initialize WGLEW"); FatalGLError("Unable to initialize WGLEW on shared context.");
return nullptr; return nullptr;
} }
SetupDebugging(); SetupDebugging();
if (!new_context->blitter_.Initialize()) { if (!new_context->blitter_.Initialize()) {
XELOGE("Unable to initialize blitter"); FatalGLError("Unable to initialize blitter on shared context.");
return nullptr; return nullptr;
} }
@ -244,18 +252,9 @@ std::unique_ptr<GraphicsContext> GLContext::CreateShared() {
return std::unique_ptr<GraphicsContext>(new_context.release()); return std::unique_ptr<GraphicsContext>(new_context.release());
} }
void FatalGLError(std::string error) {
XEFATAL(
(error +
"\nEnsure you have the latest drivers for your GPU and that it supports "
"OpenGL 4.5. See http://xenia.jp/faq/ for more information and a list"
"of supported GPUs.")
.c_str());
}
void GLContext::AssertExtensionsPresent() { void GLContext::AssertExtensionsPresent() {
if (!MakeCurrent()) { if (!MakeCurrent()) {
XEFATAL("Unable to make GL context current"); FatalGLError("Unable to make GL context current.");
return; return;
} }
@ -425,7 +424,7 @@ bool GLContext::MakeCurrent() {
if (FLAGS_thread_safe_gl) { if (FLAGS_thread_safe_gl) {
global_gl_mutex_.unlock(); global_gl_mutex_.unlock();
} }
XELOGE("Unable to make GL context current"); FatalGLError("Unable to make GL context current.");
return false; return false;
} }
tls_glew_context_ = glew_context_.get(); tls_glew_context_ = glew_context_.get();