Enough XMP to play (fake) play media.
This commit is contained in:
parent
f5e12eba76
commit
b6b27e621a
|
@ -305,6 +305,20 @@ template <>
|
||||||
inline void store_and_swap<double>(void* mem, double value) {
|
inline void store_and_swap<double>(void* mem, double value) {
|
||||||
*reinterpret_cast<double*>(mem) = byte_swap(value);
|
*reinterpret_cast<double*>(mem) = byte_swap(value);
|
||||||
}
|
}
|
||||||
|
template <>
|
||||||
|
inline void store_and_swap<std::string>(void* mem, std::string value) {
|
||||||
|
for (auto i = 0; i < value.size(); ++i) {
|
||||||
|
poly::store_and_swap<uint8_t>(reinterpret_cast<uint8_t*>(mem) + i,
|
||||||
|
value[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
template <>
|
||||||
|
inline void store_and_swap<std::wstring>(void* mem, std::wstring value) {
|
||||||
|
for (auto i = 0; i < value.size(); ++i) {
|
||||||
|
poly::store_and_swap<uint16_t>(reinterpret_cast<uint16_t*>(mem) + i,
|
||||||
|
value[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct be {
|
struct be {
|
||||||
|
|
|
@ -37,7 +37,7 @@ X_RESULT XAppManager::DispatchMessageSync(uint32_t app_id, uint32_t message,
|
||||||
|
|
||||||
X_RESULT XAppManager::DispatchMessageAsync(uint32_t app_id, uint32_t message,
|
X_RESULT XAppManager::DispatchMessageAsync(uint32_t app_id, uint32_t message,
|
||||||
uint32_t buffer_ptr,
|
uint32_t buffer_ptr,
|
||||||
size_t buffer_length) {
|
uint32_t buffer_length) {
|
||||||
const auto& it = app_lookup_.find(app_id);
|
const auto& it = app_lookup_.find(app_id);
|
||||||
if (it == app_lookup_.end()) {
|
if (it == app_lookup_.end()) {
|
||||||
return X_ERROR_NOT_FOUND;
|
return X_ERROR_NOT_FOUND;
|
||||||
|
|
|
@ -44,7 +44,7 @@ class XAppManager {
|
||||||
X_RESULT DispatchMessageSync(uint32_t app_id, uint32_t message,
|
X_RESULT DispatchMessageSync(uint32_t app_id, uint32_t message,
|
||||||
uint32_t buffer_ptr, uint32_t buffer_length);
|
uint32_t buffer_ptr, uint32_t buffer_length);
|
||||||
X_RESULT DispatchMessageAsync(uint32_t app_id, uint32_t message,
|
X_RESULT DispatchMessageAsync(uint32_t app_id, uint32_t message,
|
||||||
uint32_t buffer_ptr, size_t buffer_length);
|
uint32_t buffer_ptr, uint32_t buffer_length);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<std::unique_ptr<XApp>> apps_;
|
std::vector<std::unique_ptr<XApp>> apps_;
|
||||||
|
|
|
@ -9,81 +9,204 @@
|
||||||
|
|
||||||
#include <xenia/kernel/apps/xmp_app.h>
|
#include <xenia/kernel/apps/xmp_app.h>
|
||||||
|
|
||||||
|
#include <poly/threading.h>
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace kernel {
|
namespace kernel {
|
||||||
namespace apps {
|
namespace apps {
|
||||||
|
|
||||||
XXMPApp::XXMPApp(KernelState* kernel_state)
|
XXMPApp::XXMPApp(KernelState* kernel_state)
|
||||||
: XApp(kernel_state, 0xFA),
|
: XApp(kernel_state, 0xFA),
|
||||||
status_(Status::kStopped),
|
state_(State::kIdle),
|
||||||
disabled_(0),
|
disabled_(0),
|
||||||
unknown_state1_(0),
|
playback_mode_(PlaybackMode::kUnknown),
|
||||||
unknown_state2_(0),
|
repeat_mode_(RepeatMode::kUnknown),
|
||||||
unknown_flags_(0),
|
unknown_flags_(0),
|
||||||
unknown_float_(0.0f) {}
|
volume_(0.0f),
|
||||||
|
active_playlist_(nullptr),
|
||||||
|
active_song_index_(0),
|
||||||
|
next_playlist_handle_(1),
|
||||||
|
next_song_handle_(1) {}
|
||||||
|
|
||||||
X_RESULT XXMPApp::XMPGetStatus(uint32_t status_ptr) {
|
X_RESULT XXMPApp::XMPGetStatus(uint32_t state_ptr) {
|
||||||
// Some stupid games will hammer this on a thread - induce a delay
|
// Some stupid games will hammer this on a thread - induce a delay
|
||||||
// here to keep from starving real threads.
|
// here to keep from starving real threads.
|
||||||
Sleep(1);
|
Sleep(1);
|
||||||
|
|
||||||
XELOGD("XMPGetStatus(%.8X)", status_ptr);
|
XELOGD("XMPGetStatus(%.8X)", state_ptr);
|
||||||
poly::store_and_swap<uint32_t>(membase_ + status_ptr,
|
poly::store_and_swap<uint32_t>(membase_ + state_ptr,
|
||||||
static_cast<uint32_t>(status_));
|
static_cast<uint32_t>(state_));
|
||||||
OnStatusChanged();
|
return X_ERROR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
X_RESULT XXMPApp::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,
|
||||||
|
poly::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 = membase_ + songs_ptr + (i * 36);
|
||||||
|
song->file_path = poly::load_and_swap<std::wstring>(
|
||||||
|
membase_ + poly::load_and_swap<uint32_t>(song_base + 0));
|
||||||
|
song->name = poly::load_and_swap<std::wstring>(
|
||||||
|
membase_ + poly::load_and_swap<uint32_t>(song_base + 4));
|
||||||
|
song->artist = poly::load_and_swap<std::wstring>(
|
||||||
|
membase_ + poly::load_and_swap<uint32_t>(song_base + 8));
|
||||||
|
song->album = poly::load_and_swap<std::wstring>(
|
||||||
|
membase_ + poly::load_and_swap<uint32_t>(song_base + 12));
|
||||||
|
song->album_artist = poly::load_and_swap<std::wstring>(
|
||||||
|
membase_ + poly::load_and_swap<uint32_t>(song_base + 16));
|
||||||
|
song->genre = poly::load_and_swap<std::wstring>(
|
||||||
|
membase_ + poly::load_and_swap<uint32_t>(song_base + 20));
|
||||||
|
song->track_number = poly::load_and_swap<uint32_t>(song_base + 24);
|
||||||
|
song->duration_ms = poly::load_and_swap<uint32_t>(song_base + 28);
|
||||||
|
song->format = static_cast<Song::Format>(
|
||||||
|
poly::load_and_swap<uint32_t>(song_base + 32));
|
||||||
|
if (out_song_handles) {
|
||||||
|
poly::store_and_swap<uint32_t>(membase_ + out_song_handles + (i * 4),
|
||||||
|
song->handle);
|
||||||
|
}
|
||||||
|
playlist->songs.emplace_back(std::move(song));
|
||||||
|
}
|
||||||
|
poly::store_and_swap<uint32_t>(membase_ + out_playlist_handle,
|
||||||
|
playlist->handle);
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
playlists_.insert({playlist->handle, playlist.get()});
|
||||||
|
playlist.release();
|
||||||
|
return X_ERROR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
X_RESULT XXMPApp::XMPDeleteTitlePlaylist(uint32_t playlist_handle) {
|
||||||
|
XELOGD("XMPDeleteTitlePlaylist(%.8X)", playlist_handle);
|
||||||
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
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 XXMPApp::XMPPlayTitlePlaylist(uint32_t playlist_handle,
|
||||||
|
uint32_t song_handle) {
|
||||||
|
XELOGD("XMPPlayTitlePlaylist(%.8X, %.8X)", playlist_handle, song_handle);
|
||||||
|
Playlist* playlist = nullptr;
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
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();
|
||||||
return X_ERROR_SUCCESS;
|
return X_ERROR_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
X_RESULT XXMPApp::XMPContinue() {
|
X_RESULT XXMPApp::XMPContinue() {
|
||||||
XELOGD("XMPContinue()");
|
XELOGD("XMPContinue()");
|
||||||
if (status_ == Status::kPaused) {
|
if (state_ == State::kPaused) {
|
||||||
status_ = Status::kPlaying;
|
state_ = State::kPlaying;
|
||||||
}
|
}
|
||||||
OnStatusChanged();
|
OnStateChanged();
|
||||||
return X_ERROR_SUCCESS;
|
return X_ERROR_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
X_RESULT XXMPApp::XMPStop(uint32_t unk) {
|
X_RESULT XXMPApp::XMPStop(uint32_t unk) {
|
||||||
assert_zero(unk);
|
assert_zero(unk);
|
||||||
XELOGD("XMPStop(%.8X)", unk);
|
XELOGD("XMPStop(%.8X)", unk);
|
||||||
status_ = Status::kStopped;
|
active_playlist_ = nullptr; // ?
|
||||||
OnStatusChanged();
|
active_song_index_ = 0;
|
||||||
|
state_ = State::kIdle;
|
||||||
|
OnStateChanged();
|
||||||
return X_ERROR_SUCCESS;
|
return X_ERROR_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
X_RESULT XXMPApp::XMPPause() {
|
X_RESULT XXMPApp::XMPPause() {
|
||||||
XELOGD("XMPPause()");
|
XELOGD("XMPPause()");
|
||||||
if (status_ == Status::kPlaying) {
|
if (state_ == State::kPlaying) {
|
||||||
status_ = Status::kPaused;
|
state_ = State::kPaused;
|
||||||
}
|
}
|
||||||
OnStatusChanged();
|
OnStateChanged();
|
||||||
return X_ERROR_SUCCESS;
|
return X_ERROR_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
X_RESULT XXMPApp::XMPNext() {
|
X_RESULT XXMPApp::XMPNext() {
|
||||||
XELOGD("XMPNext()");
|
XELOGD("XMPNext()");
|
||||||
status_ = Status::kPlaying;
|
if (!active_playlist_) {
|
||||||
OnStatusChanged();
|
return X_ERROR_NOT_FOUND;
|
||||||
|
}
|
||||||
|
state_ = State::kPlaying;
|
||||||
|
active_song_index_ =
|
||||||
|
(active_song_index_ + 1) % active_playlist_->songs.size();
|
||||||
|
OnStateChanged();
|
||||||
return X_ERROR_SUCCESS;
|
return X_ERROR_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
X_RESULT XXMPApp::XMPPrevious() {
|
X_RESULT XXMPApp::XMPPrevious() {
|
||||||
XELOGD("XMPPrevious()");
|
XELOGD("XMPPrevious()");
|
||||||
status_ = Status::kPlaying;
|
if (!active_playlist_) {
|
||||||
OnStatusChanged();
|
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;
|
return X_ERROR_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
void XXMPApp::OnStatusChanged() {
|
void XXMPApp::OnStateChanged() {
|
||||||
kernel_state_->BroadcastNotification(kMsgStatusChanged,
|
kernel_state_->BroadcastNotification(kMsgStateChanged,
|
||||||
static_cast<uint32_t>(status_));
|
static_cast<uint32_t>(state_));
|
||||||
}
|
}
|
||||||
|
|
||||||
X_RESULT XXMPApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
|
X_RESULT XXMPApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
|
||||||
uint32_t buffer_length) {
|
uint32_t buffer_length) {
|
||||||
// NOTE: buffer_length may be zero or valid.
|
// NOTE: buffer_length may be zero or valid.
|
||||||
// http://freestyledash.googlecode.com/svn-history/r1/trunk/Freestyle/Scenes/Media/Music/ScnMusic.cpp
|
|
||||||
switch (message) {
|
switch (message) {
|
||||||
|
case 0x00070002: {
|
||||||
|
assert_true(!buffer_length || buffer_length == 12);
|
||||||
|
uint32_t xmp_client =
|
||||||
|
poly::load_and_swap<uint32_t>(membase_ + buffer_ptr + 0);
|
||||||
|
uint32_t playlist_handle =
|
||||||
|
poly::load_and_swap<uint32_t>(membase_ + buffer_ptr + 4);
|
||||||
|
uint32_t song_handle =
|
||||||
|
poly::load_and_swap<uint32_t>(membase_ + buffer_ptr + 8); // 0?
|
||||||
|
assert_true(xmp_client == 0x00000002);
|
||||||
|
return XMPPlayTitlePlaylist(playlist_handle, song_handle);
|
||||||
|
}
|
||||||
case 0x00070003: {
|
case 0x00070003: {
|
||||||
assert_true(!buffer_length || buffer_length == 4);
|
assert_true(!buffer_length || buffer_length == 4);
|
||||||
uint32_t xmp_client =
|
uint32_t xmp_client =
|
||||||
|
@ -124,26 +247,29 @@ X_RESULT XXMPApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
|
||||||
assert_true(!buffer_length || buffer_length == 16);
|
assert_true(!buffer_length || buffer_length == 16);
|
||||||
uint32_t xmp_client =
|
uint32_t xmp_client =
|
||||||
poly::load_and_swap<uint32_t>(membase_ + buffer_ptr + 0);
|
poly::load_and_swap<uint32_t>(membase_ + buffer_ptr + 0);
|
||||||
uint32_t unk1 = poly::load_and_swap<uint32_t>(membase_ + buffer_ptr + 4);
|
uint32_t playback_mode =
|
||||||
uint32_t unk2 = poly::load_and_swap<uint32_t>(membase_ + buffer_ptr + 8);
|
poly::load_and_swap<uint32_t>(membase_ + buffer_ptr + 4);
|
||||||
|
uint32_t repeat_mode =
|
||||||
|
poly::load_and_swap<uint32_t>(membase_ + buffer_ptr + 8);
|
||||||
uint32_t flags =
|
uint32_t flags =
|
||||||
poly::load_and_swap<uint32_t>(membase_ + buffer_ptr + 12);
|
poly::load_and_swap<uint32_t>(membase_ + buffer_ptr + 12);
|
||||||
assert_true(xmp_client == 0x00000002);
|
assert_true(xmp_client == 0x00000002);
|
||||||
XELOGD("XMPSetState?(%.8X, %.8X, %.8X)", unk1, unk2, flags);
|
XELOGD("XMPSetPlaybackBehavior(%.8X, %.8X, %.8X)", playback_mode,
|
||||||
unknown_state1_ = unk1;
|
repeat_mode, flags);
|
||||||
unknown_state2_ = unk2;
|
playback_mode_ = static_cast<PlaybackMode>(playback_mode);
|
||||||
|
repeat_mode_ = static_cast<RepeatMode>(repeat_mode);
|
||||||
unknown_flags_ = flags;
|
unknown_flags_ = flags;
|
||||||
kernel_state_->BroadcastNotification(kMsgStateChanged, 0);
|
kernel_state_->BroadcastNotification(kMsgPlaybackBehaviorChanged, 0);
|
||||||
return X_ERROR_SUCCESS;
|
return X_ERROR_SUCCESS;
|
||||||
}
|
}
|
||||||
case 0x00070009: {
|
case 0x00070009: {
|
||||||
assert_true(!buffer_length || buffer_length == 8);
|
assert_true(!buffer_length || buffer_length == 8);
|
||||||
uint32_t xmp_client =
|
uint32_t xmp_client =
|
||||||
poly::load_and_swap<uint32_t>(membase_ + buffer_ptr + 0);
|
poly::load_and_swap<uint32_t>(membase_ + buffer_ptr + 0);
|
||||||
uint32_t status_ptr = poly::load_and_swap<uint32_t>(
|
uint32_t state_ptr = poly::load_and_swap<uint32_t>(
|
||||||
membase_ + buffer_ptr + 4); // out ptr to 4b - expect 0
|
membase_ + buffer_ptr + 4); // out ptr to 4b - expect 0
|
||||||
assert_true(xmp_client == 0x00000002);
|
assert_true(xmp_client == 0x00000002);
|
||||||
return XMPGetStatus(status_ptr);
|
return XMPGetStatus(state_ptr);
|
||||||
}
|
}
|
||||||
case 0x0007000B: {
|
case 0x0007000B: {
|
||||||
assert_true(!buffer_length || buffer_length == 8);
|
assert_true(!buffer_length || buffer_length == 8);
|
||||||
|
@ -152,8 +278,8 @@ X_RESULT XXMPApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
|
||||||
uint32_t float_ptr = poly::load_and_swap<uint32_t>(
|
uint32_t float_ptr = poly::load_and_swap<uint32_t>(
|
||||||
membase_ + buffer_ptr + 4); // out ptr to 4b - floating point
|
membase_ + buffer_ptr + 4); // out ptr to 4b - floating point
|
||||||
assert_true(xmp_client == 0x00000002);
|
assert_true(xmp_client == 0x00000002);
|
||||||
XELOGD("XMPGetFloat?(%.8X)", float_ptr);
|
XELOGD("XMPGetVolume(%.8X)", float_ptr);
|
||||||
poly::store_and_swap<float>(membase_ + float_ptr, unknown_float_);
|
poly::store_and_swap<float>(membase_ + float_ptr, volume_);
|
||||||
return X_ERROR_SUCCESS;
|
return X_ERROR_SUCCESS;
|
||||||
}
|
}
|
||||||
case 0x0007000C: {
|
case 0x0007000C: {
|
||||||
|
@ -162,21 +288,96 @@ X_RESULT XXMPApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
|
||||||
poly::load_and_swap<uint32_t>(membase_ + buffer_ptr + 0);
|
poly::load_and_swap<uint32_t>(membase_ + buffer_ptr + 0);
|
||||||
float float_value = poly::load_and_swap<float>(membase_ + buffer_ptr + 4);
|
float float_value = poly::load_and_swap<float>(membase_ + buffer_ptr + 4);
|
||||||
assert_true(xmp_client == 0x00000002);
|
assert_true(xmp_client == 0x00000002);
|
||||||
XELOGD("XMPSetFloat?(%g)", float_value);
|
XELOGD("XMPSetVolume(%g)", float_value);
|
||||||
unknown_float_ = float_value;
|
volume_ = float_value;
|
||||||
return X_ERROR_SUCCESS;
|
return X_ERROR_SUCCESS;
|
||||||
}
|
}
|
||||||
|
case 0x0007000D: {
|
||||||
|
assert_true(!buffer_length || buffer_length == 36);
|
||||||
|
uint32_t xmp_client =
|
||||||
|
poly::load_and_swap<uint32_t>(membase_ + buffer_ptr + 0);
|
||||||
|
uint32_t dummy_alloc_ptr =
|
||||||
|
poly::load_and_swap<uint32_t>(membase_ + buffer_ptr + 4);
|
||||||
|
uint32_t dummy_alloc_size =
|
||||||
|
poly::load_and_swap<uint32_t>(membase_ + buffer_ptr + 8);
|
||||||
|
uint32_t songs_ptr =
|
||||||
|
poly::load_and_swap<uint32_t>(membase_ + buffer_ptr + 12);
|
||||||
|
uint32_t song_count =
|
||||||
|
poly::load_and_swap<uint32_t>(membase_ + buffer_ptr + 16);
|
||||||
|
uint32_t playlist_name_ptr =
|
||||||
|
poly::load_and_swap<uint32_t>(membase_ + buffer_ptr + 20);
|
||||||
|
uint32_t flags =
|
||||||
|
poly::load_and_swap<uint32_t>(membase_ + buffer_ptr + 24);
|
||||||
|
uint32_t song_handles_ptr =
|
||||||
|
poly::load_and_swap<uint32_t>(membase_ + buffer_ptr + 28); // 0?
|
||||||
|
uint32_t playlist_handle_ptr =
|
||||||
|
poly::load_and_swap<uint32_t>(membase_ + buffer_ptr + 32);
|
||||||
|
assert_true(xmp_client == 0x00000002);
|
||||||
|
auto playlist_name =
|
||||||
|
poly::load_and_swap<std::wstring>(membase_ + playlist_name_ptr);
|
||||||
|
// dummy_alloc_ptr is the result of a XamAlloc of dummy_alloc_size.
|
||||||
|
assert_true(dummy_alloc_size == song_count * 128);
|
||||||
|
return XMPCreateTitlePlaylist(songs_ptr, song_count, playlist_name_ptr,
|
||||||
|
playlist_name, flags, song_handles_ptr,
|
||||||
|
playlist_handle_ptr);
|
||||||
|
}
|
||||||
|
case 0x0007000E: {
|
||||||
|
assert_true(!buffer_length || buffer_length == 12);
|
||||||
|
uint32_t xmp_client =
|
||||||
|
poly::load_and_swap<uint32_t>(membase_ + buffer_ptr + 0);
|
||||||
|
uint32_t unk_ptr =
|
||||||
|
poly::load_and_swap<uint32_t>(membase_ + buffer_ptr + 4); // 0
|
||||||
|
uint32_t info_ptr =
|
||||||
|
poly::load_and_swap<uint32_t>(membase_ + buffer_ptr + 8);
|
||||||
|
assert_true(xmp_client == 0x00000002);
|
||||||
|
assert_zero(unk_ptr);
|
||||||
|
XELOGE("XMPGetInfo?(%.8X, %.8X)", unk_ptr, info_ptr);
|
||||||
|
if (!active_playlist_) {
|
||||||
|
return X_ERROR_NOT_FOUND;
|
||||||
|
}
|
||||||
|
auto& song = active_playlist_->songs[active_song_index_];
|
||||||
|
poly::store_and_swap<uint32_t>(membase_ + info_ptr + 0, song->handle);
|
||||||
|
poly::store_and_swap<std::wstring>(membase_ + info_ptr + 4 + 572 + 0,
|
||||||
|
song->name);
|
||||||
|
poly::store_and_swap<std::wstring>(membase_ + info_ptr + 4 + 572 + 40,
|
||||||
|
song->artist);
|
||||||
|
poly::store_and_swap<std::wstring>(membase_ + info_ptr + 4 + 572 + 80,
|
||||||
|
song->album);
|
||||||
|
poly::store_and_swap<std::wstring>(membase_ + info_ptr + 4 + 572 + 120,
|
||||||
|
song->album_artist);
|
||||||
|
poly::store_and_swap<std::wstring>(membase_ + info_ptr + 4 + 572 + 160,
|
||||||
|
song->genre);
|
||||||
|
poly::store_and_swap<uint32_t>(membase_ + info_ptr + 4 + 572 + 200,
|
||||||
|
song->track_number);
|
||||||
|
poly::store_and_swap<uint32_t>(membase_ + info_ptr + 4 + 572 + 204,
|
||||||
|
song->duration_ms);
|
||||||
|
poly::store_and_swap<uint32_t>(membase_ + info_ptr + 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 =
|
||||||
|
poly::load_and_swap<uint32_t>(membase_ + buffer_ptr + 0);
|
||||||
|
uint32_t playlist_handle =
|
||||||
|
poly::load_and_swap<uint32_t>(membase_ + buffer_ptr + 4);
|
||||||
|
assert_true(xmp_client == 0x00000002);
|
||||||
|
return XMPDeleteTitlePlaylist(playlist_handle);
|
||||||
|
}
|
||||||
case 0x0007001A: {
|
case 0x0007001A: {
|
||||||
assert_true(!buffer_length || buffer_length == 12);
|
assert_true(!buffer_length || buffer_length == 12);
|
||||||
uint32_t xmp_client =
|
uint32_t xmp_client =
|
||||||
poly::load_and_swap<uint32_t>(membase_ + buffer_ptr + 0);
|
poly::load_and_swap<uint32_t>(membase_ + buffer_ptr + 0);
|
||||||
uint32_t unk1 = poly::load_and_swap<uint32_t>(membase_ + buffer_ptr + 4);
|
uint32_t unk1 = poly::load_and_swap<uint32_t>(membase_ + buffer_ptr + 4);
|
||||||
uint32_t disabled =
|
uint32_t enabled =
|
||||||
poly::load_and_swap<uint32_t>(membase_ + buffer_ptr + 8);
|
poly::load_and_swap<uint32_t>(membase_ + buffer_ptr + 8);
|
||||||
assert_true(xmp_client == 0x00000002);
|
assert_true(xmp_client == 0x00000002);
|
||||||
assert_zero(unk1);
|
assert_zero(unk1);
|
||||||
XELOGD("XMPSetDisabled(%.8X, %.8X)", unk1, disabled);
|
XELOGD("XMPSetEnabled(%.8X, %.8X)", unk1, enabled);
|
||||||
disabled_ = disabled;
|
disabled_ = enabled;
|
||||||
|
if (disabled_) {
|
||||||
|
XMPStop(0);
|
||||||
|
}
|
||||||
kernel_state_->BroadcastNotification(kMsgDisableChanged, disabled_);
|
kernel_state_->BroadcastNotification(kMsgDisableChanged, disabled_);
|
||||||
return X_ERROR_SUCCESS;
|
return X_ERROR_SUCCESS;
|
||||||
}
|
}
|
||||||
|
@ -189,40 +390,47 @@ X_RESULT XXMPApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
|
||||||
uint32_t disabled_ptr = poly::load_and_swap<uint32_t>(
|
uint32_t disabled_ptr = poly::load_and_swap<uint32_t>(
|
||||||
membase_ + buffer_ptr + 8); // out ptr to 4b - expect 1 (to skip)
|
membase_ + buffer_ptr + 8); // out ptr to 4b - expect 1 (to skip)
|
||||||
assert_true(xmp_client == 0x00000002);
|
assert_true(xmp_client == 0x00000002);
|
||||||
XELOGD("XMPGetDisabled(%.8X, %.8X)", unk_ptr, disabled_ptr);
|
XELOGD("XMPGetEnabled(%.8X, %.8X)", unk_ptr, disabled_ptr);
|
||||||
poly::store_and_swap<uint32_t>(membase_ + unk_ptr, 0);
|
poly::store_and_swap<uint32_t>(membase_ + unk_ptr, 0);
|
||||||
poly::store_and_swap<uint32_t>(membase_ + disabled_ptr, disabled_);
|
poly::store_and_swap<uint32_t>(membase_ + disabled_ptr, disabled_);
|
||||||
|
// Atrain spawns a thread 82437FD0 to call this in a tight loop forever.
|
||||||
|
poly::threading::Sleep(std::chrono::milliseconds(10));
|
||||||
return X_ERROR_SUCCESS;
|
return X_ERROR_SUCCESS;
|
||||||
}
|
}
|
||||||
case 0x00070029: {
|
case 0x00070029: {
|
||||||
assert_true(!buffer_length || buffer_length == 16);
|
assert_true(!buffer_length || buffer_length == 16);
|
||||||
uint32_t xmp_client =
|
uint32_t xmp_client =
|
||||||
poly::load_and_swap<uint32_t>(membase_ + buffer_ptr + 0);
|
poly::load_and_swap<uint32_t>(membase_ + buffer_ptr + 0);
|
||||||
uint32_t unk1_ptr =
|
uint32_t playback_mode_ptr =
|
||||||
poly::load_and_swap<uint32_t>(membase_ + buffer_ptr + 4);
|
poly::load_and_swap<uint32_t>(membase_ + buffer_ptr + 4);
|
||||||
uint32_t unk2_ptr =
|
uint32_t repeat_mode_ptr =
|
||||||
poly::load_and_swap<uint32_t>(membase_ + buffer_ptr + 8);
|
poly::load_and_swap<uint32_t>(membase_ + buffer_ptr + 8);
|
||||||
uint32_t unk3_ptr =
|
uint32_t unk3_ptr =
|
||||||
poly::load_and_swap<uint32_t>(membase_ + buffer_ptr + 12);
|
poly::load_and_swap<uint32_t>(membase_ + buffer_ptr + 12);
|
||||||
assert_true(xmp_client == 0x00000002);
|
assert_true(xmp_client == 0x00000002);
|
||||||
XELOGD("XMPGetState?(%.8X, %.8X, %.8X)", unk1_ptr, unk2_ptr, unk3_ptr);
|
XELOGD("XMPGetPlaybackBehavior(%.8X, %.8X, %.8X)", playback_mode_ptr,
|
||||||
poly::store_and_swap<uint32_t>(membase_ + unk1_ptr, unknown_state1_);
|
repeat_mode_ptr, unk3_ptr);
|
||||||
poly::store_and_swap<uint32_t>(membase_ + unk2_ptr, unknown_state2_);
|
poly::store_and_swap<uint32_t>(membase_ + playback_mode_ptr,
|
||||||
|
static_cast<uint32_t>(playback_mode_));
|
||||||
|
poly::store_and_swap<uint32_t>(membase_ + repeat_mode_ptr,
|
||||||
|
static_cast<uint32_t>(repeat_mode_));
|
||||||
poly::store_and_swap<uint32_t>(membase_ + unk3_ptr, unknown_flags_);
|
poly::store_and_swap<uint32_t>(membase_ + unk3_ptr, unknown_flags_);
|
||||||
return X_ERROR_SUCCESS;
|
return X_ERROR_SUCCESS;
|
||||||
}
|
}
|
||||||
case 0x0007002E: {
|
case 0x0007002E: {
|
||||||
assert_true(!buffer_length || buffer_length == 12);
|
assert_true(!buffer_length || buffer_length == 12);
|
||||||
// 00000002 00000003 20049ce0
|
// Query of size for XamAlloc - the result of the alloc is passed to
|
||||||
uint32_t xmp_client = poly::load_and_swap<uint32_t>(
|
// 0x0007000D.
|
||||||
membase_ + buffer_ptr + 0); // 0x00000002
|
uint32_t xmp_client =
|
||||||
uint32_t unk1 = poly::load_and_swap<uint32_t>(membase_ + buffer_ptr +
|
poly::load_and_swap<uint32_t>(membase_ + buffer_ptr + 0);
|
||||||
4); // 0x00000003
|
uint32_t song_count =
|
||||||
uint32_t unk_ptr =
|
poly::load_and_swap<uint32_t>(membase_ + buffer_ptr + 4);
|
||||||
|
uint32_t size_ptr =
|
||||||
poly::load_and_swap<uint32_t>(membase_ + buffer_ptr + 8);
|
poly::load_and_swap<uint32_t>(membase_ + buffer_ptr + 8);
|
||||||
assert_true(xmp_client == 0x00000002);
|
assert_true(xmp_client == 0x00000002);
|
||||||
//
|
// We don't use the storage, so just fudge the number.
|
||||||
break;
|
poly::store_and_swap<uint32_t>(membase_ + size_ptr, song_count * 128);
|
||||||
|
return X_ERROR_SUCCESS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
XELOGE("Unimplemented XMsg message app=%.8X, msg=%.8X, arg1=%.8X, arg2=%.8X",
|
XELOGE("Unimplemented XMsg message app=%.8X, msg=%.8X, arg1=%.8X, arg2=%.8X",
|
||||||
|
|
|
@ -10,6 +10,12 @@
|
||||||
#ifndef XENIA_KERNEL_XBOXKRNL_APPS_XMP_APP_H_
|
#ifndef XENIA_KERNEL_XBOXKRNL_APPS_XMP_APP_H_
|
||||||
#define XENIA_KERNEL_XBOXKRNL_APPS_XMP_APP_H_
|
#define XENIA_KERNEL_XBOXKRNL_APPS_XMP_APP_H_
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include <xenia/common.h>
|
#include <xenia/common.h>
|
||||||
#include <xenia/kernel/app.h>
|
#include <xenia/kernel/app.h>
|
||||||
#include <xenia/kernel/kernel_state.h>
|
#include <xenia/kernel/kernel_state.h>
|
||||||
|
@ -18,18 +24,59 @@ namespace xe {
|
||||||
namespace kernel {
|
namespace kernel {
|
||||||
namespace apps {
|
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 XXMPApp : public XApp {
|
class XXMPApp : public XApp {
|
||||||
public:
|
public:
|
||||||
enum class Status : uint32_t {
|
enum class State : uint32_t {
|
||||||
kStopped = 0,
|
kIdle = 0,
|
||||||
kPlaying = 1,
|
kPlaying = 1,
|
||||||
kPaused = 2,
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
XXMPApp(KernelState* kernel_state);
|
XXMPApp(KernelState* kernel_state);
|
||||||
|
|
||||||
X_RESULT XMPGetStatus(uint32_t status_ptr);
|
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 XMPContinue();
|
||||||
X_RESULT XMPStop(uint32_t unk);
|
X_RESULT XMPStop(uint32_t unk);
|
||||||
X_RESULT XMPPause();
|
X_RESULT XMPPause();
|
||||||
|
@ -40,18 +87,25 @@ class XXMPApp : public XApp {
|
||||||
uint32_t buffer_length) override;
|
uint32_t buffer_length) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static const uint32_t kMsgStatusChanged = 0xA000001;
|
static const uint32_t kMsgStateChanged = 0x0A000001;
|
||||||
static const uint32_t kMsgStateChanged = 0xA000002;
|
static const uint32_t kMsgPlaybackBehaviorChanged = 0x0A000002;
|
||||||
static const uint32_t kMsgDisableChanged = 0xA000003;
|
static const uint32_t kMsgDisableChanged = 0x0A000003;
|
||||||
|
|
||||||
void OnStatusChanged();
|
void OnStateChanged();
|
||||||
|
|
||||||
Status status_;
|
State state_;
|
||||||
uint32_t disabled_;
|
uint32_t disabled_;
|
||||||
uint32_t unknown_state1_;
|
PlaybackMode playback_mode_;
|
||||||
uint32_t unknown_state2_;
|
RepeatMode repeat_mode_;
|
||||||
uint32_t unknown_flags_;
|
uint32_t unknown_flags_;
|
||||||
float unknown_float_;
|
float volume_;
|
||||||
|
Playlist* active_playlist_;
|
||||||
|
int active_song_index_;
|
||||||
|
|
||||||
|
std::mutex mutex_;
|
||||||
|
std::unordered_map<uint32_t, Playlist*> playlists_;
|
||||||
|
uint32_t next_playlist_handle_;
|
||||||
|
uint32_t next_song_handle_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace apps
|
} // namespace apps
|
||||||
|
|
|
@ -36,7 +36,7 @@ void XNotifyListener::Initialize(uint64_t mask) {
|
||||||
|
|
||||||
void XNotifyListener::EnqueueNotification(XNotificationID id, uint32_t data) {
|
void XNotifyListener::EnqueueNotification(XNotificationID id, uint32_t data) {
|
||||||
// Ignore if the notification doesn't match our mask.
|
// Ignore if the notification doesn't match our mask.
|
||||||
if ((mask_ & uint64_t(1 << ((id >> 25) + 1))) == 0) {
|
if ((mask_ & uint64_t(1 << (id >> 25))) == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue