Logging to with a ringbuffer. Much faster.
This commit is contained in:
parent
8dd59d07ac
commit
b7203c2989
|
@ -63,6 +63,7 @@ npm-debug.log
|
|||
private/
|
||||
*.trace
|
||||
imgui.ini
|
||||
*.log
|
||||
|
||||
# ==============================================================================
|
||||
# Build system output
|
||||
|
|
|
@ -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
|
||||
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
|
||||
that there are some major work areas still untouched:
|
||||
|
||||
|
|
|
@ -25,8 +25,8 @@ and set the 'Command' to `$(SolutionDir)$(TargetPath)` and the
|
|||
'Working Directory' to `$(SolutionDir)..\..`. You can specify flags and
|
||||
the file to run in the 'Command Arguments' field (or use `--flagfile=flags.txt`).
|
||||
|
||||
To redirect output, use the following command flags:
|
||||
`--flagfile=$(SolutionDir)scratch\flags.txt 2>&1 1>$(SolutionDir)scratch\stdout.txt`
|
||||
By default logs are written to a file with the name of the executable. You can
|
||||
override this with `--log_file=log.txt`.
|
||||
|
||||
### Linux
|
||||
|
||||
|
|
|
@ -44,8 +44,8 @@ std::unique_ptr<EmulatorWindow> EmulatorWindow::Create(Emulator* emulator) {
|
|||
xe::Profiler::ThreadEnter("Win32 Loop");
|
||||
|
||||
if (!emulator_window->Initialize()) {
|
||||
XEFATAL("Failed to initialize main window");
|
||||
exit(1);
|
||||
xe::FatalError("Failed to initialize main window");
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
extern "C" {
|
||||
#include "libavutil/log.h"
|
||||
}
|
||||
} // extern "C"
|
||||
|
||||
// As with normal Microsoft, there are like twelve different ways to access
|
||||
// the audio APIs. Early games use XMA*() methods almost exclusively to touch
|
||||
|
@ -55,9 +55,7 @@ XmaDecoder::XmaDecoder(cpu::Processor* processor)
|
|||
XmaDecoder::~XmaDecoder() = default;
|
||||
|
||||
void av_log_callback(void* avcl, int level, const char* fmt, va_list va) {
|
||||
StringBuffer buff;
|
||||
buff.AppendVarargs(fmt, va);
|
||||
xe::log_line('i', "libav: %s", buff.GetString());
|
||||
xe::LogLineVarargs('A', fmt, va);
|
||||
}
|
||||
|
||||
X_STATUS XmaDecoder::Setup(kernel::KernelState* kernel_state) {
|
||||
|
|
|
@ -34,12 +34,12 @@ std::string CanonicalizePath(const std::string& original_path) {
|
|||
pos_n = std::string::npos;
|
||||
break;
|
||||
case 1:
|
||||
// Duplicate separators
|
||||
// Duplicate separators.
|
||||
path.erase(pos, 1);
|
||||
pos_n -= 1;
|
||||
break;
|
||||
case 2:
|
||||
// Potential marker for current directory
|
||||
// Potential marker for current directory.
|
||||
if (path[pos + 1] == '.') {
|
||||
path.erase(pos, 2);
|
||||
pos_n -= 2;
|
||||
|
@ -48,10 +48,10 @@ std::string CanonicalizePath(const std::string& original_path) {
|
|||
}
|
||||
break;
|
||||
case 3:
|
||||
// Potential marker for parent directory
|
||||
// Potential marker for parent directory.
|
||||
if (path[pos + 1] == '.' && path[pos + 2] == '.') {
|
||||
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(':'));
|
||||
auto req(pos + 3);
|
||||
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);
|
||||
path.erase(last, last_diff);
|
||||
pos_n = last;
|
||||
// Also remove path reference
|
||||
// Also remove path reference.
|
||||
path_breaks.erase(path_breaks.end() - 1);
|
||||
}
|
||||
} else {
|
||||
|
@ -82,12 +82,12 @@ std::string CanonicalizePath(const std::string& original_path) {
|
|||
pos = pos_n;
|
||||
}
|
||||
|
||||
// Remove trailing seperator
|
||||
// Remove trailing seperator.
|
||||
if (!path.empty() && path.back() == path_sep) {
|
||||
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)) ||
|
||||
(path.size() == 2 && path[0] == '.' && path[1] == '.')) {
|
||||
return "";
|
||||
|
@ -96,6 +96,16 @@ std::string CanonicalizePath(const std::string& original_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::LAST(false, true);
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ std::string CanonicalizePath(const std::string& original_path);
|
|||
|
||||
bool PathExists(const std::wstring& path);
|
||||
|
||||
bool CreateParentFolder(const std::wstring& path);
|
||||
bool CreateFolder(const std::wstring& path);
|
||||
bool DeleteFolder(const std::wstring& path);
|
||||
bool IsFolder(const std::wstring& path);
|
||||
|
|
|
@ -11,12 +11,15 @@
|
|||
|
||||
#include <gflags/gflags.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdarg>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
#include "xenia/base/filesystem.h"
|
||||
#include "xenia/base/main.h"
|
||||
#include "xenia/base/math.h"
|
||||
#include "xenia/base/ring_buffer.h"
|
||||
#include "xenia/base/threading.h"
|
||||
|
||||
// For MessageBox:
|
||||
|
@ -25,91 +28,150 @@
|
|||
#include "xenia/base/platform_win.h"
|
||||
#endif // XE_PLATFORM_WIN32
|
||||
|
||||
DEFINE_bool(fast_stdout, false,
|
||||
"Don't lock around stdout/stderr. May introduce weirdness.");
|
||||
DEFINE_bool(flush_stdout, true, "Flush stdout after each log line.");
|
||||
DEFINE_string(log_file, "",
|
||||
"Logs are written to the given file instead of the default.");
|
||||
DEFINE_bool(flush_log, true, "Flush log file after each log line batch.");
|
||||
|
||||
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:
|
||||
Logger() : ring_buffer_(buffer_, kBufferSize), running_(true) {
|
||||
flush_event_ = xe::threading::Event::CreateAutoResetEvent(false);
|
||||
write_thread_ =
|
||||
xe::threading::Thread::Create({}, [this]() { WriteThread(); });
|
||||
write_thread_->set_name("xe::FileLogSink Writer");
|
||||
}
|
||||
|
||||
void format_log_line(char* buffer, size_t buffer_capacity,
|
||||
const char level_char, const char* fmt, va_list args) {
|
||||
char* buffer_ptr;
|
||||
buffer_ptr = buffer;
|
||||
*(buffer_ptr++) = level_char;
|
||||
*(buffer_ptr++) = '>';
|
||||
*(buffer_ptr++) = ' ';
|
||||
buffer_ptr +=
|
||||
std::snprintf(buffer_ptr, buffer_capacity - (buffer_ptr - buffer), "%.4X",
|
||||
xe::threading::current_thread_id());
|
||||
*(buffer_ptr++) = ' ';
|
||||
~Logger() {
|
||||
running_ = false;
|
||||
flush_event_->Set();
|
||||
xe::threading::Wait(write_thread_.get(), true);
|
||||
fflush(file_);
|
||||
fclose(file_);
|
||||
}
|
||||
|
||||
// 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;
|
||||
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 {
|
||||
buffer_ptr += chars_written;
|
||||
}
|
||||
|
||||
// Add a trailing newline.
|
||||
if (buffer_ptr[-1] != '\n') {
|
||||
buffer_ptr[0] = '\n';
|
||||
buffer_ptr[1] = 0;
|
||||
auto file_path = app_name + L".log";
|
||||
file_ = xe::filesystem::OpenFile(file_path, "wt");
|
||||
}
|
||||
}
|
||||
|
||||
void log_line(const char level_char, const char* fmt, ...) {
|
||||
// SCOPE_profile_cpu_i("emu", "log_line");
|
||||
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 LogLineFormat(const char level_char, const char* fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
format_log_line(log_buffer.data(), log_buffer.capacity(), level_char, fmt,
|
||||
args);
|
||||
size_t chars_written = vsnprintf(log_format_buffer_.data(),
|
||||
log_format_buffer_.capacity(), fmt, args);
|
||||
va_end(args);
|
||||
|
||||
if (!FLAGS_fast_stdout) {
|
||||
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();
|
||||
}
|
||||
logger_.AppendLine(xe::threading::current_thread_id(), level_char,
|
||||
log_format_buffer_.data(), chars_written);
|
||||
}
|
||||
|
||||
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_start(args, fmt);
|
||||
format_log_line(log_buffer.data(), log_buffer.capacity(), 'X', fmt, args);
|
||||
LogLineVarargs('X', fmt, 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::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);
|
||||
}
|
||||
#endif // WIN32
|
||||
|
@ -117,4 +179,6 @@ void handle_fatal(const char* fmt, ...) {
|
|||
exit(1);
|
||||
}
|
||||
|
||||
void FatalError(const std::string& str) { FatalError(str.c_str()); }
|
||||
|
||||
} // namespace xe
|
||||
|
|
|
@ -11,86 +11,47 @@
|
|||
#define XENIA_BASE_LOGGING_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include "xenia/base/string.h"
|
||||
|
||||
namespace xe {
|
||||
|
||||
#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 \
|
||||
do { \
|
||||
} while (false)
|
||||
// Initializes the logging system and any outputs requested.
|
||||
// Must be called on startup.
|
||||
void InitializeLogging(const std::wstring& app_name);
|
||||
|
||||
void log_line(const char level_char, const char* fmt, ...);
|
||||
void handle_fatal(const char* fmt, ...);
|
||||
// Appends a line to the log with printf-style formatting.
|
||||
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
|
||||
#define XELOGCORE(level, fmt, ...) xe::log_line(level, fmt, ##__VA_ARGS__)
|
||||
#define XELOGCORE(level, fmt, ...) xe::LogLineFormat(level, fmt, ##__VA_ARGS__)
|
||||
#else
|
||||
#define XELOGCORE(level, fmt, ...) XE_EMPTY_MACRO
|
||||
#define XELOGCORE(level, fmt, ...) \
|
||||
do { \
|
||||
} while (false)
|
||||
#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__)
|
||||
#else
|
||||
#define XELOGE(fmt, ...) XE_EMPTY_MACRO
|
||||
#endif
|
||||
#if XE_OPTION_LOG_WARNING
|
||||
#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__)
|
||||
#else
|
||||
#define XELOGI(fmt, ...) XE_EMPTY_MACRO
|
||||
#endif
|
||||
#if XE_OPTION_LOG_DEBUG
|
||||
#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__)
|
||||
#else
|
||||
#define XELOGCPU(fmt, ...) XE_EMPTY_MACRO
|
||||
#endif
|
||||
#if XE_OPTION_LOG_APU
|
||||
#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__)
|
||||
#else
|
||||
#define XELOGGPU(fmt, ...) XE_EMPTY_MACRO
|
||||
#endif
|
||||
#if XE_OPTION_LOG_KERNEL
|
||||
#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__)
|
||||
#else
|
||||
#define XELOGFS(fmt, ...) XE_EMPTY_MACRO
|
||||
#endif
|
||||
|
||||
} // namespace xe
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include <gflags/gflags.h>
|
||||
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/base/string.h"
|
||||
|
||||
namespace xe {
|
||||
|
@ -32,6 +33,9 @@ extern "C" int main(int argc, char** argv) {
|
|||
args.push_back(xe::to_wstring(argv[n]));
|
||||
}
|
||||
|
||||
// Initialize logging. Needs parsed FLAGS.
|
||||
xe::InitializeLogging(entry_info.name);
|
||||
|
||||
// Call app-provided entry point.
|
||||
int result = entry_info.entry_point(args);
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <gflags/gflags.h>
|
||||
#include <io.h>
|
||||
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/base/platform_win.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.
|
||||
CoInitializeEx(nullptr, COINIT_MULTITHREADED);
|
||||
|
||||
// Initialize logging. Needs parsed FLAGS.
|
||||
xe::InitializeLogging(entry_info.name);
|
||||
|
||||
// Call app-provided entry point.
|
||||
int result = entry_info.entry_point(args);
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
namespace xe {
|
||||
|
||||
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) {
|
||||
count = std::min(count, capacity_);
|
||||
|
@ -37,7 +37,7 @@ size_t RingBuffer::Read(uint8_t* buffer, size_t 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_);
|
||||
if (!count) {
|
||||
return 0;
|
||||
|
|
|
@ -20,14 +20,13 @@ class RingBuffer {
|
|||
public:
|
||||
RingBuffer(uint8_t* buffer, size_t capacity);
|
||||
|
||||
size_t Read(uint8_t* buffer, size_t count);
|
||||
size_t Write(uint8_t* buffer, size_t count);
|
||||
uint8_t* buffer() const { return buffer_; }
|
||||
size_t capacity() const { return capacity_; }
|
||||
bool empty() const { return read_offset_ == write_offset_; }
|
||||
|
||||
uint8_t* buffer() { return buffer_; }
|
||||
size_t capacity() { return capacity_; }
|
||||
|
||||
size_t read_offset() { return read_offset_; }
|
||||
size_t read_count() {
|
||||
size_t read_offset() const { return read_offset_; }
|
||||
void set_read_offset(size_t offset) { read_offset_ = offset % capacity_; }
|
||||
size_t read_count() const {
|
||||
if (read_offset_ == write_offset_) {
|
||||
return 0;
|
||||
} else if (read_offset_ < write_offset_) {
|
||||
|
@ -37,8 +36,9 @@ class RingBuffer {
|
|||
}
|
||||
}
|
||||
|
||||
size_t write_offset() { return write_offset_; }
|
||||
size_t write_count() {
|
||||
size_t write_offset() const { return write_offset_; }
|
||||
void set_write_offset(size_t offset) { write_offset_ = offset % capacity_; }
|
||||
size_t write_count() const {
|
||||
if (read_offset_ == write_offset_) {
|
||||
return capacity_;
|
||||
} 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:
|
||||
uint8_t* buffer_;
|
||||
size_t capacity_;
|
||||
size_t read_offset_;
|
||||
size_t write_offset_;
|
||||
uint8_t* buffer_ = nullptr;
|
||||
size_t capacity_ = 0;
|
||||
size_t read_offset_ = 0;
|
||||
size_t write_offset_ = 0;
|
||||
};
|
||||
|
||||
} // namespace xe
|
||||
|
|
|
@ -155,14 +155,14 @@ std::string fix_path_separators(const std::string& source, char new_sep) {
|
|||
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);
|
||||
if (!path.empty()) {
|
||||
std::string::size_type from(std::string::npos);
|
||||
if (path.back() == '\\') {
|
||||
if (path.back() == sep) {
|
||||
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 (from == std::string::npos) {
|
||||
name = path.substr(pos + 1);
|
||||
|
@ -175,14 +175,14 @@ std::string find_name_from_path(const std::string& path) {
|
|||
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);
|
||||
if (!path.empty()) {
|
||||
std::wstring::size_type from(std::wstring::npos);
|
||||
if (path.back() == '\\') {
|
||||
if (path.back() == sep) {
|
||||
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 (from == std::wstring::npos) {
|
||||
name = path.substr(pos + 1);
|
||||
|
@ -195,12 +195,12 @@ std::wstring find_name_from_path(const std::wstring& path) {
|
|||
return name;
|
||||
}
|
||||
|
||||
std::string find_base_path(const std::string& path) {
|
||||
auto last_slash = path.find_last_of('\\');
|
||||
std::string find_base_path(const std::string& path, char sep) {
|
||||
auto last_slash = path.find_last_of(sep);
|
||||
if (last_slash == std::string::npos) {
|
||||
return path;
|
||||
} 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) {
|
||||
return "";
|
||||
} else {
|
||||
|
@ -211,12 +211,12 @@ std::string find_base_path(const std::string& path) {
|
|||
}
|
||||
}
|
||||
|
||||
std::wstring find_base_path(const std::wstring& path) {
|
||||
auto last_slash = path.find_last_of('\\');
|
||||
std::wstring find_base_path(const std::wstring& path, wchar_t sep) {
|
||||
auto last_slash = path.find_last_of(sep);
|
||||
if (last_slash == std::wstring::npos) {
|
||||
return path;
|
||||
} 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) {
|
||||
return L"";
|
||||
} else {
|
||||
|
|
|
@ -45,12 +45,16 @@ std::string fix_path_separators(const std::string& source,
|
|||
char new_sep = xe::kPathSeparator);
|
||||
|
||||
// Find the top directory name or filename from a path.
|
||||
std::string find_name_from_path(const std::string& path);
|
||||
std::wstring find_name_from_path(const std::wstring& path);
|
||||
std::string find_name_from_path(const std::string& 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.
|
||||
std::string find_base_path(const std::string& path);
|
||||
std::wstring find_base_path(const std::wstring& path);
|
||||
std::string find_base_path(const std::string& path,
|
||||
char sep = xe::kPathSeparator);
|
||||
std::wstring find_base_path(const std::wstring& path,
|
||||
wchar_t sep = xe::kPathSeparator);
|
||||
|
||||
} // namespace xe
|
||||
|
||||
|
|
|
@ -75,7 +75,7 @@ X64Emitter::X64Emitter(X64Backend* backend, XbyakAllocator* allocator)
|
|||
}
|
||||
|
||||
if (!cpu_.has(Xbyak::util::Cpu::tAVX)) {
|
||||
XEFATAL(
|
||||
xe::FatalError(
|
||||
"Your CPU is too old to support Xenia. See the FAQ for system "
|
||||
"requirements at http://xenia.jp");
|
||||
return;
|
||||
|
|
|
@ -22,7 +22,9 @@
|
|||
#if 0
|
||||
#define LOGPPC(fmt, ...) XELOGCORE('p', fmt, ##__VA_ARGS__)
|
||||
#else
|
||||
#define LOGPPC(fmt, ...) XE_EMPTY_MACRO
|
||||
#define LOGPPC(fmt, ...) \
|
||||
do { \
|
||||
} while (false)
|
||||
#endif
|
||||
|
||||
namespace xe {
|
||||
|
|
|
@ -46,8 +46,8 @@ std::unique_ptr<Application> Application::Create() {
|
|||
xe::Profiler::ThreadEnter("Win32 Loop");
|
||||
|
||||
if (!app->Initialize()) {
|
||||
XEFATAL("Failed to initialize application");
|
||||
exit(1);
|
||||
xe::FatalError("Failed to initialize application");
|
||||
return;
|
||||
}
|
||||
|
||||
fence.Signal();
|
||||
|
|
|
@ -228,11 +228,11 @@ X_STATUS Emulator::LaunchDiscImage(std::wstring path) {
|
|||
// Register the disc image in the virtual filesystem.
|
||||
auto device = std::make_unique<vfs::DiscImageDevice>(mount_path, path);
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -250,11 +250,12 @@ X_STATUS Emulator::LaunchStfsContainer(std::wstring path) {
|
|||
// Register the container in the virtual filesystem.
|
||||
auto device = std::make_unique<vfs::StfsContainerDevice>(mount_path, path);
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -172,7 +172,7 @@ void CommandProcessor::ClearCaches() {
|
|||
void CommandProcessor::WorkerThreadMain() {
|
||||
context_->MakeCurrent();
|
||||
if (!SetupGL()) {
|
||||
XEFATAL("Unable to setup command processor GL state");
|
||||
xe::FatalError("Unable to setup command processor GL state");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -88,7 +88,7 @@ X_STATUS GL4GraphicsSystem::Setup(cpu::Processor* processor,
|
|||
processor_context->ClearCurrent();
|
||||
});
|
||||
if (!processor_context) {
|
||||
XEFATAL(
|
||||
xe::FatalError(
|
||||
"Unable to initialize GL context. Xenia requires OpenGL 4.5. Ensure "
|
||||
"you have the latest drivers for your GPU and that it supports OpenGL "
|
||||
"4.5. See http://xenia.jp/faq/ for more information.");
|
||||
|
|
|
@ -2197,8 +2197,8 @@ int trace_viewer_main(const std::vector<std::wstring>& args) {
|
|||
loop->PostSynchronous([&window]() {
|
||||
xe::threading::set_name("Win32 Loop");
|
||||
if (!window->Initialize()) {
|
||||
XEFATAL("Failed to initialize main window");
|
||||
exit(1);
|
||||
FatalError("Failed to initialize main window");
|
||||
return;
|
||||
}
|
||||
});
|
||||
window->on_closed.AddListener([&loop](xe::ui::UIEvent* e) {
|
||||
|
|
|
@ -76,8 +76,8 @@ void GraphicsSystem::DispatchInterruptCallback(uint32_t source, uint32_t cpu) {
|
|||
}
|
||||
thread->SetActiveCpu(cpu);
|
||||
|
||||
// XELOGGPU("Dispatching GPU interrupt at %.8X w/ mode %d on cpu %d",
|
||||
// interrupt_callback_, source, cpu);
|
||||
XELOGGPU("Dispatching GPU interrupt at %.8X w/ mode %d on cpu %d",
|
||||
interrupt_callback_, source, cpu);
|
||||
|
||||
uint64_t args[] = {source, interrupt_callback_data_};
|
||||
processor_->Execute(thread->thread_state(), interrupt_callback_, args,
|
||||
|
|
|
@ -276,7 +276,7 @@ X_STATUS XUserModule::Launch(uint32_t flags) {
|
|||
|
||||
// We know this is the 'main thread'.
|
||||
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->set_name(thread_name);
|
||||
|
||||
|
@ -308,24 +308,31 @@ void XUserModule::Dump() {
|
|||
return;
|
||||
}
|
||||
|
||||
StringBuffer sb;
|
||||
|
||||
xe::cpu::ExportResolver* export_resolver =
|
||||
kernel_state_->emulator()->export_resolver();
|
||||
auto header = xex_header();
|
||||
|
||||
// XEX header.
|
||||
printf("Module %s:\n", path_.c_str());
|
||||
printf(" Module Flags: %.8X\n", (uint32_t)header->module_flags);
|
||||
sb.AppendFormat("Module %s:\n", path_.c_str());
|
||||
sb.AppendFormat(" Module Flags: %.8X\n", (uint32_t)header->module_flags);
|
||||
|
||||
// Security header
|
||||
auto security_info = xex_module()->xex_security_info();
|
||||
printf("Security Header:\n");
|
||||
printf(" Image Flags: %.8X\n", (uint32_t)security_info->image_flags);
|
||||
printf(" Load Address: %.8X\n", (uint32_t)security_info->load_address);
|
||||
printf(" Image Size: %.8X\n", (uint32_t)security_info->image_size);
|
||||
printf(" Export Table: %.8X\n", (uint32_t)security_info->export_table);
|
||||
sb.AppendFormat("Security Header:\n");
|
||||
sb.AppendFormat(" Image Flags: %.8X\n",
|
||||
(uint32_t)security_info->image_flags);
|
||||
sb.AppendFormat(" Load Address: %.8X\n",
|
||||
(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
|
||||
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++) {
|
||||
auto& opt_header = header->headers[i];
|
||||
|
@ -335,7 +342,7 @@ void XUserModule::Dump() {
|
|||
reinterpret_cast<const uint8_t*>(header) + opt_header.offset;
|
||||
switch (opt_header.key) {
|
||||
case XEX_HEADER_RESOURCE_INFO: {
|
||||
printf(" XEX_HEADER_RESOURCE_INFO:\n");
|
||||
sb.AppendFormat(" XEX_HEADER_RESOURCE_INFO:\n");
|
||||
auto opt_resource_info =
|
||||
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));
|
||||
name[8] = 0;
|
||||
|
||||
printf(" %-8s %.8X-%.8X, %db\n", name, (uint32_t)res.address,
|
||||
(uint32_t)res.address + (uint32_t)res.size,
|
||||
(uint32_t)res.size);
|
||||
sb.AppendFormat(
|
||||
" %-8s %.8X-%.8X, %db\n", name, (uint32_t)res.address,
|
||||
(uint32_t)res.address + (uint32_t)res.size, (uint32_t)res.size);
|
||||
}
|
||||
} break;
|
||||
case XEX_HEADER_FILE_FORMAT_INFO: {
|
||||
printf(" XEX_HEADER_FILE_FORMAT_INFO (TODO):\n");
|
||||
sb.AppendFormat(" XEX_HEADER_FILE_FORMAT_INFO (TODO):\n");
|
||||
} break;
|
||||
case XEX_HEADER_DELTA_PATCH_DESCRIPTOR: {
|
||||
printf(" XEX_HEADER_DELTA_PATCH_DESCRIPTOR (TODO):\n");
|
||||
sb.AppendFormat(" XEX_HEADER_DELTA_PATCH_DESCRIPTOR (TODO):\n");
|
||||
} break;
|
||||
case XEX_HEADER_BOUNDING_PATH: {
|
||||
auto opt_bound_path =
|
||||
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;
|
||||
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);
|
||||
} break;
|
||||
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;
|
||||
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);
|
||||
} break;
|
||||
case XEX_HEADER_IMPORT_LIBRARIES: {
|
||||
printf(" XEX_HEADER_IMPORT_LIBRARIES:\n");
|
||||
sb.AppendFormat(" XEX_HEADER_IMPORT_LIBRARIES:\n");
|
||||
auto opt_import_libraries =
|
||||
reinterpret_cast<const xex2_opt_import_libraries*>(opt_header_ptr);
|
||||
|
||||
|
@ -408,38 +417,41 @@ void XUserModule::Dump() {
|
|||
auto library = reinterpret_cast<const xex2_import_library*>(
|
||||
libraries + library_offset);
|
||||
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.
|
||||
xex2_version version, version_min;
|
||||
version.value = xe::byte_swap<uint32_t>(library->version.value);
|
||||
version_min.value =
|
||||
xe::byte_swap<uint32_t>(library->version_min.value);
|
||||
printf(" Version: %d.%d.%d.%d\n", version.major, version.minor,
|
||||
version.build, version.qfe);
|
||||
printf(" Min Version: %d.%d.%d.%d\n", version_min.major,
|
||||
version_min.minor, version_min.build, version_min.qfe);
|
||||
sb.AppendFormat(" Version: %d.%d.%d.%d\n", version.major,
|
||||
version.minor, version.build, version.qfe);
|
||||
sb.AppendFormat(" Min Version: %d.%d.%d.%d\n", version_min.major,
|
||||
version_min.minor, version_min.build,
|
||||
version_min.qfe);
|
||||
|
||||
library_offset += library->size;
|
||||
}
|
||||
} break;
|
||||
case XEX_HEADER_CHECKSUM_TIMESTAMP: {
|
||||
printf(" XEX_HEADER_CHECKSUM_TIMESTAMP (TODO):\n");
|
||||
sb.AppendFormat(" XEX_HEADER_CHECKSUM_TIMESTAMP (TODO):\n");
|
||||
} break;
|
||||
case XEX_HEADER_ORIGINAL_PE_NAME: {
|
||||
auto opt_pe_name =
|
||||
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;
|
||||
case XEX_HEADER_STATIC_LIBRARIES: {
|
||||
printf(" XEX_HEADER_STATIC_LIBRARIES:\n");
|
||||
sb.AppendFormat(" XEX_HEADER_STATIC_LIBRARIES:\n");
|
||||
auto opt_static_libraries =
|
||||
reinterpret_cast<const xex2_opt_static_libraries*>(opt_header_ptr);
|
||||
|
||||
uint32_t count = (opt_static_libraries->size - 4) / 0x10;
|
||||
for (uint32_t l = 0; l < count; 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_minor),
|
||||
static_cast<uint16_t>(library.version_build),
|
||||
|
@ -447,77 +459,77 @@ void XUserModule::Dump() {
|
|||
}
|
||||
} break;
|
||||
case XEX_HEADER_TLS_INFO: {
|
||||
printf(" XEX_HEADER_TLS_INFO:\n");
|
||||
sb.AppendFormat(" XEX_HEADER_TLS_INFO:\n");
|
||||
auto opt_tls_info =
|
||||
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));
|
||||
printf(" Raw Data Address: %.8X\n",
|
||||
sb.AppendFormat(" Raw Data Address: %.8X\n",
|
||||
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));
|
||||
printf(" Raw Data Size: %d\n",
|
||||
sb.AppendFormat(" Raw Data Size: %d\n",
|
||||
static_cast<uint32_t>(opt_tls_info->raw_data_size));
|
||||
} break;
|
||||
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));
|
||||
} break;
|
||||
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));
|
||||
} break;
|
||||
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));
|
||||
} break;
|
||||
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;
|
||||
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));
|
||||
} break;
|
||||
case XEX_HEADER_EXECUTION_INFO: {
|
||||
printf(" XEX_HEADER_EXECUTION_INFO:\n");
|
||||
sb.AppendFormat(" XEX_HEADER_EXECUTION_INFO:\n");
|
||||
auto opt_exec_info =
|
||||
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));
|
||||
printf(" Title ID: %.8X\n",
|
||||
sb.AppendFormat(" Title ID: %.8X\n",
|
||||
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));
|
||||
printf(" Disc Number / Total: %d / %d\n", opt_exec_info->disc_number,
|
||||
opt_exec_info->disc_count);
|
||||
sb.AppendFormat(" Disc Number / Total: %d / %d\n",
|
||||
opt_exec_info->disc_number, opt_exec_info->disc_count);
|
||||
} break;
|
||||
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));
|
||||
} break;
|
||||
case XEX_HEADER_GAME_RATINGS: {
|
||||
printf(" XEX_HEADER_GAME_RATINGS (TODO):\n");
|
||||
sb.AppendFormat(" XEX_HEADER_GAME_RATINGS (TODO):\n");
|
||||
} break;
|
||||
case XEX_HEADER_LAN_KEY: {
|
||||
printf(" XEX_HEADER_LAN_KEY (TODO):\n");
|
||||
sb.AppendFormat(" XEX_HEADER_LAN_KEY (TODO):\n");
|
||||
} break;
|
||||
case XEX_HEADER_XBOX360_LOGO: {
|
||||
printf(" XEX_HEADER_XBOX360_LOGO (TODO):\n");
|
||||
sb.AppendFormat(" XEX_HEADER_XBOX360_LOGO (TODO):\n");
|
||||
} break;
|
||||
case XEX_HEADER_MULTIDISC_MEDIA_IDS: {
|
||||
printf(" XEX_HEADER_MULTIDISC_MEDIA_IDS (TODO):\n");
|
||||
sb.AppendFormat(" XEX_HEADER_MULTIDISC_MEDIA_IDS (TODO):\n");
|
||||
} break;
|
||||
case XEX_HEADER_ALTERNATE_TITLE_IDS: {
|
||||
printf(" XEX_HEADER_ALTERNATE_TITLE_IDS (TODO):\n");
|
||||
sb.AppendFormat(" XEX_HEADER_ALTERNATE_TITLE_IDS (TODO):\n");
|
||||
} break;
|
||||
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));
|
||||
} break;
|
||||
case XEX_HEADER_EXPORTS_BY_NAME: {
|
||||
printf(" XEX_HEADER_EXPORTS_BY_NAME:\n");
|
||||
sb.AppendFormat(" XEX_HEADER_EXPORTS_BY_NAME:\n");
|
||||
auto dir =
|
||||
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]);
|
||||
uint16_t ordinal = ordinal_table[n];
|
||||
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;
|
||||
default: {
|
||||
printf(" Unknown Header %.8X\n", (uint32_t)opt_header.key);
|
||||
sb.AppendFormat(" Unknown Header %.8X\n", (uint32_t)opt_header.key);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
printf("Sections:\n");
|
||||
sb.AppendFormat("Sections:\n");
|
||||
for (uint32_t i = 0, page = 0; i < security_info->page_descriptor_count;
|
||||
i++) {
|
||||
// Manually byteswap the bitfield data.
|
||||
|
@ -574,8 +586,8 @@ void XUserModule::Dump() {
|
|||
uint32_t start_address = security_info->load_address + (page * 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,
|
||||
page_descriptor.size, start_address, end_address,
|
||||
sb.AppendFormat(" %3u %s %3u pages %.8X - %.8X (%d bytes)\n", page,
|
||||
type, page_descriptor.size, start_address, end_address,
|
||||
page_descriptor.size * page_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.
|
||||
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++) {
|
||||
const xe_xex2_import_library_t* library = &old_header->import_libraries[n];
|
||||
|
||||
|
@ -592,14 +604,14 @@ void XUserModule::Dump() {
|
|||
size_t import_info_count;
|
||||
if (!xe_xex2_get_import_infos(xex_module()->xex(), library, &import_infos,
|
||||
&import_info_count)) {
|
||||
printf(" %s - %lld imports\n", library->name, import_info_count);
|
||||
printf(" Version: %d.%d.%d.%d\n", library->version.major,
|
||||
sb.AppendFormat(" %s - %lld imports\n", library->name, import_info_count);
|
||||
sb.AppendFormat(" Version: %d.%d.%d.%d\n", library->version.major,
|
||||
library->version.minor, library->version.build,
|
||||
library->version.qfe);
|
||||
printf(" Min Version: %d.%d.%d.%d\n", library->min_version.major,
|
||||
library->min_version.minor, library->min_version.build,
|
||||
library->min_version.qfe);
|
||||
printf("\n");
|
||||
sb.AppendFormat(" Min Version: %d.%d.%d.%d\n",
|
||||
library->min_version.major, library->min_version.minor,
|
||||
library->min_version.build, library->min_version.qfe);
|
||||
sb.AppendFormat("\n");
|
||||
|
||||
// Counts.
|
||||
int known_count = 0;
|
||||
|
@ -642,14 +654,14 @@ void XUserModule::Dump() {
|
|||
}
|
||||
}
|
||||
float total_count = static_cast<float>(import_info_count) / 100.0f;
|
||||
printf(" Total: %4llu\n", import_info_count);
|
||||
printf(" Known: %3d%% (%d known, %d unknown)\n",
|
||||
sb.AppendFormat(" Total: %4llu\n", import_info_count);
|
||||
sb.AppendFormat(" Known: %3d%% (%d known, %d unknown)\n",
|
||||
static_cast<int>(known_count / total_count), known_count,
|
||||
unknown_count);
|
||||
printf(" Implemented: %3d%% (%d implemented, %d unimplemented)\n",
|
||||
static_cast<int>(impl_count / total_count), impl_count,
|
||||
unimpl_count);
|
||||
printf("\n");
|
||||
sb.AppendFormat(
|
||||
" Implemented: %3d%% (%d implemented, %d unimplemented)\n",
|
||||
static_cast<int>(impl_count / total_count), impl_count, unimpl_count);
|
||||
sb.AppendFormat("\n");
|
||||
|
||||
// Listing.
|
||||
for (size_t m = 0; m < import_info_count; m++) {
|
||||
|
@ -674,18 +686,22 @@ void XUserModule::Dump() {
|
|||
}
|
||||
if (kernel_export &&
|
||||
kernel_export->type == cpu::Export::Type::kVariable) {
|
||||
printf(" V %.8X %.3X (%3d) %s %s\n", info->value_address,
|
||||
info->ordinal, info->ordinal, implemented ? " " : "!!", name);
|
||||
sb.AppendFormat(" V %.8X %.3X (%3d) %s %s\n",
|
||||
info->value_address, info->ordinal, info->ordinal,
|
||||
implemented ? " " : "!!", name);
|
||||
} else if (info->thunk_address) {
|
||||
printf(" F %.8X %.8X %.3X (%3d) %s %s\n", info->value_address,
|
||||
info->thunk_address, info->ordinal, info->ordinal,
|
||||
sb.AppendFormat(" F %.8X %.8X %.3X (%3d) %s %s\n",
|
||||
info->value_address, info->thunk_address,
|
||||
info->ordinal, info->ordinal,
|
||||
implemented ? " " : "!!", name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
sb.AppendFormat("\n");
|
||||
}
|
||||
|
||||
XELOGI(sb.GetString());
|
||||
}
|
||||
|
||||
} // namespace kernel
|
||||
|
|
|
@ -51,6 +51,14 @@ thread_local WGLEWContext* tls_wglew_context_ = nullptr;
|
|||
extern "C" GLEWContext* glewGetContext() { return tls_glew_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) {
|
||||
auto context = std::unique_ptr<GLContext>(new GLContext(target_window));
|
||||
if (!context->Initialize(target_window)) {
|
||||
|
@ -98,17 +106,17 @@ bool GLContext::Initialize(Window* target_window) {
|
|||
pfd.iLayerType = PFD_MAIN_PLANE;
|
||||
int pixel_format = ChoosePixelFormat(dc_, &pfd);
|
||||
if (!pixel_format) {
|
||||
XELOGE("Unable to choose pixel format");
|
||||
FatalGLError("Unable to choose pixel format.");
|
||||
return false;
|
||||
}
|
||||
if (!SetPixelFormat(dc_, pixel_format, &pfd)) {
|
||||
XELOGE("Unable to set pixel format");
|
||||
FatalGLError("Unable to set pixel format.");
|
||||
return false;
|
||||
}
|
||||
|
||||
HGLRC temp_context = wglCreateContext(dc_);
|
||||
if (!temp_context) {
|
||||
XELOGE("Unable to create temporary GL context");
|
||||
FatalGLError("Unable to create temporary GL context.");
|
||||
return false;
|
||||
}
|
||||
wglMakeCurrent(dc_, temp_context);
|
||||
|
@ -116,16 +124,16 @@ bool GLContext::Initialize(Window* target_window) {
|
|||
tls_glew_context_ = glew_context_.get();
|
||||
tls_wglew_context_ = wglew_context_.get();
|
||||
if (glewInit() != GLEW_OK) {
|
||||
XELOGE("Unable to initialize GLEW");
|
||||
FatalGLError("Unable to initialize GLEW.");
|
||||
return false;
|
||||
}
|
||||
if (wglewInit() != GLEW_OK) {
|
||||
XELOGE("Unable to initialize WGLEW");
|
||||
FatalGLError("Unable to initialize WGLEW.");
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -148,12 +156,12 @@ bool GLContext::Initialize(Window* target_window) {
|
|||
wglMakeCurrent(nullptr, nullptr);
|
||||
wglDeleteContext(temp_context);
|
||||
if (!glrc_) {
|
||||
XELOGE("Unable to create real GL context");
|
||||
FatalGLError("Unable to create real GL context.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!MakeCurrent()) {
|
||||
XELOGE("Could not make real GL context current");
|
||||
FatalGLError("Could not make real GL context current.");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -171,7 +179,7 @@ bool GLContext::Initialize(Window* target_window) {
|
|||
SetupDebugging();
|
||||
|
||||
if (!blitter_.Initialize()) {
|
||||
XELOGE("Unable to initialize blitter");
|
||||
FatalGLError("Unable to initialize blitter.");
|
||||
ClearCurrent();
|
||||
return false;
|
||||
}
|
||||
|
@ -204,7 +212,7 @@ std::unique_ptr<GraphicsContext> GLContext::CreateShared() {
|
|||
0};
|
||||
new_glrc = wglCreateContextAttribsARB(dc_, glrc_, attrib_list);
|
||||
if (!new_glrc) {
|
||||
XELOGE("Could not create shared context");
|
||||
FatalGLError("Could not create shared context.");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
@ -212,30 +220,30 @@ std::unique_ptr<GraphicsContext> GLContext::CreateShared() {
|
|||
auto new_context =
|
||||
std::unique_ptr<GLContext>(new GLContext(target_window_, new_glrc));
|
||||
if (!new_context->MakeCurrent()) {
|
||||
XELOGE("Could not make new GL context current");
|
||||
FatalGLError("Could not make new GL context current.");
|
||||
return nullptr;
|
||||
}
|
||||
if (!glGetString(GL_EXTENSIONS)) {
|
||||
new_context->ClearCurrent();
|
||||
XELOGE("New GL context did not have extensions");
|
||||
FatalGLError("New GL context did not have extensions.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (glewInit() != GLEW_OK) {
|
||||
new_context->ClearCurrent();
|
||||
XELOGE("Unable to initialize GLEW");
|
||||
FatalGLError("Unable to initialize GLEW on shared context.");
|
||||
return nullptr;
|
||||
}
|
||||
if (wglewInit() != GLEW_OK) {
|
||||
new_context->ClearCurrent();
|
||||
XELOGE("Unable to initialize WGLEW");
|
||||
FatalGLError("Unable to initialize WGLEW on shared context.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SetupDebugging();
|
||||
|
||||
if (!new_context->blitter_.Initialize()) {
|
||||
XELOGE("Unable to initialize blitter");
|
||||
FatalGLError("Unable to initialize blitter on shared context.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -244,18 +252,9 @@ std::unique_ptr<GraphicsContext> GLContext::CreateShared() {
|
|||
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() {
|
||||
if (!MakeCurrent()) {
|
||||
XEFATAL("Unable to make GL context current");
|
||||
FatalGLError("Unable to make GL context current.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -425,7 +424,7 @@ bool GLContext::MakeCurrent() {
|
|||
if (FLAGS_thread_safe_gl) {
|
||||
global_gl_mutex_.unlock();
|
||||
}
|
||||
XELOGE("Unable to make GL context current");
|
||||
FatalGLError("Unable to make GL context current.");
|
||||
return false;
|
||||
}
|
||||
tls_glew_context_ = glew_context_.get();
|
||||
|
|
Loading…
Reference in New Issue