Recording more unknown app messages for future reference.
Xam message 2B001 is called in Toy Story Mania!
Xmp messages 7002F and 70044 are called on the start of all dashboard versions before the Kinect update. When 70044 is set to return X_E_FAIL you can access the xbox music player but when it succeeds the emulator locks up and you are forced to close it manually (no crash). The music player can't be access at all past version 5787 and instead goes back to the previous screen as if it is closing itself down. 7002B (NXE and Kinect) and 70053 (Blades) are called when trying to access the music, picture, and video libraries.
Messenger is only called when using netplay with dashboard versions 5759 to 5787. After that an "xbox live is currently unavailable" messaged is displayed on 3 of the tabs (marketplace, xbox live, and games) blocking any internet related messages from appearing on versions 6683 to 6717.
Xlivebase message 5008C is called on the start of dashboard versions 1888 to 2858 and 50094 is called instead from versions 4532 to 4552. LogonGetHR is called on the startup of dashboard (netplay version)
XGI message B0036 is called after clicking on xbox live in the arcade library (v5759 to 5787) or from the game library (v6683 to v6717). B0021 is sent in the games Fatal Fury Special and Beijing 2008. B003D is used by UFC 2009 (netplay build).
218 lines
8.2 KiB
C++
218 lines
8.2 KiB
C++
/**
|
|
******************************************************************************
|
|
* Xenia : Xbox 360 Emulator Research Project *
|
|
******************************************************************************
|
|
* Copyright 2021 Ben Vanik. All rights reserved. *
|
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
|
******************************************************************************
|
|
*/
|
|
|
|
#include "xenia/kernel/xam/apps/xgi_app.h"
|
|
|
|
#include "xenia/base/logging.h"
|
|
#include "xenia/base/threading.h"
|
|
|
|
namespace xe {
|
|
namespace kernel {
|
|
namespace xam {
|
|
namespace apps {
|
|
|
|
struct X_XUSER_ACHIEVEMENT {
|
|
xe::be<uint32_t> user_idx;
|
|
xe::be<uint32_t> achievement_id;
|
|
};
|
|
|
|
XgiApp::XgiApp(KernelState* kernel_state) : App(kernel_state, 0xFB) {}
|
|
|
|
// http://mb.mirage.org/bugzilla/xliveless/main.c
|
|
|
|
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) {
|
|
case 0x000B0006: {
|
|
assert_true(!buffer_length || buffer_length == 24);
|
|
// dword r3 user index
|
|
// dword (unwritten?)
|
|
// qword 0
|
|
// dword r4 context enum
|
|
// dword r5 value
|
|
uint32_t user_index = xe::load_and_swap<uint32_t>(buffer + 0);
|
|
uint32_t context_id = xe::load_and_swap<uint32_t>(buffer + 16);
|
|
uint32_t context_value = xe::load_and_swap<uint32_t>(buffer + 20);
|
|
XELOGD("XGIUserSetContextEx({:08X}, {:08X}, {:08X})", user_index,
|
|
context_id, context_value);
|
|
|
|
const util::XdbfGameData title_xdbf = kernel_state_->title_xdbf();
|
|
if (title_xdbf.is_valid()) {
|
|
const auto context = title_xdbf.GetContext(context_id);
|
|
const XLanguage title_language = title_xdbf.GetExistingLanguage(
|
|
static_cast<XLanguage>(XLanguage::kEnglish));
|
|
const std::string desc =
|
|
title_xdbf.GetStringTableEntry(title_language, context.string_id);
|
|
XELOGD("XGIUserSetContextEx: {} - Set to value: {}", desc,
|
|
context_value);
|
|
|
|
UserProfile* user_profile = kernel_state_->user_profile(user_index);
|
|
if (user_profile) {
|
|
user_profile->contexts_[context_id] = context_value;
|
|
}
|
|
}
|
|
return X_E_SUCCESS;
|
|
}
|
|
case 0x000B0007: {
|
|
uint32_t user_index = xe::load_and_swap<uint32_t>(buffer + 0);
|
|
uint32_t property_id = xe::load_and_swap<uint32_t>(buffer + 16);
|
|
uint32_t value_size = xe::load_and_swap<uint32_t>(buffer + 20);
|
|
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);
|
|
|
|
const util::XdbfGameData title_xdbf = kernel_state_->title_xdbf();
|
|
if (title_xdbf.is_valid()) {
|
|
const auto property = title_xdbf.GetContext(property_id);
|
|
const XLanguage title_language = title_xdbf.GetExistingLanguage(
|
|
static_cast<XLanguage>(XLanguage::kEnglish));
|
|
const std::string desc =
|
|
title_xdbf.GetStringTableEntry(title_language, property.string_id);
|
|
XELOGD("XGIUserSetPropertyEx: Setting property: {}", desc);
|
|
}
|
|
|
|
return X_E_SUCCESS;
|
|
}
|
|
case 0x000B0008: {
|
|
assert_true(!buffer_length || buffer_length == 8);
|
|
uint32_t achievement_count = xe::load_and_swap<uint32_t>(buffer + 0);
|
|
uint32_t achievements_ptr = xe::load_and_swap<uint32_t>(buffer + 4);
|
|
XELOGD("XGIUserWriteAchievements({:08X}, {:08X})", achievement_count,
|
|
achievements_ptr);
|
|
|
|
auto* achievement =
|
|
(X_XUSER_ACHIEVEMENT*)memory_->TranslateVirtual(achievements_ptr);
|
|
for (uint32_t i = 0; i < achievement_count; i++, achievement++) {
|
|
kernel_state_->achievement_manager()->EarnAchievement(
|
|
achievement->user_idx, 0, achievement->achievement_id);
|
|
}
|
|
return X_E_SUCCESS;
|
|
}
|
|
case 0x000B0010: {
|
|
assert_true(!buffer_length || buffer_length == 28);
|
|
// Sequence:
|
|
// - XamSessionCreateHandle
|
|
// - XamSessionRefObjByHandle
|
|
// - [this]
|
|
// - CloseHandle
|
|
uint32_t session_ptr = xe::load_and_swap<uint32_t>(buffer + 0x0);
|
|
uint32_t flags = xe::load_and_swap<uint32_t>(buffer + 0x4);
|
|
uint32_t num_slots_public = xe::load_and_swap<uint32_t>(buffer + 0x8);
|
|
uint32_t num_slots_private = xe::load_and_swap<uint32_t>(buffer + 0xC);
|
|
uint32_t user_xuid = xe::load_and_swap<uint32_t>(buffer + 0x10);
|
|
uint32_t session_info_ptr = xe::load_and_swap<uint32_t>(buffer + 0x14);
|
|
uint32_t nonce_ptr = xe::load_and_swap<uint32_t>(buffer + 0x18);
|
|
|
|
XELOGD(
|
|
"XGISessionCreateImpl({:08X}, {:08X}, {}, {}, {:08X}, {:08X}, "
|
|
"{:08X})",
|
|
session_ptr, flags, num_slots_public, num_slots_private, user_xuid,
|
|
session_info_ptr, nonce_ptr);
|
|
return X_E_SUCCESS;
|
|
}
|
|
case 0x000B0011: {
|
|
// TODO(PermaNull): reverse buffer contents.
|
|
XELOGD("XGISessionDelete");
|
|
return X_STATUS_SUCCESS;
|
|
}
|
|
case 0x000B0012: {
|
|
assert_true(buffer_length == 0x14);
|
|
uint32_t session_ptr = xe::load_and_swap<uint32_t>(buffer + 0x0);
|
|
uint32_t user_count = xe::load_and_swap<uint32_t>(buffer + 0x4);
|
|
uint32_t unk_0 = xe::load_and_swap<uint32_t>(buffer + 0x8);
|
|
uint32_t user_index_array = xe::load_and_swap<uint32_t>(buffer + 0xC);
|
|
uint32_t private_slots_array = xe::load_and_swap<uint32_t>(buffer + 0x10);
|
|
|
|
assert_zero(unk_0);
|
|
XELOGD("XGISessionJoinLocal({:08X}, {}, {}, {:08X}, {:08X})", session_ptr,
|
|
user_count, unk_0, user_index_array, private_slots_array);
|
|
return X_E_SUCCESS;
|
|
}
|
|
case 0x000B0014: {
|
|
// Gets 584107FB in game.
|
|
// get high score table?
|
|
XELOGD("XGI_unknown");
|
|
return X_STATUS_SUCCESS;
|
|
}
|
|
case 0x000B0015: {
|
|
// send high scores?
|
|
XELOGD("XGI_unknown");
|
|
return X_STATUS_SUCCESS;
|
|
}
|
|
case 0x000B0021: {
|
|
struct XLeaderboard {
|
|
xe::be<uint32_t> titleId;
|
|
xe::be<uint32_t> xuids_count;
|
|
xe::be<uint32_t> xuids_guest_address;
|
|
xe::be<uint32_t> specs_count;
|
|
xe::be<uint32_t> specs_guest_address;
|
|
xe::be<uint32_t> results_size;
|
|
xe::be<uint32_t> results_guest_address;
|
|
}* data = reinterpret_cast<XLeaderboard*>(buffer);
|
|
|
|
if (!data->results_guest_address) {
|
|
return 1;
|
|
}
|
|
}
|
|
case 0x000B0036: {
|
|
// Called after opening xbox live arcade and clicking on xbox live v5759
|
|
// to 5787 and called after clicking xbox live in the game library from
|
|
// v6683 to v6717
|
|
XELOGD("XGIUnkB0036, unimplemented");
|
|
return X_E_FAIL;
|
|
}
|
|
case 0x000B003D: {
|
|
// Games used in:
|
|
// - 5451082a (netplay build).
|
|
XELOGD("XGIUnkB003D, unimplemented");
|
|
return X_E_FAIL;
|
|
}
|
|
case 0x000B0041: {
|
|
assert_true(!buffer_length || buffer_length == 32);
|
|
// 00000000 2789fecc 00000000 00000000 200491e0 00000000 200491f0 20049340
|
|
uint32_t user_index = xe::load_and_swap<uint32_t>(buffer + 0);
|
|
uint32_t context_ptr = xe::load_and_swap<uint32_t>(buffer + 16);
|
|
auto context =
|
|
context_ptr ? memory_->TranslateVirtual(context_ptr) : nullptr;
|
|
uint32_t context_id =
|
|
context ? xe::load_and_swap<uint32_t>(context + 0) : 0;
|
|
XELOGD("XGIUserGetContext({:08X}, {:08X}{:08X}))", user_index,
|
|
context_ptr, context_id);
|
|
uint32_t value = 0;
|
|
if (context) {
|
|
UserProfile* user_profile = kernel_state_->user_profile(user_index);
|
|
if (user_profile) {
|
|
if (user_profile->contexts_.find(context_id) !=
|
|
user_profile->contexts_.cend()) {
|
|
value = user_profile->contexts_[context_id];
|
|
}
|
|
}
|
|
xe::store_and_swap<uint32_t>(context + 4, value);
|
|
}
|
|
return X_E_FAIL;
|
|
}
|
|
case 0x000B0071: {
|
|
XELOGD("XGI 0x000B0071, unimplemented");
|
|
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_E_FAIL;
|
|
}
|
|
|
|
} // namespace apps
|
|
} // namespace xam
|
|
} // namespace kernel
|
|
} // namespace xe
|