summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgibbed <rick@gibbed.us>2020-11-23 06:22:40 -0600
committerRick Gibbed <rick@gibbed.us>2020-11-27 14:19:30 -0600
commit319699130a3406c00c804da41923489ea03ddb8c (patch)
tree576fb5d80f7e6c82cd20fb311d32484ddc9ddbcf
parent5328b72268976ef8fd53d8a2750da480d12847a2 (diff)
[XAM] Improvement rollup. Content, enumerators...
- [Kernel] Create guest object for XEnumerator. - [XAM] Split content data into host/guest variants. - [XAM] Correct message return type from RESULT to HRESULT. - [XAM] Add a new dummy content device for ODD. - [XAM] Implement XamContentAggregateCreateEnumerator. - [XAM] Implement XamGetPrivateEnumStructureFromHandle. - [XAM] Implement XMsgCompleteIORequest (sketchy). - [XAM] Implement XamGetOverlappedResult (sketchy). - [XAM] Implement XamTaskSchedule (sketchy).
-rw-r--r--src/xenia/base/string_util.h43
-rw-r--r--src/xenia/kernel/xam/app_manager.cc16
-rw-r--r--src/xenia/kernel/xam/app_manager.h10
-rw-r--r--src/xenia/kernel/xam/apps/xam_app.cc58
-rw-r--r--src/xenia/kernel/xam/apps/xam_app.h4
-rw-r--r--src/xenia/kernel/xam/apps/xgi_app.cc20
-rw-r--r--src/xenia/kernel/xam/apps/xgi_app.h4
-rw-r--r--src/xenia/kernel/xam/apps/xlivebase_app.cc18
-rw-r--r--src/xenia/kernel/xam/apps/xlivebase_app.h4
-rw-r--r--src/xenia/kernel/xam/apps/xmp_app.cc82
-rw-r--r--src/xenia/kernel/xam/apps/xmp_app.h35
-rw-r--r--src/xenia/kernel/xam/content_manager.cc26
-rw-r--r--src/xenia/kernel/xam/content_manager.h108
-rw-r--r--src/xenia/kernel/xam/xam_content.cc59
-rw-r--r--src/xenia/kernel/xam/xam_content_aggregate.cc135
-rw-r--r--src/xenia/kernel/xam/xam_content_device.cc106
-rw-r--r--src/xenia/kernel/xam/xam_content_device.h43
-rw-r--r--src/xenia/kernel/xam/xam_enum.cc55
-rw-r--r--src/xenia/kernel/xam/xam_module_export_groups.inc2
-rw-r--r--src/xenia/kernel/xam/xam_msg.cc89
-rw-r--r--src/xenia/kernel/xam/xam_task.cc66
-rw-r--r--src/xenia/kernel/xam/xam_user.cc9
-rw-r--r--src/xenia/kernel/xenumerator.cc29
-rw-r--r--src/xenia/kernel/xenumerator.h34
-rw-r--r--src/xenia/xbox.h3
25 files changed, 811 insertions, 247 deletions
diff --git a/src/xenia/base/string_util.h b/src/xenia/base/string_util.h
index adb2012af..59d6e90f6 100644
--- a/src/xenia/base/string_util.h
+++ b/src/xenia/base/string_util.h
@@ -34,6 +34,11 @@
namespace xe {
namespace string_util {
+enum class Safety {
+ IDontKnowWhatIAmDoing,
+ IKnowWhatIAmDoing,
+};
+
inline size_t copy_truncating(char* dest, const std::string_view source,
size_t dest_buffer_count) {
if (!dest_buffer_count) {
@@ -68,6 +73,44 @@ inline size_t copy_and_swap_truncating(char16_t* dest,
return chars_copied;
}
+template <Safety safety = Safety::IDontKnowWhatIAmDoing>
+inline size_t copy_maybe_truncating(char* dest, const std::string_view source,
+ size_t dest_buffer_count) {
+ static_assert(safety == Safety::IKnowWhatIAmDoing);
+ if (!dest_buffer_count) {
+ return 0;
+ }
+ size_t chars_copied = std::min(source.size(), dest_buffer_count);
+ std::memcpy(dest, source.data(), chars_copied);
+ return chars_copied;
+}
+
+template <Safety safety = Safety::IDontKnowWhatIAmDoing>
+inline size_t copy_maybe_truncating(char16_t* dest,
+ const std::u16string_view source,
+ size_t dest_buffer_count) {
+ static_assert(safety == Safety::IKnowWhatIAmDoing);
+ if (!dest_buffer_count) {
+ return 0;
+ }
+ size_t chars_copied = std::min(source.size(), dest_buffer_count);
+ std::memcpy(dest, source.data(), chars_copied * sizeof(char16_t));
+ return chars_copied;
+}
+
+template <Safety safety = Safety::IDontKnowWhatIAmDoing>
+inline size_t copy_and_swap_maybe_truncating(char16_t* dest,
+ const std::u16string_view source,
+ size_t dest_buffer_count) {
+ static_assert(safety == Safety::IKnowWhatIAmDoing);
+ if (!dest_buffer_count) {
+ return 0;
+ }
+ size_t chars_copied = std::min(source.size(), dest_buffer_count);
+ xe::copy_and_swap(dest, source.data(), chars_copied);
+ return chars_copied;
+}
+
inline std::string to_hex_string(uint32_t value) {
return fmt::format("{:08X}", value);
}
diff --git a/src/xenia/kernel/xam/app_manager.cc b/src/xenia/kernel/xam/app_manager.cc
index a8c5a9501..b9727eb4b 100644
--- a/src/xenia/kernel/xam/app_manager.cc
+++ b/src/xenia/kernel/xam/app_manager.cc
@@ -37,22 +37,22 @@ void AppManager::RegisterApp(std::unique_ptr<App> app) {
apps_.push_back(std::move(app));
}
-X_RESULT AppManager::DispatchMessageSync(uint32_t app_id, uint32_t message,
- uint32_t buffer_ptr,
- uint32_t buffer_length) {
+X_HRESULT AppManager::DispatchMessageSync(uint32_t app_id, uint32_t message,
+ uint32_t buffer_ptr,
+ uint32_t buffer_length) {
const auto& it = app_lookup_.find(app_id);
if (it == app_lookup_.end()) {
- return X_STATUS_UNSUCCESSFUL;
+ return X_E_NOTFOUND;
}
return it->second->DispatchMessageSync(message, buffer_ptr, buffer_length);
}
-X_RESULT AppManager::DispatchMessageAsync(uint32_t app_id, uint32_t message,
- uint32_t buffer_ptr,
- uint32_t buffer_length) {
+X_HRESULT AppManager::DispatchMessageAsync(uint32_t app_id, uint32_t message,
+ uint32_t buffer_ptr,
+ uint32_t buffer_length) {
const auto& it = app_lookup_.find(app_id);
if (it == app_lookup_.end()) {
- return X_ERROR_NOT_FOUND;
+ return X_E_NOTFOUND;
}
return it->second->DispatchMessageSync(message, buffer_ptr, buffer_length);
}
diff --git a/src/xenia/kernel/xam/app_manager.h b/src/xenia/kernel/xam/app_manager.h
index 8a7655406..40c7ddf53 100644
--- a/src/xenia/kernel/xam/app_manager.h
+++ b/src/xenia/kernel/xam/app_manager.h
@@ -32,8 +32,8 @@ class App {
public:
uint32_t app_id() const { return app_id_; }
- virtual X_RESULT DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
- uint32_t buffer_length) = 0;
+ virtual X_HRESULT DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
+ uint32_t buffer_length) = 0;
virtual ~App() = default;
@@ -51,10 +51,10 @@ class AppManager {
void RegisterApp(std::unique_ptr<App> app);
- X_RESULT DispatchMessageSync(uint32_t app_id, uint32_t message,
- uint32_t buffer_ptr, uint32_t buffer_length);
- X_RESULT DispatchMessageAsync(uint32_t app_id, uint32_t message,
+ X_HRESULT DispatchMessageSync(uint32_t app_id, uint32_t message,
uint32_t buffer_ptr, uint32_t buffer_length);
+ X_HRESULT DispatchMessageAsync(uint32_t app_id, uint32_t message,
+ uint32_t buffer_ptr, uint32_t buffer_length);
private:
std::vector<std::unique_ptr<App>> apps_;
diff --git a/src/xenia/kernel/xam/apps/xam_app.cc b/src/xenia/kernel/xam/apps/xam_app.cc
index 975c8e42e..1d57001c7 100644
--- a/src/xenia/kernel/xam/apps/xam_app.cc
+++ b/src/xenia/kernel/xam/apps/xam_app.cc
@@ -11,6 +11,8 @@
#include "xenia/base/logging.h"
#include "xenia/base/threading.h"
+#include "xenia/kernel/kernel_state.h"
+#include "xenia/kernel/xenumerator.h"
namespace xe {
namespace kernel {
@@ -19,11 +21,55 @@ namespace apps {
XamApp::XamApp(KernelState* kernel_state) : App(kernel_state, 0xFE) {}
-X_RESULT XamApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
- uint32_t buffer_length) {
+X_HRESULT XamApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
+ uint32_t buffer_length) {
// NOTE: buffer_length may be zero or valid.
auto buffer = memory_->TranslateVirtual(buffer_ptr);
switch (message) {
+ case 0x0002000E: {
+ struct message_data {
+ xe::be<uint32_t> user_index;
+ xe::be<uint32_t> unk_04;
+ xe::be<uint32_t> extra_ptr;
+ xe::be<uint32_t> buffer_ptr;
+ xe::be<uint32_t> buffer_size;
+ xe::be<uint32_t> unk_14;
+ xe::be<uint32_t> length_ptr;
+ xe::be<uint32_t> unk_1C;
+ }* data = reinterpret_cast<message_data*>(buffer);
+ XELOGD(
+ "XamAppEnumerateContentAggregate({}, {:08X}, {:08X}, {:08X}, {}, "
+ "{:08X}, {:08X}, {:08X})",
+ (uint32_t)data->user_index, (uint32_t)data->unk_04,
+ (uint32_t)data->extra_ptr, (uint32_t)data->buffer_ptr,
+ (uint32_t)data->buffer_size, (uint32_t)data->unk_14,
+ (uint32_t)data->length_ptr, (uint32_t)data->unk_1C);
+ auto extra = memory_->TranslateVirtual<X_KENUMERATOR_CONTENT_AGGREGATE*>(
+ data->extra_ptr);
+ auto buffer = memory_->TranslateVirtual(data->buffer_ptr);
+ auto e = kernel_state_->object_table()->LookupObject<XEnumerator>(
+ extra->handle);
+ if (!e || !buffer || !extra) {
+ return X_E_INVALIDARG;
+ }
+ assert_true(extra->magic == 'XEN\0');
+ if (data->buffer_size) {
+ std::memset(buffer, 0, data->buffer_size);
+ }
+ if (e->WriteItem(buffer)) {
+ auto content_data = reinterpret_cast<XCONTENT_AGGREGATE_DATA*>(buffer);
+ // TODO(gibbed): WTF?
+ *reinterpret_cast<be<uint32_t>*>(&buffer[0x140]) =
+ content_data->title_id;
+ if (data->length_ptr) {
+ auto length_ptr =
+ memory_->TranslateVirtual<be<uint32_t>*>(data->length_ptr);
+ *length_ptr = 1;
+ }
+ return X_E_SUCCESS;
+ }
+ return X_E_NO_MORE_FILES;
+ }
case 0x00020021: {
struct message_data {
char unk_00[64];
@@ -37,11 +83,11 @@ X_RESULT XamApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
XELOGD("XamApp(0x00020021)('{}', {:08X}, {:08X}, {:08X})", data->unk_00,
(uint32_t)data->unk_40, (uint32_t)data->unk_44,
(uint32_t)data->unk_48);
- return X_ERROR_SUCCESS;
+ return X_E_SUCCESS;
}
case 0x00021012: {
XELOGD("XamApp(0x00021012)");
- return X_ERROR_SUCCESS;
+ return X_E_SUCCESS;
}
case 0x00022005: {
struct message_data {
@@ -53,14 +99,14 @@ X_RESULT XamApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
auto adr = *unk;
XELOGD("XamApp(0x00022005)(%.8X, %.8X)", (uint32_t)data->unk_00,
(uint32_t)data->unk_04);
- return X_ERROR_SUCCESS;
+ return X_E_SUCCESS;
}
}
XELOGE(
"Unimplemented XAM message app={:08X}, msg={:08X}, arg1={:08X}, "
"arg2={:08X}",
app_id(), message, buffer_ptr, buffer_length);
- return X_STATUS_UNSUCCESSFUL;
+ return X_E_FAIL;
}
} // namespace apps
diff --git a/src/xenia/kernel/xam/apps/xam_app.h b/src/xenia/kernel/xam/apps/xam_app.h
index 6e653f384..6f9e52008 100644
--- a/src/xenia/kernel/xam/apps/xam_app.h
+++ b/src/xenia/kernel/xam/apps/xam_app.h
@@ -22,8 +22,8 @@ class XamApp : public App {
public:
explicit XamApp(KernelState* kernel_state);
- X_RESULT DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
- uint32_t buffer_length) override;
+ X_HRESULT DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
+ uint32_t buffer_length) override;
};
} // namespace apps
diff --git a/src/xenia/kernel/xam/apps/xgi_app.cc b/src/xenia/kernel/xam/apps/xgi_app.cc
index b87a906c5..c77665209 100644
--- a/src/xenia/kernel/xam/apps/xgi_app.cc
+++ b/src/xenia/kernel/xam/apps/xgi_app.cc
@@ -21,8 +21,8 @@ XgiApp::XgiApp(KernelState* kernel_state) : App(kernel_state, 0xFB) {}
// http://mb.mirage.org/bugzilla/xliveless/main.c
-X_RESULT XgiApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
- uint32_t buffer_length) {
+X_HRESULT XgiApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
+ uint32_t buffer_length) {
// NOTE: buffer_length may be zero or valid.
auto buffer = memory_->TranslateVirtual(buffer_ptr);
switch (message) {
@@ -38,7 +38,7 @@ X_RESULT XgiApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
uint32_t context_value = xe::load_and_swap<uint32_t>(buffer + 20);
XELOGD("XGIUserSetContextEx({:08X}, {:08X}, {:08X})", user_index,
context_id, context_value);
- return X_ERROR_SUCCESS;
+ return X_E_SUCCESS;
}
case 0x000B0007: {
uint32_t user_index = xe::load_and_swap<uint32_t>(buffer + 0);
@@ -47,7 +47,7 @@ X_RESULT XgiApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
uint32_t value_ptr = xe::load_and_swap<uint32_t>(buffer + 24);
XELOGD("XGIUserSetPropertyEx({:08X}, {:08X}, {}, {:08X})", user_index,
property_id, value_size, value_ptr);
- return X_ERROR_SUCCESS;
+ return X_E_SUCCESS;
}
case 0x000B0008: {
assert_true(!buffer_length || buffer_length == 8);
@@ -55,7 +55,7 @@ X_RESULT XgiApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
uint32_t achievements_ptr = xe::load_and_swap<uint32_t>(buffer + 4);
XELOGD("XGIUserWriteAchievements({:08X}, {:08X})", achievement_count,
achievements_ptr);
- return X_ERROR_SUCCESS;
+ return X_E_SUCCESS;
}
case 0x000B0010: {
assert_true(!buffer_length || buffer_length == 28);
@@ -77,7 +77,7 @@ X_RESULT XgiApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
"{:08X})",
session_ptr, flags, num_slots_public, num_slots_private, user_xuid,
session_info_ptr, nonce_ptr);
- return X_STATUS_SUCCESS;
+ return X_E_SUCCESS;
}
case 0x000B0011: {
// TODO(DrChat): Figure out what this is again
@@ -93,7 +93,7 @@ X_RESULT XgiApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
assert_zero(unk_0);
XELOGD("XGISessionJoinLocal({:08X}, {}, {}, {:08X}, {:08X})", session_ptr,
user_count, unk_0, user_index_array, private_slots_array);
- return X_STATUS_SUCCESS;
+ return X_E_SUCCESS;
}
case 0x000B0041: {
assert_true(!buffer_length || buffer_length == 32);
@@ -110,18 +110,18 @@ X_RESULT XgiApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
if (context) {
xe::store_and_swap<uint32_t>(context + 4, value);
}
- return X_ERROR_FUNCTION_FAILED;
+ return X_E_FAIL;
}
case 0x000B0071: {
XELOGD("XGI 0x000B0071, unimplemented");
- return X_ERROR_SUCCESS;
+ return X_E_SUCCESS;
}
}
XELOGE(
"Unimplemented XGI message app={:08X}, msg={:08X}, arg1={:08X}, "
"arg2={:08X}",
app_id(), message, buffer_ptr, buffer_length);
- return X_STATUS_UNSUCCESSFUL;
+ return X_E_FAIL;
}
} // namespace apps
diff --git a/src/xenia/kernel/xam/apps/xgi_app.h b/src/xenia/kernel/xam/apps/xgi_app.h
index accac54a3..dc1d4bac2 100644
--- a/src/xenia/kernel/xam/apps/xgi_app.h
+++ b/src/xenia/kernel/xam/apps/xgi_app.h
@@ -22,8 +22,8 @@ class XgiApp : public App {
public:
explicit XgiApp(KernelState* kernel_state);
- X_RESULT DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
- uint32_t buffer_length) override;
+ X_HRESULT DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
+ uint32_t buffer_length) override;
};
} // namespace apps
diff --git a/src/xenia/kernel/xam/apps/xlivebase_app.cc b/src/xenia/kernel/xam/apps/xlivebase_app.cc
index e20261b51..d35692ca8 100644
--- a/src/xenia/kernel/xam/apps/xlivebase_app.cc
+++ b/src/xenia/kernel/xam/apps/xlivebase_app.cc
@@ -22,9 +22,9 @@ XLiveBaseApp::XLiveBaseApp(KernelState* kernel_state)
// http://mb.mirage.org/bugzilla/xliveless/main.c
-X_RESULT XLiveBaseApp::DispatchMessageSync(uint32_t message,
- uint32_t buffer_ptr,
- uint32_t buffer_length) {
+X_HRESULT XLiveBaseApp::DispatchMessageSync(uint32_t message,
+ uint32_t buffer_ptr,
+ uint32_t buffer_length) {
// NOTE: buffer_length may be zero or valid.
auto buffer = memory_->TranslateVirtual(buffer_ptr);
switch (message) {
@@ -33,13 +33,13 @@ X_RESULT XLiveBaseApp::DispatchMessageSync(uint32_t message,
assert_true(!buffer_length || buffer_length == 4);
XELOGD("XLiveBaseGetLogonId({:08X})", buffer_ptr);
xe::store_and_swap<uint32_t>(buffer + 0, 1); // ?
- return X_ERROR_SUCCESS;
+ return X_E_SUCCESS;
}
case 0x00058006: {
assert_true(!buffer_length || buffer_length == 4);
XELOGD("XLiveBaseGetNatType({:08X})", buffer_ptr);
xe::store_and_swap<uint32_t>(buffer + 0, 1); // XONLINE_NAT_OPEN
- return X_ERROR_SUCCESS;
+ return X_E_SUCCESS;
}
case 0x00058020: {
// 0x00058004 is called right before this.
@@ -48,12 +48,12 @@ X_RESULT XLiveBaseApp::DispatchMessageSync(uint32_t message,
// buffer_length seems to be the same ptr sent to 0x00058004.
XELOGD("XLiveBaseFriendsCreateEnumerator({:08X}, {:08X}) unimplemented",
buffer_ptr, buffer_length);
- return X_STATUS_UNSUCCESSFUL;
+ return X_E_FAIL;
}
case 0x00058023: {
XELOGD("XliveBaseUnk58023({:08X}, {:08X}) unimplemented", buffer_ptr,
buffer_length);
- return X_STATUS_UNSUCCESSFUL;
+ return X_E_FAIL;
}
case 0x00058046: {
// Required to be successful for Forza 4 to detect signed-in profile
@@ -61,14 +61,14 @@ X_RESULT XLiveBaseApp::DispatchMessageSync(uint32_t message,
// input
XELOGD("XLiveBaseUnk58046({:08X}, {:08X}) unimplemented", buffer_ptr,
buffer_length);
- return X_ERROR_SUCCESS;
+ return X_E_SUCCESS;
}
}
XELOGE(
"Unimplemented XLIVEBASE message app={:08X}, msg={:08X}, arg1={:08X}, "
"arg2={:08X}",
app_id(), message, buffer_ptr, buffer_length);
- return X_STATUS_UNSUCCESSFUL;
+ return X_E_FAIL;
}
} // namespace apps
diff --git a/src/xenia/kernel/xam/apps/xlivebase_app.h b/src/xenia/kernel/xam/apps/xlivebase_app.h
index 0068ec781..bd1fee3d5 100644
--- a/src/xenia/kernel/xam/apps/xlivebase_app.h
+++ b/src/xenia/kernel/xam/apps/xlivebase_app.h
@@ -22,8 +22,8 @@ class XLiveBaseApp : public App {
public:
explicit XLiveBaseApp(KernelState* kernel_state);
- X_RESULT DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
- uint32_t buffer_length) override;
+ X_HRESULT DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
+ uint32_t buffer_length) override;
};
} // namespace apps
diff --git a/src/xenia/kernel/xam/apps/xmp_app.cc b/src/xenia/kernel/xam/apps/xmp_app.cc
index e048703af..8529f9523 100644
--- a/src/xenia/kernel/xam/apps/xmp_app.cc
+++ b/src/xenia/kernel/xam/apps/xmp_app.cc
@@ -30,7 +30,7 @@ XmpApp::XmpApp(KernelState* kernel_state)
next_playlist_handle_(1),
next_song_handle_(1) {}
-X_RESULT XmpApp::XMPGetStatus(uint32_t state_ptr) {
+X_HRESULT XmpApp::XMPGetStatus(uint32_t state_ptr) {
// Some stupid games will hammer this on a thread - induce a delay
// here to keep from starving real threads.
xe::threading::Sleep(std::chrono::milliseconds(1));
@@ -38,15 +38,13 @@ X_RESULT XmpApp::XMPGetStatus(uint32_t state_ptr) {
XELOGD("XMPGetStatus({:08X})", state_ptr);
xe::store_and_swap<uint32_t>(memory_->TranslateVirtual(state_ptr),
static_cast<uint32_t>(state_));
- return X_ERROR_SUCCESS;
+ return X_E_SUCCESS;
}
-X_RESULT XmpApp::XMPCreateTitlePlaylist(uint32_t songs_ptr, uint32_t song_count,
- uint32_t playlist_name_ptr,
- const std::u16string& playlist_name,
- uint32_t flags,
- uint32_t out_song_handles,
- uint32_t out_playlist_handle) {
+X_HRESULT XmpApp::XMPCreateTitlePlaylist(
+ uint32_t songs_ptr, uint32_t song_count, uint32_t playlist_name_ptr,
+ const std::u16string& playlist_name, uint32_t flags,
+ uint32_t out_song_handles, uint32_t out_playlist_handle) {
XELOGD(
"XMPCreateTitlePlaylist({:08X}, {:08X}, {:08X}({}), {:08X}, {:08X}, "
"{:08X})",
@@ -96,16 +94,16 @@ X_RESULT XmpApp::XMPCreateTitlePlaylist(uint32_t songs_ptr, uint32_t song_count,
auto global_lock = global_critical_region_.Acquire();
playlists_.insert({playlist->handle, playlist.get()});
playlist.release();
- return X_ERROR_SUCCESS;
+ return X_E_SUCCESS;
}
-X_RESULT XmpApp::XMPDeleteTitlePlaylist(uint32_t playlist_handle) {
+X_HRESULT XmpApp::XMPDeleteTitlePlaylist(uint32_t playlist_handle) {
XELOGD("XMPDeleteTitlePlaylist({:08X})", playlist_handle);
auto global_lock = global_critical_region_.Acquire();
auto it = playlists_.find(playlist_handle);
if (it == playlists_.end()) {
XELOGE("Playlist {:08X} not found", playlist_handle);
- return X_ERROR_NOT_FOUND;
+ return X_E_NOTFOUND;
}
auto playlist = it->second;
if (playlist == active_playlist_) {
@@ -113,11 +111,11 @@ X_RESULT XmpApp::XMPDeleteTitlePlaylist(uint32_t playlist_handle) {
}
playlists_.erase(it);
delete playlist;
- return X_ERROR_SUCCESS;
+ return X_E_SUCCESS;
}
-X_RESULT XmpApp::XMPPlayTitlePlaylist(uint32_t playlist_handle,
- uint32_t song_handle) {
+X_HRESULT XmpApp::XMPPlayTitlePlaylist(uint32_t playlist_handle,
+ uint32_t song_handle) {
XELOGD("XMPPlayTitlePlaylist({:08X}, {:08X})", playlist_handle, song_handle);
Playlist* playlist = nullptr;
{
@@ -125,7 +123,7 @@ X_RESULT XmpApp::XMPPlayTitlePlaylist(uint32_t playlist_handle,
auto it = playlists_.find(playlist_handle);
if (it == playlists_.end()) {
XELOGE("Playlist {:08X} not found", playlist_handle);
- return X_ERROR_NOT_FOUND;
+ return X_E_NOTFOUND;
}
playlist = it->second;
}
@@ -133,7 +131,7 @@ X_RESULT XmpApp::XMPPlayTitlePlaylist(uint32_t playlist_handle,
if (disabled_) {
// Ignored because we aren't enabled?
XELOGW("Ignoring XMPPlayTitlePlaylist because disabled");
- return X_ERROR_SUCCESS;
+ return X_E_SUCCESS;
}
// Start playlist?
@@ -143,53 +141,53 @@ X_RESULT XmpApp::XMPPlayTitlePlaylist(uint32_t playlist_handle,
state_ = State::kPlaying;
OnStateChanged();
kernel_state_->BroadcastNotification(kMsgPlaybackBehaviorChanged, 1);
- return X_ERROR_SUCCESS;
+ return X_E_SUCCESS;
}
-X_RESULT XmpApp::XMPContinue() {
+X_HRESULT XmpApp::XMPContinue() {
XELOGD("XMPContinue()");
if (state_ == State::kPaused) {
state_ = State::kPlaying;
}
OnStateChanged();
- return X_ERROR_SUCCESS;
+ return X_E_SUCCESS;
}
-X_RESULT XmpApp::XMPStop(uint32_t unk) {
+X_HRESULT XmpApp::XMPStop(uint32_t unk) {
assert_zero(unk);
XELOGD("XMPStop({:08X})", unk);
active_playlist_ = nullptr; // ?
active_song_index_ = 0;
state_ = State::kIdle;
OnStateChanged();
- return X_ERROR_SUCCESS;
+ return X_E_SUCCESS;
}
-X_RESULT XmpApp::XMPPause() {
+X_HRESULT XmpApp::XMPPause() {
XELOGD("XMPPause()");
if (state_ == State::kPlaying) {
state_ = State::kPaused;
}
OnStateChanged();
- return X_ERROR_SUCCESS;
+ return X_E_SUCCESS;
}
-X_RESULT XmpApp::XMPNext() {
+X_HRESULT XmpApp::XMPNext() {
XELOGD("XMPNext()");
if (!active_playlist_) {
- return X_ERROR_NOT_FOUND;
+ return X_E_NOTFOUND;
}
state_ = State::kPlaying;
active_song_index_ =
(active_song_index_ + 1) % active_playlist_->songs.size();
OnStateChanged();
- return X_ERROR_SUCCESS;
+ return X_E_SUCCESS;
}
-X_RESULT XmpApp::XMPPrevious() {
+X_HRESULT XmpApp::XMPPrevious() {
XELOGD("XMPPrevious()");
if (!active_playlist_) {
- return X_ERROR_NOT_FOUND;
+ return X_E_NOTFOUND;
}
state_ = State::kPlaying;
if (!active_song_index_) {
@@ -198,7 +196,7 @@ X_RESULT XmpApp::XMPPrevious() {
--active_song_index_;
}
OnStateChanged();
- return X_ERROR_SUCCESS;
+ return X_E_SUCCESS;
}
void XmpApp::OnStateChanged() {
@@ -206,8 +204,8 @@ void XmpApp::OnStateChanged() {
static_cast<uint32_t>(state_));
}
-X_RESULT XmpApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
- uint32_t buffer_length) {
+X_HRESULT XmpApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
+ uint32_t buffer_length) {
// NOTE: buffer_length may be zero or valid.
auto buffer = memory_->TranslateVirtual(buffer_ptr);
switch (message) {
@@ -271,7 +269,7 @@ X_RESULT XmpApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
repeat_mode_ = static_cast<RepeatMode>(uint32_t(args->repeat_mode));
unknown_flags_ = args->flags;
kernel_state_->BroadcastNotification(kMsgPlaybackBehaviorChanged, 0);
- return X_ERROR_SUCCESS;
+ return X_E_SUCCESS;
}
case 0x00070009: {
assert_true(!buffer_length || buffer_length == 8);
@@ -293,7 +291,7 @@ X_RESULT XmpApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
XELOGD("XMPGetVolume({:08X})", uint32_t(args->volume_ptr));
xe::store_and_swap<float>(memory_->TranslateVirtual(args->volume_ptr),
volume_);
- return X_ERROR_SUCCESS;
+ return X_E_SUCCESS;
}
case 0x0007000C: {
assert_true(!buffer_length || buffer_length == 8);
@@ -306,7 +304,7 @@ X_RESULT XmpApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
assert_true(args->xmp_client == 0x00000002);
XELOGD("XMPSetVolume({:g})", float(args->value));
volume_ = args->value;
- return X_ERROR_SUCCESS;
+ return X_E_SUCCESS;
}
case 0x0007000D: {
assert_true(!buffer_length || buffer_length == 36);
@@ -358,7 +356,7 @@ X_RESULT XmpApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
XELOGE("XMPGetInfo?({:08X}, {:08X})", uint32_t(args->unk_ptr),
uint32_t(args->info_ptr));
if (!active_playlist_) {
- return X_STATUS_UNSUCCESSFUL;
+ return X_E_FAIL;
}
auto& song = active_playlist_->songs[active_song_index_];
xe::store_and_swap<uint32_t>(info + 0, song->handle);
@@ -372,7 +370,7 @@ X_RESULT XmpApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
xe::store_and_swap<uint32_t>(info + 4 + 572 + 204, song->duration_ms);
xe::store_and_swap<uint32_t>(info + 4 + 572 + 208,
static_cast<uint32_t>(song->format));
- return X_ERROR_SUCCESS;
+ return X_E_SUCCESS;
}
case 0x00070013: {
assert_true(!buffer_length || buffer_length == 8);
@@ -409,7 +407,7 @@ X_RESULT XmpApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
XMPStop(0);
}
kernel_state_->BroadcastNotification(kMsgDisableChanged, disabled_);
- return X_ERROR_SUCCESS;
+ return X_E_SUCCESS;
}
case 0x0007001B: {
// XMPGetPlaybackController
@@ -432,7 +430,7 @@ X_RESULT XmpApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
// Atrain spawns a thread 82437FD0 to call this in a tight loop forever.
xe::threading::Sleep(std::chrono::milliseconds(10));
- return X_ERROR_SUCCESS;
+ return X_E_SUCCESS;
}
case 0x00070029: {
// XMPGetPlaybackBehavior
@@ -464,7 +462,7 @@ X_RESULT XmpApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
xe::store_and_swap<uint32_t>(memory_->TranslateVirtual(args->unk3_ptr),
unknown_flags_);
}
- return X_ERROR_SUCCESS;
+ return X_E_SUCCESS;
}
case 0x0007002E: {
assert_true(!buffer_length || buffer_length == 12);
@@ -482,20 +480,20 @@ X_RESULT XmpApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
// We don't use the storage, so just fudge the number.
xe::store_and_swap<uint32_t>(memory_->TranslateVirtual(args->size_ptr),
4 + uint32_t(args->song_count) * 128);
- return X_ERROR_SUCCESS;
+ return X_E_SUCCESS;
}
case 0x0007003D: {
// XMPCaptureOutput - not sure how this works :/
XELOGD("XMPCaptureOutput(...)");
assert_always("XMP output not unimplemented");
- return X_STATUS_UNSUCCESSFUL;
+ return X_E_FAIL;
}
}
XELOGE(
"Unimplemented XMP message app={:08X}, msg={:08X}, arg1={:08X}, "
"arg2={:08X}",
app_id(), message, buffer_ptr, buffer_length);
- return X_STATUS_UNSUCCESSFUL;
+ return X_E_FAIL;
}
} // namespace apps
diff --git a/src/xenia/kernel/xam/apps/xmp_app.h b/src/xenia/kernel/xam/apps/xmp_app.h
index de341a143..cb4966a00 100644
--- a/src/xenia/kernel/xam/apps/xmp_app.h
+++ b/src/xenia/kernel/xam/apps/xmp_app.h
@@ -68,23 +68,24 @@ class XmpApp : public App {
explicit XmpApp(KernelState* kernel_state);
- X_RESULT XMPGetStatus(uint32_t status_ptr);
-
- X_RESULT XMPCreateTitlePlaylist(uint32_t songs_ptr, uint32_t song_count,
- uint32_t playlist_name_ptr,
- const std::u16string& playlist_name,
- uint32_t flags, uint32_t out_song_handles,
- uint32_t out_playlist_handle);
- X_RESULT XMPDeleteTitlePlaylist(uint32_t playlist_handle);
- X_RESULT XMPPlayTitlePlaylist(uint32_t playlist_handle, uint32_t song_handle);
- X_RESULT XMPContinue();
- X_RESULT XMPStop(uint32_t unk);
- X_RESULT XMPPause();
- X_RESULT XMPNext();
- X_RESULT XMPPrevious();
-
- X_RESULT DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
- uint32_t buffer_length) override;
+ X_HRESULT XMPGetStatus(uint32_t status_ptr);
+
+ X_HRESULT XMPCreateTitlePlaylist(uint32_t songs_ptr, uint32_t song_count,
+ uint32_t playlist_name_ptr,
+ const std::u16string& playlist_name,
+ uint32_t flags, uint32_t out_song_handles,
+ uint32_t out_playlist_handle);
+ X_HRESULT XMPDeleteTitlePlaylist(uint32_t playlist_handle);
+ X_HRESULT XMPPlayTitlePlaylist(uint32_t playlist_handle,
+ uint32_t song_handle);
+ X_HRESULT XMPContinue();
+ X_HRESULT XMPStop(uint32_t unk);
+ X_HRESULT XMPPause();
+ X_HRESULT XMPNext();
+ X_HRESULT XMPPrevious();
+
+ X_HRESULT DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
+ uint32_t buffer_length) override;
private:
static const uint32_t kMsgStateChanged = 0x0A000001;
diff --git a/src/xenia/kernel/xam/content_manager.cc b/src/xenia/kernel/xam/content_manager.cc
index 235e550b3..105ff7422 100644
--- a/src/xenia/kernel/xam/content_manager.cc
+++ b/src/xenia/kernel/xam/content_manager.cc
@@ -30,7 +30,7 @@ static int content_device_id_ = 0;
ContentPackage::ContentPackage(KernelState* kernel_state,
const std::string_view root_name,
- const XCONTENT_DATA& data,
+ const ContentData& data,
const std::filesystem::path& package_path)
: kernel_state_(kernel_state), root_name_(root_name) {
device_path_ = fmt::format("\\Device\\Content\\{0}\\", ++content_device_id_);
@@ -88,16 +88,16 @@ std::filesystem::path ContentManager::ResolvePackageRoot(
}
std::filesystem::path ContentManager::ResolvePackagePath(
- const XCONTENT_DATA& data) {
+ const ContentData& data) {
// Content path:
// content_root/title_id/type_name/data_file_name/
auto package_root = ResolvePackageRoot(data.content_type);
return package_root / xe::to_path(data.file_name);
}
-std::vector<XCONTENT_DATA> ContentManager::ListContent(uint32_t device_id,
- uint32_t content_type) {
- std::vector<XCONTENT_DATA> result;
+std::vector<ContentData> ContentManager::ListContent(uint32_t device_id,
+ uint32_t content_type) {
+ std::vector<ContentData> result;
// Search path:
// content_root/title_id/type_name/*
@@ -108,7 +108,7 @@ std::vector<XCONTENT_DATA> ContentManager::ListContent(uint32_t device_id,
// Directories only.
continue;
}
- XCONTENT_DATA content_data;
+ ContentData content_data;
content_data.device_id = device_id;
content_data.content_type = content_type;
content_data.display_name = xe::path_to_utf16(file_info.name);
@@ -120,7 +120,7 @@ std::vector<XCONTENT_DATA> ContentManager::ListContent(uint32_t device_id,
}
std::unique_ptr<ContentPackage> ContentManager::ResolvePackage(
- const std::string_view root_name, const XCONTENT_DATA& data) {
+ const std::string_view root_name, const ContentData& data) {
auto package_path = ResolvePackagePath(data);
if (!std::filesystem::exists(package_path)) {
return nullptr;
@@ -133,13 +133,13 @@ std::unique_ptr<ContentPackage> ContentManager::ResolvePackage(
return package;
}
-bool ContentManager::ContentExists(const XCONTENT_DATA& data) {
+bool ContentManager::ContentExists(const ContentData& data) {
auto path = ResolvePackagePath(data);
return std::filesystem::exists(path);
}
X_RESULT ContentManager::CreateContent(const std::string_view root_name,
- const XCONTENT_DATA& data) {
+ const ContentData& data) {
auto global_lock = global_critical_region_.Acquire();
if (open_packages_.count(string_key(root_name))) {
@@ -166,7 +166,7 @@ X_RESULT ContentManager::CreateContent(const std::string_view root_name,
}
X_RESULT ContentManager::OpenContent(const std::string_view root_name,
- const XCONTENT_DATA& data) {
+ const ContentData& data) {
auto global_lock = global_critical_region_.Acquire();
if (open_packages_.count(string_key(root_name))) {
@@ -204,7 +204,7 @@ X_RESULT ContentManager::CloseContent(const std::string_view root_name) {
return X_ERROR_SUCCESS;
}
-X_RESULT ContentManager::GetContentThumbnail(const XCONTENT_DATA& data,
+X_RESULT ContentManager::GetContentThumbnail(const ContentData& data,
std::vector<uint8_t>* buffer) {
auto global_lock = global_critical_region_.Acquire();
auto package_path = ResolvePackagePath(data);
@@ -223,7 +223,7 @@ X_RESULT ContentManager::GetContentThumbnail(const XCONTENT_DATA& data,
}
}
-X_RESULT ContentManager::SetContentThumbnail(const XCONTENT_DATA& data,
+X_RESULT ContentManager::SetContentThumbnail(const ContentData& data,
std::vector<uint8_t> buffer) {
auto global_lock = global_critical_region_.Acquire();
auto package_path = ResolvePackagePath(data);
@@ -239,7 +239,7 @@ X_RESULT ContentManager::SetContentThumbnail(const XCONTENT_DATA& data,
}
}
-X_RESULT ContentManager::DeleteContent(const XCONTENT_DATA& data) {
+X_RESULT ContentManager::DeleteContent(const ContentData& data) {
auto global_lock = global_critical_region_.Acquire();
auto package_path = ResolvePackagePath(data);
diff --git a/src/xenia/kernel/xam/content_manager.h b/src/xenia/kernel/xam/content_manager.h
index 5d0cb7b24..a69c63592 100644
--- a/src/xenia/kernel/xam/content_manager.h
+++ b/src/xenia/kernel/xam/content_manager.h
@@ -18,6 +18,7 @@
#include "xenia/base/memory.h"
#include "xenia/base/mutex.h"
#include "xenia/base/string_key.h"
+#include "xenia/base/string_util.h"
#include "xenia/xbox.h"
namespace xe {
@@ -31,32 +32,91 @@ namespace kernel {
namespace xam {
struct XCONTENT_DATA {
- static const size_t kSize = 4 + 4 + 128 * 2 + 42 + 2; // = 306 + 2b padding
+ be<uint32_t> device_id;
+ be<uint32_t> content_type;
+ union {
+ be<uint16_t> display_name[128];
+ char16_t display_name_chars[128];
+ };
+ char file_name[42];
+ uint8_t padding[2];
+};
+static_assert_size(XCONTENT_DATA, 308);
+
+struct XCONTENT_AGGREGATE_DATA {
+ be<uint32_t> device_id;
+ be<uint32_t> content_type;
+ union {
+ be<uint16_t> display_name[128];
+ char16_t display_name_chars[128];
+ };
+ char file_name[42];
+ uint8_t padding[2];
+ be<uint32_t> title_id;
+};
+static_assert_size(XCONTENT_AGGREGATE_DATA, 312);
+
+struct ContentData {
+ uint32_t device_id;
+ uint32_t content_type;
+ std::u16string display_name;
+ std::string file_name;
+
+ ContentData() = default;
+
+ explicit ContentData(const XCONTENT_DATA& data) {
+ device_id = data.device_id;
+ content_type = data.content_type;
+ display_name = xe::load_and_swap<std::u16string>(data.display_name);
+ file_name = xe::load_and_swap<std::string>(data.file_name);
+ }
+
+ void Write(XCONTENT_DATA* data) const {
+ data->device_id = device_id;
+ data->content_type = content_type;
+ xe::string_util::copy_and_swap_truncating(
+ data->display_name_chars, display_name,
+ xe::countof(data->display_name_chars));
+ xe::string_util::copy_maybe_truncating<
+ string_util::Safety::IKnowWhatIAmDoing>(data->file_name, file_name,
+ xe::countof(data->file_name));
+ }
+};
+
+struct ContentAggregateData {
uint32_t device_id;
uint32_t content_type;
- std::u16string display_name; // 128 chars
+ std::u16string display_name;
std::string file_name;
+ uint32_t title_id;
+
+ ContentAggregateData() = default;
- XCONTENT_DATA() = default;
- explicit XCONTENT_DATA(const uint8_t* ptr) {
- device_id = xe::load_and_swap<uint32_t>(ptr + 0);
- content_type = xe::load_and_swap<uint32_t>(ptr + 4);
- display_name = xe::load_and_swap<std::u16string>(ptr + 8);
- file_name = xe::load_and_swap<std::string>(ptr + 8 + 128 * 2);
+ explicit ContentAggregateData(const XCONTENT_AGGREGATE_DATA& data) {
+ device_id = data.device_id;
+ content_type = data.content_type;
+ display_name = xe::load_and_swap<std::u16string>(data.display_name);
+ file_name = xe::load_and_swap<std::string>(data.file_name);
+ title_id = data.title_id;
}
- void Write(uint8_t* ptr) {
- xe::store_and_swap<uint32_t>(ptr + 0, device_id);
- xe::store_and_swap<uint32_t>(ptr + 4, content_type);
- xe::store_and_swap<std::u16string>(ptr + 8, display_name);
- xe::store_and_swap<std::string>(ptr + 8 + 128 * 2, file_name);
+ void Write(XCONTENT_AGGREGATE_DATA* data) const {
+ data->device_id = device_id;
+ data->content_type = content_type;
+ xe::string_util::copy_and_swap_truncating(
+ data->display_name_chars, display_name,
+ xe::countof(data->display_name_chars));
+ xe::string_util::copy_maybe_truncating<
+ string_util::Safety::IKnowWhatIAmDoing>(data->file_name, file_name,
+ xe::countof(data->file_name));
+ data->title_id = title_id;
}
};
class ContentPackage {
public:
ContentPackage(KernelState* kernel_state, const std::string_view root_name,
- const XCONTENT_DATA& data,
+ const ContentData& data,
const std::filesystem::path& package_path);
~ContentPackage();
@@ -72,28 +132,28 @@ class ContentManager {
const std::filesystem::path& root_path);
~ContentManager();
- std::vector<XCONTENT_DATA> ListContent(uint32_t device_id,
- uint32_t content_type);
+ std::vector<ContentData> ListContent(uint32_t device_id,
+ uint32_t content_type);
std::unique_ptr<ContentPackage> ResolvePackage(
- const std::string_view root_name, const XCONTENT_DATA& data);
+ const std::string_view root_name, const ContentData& data);
- bool ContentExists(const XCONTENT_DATA& data);
+ bool ContentExists(const ContentData& data);
X_RESULT CreateContent(const std::string_view root_name,
- const XCONTENT_DATA& data);
+ const ContentData& data);
X_RESULT OpenContent(const std::string_view root_name,
- const XCONTENT_DATA& data);
+ const ContentData& data);
X_RESULT CloseContent(const std::string_view root_name);
- X_RESULT GetContentThumbnail(const XCONTENT_DATA& data,
+ X_RESULT GetContentThumbnail(const ContentData& data,
std::vector<uint8_t>* buffer);
- X_RESULT SetContentThumbnail(const XCONTENT_DATA& data,
+ X_RESULT SetContentThumbnail(const ContentData& data,
std::vector<uint8_t> buffer);
- X_RESULT DeleteContent(const XCONTENT_DATA& data);
+ X_RESULT DeleteContent(const ContentData& data);
std::filesystem::path ResolveGameUserContentPath();
private:
std::filesystem::path ResolvePackageRoot(uint32_t content_type);
- std::filesystem::path ResolvePackagePath(const XCONTENT_DATA& data);
+ std::filesystem::path ResolvePackagePath(const ContentData& data);
KernelState* kernel_state_;
std::filesystem::path root_path_;
diff --git a/src/xenia/kernel/xam/xam_content.cc b/src/xenia/kernel/xam/xam_content.cc
index 3a9c29bcd..4de097fb7 100644
--- a/src/xenia/kernel/xam/xam_content.cc
+++ b/src/xenia/kernel/xam/xam_content.cc
@@ -9,8 +9,10 @@
#include "xenia/base/logging.h"
#include "xenia/base/math.h"
+#include "xenia/base/string_util.h"
#include "xenia/kernel/kernel_state.h"
#include "xenia/kernel/util/shim_utils.h"
+#include "xenia/kernel/xam/xam_content_device.h"
#include "xenia/kernel/xam/xam_private.h"
#include "xenia/kernel/xenumerator.h"
#include "xenia/xbox.h"
@@ -49,7 +51,7 @@ DECLARE_XAM_EXPORT2(XamContentGetLicenseMask, kContent, kStub, kHighFrequency);
dword_result_t XamContentResolve(dword_t user_index, lpvoid_t content_data_ptr,
lpunknown_t buffer_ptr, dword_t buffer_size,
dword_t unk1, dword_t unk2, dword_t unk3) {
- auto content_data = XCONTENT_DATA((uint8_t*)content_data_ptr);
+ auto content_data = content_data_ptr.as<XCONTENT_DATA*>();
// Result of buffer_ptr is sent to RtlInitAnsiString.
// buffer_size is usually 260 (max path).
@@ -69,8 +71,9 @@ dword_result_t XamContentCreateEnumerator(dword_t user_index, dword_t device_id,
lpdword_t buffer_size_ptr,
lpdword_t handle_out) {
assert_not_null(handle_out);
- if ((device_id && (device_id & 0x0000000F) != dummy_device_info_.device_id) ||
- !handle_out) {
+
+ auto device_info = device_id == 0 ? nullptr : GetDummyDeviceInfo(device_id);
+ if ((device_id && device_info == nullptr) || !handle_out) {
if (buffer_size_ptr) {
*buffer_size_ptr = 0;
}
@@ -80,22 +83,29 @@ dword_result_t XamContentCreateEnumerator(dword_t user_index, dword_t device_id,
}
if (buffer_size_ptr) {
- *buffer_size_ptr = (uint32_t)XCONTENT_DATA::kSize * items_per_enumerate;
+ *buffer_size_ptr = sizeof(XCONTENT_DATA) * items_per_enumerate;
+ }
+
+ auto e = object_ref<XStaticEnumerator>(new XStaticEnumerator(
+ kernel_state(), items_per_enumerate, sizeof(XCONTENT_DATA)));
+ auto result = e->Initialize(0xFF, 0xFE, 0x20005, 0x20007, 0);
+ if (XFAILED(result)) {
+ return result;
+ }
+
+ if (!device_info || device_info->device_id == DummyDeviceId::HDD) {
+ // Get all content data.
+ auto content_datas = kernel_state()->content_manager()->ListContent(
+ static_cast<uint32_t>(DummyDeviceId::HDD), content_type);
+ for (const auto& content_data : content_datas) {
+ auto item = reinterpret_cast<XCONTENT_DATA*>(e->AppendItem());
+ assert_not_null(item);
+ content_data.Write(item);
+ }
}
- auto e = new XStaticEnumerator(kernel_state(), items_per_enumerate,
- XCONTENT_DATA::kSize);
- e->Initialize();
-
- // Get all content data.
- auto content_datas = kernel_state()->content_manager()->ListContent(
- device_id ? static_cast<uint32_t>(device_id)
- : dummy_device_info_.device_id,
- content_type);
- for (auto& content_data : content_datas) {
- auto ptr = e->AppendItem();
- assert_not_null(ptr);
- content_data.Write(ptr);
+ if (!device_info || device_info->device_id == DummyDeviceId::ODD) {
+ // TODO(gibbed): disc drive content
}
XELOGD("XamContentCreateEnumerator: added {} items to enumerator",
@@ -113,7 +123,8 @@ dword_result_t XamContentCreateEx(dword_t user_index, lpstring_t root_name,
dword_t cache_size, qword_t content_size,
lpvoid_t overlapped_ptr) {
X_RESULT result = X_ERROR_INVALID_PARAMETER;
- auto content_data = XCONTENT_DATA((uint8_t*)content_data_ptr);
+ auto content_data =
+ static_cast<ContentData>(*content_data_ptr.as<XCONTENT_DATA*>());
auto content_manager = kernel_state()->content_manager();
bool create = false;
@@ -265,7 +276,8 @@ dword_result_t XamContentGetCreator(dword_t user_index,
lpunknown_t overlapped_ptr) {
auto result = X_ERROR_SUCCESS;
- auto content_data = XCONTENT_DATA((uint8_t*)content_data_ptr);
+ auto content_data =
+ static_cast<ContentData>(*content_data_ptr.as<XCONTENT_DATA*>());
if (content_data.content_type == 1) {
// User always creates saves.
@@ -296,7 +308,8 @@ dword_result_t XamContentGetThumbnail(dword_t user_index,
lpunknown_t overlapped_ptr) {
assert_not_null(buffer_size_ptr);
uint32_t buffer_size = *buffer_size_ptr;
- auto content_data = XCONTENT_DATA((uint8_t*)content_data_ptr);
+ auto content_data =
+ static_cast<ContentData>(*content_data_ptr.as<XCONTENT_DATA*>());
// Get thumbnail (if it exists).
std::vector<uint8_t> buffer;
@@ -332,7 +345,8 @@ dword_result_t XamContentSetThumbnail(dword_t user_index,
lpvoid_t content_data_ptr,
lpvoid_t buffer_ptr, dword_t buffer_size,
lpunknown_t overlapped_ptr) {
- auto content_data = XCONTENT_DATA((uint8_t*)content_data_ptr);
+ auto content_data =
+ static_cast<ContentData>(*content_data_ptr.as<XCONTENT_DATA*>());
// Buffer is PNG data.
auto buffer = std::vector<uint8_t>((uint8_t*)buffer_ptr,
@@ -351,7 +365,8 @@ DECLARE_XAM_EXPORT1(XamContentSetThumbnail, kContent, kImplemented);
dword_result_t XamContentDelete(dword_t user_index, lpvoid_t content_data_ptr,
lpunknown_t overlapped_ptr) {
- auto content_data = XCONTENT_DATA((uint8_t*)content_data_ptr);
+ auto content_data =
+ static_cast<ContentData>(*content_data_ptr.as<XCONTENT_DATA*>());
auto result = kernel_state()->content_manager()->DeleteContent(content_data);
diff --git a/src/xenia/kernel/xam/xam_content_aggregate.cc b/src/xenia/kernel/xam/xam_content_aggregate.cc
new file mode 100644
index 000000000..9e002723e
--- /dev/null
+++ b/src/xenia/kernel/xam/xam_content_aggregate.cc
@@ -0,0 +1,135 @@
+/**
+ ******************************************************************************
+ * Xenia : Xbox 360 Emulator Research Project *
+ ******************************************************************************
+ * Copyright 2020 Ben Vanik. All rights reserved. *
+ * Released under the BSD license - see LICENSE in the root for more details. *
+ ******************************************************************************
+ */
+
+#include "xenia/base/logging.h"
+#include "xenia/base/math.h"
+#include "xenia/base/string_util.h"
+#include "xenia/kernel/kernel_state.h"
+#include "xenia/kernel/util/shim_utils.h"
+#include "xenia/kernel/xam/xam_content_device.h"
+#include "xenia/kernel/xam/xam_private.h"
+#include "xenia/kernel/xenumerator.h"
+#include "xenia/vfs/file.h"
+#include "xenia/xbox.h"
+
+namespace xe {
+namespace kernel {
+namespace xam {
+
+void AddODDContentTest(object_ref<XStaticEnumerator> e, uint32_t content_type) {
+ auto root_entry = kernel_state()->file_system()->ResolvePath(
+ "game:\\Content\\0000000000000000");
+ if (!root_entry) {
+ return;
+ }
+
+ auto content_type_path = fmt::format("{:08X}", content_type);
+
+ xe::filesystem::WildcardEngine title_find_engine;
+ title_find_engine.SetRule("????????");
+
+ xe::filesystem::WildcardEngine content_find_engine;
+ content_find_engine.SetRule("????????????????");
+
+ size_t title_find_index = 0;
+ vfs::Entry* title_entry;
+ for (;;) {
+ title_entry =
+ root_entry->IterateChildren(title_find_engine, &title_find_index);
+ if (!title_entry) {
+ break;
+ }
+
+ auto title_id =
+ string_util::from_string<uint32_t>(title_entry->name(), true);
+
+ auto content_root_entry = title_entry->ResolvePath(content_type_path);
+ if (content_root_entry) {
+ size_t content_find_index = 0;
+ vfs::Entry* content_entry;
+ for (;;) {
+ content_entry = content_root_entry->IterateChildren(
+ content_find_engine, &content_find_index);
+ if (!content_entry) {
+ break;
+ }
+
+ auto item = reinterpret_cast<XCONTENT_AGGREGATE_DATA*>(e->AppendItem());
+ assert_not_null(item);
+ ContentAggregateData content_aggregate_data = {};
+ content_aggregate_data.device_id =
+ static_cast<uint32_t>(DummyDeviceId::ODD);
+ content_aggregate_data.content_type = content_type;
+ content_aggregate_data.display_name = to_utf16(content_entry->name());
+ content_aggregate_data.file_name = content_entry->name();
+ content_aggregate_data.title_id = title_id;
+ content_aggregate_data.Write(item);
+ }
+ }
+ }
+}
+
+dword_result_t XamContentAggregateCreateEnumerator(qword_t xuid,
+ dword_t device_id,
+ dword_t content_type,
+ unknown_t unk3,
+ lpdword_t handle_out) {
+ assert_not_null(handle_out);
+
+ auto device_info = device_id == 0 ? nullptr : GetDummyDeviceInfo(device_id);
+ if ((device_id && device_info == nullptr) || !handle_out) {
+ return X_E_INVALIDARG;
+ }
+
+ auto e = object_ref<XStaticEnumerator>(new XStaticEnumerator(
+ kernel_state(), 1, sizeof(XCONTENT_AGGREGATE_DATA)));
+ X_KENUMERATOR_CONTENT_AGGREGATE* extra;
+ auto result = e->Initialize(0xFF, 0xFE, 0x2000E, 0x20010, 0, &extra);
+ if (XFAILED(result)) {
+ return result;
+ }
+
+ extra->magic = 'XEN\0';
+ extra->handle = e->handle();
+
+ if (!device_info || device_info->device_type == DeviceType::HDD) {
+ // Get all content data.
+ auto content_datas = kernel_state()->content_manager()->ListContent(
+ static_cast<uint32_t>(DummyDeviceId::HDD), content_type);
+ for (const auto& content_data : content_datas) {
+ auto item = reinterpret_cast<XCONTENT_AGGREGATE_DATA*>(e->AppendItem());
+ assert_not_null(item);
+ ContentAggregateData content_aggregate_data = {};
+ content_aggregate_data.device_id = content_data.device_id;
+ content_aggregate_data.content_type = content_data.content_type;
+ content_aggregate_data.display_name = content_data.display_name;
+ content_aggregate_data.file_name = content_data.file_name;
+ content_aggregate_data.title_id = 1u;
+ content_aggregate_data.Write(item);
+ }
+ }
+
+ if (!device_info || device_info->device_type == DeviceType::ODD) {
+ AddODDContentTest(e, content_type);
+ }
+
+ XELOGD("XamContentAggregateCreateEnumerator: added {} items to enumerator",
+ e->item_count());
+
+ *handle_out = e->handle();
+ return X_ERROR_SUCCESS;
+}
+DECLARE_XAM_EXPORT1(XamContentAggregateCreateEnumerator, kContent, kStub);
+
+void RegisterContentAggregateExports(xe::cpu::ExportResolver* export_resolver,
+ KernelState* kernel_state) {}
+
+} // namespace xam
+} // namespace kernel
+} // namespace xe
diff --git a/src/xenia/kernel/xam/xam_content_device.cc b/src/xenia/kernel/xam/xam_content_device.cc
index 899cb7a6f..d0aaa34f1 100644
--- a/src/xenia/kernel/xam/xam_content_device.cc
+++ b/src/xenia/kernel/xam/xam_content_device.cc
@@ -7,6 +7,8 @@
******************************************************************************
*/
+#include "xenia/kernel/xam/xam_content_device.h"
+
#include "xenia/base/logging.h"
#include "xenia/base/math.h"
#include "xenia/kernel/kernel_state.h"
@@ -19,14 +21,6 @@ namespace xe {
namespace kernel {
namespace xam {
-struct DeviceInfo {
- uint32_t device_id;
- uint32_t device_type;
- uint64_t total_bytes;
- uint64_t free_bytes;
- char16_t name[28];
-};
-
// TODO(gibbed): real information.
//
// Until we expose real information about a HDD device, we
@@ -37,36 +31,54 @@ struct DeviceInfo {
// when it is a 64-bit value. Which means any size above ~4GB
// will not be recognized properly.
#define ONE_GB (1024ull * 1024ull * 1024ull)
-static const DeviceInfo dummy_device_info_ = {
- 0x00000001, // id
- 1, // 1=HDD
+
+static const DummyDeviceInfo dummy_hdd_device_info_ = {
+ DummyDeviceId::HDD, DeviceType::HDD,
20ull * ONE_GB, // 20GB
3ull * ONE_GB, // 3GB, so it looks a little used.
u"Dummy HDD",
};
+static const DummyDeviceInfo dummy_odd_device_info_ = {
+ DummyDeviceId::ODD, DeviceType::ODD,
+ 7ull * ONE_GB, // 7GB (rough maximum)
+ 0ull * ONE_GB, // read-only FS, so no free space
+ u"Dummy ODD",
+};
+static const DummyDeviceInfo* dummy_device_infos_[] = {
+ &dummy_hdd_device_info_,
+ &dummy_odd_device_info_,
+};
#undef ONE_GB
+const DummyDeviceInfo* GetDummyDeviceInfo(uint32_t device_id) {
+ const auto& begin = std::begin(dummy_device_infos_);
+ const auto& end = std::end(dummy_device_infos_);
+ auto it = std::find_if(begin, end, [device_id](const auto& item) {
+ return static_cast<uint32_t>(item->device_id) == device_id;
+ });
+ return it == end ? nullptr : *it;
+}
+
dword_result_t XamContentGetDeviceName(dword_t device_id,
lpu16string_t name_buffer,
dword_t name_capacity) {
- if ((device_id & 0x0000000F) != dummy_device_info_.device_id) {
+ auto device_info = GetDummyDeviceInfo(device_id);
+ if (device_info == nullptr) {
return X_ERROR_DEVICE_NOT_CONNECTED;
}
-
- auto name = std::u16string(dummy_device_info_.name);
+ auto name = std::u16string(device_info->name);
if (name_capacity < name.size() + 1) {
return X_ERROR_INSUFFICIENT_BUFFER;
}
-
- xe::store_and_swap<std::u16string>(name_buffer, name);
- ((char16_t*)name_buffer)[name.size()] = 0;
+ xe::string_util::copy_and_swap_truncating(name_buffer, name, name_capacity);
return X_ERROR_SUCCESS;
}
DECLARE_XAM_EXPORT1(XamContentGetDeviceName, kContent, kImplemented);
dword_result_t XamContentGetDeviceState(dword_t device_id,
lpunknown_t overlapped_ptr) {
- if ((device_id & 0x0000000F) != dummy_device_info_.device_id) {
+ auto device_info = GetDummyDeviceInfo(device_id);
+ if (device_info == nullptr) {
if (overlapped_ptr) {
kernel_state()->CompleteOverlappedImmediateEx(
overlapped_ptr, X_ERROR_FUNCTION_FAILED, X_ERROR_DEVICE_NOT_CONNECTED,
@@ -76,7 +88,6 @@ dword_result_t XamContentGetDeviceState(dword_t device_id,
return X_ERROR_DEVICE_NOT_CONNECTED;
}
}
-
if (overlapped_ptr) {
kernel_state()->CompleteOverlappedImmediate(overlapped_ptr,
X_ERROR_SUCCESS);
@@ -92,24 +103,27 @@ typedef struct {
xe::be<uint32_t> device_type;
xe::be<uint64_t> total_bytes;
xe::be<uint64_t> free_bytes;
- xe::be<uint16_t> name[28];
+ union {
+ xe::be<uint16_t> name[28];
+ char16_t name_chars[28];
+ };
} X_CONTENT_DEVICE_DATA;
static_assert_size(X_CONTENT_DEVICE_DATA, 0x50);
dword_result_t XamContentGetDeviceData(
dword_t device_id, pointer_t<X_CONTENT_DEVICE_DATA> device_data) {
- if ((device_id & 0x0000000F) != dummy_device_info_.device_id) {
- // TODO(benvanik): memset 0 the data?
+ auto device_info = GetDummyDeviceInfo(device_id);
+ if (device_info == nullptr) {
return X_ERROR_DEVICE_NOT_CONNECTED;
}
-
device_data.Zero();
- const auto& device_info = dummy_device_info_;
- device_data->device_id = device_info.device_id;
- device_data->device_type = device_info.device_type;
- device_data->total_bytes = device_info.total_bytes;
- device_data->free_bytes = device_info.free_bytes;
- xe::store_and_swap<std::u16string>(&device_data->name[0], device_info.name);
+ device_data->device_id = static_cast<uint32_t>(device_info->device_id);
+ device_data->device_type = static_cast<uint32_t>(device_info->device_type);
+ device_data->total_bytes = device_info->total_bytes;
+ device_data->free_bytes = device_info->free_bytes;
+ xe::string_util::copy_and_swap_truncating(
+ device_data->name_chars, device_info->name,
+ xe::countof(device_data->name_chars));
return X_ERROR_SUCCESS;
}
DECLARE_XAM_EXPORT1(XamContentGetDeviceData, kContent, kImplemented);
@@ -122,21 +136,29 @@ dword_result_t XamContentCreateDeviceEnumerator(dword_t content_type,
assert_not_null(handle_out);
if (buffer_size_ptr) {
- *buffer_size_ptr = sizeof(DeviceInfo) * max_count;
+ *buffer_size_ptr = sizeof(X_CONTENT_DEVICE_DATA) * max_count;
}
- auto e = new XStaticEnumerator(kernel_state(), max_count, sizeof(DeviceInfo));
- e->Initialize();
-
- // Copy our dummy device into the enumerator
- DeviceInfo* dev = (DeviceInfo*)e->AppendItem();
- if (dev) {
- xe::store_and_swap(&dev->device_id, dummy_device_info_.device_id);
- xe::store_and_swap(&dev->device_type, dummy_device_info_.device_type);
- xe::store_and_swap(&dev->total_bytes, dummy_device_info_.total_bytes);
- xe::store_and_swap(&dev->free_bytes, dummy_device_info_.free_bytes);
- xe::copy_and_swap(dev->name, dummy_device_info_.name,
- xe::countof(dev->name));
+ auto e = object_ref<XStaticEnumerator>(new XStaticEnumerator(
+ kernel_state(), max_count, sizeof(X_CONTENT_DEVICE_DATA)));
+ auto result = e->Initialize(0xFE, 0xFE, 0x2000A, 0x20009, 0);
+ if (XFAILED(result)) {
+ return result;
+ }
+
+ for (const auto& device_info : dummy_device_infos_) {
+ // Copy our dummy device into the enumerator
+ auto device_data = (X_CONTENT_DEVICE_DATA*)e->AppendItem();
+ if (device_data) {
+ device_data->device_id = static_cast<uint32_t>(device_info->device_id);
+ device_data->device_type =
+ static_cast<uint32_t>(device_info->device_type);
+ device_data->total_bytes = device_info->total_bytes;
+ device_data->free_bytes = device_info->free_bytes;
+ xe::string_util::copy_and_swap_truncating(
+ device_data->name_chars, device_info->name,
+ xe::countof(device_data->name_chars));
+ }
}
*handle_out = e->handle();
diff --git a/src/xenia/kernel/xam/xam_content_device.h b/src/xenia/kernel/xam/xam_content_device.h
new file mode 100644
index 000000000..c22f29543
--- /dev/null
+++ b/src/xenia/kernel/xam/xam_content_device.h
@@ -0,0 +1,43 @@
+/**
+ ******************************************************************************
+ * Xenia : Xbox 360 Emulator Research Project *
+ ******************************************************************************
+ * Copyright 2020 Ben Vanik. All rights reserved. *
+ * Released under the BSD license - see LICENSE in the root for more details. *
+ ******************************************************************************
+ */
+
+#ifndef XENIA_KERNEL_XAM_XAM_CONTENT_DEVICE_H_
+#define XENIA_KERNEL_XAM_XAM_CONTENT_DEVICE_H_
+
+#include "xenia/xbox.h"
+
+namespace xe {
+namespace kernel {
+namespace xam {
+
+enum class DeviceType : uint32_t {
+ HDD = 1,
+ ODD = 4,
+};
+
+enum class DummyDeviceId : uint32_t {
+ HDD = 1,
+ ODD = 2,
+};
+
+struct DummyDeviceInfo {
+ DummyDeviceId device_id;
+ DeviceType device_type;
+ uint64_t total_bytes;
+ uint64_t free_bytes;
+ const std::u16string_view name;
+};
+
+const DummyDeviceInfo* GetDummyDeviceInfo(uint32_t device_id);
+
+} // namespace xam
+} // namespace kernel
+} // namespace xe
+
+#endif // XENIA_KERNEL_XAM_XAM_CONTENT_DEVICE_H_
diff --git a/src/xenia/kernel/xam/xam_enum.cc b/src/xenia/kernel/xam/xam_enum.cc
index d52a6e55b..2cab56ab7 100644
--- a/src/xenia/kernel/xam/xam_enum.cc
+++ b/src/xenia/kernel/xam/xam_enum.cc
@@ -10,12 +10,10 @@
#include "xenia/base/logging.h"
#include "xenia/base/string_util.h"
#include "xenia/kernel/kernel_state.h"
-#include "xenia/kernel/user_module.h"
#include "xenia/kernel/util/shim_utils.h"
#include "xenia/kernel/xam/xam_module.h"
#include "xenia/kernel/xam/xam_private.h"
#include "xenia/kernel/xenumerator.h"
-#include "xenia/kernel/xthread.h"
#include "xenia/xbox.h"
#if XE_PLATFORM_WIN32
@@ -29,23 +27,23 @@ namespace kernel {
namespace xam {
// https://github.com/LestaD/SourceEngine2007/blob/master/se2007/engine/xboxsystem.cpp#L518
-dword_result_t XamEnumerate(dword_t handle, dword_t flags, lpvoid_t buffer,
- dword_t buffer_length, lpdword_t items_returned,
- pointer_t<XAM_OVERLAPPED> overlapped) {
+uint32_t xeXamEnumerate(uint32_t handle, uint32_t flags, void* buffer,
+ uint32_t buffer_length, uint32_t* items_returned,
+ uint32_t overlapped_ptr) {
assert_true(flags == 0);
auto e = kernel_state()->object_table()->LookupObject<XEnumerator>(handle);
if (!e) {
- if (overlapped) {
+ if (overlapped_ptr) {
kernel_state()->CompleteOverlappedImmediateEx(
- overlapped, X_ERROR_INVALID_HANDLE, X_ERROR_INVALID_HANDLE, 0);
+ overlapped_ptr, X_ERROR_INVALID_HANDLE, X_ERROR_INVALID_HANDLE, 0);
return X_ERROR_IO_PENDING;
} else {
return X_ERROR_INVALID_HANDLE;
}
}
- size_t actual_buffer_length = (uint32_t)buffer_length;
+ size_t actual_buffer_length = buffer_length;
if (buffer_length == e->items_per_enumerate()) {
actual_buffer_length = e->item_size() * e->items_per_enumerate();
// Known culprits:
@@ -58,7 +56,7 @@ dword_result_t XamEnumerate(dword_t handle, dword_t flags, lpvoid_t buffer,
e->items_per_enumerate());
}
- buffer.Zero(actual_buffer_length);
+ std::memset(buffer, 0, actual_buffer_length);
X_RESULT result;
uint32_t item_count = 0;
@@ -68,7 +66,7 @@ dword_result_t XamEnumerate(dword_t handle, dword_t flags, lpvoid_t buffer,
} else if (e->current_item() >= e->item_count()) {
result = X_ERROR_NO_MORE_FILES;
} else {
- auto item_buffer = buffer.as<uint8_t*>();
+ auto item_buffer = static_cast<uint8_t*>(buffer);
auto max_items = actual_buffer_length / e->item_size();
while (max_items--) {
if (!e->WriteItem(item_buffer)) {
@@ -81,13 +79,13 @@ dword_result_t XamEnumerate(dword_t handle, dword_t flags, lpvoid_t buffer,
}
if (items_returned) {
- assert_true(!overlapped);
+ assert_true(!overlapped_ptr);
*items_returned = result == X_ERROR_SUCCESS ? item_count : 0;
return result;
- } else if (overlapped) {
+ } else if (overlapped_ptr) {
assert_true(!items_returned);
kernel_state()->CompleteOverlappedImmediateEx(
- overlapped,
+ overlapped_ptr,
result == X_ERROR_SUCCESS ? X_ERROR_SUCCESS : X_ERROR_FUNCTION_FAILED,
X_HRESULT_FROM_WIN32(result),
result == X_ERROR_SUCCESS ? item_count : 0);
@@ -97,6 +95,18 @@ dword_result_t XamEnumerate(dword_t handle, dword_t flags, lpvoid_t buffer,
return X_ERROR_INVALID_PARAMETER;
}
}
+
+dword_result_t XamEnumerate(dword_t handle, dword_t flags, lpvoid_t buffer,
+ dword_t buffer_length, lpdword_t items_returned,
+ pointer_t<XAM_OVERLAPPED> overlapped) {
+ uint32_t dummy;
+ auto result = xeXamEnumerate(handle, flags, buffer, buffer_length,
+ !overlapped ? &dummy : nullptr, overlapped);
+ if (!overlapped && items_returned) {
+ *items_returned = dummy;
+ }
+ return result;
+}
DECLARE_XAM_EXPORT1(XamEnumerate, kNone, kImplemented);
dword_result_t XamCreateEnumeratorHandle(unknown_t unk1, unknown_t unk2,
@@ -107,9 +117,22 @@ dword_result_t XamCreateEnumeratorHandle(unknown_t unk1, unknown_t unk2,
}
DECLARE_XAM_EXPORT1(XamCreateEnumeratorHandle, kNone, kStub);
-dword_result_t XamGetPrivateEnumStructureFromHandle(unknown_t unk1,
- unknown_t unk2) {
- return X_ERROR_INVALID_PARAMETER;
+dword_result_t XamGetPrivateEnumStructureFromHandle(dword_t handle,
+ lpdword_t out_object_ptr) {
+ auto e = kernel_state()->object_table()->LookupObject<XEnumerator>(handle);
+ if (!e) {
+ return X_STATUS_INVALID_HANDLE;
+ }
+
+ // Caller takes the reference.
+ // It's released in ObDereferenceObject.
+ e->RetainHandle();
+
+ if (out_object_ptr.guest_address()) {
+ *out_object_ptr = e->guest_object();
+ }
+
+ return X_STATUS_SUCCESS;
}
DECLARE_XAM_EXPORT1(XamGetPrivateEnumStructureFromHandle, kNone, kStub);
diff --git a/src/xenia/kernel/xam/xam_module_export_groups.inc b/src/xenia/kernel/xam/xam_module_export_groups.inc
index 7a21a17e8..d01464f93 100644
--- a/src/xenia/kernel/xam/xam_module_export_groups.inc
+++ b/src/xenia/kernel/xam/xam_module_export_groups.inc
@@ -12,6 +12,7 @@
XE_MODULE_EXPORT_GROUP(xam, Avatar)
XE_MODULE_EXPORT_GROUP(xam, Content)
+XE_MODULE_EXPORT_GROUP(xam, ContentAggregate)
XE_MODULE_EXPORT_GROUP(xam, ContentDevice)
XE_MODULE_EXPORT_GROUP(xam, Enum)
XE_MODULE_EXPORT_GROUP(xam, Info)
@@ -21,6 +22,7 @@ XE_MODULE_EXPORT_GROUP(xam, Msg)
XE_MODULE_EXPORT_GROUP(xam, Net)
XE_MODULE_EXPORT_GROUP(xam, Notify)
XE_MODULE_EXPORT_GROUP(xam, NUI)
+XE_MODULE_EXPORT_GROUP(xam, Task)
XE_MODULE_EXPORT_GROUP(xam, UI)
XE_MODULE_EXPORT_GROUP(xam, User)
XE_MODULE_EXPORT_GROUP(xam, Video)
diff --git a/src/xenia/kernel/xam/xam_msg.cc b/src/xenia/kernel/xam/xam_msg.cc
index 5bcefa25d..aedac0573 100644
--- a/src/xenia/kernel/xam/xam_msg.cc
+++ b/src/xenia/kernel/xam/xam_msg.cc
@@ -11,7 +11,9 @@
#include "xenia/kernel/kernel_state.h"
#include "xenia/kernel/util/shim_utils.h"
#include "xenia/kernel/xam/xam_private.h"
+#include "xenia/kernel/xboxkrnl/xboxkrnl_error.h"
#include "xenia/kernel/xevent.h"
+#include "xenia/kernel/xthread.h"
#include "xenia/xbox.h"
namespace xe {
@@ -40,39 +42,49 @@ dword_result_t XMsgSystemProcessCall(dword_t app, dword_t message,
}
DECLARE_XAM_EXPORT1(XMsgSystemProcessCall, kNone, kImplemented);
-dword_result_t XMsgStartIORequest(dword_t app, dword_t message,
- pointer_t<XAM_OVERLAPPED> overlapped_ptr,
- dword_t buffer, dword_t buffer_length) {
- auto result = kernel_state()->app_manager()->DispatchMessageAsync(
- app, message, buffer, buffer_length);
- if (result == X_ERROR_NOT_FOUND) {
- XELOGE("XMsgStartIORequest: app {:08X} undefined", app);
- }
- if (overlapped_ptr) {
- kernel_state()->CompleteOverlappedImmediate(overlapped_ptr, result);
- result = X_ERROR_IO_PENDING;
- }
- return result;
-}
-DECLARE_XAM_EXPORT1(XMsgStartIORequest, kNone, kImplemented);
+struct XMSGSTARTIOREQUEST_UNKNOWNARG {
+ be<uint32_t> unk_0;
+ be<uint32_t> unk_1;
+};
-dword_result_t XMsgStartIORequestEx(dword_t app, dword_t message,
- pointer_t<XAM_OVERLAPPED> overlapped_ptr,
- dword_t buffer, dword_t buffer_length,
- lpdword_t unknown_ptr) {
+X_HRESULT xeXMsgStartIORequestEx(uint32_t app, uint32_t message,
+ uint32_t overlapped_ptr, uint32_t buffer_ptr,
+ uint32_t buffer_length,
+ XMSGSTARTIOREQUEST_UNKNOWNARG* unknown) {
auto result = kernel_state()->app_manager()->DispatchMessageAsync(
- app, message, buffer, buffer_length);
- if (result == X_ERROR_NOT_FOUND) {
+ app, message, buffer_ptr, buffer_length);
+ if (result == X_E_NOTFOUND) {
XELOGE("XMsgStartIORequestEx: app {:08X} undefined", app);
+ result = X_E_INVALIDARG;
+ XThread::SetLastError(X_ERROR_NOT_FOUND);
}
if (overlapped_ptr) {
kernel_state()->CompleteOverlappedImmediate(overlapped_ptr, result);
result = X_ERROR_IO_PENDING;
}
+ if (result == X_ERROR_SUCCESS || X_ERROR_IO_PENDING) {
+ XThread::SetLastError(0);
+ }
return result;
}
+
+dword_result_t XMsgStartIORequestEx(
+ dword_t app, dword_t message, pointer_t<XAM_OVERLAPPED> overlapped_ptr,
+ dword_t buffer_ptr, dword_t buffer_length,
+ pointer_t<XMSGSTARTIOREQUEST_UNKNOWNARG> unknown_ptr) {
+ return xeXMsgStartIORequestEx(app, message, overlapped_ptr, buffer_ptr,
+ buffer_length, unknown_ptr);
+}
DECLARE_XAM_EXPORT1(XMsgStartIORequestEx, kNone, kImplemented);
+dword_result_t XMsgStartIORequest(dword_t app, dword_t message,
+ pointer_t<XAM_OVERLAPPED> overlapped_ptr,
+ dword_t buffer_ptr, dword_t buffer_length) {
+ return xeXMsgStartIORequestEx(app, message, overlapped_ptr, buffer_ptr,
+ buffer_length, nullptr);
+}
+DECLARE_XAM_EXPORT1(XMsgStartIORequest, kNone, kImplemented);
+
dword_result_t XMsgCancelIORequest(pointer_t<XAM_OVERLAPPED> overlapped_ptr,
dword_t wait) {
X_HANDLE event_handle = XOverlappedGetEvent(overlapped_ptr);
@@ -88,9 +100,42 @@ dword_result_t XMsgCancelIORequest(pointer_t<XAM_OVERLAPPED> overlapped_ptr,
}
DECLARE_XAM_EXPORT1(XMsgCancelIORequest, kNone, kImplemented);
+dword_result_t XMsgCompleteIORequest(pointer_t<XAM_OVERLAPPED> overlapped_ptr,
+ dword_t result, dword_t extended_error,
+ dword_t length) {
+ kernel_state()->CompleteOverlappedImmediateEx(overlapped_ptr, result,
+ extended_error, length);
+ return X_ERROR_SUCCESS;
+}
+DECLARE_XAM_EXPORT2(XMsgCompleteIORequest, kNone, kImplemented, kSketchy);
+
+dword_result_t XamGetOverlappedResult(pointer_t<XAM_OVERLAPPED> overlapped_ptr,
+ lpdword_t length_ptr, dword_t unknown) {
+ uint32_t result;
+ if (overlapped_ptr->result != X_ERROR_IO_PENDING) {
+ result = overlapped_ptr->result;
+ } else if (!overlapped_ptr->event) {
+ result = X_ERROR_IO_INCOMPLETE;
+ } else {
+ auto ev = kernel_state()->object_table()->LookupObject<XEvent>(
+ overlapped_ptr->event);
+ result = ev->Wait(3, 1, 0, nullptr);
+ if (XSUCCEEDED(result)) {
+ result = overlapped_ptr->result;
+ } else {
+ result = xboxkrnl::xeRtlNtStatusToDosError(result);
+ }
+ }
+ if (XSUCCEEDED(result) && length_ptr) {
+ *length_ptr = overlapped_ptr->length;
+ }
+ return result;
+}
+DECLARE_XAM_EXPORT2(XamGetOverlappedResult, kNone, kImplemented, kSketchy);
+
void RegisterMsgExports(xe::cpu::ExportResolver* export_resolver,
KernelState* kernel_state) {}
} // namespace xam
} // namespace kernel
-} // namespace xe \ No newline at end of file
+} // namespace xe
diff --git a/src/xenia/kernel/xam/xam_task.cc b/src/xenia/kernel/xam/xam_task.cc
new file mode 100644
index 000000000..facac462c
--- /dev/null
+++ b/src/xenia/kernel/xam/xam_task.cc
@@ -0,0 +1,66 @@
+/**
+ ******************************************************************************
+ * Xenia : Xbox 360 Emulator Research Project *
+ ******************************************************************************
+ * Copyright 2020 Ben Vanik. All rights reserved. *
+ * Released under the BSD license - see LICENSE in the root for more details. *
+ ******************************************************************************
+ */
+
+#include "xenia/base/logging.h"
+#include "xenia/base/string_util.h"
+#include "xenia/cpu/processor.h"
+#include "xenia/kernel/kernel_state.h"
+#include "xenia/kernel/util/shim_utils.h"
+#include "xenia/kernel/xam/xam_module.h"
+#include "xenia/kernel/xam/xam_private.h"
+#include "xenia/kernel/xthread.h"
+#include "xenia/xbox.h"
+
+#if XE_PLATFORM_WIN32
+#include "xenia/base/platform_win.h"
+#endif
+
+#include "third_party/fmt/include/fmt/format.h"
+
+namespace xe {
+namespace kernel {
+namespace xam {
+
+struct XTASK_MESSAGE {
+ be<uint32_t> unknown_00;
+ be<uint32_t> unknown_04;
+ be<uint32_t> unknown_08;
+ be<uint32_t> callback_arg_ptr;
+ be<uint32_t> event_handle;
+ be<uint32_t> unknown_14;
+ be<uint32_t> task_handle;
+};
+static_assert_size(XTASK_MESSAGE, 0x1C);
+
+dword_result_t XamTaskSchedule(lpvoid_t callback,
+ pointer_t<XTASK_MESSAGE> message,
+ dword_t unknown, lpdword_t handle_ptr) {
+ assert_zero(unknown);
+
+ // TODO(gibbed): figure out what this is for
+ *handle_ptr = 12345;
+
+ XELOGW("!! Executing scheduled task ({:08X}) synchronously, PROBABLY BAD !! ",
+ callback.guest_address());
+
+ // TODO(gibbed): this is supposed to be async... let's cheat.
+ auto thread_state = XThread::GetCurrentThread()->thread_state();
+ uint64_t args[] = {message.guest_address()};
+ auto result = kernel_state()->processor()->Execute(thread_state, callback,
+ args, xe::countof(args));
+ return X_STATUS_SUCCESS;
+}
+DECLARE_XAM_EXPORT2(XamTaskSchedule, kNone, kImplemented, kSketchy);
+
+void RegisterTaskExports(xe::cpu::ExportResolver* export_resolver,
+ KernelState* kernel_state) {}
+
+} // namespace xam
+} // namespace kernel
+} // namespace xe
diff --git a/src/xenia/kernel/xam/xam_user.cc b/src/xenia/kernel/xam/xam_user.cc
index 8dc8ae492..92a5142bd 100644
--- a/src/xenia/kernel/xam/xam_user.cc
+++ b/src/xenia/kernel/xam/xam_user.cc
@@ -562,11 +562,14 @@ dword_result_t XamUserCreateAchievementEnumerator(dword_t title_id,
*buffer_size_ptr = 500 * count;
}
- auto e = new XStaticEnumerator(kernel_state(), count, 500);
- e->Initialize();
+ auto e = object_ref<XStaticEnumerator>(
+ new XStaticEnumerator(kernel_state(), count, 500));
+ auto result = e->Initialize(user_index, 0xFB, 0xB000A, 0xB000B, 0);
+ if (XFAILED(result)) {
+ return result;
+ }
*handle_ptr = e->handle();
-
return X_ERROR_SUCCESS;
}
DECLARE_XAM_EXPORT1(XamUserCreateAchievementEnumerator, kUserProfiles,
diff --git a/src/xenia/kernel/xenumerator.cc b/src/xenia/kernel/xenumerator.cc
index 5de0a3d12..693d756e0 100644
--- a/src/xenia/kernel/xenumerator.cc
+++ b/src/xenia/kernel/xenumerator.cc
@@ -20,7 +20,34 @@ XEnumerator::XEnumerator(KernelState* kernel_state, size_t items_per_enumerate,
XEnumerator::~XEnumerator() = default;
-void XEnumerator::Initialize() {}
+X_STATUS XEnumerator::Initialize(uint32_t user_index, uint32_t app_id,
+ uint32_t message, uint32_t message2,
+ uint32_t flags, uint32_t extra_size,
+ void** extra_buffer) {
+ auto native_object = CreateNative(sizeof(X_KENUMERATOR) + extra_size);
+ if (!native_object) {
+ return X_STATUS_NO_MEMORY;
+ }
+ auto guest_object = reinterpret_cast<X_KENUMERATOR*>(native_object);
+ guest_object->app_id = app_id;
+ guest_object->message = message;
+ guest_object->message2 = message2;
+ guest_object->user_index = user_index;
+ guest_object->items_per_enumerate =
+ static_cast<uint32_t>(items_per_enumerate_);
+ guest_object->flags = flags;
+ if (extra_buffer) {
+ *extra_buffer =
+ !extra_buffer ? nullptr : &native_object[sizeof(X_KENUMERATOR)];
+ }
+ return X_STATUS_SUCCESS;
+}
+
+X_STATUS XEnumerator::Initialize(uint32_t user_index, uint32_t app_id,
+ uint32_t message, uint32_t message2,
+ uint32_t flags) {
+ return Initialize(user_index, app_id, message, message2, flags, 0, nullptr);
+}
} // namespace kernel
} // namespace xe
diff --git a/src/xenia/kernel/xenumerator.h b/src/xenia/kernel/xenumerator.h
index 189453813..0217b02e5 100644
--- a/src/xenia/kernel/xenumerator.h
+++ b/src/xenia/kernel/xenumerator.h
@@ -19,6 +19,21 @@
namespace xe {
namespace kernel {
+struct X_KENUMERATOR {
+ be<uint32_t> app_id;
+ be<uint32_t> message;
+ be<uint32_t> message2;
+ be<uint32_t> user_index;
+ be<uint32_t> items_per_enumerate;
+ be<uint32_t> flags;
+};
+static_assert_size(X_KENUMERATOR, 0x18);
+
+struct X_KENUMERATOR_CONTENT_AGGREGATE {
+ be<uint32_t> magic;
+ be<uint32_t> handle;
+};
+
class XEnumerator : public XObject {
public:
static const XObject::Type kObjectType = XObject::Type::Enumerator;
@@ -27,7 +42,24 @@ class XEnumerator : public XObject {
size_t item_size);
virtual ~XEnumerator();
- void Initialize();
+ X_STATUS Initialize(uint32_t user_index, uint32_t app_id, uint32_t message,
+ uint32_t message2, uint32_t flags, uint32_t extra_size,
+ void** extra_buffer);
+
+ X_STATUS Initialize(uint32_t user_index, uint32_t app_id, uint32_t message,
+ uint32_t message2, uint32_t flags);
+
+ template <typename T>
+ X_STATUS Initialize(uint32_t user_index, uint32_t app_id, uint32_t message,
+ uint32_t message2, uint32_t flags, T** extra) {
+ void* dummy;
+ auto result = Initialize(user_index, app_id, message, message2, flags,
+ static_cast<uint32_t>(sizeof(T)), &dummy);
+ if (extra) {
+ *extra = XFAILED(result) ? nullptr : static_cast<T*>(dummy);
+ }
+ return result;
+ }
virtual uint32_t item_count() const = 0;
virtual void WriteItems(uint8_t* buffer) = 0;
diff --git a/src/xenia/xbox.h b/src/xenia/xbox.h
index 40ad730a1..62f4a1f65 100644
--- a/src/xenia/xbox.h
+++ b/src/xenia/xbox.h
@@ -94,6 +94,7 @@ typedef uint32_t X_RESULT;
#define X_ERROR_BAD_ARGUMENTS X_RESULT_FROM_WIN32(0x000000A0L)
#define X_ERROR_BUSY X_RESULT_FROM_WIN32(0x000000AAL)
#define X_ERROR_ALREADY_EXISTS X_RESULT_FROM_WIN32(0x000000B7L)
+#define X_ERROR_IO_INCOMPLETE X_RESULT_FROM_WIN32(0x000003E4L)
#define X_ERROR_IO_PENDING X_RESULT_FROM_WIN32(0x000003E5L)
#define X_ERROR_DEVICE_NOT_CONNECTED X_RESULT_FROM_WIN32(0x0000048FL)
#define X_ERROR_NOT_FOUND X_RESULT_FROM_WIN32(0x00000490L)
@@ -112,6 +113,8 @@ typedef uint32_t X_HRESULT;
#define X_E_FALSE static_cast<X_HRESULT>(0x80000000L)
#define X_E_SUCCESS X_HRESULT_FROM_WIN32(X_ERROR_SUCCESS)
+#define X_E_FAIL static_cast<X_HRESULT>(0x80004005L)
+#define X_E_NO_MORE_FILES X_HRESULT_FROM_WIN32(X_ERROR_NO_MORE_FILES)
#define X_E_INVALIDARG X_HRESULT_FROM_WIN32(X_ERROR_INVALID_PARAMETER)
#define X_E_DEVICE_NOT_CONNECTED X_HRESULT_FROM_WIN32(X_ERROR_DEVICE_NOT_CONNECTED)
#define X_E_NOTFOUND X_HRESULT_FROM_WIN32(X_ERROR_NOT_FOUND)