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::IInputSystem>(bool tool_mode)> input_factory;
|
||||
std::function<void(Runtime*, system::KernelState*)> kernel_init;
|
||||
std::filesystem::path config_path;
|
||||
bool tool_mode = false;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -300,23 +300,24 @@ class KernelState {
|
|||
void CompleteOverlappedImmediateEx(uint32_t overlapped_ptr, X_RESULT result,
|
||||
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,
|
||||
std::function<void()> pre_callback = nullptr,
|
||||
std::function<void()> post_callback = nullptr);
|
||||
void CompleteOverlappedDeferredEx(std::function<void()> completion_callback,
|
||||
std::move_only_function<void()> pre_callback = nullptr,
|
||||
std::move_only_function<void()> post_callback = nullptr);
|
||||
void CompleteOverlappedDeferredEx(std::move_only_function<void()> completion_callback,
|
||||
uint32_t overlapped_ptr, X_RESULT result,
|
||||
uint32_t extended_error, uint32_t length,
|
||||
std::function<void()> pre_callback = nullptr,
|
||||
std::function<void()> post_callback = nullptr);
|
||||
std::move_only_function<void()> pre_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,
|
||||
std::function<void()> pre_callback = nullptr,
|
||||
std::function<void()> post_callback = nullptr);
|
||||
std::move_only_function<void()> pre_callback = nullptr,
|
||||
std::move_only_function<void()> post_callback = nullptr);
|
||||
void CompleteOverlappedDeferredEx(
|
||||
std::function<X_RESULT(uint32_t&, uint32_t&)> completion_callback, uint32_t overlapped_ptr,
|
||||
std::function<void()> pre_callback = nullptr, std::function<void()> post_callback = nullptr);
|
||||
std::move_only_function<X_RESULT(uint32_t&, uint32_t&)> completion_callback,
|
||||
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 Restore(stream::ByteStream* stream);
|
||||
|
|
@ -373,7 +374,7 @@ class KernelState {
|
|||
// Must be guarded by the global critical region.
|
||||
util::NativeList dpc_list_;
|
||||
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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -26,8 +26,8 @@ class TimerQueue;
|
|||
struct TimerQueueWaitItem {
|
||||
using clock = std::chrono::steady_clock;
|
||||
|
||||
TimerQueueWaitItem(std::function<void(void*)> callback, void* userdata, TimerQueue* parent_queue,
|
||||
clock::time_point due, clock::duration interval)
|
||||
TimerQueueWaitItem(std::move_only_function<void(void*)> callback, void* userdata,
|
||||
TimerQueue* parent_queue, clock::time_point due, clock::duration interval)
|
||||
: callback_(std::move(callback)),
|
||||
userdata_(userdata),
|
||||
parent_queue_(parent_queue),
|
||||
|
|
@ -55,7 +55,7 @@ struct TimerQueueWaitItem {
|
|||
};
|
||||
static_assert(std::atomic<State>::is_always_lock_free);
|
||||
|
||||
std::function<void(void*)> callback_;
|
||||
std::move_only_function<void(void*)> callback_;
|
||||
void* userdata_;
|
||||
TimerQueue* parent_queue_;
|
||||
clock::time_point due_;
|
||||
|
|
@ -63,14 +63,14 @@ struct TimerQueueWaitItem {
|
|||
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,
|
||||
TimerQueueWaitItem::clock::time_point due);
|
||||
|
||||
// Callback is first executed at due, then again repeatedly after interval
|
||||
// passes (unless interval == 0). The first callback will be scheduled at
|
||||
// `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,
|
||||
TimerQueueWaitItem::clock::time_point due,
|
||||
TimerQueueWaitItem::clock::duration interval);
|
||||
|
|
|
|||
|
|
@ -537,20 +537,76 @@ bool IsFinalized() {
|
|||
}
|
||||
|
||||
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 {
|
||||
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);
|
||||
if (!file) {
|
||||
REXLOG_ERROR("SaveConfig: failed to open {}", config_path.string());
|
||||
return;
|
||||
}
|
||||
file << "# Auto-generated cvar configuration\n";
|
||||
file << content;
|
||||
file << config;
|
||||
REXLOG_INFO("Saved config to {}", config_path.string());
|
||||
} catch (const std::exception& e) {
|
||||
REXLOG_ERROR("SaveConfig: {}", e.what());
|
||||
|
|
|
|||
|
|
@ -187,14 +187,14 @@ void TimerQueueWaitItem::Disarm() {
|
|||
}
|
||||
}
|
||||
|
||||
std::weak_ptr<WaitItem> QueueTimerOnce(std::function<void(void*)> callback, void* userdata,
|
||||
WaitItem::clock::time_point due) {
|
||||
std::weak_ptr<WaitItem> QueueTimerOnce(std::move_only_function<void(void*)> callback,
|
||||
void* userdata, WaitItem::clock::time_point due) {
|
||||
return timer_queue_.QueueTimer(std::make_shared<WaitItem>(
|
||||
std::move(callback), userdata, &timer_queue_, due, WaitItem::clock::duration::zero()));
|
||||
}
|
||||
|
||||
std::weak_ptr<WaitItem> QueueTimerRecurring(std::function<void(void*)> callback, void* userdata,
|
||||
WaitItem::clock::time_point due,
|
||||
std::weak_ptr<WaitItem> QueueTimerRecurring(std::move_only_function<void(void*)> callback,
|
||||
void* userdata, WaitItem::clock::time_point due,
|
||||
WaitItem::clock::duration interval) {
|
||||
return timer_queue_.QueueTimer(
|
||||
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);
|
||||
REXKRNL_TRACE("rexcrt_FindFirstFileA: '{}' first match='{}' handle={:#x}", path, entry->name(),
|
||||
REXKRNL_DEBUG("rexcrt_FindFirstFileA: '{}' first match='{}' handle={:#x}", path, entry->name(),
|
||||
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) {
|
||||
const char* src = static_cast<const char*>(lpExistingFileName);
|
||||
const char* dst = 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);
|
||||
REXKRNL_WARN("rexcrt_MoveFileA: STUB '{}' -> '{}'", static_cast<const char*>(lpExistingFileName),
|
||||
static_cast<const char*>(lpNewFileName));
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
@ -360,7 +344,7 @@ u32 GetFileAttributesA_entry(mapped_string lpFileName) {
|
|||
REXKRNL_DEBUG("rexcrt_GetFileAttributesA: not found '{}'", path);
|
||||
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();
|
||||
}
|
||||
|
||||
|
|
@ -505,7 +489,7 @@ u32 CopyFileA_entry(mapped_string lpExistingFileName, mapped_string lpNewFileNam
|
|||
|
||||
dst_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;
|
||||
}
|
||||
|
||||
|
|
@ -560,7 +544,7 @@ u32 GetDiskFreeSpaceExA_entry(mapped_string lpDirectoryName,
|
|||
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));
|
||||
return 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1346,7 +1346,7 @@ u32 InterlockedPushEntrySList_entry(ppc_ptr_t<X_SLIST_HEADER> plist_ptr,
|
|||
assert_not_null(entry);
|
||||
|
||||
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;
|
||||
do {
|
||||
old_hdr = *plist_ptr;
|
||||
|
|
|
|||
|
|
@ -1017,54 +1017,57 @@ void KernelState::CompleteOverlappedImmediateEx(uint32_t overlapped_ptr, X_RESUL
|
|||
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,
|
||||
std::function<void()> pre_callback,
|
||||
std::function<void()> post_callback) {
|
||||
std::move_only_function<void()> pre_callback,
|
||||
std::move_only_function<void()> post_callback) {
|
||||
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 extended_error, uint32_t length,
|
||||
std::function<void()> pre_callback,
|
||||
std::function<void()> post_callback) {
|
||||
std::move_only_function<void()> pre_callback,
|
||||
std::move_only_function<void()> post_callback) {
|
||||
CompleteOverlappedDeferredEx(
|
||||
[completion_callback, result, extended_error, length](uint32_t& cb_extended_error,
|
||||
uint32_t& cb_length) -> X_RESULT {
|
||||
[completion_callback = std::move(completion_callback), result, extended_error, length](
|
||||
uint32_t& cb_extended_error, uint32_t& cb_length) mutable -> X_RESULT {
|
||||
completion_callback();
|
||||
cb_extended_error = extended_error;
|
||||
cb_length = length;
|
||||
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,
|
||||
std::function<void()> pre_callback,
|
||||
std::function<void()> post_callback) {
|
||||
std::move_only_function<void()> pre_callback,
|
||||
std::move_only_function<void()> post_callback) {
|
||||
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();
|
||||
extended_error = static_cast<uint32_t>(result);
|
||||
length = 0;
|
||||
return result;
|
||||
},
|
||||
overlapped_ptr, pre_callback, post_callback);
|
||||
overlapped_ptr, std::move(pre_callback), std::move(post_callback));
|
||||
}
|
||||
|
||||
void KernelState::CompleteOverlappedDeferredEx(
|
||||
std::function<X_RESULT(uint32_t&, uint32_t&)> completion_callback, uint32_t overlapped_ptr,
|
||||
std::function<void()> pre_callback, std::function<void()> post_callback) {
|
||||
std::move_only_function<X_RESULT(uint32_t&, uint32_t&)> completion_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);
|
||||
auto ptr = memory()->TranslateVirtual(overlapped_ptr);
|
||||
XOverlappedSetResult(ptr, X_ERROR_IO_PENDING);
|
||||
XOverlappedSetContext(ptr, XThread::GetCurrentThreadHandle());
|
||||
auto global_lock = global_critical_region_.Acquire();
|
||||
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);
|
||||
if (pre_callback) {
|
||||
pre_callback();
|
||||
|
|
|
|||
|
|
@ -62,6 +62,11 @@ X_STATUS Runtime::Setup(RuntimeConfig config) {
|
|||
}
|
||||
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) {
|
||||
REXSYS_ERROR("Runtime::Setup failed: {}", reason);
|
||||
Shutdown();
|
||||
|
|
|
|||
|
|
@ -23,8 +23,7 @@
|
|||
#include <rex/system/xfile.h>
|
||||
#include <rex/system/xthread.h>
|
||||
|
||||
REXCVAR_DEFINE_BOOL(xex_apply_patches, false, "Kernel",
|
||||
"Search for and apply XEX patches (path + 'p') on module load");
|
||||
REXCVAR_DEFINE_BOOL(xex_apply_patches, true, "Kernel", "Apply XEX patches");
|
||||
|
||||
namespace rex::system {
|
||||
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ XdbfBlock XdbfWrapper::GetEntry(XdbfSection section, uint64_t id) const {
|
|||
return block;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
return {0};
|
||||
}
|
||||
|
||||
std::string XdbfWrapper::GetStringTableEntry(XLanguage language, uint16_t string_id) const {
|
||||
|
|
|
|||
|
|
@ -159,7 +159,6 @@ bool Memory::Initialize() {
|
|||
}
|
||||
virtual_membase_ = mapping_base_;
|
||||
physical_membase_ = mapping_base_ + 0x100000000ull;
|
||||
|
||||
// Prepare virtual heaps.
|
||||
heaps_.v00000000.Initialize(this, virtual_membase_, memory::HeapType::kGuestVirtual, 0x00000000,
|
||||
0x40000000, 4096);
|
||||
|
|
@ -186,6 +185,16 @@ bool Memory::Initialize() {
|
|||
!REXCVAR_GET(protect_zero)
|
||||
? memory::kMemoryProtectRead | memory::kMemoryProtectWrite
|
||||
: 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,
|
||||
memory::kMemoryProtectNoAccess);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue