Shuffling kernel/.
This commit is contained in:
60
src/xenia/kernel/xam/app_manager.cc
Normal file
60
src/xenia/kernel/xam/app_manager.cc
Normal 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
|
||||
66
src/xenia/kernel/xam/app_manager.h
Normal file
66
src/xenia/kernel/xam/app_manager.h
Normal 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_
|
||||
100
src/xenia/kernel/xam/apps/xgi_app.cc
Normal file
100
src/xenia/kernel/xam/apps/xgi_app.cc
Normal 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
|
||||
34
src/xenia/kernel/xam/apps/xgi_app.h
Normal file
34
src/xenia/kernel/xam/apps/xgi_app.h
Normal 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_
|
||||
63
src/xenia/kernel/xam/apps/xlivebase_app.cc
Normal file
63
src/xenia/kernel/xam/apps/xlivebase_app.cc
Normal 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
|
||||
34
src/xenia/kernel/xam/apps/xlivebase_app.h
Normal file
34
src/xenia/kernel/xam/apps/xlivebase_app.h
Normal 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_
|
||||
427
src/xenia/kernel/xam/apps/xmp_app.cc
Normal file
427
src/xenia/kernel/xam/apps/xmp_app.cc
Normal 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
|
||||
116
src/xenia/kernel/xam/apps/xmp_app.h
Normal file
116
src/xenia/kernel/xam/apps/xmp_app.h
Normal 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_
|
||||
256
src/xenia/kernel/xam/content_manager.cc
Normal file
256
src/xenia/kernel/xam/content_manager.cc
Normal 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
|
||||
104
src/xenia/kernel/xam/content_manager.h
Normal file
104
src/xenia/kernel/xam/content_manager.h
Normal 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_
|
||||
103
src/xenia/kernel/xam/user_profile.cc
Normal file
103
src/xenia/kernel/xam/user_profile.cc
Normal 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
|
||||
201
src/xenia/kernel/xam/user_profile.h
Normal file
201
src/xenia/kernel/xam/user_profile.h
Normal 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_
|
||||
43
src/xenia/kernel/xam/xam_avatar.cc
Normal file
43
src/xenia/kernel/xam/xam_avatar.cc
Normal 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
|
||||
524
src/xenia/kernel/xam/xam_content.cc
Normal file
524
src/xenia/kernel/xam/xam_content.cc
Normal 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
|
||||
259
src/xenia/kernel/xam/xam_info.cc
Normal file
259
src/xenia/kernel/xam/xam_info.cc
Normal 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
|
||||
272
src/xenia/kernel/xam/xam_input.cc
Normal file
272
src/xenia/kernel/xam/xam_input.cc
Normal 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
|
||||
72
src/xenia/kernel/xam/xam_module.cc
Normal file
72
src/xenia/kernel/xam/xam_module.cc
Normal 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
|
||||
49
src/xenia/kernel/xam/xam_module.h
Normal file
49
src/xenia/kernel/xam/xam_module.h
Normal 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_
|
||||
132
src/xenia/kernel/xam/xam_msg.cc
Normal file
132
src/xenia/kernel/xam/xam_msg.cc
Normal 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
|
||||
748
src/xenia/kernel/xam/xam_net.cc
Normal file
748
src/xenia/kernel/xam/xam_net.cc
Normal 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
|
||||
115
src/xenia/kernel/xam/xam_notify.cc
Normal file
115
src/xenia/kernel/xam/xam_notify.cc
Normal 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, ¶m);
|
||||
} else {
|
||||
// Just get next.
|
||||
dequeued = listener->DequeueNotification(&id, ¶m);
|
||||
}
|
||||
|
||||
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
|
||||
42
src/xenia/kernel/xam/xam_nui.cc
Normal file
42
src/xenia/kernel/xam/xam_nui.cc
Normal 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
|
||||
24
src/xenia/kernel/xam/xam_ordinals.h
Normal file
24
src/xenia/kernel/xam/xam_ordinals.h
Normal 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_
|
||||
53
src/xenia/kernel/xam/xam_private.h
Normal file
53
src/xenia/kernel/xam/xam_private.h
Normal 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_
|
||||
1422
src/xenia/kernel/xam/xam_table.inc
Normal file
1422
src/xenia/kernel/xam/xam_table.inc
Normal file
File diff suppressed because it is too large
Load Diff
419
src/xenia/kernel/xam/xam_ui.cc
Normal file
419
src/xenia/kernel/xam/xam_ui.cc
Normal 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
|
||||
566
src/xenia/kernel/xam/xam_user.cc
Normal file
566
src/xenia/kernel/xam/xam_user.cc
Normal 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);
|
||||
}
|
||||
35
src/xenia/kernel/xam/xam_video.cc
Normal file
35
src/xenia/kernel/xam/xam_video.cc
Normal 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
|
||||
46
src/xenia/kernel/xam/xam_voice.cc
Normal file
46
src/xenia/kernel/xam/xam_voice.cc
Normal 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
|
||||
Reference in New Issue
Block a user