align module and memory behavior for Skate 3
This commit is contained in:
parent
7325992255
commit
1081b0ea27
12 changed files with 131 additions and 73 deletions
|
|
@ -63,6 +63,7 @@ struct RuntimeConfig {
|
||||||
std::function<std::unique_ptr<system::IAudioSystem>(runtime::FunctionDispatcher*)> audio_factory;
|
std::function<std::unique_ptr<system::IAudioSystem>(runtime::FunctionDispatcher*)> audio_factory;
|
||||||
std::function<std::unique_ptr<system::IInputSystem>(bool tool_mode)> input_factory;
|
std::function<std::unique_ptr<system::IInputSystem>(bool tool_mode)> input_factory;
|
||||||
std::function<void(Runtime*, system::KernelState*)> kernel_init;
|
std::function<void(Runtime*, system::KernelState*)> kernel_init;
|
||||||
|
std::filesystem::path config_path;
|
||||||
bool tool_mode = false;
|
bool tool_mode = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -300,23 +300,24 @@ class KernelState {
|
||||||
void CompleteOverlappedImmediateEx(uint32_t overlapped_ptr, X_RESULT result,
|
void CompleteOverlappedImmediateEx(uint32_t overlapped_ptr, X_RESULT result,
|
||||||
uint32_t extended_error, uint32_t length);
|
uint32_t extended_error, uint32_t length);
|
||||||
|
|
||||||
void CompleteOverlappedDeferred(std::function<void()> completion_callback,
|
void CompleteOverlappedDeferred(std::move_only_function<void()> completion_callback,
|
||||||
uint32_t overlapped_ptr, X_RESULT result,
|
uint32_t overlapped_ptr, X_RESULT result,
|
||||||
std::function<void()> pre_callback = nullptr,
|
std::move_only_function<void()> pre_callback = nullptr,
|
||||||
std::function<void()> post_callback = nullptr);
|
std::move_only_function<void()> post_callback = nullptr);
|
||||||
void CompleteOverlappedDeferredEx(std::function<void()> completion_callback,
|
void CompleteOverlappedDeferredEx(std::move_only_function<void()> completion_callback,
|
||||||
uint32_t overlapped_ptr, X_RESULT result,
|
uint32_t overlapped_ptr, X_RESULT result,
|
||||||
uint32_t extended_error, uint32_t length,
|
uint32_t extended_error, uint32_t length,
|
||||||
std::function<void()> pre_callback = nullptr,
|
std::move_only_function<void()> pre_callback = nullptr,
|
||||||
std::function<void()> post_callback = nullptr);
|
std::move_only_function<void()> post_callback = nullptr);
|
||||||
|
|
||||||
void CompleteOverlappedDeferred(std::function<X_RESULT()> completion_callback,
|
void CompleteOverlappedDeferred(std::move_only_function<X_RESULT()> completion_callback,
|
||||||
uint32_t overlapped_ptr,
|
uint32_t overlapped_ptr,
|
||||||
std::function<void()> pre_callback = nullptr,
|
std::move_only_function<void()> pre_callback = nullptr,
|
||||||
std::function<void()> post_callback = nullptr);
|
std::move_only_function<void()> post_callback = nullptr);
|
||||||
void CompleteOverlappedDeferredEx(
|
void CompleteOverlappedDeferredEx(
|
||||||
std::function<X_RESULT(uint32_t&, uint32_t&)> completion_callback, uint32_t overlapped_ptr,
|
std::move_only_function<X_RESULT(uint32_t&, uint32_t&)> completion_callback,
|
||||||
std::function<void()> pre_callback = nullptr, std::function<void()> post_callback = nullptr);
|
uint32_t overlapped_ptr, std::move_only_function<void()> pre_callback = nullptr,
|
||||||
|
std::move_only_function<void()> post_callback = nullptr);
|
||||||
|
|
||||||
bool Save(stream::ByteStream* stream);
|
bool Save(stream::ByteStream* stream);
|
||||||
bool Restore(stream::ByteStream* stream);
|
bool Restore(stream::ByteStream* stream);
|
||||||
|
|
@ -373,7 +374,7 @@ class KernelState {
|
||||||
// Must be guarded by the global critical region.
|
// Must be guarded by the global critical region.
|
||||||
util::NativeList dpc_list_;
|
util::NativeList dpc_list_;
|
||||||
std::condition_variable_any dispatch_cond_;
|
std::condition_variable_any dispatch_cond_;
|
||||||
std::list<std::function<void()>> dispatch_queue_;
|
std::list<std::move_only_function<void()>> dispatch_queue_;
|
||||||
|
|
||||||
friend class XObject;
|
friend class XObject;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -26,8 +26,8 @@ class TimerQueue;
|
||||||
struct TimerQueueWaitItem {
|
struct TimerQueueWaitItem {
|
||||||
using clock = std::chrono::steady_clock;
|
using clock = std::chrono::steady_clock;
|
||||||
|
|
||||||
TimerQueueWaitItem(std::function<void(void*)> callback, void* userdata, TimerQueue* parent_queue,
|
TimerQueueWaitItem(std::move_only_function<void(void*)> callback, void* userdata,
|
||||||
clock::time_point due, clock::duration interval)
|
TimerQueue* parent_queue, clock::time_point due, clock::duration interval)
|
||||||
: callback_(std::move(callback)),
|
: callback_(std::move(callback)),
|
||||||
userdata_(userdata),
|
userdata_(userdata),
|
||||||
parent_queue_(parent_queue),
|
parent_queue_(parent_queue),
|
||||||
|
|
@ -55,7 +55,7 @@ struct TimerQueueWaitItem {
|
||||||
};
|
};
|
||||||
static_assert(std::atomic<State>::is_always_lock_free);
|
static_assert(std::atomic<State>::is_always_lock_free);
|
||||||
|
|
||||||
std::function<void(void*)> callback_;
|
std::move_only_function<void(void*)> callback_;
|
||||||
void* userdata_;
|
void* userdata_;
|
||||||
TimerQueue* parent_queue_;
|
TimerQueue* parent_queue_;
|
||||||
clock::time_point due_;
|
clock::time_point due_;
|
||||||
|
|
@ -63,14 +63,14 @@ struct TimerQueueWaitItem {
|
||||||
std::atomic<State> state_;
|
std::atomic<State> state_;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::weak_ptr<TimerQueueWaitItem> QueueTimerOnce(std::function<void(void*)> callback,
|
std::weak_ptr<TimerQueueWaitItem> QueueTimerOnce(std::move_only_function<void(void*)> callback,
|
||||||
void* userdata,
|
void* userdata,
|
||||||
TimerQueueWaitItem::clock::time_point due);
|
TimerQueueWaitItem::clock::time_point due);
|
||||||
|
|
||||||
// Callback is first executed at due, then again repeatedly after interval
|
// Callback is first executed at due, then again repeatedly after interval
|
||||||
// passes (unless interval == 0). The first callback will be scheduled at
|
// passes (unless interval == 0). The first callback will be scheduled at
|
||||||
// `max(now() - interval, due)` to mitigate callback flooding.
|
// `max(now() - interval, due)` to mitigate callback flooding.
|
||||||
std::weak_ptr<TimerQueueWaitItem> QueueTimerRecurring(std::function<void(void*)> callback,
|
std::weak_ptr<TimerQueueWaitItem> QueueTimerRecurring(std::move_only_function<void(void*)> callback,
|
||||||
void* userdata,
|
void* userdata,
|
||||||
TimerQueueWaitItem::clock::time_point due,
|
TimerQueueWaitItem::clock::time_point due,
|
||||||
TimerQueueWaitItem::clock::duration interval);
|
TimerQueueWaitItem::clock::duration interval);
|
||||||
|
|
|
||||||
|
|
@ -537,20 +537,76 @@ bool IsFinalized() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SaveConfig(const std::filesystem::path& config_path) {
|
void SaveConfig(const std::filesystem::path& config_path) {
|
||||||
std::string content = SerializeToTOML();
|
|
||||||
if (content.empty()) {
|
|
||||||
REXLOG_DEBUG("SaveConfig: no modified flags to save");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
toml::table config;
|
||||||
|
if (std::filesystem::exists(config_path)) {
|
||||||
|
config = toml::parse_file(config_path.string());
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard lock(GetRegistryMutex());
|
||||||
|
bool modified = false;
|
||||||
|
for (const auto& entry : GetRegistryStorage()) {
|
||||||
|
if (entry.getter() == entry.default_value) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string value = entry.getter();
|
||||||
|
switch (entry.type) {
|
||||||
|
case FlagType::Boolean:
|
||||||
|
config.insert_or_assign(entry.name, value == "true" || value == "1" || value == "yes");
|
||||||
|
break;
|
||||||
|
case FlagType::Int32:
|
||||||
|
case FlagType::Int64: {
|
||||||
|
int64_t parsed = 0;
|
||||||
|
auto [ptr, ec] = std::from_chars(value.data(), value.data() + value.size(), parsed);
|
||||||
|
if (ec == std::errc()) {
|
||||||
|
config.insert_or_assign(entry.name, parsed);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case FlagType::Uint32:
|
||||||
|
case FlagType::Uint64: {
|
||||||
|
uint64_t parsed = 0;
|
||||||
|
auto [ptr, ec] = std::from_chars(value.data(), value.data() + value.size(), parsed);
|
||||||
|
if (ec == std::errc()) {
|
||||||
|
if (parsed <= static_cast<uint64_t>(std::numeric_limits<int64_t>::max())) {
|
||||||
|
config.insert_or_assign(entry.name, static_cast<int64_t>(parsed));
|
||||||
|
} else {
|
||||||
|
config.insert_or_assign(entry.name, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case FlagType::Double: {
|
||||||
|
double parsed = 0.0;
|
||||||
|
if (ParseDouble(value, parsed)) {
|
||||||
|
config.insert_or_assign(entry.name, parsed);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case FlagType::String:
|
||||||
|
config.insert_or_assign(entry.name, value);
|
||||||
|
break;
|
||||||
|
case FlagType::Command:
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
modified = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!modified) {
|
||||||
|
REXLOG_DEBUG("SaveConfig: no modified flags to save");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::ofstream file(config_path);
|
std::ofstream file(config_path);
|
||||||
if (!file) {
|
if (!file) {
|
||||||
REXLOG_ERROR("SaveConfig: failed to open {}", config_path.string());
|
REXLOG_ERROR("SaveConfig: failed to open {}", config_path.string());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
file << "# Auto-generated cvar configuration\n";
|
file << "# Auto-generated cvar configuration\n";
|
||||||
file << content;
|
file << config;
|
||||||
REXLOG_INFO("Saved config to {}", config_path.string());
|
REXLOG_INFO("Saved config to {}", config_path.string());
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
REXLOG_ERROR("SaveConfig: {}", e.what());
|
REXLOG_ERROR("SaveConfig: {}", e.what());
|
||||||
|
|
|
||||||
|
|
@ -187,14 +187,14 @@ void TimerQueueWaitItem::Disarm() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::weak_ptr<WaitItem> QueueTimerOnce(std::function<void(void*)> callback, void* userdata,
|
std::weak_ptr<WaitItem> QueueTimerOnce(std::move_only_function<void(void*)> callback,
|
||||||
WaitItem::clock::time_point due) {
|
void* userdata, WaitItem::clock::time_point due) {
|
||||||
return timer_queue_.QueueTimer(std::make_shared<WaitItem>(
|
return timer_queue_.QueueTimer(std::make_shared<WaitItem>(
|
||||||
std::move(callback), userdata, &timer_queue_, due, WaitItem::clock::duration::zero()));
|
std::move(callback), userdata, &timer_queue_, due, WaitItem::clock::duration::zero()));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::weak_ptr<WaitItem> QueueTimerRecurring(std::function<void(void*)> callback, void* userdata,
|
std::weak_ptr<WaitItem> QueueTimerRecurring(std::move_only_function<void(void*)> callback,
|
||||||
WaitItem::clock::time_point due,
|
void* userdata, WaitItem::clock::time_point due,
|
||||||
WaitItem::clock::duration interval) {
|
WaitItem::clock::duration interval) {
|
||||||
return timer_queue_.QueueTimer(
|
return timer_queue_.QueueTimer(
|
||||||
std::make_shared<WaitItem>(std::move(callback), userdata, &timer_queue_, due, interval));
|
std::make_shared<WaitItem>(std::move(callback), userdata, &timer_queue_, due, interval));
|
||||||
|
|
|
||||||
|
|
@ -296,7 +296,7 @@ u32 FindFirstFileA_entry(mapped_string lpFileName, mapped_void lpFindFileData) {
|
||||||
}
|
}
|
||||||
|
|
||||||
FillFindData(lpFindFileData, entry);
|
FillFindData(lpFindFileData, entry);
|
||||||
REXKRNL_TRACE("rexcrt_FindFirstFileA: '{}' first match='{}' handle={:#x}", path, entry->name(),
|
REXKRNL_DEBUG("rexcrt_FindFirstFileA: '{}' first match='{}' handle={:#x}", path, entry->name(),
|
||||||
xfile->handle());
|
xfile->handle());
|
||||||
return xfile->handle();
|
return xfile->handle();
|
||||||
}
|
}
|
||||||
|
|
@ -326,24 +326,8 @@ u32 CreateDirectoryA_entry(mapped_string lpPathName, mapped_void lpSecurityAttri
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 MoveFileA_entry(mapped_string lpExistingFileName, mapped_string lpNewFileName) {
|
u32 MoveFileA_entry(mapped_string lpExistingFileName, mapped_string lpNewFileName) {
|
||||||
const char* src = static_cast<const char*>(lpExistingFileName);
|
REXKRNL_WARN("rexcrt_MoveFileA: STUB '{}' -> '{}'", static_cast<const char*>(lpExistingFileName),
|
||||||
const char* dst = static_cast<const char*>(lpNewFileName);
|
static_cast<const char*>(lpNewFileName));
|
||||||
|
|
||||||
auto* fs = REX_KERNEL_FS();
|
|
||||||
auto* src_entry = fs->ResolvePath(src);
|
|
||||||
if (!src_entry) {
|
|
||||||
REXKRNL_DEBUG("rexcrt_MoveFileA: source not found '{}'", src);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
// Win32 MoveFileA fails if the destination already exists; callers wanting
|
|
||||||
// overwrite semantics use MoveFileExA with MOVEFILE_REPLACE_EXISTING.
|
|
||||||
if (fs->ResolvePath(dst)) {
|
|
||||||
REXKRNL_DEBUG("rexcrt_MoveFileA: destination exists '{}'", dst);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
src_entry->Rename(rex::to_path(dst));
|
|
||||||
REXKRNL_TRACE("rexcrt_MoveFileA: '{}' -> '{}'", src, dst);
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -360,7 +344,7 @@ u32 GetFileAttributesA_entry(mapped_string lpFileName) {
|
||||||
REXKRNL_DEBUG("rexcrt_GetFileAttributesA: not found '{}'", path);
|
REXKRNL_DEBUG("rexcrt_GetFileAttributesA: not found '{}'", path);
|
||||||
return kInvalidHandleValue; // INVALID_FILE_ATTRIBUTES
|
return kInvalidHandleValue; // INVALID_FILE_ATTRIBUTES
|
||||||
}
|
}
|
||||||
REXKRNL_TRACE("rexcrt_GetFileAttributesA: '{}' -> attrs={:#x}", path, entry->attributes());
|
REXKRNL_DEBUG("rexcrt_GetFileAttributesA: '{}' -> attrs={:#x}", path, entry->attributes());
|
||||||
return entry->attributes();
|
return entry->attributes();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -505,7 +489,7 @@ u32 CopyFileA_entry(mapped_string lpExistingFileName, mapped_string lpNewFileNam
|
||||||
|
|
||||||
dst_file->Destroy();
|
dst_file->Destroy();
|
||||||
src_file->Destroy();
|
src_file->Destroy();
|
||||||
REXKRNL_TRACE("rexcrt_CopyFileA: '{}' -> '{}' {}", src, dst, ok ? "OK" : "FAILED");
|
REXKRNL_DEBUG("rexcrt_CopyFileA: '{}' -> '{}' {}", src, dst, ok ? "OK" : "FAILED");
|
||||||
return ok ? 1u : 0u;
|
return ok ? 1u : 0u;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -560,7 +544,7 @@ u32 GetDiskFreeSpaceExA_entry(mapped_string lpDirectoryName,
|
||||||
out[1] = static_cast<uint32_t>(free_bytes >> 32);
|
out[1] = static_cast<uint32_t>(free_bytes >> 32);
|
||||||
}
|
}
|
||||||
|
|
||||||
REXKRNL_TRACE("rexcrt_GetDiskFreeSpaceExA: '{}' total={}MB free={}MB", path,
|
REXKRNL_DEBUG("rexcrt_GetDiskFreeSpaceExA: '{}' total={}MB free={}MB", path,
|
||||||
total_bytes / (1024 * 1024), free_bytes / (1024 * 1024));
|
total_bytes / (1024 * 1024), free_bytes / (1024 * 1024));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1346,7 +1346,7 @@ u32 InterlockedPushEntrySList_entry(ppc_ptr_t<X_SLIST_HEADER> plist_ptr,
|
||||||
assert_not_null(entry);
|
assert_not_null(entry);
|
||||||
|
|
||||||
alignas(8) X_SLIST_HEADER old_hdr = *plist_ptr;
|
alignas(8) X_SLIST_HEADER old_hdr = *plist_ptr;
|
||||||
alignas(8) X_SLIST_HEADER new_hdr = {};
|
alignas(8) X_SLIST_HEADER new_hdr = {0};
|
||||||
uint32_t old_head = 0;
|
uint32_t old_head = 0;
|
||||||
do {
|
do {
|
||||||
old_hdr = *plist_ptr;
|
old_hdr = *plist_ptr;
|
||||||
|
|
|
||||||
|
|
@ -1017,54 +1017,57 @@ void KernelState::CompleteOverlappedImmediateEx(uint32_t overlapped_ptr, X_RESUL
|
||||||
CompleteOverlappedEx(overlapped_ptr, result, extended_error, length);
|
CompleteOverlappedEx(overlapped_ptr, result, extended_error, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
void KernelState::CompleteOverlappedDeferred(std::function<void()> completion_callback,
|
void KernelState::CompleteOverlappedDeferred(std::move_only_function<void()> completion_callback,
|
||||||
uint32_t overlapped_ptr, X_RESULT result,
|
uint32_t overlapped_ptr, X_RESULT result,
|
||||||
std::function<void()> pre_callback,
|
std::move_only_function<void()> pre_callback,
|
||||||
std::function<void()> post_callback) {
|
std::move_only_function<void()> post_callback) {
|
||||||
CompleteOverlappedDeferredEx(std::move(completion_callback), overlapped_ptr, result, result, 0,
|
CompleteOverlappedDeferredEx(std::move(completion_callback), overlapped_ptr, result, result, 0,
|
||||||
pre_callback, post_callback);
|
std::move(pre_callback), std::move(post_callback));
|
||||||
}
|
}
|
||||||
|
|
||||||
void KernelState::CompleteOverlappedDeferredEx(std::function<void()> completion_callback,
|
void KernelState::CompleteOverlappedDeferredEx(std::move_only_function<void()> completion_callback,
|
||||||
uint32_t overlapped_ptr, X_RESULT result,
|
uint32_t overlapped_ptr, X_RESULT result,
|
||||||
uint32_t extended_error, uint32_t length,
|
uint32_t extended_error, uint32_t length,
|
||||||
std::function<void()> pre_callback,
|
std::move_only_function<void()> pre_callback,
|
||||||
std::function<void()> post_callback) {
|
std::move_only_function<void()> post_callback) {
|
||||||
CompleteOverlappedDeferredEx(
|
CompleteOverlappedDeferredEx(
|
||||||
[completion_callback, result, extended_error, length](uint32_t& cb_extended_error,
|
[completion_callback = std::move(completion_callback), result, extended_error, length](
|
||||||
uint32_t& cb_length) -> X_RESULT {
|
uint32_t& cb_extended_error, uint32_t& cb_length) mutable -> X_RESULT {
|
||||||
completion_callback();
|
completion_callback();
|
||||||
cb_extended_error = extended_error;
|
cb_extended_error = extended_error;
|
||||||
cb_length = length;
|
cb_length = length;
|
||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
overlapped_ptr, pre_callback, post_callback);
|
overlapped_ptr, std::move(pre_callback), std::move(post_callback));
|
||||||
}
|
}
|
||||||
|
|
||||||
void KernelState::CompleteOverlappedDeferred(std::function<X_RESULT()> completion_callback,
|
void KernelState::CompleteOverlappedDeferred(std::move_only_function<X_RESULT()> completion_callback,
|
||||||
uint32_t overlapped_ptr,
|
uint32_t overlapped_ptr,
|
||||||
std::function<void()> pre_callback,
|
std::move_only_function<void()> pre_callback,
|
||||||
std::function<void()> post_callback) {
|
std::move_only_function<void()> post_callback) {
|
||||||
CompleteOverlappedDeferredEx(
|
CompleteOverlappedDeferredEx(
|
||||||
[completion_callback](uint32_t& extended_error, uint32_t& length) -> X_RESULT {
|
[completion_callback = std::move(completion_callback)](uint32_t& extended_error,
|
||||||
|
uint32_t& length) mutable -> X_RESULT {
|
||||||
auto result = completion_callback();
|
auto result = completion_callback();
|
||||||
extended_error = static_cast<uint32_t>(result);
|
extended_error = static_cast<uint32_t>(result);
|
||||||
length = 0;
|
length = 0;
|
||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
overlapped_ptr, pre_callback, post_callback);
|
overlapped_ptr, std::move(pre_callback), std::move(post_callback));
|
||||||
}
|
}
|
||||||
|
|
||||||
void KernelState::CompleteOverlappedDeferredEx(
|
void KernelState::CompleteOverlappedDeferredEx(
|
||||||
std::function<X_RESULT(uint32_t&, uint32_t&)> completion_callback, uint32_t overlapped_ptr,
|
std::move_only_function<X_RESULT(uint32_t&, uint32_t&)> completion_callback,
|
||||||
std::function<void()> pre_callback, std::function<void()> post_callback) {
|
uint32_t overlapped_ptr, std::move_only_function<void()> pre_callback,
|
||||||
|
std::move_only_function<void()> post_callback) {
|
||||||
REXSYS_DEBUG("CompleteOverlappedDeferredEx: queuing for overlapped {:08X}", overlapped_ptr);
|
REXSYS_DEBUG("CompleteOverlappedDeferredEx: queuing for overlapped {:08X}", overlapped_ptr);
|
||||||
auto ptr = memory()->TranslateVirtual(overlapped_ptr);
|
auto ptr = memory()->TranslateVirtual(overlapped_ptr);
|
||||||
XOverlappedSetResult(ptr, X_ERROR_IO_PENDING);
|
XOverlappedSetResult(ptr, X_ERROR_IO_PENDING);
|
||||||
XOverlappedSetContext(ptr, XThread::GetCurrentThreadHandle());
|
XOverlappedSetContext(ptr, XThread::GetCurrentThreadHandle());
|
||||||
auto global_lock = global_critical_region_.Acquire();
|
auto global_lock = global_critical_region_.Acquire();
|
||||||
dispatch_queue_.push_back(
|
dispatch_queue_.push_back(
|
||||||
[this, completion_callback, overlapped_ptr, pre_callback, post_callback]() {
|
[this, overlapped_ptr, completion_callback = std::move(completion_callback),
|
||||||
|
pre_callback = std::move(pre_callback), post_callback = std::move(post_callback)]() mutable {
|
||||||
REXSYS_DEBUG("Deferred overlapped {:08X}: running pre_callback", overlapped_ptr);
|
REXSYS_DEBUG("Deferred overlapped {:08X}: running pre_callback", overlapped_ptr);
|
||||||
if (pre_callback) {
|
if (pre_callback) {
|
||||||
pre_callback();
|
pre_callback();
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,11 @@ X_STATUS Runtime::Setup(RuntimeConfig config) {
|
||||||
}
|
}
|
||||||
instance_ = this;
|
instance_ = this;
|
||||||
|
|
||||||
|
if (!config.config_path.empty() && std::filesystem::exists(config.config_path)) {
|
||||||
|
REXSYS_INFO("Runtime applying config: {}", config.config_path.string());
|
||||||
|
rex::cvar::LoadConfig(config.config_path);
|
||||||
|
}
|
||||||
|
|
||||||
auto fail = [this](X_STATUS status, std::string_view reason) {
|
auto fail = [this](X_STATUS status, std::string_view reason) {
|
||||||
REXSYS_ERROR("Runtime::Setup failed: {}", reason);
|
REXSYS_ERROR("Runtime::Setup failed: {}", reason);
|
||||||
Shutdown();
|
Shutdown();
|
||||||
|
|
|
||||||
|
|
@ -23,8 +23,7 @@
|
||||||
#include <rex/system/xfile.h>
|
#include <rex/system/xfile.h>
|
||||||
#include <rex/system/xthread.h>
|
#include <rex/system/xthread.h>
|
||||||
|
|
||||||
REXCVAR_DEFINE_BOOL(xex_apply_patches, false, "Kernel",
|
REXCVAR_DEFINE_BOOL(xex_apply_patches, true, "Kernel", "Apply XEX patches");
|
||||||
"Search for and apply XEX patches (path + 'p') on module load");
|
|
||||||
|
|
||||||
namespace rex::system {
|
namespace rex::system {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ XdbfBlock XdbfWrapper::GetEntry(XdbfSection section, uint64_t id) const {
|
||||||
return block;
|
return block;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {};
|
return {0};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string XdbfWrapper::GetStringTableEntry(XLanguage language, uint16_t string_id) const {
|
std::string XdbfWrapper::GetStringTableEntry(XLanguage language, uint16_t string_id) const {
|
||||||
|
|
|
||||||
|
|
@ -159,7 +159,6 @@ bool Memory::Initialize() {
|
||||||
}
|
}
|
||||||
virtual_membase_ = mapping_base_;
|
virtual_membase_ = mapping_base_;
|
||||||
physical_membase_ = mapping_base_ + 0x100000000ull;
|
physical_membase_ = mapping_base_ + 0x100000000ull;
|
||||||
|
|
||||||
// Prepare virtual heaps.
|
// Prepare virtual heaps.
|
||||||
heaps_.v00000000.Initialize(this, virtual_membase_, memory::HeapType::kGuestVirtual, 0x00000000,
|
heaps_.v00000000.Initialize(this, virtual_membase_, memory::HeapType::kGuestVirtual, 0x00000000,
|
||||||
0x40000000, 4096);
|
0x40000000, 4096);
|
||||||
|
|
@ -186,6 +185,16 @@ bool Memory::Initialize() {
|
||||||
!REXCVAR_GET(protect_zero)
|
!REXCVAR_GET(protect_zero)
|
||||||
? memory::kMemoryProtectRead | memory::kMemoryProtectWrite
|
? memory::kMemoryProtectRead | memory::kMemoryProtectWrite
|
||||||
: memory::kMemoryProtectNoAccess);
|
: memory::kMemoryProtectNoAccess);
|
||||||
|
// Some titles read through a null base with a positive offset above the
|
||||||
|
// protected zero page during startup. Keep the guest heap metadata unchanged,
|
||||||
|
// but commit this host page so raw recompiled loads don't crash before guest
|
||||||
|
// initialization can finish.
|
||||||
|
if (!rex::memory::AllocFixed(virtual_membase_ + 0x10000, 0x10000,
|
||||||
|
rex::memory::AllocationType::kCommit,
|
||||||
|
rex::memory::PageAccess::kReadWrite)) {
|
||||||
|
REXSYS_ERROR("Unable to commit low null-offset page at 0x00010000");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
heaps_.physical.AllocFixed(0x1FFF0000, 0x10000, 0x10000, memory::kMemoryAllocationReserve,
|
heaps_.physical.AllocFixed(0x1FFF0000, 0x10000, 0x10000, memory::kMemoryAllocationReserve,
|
||||||
memory::kMemoryProtectNoAccess);
|
memory::kMemoryProtectNoAccess);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue