Shuffling kernel/.
This commit is contained in:
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_
|
||||
Reference in New Issue
Block a user