diff --git a/src/xenia/emulator.cc b/src/xenia/emulator.cc index 8030a8078..81fe5cf57 100644 --- a/src/xenia/emulator.cc +++ b/src/xenia/emulator.cc @@ -50,7 +50,24 @@ namespace xe { Emulator::Emulator(const std::wstring& command_line, const std::wstring& content_root) - : command_line_(command_line), content_root_(content_root) {} + : on_launch(), + on_exit(), + command_line_(command_line), + content_root_(content_root), + game_title_(), + display_window_(nullptr), + memory_(), + audio_system_(), + graphics_system_(), + input_system_(), + export_resolver_(), + file_system_(), + kernel_state_(), + main_thread_(), + title_id_(0), + paused_(false), + restoring_(false), + restore_fence_() {} Emulator::~Emulator() { // Note that we delete things in the reverse order they were initialized. @@ -184,10 +201,13 @@ X_STATUS Emulator::Setup( } } +#define LOAD_KERNEL_MODULE(t) \ + static_cast(kernel_state_->LoadKernelModule()) // HLE kernel modules. - kernel_state_->LoadKernelModule(); - kernel_state_->LoadKernelModule(); - kernel_state_->LoadKernelModule(); + LOAD_KERNEL_MODULE(xboxkrnl::XboxkrnlModule); + LOAD_KERNEL_MODULE(xam::XamModule); + LOAD_KERNEL_MODULE(xbdm::XbdmModule); +#undef LOAD_KERNEL_MODULE // Initialize emulator fallback exception handling last. ExceptionHandler::Install(Emulator::ExceptionCallbackThunk, this); @@ -455,7 +475,7 @@ bool Emulator::RestoreFromFile(const std::wstring& path) { kernel_state_->object_table()->GetObjectsByType(); for (auto thread : threads) { if (thread->main_thread()) { - main_thread_ = thread->thread(); + main_thread_ = thread; break; } } @@ -557,7 +577,7 @@ bool Emulator::ExceptionCallback(Exception* ex) { void Emulator::WaitUntilExit() { while (true) { - xe::threading::Wait(main_thread_, false); + xe::threading::Wait(main_thread_->thread(), false); if (restoring_) { restore_fence_.Wait(); @@ -642,12 +662,12 @@ X_STATUS Emulator::CompleteLaunch(const std::wstring& path, } } - auto main_xthread = kernel_state_->LaunchModule(module); - if (!main_xthread) { + auto main_thread = kernel_state_->LaunchModule(module); + if (!main_thread) { return X_STATUS_UNSUCCESSFUL; } - main_thread_ = main_xthread->thread(); + main_thread_ = main_thread; on_launch(); return X_STATUS_SUCCESS; diff --git a/src/xenia/emulator.h b/src/xenia/emulator.h index a60b4e2e7..efc51a8ad 100644 --- a/src/xenia/emulator.h +++ b/src/xenia/emulator.h @@ -175,11 +175,11 @@ class Emulator { std::unique_ptr file_system_; std::unique_ptr kernel_state_; - threading::Thread* main_thread_ = nullptr; - uint32_t title_id_ = 0; // Currently running title ID + kernel::object_ref main_thread_; + uint32_t title_id_; // Currently running title ID - bool paused_ = false; - bool restoring_ = false; + bool paused_; + bool restoring_; threading::Fence restore_fence_; // Fired on restore finish. }; diff --git a/src/xenia/kernel/kernel_state.cc b/src/xenia/kernel/kernel_state.cc index 94a3beac5..3d28b374a 100644 --- a/src/xenia/kernel/kernel_state.cc +++ b/src/xenia/kernel/kernel_state.cc @@ -19,13 +19,13 @@ #include "xenia/base/string.h" #include "xenia/cpu/processor.h" #include "xenia/emulator.h" -#include "xenia/kernel/notify_listener.h" #include "xenia/kernel/user_module.h" #include "xenia/kernel/util/shim_utils.h" #include "xenia/kernel/xam/xam_module.h" #include "xenia/kernel/xboxkrnl/xboxkrnl_module.h" #include "xenia/kernel/xevent.h" #include "xenia/kernel/xmodule.h" +#include "xenia/kernel/xnotifylistener.h" #include "xenia/kernel/xobject.h" #include "xenia/kernel/xthread.h" @@ -573,7 +573,7 @@ object_ref KernelState::GetThreadByID(uint32_t thread_id) { return retain_object(thread); } -void KernelState::RegisterNotifyListener(NotifyListener* listener) { +void KernelState::RegisterNotifyListener(XNotifyListener* listener) { auto global_lock = global_critical_region_.Acquire(); notify_listeners_.push_back(retain_object(listener)); @@ -597,7 +597,7 @@ void KernelState::RegisterNotifyListener(NotifyListener* listener) { } } -void KernelState::UnregisterNotifyListener(NotifyListener* listener) { +void KernelState::UnregisterNotifyListener(XNotifyListener* listener) { auto global_lock = global_critical_region_.Acquire(); for (auto it = notify_listeners_.begin(); it != notify_listeners_.end(); ++it) { diff --git a/src/xenia/kernel/kernel_state.h b/src/xenia/kernel/kernel_state.h index d78eaf421..cc92a9813 100644 --- a/src/xenia/kernel/kernel_state.h +++ b/src/xenia/kernel/kernel_state.h @@ -48,7 +48,7 @@ class Dispatcher; class XHostThread; class KernelModule; class XModule; -class NotifyListener; +class XNotifyListener; class XThread; class UserModule; @@ -158,8 +158,8 @@ class KernelState { void OnThreadExit(XThread* thread); object_ref GetThreadByID(uint32_t thread_id); - void RegisterNotifyListener(NotifyListener* listener); - void UnregisterNotifyListener(NotifyListener* listener); + void RegisterNotifyListener(XNotifyListener* listener); + void UnregisterNotifyListener(XNotifyListener* listener); void BroadcastNotification(XNotificationID id, uint32_t data); util::NativeList* dpc_list() { return &dpc_list_; } @@ -196,7 +196,7 @@ class KernelState { // Must be guarded by the global critical region. util::ObjectTable object_table_; std::unordered_map threads_by_id_; - std::vector> notify_listeners_; + std::vector> notify_listeners_; bool has_notified_startup_ = false; uint32_t process_type_ = X_PROCTYPE_USER; diff --git a/src/xenia/kernel/xam/xam_notify.cc b/src/xenia/kernel/xam/xam_notify.cc index e3765af25..d65b0906a 100644 --- a/src/xenia/kernel/xam/xam_notify.cc +++ b/src/xenia/kernel/xam/xam_notify.cc @@ -9,9 +9,9 @@ #include "xenia/base/logging.h" #include "xenia/kernel/kernel_state.h" -#include "xenia/kernel/notify_listener.h" #include "xenia/kernel/util/shim_utils.h" #include "xenia/kernel/xam/xam_private.h" +#include "xenia/kernel/xnotifylistener.h" #include "xenia/xbox.h" namespace xe { @@ -23,7 +23,7 @@ dword_result_t XamNotifyCreateListenerInternal(qword_t mask, dword_t unk, // r4=1 may indicate user process? auto listener = - object_ref(new NotifyListener(kernel_state())); + object_ref(new XNotifyListener(kernel_state())); listener->Initialize(mask); // Handle ref is incremented, so return that. @@ -48,7 +48,7 @@ dword_result_t XNotifyGetNext(dword_t handle, dword_t match_id, // Grab listener. auto listener = - kernel_state()->object_table()->LookupObject(handle); + kernel_state()->object_table()->LookupObject(handle); if (!listener) { return 0; } diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_io.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_io.cc index a3398eb1a..120da350d 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_io.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_io.cc @@ -16,6 +16,7 @@ #include "xenia/kernel/xevent.h" #include "xenia/kernel/xfile.h" #include "xenia/kernel/xiocompletion.h" +#include "xenia/kernel/xsymboliclink.h" #include "xenia/kernel/xthread.h" #include "xenia/vfs/device.h" #include "xenia/xbox.h" @@ -717,6 +718,64 @@ dword_result_t NtFlushBuffersFile( } DECLARE_XBOXKRNL_EXPORT1(NtFlushBuffersFile, kFileSystem, kStub); +// https://docs.microsoft.com/en-us/windows/win32/devnotes/ntopensymboliclinkobject +dword_result_t NtOpenSymbolicLinkObject( + lpdword_t handle_out, pointer_t object_attrs) { + if (!object_attrs) { + return X_STATUS_INVALID_PARAMETER; + } + assert_not_null(handle_out); + + assert_true(object_attrs->attributes == 64); // case insensitive + + auto object_name = + kernel_memory()->TranslateVirtual(object_attrs->name_ptr); + + std::string target_path = + object_name->to_string(kernel_memory()->virtual_membase()); + if (object_attrs->root_directory != 0) { + assert_always(); + } + + auto pos = target_path.find("\\??\\"); + if (pos != target_path.npos && pos == 0) { + target_path = target_path.substr(4); // Strip the full qualifier + } + + std::string link_path; + if (!kernel_state()->file_system()->FindSymbolicLink(target_path, + link_path)) { + return X_STATUS_NO_SUCH_FILE; + } + + object_ref symlink(new XSymbolicLink(kernel_state())); + symlink->Initialize(target_path, link_path); + + *handle_out = symlink->handle(); + + return X_STATUS_SUCCESS; +} +DECLARE_XBOXKRNL_EXPORT1(NtOpenSymbolicLinkObject, kFileSystem, kImplemented); + +// https://docs.microsoft.com/en-us/windows/win32/devnotes/ntquerysymboliclinkobject +dword_result_t NtQuerySymbolicLinkObject(dword_t handle, + pointer_t target) { + auto symlink = + kernel_state()->object_table()->LookupObject(handle); + if (!symlink) { + return X_STATUS_NO_SUCH_FILE; + } + auto length = std::min(static_cast(target->maximum_length), + symlink->target().size()); + if (length > 0) { + auto target_buf = kernel_memory()->TranslateVirtual(target->pointer); + std::memcpy(target_buf, symlink->target().c_str(), length); + } + target->length = static_cast(length); + return X_STATUS_SUCCESS; +} +DECLARE_XBOXKRNL_EXPORT1(NtQuerySymbolicLinkObject, kFileSystem, kImplemented); + dword_result_t FscGetCacheElementCount(dword_t r3) { return 0; } DECLARE_XBOXKRNL_EXPORT1(FscGetCacheElementCount, kFileSystem, kStub); diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_memory.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_memory.cc index 177398fc0..1609b7f70 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_memory.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_memory.cc @@ -543,6 +543,12 @@ dword_result_t ExAllocatePoolTypeWithTag(dword_t size, dword_t tag, } DECLARE_XBOXKRNL_EXPORT1(ExAllocatePoolTypeWithTag, kMemory, kImplemented); +dword_result_t ExAllocatePool(dword_t size) { + const uint32_t none = 0x656E6F4E; // 'None' + return ExAllocatePoolTypeWithTag(size, none, 0); +} +DECLARE_XBOXKRNL_EXPORT1(ExAllocatePool, kMemory, kImplemented); + void ExFreePool(lpvoid_t base_address) { kernel_state()->memory()->SystemHeapFree(base_address); } diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_rtl.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_rtl.cc index 2b3ab5f67..3b71a4dd5 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_rtl.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_rtl.cc @@ -312,6 +312,30 @@ dword_result_t RtlUnicodeToMultiByteN(pointer_t destination_ptr, DECLARE_XBOXKRNL_EXPORT3(RtlUnicodeToMultiByteN, kNone, kImplemented, kHighFrequency, kSketchy); +// https://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/Executable%20Images/RtlImageNtHeader.html +pointer_result_t RtlImageNtHeader(lpvoid_t module) { + if (!module) { + return 0; + } + + // Little-endian! no swapping! + + auto dos_header = module.as(); + auto dos_magic = *reinterpret_cast(&dos_header[0x00]); + if (dos_magic != 0x5A4D) { // 'MZ' + return 0; + } + auto dos_lfanew = *reinterpret_cast(&dos_header[0x3C]); + + auto nt_header = &dos_header[dos_lfanew]; + auto nt_magic = *reinterpret_cast(&nt_header[0x00]); + if (nt_magic != 0x4550) { // 'PE' + return 0; + } + return static_cast(nt_header - kernel_memory()->virtual_membase()); +} +DECLARE_XBOXKRNL_EXPORT1(RtlImageNtHeader, kNone, kImplemented); + pointer_result_t RtlImageXexHeaderField(pointer_t xex_header, dword_t field_dword) { uint32_t field_value = 0; diff --git a/src/xenia/kernel/xenumerator.cc b/src/xenia/kernel/xenumerator.cc index f60983c97..03f324dc1 100644 --- a/src/xenia/kernel/xenumerator.cc +++ b/src/xenia/kernel/xenumerator.cc @@ -14,7 +14,7 @@ namespace kernel { XEnumerator::XEnumerator(KernelState* kernel_state, size_t items_per_enumerate, size_t item_size) - : XObject(kernel_state, kTypeEnumerator), + : XObject(kernel_state, kType), items_per_enumerate_(items_per_enumerate), item_size_(item_size) {} diff --git a/src/xenia/kernel/xevent.cc b/src/xenia/kernel/xevent.cc index 8b233fb5b..0da5358bd 100644 --- a/src/xenia/kernel/xevent.cc +++ b/src/xenia/kernel/xevent.cc @@ -15,7 +15,7 @@ namespace xe { namespace kernel { -XEvent::XEvent(KernelState* kernel_state) : XObject(kernel_state, kTypeEvent) {} +XEvent::XEvent(KernelState* kernel_state) : XObject(kernel_state, kType) {} XEvent::~XEvent() = default; diff --git a/src/xenia/kernel/xfile.cc b/src/xenia/kernel/xfile.cc index 68461edce..8a38b207a 100644 --- a/src/xenia/kernel/xfile.cc +++ b/src/xenia/kernel/xfile.cc @@ -20,13 +20,11 @@ namespace xe { namespace kernel { XFile::XFile(KernelState* kernel_state, vfs::File* file, bool synchronous) - : XObject(kernel_state, kTypeFile), - file_(file), - is_synchronous_(synchronous) { + : XObject(kernel_state, kType), file_(file), is_synchronous_(synchronous) { async_event_ = threading::Event::CreateAutoResetEvent(false); } -XFile::XFile() : XObject(kTypeFile) { +XFile::XFile() : XObject(kType) { async_event_ = threading::Event::CreateAutoResetEvent(false); } diff --git a/src/xenia/kernel/xiocompletion.cc b/src/xenia/kernel/xiocompletion.cc index 0ccc733ed..829163b43 100644 --- a/src/xenia/kernel/xiocompletion.cc +++ b/src/xenia/kernel/xiocompletion.cc @@ -13,7 +13,7 @@ namespace xe { namespace kernel { XIOCompletion::XIOCompletion(KernelState* kernel_state) - : XObject(kernel_state, kTypeIOCompletion) { + : XObject(kernel_state, kType) { notification_semaphore_ = threading::Semaphore::Create(0, kMaxNotifications); } diff --git a/src/xenia/kernel/xmodule.cc b/src/xenia/kernel/xmodule.cc index bde32fb7a..0d734bad4 100644 --- a/src/xenia/kernel/xmodule.cc +++ b/src/xenia/kernel/xmodule.cc @@ -19,7 +19,7 @@ namespace xe { namespace kernel { XModule::XModule(KernelState* kernel_state, ModuleType module_type) - : XObject(kernel_state, kTypeModule), + : XObject(kernel_state, kType), module_type_(module_type), processor_module_(nullptr), hmodule_ptr_(0) { diff --git a/src/xenia/kernel/xmutant.cc b/src/xenia/kernel/xmutant.cc index 0e950c32b..690ae1f11 100644 --- a/src/xenia/kernel/xmutant.cc +++ b/src/xenia/kernel/xmutant.cc @@ -17,10 +17,9 @@ namespace xe { namespace kernel { -XMutant::XMutant() : XObject(kTypeMutant) {} +XMutant::XMutant() : XObject(kType) {} -XMutant::XMutant(KernelState* kernel_state) - : XObject(kernel_state, kTypeMutant) {} +XMutant::XMutant(KernelState* kernel_state) : XObject(kernel_state, kType) {} XMutant::~XMutant() = default; diff --git a/src/xenia/kernel/notify_listener.cc b/src/xenia/kernel/xnotifylistener.cc similarity index 75% rename from src/xenia/kernel/notify_listener.cc rename to src/xenia/kernel/xnotifylistener.cc index 81cc1d35b..747f53574 100644 --- a/src/xenia/kernel/notify_listener.cc +++ b/src/xenia/kernel/xnotifylistener.cc @@ -7,7 +7,7 @@ ****************************************************************************** */ -#include "xenia/kernel/notify_listener.h" +#include "xenia/kernel/xnotifylistener.h" #include "xenia/base/byte_stream.h" #include "xenia/kernel/kernel_state.h" @@ -15,12 +15,12 @@ namespace xe { namespace kernel { -NotifyListener::NotifyListener(KernelState* kernel_state) - : XObject(kernel_state, kTypeNotifyListener) {} +XNotifyListener::XNotifyListener(KernelState* kernel_state) + : XObject(kernel_state, kType) {} -NotifyListener::~NotifyListener() {} +XNotifyListener::~XNotifyListener() {} -void NotifyListener::Initialize(uint64_t mask) { +void XNotifyListener::Initialize(uint64_t mask) { assert_false(wait_handle_); wait_handle_ = xe::threading::Event::CreateManualResetEvent(false); @@ -29,7 +29,7 @@ void NotifyListener::Initialize(uint64_t mask) { kernel_state_->RegisterNotifyListener(this); } -void NotifyListener::EnqueueNotification(XNotificationID id, uint32_t data) { +void XNotifyListener::EnqueueNotification(XNotificationID id, uint32_t data) { // Ignore if the notification doesn't match our mask. if ((mask_ & uint64_t(1ULL << (id >> 25))) == 0) { return; @@ -47,8 +47,8 @@ void NotifyListener::EnqueueNotification(XNotificationID id, uint32_t data) { wait_handle_->Set(); } -bool NotifyListener::DequeueNotification(XNotificationID* out_id, - uint32_t* out_data) { +bool XNotifyListener::DequeueNotification(XNotificationID* out_id, + uint32_t* out_data) { auto global_lock = global_critical_region_.Acquire(); bool dequeued = false; if (notification_count_) { @@ -65,8 +65,8 @@ bool NotifyListener::DequeueNotification(XNotificationID* out_id, return dequeued; } -bool NotifyListener::DequeueNotification(XNotificationID id, - uint32_t* out_data) { +bool XNotifyListener::DequeueNotification(XNotificationID id, + uint32_t* out_data) { auto global_lock = global_critical_region_.Acquire(); bool dequeued = false; if (notification_count_) { @@ -84,7 +84,7 @@ bool NotifyListener::DequeueNotification(XNotificationID id, return dequeued; } -bool NotifyListener::Save(ByteStream* stream) { +bool XNotifyListener::Save(ByteStream* stream) { SaveObject(stream); stream->Write(mask_); @@ -99,9 +99,9 @@ bool NotifyListener::Save(ByteStream* stream) { return true; } -object_ref NotifyListener::Restore(KernelState* kernel_state, - ByteStream* stream) { - auto notify = new NotifyListener(nullptr); +object_ref XNotifyListener::Restore(KernelState* kernel_state, + ByteStream* stream) { + auto notify = new XNotifyListener(nullptr); notify->kernel_state_ = kernel_state; notify->RestoreObject(stream); @@ -115,7 +115,7 @@ object_ref NotifyListener::Restore(KernelState* kernel_state, notify->notifications_.insert(pair); } - return object_ref(notify); + return object_ref(notify); } } // namespace kernel diff --git a/src/xenia/kernel/notify_listener.h b/src/xenia/kernel/xnotifylistener.h similarity index 79% rename from src/xenia/kernel/notify_listener.h rename to src/xenia/kernel/xnotifylistener.h index 47428252a..83599dd0b 100644 --- a/src/xenia/kernel/notify_listener.h +++ b/src/xenia/kernel/xnotifylistener.h @@ -7,8 +7,8 @@ ****************************************************************************** */ -#ifndef XENIA_KERNEL_NOTIFY_LISTENER_H_ -#define XENIA_KERNEL_NOTIFY_LISTENER_H_ +#ifndef XENIA_KERNEL_XNOTIFYLISTENER_H_ +#define XENIA_KERNEL_XNOTIFYLISTENER_H_ #include #include @@ -21,12 +21,12 @@ namespace xe { namespace kernel { -class NotifyListener : public XObject { +class XNotifyListener : public XObject { public: static const Type kType = kTypeNotifyListener; - explicit NotifyListener(KernelState* kernel_state); - ~NotifyListener() override; + explicit XNotifyListener(KernelState* kernel_state); + ~XNotifyListener() override; uint64_t mask() const { return mask_; } @@ -37,8 +37,8 @@ class NotifyListener : public XObject { bool DequeueNotification(XNotificationID id, uint32_t* out_data); bool Save(ByteStream* stream) override; - static object_ref Restore(KernelState* kernel_state, - ByteStream* stream); + static object_ref Restore(KernelState* kernel_state, + ByteStream* stream); protected: xe::threading::WaitHandle* GetWaitHandle() override { @@ -56,4 +56,4 @@ class NotifyListener : public XObject { } // namespace kernel } // namespace xe -#endif // XENIA_KERNEL_NOTIFY_LISTENER_H_ +#endif // XENIA_KERNEL_XNOTIFYLISTENER_H_ diff --git a/src/xenia/kernel/xobject.cc b/src/xenia/kernel/xobject.cc index 91fad29ce..546d7a3a9 100644 --- a/src/xenia/kernel/xobject.cc +++ b/src/xenia/kernel/xobject.cc @@ -14,14 +14,15 @@ #include "xenia/base/byte_stream.h" #include "xenia/base/clock.h" #include "xenia/kernel/kernel_state.h" -#include "xenia/kernel/notify_listener.h" #include "xenia/kernel/xboxkrnl/xboxkrnl_private.h" #include "xenia/kernel/xenumerator.h" #include "xenia/kernel/xevent.h" #include "xenia/kernel/xfile.h" #include "xenia/kernel/xmodule.h" #include "xenia/kernel/xmutant.h" +#include "xenia/kernel/xnotifylistener.h" #include "xenia/kernel/xsemaphore.h" +#include "xenia/kernel/xsymboliclink.h" #include "xenia/kernel/xthread.h" namespace xe { @@ -139,13 +140,15 @@ object_ref XObject::Restore(KernelState* kernel_state, Type type, case kTypeMutant: return XMutant::Restore(kernel_state, stream); case kTypeNotifyListener: - return NotifyListener::Restore(kernel_state, stream); + return XNotifyListener::Restore(kernel_state, stream); case kTypeSemaphore: return XSemaphore::Restore(kernel_state, stream); case kTypeSession: break; case kTypeSocket: break; + case kTypeSymbolicLink: + return XSymbolicLink::Restore(kernel_state, stream); case kTypeThread: return XThread::Restore(kernel_state, stream); case kTypeTimer: diff --git a/src/xenia/kernel/xobject.h b/src/xenia/kernel/xobject.h index 3ecb908c8..280250ca5 100644 --- a/src/xenia/kernel/xobject.h +++ b/src/xenia/kernel/xobject.h @@ -121,6 +121,7 @@ class XObject { kTypeSemaphore, kTypeSession, kTypeSocket, + kTypeSymbolicLink, kTypeThread, kTypeTimer, }; diff --git a/src/xenia/kernel/xsocket.cc b/src/xenia/kernel/xsocket.cc index 571e13123..ad35ff69a 100644 --- a/src/xenia/kernel/xsocket.cc +++ b/src/xenia/kernel/xsocket.cc @@ -31,12 +31,10 @@ namespace xe { namespace kernel { -XSocket::XSocket(KernelState* kernel_state) - : XObject(kernel_state, XObject::kTypeSocket) {} +XSocket::XSocket(KernelState* kernel_state) : XObject(kernel_state, kType) {} XSocket::XSocket(KernelState* kernel_state, uint64_t native_handle) - : XObject(kernel_state, XObject::kTypeSocket), - native_handle_(native_handle) {} + : XObject(kernel_state, kType), native_handle_(native_handle) {} XSocket::~XSocket() { Close(); } diff --git a/src/xenia/kernel/xsymboliclink.cc b/src/xenia/kernel/xsymboliclink.cc new file mode 100644 index 000000000..0908375ca --- /dev/null +++ b/src/xenia/kernel/xsymboliclink.cc @@ -0,0 +1,58 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2013 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/kernel/xsymboliclink.h" + +#include "xenia/base/byte_stream.h" +#include "xenia/kernel/kernel_state.h" + +namespace xe { +namespace kernel { + +XSymbolicLink::XSymbolicLink(KernelState* kernel_state) + : XObject(kernel_state, kType), path_(), target_() {} + +XSymbolicLink::XSymbolicLink() : XObject(kType), path_(), target_() {} + +XSymbolicLink::~XSymbolicLink() {} + +void XSymbolicLink::Initialize(const std::string& path, + const std::string& target) { + path_ = path; + target_ = target; + // TODO(gibbed): kernel_state_->RegisterSymbolicLink(this); +} + +bool XSymbolicLink::Save(ByteStream* stream) { + if (!SaveObject(stream)) { + return false; + } + stream->Write(path_); + stream->Write(target_); + return true; +} + +object_ref XSymbolicLink::Restore(KernelState* kernel_state, + ByteStream* stream) { + auto symlink = new XSymbolicLink(); + symlink->kernel_state_ = kernel_state; + if (!symlink->RestoreObject(stream)) { + delete symlink; + return nullptr; + } + + auto path = stream->Read(); + auto target = stream->Read(); + symlink->Initialize(path, target); + + return object_ref(symlink); +} + +} // namespace kernel +} // namespace xe diff --git a/src/xenia/kernel/xsymboliclink.h b/src/xenia/kernel/xsymboliclink.h new file mode 100644 index 000000000..7ca5dc58e --- /dev/null +++ b/src/xenia/kernel/xsymboliclink.h @@ -0,0 +1,50 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2013 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_KERNEL_XSYMBOLICLINK_H_ +#define XENIA_KERNEL_XSYMBOLICLINK_H_ + +#include +#include + +#include "xenia/base/mutex.h" +#include "xenia/base/threading.h" +#include "xenia/kernel/xobject.h" +#include "xenia/xbox.h" + +namespace xe { +namespace kernel { + +class XSymbolicLink : public XObject { + public: + static const Type kType = kTypeSymbolicLink; + + explicit XSymbolicLink(KernelState* kernel_state); + ~XSymbolicLink() override; + + void Initialize(const std::string& path, const std::string& target); + + bool Save(ByteStream* stream) override; + static object_ref Restore(KernelState* kernel_state, + ByteStream* stream); + + const std ::string& path() const { return path_; } + const std ::string& target() const { return target_; } + + private: + XSymbolicLink(); + + std::string path_; + std::string target_; +}; + +} // namespace kernel +} // namespace xe + +#endif // XENIA_KERNEL_XSYMBOLICLINK_H_ diff --git a/src/xenia/kernel/xthread.cc b/src/xenia/kernel/xthread.cc index 137c15d02..97bc8d4ad 100644 --- a/src/xenia/kernel/xthread.cc +++ b/src/xenia/kernel/xthread.cc @@ -49,13 +49,13 @@ using xe::cpu::ppc::PPCOpcode; uint32_t next_xthread_id_ = 0; XThread::XThread(KernelState* kernel_state) - : XObject(kernel_state, kTypeThread), guest_thread_(true) {} + : XObject(kernel_state, kType), guest_thread_(true) {} XThread::XThread(KernelState* kernel_state, uint32_t stack_size, uint32_t xapi_thread_startup, uint32_t start_address, uint32_t start_context, uint32_t creation_flags, bool guest_thread, bool main_thread) - : XObject(kernel_state, kTypeThread), + : XObject(kernel_state, kType), thread_id_(++next_xthread_id_), guest_thread_(guest_thread), main_thread_(main_thread), diff --git a/src/xenia/kernel/xtimer.cc b/src/xenia/kernel/xtimer.cc index bda94f3c6..b44e80a50 100644 --- a/src/xenia/kernel/xtimer.cc +++ b/src/xenia/kernel/xtimer.cc @@ -17,7 +17,7 @@ namespace xe { namespace kernel { -XTimer::XTimer(KernelState* kernel_state) : XObject(kernel_state, kTypeTimer) {} +XTimer::XTimer(KernelState* kernel_state) : XObject(kernel_state, kType) {} XTimer::~XTimer() = default; diff --git a/src/xenia/vfs/device.h b/src/xenia/vfs/device.h index b5f1ed6cb..310cc6426 100644 --- a/src/xenia/vfs/device.h +++ b/src/xenia/vfs/device.h @@ -31,7 +31,7 @@ class Device { virtual bool is_read_only() const { return true; } virtual void Dump(StringBuffer* string_buffer) = 0; - virtual Entry* ResolvePath(std::string path) = 0; + virtual Entry* ResolvePath(const std::string& path) = 0; virtual uint32_t total_allocation_units() const = 0; virtual uint32_t available_allocation_units() const = 0; diff --git a/src/xenia/vfs/devices/disc_image_device.cc b/src/xenia/vfs/devices/disc_image_device.cc index 613cafb92..025686ce0 100644 --- a/src/xenia/vfs/devices/disc_image_device.cc +++ b/src/xenia/vfs/devices/disc_image_device.cc @@ -54,7 +54,7 @@ void DiscImageDevice::Dump(StringBuffer* string_buffer) { root_entry_->Dump(string_buffer, 0); } -Entry* DiscImageDevice::ResolvePath(std::string path) { +Entry* DiscImageDevice::ResolvePath(const std::string& path) { // The filesystem will have stripped our prefix off already, so the path will // be in the form: // some\PATH.foo diff --git a/src/xenia/vfs/devices/disc_image_device.h b/src/xenia/vfs/devices/disc_image_device.h index 8b3273947..9b26a74a2 100644 --- a/src/xenia/vfs/devices/disc_image_device.h +++ b/src/xenia/vfs/devices/disc_image_device.h @@ -29,7 +29,7 @@ class DiscImageDevice : public Device { bool Initialize() override; void Dump(StringBuffer* string_buffer) override; - Entry* ResolvePath(std::string path) override; + Entry* ResolvePath(const std::string& path) override; uint32_t total_allocation_units() const override { return uint32_t(mmap_->size() / sectors_per_allocation_unit() / diff --git a/src/xenia/vfs/devices/host_path_device.cc b/src/xenia/vfs/devices/host_path_device.cc index 1347cd8ec..e61d2a6c0 100644 --- a/src/xenia/vfs/devices/host_path_device.cc +++ b/src/xenia/vfs/devices/host_path_device.cc @@ -48,7 +48,7 @@ void HostPathDevice::Dump(StringBuffer* string_buffer) { root_entry_->Dump(string_buffer, 0); } -Entry* HostPathDevice::ResolvePath(std::string path) { +Entry* HostPathDevice::ResolvePath(const std::string& path) { // The filesystem will have stripped our prefix off already, so the path will // be in the form: // some\PATH.foo diff --git a/src/xenia/vfs/devices/host_path_device.h b/src/xenia/vfs/devices/host_path_device.h index 751092b91..d000a43a3 100644 --- a/src/xenia/vfs/devices/host_path_device.h +++ b/src/xenia/vfs/devices/host_path_device.h @@ -27,7 +27,7 @@ class HostPathDevice : public Device { bool Initialize() override; void Dump(StringBuffer* string_buffer) override; - Entry* ResolvePath(std::string path) override; + Entry* ResolvePath(const std::string& path) override; bool is_read_only() const override { return read_only_; } diff --git a/src/xenia/vfs/devices/stfs_container_device.cc b/src/xenia/vfs/devices/stfs_container_device.cc index b93173319..728bba7c5 100644 --- a/src/xenia/vfs/devices/stfs_container_device.cc +++ b/src/xenia/vfs/devices/stfs_container_device.cc @@ -149,7 +149,7 @@ void StfsContainerDevice::Dump(StringBuffer* string_buffer) { root_entry_->Dump(string_buffer, 0); } -Entry* StfsContainerDevice::ResolvePath(std::string path) { +Entry* StfsContainerDevice::ResolvePath(const std::string& path) { // The filesystem will have stripped our prefix off already, so the path will // be in the form: // some\PATH.foo diff --git a/src/xenia/vfs/devices/stfs_container_device.h b/src/xenia/vfs/devices/stfs_container_device.h index 9819084f0..4ca6618e9 100644 --- a/src/xenia/vfs/devices/stfs_container_device.h +++ b/src/xenia/vfs/devices/stfs_container_device.h @@ -171,7 +171,7 @@ class StfsContainerDevice : public Device { bool Initialize() override; void Dump(StringBuffer* string_buffer) override; - Entry* ResolvePath(std::string path) override; + Entry* ResolvePath(const std::string& path) override; uint32_t total_allocation_units() const override { return uint32_t(mmap_total_size_ / sectors_per_allocation_unit() / diff --git a/src/xenia/vfs/virtual_file_system.cc b/src/xenia/vfs/virtual_file_system.cc index a6be5f3cc..aae42a1f0 100644 --- a/src/xenia/vfs/virtual_file_system.cc +++ b/src/xenia/vfs/virtual_file_system.cc @@ -66,15 +66,40 @@ bool VirtualFileSystem::UnregisterSymbolicLink(const std::string& path) { return true; } -bool VirtualFileSystem::IsSymbolicLink(const std::string& path) { - auto global_lock = global_critical_region_.Acquire(); - auto it = symlinks_.find(path); - if (it == symlinks_.end()) { +bool VirtualFileSystem::FindSymbolicLink(const std::string& path, + std::string& target) { + auto it = + std::find_if(symlinks_.cbegin(), symlinks_.cend(), [&](const auto& s) { + return xe::find_first_of_case(path, s.first) == 0; + }); + if (it == symlinks_.cend()) { return false; } + target = (*it).second; return true; } +bool VirtualFileSystem::ResolveSymbolicLink(const std::string& path, + std::string& result) { + result = path; + bool was_resolved = false; + while (true) { + auto it = + std::find_if(symlinks_.cbegin(), symlinks_.cend(), [&](const auto& s) { + return xe::find_first_of_case(result, s.first) == 0; + }); + if (it == symlinks_.cend()) { + break; + } + // Found symlink! + auto target_path = (*it).second; + auto relative_path = result.substr((*it).first.size()); + result = target_path + relative_path; + was_resolved = true; + } + return was_resolved; +} + Entry* VirtualFileSystem::ResolvePath(const std::string& path) { auto global_lock = global_critical_region_.Acquire(); @@ -82,54 +107,24 @@ Entry* VirtualFileSystem::ResolvePath(const std::string& path) { std::string normalized_path(xe::filesystem::CanonicalizePath(path)); // Resolve symlinks. - std::string device_path; - std::string relative_path; - for (int i = 0; i < 2; i++) { - for (const auto& it : symlinks_) { - if (xe::find_first_of_case(normalized_path, it.first) == 0) { - // Found symlink! - device_path = it.second; - if (relative_path.empty()) { - relative_path = normalized_path.substr(it.first.size()); - } - - // Bit of a cheaty move here, but allows double symlinks to be resolved. - normalized_path = device_path; - break; - } - } - - // Break as soon as we've completely resolved the symlinks to a device. - if (!IsSymbolicLink(device_path)) { - break; - } + std::string resolved_path; + if (ResolveSymbolicLink(normalized_path, resolved_path)) { + normalized_path = resolved_path; } - if (device_path.empty()) { - // Symlink wasn't passed in - Check if we've received a raw device name. - for (auto& device : devices_) { - if (xe::find_first_of_case(normalized_path, device->mount_path()) == 0) { - device_path = device->mount_path(); - relative_path = normalized_path.substr(device_path.size()); - } - } - } - - if (device_path.empty()) { - XELOGE("ResolvePath(%s) failed - no root found", path.c_str()); + // Find the device. + auto it = + std::find_if(devices_.cbegin(), devices_.cend(), [&](const auto& d) { + return xe::find_first_of_case(normalized_path, d->mount_path()) == 0; + }); + if (it == devices_.cend()) { + XELOGE("ResolvePath(%s) failed - device not found", path.c_str()); return nullptr; } - // Scan all devices. - for (auto& device : devices_) { - if (strcasecmp(device_path.c_str(), device->mount_path().c_str()) == 0) { - return device->ResolvePath(relative_path); - } - } - - XELOGE("ResolvePath(%s) failed - device not found (%s)", path.c_str(), - device_path.c_str()); - return nullptr; + const auto& device = *it; + auto relative_path = normalized_path.substr(device->mount_path().size()); + return device->ResolvePath(relative_path); } Entry* VirtualFileSystem::ResolveBasePath(const std::string& path) { diff --git a/src/xenia/vfs/virtual_file_system.h b/src/xenia/vfs/virtual_file_system.h index 94a2872ec..523299ad7 100644 --- a/src/xenia/vfs/virtual_file_system.h +++ b/src/xenia/vfs/virtual_file_system.h @@ -33,7 +33,7 @@ class VirtualFileSystem { bool RegisterSymbolicLink(const std::string& path, const std::string& target); bool UnregisterSymbolicLink(const std::string& path); - bool IsSymbolicLink(const std::string& path); + bool FindSymbolicLink(const std::string& path, std::string& target); Entry* ResolvePath(const std::string& path); Entry* ResolveBasePath(const std::string& path); @@ -50,6 +50,8 @@ class VirtualFileSystem { xe::global_critical_region global_critical_region_; std::vector> devices_; std::unordered_map symlinks_; + + bool ResolveSymbolicLink(const std::string& path, std::string& result); }; } // namespace vfs