Shuffling kernel/.

This commit is contained in:
Ben Vanik
2015-09-06 18:07:52 -07:00
parent 494cba7131
commit e5fbf840d2
118 changed files with 749 additions and 620 deletions

View File

@@ -0,0 +1,60 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/kernel/xam/app_manager.h"
#include "xenia/kernel/kernel_state.h"
#include "xenia/kernel/xam/apps/xgi_app.h"
#include "xenia/kernel/xam/apps/xlivebase_app.h"
#include "xenia/kernel/xam/apps/xmp_app.h"
namespace xe {
namespace kernel {
namespace xam {
App::App(KernelState* kernel_state, uint32_t app_id)
: kernel_state_(kernel_state),
memory_(kernel_state->memory()),
app_id_(app_id) {}
void AppManager::RegisterApps(KernelState* kernel_state, AppManager* manager) {
manager->RegisterApp(std::make_unique<apps::XgiApp>(kernel_state));
manager->RegisterApp(std::make_unique<apps::XLiveBaseApp>(kernel_state));
manager->RegisterApp(std::make_unique<apps::XmpApp>(kernel_state));
}
void AppManager::RegisterApp(std::unique_ptr<App> app) {
assert_zero(app_lookup_.count(app->app_id()));
app_lookup_.insert({app->app_id(), app.get()});
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) {
const auto& it = app_lookup_.find(app_id);
if (it == app_lookup_.end()) {
return X_STATUS_UNSUCCESSFUL;
}
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) {
const auto& it = app_lookup_.find(app_id);
if (it == app_lookup_.end()) {
return X_ERROR_NOT_FOUND;
}
return it->second->DispatchMessageSync(message, buffer_ptr, buffer_length);
}
} // namespace xam
} // namespace kernel
} // namespace xe

View File

@@ -0,0 +1,66 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_KERNEL_XAM_APP_MANAGER_H_
#define XENIA_KERNEL_XAM_APP_MANAGER_H_
#include <cstddef>
#include <memory>
#include <unordered_map>
#include <vector>
#include "xenia/memory.h"
#include "xenia/xbox.h"
namespace xe {
namespace kernel {
class KernelState;
} // namespace kernel
} // namespace xe
namespace xe {
namespace kernel {
namespace xam {
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;
protected:
App(KernelState* kernel_state, uint32_t app_id);
KernelState* kernel_state_;
Memory* memory_;
uint32_t app_id_;
};
class AppManager {
public:
static void RegisterApps(KernelState* kernel_state, AppManager* manager);
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,
uint32_t buffer_ptr, uint32_t buffer_length);
private:
std::vector<std::unique_ptr<App>> apps_;
std::unordered_map<uint32_t, App*> app_lookup_;
};
} // namespace xam
} // namespace kernel
} // namespace xe
#endif // XENIA_KERNEL_XAM_APP_MANAGER_H_

View File

@@ -0,0 +1,100 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2015 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 {
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) {
// 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("XUserSetContextEx(%.8X, %.8X, %.8X)", user_index, context_id,
context_value);
return X_ERROR_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("XUserSetPropertyEx(%.8X, %.8X, %d, %.8X)", user_index,
property_id, value_size, value_ptr);
return X_ERROR_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("XUserWriteAchievements(%.8X, %.8X)", achievement_count,
achievements_ptr);
return X_ERROR_SUCCESS;
}
case 0x000B0010: {
assert_true(!buffer_length || buffer_length == 28);
// Sequence:
// - XamSessionCreateHandle
// - XamSessionRefObjByHandle
// - [this]
// - CloseHandle
XELOGD("XSessionCreateImpl(...)");
return X_STATUS_UNSUCCESSFUL;
}
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("XUserGetContext(%.8X, %.8X(%.8X))", user_index, context_ptr,
context_id);
uint32_t value = 0;
if (context) {
xe::store_and_swap<uint32_t>(context + 4, value);
}
return X_ERROR_FUNCTION_FAILED;
}
case 0x000B0071: {
XELOGD("XGI 0x000B0071, unimplemented");
return X_ERROR_SUCCESS;
}
}
XELOGE("Unimplemented XGI message app=%.8X, msg=%.8X, arg1=%.8X, arg2=%.8X",
app_id(), message, buffer_ptr, buffer_length);
return X_STATUS_UNSUCCESSFUL;
}
} // namespace apps
} // namespace xam
} // namespace kernel
} // namespace xe

View File

@@ -0,0 +1,34 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_KERNEL_XAM_APPS_XGI_APP_H_
#define XENIA_KERNEL_XAM_APPS_XGI_APP_H_
#include "xenia/kernel/kernel_state.h"
#include "xenia/kernel/xam/app_manager.h"
namespace xe {
namespace kernel {
namespace xam {
namespace apps {
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;
};
} // namespace apps
} // namespace xam
} // namespace kernel
} // namespace xe
#endif // XENIA_KERNEL_XAM_APPS_XGI_APP_H_

View File

@@ -0,0 +1,63 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/kernel/xam/apps/xlivebase_app.h"
#include "xenia/base/logging.h"
#include "xenia/base/threading.h"
namespace xe {
namespace kernel {
namespace xam {
namespace apps {
XLiveBaseApp::XLiveBaseApp(KernelState* kernel_state)
: App(kernel_state, 0xFC) {}
// http://mb.mirage.org/bugzilla/xliveless/main.c
X_RESULT 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) {
case 0x00058004: {
// Called on startup, seems to just return a bool in the buffer.
assert_true(!buffer_length || buffer_length == 4);
XELOGD("XLiveBaseGetLogonId(%.8X)", buffer_ptr);
xe::store_and_swap<uint32_t>(buffer + 0, 1); // ?
return X_ERROR_SUCCESS;
}
case 0x00058020: {
// 0x00058004 is called right before this.
// We should create a XamEnumerate-able empty list here, but I'm not
// sure of the format.
// buffer_length seems to be the same ptr sent to 0x00058004.
XELOGD("XLiveBaseFriendsCreateEnumerator(%.8X, %.8X) unimplemented",
buffer_ptr, buffer_length);
return X_STATUS_UNSUCCESSFUL;
}
case 0x00058023: {
XELOGD("XliveBaseUnk58023(%.8X, %.8X) unimplemented", buffer_ptr,
buffer_length);
return X_STATUS_UNSUCCESSFUL;
}
}
XELOGE(
"Unimplemented XLIVEBASE message app=%.8X, msg=%.8X, arg1=%.8X, "
"arg2=%.8X",
app_id(), message, buffer_ptr, buffer_length);
return X_STATUS_UNSUCCESSFUL;
}
} // namespace apps
} // namespace xam
} // namespace kernel
} // namespace xe

View File

@@ -0,0 +1,34 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_KERNEL_XAM_APPS_XLIVEBASE_APP_H_
#define XENIA_KERNEL_XAM_APPS_XLIVEBASE_APP_H_
#include "xenia/kernel/kernel_state.h"
#include "xenia/kernel/xam/app_manager.h"
namespace xe {
namespace kernel {
namespace xam {
namespace apps {
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;
};
} // namespace apps
} // namespace xam
} // namespace kernel
} // namespace xe
#endif // XENIA_KERNEL_XAM_APPS_XLIVEBASE_APP_H_

View File

@@ -0,0 +1,427 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/kernel/xam/apps/xmp_app.h"
#include "xenia/base/logging.h"
#include "xenia/base/threading.h"
namespace xe {
namespace kernel {
namespace xam {
namespace apps {
XmpApp::XmpApp(KernelState* kernel_state)
: App(kernel_state, 0xFA),
state_(State::kIdle),
disabled_(0),
playback_mode_(PlaybackMode::kUnknown),
repeat_mode_(RepeatMode::kUnknown),
unknown_flags_(0),
volume_(0.0f),
active_playlist_(nullptr),
active_song_index_(0),
next_playlist_handle_(1),
next_song_handle_(1) {}
X_RESULT 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));
XELOGD("XMPGetStatus(%.8X)", state_ptr);
xe::store_and_swap<uint32_t>(memory_->TranslateVirtual(state_ptr),
static_cast<uint32_t>(state_));
return X_ERROR_SUCCESS;
}
X_RESULT XmpApp::XMPCreateTitlePlaylist(uint32_t songs_ptr, uint32_t song_count,
uint32_t playlist_name_ptr,
std::wstring playlist_name,
uint32_t flags,
uint32_t out_song_handles,
uint32_t out_playlist_handle) {
XELOGD("XMPCreateTitlePlaylist(%.8X, %.8X, %.8X(%s), %.8X, %.8X, %.8X)",
songs_ptr, song_count, playlist_name_ptr,
xe::to_string(playlist_name).c_str(), flags, out_song_handles,
out_playlist_handle);
auto playlist = std::make_unique<Playlist>();
playlist->handle = ++next_playlist_handle_;
playlist->name = std::move(playlist_name);
playlist->flags = flags;
for (uint32_t i = 0; i < song_count; ++i) {
auto song = std::make_unique<Song>();
song->handle = ++next_song_handle_;
uint8_t* song_base = memory_->TranslateVirtual(songs_ptr + (i * 36));
song->file_path = xe::load_and_swap<std::wstring>(
memory_->TranslateVirtual(xe::load_and_swap<uint32_t>(song_base + 0)));
song->name = xe::load_and_swap<std::wstring>(
memory_->TranslateVirtual(xe::load_and_swap<uint32_t>(song_base + 4)));
song->artist = xe::load_and_swap<std::wstring>(
memory_->TranslateVirtual(xe::load_and_swap<uint32_t>(song_base + 8)));
song->album = xe::load_and_swap<std::wstring>(
memory_->TranslateVirtual(xe::load_and_swap<uint32_t>(song_base + 12)));
song->album_artist = xe::load_and_swap<std::wstring>(
memory_->TranslateVirtual(xe::load_and_swap<uint32_t>(song_base + 16)));
song->genre = xe::load_and_swap<std::wstring>(
memory_->TranslateVirtual(xe::load_and_swap<uint32_t>(song_base + 20)));
song->track_number = xe::load_and_swap<uint32_t>(song_base + 24);
song->duration_ms = xe::load_and_swap<uint32_t>(song_base + 28);
song->format =
static_cast<Song::Format>(xe::load_and_swap<uint32_t>(song_base + 32));
if (out_song_handles) {
xe::store_and_swap<uint32_t>(
memory_->TranslateVirtual(out_song_handles + (i * 4)), song->handle);
}
playlist->songs.emplace_back(std::move(song));
}
xe::store_and_swap<uint32_t>(memory_->TranslateVirtual(out_playlist_handle),
playlist->handle);
auto global_lock = global_critical_region_.Acquire();
playlists_.insert({playlist->handle, playlist.get()});
playlist.release();
return X_ERROR_SUCCESS;
}
X_RESULT XmpApp::XMPDeleteTitlePlaylist(uint32_t playlist_handle) {
XELOGD("XMPDeleteTitlePlaylist(%.8X)", playlist_handle);
auto global_lock = global_critical_region_.Acquire();
auto it = playlists_.find(playlist_handle);
if (it == playlists_.end()) {
XELOGE("Playlist %.8X not found", playlist_handle);
return X_ERROR_NOT_FOUND;
}
auto playlist = it->second;
if (playlist == active_playlist_) {
XMPStop(0);
}
playlists_.erase(it);
delete playlist;
return X_ERROR_SUCCESS;
}
X_RESULT XmpApp::XMPPlayTitlePlaylist(uint32_t playlist_handle,
uint32_t song_handle) {
XELOGD("XMPPlayTitlePlaylist(%.8X, %.8X)", playlist_handle, song_handle);
Playlist* playlist = nullptr;
{
auto global_lock = global_critical_region_.Acquire();
auto it = playlists_.find(playlist_handle);
if (it == playlists_.end()) {
XELOGE("Playlist %.8X not found", playlist_handle);
return X_ERROR_NOT_FOUND;
}
playlist = it->second;
}
if (disabled_) {
// Ignored because we aren't enabled?
XELOGW("Ignoring XMPPlayTitlePlaylist because disabled");
return X_ERROR_SUCCESS;
}
// Start playlist?
XELOGW("Playlist playback not supported");
active_playlist_ = playlist;
active_song_index_ = 0;
state_ = State::kPlaying;
OnStateChanged();
kernel_state_->BroadcastNotification(kMsgPlaybackBehaviorChanged, 1);
return X_ERROR_SUCCESS;
}
X_RESULT XmpApp::XMPContinue() {
XELOGD("XMPContinue()");
if (state_ == State::kPaused) {
state_ = State::kPlaying;
}
OnStateChanged();
return X_ERROR_SUCCESS;
}
X_RESULT XmpApp::XMPStop(uint32_t unk) {
assert_zero(unk);
XELOGD("XMPStop(%.8X)", unk);
active_playlist_ = nullptr; // ?
active_song_index_ = 0;
state_ = State::kIdle;
OnStateChanged();
return X_ERROR_SUCCESS;
}
X_RESULT XmpApp::XMPPause() {
XELOGD("XMPPause()");
if (state_ == State::kPlaying) {
state_ = State::kPaused;
}
OnStateChanged();
return X_ERROR_SUCCESS;
}
X_RESULT XmpApp::XMPNext() {
XELOGD("XMPNext()");
if (!active_playlist_) {
return X_ERROR_NOT_FOUND;
}
state_ = State::kPlaying;
active_song_index_ =
(active_song_index_ + 1) % active_playlist_->songs.size();
OnStateChanged();
return X_ERROR_SUCCESS;
}
X_RESULT XmpApp::XMPPrevious() {
XELOGD("XMPPrevious()");
if (!active_playlist_) {
return X_ERROR_NOT_FOUND;
}
state_ = State::kPlaying;
if (!active_song_index_) {
active_song_index_ = static_cast<int>(active_playlist_->songs.size()) - 1;
} else {
--active_song_index_;
}
OnStateChanged();
return X_ERROR_SUCCESS;
}
void XmpApp::OnStateChanged() {
kernel_state_->BroadcastNotification(kMsgStateChanged,
static_cast<uint32_t>(state_));
}
X_RESULT 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) {
case 0x00070002: {
assert_true(!buffer_length || buffer_length == 12);
uint32_t xmp_client = xe::load_and_swap<uint32_t>(buffer + 0);
uint32_t storage_ptr = xe::load_and_swap<uint32_t>(buffer + 4);
uint32_t song_handle = xe::load_and_swap<uint32_t>(buffer + 8); // 0?
uint32_t playlist_handle =
xe::load_and_swap<uint32_t>(memory_->TranslateVirtual(storage_ptr));
assert_true(xmp_client == 0x00000002);
return XMPPlayTitlePlaylist(playlist_handle, song_handle);
}
case 0x00070003: {
assert_true(!buffer_length || buffer_length == 4);
uint32_t xmp_client = xe::load_and_swap<uint32_t>(buffer + 0);
assert_true(xmp_client == 0x00000002);
return XMPContinue();
}
case 0x00070004: {
assert_true(!buffer_length || buffer_length == 8);
uint32_t xmp_client = xe::load_and_swap<uint32_t>(buffer + 0);
uint32_t unk = xe::load_and_swap<uint32_t>(buffer + 4);
assert_true(xmp_client == 0x00000002);
return XMPStop(unk);
}
case 0x00070005: {
assert_true(!buffer_length || buffer_length == 4);
uint32_t xmp_client = xe::load_and_swap<uint32_t>(buffer + 0);
assert_true(xmp_client == 0x00000002);
return XMPPause();
}
case 0x00070006: {
assert_true(!buffer_length || buffer_length == 4);
uint32_t xmp_client = xe::load_and_swap<uint32_t>(buffer + 0);
assert_true(xmp_client == 0x00000002);
return XMPNext();
}
case 0x00070007: {
assert_true(!buffer_length || buffer_length == 4);
uint32_t xmp_client = xe::load_and_swap<uint32_t>(buffer + 0);
assert_true(xmp_client == 0x00000002);
return XMPPrevious();
}
case 0x00070008: {
assert_true(!buffer_length || buffer_length == 16);
uint32_t xmp_client = xe::load_and_swap<uint32_t>(buffer + 0);
uint32_t playback_mode = xe::load_and_swap<uint32_t>(buffer + 4);
uint32_t repeat_mode = xe::load_and_swap<uint32_t>(buffer + 8);
uint32_t flags = xe::load_and_swap<uint32_t>(buffer + 12);
assert_true(xmp_client == 0x00000002);
XELOGD("XMPSetPlaybackBehavior(%.8X, %.8X, %.8X)", playback_mode,
repeat_mode, flags);
playback_mode_ = static_cast<PlaybackMode>(playback_mode);
repeat_mode_ = static_cast<RepeatMode>(repeat_mode);
unknown_flags_ = flags;
kernel_state_->BroadcastNotification(kMsgPlaybackBehaviorChanged, 0);
return X_ERROR_SUCCESS;
}
case 0x00070009: {
assert_true(!buffer_length || buffer_length == 8);
uint32_t xmp_client = xe::load_and_swap<uint32_t>(buffer + 0);
uint32_t state_ptr =
xe::load_and_swap<uint32_t>(buffer + 4); // out ptr to 4b - expect 0
assert_true(xmp_client == 0x00000002);
return XMPGetStatus(state_ptr);
}
case 0x0007000B: {
assert_true(!buffer_length || buffer_length == 8);
uint32_t xmp_client = xe::load_and_swap<uint32_t>(buffer + 0);
uint32_t float_ptr = xe::load_and_swap<uint32_t>(
buffer + 4); // out ptr to 4b - floating point
assert_true(xmp_client == 0x00000002);
XELOGD("XMPGetVolume(%.8X)", float_ptr);
xe::store_and_swap<float>(memory_->TranslateVirtual(float_ptr), volume_);
return X_ERROR_SUCCESS;
}
case 0x0007000C: {
assert_true(!buffer_length || buffer_length == 8);
uint32_t xmp_client = xe::load_and_swap<uint32_t>(buffer + 0);
float float_value = xe::load_and_swap<float>(buffer + 4);
assert_true(xmp_client == 0x00000002);
XELOGD("XMPSetVolume(%g)", float_value);
volume_ = float_value;
return X_ERROR_SUCCESS;
}
case 0x0007000D: {
assert_true(!buffer_length || buffer_length == 36);
uint32_t xmp_client = xe::load_and_swap<uint32_t>(buffer + 0);
uint32_t storage_ptr = xe::load_and_swap<uint32_t>(buffer + 4);
uint32_t storage_size = xe::load_and_swap<uint32_t>(buffer + 8);
uint32_t songs_ptr = xe::load_and_swap<uint32_t>(buffer + 12);
uint32_t song_count = xe::load_and_swap<uint32_t>(buffer + 16);
uint32_t playlist_name_ptr = xe::load_and_swap<uint32_t>(buffer + 20);
uint32_t flags = xe::load_and_swap<uint32_t>(buffer + 24);
uint32_t song_handles_ptr =
xe::load_and_swap<uint32_t>(buffer + 28); // 0?
uint32_t playlist_handle_ptr = xe::load_and_swap<uint32_t>(buffer + 32);
xe::store_and_swap<uint32_t>(
memory_->TranslateVirtual(playlist_handle_ptr), storage_ptr);
assert_true(xmp_client == 0x00000002);
std::wstring playlist_name;
if (!playlist_name_ptr) {
playlist_name = L"";
} else {
playlist_name = xe::load_and_swap<std::wstring>(
memory_->TranslateVirtual(playlist_name_ptr));
}
// dummy_alloc_ptr is the result of a XamAlloc of storage_size.
assert_true(storage_size == 4 + song_count * 128);
return XMPCreateTitlePlaylist(songs_ptr, song_count, playlist_name_ptr,
playlist_name, flags, song_handles_ptr,
storage_ptr);
}
case 0x0007000E: {
assert_true(!buffer_length || buffer_length == 12);
uint32_t xmp_client = xe::load_and_swap<uint32_t>(buffer + 0);
uint32_t unk_ptr = xe::load_and_swap<uint32_t>(buffer + 4); // 0
uint32_t info_ptr = xe::load_and_swap<uint32_t>(buffer + 8);
auto info = memory_->TranslateVirtual(info_ptr);
assert_true(xmp_client == 0x00000002);
assert_zero(unk_ptr);
XELOGE("XMPGetInfo?(%.8X, %.8X)", unk_ptr, info_ptr);
if (!active_playlist_) {
return X_STATUS_UNSUCCESSFUL;
}
auto& song = active_playlist_->songs[active_song_index_];
xe::store_and_swap<uint32_t>(info + 0, song->handle);
xe::store_and_swap<std::wstring>(info + 4 + 572 + 0, song->name);
xe::store_and_swap<std::wstring>(info + 4 + 572 + 40, song->artist);
xe::store_and_swap<std::wstring>(info + 4 + 572 + 80, song->album);
xe::store_and_swap<std::wstring>(info + 4 + 572 + 120,
song->album_artist);
xe::store_and_swap<std::wstring>(info + 4 + 572 + 160, song->genre);
xe::store_and_swap<uint32_t>(info + 4 + 572 + 200, song->track_number);
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;
}
case 0x00070013: {
assert_true(!buffer_length || buffer_length == 8);
uint32_t xmp_client = xe::load_and_swap<uint32_t>(buffer + 0);
uint32_t storage_ptr = xe::load_and_swap<uint32_t>(buffer + 4);
uint32_t playlist_handle =
xe::load_and_swap<uint32_t>(memory_->TranslateVirtual(storage_ptr));
assert_true(xmp_client == 0x00000002);
return XMPDeleteTitlePlaylist(playlist_handle);
}
case 0x0007001A: {
assert_true(!buffer_length || buffer_length == 12);
uint32_t xmp_client = xe::load_and_swap<uint32_t>(buffer + 0);
uint32_t unk1 = xe::load_and_swap<uint32_t>(buffer + 4);
uint32_t enabled = xe::load_and_swap<uint32_t>(buffer + 8);
assert_true(xmp_client == 0x00000002);
assert_zero(unk1);
XELOGD("XMPSetEnabled(%.8X, %.8X)", unk1, enabled);
disabled_ = enabled;
if (disabled_) {
XMPStop(0);
}
kernel_state_->BroadcastNotification(kMsgDisableChanged, disabled_);
return X_ERROR_SUCCESS;
}
case 0x0007001B: {
assert_true(!buffer_length || buffer_length == 12);
uint32_t xmp_client = xe::load_and_swap<uint32_t>(buffer + 0);
uint32_t unk_ptr =
xe::load_and_swap<uint32_t>(buffer + 4); // out ptr to 4b - expect 0
uint32_t disabled_ptr = xe::load_and_swap<uint32_t>(
buffer + 8); // out ptr to 4b - expect 1 (to skip)
assert_true(xmp_client == 0x00000002);
XELOGD("XMPGetEnabled(%.8X, %.8X)", unk_ptr, disabled_ptr);
xe::store_and_swap<uint32_t>(memory_->TranslateVirtual(unk_ptr), 0);
xe::store_and_swap<uint32_t>(memory_->TranslateVirtual(disabled_ptr),
disabled_);
// Atrain spawns a thread 82437FD0 to call this in a tight loop forever.
xe::threading::Sleep(std::chrono::milliseconds(10));
return X_ERROR_SUCCESS;
}
case 0x00070029: {
assert_true(!buffer_length || buffer_length == 16);
uint32_t xmp_client = xe::load_and_swap<uint32_t>(buffer + 0);
uint32_t playback_mode_ptr = xe::load_and_swap<uint32_t>(buffer + 4);
uint32_t repeat_mode_ptr = xe::load_and_swap<uint32_t>(buffer + 8);
uint32_t unk3_ptr = xe::load_and_swap<uint32_t>(buffer + 12);
assert_true(xmp_client == 0x00000002);
XELOGD("XMPGetPlaybackBehavior(%.8X, %.8X, %.8X)", playback_mode_ptr,
repeat_mode_ptr, unk3_ptr);
xe::store_and_swap<uint32_t>(memory_->TranslateVirtual(playback_mode_ptr),
static_cast<uint32_t>(playback_mode_));
xe::store_and_swap<uint32_t>(memory_->TranslateVirtual(repeat_mode_ptr),
static_cast<uint32_t>(repeat_mode_));
xe::store_and_swap<uint32_t>(memory_->TranslateVirtual(unk3_ptr),
unknown_flags_);
return X_ERROR_SUCCESS;
}
case 0x0007002E: {
assert_true(!buffer_length || buffer_length == 12);
// Query of size for XamAlloc - the result of the alloc is passed to
// 0x0007000D.
uint32_t xmp_client = xe::load_and_swap<uint32_t>(buffer + 0);
uint32_t song_count = xe::load_and_swap<uint32_t>(buffer + 4);
uint32_t size_ptr = xe::load_and_swap<uint32_t>(buffer + 8);
assert_true(xmp_client == 0x00000002);
// We don't use the storage, so just fudge the number.
xe::store_and_swap<uint32_t>(memory_->TranslateVirtual(size_ptr),
4 + song_count * 128);
return X_ERROR_SUCCESS;
}
case 0x0007003D: {
// XMPCaptureOutput - not sure how this works :/
XELOGD("XMPCaptureOutput(...)");
assert_always("XMP output not unimplemented");
return X_STATUS_UNSUCCESSFUL;
}
}
XELOGE("Unimplemented XMP message app=%.8X, msg=%.8X, arg1=%.8X, arg2=%.8X",
app_id(), message, buffer_ptr, buffer_length);
return X_STATUS_UNSUCCESSFUL;
}
} // namespace apps
} // namespace xam
} // namespace kernel
} // namespace xe

View File

@@ -0,0 +1,116 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_KERNEL_XAM_APPS_XMP_APP_H_
#define XENIA_KERNEL_XAM_APPS_XMP_APP_H_
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
#include "xenia/base/mutex.h"
#include "xenia/kernel/kernel_state.h"
#include "xenia/kernel/xam/app_manager.h"
namespace xe {
namespace kernel {
namespace xam {
namespace apps {
// Only source of docs for a lot of these functions:
// http://freestyledash.googlecode.com/svn-history/r1/trunk/Freestyle/Scenes/Media/Music/ScnMusic.cpp
class XmpApp : public App {
public:
enum class State : uint32_t {
kIdle = 0,
kPlaying = 1,
kPaused = 2,
};
enum class PlaybackMode : uint32_t {
// kInOrder = ?,
kUnknown = 0,
};
enum class RepeatMode : uint32_t {
// kNoRepeat = ?,
kUnknown = 0,
};
struct Song {
enum class Format : uint32_t {
kWma = 0,
kMp3 = 1,
};
uint32_t handle;
std::wstring file_path;
std::wstring name;
std::wstring artist;
std::wstring album;
std::wstring album_artist;
std::wstring genre;
uint32_t track_number;
uint32_t duration_ms;
Format format;
};
struct Playlist {
uint32_t handle;
std::wstring name;
uint32_t flags;
std::vector<std::unique_ptr<Song>> songs;
};
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,
std::wstring 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;
private:
static const uint32_t kMsgStateChanged = 0x0A000001;
static const uint32_t kMsgPlaybackBehaviorChanged = 0x0A000002;
static const uint32_t kMsgDisableChanged = 0x0A000003;
void OnStateChanged();
State state_;
uint32_t disabled_;
PlaybackMode playback_mode_;
RepeatMode repeat_mode_;
uint32_t unknown_flags_;
float volume_;
Playlist* active_playlist_;
int active_song_index_;
xe::global_critical_region global_critical_region_;
std::unordered_map<uint32_t, Playlist*> playlists_;
uint32_t next_playlist_handle_;
uint32_t next_song_handle_;
};
} // namespace apps
} // namespace xam
} // namespace kernel
} // namespace xe
#endif // XENIA_KERNEL_XAM_APPS_XMP_APP_H_

View File

@@ -0,0 +1,256 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/kernel/xam/content_manager.h"
#include <string>
#include "xenia/base/filesystem.h"
#include "xenia/base/string.h"
#include "xenia/kernel/kernel_state.h"
#include "xenia/kernel/xobject.h"
#include "xenia/vfs/devices/host_path_device.h"
namespace xe {
namespace kernel {
namespace xam {
static const wchar_t* kThumbnailFileName = L"__thumbnail.png";
static int content_device_id_ = 0;
ContentPackage::ContentPackage(KernelState* kernel_state, std::string root_name,
const XCONTENT_DATA& data,
std::wstring package_path)
: kernel_state_(kernel_state), root_name_(std::move(root_name)) {
device_path_ = std::string("\\Device\\Content\\") +
std::to_string(++content_device_id_) + "\\";
auto fs = kernel_state_->file_system();
auto device =
std::make_unique<vfs::HostPathDevice>(device_path_, package_path, false);
device->Initialize();
fs->RegisterDevice(std::move(device));
fs->RegisterSymbolicLink(root_name_ + ":", device_path_);
}
ContentPackage::~ContentPackage() {
kernel_state_->file_system()->UnregisterSymbolicLink(root_name_ + ":");
// TODO(benvanik): unregister device.
}
ContentManager::ContentManager(KernelState* kernel_state,
std::wstring root_path)
: kernel_state_(kernel_state), root_path_(std::move(root_path)) {}
ContentManager::~ContentManager() = default;
std::wstring ContentManager::ResolvePackageRoot(uint32_t content_type) {
wchar_t title_id[9] = L"00000000";
std::swprintf(title_id, 9, L"%.8X", kernel_state_->title_id());
std::wstring type_name;
switch (content_type) {
case 1:
// Save games.
type_name = L"00000001";
break;
case 2:
// DLC from the marketplace.
type_name = L"00000002";
break;
case 3:
// Publisher content?
type_name = L"00000003";
break;
case 0x000D0000:
// ???
type_name = L"000D0000";
break;
default:
assert_unhandled_case(data.content_type);
return nullptr;
}
// Package root path:
// content_root/title_id/type_name/
auto package_root =
xe::join_paths(root_path_, xe::join_paths(title_id, type_name));
return package_root + xe::kWPathSeparator;
}
std::wstring ContentManager::ResolvePackagePath(const XCONTENT_DATA& data) {
// Content path:
// content_root/title_id/type_name/data_file_name/
auto package_root = ResolvePackageRoot(data.content_type);
auto package_path =
xe::join_paths(package_root, xe::to_wstring(data.file_name));
package_path += xe::kPathSeparator;
return package_path;
}
std::vector<XCONTENT_DATA> ContentManager::ListContent(uint32_t device_id,
uint32_t content_type) {
std::vector<XCONTENT_DATA> result;
// Search path:
// content_root/title_id/type_name/*
auto package_root = ResolvePackageRoot(content_type);
auto file_infos = xe::filesystem::ListFiles(package_root);
for (const auto& file_info : file_infos) {
if (file_info.type != xe::filesystem::FileInfo::Type::kDirectory) {
// Directories only.
continue;
}
XCONTENT_DATA content_data;
content_data.device_id = device_id;
content_data.content_type = content_type;
content_data.display_name = file_info.name;
content_data.file_name = xe::to_string(file_info.name);
result.emplace_back(std::move(content_data));
}
return result;
}
std::unique_ptr<ContentPackage> ContentManager::ResolvePackage(
std::string root_name, const XCONTENT_DATA& data) {
auto package_path = ResolvePackagePath(data);
if (!xe::filesystem::PathExists(package_path)) {
return nullptr;
}
auto global_lock = global_critical_region_.Acquire();
auto package = std::make_unique<ContentPackage>(kernel_state_, root_name,
data, package_path);
return package;
}
bool ContentManager::ContentExists(const XCONTENT_DATA& data) {
auto path = ResolvePackagePath(data);
return xe::filesystem::PathExists(path);
}
X_RESULT ContentManager::CreateContent(std::string root_name,
const XCONTENT_DATA& data) {
auto global_lock = global_critical_region_.Acquire();
if (open_packages_.count(root_name)) {
// Already content open with this root name.
return X_ERROR_INVALID_NAME;
}
auto package_path = ResolvePackagePath(data);
if (xe::filesystem::PathExists(package_path)) {
// Exists, must not!
return X_ERROR_ALREADY_EXISTS;
}
if (!xe::filesystem::CreateFolder(package_path)) {
return X_ERROR_ACCESS_DENIED;
}
auto package = ResolvePackage(root_name, data);
assert_not_null(package);
open_packages_.insert({root_name, package.release()});
return X_ERROR_SUCCESS;
}
X_RESULT ContentManager::OpenContent(std::string root_name,
const XCONTENT_DATA& data) {
auto global_lock = global_critical_region_.Acquire();
if (open_packages_.count(root_name)) {
// Already content open with this root name.
return X_ERROR_INVALID_NAME;
}
auto package_path = ResolvePackagePath(data);
if (!xe::filesystem::PathExists(package_path)) {
// Does not exist, must be created.
return X_ERROR_FILE_NOT_FOUND;
}
// Open package.
auto package = ResolvePackage(root_name, data);
assert_not_null(package);
open_packages_.insert({root_name, package.release()});
return X_ERROR_SUCCESS;
}
X_RESULT ContentManager::CloseContent(std::string root_name) {
auto global_lock = global_critical_region_.Acquire();
auto it = open_packages_.find(root_name);
if (it == open_packages_.end()) {
return X_ERROR_FILE_NOT_FOUND;
}
auto package = it->second;
open_packages_.erase(it);
delete package;
return X_ERROR_SUCCESS;
}
X_RESULT ContentManager::GetContentThumbnail(const XCONTENT_DATA& data,
std::vector<uint8_t>* buffer) {
auto global_lock = global_critical_region_.Acquire();
auto package_path = ResolvePackagePath(data);
auto thumb_path = xe::join_paths(package_path, kThumbnailFileName);
if (xe::filesystem::PathExists(thumb_path)) {
auto file = xe::filesystem::OpenFile(thumb_path, "rb");
fseek(file, 0, SEEK_END);
size_t file_len = ftell(file);
fseek(file, 0, SEEK_SET);
buffer->resize(file_len);
fread(const_cast<uint8_t*>(buffer->data()), 1, buffer->size(), file);
fclose(file);
return X_ERROR_SUCCESS;
} else {
return X_ERROR_FILE_NOT_FOUND;
}
}
X_RESULT ContentManager::SetContentThumbnail(const XCONTENT_DATA& data,
std::vector<uint8_t> buffer) {
auto global_lock = global_critical_region_.Acquire();
auto package_path = ResolvePackagePath(data);
xe::filesystem::CreateFolder(package_path);
if (xe::filesystem::PathExists(package_path)) {
auto thumb_path = xe::join_paths(package_path, kThumbnailFileName);
auto file = xe::filesystem::OpenFile(thumb_path, "wb");
fwrite(buffer.data(), 1, buffer.size(), file);
fclose(file);
return X_ERROR_SUCCESS;
} else {
return X_ERROR_FILE_NOT_FOUND;
}
}
X_RESULT ContentManager::DeleteContent(const XCONTENT_DATA& data) {
auto global_lock = global_critical_region_.Acquire();
auto package_path = ResolvePackagePath(data);
if (xe::filesystem::PathExists(package_path)) {
xe::filesystem::DeleteFolder(package_path);
return X_ERROR_SUCCESS;
} else {
return X_ERROR_FILE_NOT_FOUND;
}
}
} // namespace xam
} // namespace kernel
} // namespace xe

View File

@@ -0,0 +1,104 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_KERNEL_XAM_CONTENT_MANAGER_H_
#define XENIA_KERNEL_XAM_CONTENT_MANAGER_H_
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
#include "xenia/base/memory.h"
#include "xenia/base/mutex.h"
#include "xenia/xbox.h"
namespace xe {
namespace kernel {
class KernelState;
} // namespace kernel
} // namespace xe
namespace xe {
namespace kernel {
namespace xam {
struct XCONTENT_DATA {
static const size_t kSize = 4 + 4 + 128 * 2 + 42 + 2; // = 306 + 2b padding
uint32_t device_id;
uint32_t content_type;
std::wstring display_name; // 128 chars
std::string file_name;
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::wstring>(ptr + 8);
file_name = xe::load_and_swap<std::string>(ptr + 8 + 128 * 2);
}
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::wstring>(ptr + 8, display_name);
xe::store_and_swap<std::string>(ptr + 8 + 128 * 2, file_name);
}
};
class ContentPackage {
public:
ContentPackage(KernelState* kernel_state, std::string root_name,
const XCONTENT_DATA& data, std::wstring package_path);
~ContentPackage();
private:
KernelState* kernel_state_;
std::string root_name_;
std::string device_path_;
};
class ContentManager {
public:
ContentManager(KernelState* kernel_state, std::wstring root_path);
~ContentManager();
std::vector<XCONTENT_DATA> ListContent(uint32_t device_id,
uint32_t content_type);
std::unique_ptr<ContentPackage> ResolvePackage(std::string root_name,
const XCONTENT_DATA& data);
bool ContentExists(const XCONTENT_DATA& data);
X_RESULT CreateContent(std::string root_name, const XCONTENT_DATA& data);
X_RESULT OpenContent(std::string root_name, const XCONTENT_DATA& data);
X_RESULT CloseContent(std::string root_name);
X_RESULT GetContentThumbnail(const XCONTENT_DATA& data,
std::vector<uint8_t>* buffer);
X_RESULT SetContentThumbnail(const XCONTENT_DATA& data,
std::vector<uint8_t> buffer);
X_RESULT DeleteContent(const XCONTENT_DATA& data);
private:
std::wstring ResolvePackageRoot(uint32_t content_type);
std::wstring ResolvePackagePath(const XCONTENT_DATA& data);
KernelState* kernel_state_;
std::wstring root_path_;
// TODO(benvanik): remove use of global lock, it's bad here!
xe::global_critical_region global_critical_region_;
std::unordered_map<std::string, ContentPackage*> open_packages_;
};
} // namespace xam
} // namespace kernel
} // namespace xe
#endif // XENIA_KERNEL_XAM_CONTENT_MANAGER_H_

View File

@@ -0,0 +1,103 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/kernel/xam/user_profile.h"
namespace xe {
namespace kernel {
namespace xam {
UserProfile::UserProfile() {
xuid_ = 0xBABEBABEBABEBABE;
name_ = "User";
// http://cs.rin.ru/forum/viewtopic.php?f=38&t=60668&hilit=gfwl+live&start=195
// https://github.com/arkem/py360/blob/master/py360/constants.py
// XPROFILE_GAMER_YAXIS_INVERSION
AddSetting(std::make_unique<Int32Setting>(0x10040002, 0));
// XPROFILE_OPTION_CONTROLLER_VIBRATION
AddSetting(std::make_unique<Int32Setting>(0x10040003, 3));
// XPROFILE_GAMERCARD_ZONE
AddSetting(std::make_unique<Int32Setting>(0x10040004, 0));
// XPROFILE_GAMERCARD_REGION
AddSetting(std::make_unique<Int32Setting>(0x10040005, 0));
// XPROFILE_GAMERCARD_CRED
AddSetting(std::make_unique<Int32Setting>(0x10040006, 0xFA));
// XPROFILE_GAMERCARD_REP
AddSetting(std::make_unique<FloatSetting>(0x5004000B, 0.0f));
// XPROFILE_OPTION_VOICE_MUTED
AddSetting(std::make_unique<Int32Setting>(0x1004000C, 0));
// XPROFILE_OPTION_VOICE_THRU_SPEAKERS
AddSetting(std::make_unique<Int32Setting>(0x1004000D, 0));
// XPROFILE_OPTION_VOICE_VOLUME
AddSetting(std::make_unique<Int32Setting>(0x1004000E, 0x64));
// XPROFILE_GAMERCARD_MOTTO
AddSetting(std::make_unique<UnicodeSetting>(0x402C0011, L""));
// XPROFILE_GAMERCARD_TITLES_PLAYED
AddSetting(std::make_unique<Int32Setting>(0x10040012, 1));
// XPROFILE_GAMERCARD_ACHIEVEMENTS_EARNED
AddSetting(std::make_unique<Int32Setting>(0x10040013, 0));
// XPROFILE_GAMER_DIFFICULTY
AddSetting(std::make_unique<Int32Setting>(0x10040015, 0));
// XPROFILE_GAMER_CONTROL_SENSITIVITY
AddSetting(std::make_unique<Int32Setting>(0x10040018, 0));
// Preferred color 1
AddSetting(std::make_unique<Int32Setting>(0x1004001D, 0xFFFF0000u));
// Preferred color 2
AddSetting(std::make_unique<Int32Setting>(0x1004001E, 0xFF00FF00u));
// XPROFILE_GAMER_ACTION_AUTO_AIM
AddSetting(std::make_unique<Int32Setting>(0x10040022, 1));
// XPROFILE_GAMER_ACTION_AUTO_CENTER
AddSetting(std::make_unique<Int32Setting>(0x10040023, 0));
// XPROFILE_GAMER_ACTION_MOVEMENT_CONTROL
AddSetting(std::make_unique<Int32Setting>(0x10040024, 0));
// XPROFILE_GAMER_RACE_TRANSMISSION
AddSetting(std::make_unique<Int32Setting>(0x10040026, 0));
// XPROFILE_GAMER_RACE_CAMERA_LOCATION
AddSetting(std::make_unique<Int32Setting>(0x10040027, 0));
// XPROFILE_GAMER_RACE_BRAKE_CONTROL
AddSetting(std::make_unique<Int32Setting>(0x10040028, 0));
// XPROFILE_GAMER_RACE_ACCELERATOR_CONTROL
AddSetting(std::make_unique<Int32Setting>(0x10040029, 0));
// XPROFILE_GAMERCARD_TITLE_CRED_EARNED
AddSetting(std::make_unique<Int32Setting>(0x10040038, 0));
// XPROFILE_GAMERCARD_TITLE_ACHIEVEMENTS_EARNED
AddSetting(std::make_unique<Int32Setting>(0x10040039, 0));
// If we set this, games will try to get it.
// XPROFILE_GAMERCARD_PICTURE_KEY
AddSetting(
std::make_unique<UnicodeSetting>(0x4064000F, L"gamercard_picture_key"));
std::vector<uint8_t> zeros;
zeros.resize(1000);
// XPROFILE_TITLE_SPECIFIC1
AddSetting(std::make_unique<BinarySetting>(0x63E83FFF, zeros));
// XPROFILE_TITLE_SPECIFIC2
AddSetting(std::make_unique<BinarySetting>(0x63E83FFE, zeros));
// XPROFILE_TITLE_SPECIFIC3
AddSetting(std::make_unique<BinarySetting>(0x63E83FFD, zeros));
}
void UserProfile::AddSetting(std::unique_ptr<Setting> setting) {
settings_.insert({setting->setting_id, setting.get()});
setting_list_.push_back(std::move(setting));
}
UserProfile::Setting* UserProfile::GetSetting(uint32_t setting_id) {
const auto& it = settings_.find(setting_id);
if (it == settings_.end()) {
return nullptr;
}
return it->second;
}
} // namespace xam
} // namespace kernel
} // namespace xe

View File

@@ -0,0 +1,201 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_KERNEL_XAM_USER_PROFILE_H_
#define XENIA_KERNEL_XAM_USER_PROFILE_H_
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
#include "xenia/xbox.h"
namespace xe {
namespace kernel {
namespace xam {
class UserProfile {
public:
struct Setting {
enum class Type {
UNKNOWN = 0,
INT32 = 1,
INT64 = 2,
DOUBLE = 3,
WSTRING = 4,
FLOAT = 5,
BINARY = 6,
DATETIME = 7,
INVALID = 0xFF,
};
union Key {
struct {
uint32_t id : 14;
uint32_t unk : 2;
uint32_t size : 12;
uint32_t type : 4;
};
uint32_t value;
};
uint32_t setting_id;
Type type;
size_t size;
Setting(uint32_t setting_id, Type type, size_t size)
: setting_id(setting_id), type(type), size(size) {}
virtual size_t extra_size() const { return 0; }
virtual size_t Append(uint8_t* user_data, uint8_t* buffer,
uint32_t buffer_ptr, size_t buffer_offset) {
xe::store_and_swap<uint8_t>(user_data + kTypeOffset,
static_cast<uint8_t>(type));
return buffer_offset;
}
bool is_title_specific() const { return (setting_id & 0x3F00) == 0x3F00; }
protected:
const size_t kTypeOffset = 0;
const size_t kValueOffset = 8;
const size_t kPointerOffset = 12;
};
struct Int32Setting : public Setting {
Int32Setting(uint32_t setting_id, int32_t value)
: Setting(setting_id, Type::INT32, 4), value(value) {}
int32_t value;
size_t Append(uint8_t* user_data, uint8_t* buffer, uint32_t buffer_ptr,
size_t buffer_offset) override {
buffer_offset =
Setting::Append(user_data, buffer, buffer_ptr, buffer_offset);
xe::store_and_swap<int32_t>(user_data + kValueOffset, value);
return buffer_offset;
}
};
struct Int64Setting : public Setting {
Int64Setting(uint32_t setting_id, int64_t value)
: Setting(setting_id, Type::INT64, 8), value(value) {}
int64_t value;
size_t Append(uint8_t* user_data, uint8_t* buffer, uint32_t buffer_ptr,
size_t buffer_offset) override {
buffer_offset =
Setting::Append(user_data, buffer, buffer_ptr, buffer_offset);
xe::store_and_swap<int64_t>(user_data + kValueOffset, value);
return buffer_offset;
}
};
struct DoubleSetting : public Setting {
DoubleSetting(uint32_t setting_id, double value)
: Setting(setting_id, Type::DOUBLE, 8), value(value) {}
double value;
size_t Append(uint8_t* user_data, uint8_t* buffer, uint32_t buffer_ptr,
size_t buffer_offset) override {
buffer_offset =
Setting::Append(user_data, buffer, buffer_ptr, buffer_offset);
xe::store_and_swap<double>(user_data + kValueOffset, value);
return buffer_offset;
}
};
struct UnicodeSetting : public Setting {
UnicodeSetting(uint32_t setting_id, const std::wstring& value)
: Setting(setting_id, Type::WSTRING, 8), value(value) {}
std::wstring value;
size_t extra_size() const override {
return value.empty() ? 0 : 2 * (static_cast<int32_t>(value.size()) + 1);
}
size_t Append(uint8_t* user_data, uint8_t* buffer, uint32_t buffer_ptr,
size_t buffer_offset) override {
buffer_offset =
Setting::Append(user_data, buffer, buffer_ptr, buffer_offset);
int32_t length;
if (value.empty()) {
length = 0;
xe::store_and_swap<int32_t>(user_data + kValueOffset, 0);
xe::store_and_swap<uint32_t>(user_data + kPointerOffset, 0);
} else {
length = 2 * (static_cast<int32_t>(value.size()) + 1);
xe::store_and_swap<int32_t>(user_data + kValueOffset, length);
xe::store_and_swap<uint32_t>(
user_data + kPointerOffset,
buffer_ptr + static_cast<uint32_t>(buffer_offset));
memcpy(buffer + buffer_offset, value.data(), length);
}
return buffer_offset + length;
}
};
struct FloatSetting : public Setting {
FloatSetting(uint32_t setting_id, float value)
: Setting(setting_id, Type::FLOAT, 4), value(value) {}
float value;
size_t Append(uint8_t* user_data, uint8_t* buffer, uint32_t buffer_ptr,
size_t buffer_offset) override {
buffer_offset =
Setting::Append(user_data, buffer, buffer_ptr, buffer_offset);
xe::store_and_swap<float>(user_data + kValueOffset, value);
return buffer_offset;
}
};
struct BinarySetting : public Setting {
BinarySetting(uint32_t setting_id, const std::vector<uint8_t>& value)
: Setting(setting_id, Type::BINARY, 8), value(value) {}
std::vector<uint8_t> value;
size_t extra_size() const override {
return static_cast<int32_t>(value.size());
}
size_t Append(uint8_t* user_data, uint8_t* buffer, uint32_t buffer_ptr,
size_t buffer_offset) override {
buffer_offset =
Setting::Append(user_data, buffer, buffer_ptr, buffer_offset);
int32_t length;
if (value.empty()) {
length = 0;
xe::store_and_swap<int32_t>(user_data + kValueOffset, 0);
xe::store_and_swap<int32_t>(user_data + kPointerOffset, 0);
} else {
length = static_cast<int32_t>(value.size());
xe::store_and_swap<int32_t>(user_data + kValueOffset, length);
xe::store_and_swap<uint32_t>(
user_data + kPointerOffset,
buffer_ptr + static_cast<uint32_t>(buffer_offset));
memcpy(buffer + buffer_offset, value.data(), length);
}
return buffer_offset + length;
}
};
struct DateTimeSetting : public Setting {
DateTimeSetting(uint32_t setting_id, int64_t value)
: Setting(setting_id, Type::DATETIME, 8), value(value) {}
int64_t value;
size_t Append(uint8_t* user_data, uint8_t* buffer, uint32_t buffer_ptr,
size_t buffer_offset) override {
buffer_offset =
Setting::Append(user_data, buffer, buffer_ptr, buffer_offset);
xe::store_and_swap<int64_t>(user_data + kValueOffset, value);
return buffer_offset;
}
};
UserProfile();
uint64_t xuid() const { return xuid_; }
std::string name() const { return name_; }
uint32_t signin_state() const { return 1; }
void AddSetting(std::unique_ptr<Setting> setting);
Setting* GetSetting(uint32_t setting_id);
private:
uint64_t xuid_;
std::string name_;
std::vector<std::unique_ptr<Setting>> setting_list_;
std::unordered_map<uint32_t, Setting*> settings_;
};
} // namespace xam
} // namespace kernel
} // namespace xe
#endif // XENIA_KERNEL_XAM_USER_PROFILE_H_

View File

@@ -0,0 +1,43 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2015 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/kernel/kernel_state.h"
#include "xenia/kernel/util/shim_utils.h"
#include "xenia/kernel/xam/xam_private.h"
#include "xenia/xbox.h"
namespace xe {
namespace kernel {
namespace xam {
dword_result_t XamAvatarInitialize(
dword_t unk1, // 1, 4, etc
dword_t unk2, // 0 or 1
dword_t processor_number, // for thread creation?
lpdword_t function_ptrs, // 20b, 5 pointers
lpunknown_t unk5, // ptr in data segment
dword_t unk6 // flags - 0x00300000, 0x30, etc
) {
// Negative to fail. Game should immediately call XamAvatarShutdown.
return ~0u;
}
DECLARE_XAM_EXPORT(XamAvatarInitialize, ExportTag::kStub);
void XamAvatarShutdown() {
// No-op.
}
DECLARE_XAM_EXPORT(XamAvatarShutdown, ExportTag::kStub);
void RegisterAvatarExports(xe::cpu::ExportResolver* export_resolver,
KernelState* kernel_state) {}
} // namespace xam
} // namespace kernel
} // namespace xe

View File

@@ -0,0 +1,524 @@
/**
******************************************************************************
* 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/base/logging.h"
#include "xenia/kernel/kernel_state.h"
#include "xenia/kernel/util/shim_utils.h"
#include "xenia/kernel/xam/xam_private.h"
#include "xenia/kernel/xenumerator.h"
#include "xenia/xbox.h"
namespace xe {
namespace kernel {
namespace xam {
struct DeviceInfo {
uint32_t device_id;
uint32_t device_type;
uint64_t total_bytes;
uint64_t free_bytes;
std::wstring name;
};
static const DeviceInfo dummy_device_info_ = {
0xF00D0000,
1,
120ull * 1024ull * 1024ull * 1024ull, // 120GB
100ull * 1024ull * 1024ull * 1024ull, // 100GB, so it looks a little used.
L"Dummy HDD",
};
SHIM_CALL XamContentGetLicenseMask_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t mask_ptr = SHIM_GET_ARG_32(0);
uint32_t overlapped_ptr = SHIM_GET_ARG_32(1);
XELOGD("XamContentGetLicenseMask(%.8X, %.8X)", mask_ptr, overlapped_ptr);
// Each bit in the mask represents a granted license. Available licenses
// seems to vary from game to game, but most appear to use bit 0 to indicate
// if the game is purchased or not.
SHIM_SET_MEM_32(mask_ptr, 0);
if (overlapped_ptr) {
kernel_state->CompleteOverlappedImmediate(overlapped_ptr, X_ERROR_SUCCESS);
SHIM_SET_RETURN_32(X_ERROR_IO_PENDING);
} else {
SHIM_SET_RETURN_32(X_ERROR_SUCCESS);
}
}
SHIM_CALL XamContentGetDeviceName_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t device_id = SHIM_GET_ARG_32(0);
uint32_t name_ptr = SHIM_GET_ARG_32(1);
uint32_t name_capacity = SHIM_GET_ARG_32(2);
XELOGD("XamContentGetDeviceName(%.8X, %.8X, %d)", device_id, name_ptr,
name_capacity);
if ((device_id & 0xFFFF0000) != dummy_device_info_.device_id) {
SHIM_SET_RETURN_32(X_ERROR_DEVICE_NOT_CONNECTED);
return;
}
if (name_capacity < dummy_device_info_.name.size() + 1) {
SHIM_SET_RETURN_32(X_ERROR_INSUFFICIENT_BUFFER);
return;
}
xe::store_and_swap<std::wstring>(SHIM_MEM_ADDR(name_ptr),
dummy_device_info_.name);
SHIM_SET_RETURN_32(X_ERROR_SUCCESS);
}
SHIM_CALL XamContentGetDeviceState_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t device_id = SHIM_GET_ARG_32(0);
uint32_t overlapped_ptr = SHIM_GET_ARG_32(1);
XELOGD("XamContentGetDeviceState(%.8X, %.8X)", device_id, overlapped_ptr);
if ((device_id & 0xFFFF0000) != dummy_device_info_.device_id) {
if (overlapped_ptr) {
kernel_state->CompleteOverlappedImmediateEx(
overlapped_ptr, X_ERROR_FUNCTION_FAILED, X_ERROR_DEVICE_NOT_CONNECTED,
0);
SHIM_SET_RETURN_32(X_ERROR_IO_PENDING);
} else {
SHIM_SET_RETURN_32(X_ERROR_DEVICE_NOT_CONNECTED);
}
return;
}
if (overlapped_ptr) {
kernel_state->CompleteOverlappedImmediate(overlapped_ptr, X_ERROR_SUCCESS);
SHIM_SET_RETURN_32(X_ERROR_IO_PENDING);
} else {
SHIM_SET_RETURN_32(X_ERROR_SUCCESS);
}
}
SHIM_CALL XamContentGetDeviceData_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t device_id = SHIM_GET_ARG_32(0);
uint32_t device_data_ptr = SHIM_GET_ARG_32(1);
XELOGD("XamContentGetDeviceData(%.8X, %.8X)", device_id, device_data_ptr);
if ((device_id & 0xFFFF0000) != dummy_device_info_.device_id) {
// TODO(benvanik): memset 0 the data?
SHIM_SET_RETURN_32(X_ERROR_DEVICE_NOT_CONNECTED);
return;
}
const auto& device_info = dummy_device_info_;
SHIM_SET_MEM_32(device_data_ptr + 0, device_info.device_id);
SHIM_SET_MEM_32(device_data_ptr + 4, device_id & 0xFFFF); // Fake it.
SHIM_SET_MEM_64(device_data_ptr + 8, device_info.total_bytes);
SHIM_SET_MEM_64(device_data_ptr + 16, device_info.free_bytes);
xe::store_and_swap<std::wstring>(SHIM_MEM_ADDR(device_data_ptr + 24),
device_info.name);
SHIM_SET_RETURN_32(X_ERROR_SUCCESS);
}
SHIM_CALL XamContentResolve_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t user_index = SHIM_GET_ARG_32(0);
uint32_t content_data_ptr = SHIM_GET_ARG_32(1);
uint32_t buffer_ptr = SHIM_GET_ARG_32(2);
uint32_t buffer_size = SHIM_GET_ARG_32(3);
uint32_t unk1 = SHIM_GET_ARG_32(4); // 1
uint32_t unk2 = SHIM_GET_ARG_32(5); // 0
uint32_t unk3 = SHIM_GET_ARG_32(6); // 0
auto content_data = XCONTENT_DATA(SHIM_MEM_ADDR(content_data_ptr));
XELOGD("XamContentResolve(%d, %.8X, %.8X, %d, %.8X, %.8X, %.8X)", user_index,
content_data_ptr, buffer_ptr, buffer_size, unk1, unk2, unk3);
// Result of buffer_ptr is sent to RtlInitAnsiString.
// buffer_size is usually 260 (max path).
// Games expect zero if resolve was successful.
assert_always();
XELOGW("XamContentResolve unimplemented!");
SHIM_SET_RETURN_32(X_ERROR_NOT_FOUND);
}
// http://gameservice.googlecode.com/svn-history/r14/trunk/ContentManager.cpp
// https://github.com/LestaD/SourceEngine2007/blob/master/se2007/engine/xboxsystem.cpp#L499
dword_result_t XamContentCreateEnumerator(dword_t user_index, dword_t device_id,
dword_t content_type,
dword_t content_flags,
dword_t max_count,
lpdword_t buffer_size_ptr,
lpdword_t handle_out) {
assert_not_null(handle_out);
if ((device_id && (device_id & 0xFFFF0000) != dummy_device_info_.device_id) ||
!handle_out) {
if (buffer_size_ptr) {
*buffer_size_ptr = 0;
}
// TODO(benvanik): memset 0 the data?
return X_E_INVALIDARG;
}
if (buffer_size_ptr) {
*buffer_size_ptr = (uint32_t)XCONTENT_DATA::kSize;
}
auto e =
new XStaticEnumerator(kernel_state(), max_count, XCONTENT_DATA::kSize);
e->Initialize();
// Get all content data.
auto content_datas = kernel_state()->content_manager()->ListContent(
device_id ? device_id : dummy_device_info_.device_id, content_type);
for (auto& content_data : content_datas) {
auto ptr = e->AppendItem();
if (!ptr) {
// Too many items.
break;
}
content_data.Write(ptr);
}
*handle_out = e->handle();
return X_ERROR_SUCCESS;
}
DECLARE_XAM_EXPORT(XamContentCreateEnumerator, ExportTag::kImplemented);
void XamContentCreateCore(PPCContext* ppc_context, KernelState* kernel_state,
uint32_t user_index, std::string root_name,
XCONTENT_DATA content_data, uint32_t flags,
uint32_t disposition_ptr, uint32_t license_mask_ptr,
uint32_t cache_size, uint64_t content_size,
uint32_t overlapped_ptr) {
assert_zero(license_mask_ptr);
X_RESULT result = X_ERROR_INVALID_PARAMETER;
auto content_manager = kernel_state->content_manager();
bool create = false;
bool open = false;
switch (flags & 0xF) {
case 1: // CREATE_NEW
// Fail if exists.
if (content_manager->ContentExists(content_data)) {
result = X_ERROR_ALREADY_EXISTS;
} else {
create = true;
}
break;
case 2: // CREATE_ALWAYS
// Overwrite existing, if any.
if (content_manager->ContentExists(content_data)) {
content_manager->DeleteContent(content_data);
create = true;
} else {
create = true;
}
break;
case 3: // OPEN_EXISTING
// Open only if exists.
if (!content_manager->ContentExists(content_data)) {
result = X_ERROR_PATH_NOT_FOUND;
} else {
open = true;
}
break;
case 4: // OPEN_ALWAYS
// Create if needed.
if (!content_manager->ContentExists(content_data)) {
create = true;
} else {
open = true;
}
break;
case 5: // TRUNCATE_EXISTING
// Fail if doesn't exist, if does exist delete and recreate.
if (!content_manager->ContentExists(content_data)) {
result = X_ERROR_PATH_NOT_FOUND;
} else {
content_manager->DeleteContent(content_data);
create = true;
}
break;
default:
assert_unhandled_case(flags & 0xF);
break;
}
uint32_t disposition = create ? 1 : 2;
if (disposition_ptr) {
if (overlapped_ptr) {
// If async always set to zero, but don't set to a real value.
SHIM_SET_MEM_32(disposition_ptr, 0);
} else {
SHIM_SET_MEM_32(disposition_ptr, disposition);
}
}
if (create) {
result = content_manager->CreateContent(root_name, content_data);
} else if (open) {
result = content_manager->OpenContent(root_name, content_data);
}
if (overlapped_ptr) {
kernel_state->CompleteOverlappedImmediateEx(overlapped_ptr, result,
disposition, 0);
SHIM_SET_RETURN_32(X_ERROR_IO_PENDING);
} else {
SHIM_SET_RETURN_32(result);
}
}
SHIM_CALL XamContentCreate_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t user_index = SHIM_GET_ARG_32(0);
uint32_t root_name_ptr = SHIM_GET_ARG_32(1);
uint32_t content_data_ptr = SHIM_GET_ARG_32(2);
uint32_t flags = SHIM_GET_ARG_32(3);
uint32_t disposition_ptr = SHIM_GET_ARG_32(4);
uint32_t license_mask_ptr = SHIM_GET_ARG_32(5);
uint32_t overlapped_ptr = SHIM_GET_ARG_32(6);
auto root_name = xe::load_and_swap<std::string>(SHIM_MEM_ADDR(root_name_ptr));
auto content_data = XCONTENT_DATA(SHIM_MEM_ADDR(content_data_ptr));
XELOGD("XamContentCreate(%d, %.8X(%s), %.8X, %.8X, %.8X, %.8X, %.8X)",
user_index, root_name_ptr, root_name.c_str(), content_data_ptr, flags,
disposition_ptr, license_mask_ptr, overlapped_ptr);
XamContentCreateCore(ppc_context, kernel_state, user_index, root_name,
content_data, flags, disposition_ptr, license_mask_ptr,
0, 0, overlapped_ptr);
}
SHIM_CALL XamContentCreateEx_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t user_index = SHIM_GET_ARG_32(0);
uint32_t root_name_ptr = SHIM_GET_ARG_32(1);
uint32_t content_data_ptr = SHIM_GET_ARG_32(2);
uint32_t flags = SHIM_GET_ARG_32(3);
uint32_t disposition_ptr = SHIM_GET_ARG_32(4);
uint32_t license_mask_ptr = SHIM_GET_ARG_32(5);
uint32_t cache_size = SHIM_GET_ARG_32(6);
uint64_t content_size = SHIM_GET_ARG_64(7);
uint32_t overlapped_ptr = SHIM_GET_ARG_32(8);
auto root_name = xe::load_and_swap<std::string>(SHIM_MEM_ADDR(root_name_ptr));
auto content_data = XCONTENT_DATA(SHIM_MEM_ADDR(content_data_ptr));
XELOGD(
"XamContentCreateEx(%d, %.8X(%s), %.8X, %.8X, %.8X, %.8X, %.8X, %.8llX, "
"%.8X)",
user_index, root_name_ptr, root_name.c_str(), content_data_ptr, flags,
disposition_ptr, license_mask_ptr, cache_size, content_size,
overlapped_ptr);
XamContentCreateCore(ppc_context, kernel_state, user_index, root_name,
content_data, flags, disposition_ptr, license_mask_ptr,
cache_size, content_size, overlapped_ptr);
}
SHIM_CALL XamContentFlush_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t root_name_ptr = SHIM_GET_ARG_32(0);
uint32_t overlapped_ptr = SHIM_GET_ARG_32(1);
auto root_name = xe::load_and_swap<std::string>(SHIM_MEM_ADDR(root_name_ptr));
XELOGD("XamContentFlush(%.8X(%s), %.8X)", root_name_ptr, root_name.c_str(),
overlapped_ptr);
X_RESULT result = X_ERROR_SUCCESS;
if (overlapped_ptr) {
kernel_state->CompleteOverlappedImmediate(overlapped_ptr, result);
SHIM_SET_RETURN_32(X_ERROR_IO_PENDING);
} else {
SHIM_SET_RETURN_32(result);
}
}
SHIM_CALL XamContentClose_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t root_name_ptr = SHIM_GET_ARG_32(0);
uint32_t overlapped_ptr = SHIM_GET_ARG_32(1);
auto root_name = xe::load_and_swap<std::string>(SHIM_MEM_ADDR(root_name_ptr));
XELOGD("XamContentClose(%.8X(%s), %.8X)", root_name_ptr, root_name.c_str(),
overlapped_ptr);
// Closes a previously opened root from XamContentCreate*.
auto result = kernel_state->content_manager()->CloseContent(root_name);
if (overlapped_ptr) {
kernel_state->CompleteOverlappedImmediate(overlapped_ptr, result);
SHIM_SET_RETURN_32(X_ERROR_IO_PENDING);
} else {
SHIM_SET_RETURN_32(result);
}
}
SHIM_CALL XamContentGetCreator_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t user_index = SHIM_GET_ARG_32(0);
uint32_t content_data_ptr = SHIM_GET_ARG_32(1);
uint32_t is_creator_ptr = SHIM_GET_ARG_32(2);
uint32_t creator_xuid_ptr = SHIM_GET_ARG_32(3);
uint32_t overlapped_ptr = SHIM_GET_ARG_32(4);
auto content_data = XCONTENT_DATA(SHIM_MEM_ADDR(content_data_ptr));
XELOGD("XamContentGetCreator(%d, %.8X, %.8X, %.8X, %.8X)", user_index,
content_data_ptr, is_creator_ptr, creator_xuid_ptr, overlapped_ptr);
auto result = X_ERROR_SUCCESS;
if (content_data.content_type == 1) {
// User always creates saves.
SHIM_SET_MEM_32(is_creator_ptr, 1);
if (creator_xuid_ptr) {
SHIM_SET_MEM_64(creator_xuid_ptr, kernel_state->user_profile()->xuid());
}
} else {
SHIM_SET_MEM_32(is_creator_ptr, 0);
if (creator_xuid_ptr) {
SHIM_SET_MEM_64(creator_xuid_ptr, 0);
}
}
if (overlapped_ptr) {
kernel_state->CompleteOverlappedImmediate(overlapped_ptr, result);
SHIM_SET_RETURN_32(X_ERROR_IO_PENDING);
} else {
SHIM_SET_RETURN_32(result);
}
}
SHIM_CALL XamContentGetThumbnail_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t user_index = SHIM_GET_ARG_32(0);
uint32_t content_data_ptr = SHIM_GET_ARG_32(1);
uint32_t buffer_ptr = SHIM_GET_ARG_32(2);
uint32_t buffer_size_ptr = SHIM_GET_ARG_32(3);
uint32_t overlapped_ptr = SHIM_GET_ARG_32(4);
assert_not_zero(buffer_size_ptr);
uint32_t buffer_size = SHIM_MEM_32(buffer_size_ptr);
auto content_data = XCONTENT_DATA(SHIM_MEM_ADDR(content_data_ptr));
XELOGD("XamContentGetThumbnail(%d, %.8X, %.8X, %.8X(%d), %.8X)", user_index,
content_data_ptr, buffer_ptr, buffer_size_ptr, buffer_size,
overlapped_ptr);
// Get thumbnail (if it exists).
std::vector<uint8_t> buffer;
auto result = kernel_state->content_manager()->GetContentThumbnail(
content_data, &buffer);
SHIM_SET_MEM_32(buffer_size_ptr, uint32_t(buffer.size()));
if (XSUCCEEDED(result)) {
// Write data, if we were given a pointer.
// This may have just been a size query.
if (buffer_ptr) {
if (buffer_size < buffer.size()) {
// Dest buffer too small.
result = X_ERROR_INSUFFICIENT_BUFFER;
} else {
// Copy data.
std::memcpy(SHIM_MEM_ADDR(buffer_ptr), buffer.data(), buffer.size());
}
}
}
if (overlapped_ptr) {
kernel_state->CompleteOverlappedImmediate(overlapped_ptr, result);
SHIM_SET_RETURN_32(X_ERROR_IO_PENDING);
} else {
SHIM_SET_RETURN_32(result);
}
}
SHIM_CALL XamContentSetThumbnail_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t user_index = SHIM_GET_ARG_32(0);
uint32_t content_data_ptr = SHIM_GET_ARG_32(1);
uint32_t buffer_ptr = SHIM_GET_ARG_32(2);
uint32_t buffer_size = SHIM_GET_ARG_32(3);
uint32_t overlapped_ptr = SHIM_GET_ARG_32(4);
auto content_data = XCONTENT_DATA(SHIM_MEM_ADDR(content_data_ptr));
XELOGD("XamContentSetThumbnail(%d, %.8X, %.8X, %d, %.8X)", user_index,
content_data_ptr, buffer_ptr, buffer_size, overlapped_ptr);
// Buffer is PNG data.
auto buffer = std::vector<uint8_t>(SHIM_MEM_ADDR(buffer_ptr),
SHIM_MEM_ADDR(buffer_ptr) + buffer_size);
auto result = kernel_state->content_manager()->SetContentThumbnail(
content_data, std::move(buffer));
if (overlapped_ptr) {
kernel_state->CompleteOverlappedImmediate(overlapped_ptr, result);
SHIM_SET_RETURN_32(X_ERROR_IO_PENDING);
} else {
SHIM_SET_RETURN_32(result);
}
}
SHIM_CALL XamContentDelete_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t user_index = SHIM_GET_ARG_32(0);
uint32_t content_data_ptr = SHIM_GET_ARG_32(1);
uint32_t overlapped_ptr = SHIM_GET_ARG_32(2);
auto content_data = XCONTENT_DATA(SHIM_MEM_ADDR(content_data_ptr));
XELOGD("XamContentDelete(%d, %.8X, %.8X)", user_index, content_data_ptr,
overlapped_ptr);
auto result = kernel_state->content_manager()->DeleteContent(content_data);
if (overlapped_ptr) {
kernel_state->CompleteOverlappedImmediate(overlapped_ptr, result);
SHIM_SET_RETURN_32(X_ERROR_IO_PENDING);
} else {
SHIM_SET_RETURN_32(result);
}
}
void RegisterContentExports(xe::cpu::ExportResolver* export_resolver,
KernelState* kernel_state) {
SHIM_SET_MAPPING("xam.xex", XamContentGetLicenseMask, state);
SHIM_SET_MAPPING("xam.xex", XamContentGetDeviceName, state);
SHIM_SET_MAPPING("xam.xex", XamContentGetDeviceState, state);
SHIM_SET_MAPPING("xam.xex", XamContentGetDeviceData, state);
SHIM_SET_MAPPING("xam.xex", XamContentResolve, state);
SHIM_SET_MAPPING("xam.xex", XamContentCreate, state);
SHIM_SET_MAPPING("xam.xex", XamContentCreateEx, state);
SHIM_SET_MAPPING("xam.xex", XamContentFlush, state);
SHIM_SET_MAPPING("xam.xex", XamContentClose, state);
SHIM_SET_MAPPING("xam.xex", XamContentGetCreator, state);
SHIM_SET_MAPPING("xam.xex", XamContentGetThumbnail, state);
SHIM_SET_MAPPING("xam.xex", XamContentSetThumbnail, state);
SHIM_SET_MAPPING("xam.xex", XamContentDelete, state);
}
} // namespace xam
} // namespace kernel
} // namespace xe

View File

@@ -0,0 +1,259 @@
/**
******************************************************************************
* 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/base/logging.h"
#include "xenia/kernel/kernel_state.h"
#include "xenia/kernel/user_module.h"
#include "xenia/kernel/util/shim_utils.h"
#include "xenia/kernel/util/xex2.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"
namespace xe {
namespace kernel {
namespace xam {
constexpr uint32_t X_LANGUAGE_ENGLISH = 1;
constexpr uint32_t X_LANGUAGE_JAPANESE = 2;
SHIM_CALL XamGetSystemVersion_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
XELOGD("XamGetSystemVersion()");
// eh, just picking one. If we go too low we may break new games, but
// this value seems to be used for conditionally loading symbols and if
// we pretend to be old we have less to worry with implementing.
// 0x200A3200
// 0x20096B00
SHIM_SET_RETURN_32(0);
}
SHIM_CALL XGetAVPack_shim(PPCContext* ppc_context, KernelState* kernel_state) {
// DWORD
// Not sure what the values are for this, but 6 is VGA.
// Other likely values are 3/4/8 for HDMI or something.
// Games seem to use this as a PAL check - if the result is not 3/4/6/8
// they explode with errors if not in PAL mode.
SHIM_SET_RETURN_32(6);
}
SHIM_CALL XGetGameRegion_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
XELOGD("XGetGameRegion()");
SHIM_SET_RETURN_32(0xFFFF);
}
SHIM_CALL XGetLanguage_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
XELOGD("XGetLanguage()");
uint32_t desired_language = X_LANGUAGE_ENGLISH;
// Switch the language based on game region.
// TODO(benvanik): pull from xex header.
uint32_t game_region = XEX_REGION_NTSCU;
if (game_region & XEX_REGION_NTSCU) {
desired_language = X_LANGUAGE_ENGLISH;
} else if (game_region & XEX_REGION_NTSCJ) {
desired_language = X_LANGUAGE_JAPANESE;
}
// Add more overrides?
SHIM_SET_RETURN_32(desired_language);
}
SHIM_CALL XamGetExecutionId_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t info_ptr = SHIM_GET_ARG_32(0);
XELOGD("XamGetExecutionId(%.8X)", info_ptr);
auto module = kernel_state->GetExecutableModule();
assert_not_null(module);
uint32_t guest_hdr_ptr;
X_STATUS result =
module->GetOptHeader(XEX_HEADER_EXECUTION_INFO, &guest_hdr_ptr);
if (XFAILED(result)) {
SHIM_SET_RETURN_32(result);
return;
}
SHIM_SET_MEM_32(info_ptr, guest_hdr_ptr);
SHIM_SET_RETURN_32(X_STATUS_SUCCESS);
}
dword_result_t XamLoaderSetLaunchData(lpvoid_t data, dword_t size) {
auto xam = kernel_state()->GetKernelModule<XamModule>("xam.xex");
auto& loader_data = xam->loader_data();
if (loader_data.launch_data_ptr) {
kernel_memory()->SystemHeapFree(loader_data.launch_data_ptr);
}
loader_data.launch_data_ptr = kernel_memory()->SystemHeapAlloc(size);
loader_data.launch_data_size = size;
std::memcpy(kernel_memory()->TranslateVirtual(loader_data.launch_data_ptr),
data, size);
// FIXME: Unknown return value.
return 0;
}
DECLARE_XAM_EXPORT(XamLoaderSetLaunchData, ExportTag::kSketchy);
dword_result_t XamLoaderGetLaunchDataSize(lpdword_t size_ptr) {
auto xam = kernel_state()->GetKernelModule<XamModule>("xam.xex");
*size_ptr = xam->loader_data().launch_data_size;
return 0;
}
DECLARE_XAM_EXPORT(XamLoaderGetLaunchDataSize, ExportTag::kSketchy);
dword_result_t XamLoaderGetLaunchData(lpvoid_t buffer_ptr,
dword_t buffer_size) {
auto xam = kernel_state()->GetKernelModule<XamModule>("xam.xex");
auto& loader_data = xam->loader_data();
if (loader_data.launch_data_ptr) {
uint8_t* loader_buffer_ptr =
kernel_memory()->TranslateVirtual(loader_data.launch_data_ptr);
uint32_t copy_size =
std::min(loader_data.launch_data_size, (uint32_t)buffer_size);
std::memcpy(buffer_ptr, loader_buffer_ptr, copy_size);
}
return 0;
}
DECLARE_XAM_EXPORT(XamLoaderGetLaunchData, ExportTag::kSketchy);
void XamLoaderLaunchTitle(lpstring_t raw_name, dword_t flags) {
auto xam = kernel_state()->GetKernelModule<XamModule>("xam.xex");
auto& loader_data = xam->loader_data();
loader_data.launch_flags = flags;
// Translate the launch path to a full path.
if (raw_name) {
std::string name = xe::find_name_from_path(std::string(raw_name));
std::string path(raw_name);
if (name == std::string(raw_name)) {
path = xe::join_paths(
xe::find_base_path(kernel_state()->GetExecutableModule()->path()),
name);
}
loader_data.launch_path = path;
} else {
assert_always("Game requested exit to dashboard via XamLoaderLaunchTitle");
}
// This function does not return.
kernel_state()->TerminateTitle(true);
}
DECLARE_XAM_EXPORT(XamLoaderLaunchTitle, ExportTag::kSketchy);
void XamLoaderTerminateTitle() {
// This function does not return.
kernel_state()->TerminateTitle(true);
}
DECLARE_XAM_EXPORT(XamLoaderTerminateTitle, ExportTag::kSketchy);
SHIM_CALL XamAlloc_shim(PPCContext* ppc_context, KernelState* kernel_state) {
uint32_t unk = SHIM_GET_ARG_32(0);
uint32_t size = SHIM_GET_ARG_32(1);
uint32_t out_ptr = SHIM_GET_ARG_32(2);
XELOGD("XamAlloc(%d, %d, %.8X)", unk, size, out_ptr);
assert_true(unk == 0);
// Allocate from the heap. Not sure why XAM does this specially, perhaps
// it keeps stuff in a separate heap?
uint32_t ptr = kernel_state->memory()->SystemHeapAlloc(size);
SHIM_SET_MEM_32(out_ptr, ptr);
SHIM_SET_RETURN_32(X_ERROR_SUCCESS);
}
SHIM_CALL XamFree_shim(PPCContext* ppc_context, KernelState* kernel_state) {
uint32_t ptr = SHIM_GET_ARG_32(0);
XELOGD("XamFree(%.8X)", ptr);
kernel_state->memory()->SystemHeapFree(ptr);
SHIM_SET_RETURN_32(X_ERROR_SUCCESS);
}
// 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) {
assert_true(flags == 0);
auto e = kernel_state()->object_table()->LookupObject<XEnumerator>(handle);
if (!e) {
if (overlapped) {
kernel_state()->CompleteOverlappedImmediateEx(
overlapped, X_ERROR_INVALID_HANDLE, X_ERROR_INVALID_HANDLE, 0);
return X_ERROR_IO_PENDING;
} else {
return X_ERROR_INVALID_HANDLE;
}
}
buffer.Zero(buffer_length);
X_RESULT result =
e->WriteItem(buffer) ? X_ERROR_SUCCESS : X_ERROR_NO_MORE_FILES;
// Return X_ERROR_NO_MORE_FILES in HRESULT form.
X_HRESULT extended_result = result != 0 ? 0x80070012 : 0;
if (items_returned) {
assert_true(!overlapped);
*items_returned = result == X_ERROR_SUCCESS ? 1 : 0;
return result;
} else if (overlapped) {
assert_true(!items_returned);
kernel_state()->CompleteOverlappedImmediateEx(
overlapped, result, extended_result,
result == X_ERROR_SUCCESS ? e->item_count() : 0);
return X_ERROR_IO_PENDING;
} else {
assert_always();
return X_ERROR_INVALID_PARAMETER;
}
}
DECLARE_XAM_EXPORT(XamEnumerate, ExportTag::kImplemented);
void RegisterInfoExports(xe::cpu::ExportResolver* export_resolver,
KernelState* kernel_state) {
SHIM_SET_MAPPING("xam.xex", XamGetSystemVersion, state);
SHIM_SET_MAPPING("xam.xex", XGetAVPack, state);
SHIM_SET_MAPPING("xam.xex", XGetGameRegion, state);
SHIM_SET_MAPPING("xam.xex", XGetLanguage, state);
SHIM_SET_MAPPING("xam.xex", XamGetExecutionId, state);
SHIM_SET_MAPPING("xam.xex", XamAlloc, state);
SHIM_SET_MAPPING("xam.xex", XamFree, state);
}
} // namespace xam
} // namespace kernel
} // namespace xe

View File

@@ -0,0 +1,272 @@
/**
******************************************************************************
* 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/base/logging.h"
#include "xenia/emulator.h"
#include "xenia/hid/input.h"
#include "xenia/hid/input_system.h"
#include "xenia/kernel/kernel_state.h"
#include "xenia/kernel/util/shim_utils.h"
#include "xenia/kernel/xam/xam_private.h"
#include "xenia/xbox.h"
namespace xe {
namespace kernel {
namespace xam {
using xe::hid::X_INPUT_CAPABILITIES;
using xe::hid::X_INPUT_KEYSTROKE;
using xe::hid::X_INPUT_STATE;
using xe::hid::X_INPUT_VIBRATION;
constexpr uint32_t XINPUT_FLAG_GAMEPAD = 0x01;
constexpr uint32_t XINPUT_FLAG_ANY_USER = 1 << 30;
SHIM_CALL XamResetInactivity_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t unk = SHIM_GET_ARG_32(0);
XELOGD("XamResetInactivity(%d)", unk);
// Result ignored.
SHIM_SET_RETURN_32(0);
}
SHIM_CALL XamEnableInactivityProcessing_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t zero = SHIM_GET_ARG_32(0);
uint32_t unk = SHIM_GET_ARG_32(1);
XELOGD("XamEnableInactivityProcessing(%d, %d)", zero, unk);
// Expects 0.
SHIM_SET_RETURN_32(0);
}
// http://msdn.microsoft.com/en-us/library/windows/desktop/microsoft.directx_sdk.reference.xinputgetcapabilities(v=vs.85).aspx
SHIM_CALL XamInputGetCapabilities_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t user_index = SHIM_GET_ARG_32(0);
uint32_t flags = SHIM_GET_ARG_32(1);
uint32_t caps_ptr = SHIM_GET_ARG_32(2);
XELOGD("XamInputGetCapabilities(%d, %.8X, %.8X)", user_index, flags,
caps_ptr);
if (!caps_ptr) {
SHIM_SET_RETURN_32(X_ERROR_BAD_ARGUMENTS);
return;
}
if ((flags & 0xFF) && (flags & XINPUT_FLAG_GAMEPAD) == 0) {
// Ignore any query for other types of devices.
SHIM_SET_RETURN_32(X_ERROR_DEVICE_NOT_CONNECTED);
return;
}
if ((user_index & 0xFF) == 0xFF || (flags & XINPUT_FLAG_ANY_USER)) {
// Always pin user to 0.
user_index = 0;
}
auto input_system = kernel_state->emulator()->input_system();
auto caps = SHIM_STRUCT(X_INPUT_CAPABILITIES, caps_ptr);
X_RESULT result = input_system->GetCapabilities(user_index, flags, caps);
SHIM_SET_RETURN_32(result);
}
SHIM_CALL XamInputGetCapabilitiesEx_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t unk = SHIM_GET_ARG_32(0);
uint32_t user_index = SHIM_GET_ARG_32(1);
uint32_t flags = SHIM_GET_ARG_32(2);
uint32_t caps_ptr = SHIM_GET_ARG_32(3);
XELOGD("XamInputGetCapabilitiesEx(%d, %d, %.8X, %.8X)", unk, user_index,
flags, caps_ptr);
if (!caps_ptr) {
SHIM_SET_RETURN_32(X_ERROR_BAD_ARGUMENTS);
return;
}
if ((flags & 0xFF) && (flags & XINPUT_FLAG_GAMEPAD) == 0) {
// Ignore any query for other types of devices.
SHIM_SET_RETURN_32(X_ERROR_DEVICE_NOT_CONNECTED);
return;
}
if ((user_index & 0xFF) == 0xFF || (flags & XINPUT_FLAG_ANY_USER)) {
// Always pin user to 0.
user_index = 0;
}
auto input_system = kernel_state->emulator()->input_system();
auto caps = SHIM_STRUCT(X_INPUT_CAPABILITIES, caps_ptr);
X_RESULT result = input_system->GetCapabilities(user_index, flags, caps);
SHIM_SET_RETURN_32(result);
}
// http://msdn.microsoft.com/en-us/library/windows/desktop/microsoft.directx_sdk.reference.xinputgetstate(v=vs.85).aspx
SHIM_CALL XamInputGetState_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t user_index = SHIM_GET_ARG_32(0);
uint32_t flags = SHIM_GET_ARG_32(1);
uint32_t state_ptr = SHIM_GET_ARG_32(2);
XELOGD("XamInputGetState(%d, %.8X, %.8X)", user_index, flags, state_ptr);
// Games call this with a NULL state ptr, probably as a query.
if ((flags & 0xFF) && (flags & XINPUT_FLAG_GAMEPAD) == 0) {
// Ignore any query for other types of devices.
SHIM_SET_RETURN_32(X_ERROR_DEVICE_NOT_CONNECTED);
return;
}
if ((user_index & 0xFF) == 0xFF || (flags & XINPUT_FLAG_ANY_USER)) {
// Always pin user to 0.
user_index = 0;
}
auto input_system = kernel_state->emulator()->input_system();
auto input_state = SHIM_STRUCT(X_INPUT_STATE, state_ptr);
X_RESULT result = input_system->GetState(user_index, input_state);
SHIM_SET_RETURN_32(result);
}
// http://msdn.microsoft.com/en-us/library/windows/desktop/microsoft.directx_sdk.reference.xinputsetstate(v=vs.85).aspx
SHIM_CALL XamInputSetState_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t user_index = SHIM_GET_ARG_32(0);
uint32_t unk = SHIM_GET_ARG_32(1);
uint32_t vibration_ptr = SHIM_GET_ARG_32(2);
XELOGD("XamInputSetState(%d, %.8X, %.8X)", user_index, unk, vibration_ptr);
if (!vibration_ptr) {
SHIM_SET_RETURN_32(X_ERROR_BAD_ARGUMENTS);
return;
}
if ((user_index & 0xFF) == 0xFF) {
// Always pin user to 0.
user_index = 0;
}
auto input_system = kernel_state->emulator()->input_system();
auto vibration = SHIM_STRUCT(X_INPUT_VIBRATION, vibration_ptr);
X_RESULT result = input_system->SetState(user_index, vibration);
SHIM_SET_RETURN_32(result);
}
// http://msdn.microsoft.com/en-us/library/windows/desktop/microsoft.directx_sdk.reference.xinputgetkeystroke(v=vs.85).aspx
SHIM_CALL XamInputGetKeystroke_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t user_index = SHIM_GET_ARG_32(0);
uint32_t flags = SHIM_GET_ARG_32(1);
uint32_t keystroke_ptr = SHIM_GET_ARG_32(2);
// http://ffplay360.googlecode.com/svn/Test/Common/AtgXime.cpp
// user index = index or XUSER_INDEX_ANY
// flags = XINPUT_FLAG_GAMEPAD (| _ANYUSER | _ANYDEVICE)
XELOGD("XamInputGetKeystroke(%d, %.8X, %.8X)", user_index, flags,
keystroke_ptr);
if (!keystroke_ptr) {
SHIM_SET_RETURN_32(X_ERROR_BAD_ARGUMENTS);
return;
}
if ((flags & 0xFF) && (flags & XINPUT_FLAG_GAMEPAD) == 0) {
// Ignore any query for other types of devices.
SHIM_SET_RETURN_32(X_ERROR_DEVICE_NOT_CONNECTED);
return;
}
if ((user_index & 0xFF) == 0xFF || (flags & XINPUT_FLAG_ANY_USER)) {
// Always pin user to 0.
user_index = 0;
}
auto input_system = kernel_state->emulator()->input_system();
auto keystroke = SHIM_STRUCT(X_INPUT_KEYSTROKE, keystroke_ptr);
X_RESULT result = input_system->GetKeystroke(user_index, flags, keystroke);
SHIM_SET_RETURN_32(result);
}
// Same as non-ex, just takes a pointer to user index.
SHIM_CALL XamInputGetKeystrokeEx_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t user_index_ptr = SHIM_GET_ARG_32(0);
uint32_t flags = SHIM_GET_ARG_32(1);
uint32_t keystroke_ptr = SHIM_GET_ARG_32(2);
uint32_t user_index = SHIM_MEM_32(user_index_ptr);
XELOGD("XamInputGetKeystrokeEx(%.8X(%d), %.8X, %.8X)", user_index_ptr,
user_index, flags, keystroke_ptr);
if (!keystroke_ptr) {
SHIM_SET_RETURN_32(X_ERROR_BAD_ARGUMENTS);
return;
}
if ((flags & 0xFF) && (flags & XINPUT_FLAG_GAMEPAD) == 0) {
// Ignore any query for other types of devices.
SHIM_SET_RETURN_32(X_ERROR_DEVICE_NOT_CONNECTED);
return;
}
if ((user_index & 0xFF) == 0xFF || (flags & XINPUT_FLAG_ANY_USER)) {
// Always pin user to 0.
user_index = 0;
}
auto input_system = kernel_state->emulator()->input_system();
auto keystroke = SHIM_STRUCT(X_INPUT_KEYSTROKE, keystroke_ptr);
X_RESULT result = input_system->GetKeystroke(user_index, flags, keystroke);
if (XSUCCEEDED(result)) {
SHIM_SET_MEM_32(user_index_ptr, keystroke->user_index);
}
SHIM_SET_RETURN_32(result);
}
SHIM_CALL XamUserGetDeviceContext_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t user_index = SHIM_GET_ARG_32(0);
uint32_t unk = SHIM_GET_ARG_32(1);
uint32_t out_ptr = SHIM_GET_ARG_32(2);
XELOGD("XamUserGetDeviceContext(%d, %d, %.8X)", user_index, unk, out_ptr);
// Games check the result - usually with some masking.
// If this function fails they assume zero, so let's fail AND
// set zero just to be safe.
SHIM_SET_MEM_32(out_ptr, 0);
if (!user_index || (user_index & 0xFF) == 0xFF) {
SHIM_SET_RETURN_32(0);
} else {
SHIM_SET_RETURN_32(-1);
}
}
void RegisterInputExports(xe::cpu::ExportResolver* export_resolver,
KernelState* kernel_state) {
SHIM_SET_MAPPING("xam.xex", XamResetInactivity, state);
SHIM_SET_MAPPING("xam.xex", XamEnableInactivityProcessing, state);
SHIM_SET_MAPPING("xam.xex", XamInputGetCapabilities, state);
SHIM_SET_MAPPING("xam.xex", XamInputGetCapabilitiesEx, state);
SHIM_SET_MAPPING("xam.xex", XamInputGetState, state);
SHIM_SET_MAPPING("xam.xex", XamInputSetState, state);
SHIM_SET_MAPPING("xam.xex", XamInputGetKeystroke, state);
SHIM_SET_MAPPING("xam.xex", XamInputGetKeystrokeEx, state);
SHIM_SET_MAPPING("xam.xex", XamUserGetDeviceContext, state);
}
} // namespace xam
} // namespace kernel
} // namespace xe

View File

@@ -0,0 +1,72 @@
/**
******************************************************************************
* 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/xam/xam_module.h"
#include <vector>
#include "xenia/base/math.h"
#include "xenia/kernel/kernel_state.h"
#include "xenia/kernel/xam/xam_private.h"
namespace xe {
namespace kernel {
namespace xam {
XamModule::XamModule(Emulator* emulator, KernelState* kernel_state)
: KernelModule(kernel_state, "xe:\\xam.xex"), loader_data_() {
RegisterExportTable(export_resolver_);
// Register all exported functions.
RegisterAvatarExports(export_resolver_, kernel_state_);
RegisterContentExports(export_resolver_, kernel_state_);
RegisterInfoExports(export_resolver_, kernel_state_);
RegisterInputExports(export_resolver_, kernel_state_);
RegisterMsgExports(export_resolver_, kernel_state_);
RegisterNetExports(export_resolver_, kernel_state_);
RegisterNotifyExports(export_resolver_, kernel_state_);
RegisterNuiExports(export_resolver_, kernel_state_);
RegisterUIExports(export_resolver_, kernel_state_);
RegisterUserExports(export_resolver_, kernel_state_);
RegisterVideoExports(export_resolver_, kernel_state_);
RegisterVoiceExports(export_resolver_, kernel_state_);
}
std::vector<xe::cpu::Export*> xam_exports(4096);
xe::cpu::Export* RegisterExport_xam(xe::cpu::Export* export_entry) {
assert_true(export_entry->ordinal < xam_exports.size());
xam_exports[export_entry->ordinal] = export_entry;
return export_entry;
}
void XamModule::RegisterExportTable(xe::cpu::ExportResolver* export_resolver) {
assert_not_null(export_resolver);
// Build the export table used for resolution.
#include "xenia/kernel/util/export_table_pre.inc"
static xe::cpu::Export xam_export_table[] = {
#include "xenia/kernel/xam/xam_table.inc"
};
#include "xenia/kernel/util/export_table_post.inc"
for (size_t i = 0; i < xe::countof(xam_export_table); ++i) {
auto& export_entry = xam_export_table[i];
assert_true(export_entry.ordinal < xam_exports.size());
if (!xam_exports[export_entry.ordinal]) {
xam_exports[export_entry.ordinal] = &export_entry;
}
}
export_resolver->RegisterTable("xam.xex", &xam_exports);
}
XamModule::~XamModule() {}
} // namespace xam
} // namespace kernel
} // namespace xe

View File

@@ -0,0 +1,49 @@
/**
******************************************************************************
* 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_XAM_XAM_MODULE_H_
#define XENIA_KERNEL_XAM_XAM_MODULE_H_
#include <string>
#include "xenia/cpu/export_resolver.h"
#include "xenia/kernel/kernel_module.h"
#include "xenia/kernel/kernel_state.h"
#include "xenia/kernel/xam/xam_ordinals.h"
namespace xe {
namespace kernel {
namespace xam {
class XamModule : public KernelModule {
public:
XamModule(Emulator* emulator, KernelState* kernel_state);
virtual ~XamModule();
static void RegisterExportTable(xe::cpu::ExportResolver* export_resolver);
struct LoaderData {
uint32_t launch_data_ptr = 0;
uint32_t launch_data_size = 0;
uint32_t launch_flags = 0;
std::string launch_path; // Full path to next xex
};
const LoaderData& loader_data() const { return loader_data_; }
LoaderData& loader_data() { return loader_data_; }
private:
LoaderData loader_data_;
};
} // namespace xam
} // namespace kernel
} // namespace xe
#endif // XENIA_KERNEL_XAM_XAM_MODULE_H_

View File

@@ -0,0 +1,132 @@
/**
******************************************************************************
* 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/base/logging.h"
#include "xenia/kernel/kernel_state.h"
#include "xenia/kernel/util/shim_utils.h"
#include "xenia/kernel/xam/xam_private.h"
#include "xenia/kernel/xevent.h"
#include "xenia/xbox.h"
namespace xe {
namespace kernel {
namespace xam {
SHIM_CALL XMsgInProcessCall_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t app = SHIM_GET_ARG_32(0);
uint32_t message = SHIM_GET_ARG_32(1);
uint32_t arg1 = SHIM_GET_ARG_32(2);
uint32_t arg2 = SHIM_GET_ARG_32(3);
XELOGD("XMsgInProcessCall(%.8X, %.8X, %.8X, %.8X)", app, message, arg1, arg2);
auto result = kernel_state->app_manager()->DispatchMessageSync(app, message,
arg1, arg2);
if (result == X_ERROR_NOT_FOUND) {
XELOGE("XMsgInProcessCall: app %.8X undefined", app);
}
SHIM_SET_RETURN_32(result);
}
SHIM_CALL XMsgSystemProcessCall_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t app = SHIM_GET_ARG_32(0);
uint32_t message = SHIM_GET_ARG_32(1);
uint32_t buffer = SHIM_GET_ARG_32(2);
uint32_t buffer_length = SHIM_GET_ARG_32(3);
XELOGD("XMsgSystemProcessCall(%.8X, %.8X, %.8X, %.8X)", app, message, buffer,
buffer_length);
auto result = kernel_state->app_manager()->DispatchMessageAsync(
app, message, buffer, buffer_length);
if (result == X_ERROR_NOT_FOUND) {
XELOGE("XMsgSystemProcessCall: app %.8X undefined", app);
}
SHIM_SET_RETURN_32(result);
}
SHIM_CALL XMsgStartIORequest_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t app = SHIM_GET_ARG_32(0);
uint32_t message = SHIM_GET_ARG_32(1);
uint32_t overlapped_ptr = SHIM_GET_ARG_32(2);
uint32_t buffer = SHIM_GET_ARG_32(3);
uint32_t buffer_length = SHIM_GET_ARG_32(4);
XELOGD("XMsgStartIORequest(%.8X, %.8X, %.8X, %.8X, %d)", app, message,
overlapped_ptr, buffer, buffer_length);
auto result = kernel_state->app_manager()->DispatchMessageAsync(
app, message, buffer, buffer_length);
if (result == X_ERROR_NOT_FOUND) {
XELOGE("XMsgStartIORequest: app %.8X undefined", app);
}
if (overlapped_ptr) {
kernel_state->CompleteOverlappedImmediate(overlapped_ptr, result);
result = X_ERROR_IO_PENDING;
}
SHIM_SET_RETURN_32(result);
}
SHIM_CALL XMsgStartIORequestEx_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t app = SHIM_GET_ARG_32(0);
uint32_t message = SHIM_GET_ARG_32(1);
uint32_t overlapped_ptr = SHIM_GET_ARG_32(2);
uint32_t buffer = SHIM_GET_ARG_32(3);
uint32_t buffer_length = SHIM_GET_ARG_32(4);
uint32_t unknown_ptr = SHIM_GET_ARG_32(5);
XELOGD("XMsgStartIORequestEx(%.8X, %.8X, %.8X, %.8X, %d, %.8X)", app, message,
overlapped_ptr, buffer, buffer_length, unknown_ptr);
auto result = kernel_state->app_manager()->DispatchMessageAsync(
app, message, buffer, buffer_length);
if (result == X_ERROR_NOT_FOUND) {
XELOGE("XMsgStartIORequestEx: app %.8X undefined", app);
}
if (overlapped_ptr) {
kernel_state->CompleteOverlappedImmediate(overlapped_ptr, result);
result = X_ERROR_IO_PENDING;
}
SHIM_SET_RETURN_32(result);
}
SHIM_CALL XMsgCancelIORequest_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t overlapped_ptr = SHIM_GET_ARG_32(0);
uint32_t wait = SHIM_GET_ARG_32(1);
XELOGD("XMsgCancelIORequest(%.8X, %d)", overlapped_ptr, wait);
X_HANDLE event_handle = XOverlappedGetEvent(SHIM_MEM_ADDR(overlapped_ptr));
if (event_handle && wait) {
auto ev = kernel_state->object_table()->LookupObject<XEvent>(event_handle);
if (ev) {
ev->Wait(0, 0, true, nullptr);
}
}
SHIM_SET_RETURN_32(0);
}
void RegisterMsgExports(xe::cpu::ExportResolver* export_resolver,
KernelState* kernel_state) {
SHIM_SET_MAPPING("xam.xex", XMsgInProcessCall, state);
SHIM_SET_MAPPING("xam.xex", XMsgSystemProcessCall, state);
SHIM_SET_MAPPING("xam.xex", XMsgStartIORequest, state);
SHIM_SET_MAPPING("xam.xex", XMsgStartIORequestEx, state);
SHIM_SET_MAPPING("xam.xex", XMsgCancelIORequest, state);
}
} // namespace xam
} // namespace kernel
} // namespace xe

View File

@@ -0,0 +1,748 @@
/**
******************************************************************************
* 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 <cstring>
#include "xenia/base/clock.h"
#include "xenia/base/logging.h"
#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/xboxkrnl/xboxkrnl_threading.h"
#include "xenia/kernel/xevent.h"
#include "xenia/kernel/xthread.h"
#include "xenia/xbox.h"
// NOTE: must be included last as it expects windows.h to already be included.
#define _WINSOCK_DEPRECATED_NO_WARNINGS // inet_addr
#include <winsock2.h> // NOLINT(build/include_order)
namespace xe {
namespace kernel {
namespace xam {
// https://github.com/G91/TitanOffLine/blob/1e692d9bb9dfac386d08045ccdadf4ae3227bb5e/xkelib/xam/xamNet.h
enum {
XNCALLER_INVALID = 0x0,
XNCALLER_TITLE = 0x1,
XNCALLER_SYSAPP = 0x2,
XNCALLER_XBDM = 0x3,
XNCALLER_TEST = 0x4,
NUM_XNCALLER_TYPES = 0x4,
};
// https://github.com/pmrowla/hl2sdk-csgo/blob/master/common/xbox/xboxstubs.h
typedef struct {
// FYI: IN_ADDR should be in network-byte order.
IN_ADDR ina; // IP address (zero if not static/DHCP)
IN_ADDR inaOnline; // Online IP address (zero if not online)
xe::be<uint16_t> wPortOnline; // Online port
uint8_t abEnet[6]; // Ethernet MAC address
uint8_t abOnline[20]; // Online identification
} XNADDR;
struct Xsockaddr_t {
xe::be<uint16_t> sa_family;
char sa_data[14];
};
struct XWSABUF {
xe::be<uint32_t> len;
xe::be<uint32_t> buf_ptr;
};
struct XWSAOVERLAPPED {
xe::be<uint32_t> internal;
xe::be<uint32_t> internal_high;
union {
struct {
xe::be<uint32_t> offset;
xe::be<uint32_t> offset_high;
};
xe::be<uint32_t> pointer;
};
xe::be<uint32_t> event_handle;
};
void LoadSockaddr(const uint8_t* ptr, sockaddr* out_addr) {
out_addr->sa_family = xe::load_and_swap<uint16_t>(ptr + 0);
switch (out_addr->sa_family) {
case AF_INET: {
auto in_addr = reinterpret_cast<sockaddr_in*>(out_addr);
in_addr->sin_port = xe::load_and_swap<uint16_t>(ptr + 2);
// Maybe? Depends on type.
in_addr->sin_addr.S_un.S_addr = xe::load_and_swap<uint32_t>(ptr + 4);
break;
}
default:
assert_unhandled_case(out_addr->sa_family);
break;
}
}
void StoreSockaddr(const sockaddr& addr, uint8_t* ptr) {
switch (addr.sa_family) {
case AF_UNSPEC:
std::memset(ptr, 0, sizeof(addr));
break;
case AF_INET: {
auto& in_addr = reinterpret_cast<const sockaddr_in&>(addr);
xe::store_and_swap<uint16_t>(ptr + 0, in_addr.sin_family);
xe::store_and_swap<uint16_t>(ptr + 2, in_addr.sin_port);
// Maybe? Depends on type.
xe::store_and_swap<uint32_t>(ptr + 4, in_addr.sin_addr.S_un.S_addr);
break;
}
default:
assert_unhandled_case(addr.sa_family);
break;
}
}
// https://github.com/joolswills/mameox/blob/master/MAMEoX/Sources/xbox_Network.cpp#L136
struct XNetStartupParams {
BYTE cfgSizeOfStruct;
BYTE cfgFlags;
BYTE cfgSockMaxDgramSockets;
BYTE cfgSockMaxStreamSockets;
BYTE cfgSockDefaultRecvBufsizeInK;
BYTE cfgSockDefaultSendBufsizeInK;
BYTE cfgKeyRegMax;
BYTE cfgSecRegMax;
BYTE cfgQosDataLimitDiv4;
BYTE cfgQosProbeTimeoutInSeconds;
BYTE cfgQosProbeRetries;
BYTE cfgQosSrvMaxSimultaneousResponses;
BYTE cfgQosPairWaitTimeInSeconds;
};
XNetStartupParams xnet_startup_params = {0};
SHIM_CALL NetDll_XNetStartup_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t caller = SHIM_GET_ARG_32(0);
uint32_t params_ptr = SHIM_GET_ARG_32(1);
XELOGD("NetDll_XNetStartup(%d, %.8X)", caller, params_ptr);
if (params_ptr) {
auto params =
kernel_memory()->TranslateVirtual<XNetStartupParams*>(params_ptr);
assert_true(params->cfgSizeOfStruct == sizeof(XNetStartupParams));
std::memcpy(&xnet_startup_params, params, sizeof(XNetStartupParams));
}
SHIM_SET_RETURN_32(0);
}
SHIM_CALL NetDll_XNetCleanup_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t caller = SHIM_GET_ARG_32(0);
uint32_t params_ptr = SHIM_GET_ARG_32(1);
XELOGD("NetDll_XNetCleanup(%d, %.8X)", caller, params_ptr);
SHIM_SET_RETURN_32(0);
}
dword_result_t NetDll_XNetGetOpt(dword_t one, dword_t option_id,
lpvoid_t buffer_ptr, lpdword_t buffer_size) {
assert_true(one == 1);
switch (option_id) {
case 1:
if (*buffer_size < sizeof(XNetStartupParams)) {
*buffer_size = sizeof(XNetStartupParams);
return WSAEMSGSIZE;
}
std::memcpy(buffer_ptr, &xnet_startup_params, sizeof(XNetStartupParams));
return 0;
default:
XELOGE("NetDll_XNetGetOpt: option %d unimplemented", option_id);
return WSAEINVAL;
}
}
DECLARE_XAM_EXPORT(NetDll_XNetGetOpt, ExportTag::kNetworking);
SHIM_CALL NetDll_XNetRandom_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t caller = SHIM_GET_ARG_32(0);
uint32_t buffer_ptr = SHIM_GET_ARG_32(1);
uint32_t length = SHIM_GET_ARG_32(2);
XELOGD("NetDll_XNetRandom(%d, %.8X, %d)", caller, buffer_ptr, length);
// For now, constant values.
// This makes replicating things easier.
std::memset(SHIM_MEM_ADDR(buffer_ptr), 0xBB, length);
SHIM_SET_RETURN_32(0);
}
SHIM_CALL NetDll_WSAStartup_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t caller = SHIM_GET_ARG_32(0);
uint16_t version = SHIM_GET_ARG_16(1);
uint32_t data_ptr = SHIM_GET_ARG_32(2);
XELOGD("NetDll_WSAStartup(%d, %.4X, %.8X)", caller, version, data_ptr);
// TODO(benvanik): abstraction layer needed.
WSADATA wsaData;
ZeroMemory(&wsaData, sizeof(WSADATA));
int ret = WSAStartup(version, &wsaData);
auto data_out = kernel_state->memory()->TranslateVirtual(data_ptr);
if (data_ptr) {
xe::store_and_swap<uint16_t>(data_out + 0x000, wsaData.wVersion);
xe::store_and_swap<uint16_t>(data_out + 0x002, wsaData.wHighVersion);
std::memcpy(data_out + 0x004, wsaData.szDescription, 0x100);
std::memcpy(data_out + 0x105, wsaData.szSystemStatus, 0x80);
xe::store_and_swap<uint16_t>(data_out + 0x186, wsaData.iMaxSockets);
xe::store_and_swap<uint16_t>(data_out + 0x188, wsaData.iMaxUdpDg);
// Some games (PoG) want this value round-tripped - they'll compare if it
// changes and bugcheck if it does.
uint32_t vendor_ptr = SHIM_MEM_32(data_ptr + 0x190);
SHIM_SET_MEM_32(data_ptr + 0x190, vendor_ptr);
}
SHIM_SET_RETURN_32(ret);
}
SHIM_CALL NetDll_WSACleanup_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t caller = SHIM_GET_ARG_32(0);
XELOGD("NetDll_WSACleanup(%d)", caller);
// Don't actually call WSACleanup - we use it for the debugger and such.
// int ret = WSACleanup();
int ret = 0;
SHIM_SET_RETURN_32(ret);
}
SHIM_CALL NetDll_WSAGetLastError_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
XELOGD("NetDll_WSAGetLastError()");
int err = WSAGetLastError();
SHIM_SET_RETURN_32(err);
}
dword_result_t NetDll_WSARecvFrom(dword_t caller, dword_t socket,
pointer_t<XWSABUF> buffers_ptr,
dword_t buffer_count,
lpdword_t num_bytes_recv, lpdword_t flags_ptr,
pointer_t<Xsockaddr_t> from_addr,
pointer_t<XWSAOVERLAPPED> overlapped_ptr,
lpvoid_t completion_routine_ptr) {
if (overlapped_ptr) {
auto evt = kernel_state()->object_table()->LookupObject<XEvent>(
overlapped_ptr->event_handle);
if (evt) {
evt->Set(0, false);
}
}
return 0;
}
DECLARE_XAM_EXPORT(NetDll_WSARecvFrom, ExportTag::kNetworking);
dword_result_t NetDll_WSAWaitForMultipleEvents(
dword_t num_events, pointer_t<xe::be<uint32_t>> events, dword_t wait_all,
dword_t timeout, dword_t alertable) {
if (num_events > 64) {
XThread::GetCurrentThread()->set_last_error(87); // ERROR_INVALID_PARAMETER
return ~0u;
}
xe::be<uint64_t> timeout_wait = (uint64_t)timeout;
X_STATUS result = 0;
do {
result = xboxkrnl::NtWaitForMultipleObjectsEx(
num_events, events, wait_all, 1, alertable,
timeout != -1 ? &timeout_wait : nullptr);
} while (result == X_STATUS_ALERTED);
if (XFAILED(result)) {
uint32_t error = xboxkrnl::RtlNtStatusToDosError(result);
XThread::GetCurrentThread()->set_last_error(error);
return ~0u;
}
return 0;
}
DECLARE_XAM_EXPORT(NetDll_WSAWaitForMultipleEvents,
ExportTag::kNetworking | ExportTag::kThreading);
dword_result_t NetDll_WSACreateEvent() {
XEvent* ev = new XEvent(kernel_state());
ev->Initialize(true, false);
return ev->handle();
}
DECLARE_XAM_EXPORT(NetDll_WSACreateEvent,
ExportTag::kNetworking | ExportTag::kThreading);
dword_result_t NetDll_WSACloseEvent(dword_t event_handle) {
X_STATUS result = kernel_state()->object_table()->ReleaseHandle(event_handle);
if (XFAILED(result)) {
uint32_t error = xboxkrnl::RtlNtStatusToDosError(result);
XThread::GetCurrentThread()->set_last_error(error);
return 0;
}
return 1;
}
DECLARE_XAM_EXPORT(NetDll_WSACloseEvent,
ExportTag::kNetworking | ExportTag::kThreading);
dword_result_t NetDll_WSAResetEvent(dword_t event_handle) {
X_STATUS result = xboxkrnl::NtClearEvent(event_handle);
if (XFAILED(result)) {
uint32_t error = xboxkrnl::RtlNtStatusToDosError(result);
XThread::GetCurrentThread()->set_last_error(error);
return 0;
}
return 1;
}
DECLARE_XAM_EXPORT(NetDll_WSAResetEvent,
ExportTag::kNetworking | ExportTag::kThreading);
dword_result_t NetDll_WSASetEvent(dword_t event_handle) {
X_STATUS result = xboxkrnl::NtSetEvent(event_handle, nullptr);
if (XFAILED(result)) {
uint32_t error = xboxkrnl::RtlNtStatusToDosError(result);
XThread::GetCurrentThread()->set_last_error(error);
return 0;
}
return 1;
}
DECLARE_XAM_EXPORT(NetDll_WSASetEvent,
ExportTag::kNetworking | ExportTag::kThreading);
struct XnAddrStatus {
// Address acquisition is not yet complete
static const uint32_t XNET_GET_XNADDR_PENDING = 0x00000000;
// XNet is uninitialized or no debugger found
static const uint32_t XNET_GET_XNADDR_NONE = 0x00000001;
// Host has ethernet address (no IP address)
static const uint32_t XNET_GET_XNADDR_ETHERNET = 0x00000002;
// Host has statically assigned IP address
static const uint32_t XNET_GET_XNADDR_STATIC = 0x00000004;
// Host has DHCP assigned IP address
static const uint32_t XNET_GET_XNADDR_DHCP = 0x00000008;
// Host has PPPoE assigned IP address
static const uint32_t XNET_GET_XNADDR_PPPOE = 0x00000010;
// Host has one or more gateways configured
static const uint32_t XNET_GET_XNADDR_GATEWAY = 0x00000020;
// Host has one or more DNS servers configured
static const uint32_t XNET_GET_XNADDR_DNS = 0x00000040;
// Host is currently connected to online service
static const uint32_t XNET_GET_XNADDR_ONLINE = 0x00000080;
// Network configuration requires troubleshooting
static const uint32_t XNET_GET_XNADDR_TROUBLESHOOT = 0x00008000;
};
dword_result_t NetDll_XNetGetTitleXnAddr(dword_t caller,
pointer_t<XNADDR> addr_ptr) {
// Just return a loopback address atm.
addr_ptr->ina.S_un.S_addr = htonl(INADDR_LOOPBACK);
addr_ptr->inaOnline.S_un.S_addr = 0;
addr_ptr->wPortOnline = 0;
std::memset(addr_ptr->abEnet, 0, 6);
std::memset(addr_ptr->abOnline, 0, 20);
return XnAddrStatus::XNET_GET_XNADDR_STATIC;
}
DECLARE_XAM_EXPORT(NetDll_XNetGetTitleXnAddr,
ExportTag::kNetworking | ExportTag::kStub);
dword_result_t NetDll_XNetGetDebugXnAddr(dword_t caller,
pointer_t<XNADDR> addr_ptr) {
addr_ptr.Zero();
// XNET_GET_XNADDR_NONE causes caller to gracefully return.
return XnAddrStatus::XNET_GET_XNADDR_NONE;
}
DECLARE_XAM_EXPORT(NetDll_XNetGetDebugXnAddr,
ExportTag::kNetworking | ExportTag::kStub);
// http://www.google.com/patents/WO2008112448A1?cl=en
// Reserves a port for use by system link
dword_result_t NetDll_XNetSetSystemLinkPort(dword_t caller, dword_t port) {
return 1;
}
DECLARE_XAM_EXPORT(NetDll_XNetSetSystemLinkPort,
ExportTag::kNetworking | ExportTag::kStub);
SHIM_CALL NetDll_XNetGetEthernetLinkStatus_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
// Games seem to call this before *Startup. If we return 0, they don't even
// try.
uint32_t caller = SHIM_GET_ARG_32(0);
XELOGD("NetDll_XNetGetEthernetLinkStatus(%d)", caller);
SHIM_SET_RETURN_32(0);
}
SHIM_CALL NetDll_XNetQosServiceLookup_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t caller = SHIM_GET_ARG_32(0);
uint32_t zero = SHIM_GET_ARG_32(1);
uint32_t event_handle = SHIM_GET_ARG_32(2);
uint32_t out_ptr = SHIM_GET_ARG_32(3);
XELOGD("NetDll_XNetQosServiceLookup(%d, %d, %.8X, %.8X)", caller, zero,
event_handle, out_ptr);
// Non-zero is error.
SHIM_SET_RETURN_32(1);
}
dword_result_t NetDll_inet_addr(lpstring_t addr_ptr) {
uint32_t addr = inet_addr(addr_ptr);
return xe::byte_swap(addr);
}
DECLARE_XAM_EXPORT(NetDll_inet_addr,
ExportTag::kImplemented | ExportTag::kNetworking);
SHIM_CALL NetDll_socket_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t caller = SHIM_GET_ARG_32(0);
uint32_t af = SHIM_GET_ARG_32(1);
uint32_t type = SHIM_GET_ARG_32(2);
uint32_t protocol = SHIM_GET_ARG_32(3);
XELOGD("NetDll_socket(%d, %d, %d, %d)", caller, af, type, protocol);
if (protocol == 0xFE) {
protocol = IPPROTO_UDP;
}
SOCKET socket_handle = socket(af, type, protocol);
assert_true(socket_handle >> 32 == 0);
XELOGD("NetDll_socket = %.8X", socket_handle);
SHIM_SET_RETURN_32(static_cast<uint32_t>(socket_handle));
}
SHIM_CALL NetDll_closesocket_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t caller = SHIM_GET_ARG_32(0);
uint32_t socket_handle = SHIM_GET_ARG_32(1);
XELOGD("NetDll_closesocket(%d, %.8X)", caller, socket_handle);
int ret = closesocket(socket_handle);
SHIM_SET_RETURN_32(ret);
}
SHIM_CALL NetDll_setsockopt_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t caller = SHIM_GET_ARG_32(0);
uint32_t socket_handle = SHIM_GET_ARG_32(1);
uint32_t level = SHIM_GET_ARG_32(2);
uint32_t optname = SHIM_GET_ARG_32(3);
uint32_t optval_ptr = SHIM_GET_ARG_32(4);
uint32_t optlen = SHIM_GET_ARG_32(5);
XELOGD("NetDll_setsockopt(%d, %.8X, %d, %d, %.8X, %d)", caller, socket_handle,
level, optname, optval_ptr, optlen);
char* optval = reinterpret_cast<char*>(SHIM_MEM_ADDR(optval_ptr));
int ret = setsockopt(socket_handle, level, optname, optval, optlen);
SHIM_SET_RETURN_32(ret);
}
SHIM_CALL NetDll_ioctlsocket_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t caller = SHIM_GET_ARG_32(0);
uint32_t socket_handle = SHIM_GET_ARG_32(1);
uint32_t cmd = SHIM_GET_ARG_32(2);
uint32_t arg_ptr = SHIM_GET_ARG_32(3);
XELOGD("NetDll_ioctlsocket(%d, %.8X, %.8X, %.8X)", caller, socket_handle, cmd,
arg_ptr);
u_long arg = SHIM_MEM_32(arg_ptr);
int ret = ioctlsocket(socket_handle, cmd, &arg);
SHIM_SET_MEM_32(arg_ptr, arg);
SHIM_SET_RETURN_32(ret);
}
SHIM_CALL NetDll_bind_shim(PPCContext* ppc_context, KernelState* kernel_state) {
uint32_t caller = SHIM_GET_ARG_32(0);
uint32_t socket_handle = SHIM_GET_ARG_32(1);
uint32_t name_ptr = SHIM_GET_ARG_32(2);
uint32_t namelen = SHIM_GET_ARG_32(3);
XELOGD("NetDll_bind(%d, %.8X, %.8X, %d)", caller, socket_handle, name_ptr,
namelen);
sockaddr name;
LoadSockaddr(SHIM_MEM_ADDR(name_ptr), &name);
int ret = bind(socket_handle, &name, namelen);
SHIM_SET_RETURN_32(ret);
}
SHIM_CALL NetDll_connect_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t caller = SHIM_GET_ARG_32(0);
uint32_t socket_handle = SHIM_GET_ARG_32(1);
uint32_t name_ptr = SHIM_GET_ARG_32(2);
uint32_t namelen = SHIM_GET_ARG_32(3);
XELOGD("NetDll_connect(%d, %.8X, %.8X, %d)", caller, socket_handle, name_ptr,
namelen);
sockaddr name;
LoadSockaddr(SHIM_MEM_ADDR(name_ptr), &name);
int ret = connect(socket_handle, &name, namelen);
SHIM_SET_RETURN_32(ret);
}
SHIM_CALL NetDll_listen_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t caller = SHIM_GET_ARG_32(0);
uint32_t socket_handle = SHIM_GET_ARG_32(1);
int32_t backlog = SHIM_GET_ARG_32(2);
XELOGD("NetDll_listen(%d, %.8X, %d)", caller, socket_handle, backlog);
int ret = listen(socket_handle, backlog);
SHIM_SET_RETURN_32(ret);
}
SHIM_CALL NetDll_accept_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t caller = SHIM_GET_ARG_32(0);
uint32_t socket_handle = SHIM_GET_ARG_32(1);
uint32_t addr_ptr = SHIM_GET_ARG_32(2);
uint32_t addrlen_ptr = SHIM_GET_ARG_32(3);
XELOGD("NetDll_accept(%d, %.8X, %d)", caller, socket_handle, addr_ptr,
addrlen_ptr);
sockaddr addr;
int addrlen;
SOCKET ret_socket = accept(socket_handle, &addr, &addrlen);
if (ret_socket == INVALID_SOCKET) {
if (addr_ptr) {
std::memset(SHIM_MEM_ADDR(addr_ptr), 0, sizeof(addr));
}
if (addrlen_ptr) {
SHIM_SET_MEM_32(addrlen_ptr, sizeof(addr));
}
SHIM_SET_RETURN_32(-1);
} else {
assert_true(ret_socket >> 32 == 0);
if (addr_ptr) {
StoreSockaddr(addr, SHIM_MEM_ADDR(addr_ptr));
}
if (addrlen_ptr) {
SHIM_SET_MEM_32(addrlen_ptr, addrlen);
}
SHIM_SET_RETURN_32(static_cast<uint32_t>(ret_socket));
}
}
void LoadFdset(const uint8_t* src, fd_set* dest) {
dest->fd_count = xe::load_and_swap<uint32_t>(src);
for (int i = 0; i < 64; ++i) {
auto socket_handle =
static_cast<SOCKET>(xe::load_and_swap<uint32_t>(src + 4 + i * 4));
dest->fd_array[i] = socket_handle;
}
}
void StoreFdset(const fd_set& src, uint8_t* dest) {
xe::store_and_swap<uint32_t>(dest, src.fd_count);
for (int i = 0; i < 64; ++i) {
SOCKET socket_handle = src.fd_array[i];
assert_true(socket_handle >> 32 == 0);
xe::store_and_swap<uint32_t>(dest + 4 + i * 4,
static_cast<uint32_t>(socket_handle));
}
}
SHIM_CALL NetDll_select_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t caller = SHIM_GET_ARG_32(0);
uint32_t nfds = SHIM_GET_ARG_32(1);
uint32_t readfds_ptr = SHIM_GET_ARG_32(2);
uint32_t writefds_ptr = SHIM_GET_ARG_32(3);
uint32_t exceptfds_ptr = SHIM_GET_ARG_32(4);
uint32_t timeout_ptr = SHIM_GET_ARG_32(5);
XELOGD("NetDll_select(%d, %d, %.8X, %.8X, %.8X, %.8X)", caller, nfds,
readfds_ptr, writefds_ptr, exceptfds_ptr, timeout_ptr);
fd_set readfds = {0};
if (readfds_ptr) {
LoadFdset(SHIM_MEM_ADDR(readfds_ptr), &readfds);
}
fd_set writefds = {0};
if (writefds_ptr) {
LoadFdset(SHIM_MEM_ADDR(writefds_ptr), &writefds);
}
fd_set exceptfds = {0};
if (exceptfds_ptr) {
LoadFdset(SHIM_MEM_ADDR(exceptfds_ptr), &exceptfds);
}
timeval* timeout_in = nullptr;
timeval timeout;
if (timeout_ptr) {
timeout = {static_cast<int32_t>(SHIM_MEM_32(timeout_ptr + 0)),
static_cast<int32_t>(SHIM_MEM_32(timeout_ptr + 4))};
Clock::ScaleGuestDurationTimeval(
reinterpret_cast<int32_t*>(&timeout.tv_sec),
reinterpret_cast<int32_t*>(&timeout.tv_usec));
timeout_in = &timeout;
}
int ret = select(nfds, readfds_ptr ? &readfds : nullptr,
writefds_ptr ? &writefds : nullptr,
exceptfds_ptr ? &exceptfds : nullptr, timeout_in);
if (readfds_ptr) {
StoreFdset(readfds, SHIM_MEM_ADDR(readfds_ptr));
}
if (writefds_ptr) {
StoreFdset(writefds, SHIM_MEM_ADDR(writefds_ptr));
}
if (exceptfds_ptr) {
StoreFdset(exceptfds, SHIM_MEM_ADDR(exceptfds_ptr));
}
SHIM_SET_RETURN_32(ret);
}
SHIM_CALL NetDll_recv_shim(PPCContext* ppc_context, KernelState* kernel_state) {
uint32_t caller = SHIM_GET_ARG_32(0);
uint32_t socket_handle = SHIM_GET_ARG_32(1);
uint32_t buf_ptr = SHIM_GET_ARG_32(2);
uint32_t len = SHIM_GET_ARG_32(3);
uint32_t flags = SHIM_GET_ARG_32(4);
XELOGD("NetDll_recv(%d, %.8X, %.8X, %d, %d)", caller, socket_handle, buf_ptr,
len, flags);
int ret = recv(socket_handle, reinterpret_cast<char*>(SHIM_MEM_ADDR(buf_ptr)),
len, flags);
SHIM_SET_RETURN_32(ret);
}
SHIM_CALL NetDll_recvfrom_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t caller = SHIM_GET_ARG_32(0);
uint32_t socket_handle = SHIM_GET_ARG_32(1);
uint32_t buf_ptr = SHIM_GET_ARG_32(2);
uint32_t len = SHIM_GET_ARG_32(3);
uint32_t flags = SHIM_GET_ARG_32(4);
uint32_t from_ptr = SHIM_GET_ARG_32(5);
uint32_t fromlen_ptr = SHIM_GET_ARG_32(6);
XELOGD("NetDll_recvfrom(%d, %.8X, %.8X, %d, %d, %.8X, %.8X)", caller,
socket_handle, buf_ptr, len, flags, from_ptr, fromlen_ptr);
sockaddr from;
int fromlen = sizeof(from);
int ret =
recvfrom(socket_handle, reinterpret_cast<char*>(SHIM_MEM_ADDR(buf_ptr)),
len, flags, &from, &fromlen);
if (ret == -1) {
std::memset(SHIM_MEM_ADDR(from_ptr), 0, sizeof(from));
SHIM_SET_MEM_32(fromlen_ptr, sizeof(from));
} else {
StoreSockaddr(from, SHIM_MEM_ADDR(from_ptr));
SHIM_SET_MEM_32(fromlen_ptr, fromlen);
}
SHIM_SET_RETURN_32(ret);
}
SHIM_CALL NetDll_send_shim(PPCContext* ppc_context, KernelState* kernel_state) {
uint32_t caller = SHIM_GET_ARG_32(0);
uint32_t socket_handle = SHIM_GET_ARG_32(1);
uint32_t buf_ptr = SHIM_GET_ARG_32(2);
uint32_t len = SHIM_GET_ARG_32(3);
uint32_t flags = SHIM_GET_ARG_32(4);
XELOGD("NetDll_send(%d, %.8X, %.8X, %d, %d)", caller, socket_handle, buf_ptr,
len, flags);
int ret = send(socket_handle, reinterpret_cast<char*>(SHIM_MEM_ADDR(buf_ptr)),
len, flags);
SHIM_SET_RETURN_32(ret);
}
SHIM_CALL NetDll_sendto_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t caller = SHIM_GET_ARG_32(0);
uint32_t socket_handle = SHIM_GET_ARG_32(1);
uint32_t buf_ptr = SHIM_GET_ARG_32(2);
uint32_t len = SHIM_GET_ARG_32(3);
uint32_t flags = SHIM_GET_ARG_32(4);
uint32_t to_ptr = SHIM_GET_ARG_32(5);
uint32_t tolen = SHIM_GET_ARG_32(6);
XELOGD("NetDll_sendto(%d, %.8X, %.8X, %d, %d, %.8X, %d)", caller,
socket_handle, buf_ptr, len, flags, to_ptr, tolen);
sockaddr to;
LoadSockaddr(SHIM_MEM_ADDR(to_ptr), &to);
int ret =
sendto(socket_handle, reinterpret_cast<char*>(SHIM_MEM_ADDR(buf_ptr)),
len, flags, &to, tolen);
SHIM_SET_RETURN_32(ret);
}
void RegisterNetExports(xe::cpu::ExportResolver* export_resolver,
KernelState* kernel_state) {
SHIM_SET_MAPPING("xam.xex", NetDll_XNetStartup, state);
SHIM_SET_MAPPING("xam.xex", NetDll_XNetCleanup, state);
SHIM_SET_MAPPING("xam.xex", NetDll_XNetRandom, state);
SHIM_SET_MAPPING("xam.xex", NetDll_WSAStartup, state);
SHIM_SET_MAPPING("xam.xex", NetDll_WSACleanup, state);
SHIM_SET_MAPPING("xam.xex", NetDll_WSAGetLastError, state);
SHIM_SET_MAPPING("xam.xex", NetDll_XNetGetEthernetLinkStatus, state);
SHIM_SET_MAPPING("xam.xex", NetDll_XNetQosServiceLookup, state);
SHIM_SET_MAPPING("xam.xex", NetDll_socket, state);
SHIM_SET_MAPPING("xam.xex", NetDll_closesocket, state);
SHIM_SET_MAPPING("xam.xex", NetDll_setsockopt, state);
SHIM_SET_MAPPING("xam.xex", NetDll_ioctlsocket, state);
SHIM_SET_MAPPING("xam.xex", NetDll_bind, state);
SHIM_SET_MAPPING("xam.xex", NetDll_connect, state);
SHIM_SET_MAPPING("xam.xex", NetDll_listen, state);
SHIM_SET_MAPPING("xam.xex", NetDll_accept, state);
SHIM_SET_MAPPING("xam.xex", NetDll_select, state);
SHIM_SET_MAPPING("xam.xex", NetDll_recv, state);
SHIM_SET_MAPPING("xam.xex", NetDll_recvfrom, state);
SHIM_SET_MAPPING("xam.xex", NetDll_send, state);
SHIM_SET_MAPPING("xam.xex", NetDll_sendto, state);
}
} // namespace xam
} // namespace kernel
} // namespace xe

View File

@@ -0,0 +1,115 @@
/**
******************************************************************************
* 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/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/xbox.h"
namespace xe {
namespace kernel {
namespace xam {
SHIM_CALL XamNotifyCreateListener_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint64_t mask = SHIM_GET_ARG_64(0);
uint32_t one = SHIM_GET_ARG_32(1);
XELOGD("XamNotifyCreateListener(%.8llX, %d)", mask, one);
// r4=1 may indicate user process?
auto listener = object_ref<NotifyListener>(new NotifyListener(kernel_state));
listener->Initialize(mask);
// Handle ref is incremented, so return that.
uint32_t handle = listener->handle();
SHIM_SET_RETURN_32(handle);
}
// http://ffplay360.googlecode.com/svn/Test/Common/AtgSignIn.cpp
SHIM_CALL XNotifyGetNext_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t handle = SHIM_GET_ARG_32(0);
uint32_t match_id = SHIM_GET_ARG_32(1);
uint32_t id_ptr = SHIM_GET_ARG_32(2);
uint32_t param_ptr = SHIM_GET_ARG_32(3);
XELOGD("XNotifyGetNext(%.8X, %.8X, %.8X, %.8X)", handle, match_id, id_ptr,
param_ptr);
if (!handle) {
SHIM_SET_RETURN_32(0);
return;
}
// Grab listener.
auto listener =
kernel_state->object_table()->LookupObject<NotifyListener>(handle);
if (!listener) {
SHIM_SET_RETURN_32(0);
return;
}
bool dequeued = false;
uint32_t id = 0;
uint32_t param = 0;
if (match_id) {
// Asking for a specific notification
id = match_id;
dequeued = listener->DequeueNotification(match_id, &param);
} else {
// Just get next.
dequeued = listener->DequeueNotification(&id, &param);
}
if (dequeued) {
SHIM_SET_MEM_32(id_ptr, id);
SHIM_SET_MEM_32(param_ptr, param);
} else {
SHIM_SET_MEM_32(id_ptr, 0);
SHIM_SET_MEM_32(param_ptr, 0);
}
SHIM_SET_RETURN_32(dequeued ? 1 : 0);
}
SHIM_CALL XNotifyDelayUI_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t delay_ms = SHIM_GET_ARG_32(0);
XELOGD("XNotifyDelayUI(%d)", delay_ms);
// Ignored.
SHIM_SET_RETURN_32(0);
}
SHIM_CALL XNotifyPositionUI_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t position = SHIM_GET_ARG_32(0);
XELOGD("XNotifyPositionUI(%.8X)", position);
// Ignored.
}
void RegisterNotifyExports(xe::cpu::ExportResolver* export_resolver,
KernelState* kernel_state) {
SHIM_SET_MAPPING("xam.xex", XamNotifyCreateListener, state);
SHIM_SET_MAPPING("xam.xex", XNotifyGetNext, state);
SHIM_SET_MAPPING("xam.xex", XNotifyDelayUI, state);
SHIM_SET_MAPPING("xam.xex", XNotifyPositionUI, state);
}
} // namespace xam
} // namespace kernel
} // namespace xe

View File

@@ -0,0 +1,42 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2015 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/emulator.h"
#include "xenia/kernel/kernel_state.h"
#include "xenia/kernel/util/shim_utils.h"
#include "xenia/kernel/xam/xam_private.h"
#include "xenia/xbox.h"
namespace xe {
namespace kernel {
namespace xam {
struct X_NUI_DEVICE_STATUS {
xe::be<uint32_t> unk0;
xe::be<uint32_t> unk1;
xe::be<uint32_t> unk2;
xe::be<uint32_t> status;
xe::be<uint32_t> unk4;
xe::be<uint32_t> unk5;
};
static_assert(sizeof(X_NUI_DEVICE_STATUS) == 24, "Size matters");
void XamNuiGetDeviceStatus(pointer_t<X_NUI_DEVICE_STATUS> status_ptr) {
status_ptr.Zero();
status_ptr->status = 0; // Not connected.
}
DECLARE_XAM_EXPORT(XamNuiGetDeviceStatus, ExportTag::kStub);
void RegisterNuiExports(xe::cpu::ExportResolver* export_resolver,
KernelState* kernel_state) {}
} // namespace xam
} // namespace kernel
} // namespace xe

View File

@@ -0,0 +1,24 @@
/**
******************************************************************************
* 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_XAM_XAM_ORDINALS_H_
#define XENIA_KERNEL_XAM_XAM_ORDINALS_H_
#include "xenia/cpu/export_resolver.h"
// Build an ordinal enum to make it easy to lookup ordinals.
#include "xenia/kernel/util/ordinal_table_pre.inc"
namespace ordinals {
enum {
#include "xenia/kernel/xam/xam_table.inc"
};
} // namespace ordinals
#include "xenia/kernel/util/ordinal_table_post.inc"
#endif // XENIA_KERNEL_XAM_XAM_ORDINALS_H_

View File

@@ -0,0 +1,53 @@
/**
******************************************************************************
* 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_XAM_XAM_PRIVATE_H_
#define XENIA_KERNEL_XAM_XAM_PRIVATE_H_
#include "xenia/cpu/export_resolver.h"
#include "xenia/kernel/kernel_state.h"
#include "xenia/kernel/xam/xam_ordinals.h"
namespace xe {
namespace kernel {
namespace xam {
xe::cpu::Export* RegisterExport_xam(xe::cpu::Export* export_entry);
// Registration functions, one per file.
void RegisterAvatarExports(xe::cpu::ExportResolver* export_resolver,
KernelState* kernel_state);
void RegisterContentExports(xe::cpu::ExportResolver* export_resolver,
KernelState* kernel_state);
void RegisterInfoExports(xe::cpu::ExportResolver* export_resolver,
KernelState* kernel_state);
void RegisterInputExports(xe::cpu::ExportResolver* export_resolver,
KernelState* kernel_state);
void RegisterMsgExports(xe::cpu::ExportResolver* export_resolver,
KernelState* kernel_state);
void RegisterNetExports(xe::cpu::ExportResolver* export_resolver,
KernelState* kernel_state);
void RegisterNotifyExports(xe::cpu::ExportResolver* export_resolver,
KernelState* kernel_state);
void RegisterNuiExports(xe::cpu::ExportResolver* export_resolver,
KernelState* kernel_state);
void RegisterUIExports(xe::cpu::ExportResolver* export_resolver,
KernelState* kernel_state);
void RegisterUserExports(xe::cpu::ExportResolver* export_resolver,
KernelState* kernel_state);
void RegisterVideoExports(xe::cpu::ExportResolver* export_resolver,
KernelState* kernel_state);
void RegisterVoiceExports(xe::cpu::ExportResolver* export_resolver,
KernelState* kernel_state);
} // namespace xam
} // namespace kernel
} // namespace xe
#endif // XENIA_KERNEL_XAM_XAM_PRIVATE_H_

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,419 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "el/elemental_forms.h"
#include "xenia/base/logging.h"
#include "xenia/emulator.h"
#include "xenia/kernel/kernel_state.h"
#include "xenia/kernel/util/shim_utils.h"
#include "xenia/kernel/xam/xam_private.h"
#include "xenia/ui/window.h"
#include "xenia/xbox.h"
namespace xe {
namespace kernel {
namespace xam {
SHIM_CALL XamIsUIActive_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
XELOGD("XamIsUIActive()");
SHIM_SET_RETURN_32(el::ModalForm::is_any_visible());
}
class MessageBoxWindow : public el::ModalForm {
public:
explicit MessageBoxWindow(xe::threading::Fence* fence)
: ModalForm([fence]() { fence->Signal(); }) {}
~MessageBoxWindow() override = default;
// TODO(benvanik): icon.
void Show(el::Element* root_element, std::wstring title, std::wstring message,
std::vector<std::wstring> buttons, uint32_t default_button,
uint32_t* out_chosen_button) {
title_ = std::move(title);
message_ = std::move(message);
buttons_ = std::move(buttons);
default_button_ = default_button;
out_chosen_button_ = out_chosen_button;
// In case we are cancelled, always set default button.
*out_chosen_button_ = default_button;
ModalForm::Show(root_element);
EnsureFocus();
}
protected:
void BuildUI() override {
using namespace el::dsl; // NOLINT(build/namespaces)
set_text(xe::to_string(title_));
auto button_bar = LayoutBoxNode().distribution_position(
LayoutDistributionPosition::kRightBottom);
for (int32_t i = 0; i < buttons_.size(); ++i) {
button_bar.child(ButtonNode(xe::to_string(buttons_[i]).c_str())
.id(100 + i)
.data(100 + i));
}
LoadNodeTree(
LayoutBoxNode()
.axis(Axis::kY)
.gravity(Gravity::kAll)
.position(LayoutPosition::kLeftTop)
.distribution(LayoutDistribution::kAvailable)
.distribution_position(LayoutDistributionPosition::kLeftTop)
.child(LabelNode(xe::to_string(message_).c_str()))
.child(button_bar));
auto default_button = GetElementById<el::Button>(100 + default_button_);
el::Button::set_auto_focus_state(true);
default_button->set_focus(el::FocusReason::kNavigation);
}
bool OnEvent(const el::Event& ev) override {
if (ev.target->IsOfType<el::Button>() &&
(ev.type == el::EventType::kClick ||
ev.special_key == el::SpecialKey::kEnter)) {
int data_value = ev.target->data.as_integer();
if (data_value) {
*out_chosen_button_ = data_value - 100;
}
Die();
return true;
}
return ModalForm::OnEvent(ev);
}
std::wstring title_;
std::wstring message_;
std::vector<std::wstring> buttons_;
uint32_t default_button_ = 0;
uint32_t* out_chosen_button_ = nullptr;
};
// http://www.se7ensins.com/forums/threads/working-xshowmessageboxui.844116/?jdfwkey=sb0vm
SHIM_CALL XamShowMessageBoxUI_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t user_index = SHIM_GET_ARG_32(0);
uint32_t title_ptr = SHIM_GET_ARG_32(1);
uint32_t text_ptr = SHIM_GET_ARG_32(2);
uint32_t button_count = SHIM_GET_ARG_32(3);
uint32_t button_ptrs = SHIM_GET_ARG_32(4);
uint32_t active_button = SHIM_GET_ARG_32(5);
uint32_t flags = SHIM_GET_ARG_32(6);
uint32_t result_ptr = SHIM_GET_ARG_32(7);
uint32_t overlapped_ptr = SHIM_GET_ARG_32(8);
auto title = xe::load_and_swap<std::wstring>(SHIM_MEM_ADDR(title_ptr));
auto text = xe::load_and_swap<std::wstring>(SHIM_MEM_ADDR(text_ptr));
std::vector<std::wstring> buttons;
std::wstring all_buttons;
for (uint32_t j = 0; j < button_count; ++j) {
uint32_t button_ptr = SHIM_MEM_32(button_ptrs + j * 4);
auto button = xe::load_and_swap<std::wstring>(SHIM_MEM_ADDR(button_ptr));
all_buttons.append(button);
if (j + 1 < button_count) {
all_buttons.append(L" | ");
}
buttons.push_back(button);
}
XELOGD(
"XamShowMessageBoxUI(%d, %.8X(%S), %.8X(%S), %d, %.8X(%S), %d, %X, %.8X, "
"%.8X)",
user_index, title_ptr, title.c_str(), text_ptr, text.c_str(),
button_count, button_ptrs, all_buttons.c_str(), active_button, flags,
result_ptr, overlapped_ptr);
uint32_t chosen_button;
if (FLAGS_headless) {
// Auto-pick the focused button.
chosen_button = active_button;
} else {
auto display_window = kernel_state->emulator()->display_window();
xe::threading::Fence fence;
display_window->loop()->PostSynchronous([&]() {
auto root_element = display_window->root_element();
auto window = new MessageBoxWindow(&fence);
switch (flags & 0xF) {
case 0:
// config.pszMainIcon = nullptr;
break;
case 1:
// config.pszMainIcon = TD_ERROR_ICON;
break;
case 2:
// config.pszMainIcon = TD_WARNING_ICON;
break;
case 3:
// config.pszMainIcon = TD_INFORMATION_ICON;
break;
}
window->Show(root_element, title, text, buttons, active_button,
&chosen_button);
});
fence.Wait();
}
SHIM_SET_MEM_32(result_ptr, chosen_button);
kernel_state->CompleteOverlappedImmediate(overlapped_ptr, X_ERROR_SUCCESS);
SHIM_SET_RETURN_32(X_ERROR_IO_PENDING);
}
class KeyboardInputWindow : public el::ModalForm {
public:
explicit KeyboardInputWindow(xe::threading::Fence* fence)
: ModalForm([fence]() { fence->Signal(); }) {}
~KeyboardInputWindow() override = default;
// TODO(benvanik): icon.
void Show(el::Element* root_element, std::wstring title,
std::wstring description, std::wstring default_text,
std::wstring* out_text, size_t max_length) {
title_ = std::move(title);
description_ = std::move(description);
default_text_ = std::move(default_text);
out_text_ = out_text;
max_length_ = max_length;
// Incase we're cancelled, set as the default text.
if (out_text) {
*out_text = default_text;
}
ModalForm::Show(root_element);
EnsureFocus();
}
protected:
void BuildUI() override {
using namespace el::dsl; // NOLINT(build/namespaces)
set_text(xe::to_string(title_));
LoadNodeTree(
LayoutBoxNode()
.axis(Axis::kY)
.gravity(Gravity::kAll)
.position(LayoutPosition::kLeftTop)
.min_width(300)
.distribution(LayoutDistribution::kAvailable)
.distribution_position(LayoutDistributionPosition::kLeftTop)
.child(LabelNode(xe::to_string(description_).c_str()))
.child(LayoutBoxNode()
.axis(Axis::kX)
.distribution(LayoutDistribution::kGravity)
.child(TextBoxNode()
.id("text_input")
.gravity(Gravity::kLeftRight)
.placeholder(xe::to_string(default_text_))
.min_width(150)
.autofocus(true)))
.child(LayoutBoxNode()
.distribution_position(
LayoutDistributionPosition::kRightBottom)
.child(ButtonNode("OK").id("ok_button"))
.child(ButtonNode("Cancel").id("cancel_button"))));
}
bool OnEvent(const el::Event& ev) override {
if (ev.special_key == el::SpecialKey::kEnter ||
(ev.target->id() == TBIDC("ok_button") &&
ev.type == el::EventType::kClick)) {
// Pressed enter or clicked OK
// Grab the text.
if (out_text_) {
*out_text_ =
xe::to_wstring(GetElementById<el::TextBox>("text_input")->text());
}
Die();
return true;
} else if (ev.target->id() == TBIDC("cancel_button") &&
ev.type == el::EventType::kClick) {
// Cancel.
Die();
return true;
}
return ModalForm::OnEvent(ev);
}
std::wstring title_;
std::wstring description_;
std::wstring default_text_;
std::wstring* out_text_ = nullptr;
size_t max_length_ = 0;
};
// http://www.se7ensins.com/forums/threads/release-how-to-use-xshowkeyboardui-release.906568/
dword_result_t XamShowKeyboardUI(dword_t r3, dword_t flags,
lpwstring_t default_text, lpwstring_t title,
lpwstring_t description, lpwstring_t buffer,
dword_t buffer_length,
pointer_t<XAM_OVERLAPPED> overlapped) {
// Unknown parameters. I've only seen zero.
assert_zero(r3);
if (!buffer) {
return X_ERROR_INVALID_PARAMETER;
}
if (FLAGS_headless) {
// Redirect default_text back into the buffer.
std::memset(buffer, 0, buffer_length * 2);
if (default_text) {
xe::store_and_swap<std::wstring>(buffer, default_text.value());
}
if (overlapped) {
kernel_state()->CompleteOverlappedImmediate(overlapped, X_ERROR_SUCCESS);
return X_ERROR_IO_PENDING;
} else {
return X_ERROR_SUCCESS;
}
}
std::wstring out_text;
auto display_window = kernel_state()->emulator()->display_window();
xe::threading::Fence fence;
display_window->loop()->PostSynchronous([&]() {
auto root_element = display_window->root_element();
auto window = new KeyboardInputWindow(&fence);
window->Show(root_element, title ? title.value() : L"",
description ? description.value() : L"",
default_text ? default_text.value() : L"", &out_text,
buffer_length);
});
fence.Wait();
// Zero the output buffer.
std::memset(buffer, 0, buffer_length * 2);
// Truncate the string.
out_text = out_text.substr(0, buffer_length - 1);
xe::store_and_swap<std::wstring>(buffer, out_text);
if (overlapped) {
kernel_state()->CompleteOverlappedImmediate(overlapped, X_ERROR_SUCCESS);
return X_ERROR_IO_PENDING;
} else {
return X_ERROR_SUCCESS;
}
}
DECLARE_XAM_EXPORT(XamShowKeyboardUI, ExportTag::kImplemented);
dword_result_t XamShowDeviceSelectorUI(dword_t user_index, dword_t content_type,
dword_t content_flags,
qword_t total_requested,
lpdword_t device_id_ptr,
pointer_t<XAM_OVERLAPPED> overlapped) {
// NOTE: 0xF00D0000 magic from xam_content.cc
switch (content_type) {
case 1: // save game
*device_id_ptr = 0xF00D0000 | 0x0001;
break;
case 2: // marketplace
*device_id_ptr = 0xF00D0000 | 0x0002;
break;
case 3: // title/publisher update?
*device_id_ptr = 0xF00D0000 | 0x0003;
break;
}
if (overlapped) {
kernel_state()->CompleteOverlappedImmediate(overlapped, X_ERROR_SUCCESS);
return X_ERROR_IO_PENDING;
} else {
return X_ERROR_SUCCESS;
}
}
DECLARE_XAM_EXPORT(XamShowDeviceSelectorUI, ExportTag::kImplemented);
class DirtyDiscWindow : public el::ModalForm {
public:
explicit DirtyDiscWindow(xe::threading::Fence* fence)
: ModalForm([fence]() { fence->Signal(); }) {}
~DirtyDiscWindow() override = default;
protected:
void BuildUI() override {
using namespace el::dsl; // NOLINT(build/namespaces)
set_text("Disc Read Error");
LoadNodeTree(
LayoutBoxNode()
.axis(Axis::kY)
.gravity(Gravity::kAll)
.position(LayoutPosition::kLeftTop)
.distribution(LayoutDistribution::kAvailable)
.distribution_position(LayoutDistributionPosition::kLeftTop)
.child(LabelNode(
"There's been an issue reading content from the game disc."))
.child(LabelNode(
"This is likely caused by bad or unimplemented file IO "
"calls."))
.child(LayoutBoxNode()
.distribution_position(
LayoutDistributionPosition::kRightBottom)
.child(ButtonNode("Exit").id("exit_button"))));
}
bool OnEvent(const el::Event& ev) override {
if (ev.target->id() == TBIDC("exit_button") &&
ev.type == el::EventType::kClick) {
Die();
return true;
}
return ModalForm::OnEvent(ev);
}
};
SHIM_CALL XamShowDirtyDiscErrorUI_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t user_index = SHIM_GET_ARG_32(0);
XELOGD("XamShowDirtyDiscErrorUI(%d)", user_index);
if (FLAGS_headless) {
assert_always();
exit(1);
return;
}
auto display_window = kernel_state->emulator()->display_window();
xe::threading::Fence fence;
display_window->loop()->PostSynchronous([&]() {
auto root_element = display_window->root_element();
auto window = new DirtyDiscWindow(&fence);
window->Show(root_element);
});
fence.Wait();
// This is death, and should never return.
// TODO(benvanik): cleaner exit.
exit(1);
}
void RegisterUIExports(xe::cpu::ExportResolver* export_resolver,
KernelState* kernel_state) {
SHIM_SET_MAPPING("xam.xex", XamIsUIActive, state);
SHIM_SET_MAPPING("xam.xex", XamShowMessageBoxUI, state);
SHIM_SET_MAPPING("xam.xex", XamShowDirtyDiscErrorUI, state);
}
} // namespace xam
} // namespace kernel
} // namespace xe

View File

@@ -0,0 +1,566 @@
/**
******************************************************************************
* 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 <cstring>
#include "xenia/base/logging.h"
#include "xenia/kernel/kernel_state.h"
#include "xenia/kernel/util/shim_utils.h"
#include "xenia/kernel/xam/xam_private.h"
#include "xenia/kernel/xenumerator.h"
#include "xenia/kernel/xthread.h"
#include "xenia/xbox.h"
namespace xe {
namespace kernel {
namespace xam {
SHIM_CALL XamUserGetXUID_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t user_index = SHIM_GET_ARG_32(0);
uint32_t unk = SHIM_GET_ARG_32(1);
uint32_t xuid_ptr = SHIM_GET_ARG_32(2);
XELOGD("XamUserGetXUID(%d, %.8X, %.8X)", user_index, unk, xuid_ptr);
if (user_index == 0) {
const auto& user_profile = kernel_state->user_profile();
if (xuid_ptr) {
SHIM_SET_MEM_64(xuid_ptr, user_profile->xuid());
}
SHIM_SET_RETURN_32(0);
} else {
SHIM_SET_RETURN_32(X_ERROR_NO_SUCH_USER);
}
}
SHIM_CALL XamUserGetSigninState_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t user_index = SHIM_GET_ARG_32(0);
XELOGD("XamUserGetSigninState(%d)", user_index);
// Yield, as some games spam this.
xe::threading::MaybeYield();
// Lie and say we are signed in, but local-only.
// This should keep games from asking us to sign in and also keep them
// from initializing the network.
if (user_index == 0 || (user_index & 0xFF) == 0xFF) {
const auto& user_profile = kernel_state->user_profile();
auto signin_state = user_profile->signin_state();
SHIM_SET_RETURN_32(signin_state);
} else {
SHIM_SET_RETURN_32(0);
}
}
SHIM_CALL XamUserGetSigninInfo_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t user_index = SHIM_GET_ARG_32(0);
uint32_t flags = SHIM_GET_ARG_32(1);
uint32_t info_ptr = SHIM_GET_ARG_32(2);
XELOGD("XamUserGetSigninInfo(%d, %.8X, %.8X)", user_index, flags, info_ptr);
if (user_index == 0) {
const auto& user_profile = kernel_state->user_profile();
SHIM_SET_MEM_64(info_ptr + 0, user_profile->xuid());
SHIM_SET_MEM_32(info_ptr + 8, 0); // maybe zero?
SHIM_SET_MEM_32(info_ptr + 12, user_profile->signin_state());
SHIM_SET_MEM_32(info_ptr + 16, 0); // ?
SHIM_SET_MEM_32(info_ptr + 20, 0); // ?
char* buffer = reinterpret_cast<char*>(SHIM_MEM_ADDR(info_ptr + 24));
std::strcpy(buffer, user_profile->name().data());
SHIM_SET_RETURN_32(0);
} else {
SHIM_SET_RETURN_32(X_ERROR_NO_SUCH_USER);
}
}
SHIM_CALL XamUserGetName_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t user_index = SHIM_GET_ARG_32(0);
uint32_t buffer_ptr = SHIM_GET_ARG_32(1);
uint32_t buffer_len = SHIM_GET_ARG_32(2);
XELOGD("XamUserGetName(%d, %.8X, %d)", user_index, buffer_ptr, buffer_len);
if (user_index == 0) {
const auto& user_profile = kernel_state->user_profile();
char* buffer = reinterpret_cast<char*>(SHIM_MEM_ADDR(buffer_ptr));
std::strcpy(buffer, user_profile->name().data());
SHIM_SET_RETURN_32(0);
} else {
SHIM_SET_RETURN_32(X_ERROR_NO_SUCH_USER);
}
}
typedef struct {
xe::be<uint32_t> setting_count;
xe::be<uint32_t> settings_ptr;
} X_USER_READ_PROFILE_SETTINGS;
static_assert_size(X_USER_READ_PROFILE_SETTINGS, 8);
typedef struct {
xe::be<uint32_t> from;
xe::be<uint32_t> unk04;
xe::be<uint32_t> user_index;
xe::be<uint32_t> unk0C;
xe::be<uint32_t> setting_id;
xe::be<uint32_t> unk14;
uint8_t setting_data[16];
} X_USER_READ_PROFILE_SETTING;
static_assert_size(X_USER_READ_PROFILE_SETTING, 40);
// http://freestyledash.googlecode.com/svn/trunk/Freestyle/Tools/Generic/xboxtools.cpp
SHIM_CALL XamUserReadProfileSettings_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t title_id = SHIM_GET_ARG_32(0);
uint32_t user_index = SHIM_GET_ARG_32(1);
uint32_t unk_0 = SHIM_GET_ARG_32(2);
uint32_t unk_1 = SHIM_GET_ARG_32(3);
uint32_t setting_count = SHIM_GET_ARG_32(4);
uint32_t setting_ids_ptr = SHIM_GET_ARG_32(5);
uint32_t buffer_size_ptr = SHIM_GET_ARG_32(6);
uint32_t buffer_ptr = SHIM_GET_ARG_32(7);
uint32_t overlapped_ptr = SHIM_GET_ARG_32(8);
uint32_t buffer_size = !buffer_size_ptr ? 0 : SHIM_MEM_32(buffer_size_ptr);
XELOGD(
"XamUserReadProfileSettings(%.8X, %d, %d, %d, %d, %.8X, %.8X(%d), %.8X, "
"%.8X)",
title_id, user_index, unk_0, unk_1, setting_count, setting_ids_ptr,
buffer_size_ptr, buffer_size, buffer_ptr, overlapped_ptr);
assert_zero(unk_0);
assert_zero(unk_1);
// TODO(gibbed): why is this a thing?
if (user_index == 255) {
user_index = 0;
}
// Title ID = 0 means us.
// 0xfffe07d1 = profile?
if (user_index) {
// Only support user 0.
if (overlapped_ptr) {
kernel_state->CompleteOverlappedImmediate(overlapped_ptr,
X_ERROR_NOT_FOUND);
SHIM_SET_RETURN_32(X_ERROR_IO_PENDING);
} else {
SHIM_SET_RETURN_32(X_ERROR_NOT_FOUND);
}
return;
}
const auto& user_profile = kernel_state->user_profile();
// First call asks for size (fill buffer_size_ptr).
// Second call asks for buffer contents with that size.
auto setting_ids = (xe::be<uint32_t>*)SHIM_MEM_ADDR(setting_ids_ptr);
// Compute required base size.
uint32_t base_size_needed = sizeof(X_USER_READ_PROFILE_SETTINGS);
base_size_needed += setting_count * sizeof(X_USER_READ_PROFILE_SETTING);
// Compute required extra size.
uint32_t size_needed = base_size_needed;
bool any_missing = false;
for (uint32_t n = 0; n < setting_count; ++n) {
uint32_t setting_id = setting_ids[n];
auto setting = user_profile->GetSetting(setting_id);
if (setting) {
auto extra_size = static_cast<uint32_t>(setting->extra_size());
size_needed += extra_size;
} else {
any_missing = true;
XELOGE("XamUserReadProfileSettings requested unimplemented setting %.8X",
setting_id);
}
}
if (any_missing) {
// TODO(benvanik): don't fail? most games don't even check!
if (overlapped_ptr) {
kernel_state->CompleteOverlappedImmediate(overlapped_ptr,
X_ERROR_INVALID_PARAMETER);
SHIM_SET_RETURN_32(X_ERROR_IO_PENDING);
} else {
SHIM_SET_RETURN_32(X_ERROR_INVALID_PARAMETER);
}
return;
}
SHIM_SET_MEM_32(buffer_size_ptr, size_needed);
if (!buffer_ptr || buffer_size < size_needed) {
if (overlapped_ptr) {
kernel_state->CompleteOverlappedImmediate(overlapped_ptr,
X_ERROR_INSUFFICIENT_BUFFER);
SHIM_SET_RETURN_32(X_ERROR_IO_PENDING);
} else {
SHIM_SET_RETURN_32(X_ERROR_INSUFFICIENT_BUFFER);
}
return;
}
auto out_header = reinterpret_cast<X_USER_READ_PROFILE_SETTINGS*>(
SHIM_MEM_ADDR(buffer_ptr));
out_header->setting_count = setting_count;
out_header->settings_ptr = buffer_ptr + 8;
auto out_setting = reinterpret_cast<X_USER_READ_PROFILE_SETTING*>(
SHIM_MEM_ADDR(out_header->settings_ptr));
size_t buffer_offset = base_size_needed;
for (uint32_t n = 0; n < setting_count; ++n) {
uint32_t setting_id = setting_ids[n];
auto setting = user_profile->GetSetting(setting_id);
std::memset(out_setting, 0, sizeof(X_USER_READ_PROFILE_SETTING));
out_setting->from = !setting ? 0 : setting->is_title_specific() ? 2 : 1;
out_setting->user_index = user_index;
out_setting->setting_id = setting_id;
if (setting) {
buffer_offset =
setting->Append(&out_setting->setting_data[0],
SHIM_MEM_ADDR(buffer_ptr), buffer_ptr, buffer_offset);
}
// TODO(benvanik): why did I do this?
/*else {
std::memset(&out_setting->setting_data[0], 0,
sizeof(out_setting->setting_data));
}*/
++out_setting;
}
if (overlapped_ptr) {
kernel_state->CompleteOverlappedImmediate(overlapped_ptr, X_ERROR_SUCCESS);
SHIM_SET_RETURN_32(X_ERROR_IO_PENDING);
} else {
SHIM_SET_RETURN_32(X_ERROR_SUCCESS);
}
}
SHIM_CALL XamUserWriteProfileSettings_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t user_index = SHIM_GET_ARG_32(0);
uint32_t unknown = SHIM_GET_ARG_32(1);
uint32_t setting_count = SHIM_GET_ARG_32(2);
uint32_t settings_ptr = SHIM_GET_ARG_32(3);
uint32_t overlapped_ptr = SHIM_GET_ARG_32(4);
XELOGD(
"XamUserWriteProfileSettings(%.8X, %d, %d, %d, %d, %.8X, %.8X(%d), %.8X, "
"%.8X)",
user_index, unknown, setting_count, settings_ptr, overlapped_ptr);
if (!setting_count || !settings_ptr) {
if (overlapped_ptr) {
kernel_state->CompleteOverlappedImmediate(overlapped_ptr,
X_ERROR_INVALID_PARAMETER);
SHIM_SET_RETURN_32(X_ERROR_IO_PENDING);
} else {
SHIM_SET_RETURN_32(X_ERROR_INVALID_PARAMETER);
}
return;
}
if (user_index == 255) {
user_index = 0;
}
if (user_index) {
// Only support user 0.
SHIM_SET_RETURN_32(X_ERROR_NOT_FOUND);
return;
}
// TODO(benvanik): update and save settings.
// const auto& user_profile = kernel_state->user_profile();
if (overlapped_ptr) {
kernel_state->CompleteOverlappedImmediate(overlapped_ptr, X_ERROR_SUCCESS);
SHIM_SET_RETURN_32(X_ERROR_IO_PENDING);
} else {
SHIM_SET_RETURN_32(X_ERROR_SUCCESS);
}
}
SHIM_CALL XamUserCheckPrivilege_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t user_index = SHIM_GET_ARG_32(0);
uint32_t mask = SHIM_GET_ARG_32(1);
uint32_t out_value_ptr = SHIM_GET_ARG_32(2);
XELOGD("XamUserCheckPrivilege(%d, %.8X, %.8X)", user_index, mask,
out_value_ptr);
// If we deny everything, games should hopefully not try to do stuff.
SHIM_SET_MEM_32(out_value_ptr, 0);
SHIM_SET_RETURN_32(X_ERROR_SUCCESS);
}
SHIM_CALL XamUserContentRestrictionGetFlags_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t user_index = SHIM_GET_ARG_32(0);
uint32_t out_flags_ptr = SHIM_GET_ARG_32(1);
XELOGD("XamUserContentRestrictionGetFlags(%d, %.8X)", user_index,
out_flags_ptr);
// No restrictions?
SHIM_SET_MEM_32(out_flags_ptr, 0);
SHIM_SET_RETURN_32(X_ERROR_SUCCESS);
}
SHIM_CALL XamUserContentRestrictionGetRating_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t user_index = SHIM_GET_ARG_32(0);
uint32_t unk1 = SHIM_GET_ARG_32(1);
uint32_t out_unk2_ptr = SHIM_GET_ARG_32(2);
uint32_t out_unk3_ptr = SHIM_GET_ARG_32(3);
XELOGD("XamUserContentRestrictionGetRating(%d, %.8X, %.8X, %.8X)", user_index,
unk1, out_unk2_ptr, out_unk3_ptr);
// Some games have special case paths for 3F that differ from the failure
// path, so my guess is that's 'don't care'.
SHIM_SET_MEM_32(out_unk2_ptr, 0x3F);
SHIM_SET_MEM_32(out_unk3_ptr, 0);
SHIM_SET_RETURN_32(X_ERROR_SUCCESS);
}
SHIM_CALL XamUserContentRestrictionCheckAccess_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t user_index = SHIM_GET_ARG_32(0);
uint32_t unk1 = SHIM_GET_ARG_32(1);
uint32_t unk2 = SHIM_GET_ARG_32(2);
uint32_t unk3 = SHIM_GET_ARG_32(3);
uint32_t unk4 = SHIM_GET_ARG_32(4);
uint32_t out_unk5_ptr = SHIM_GET_ARG_32(5);
uint32_t overlapped_ptr = SHIM_GET_ARG_32(6);
XELOGD(
"XamUserContentRestrictionCheckAccess(%d, %.8X, %.8X, %.8X, %.8X, %.8X, "
"%.8X)",
user_index, unk1, unk2, unk3, unk4, out_unk5_ptr, overlapped_ptr);
SHIM_SET_MEM_32(out_unk5_ptr, 1);
if (overlapped_ptr) {
// TODO(benvanik): does this need the access arg on it?
kernel_state->CompleteOverlappedImmediate(overlapped_ptr, X_ERROR_SUCCESS);
}
SHIM_SET_RETURN_32(X_ERROR_SUCCESS);
}
SHIM_CALL XamUserAreUsersFriends_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t user_index = SHIM_GET_ARG_32(0);
uint32_t unk1 = SHIM_GET_ARG_32(1);
uint32_t unk2 = SHIM_GET_ARG_32(2);
uint32_t out_value_ptr = SHIM_GET_ARG_32(3);
uint32_t overlapped_ptr = SHIM_GET_ARG_32(4);
XELOGD("XamUserAreUsersFriends(%d, %.8X, %.8X, %.8X, %.8X)", user_index, unk1,
unk2, out_value_ptr, overlapped_ptr);
if (user_index == 255) {
user_index = 0;
}
SHIM_SET_MEM_32(out_value_ptr, 0);
if (user_index) {
// Only support user 0.
SHIM_SET_RETURN_32(X_ERROR_NOT_LOGGED_ON);
return;
}
X_RESULT result = X_ERROR_SUCCESS;
const auto& user_profile = kernel_state->user_profile();
if (user_profile->signin_state() == 0) {
result = X_ERROR_NOT_LOGGED_ON;
}
// No friends!
SHIM_SET_MEM_32(out_value_ptr, 0);
if (overlapped_ptr) {
kernel_state->CompleteOverlappedImmediate(overlapped_ptr, result);
SHIM_SET_RETURN_32(X_ERROR_IO_PENDING);
} else {
SHIM_SET_RETURN_32(result);
}
}
SHIM_CALL XamShowSigninUI_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t unk_0 = SHIM_GET_ARG_32(0);
uint32_t unk_mask = SHIM_GET_ARG_32(1);
XELOGD("XamShowSigninUI(%d, %.8X)", unk_0, unk_mask);
// Mask values vary. Probably matching user types? Local/remote?
// Games seem to sit and loop until we trigger this notification.
kernel_state->BroadcastNotification(0x00000009, 0);
SHIM_SET_RETURN_32(X_ERROR_SUCCESS);
}
SHIM_CALL XamUserCreateAchievementEnumerator_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t title_id = SHIM_GET_ARG_32(0);
uint32_t user_index = SHIM_GET_ARG_32(1);
uint32_t xuid = SHIM_GET_ARG_32(2);
uint32_t flags = SHIM_GET_ARG_32(3);
uint32_t offset = SHIM_GET_ARG_32(4);
uint32_t count = SHIM_GET_ARG_32(5);
uint32_t buffer_size_ptr = SHIM_GET_ARG_32(6);
uint32_t handle_ptr = SHIM_GET_ARG_32(7);
XELOGD(
"XamUserCreateAchievementEnumerator(%.8X, %d, %.8X, %.8X, %d, %d, %.8X, "
"%.8X)",
title_id, user_index, xuid, flags, offset, count, buffer_size_ptr,
handle_ptr);
if (buffer_size_ptr) {
// TODO(benvanik): struct size/etc.
SHIM_SET_MEM_32(buffer_size_ptr, 64 * count);
}
auto e = new XStaticEnumerator(kernel_state, count, 64);
e->Initialize();
SHIM_SET_MEM_32(handle_ptr, e->handle());
SHIM_SET_RETURN_32(X_ERROR_SUCCESS);
}
SHIM_CALL XamParseGamerTileKey_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t key_ptr = SHIM_GET_ARG_32(0);
uint32_t out0_ptr = SHIM_GET_ARG_32(1);
uint32_t out1_ptr = SHIM_GET_ARG_32(2);
uint32_t out2_ptr = SHIM_GET_ARG_32(3);
XELOGD("XamParseGamerTileKey(%.8X, %.8X, %.8X, %.8X, %.8X, %.8X)", key_ptr,
out0_ptr, out1_ptr, out2_ptr);
SHIM_SET_MEM_32(out0_ptr, 0xC0DE0001);
SHIM_SET_MEM_32(out1_ptr, 0xC0DE0002);
SHIM_SET_MEM_32(out2_ptr, 0xC0DE0003);
SHIM_SET_RETURN_32(X_ERROR_SUCCESS);
}
SHIM_CALL XamReadTileToTexture_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t unk0 = SHIM_GET_ARG_32(0); // const?
uint32_t unk1 = SHIM_GET_ARG_32(1); // out0 from XamParseGamerTileKey
uint32_t unk2 = SHIM_GET_ARG_32(2); // some variant of out1/out2
uint32_t unk3 = SHIM_GET_ARG_32(3); // const?
uint32_t buffer_ptr = SHIM_GET_ARG_32(4);
uint32_t stride = SHIM_GET_ARG_32(5);
uint32_t height = SHIM_GET_ARG_32(6);
uint32_t overlapped_ptr = SHIM_GET_ARG_32(7);
XELOGD("XamReadTileToTexture(%.8X, %.8X, %.8X, %.8X, %.8X, %.8X)", unk0, unk1,
unk2, unk3, buffer_ptr, stride, height, overlapped_ptr);
if (overlapped_ptr) {
kernel_state->CompleteOverlappedImmediate(overlapped_ptr, X_ERROR_SUCCESS);
SHIM_SET_RETURN_32(X_ERROR_IO_PENDING);
} else {
SHIM_SET_RETURN_32(X_ERROR_SUCCESS);
}
}
SHIM_CALL XamWriteGamerTile_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t arg0 = SHIM_GET_ARG_32(0);
uint32_t arg1 = SHIM_GET_ARG_32(1);
uint32_t arg2 = SHIM_GET_ARG_32(2);
uint32_t arg3 = SHIM_GET_ARG_32(3);
uint32_t arg4 = SHIM_GET_ARG_32(4);
uint32_t overlapped_ptr = SHIM_GET_ARG_32(5);
XELOGD("XamWriteGamerTile(%.8X, %.8X, %.8X, %.8X, %.8X, %.8X)", arg0, arg1,
arg2, arg3, arg4, overlapped_ptr);
if (overlapped_ptr) {
kernel_state->CompleteOverlappedImmediate(overlapped_ptr, X_ERROR_SUCCESS);
SHIM_SET_RETURN_32(X_ERROR_IO_PENDING);
} else {
SHIM_SET_RETURN_32(X_ERROR_SUCCESS);
}
}
SHIM_CALL XamSessionCreateHandle_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t handle_ptr = SHIM_GET_ARG_32(0);
XELOGD("XamSessionCreateHandle(%.8X)", handle_ptr);
SHIM_SET_MEM_32(handle_ptr, 0xCAFEDEAD);
SHIM_SET_RETURN_32(X_ERROR_SUCCESS);
}
SHIM_CALL XamSessionRefObjByHandle_shim(PPCContext* ppc_context,
KernelState* kernel_state) {
uint32_t handle = SHIM_GET_ARG_32(0);
uint32_t obj_ptr = SHIM_GET_ARG_32(1);
XELOGD("XamSessionRefObjByHandle(%.8X, %.8X)", handle, obj_ptr);
assert_true(handle == 0xCAFEDEAD);
SHIM_SET_MEM_32(obj_ptr, 0);
SHIM_SET_RETURN_32(X_ERROR_FUNCTION_FAILED);
}
} // namespace xam
} // namespace kernel
} // namespace xe
void xe::kernel::xam::RegisterUserExports(
xe::cpu::ExportResolver* export_resolver, KernelState* kernel_state) {
SHIM_SET_MAPPING("xam.xex", XamUserGetXUID, state);
SHIM_SET_MAPPING("xam.xex", XamUserGetSigninState, state);
SHIM_SET_MAPPING("xam.xex", XamUserGetSigninInfo, state);
SHIM_SET_MAPPING("xam.xex", XamUserGetName, state);
SHIM_SET_MAPPING("xam.xex", XamUserReadProfileSettings, state);
SHIM_SET_MAPPING("xam.xex", XamUserWriteProfileSettings, state);
SHIM_SET_MAPPING("xam.xex", XamUserCheckPrivilege, state);
SHIM_SET_MAPPING("xam.xex", XamUserContentRestrictionGetFlags, state);
SHIM_SET_MAPPING("xam.xex", XamUserContentRestrictionGetRating, state);
SHIM_SET_MAPPING("xam.xex", XamUserContentRestrictionCheckAccess, state);
SHIM_SET_MAPPING("xam.xex", XamUserAreUsersFriends, state);
SHIM_SET_MAPPING("xam.xex", XamShowSigninUI, state);
SHIM_SET_MAPPING("xam.xex", XamUserCreateAchievementEnumerator, state);
SHIM_SET_MAPPING("xam.xex", XamParseGamerTileKey, state);
SHIM_SET_MAPPING("xam.xex", XamReadTileToTexture, state);
SHIM_SET_MAPPING("xam.xex", XamWriteGamerTile, state);
SHIM_SET_MAPPING("xam.xex", XamSessionCreateHandle, state);
SHIM_SET_MAPPING("xam.xex", XamSessionRefObjByHandle, state);
}

View File

@@ -0,0 +1,35 @@
/**
******************************************************************************
* 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/base/logging.h"
#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_video.h"
#include "xenia/xbox.h"
namespace xe {
namespace kernel {
namespace xam {
void XGetVideoMode(pointer_t<X_VIDEO_MODE> video_mode) {
// TODO(benvanik): actually check to see if these are the same.
xboxkrnl::VdQueryVideoMode(std::move(video_mode));
}
DECLARE_XAM_EXPORT(XGetVideoMode, ExportTag::kVideo | ExportTag::kSketchy);
dword_result_t XGetVideoCapabilities() { return 0; }
DECLARE_XAM_EXPORT(XGetVideoCapabilities, ExportTag::kVideo | ExportTag::kStub);
void RegisterVideoExports(xe::cpu::ExportResolver* export_resolver,
KernelState* kernel_state) {}
} // namespace xam
} // namespace kernel
} // namespace xe

View File

@@ -0,0 +1,46 @@
/**
******************************************************************************
* 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/base/logging.h"
#include "xenia/kernel/kernel_state.h"
#include "xenia/kernel/util/shim_utils.h"
#include "xenia/kernel/xam/xam_private.h"
#include "xenia/xbox.h"
namespace xe {
namespace kernel {
namespace xam {
dword_result_t XamVoiceIsActiveProcess() {
// Returning 0 here will short-circuit a bunch of voice stuff.
return 0;
}
DECLARE_XAM_EXPORT(XamVoiceIsActiveProcess, ExportTag::kStub);
dword_result_t XamVoiceCreate(unknown_t unk1, // 0
unknown_t unk2, // 0xF
lpdword_t out_voice_ptr) {
// Null out the ptr.
out_voice_ptr.Zero();
return X_ERROR_ACCESS_DENIED;
}
DECLARE_XAM_EXPORT(XamVoiceCreate, ExportTag::kStub);
dword_result_t XamVoiceClose(lpunknown_t voice_ptr) { return 0; }
DECLARE_XAM_EXPORT(XamVoiceClose, ExportTag::kStub);
dword_result_t XamVoiceHeadsetPresent(lpunknown_t voice_ptr) { return 0; }
DECLARE_XAM_EXPORT(XamVoiceHeadsetPresent, ExportTag::kStub);
void RegisterVoiceExports(xe::cpu::ExportResolver* export_resolver,
KernelState* kernel_state) {}
} // namespace xam
} // namespace kernel
} // namespace xe