Enough XMP to play (fake) play media.

This commit is contained in:
Ben Vanik 2015-01-11 00:28:24 -08:00
parent f5e12eba76
commit b6b27e621a
6 changed files with 343 additions and 67 deletions

View File

@ -305,6 +305,20 @@ template <>
inline void store_and_swap<double>(void* mem, double 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>
struct be {

View File

@ -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,
uint32_t buffer_ptr,
size_t buffer_length) {
uint32_t buffer_length) {
const auto& it = app_lookup_.find(app_id);
if (it == app_lookup_.end()) {
return X_ERROR_NOT_FOUND;

View File

@ -44,7 +44,7 @@ class XAppManager {
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, size_t buffer_length);
uint32_t buffer_ptr, uint32_t buffer_length);
private:
std::vector<std::unique_ptr<XApp>> apps_;

View File

@ -9,81 +9,204 @@
#include <xenia/kernel/apps/xmp_app.h>
#include <poly/threading.h>
namespace xe {
namespace kernel {
namespace apps {
XXMPApp::XXMPApp(KernelState* kernel_state)
: XApp(kernel_state, 0xFA),
status_(Status::kStopped),
state_(State::kIdle),
disabled_(0),
unknown_state1_(0),
unknown_state2_(0),
playback_mode_(PlaybackMode::kUnknown),
repeat_mode_(RepeatMode::kUnknown),
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
// here to keep from starving real threads.
Sleep(1);
XELOGD("XMPGetStatus(%.8X)", status_ptr);
poly::store_and_swap<uint32_t>(membase_ + status_ptr,
static_cast<uint32_t>(status_));
OnStatusChanged();
XELOGD("XMPGetStatus(%.8X)", state_ptr);
poly::store_and_swap<uint32_t>(membase_ + state_ptr,
static_cast<uint32_t>(state_));
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;
}
X_RESULT XXMPApp::XMPContinue() {
XELOGD("XMPContinue()");
if (status_ == Status::kPaused) {
status_ = Status::kPlaying;
if (state_ == State::kPaused) {
state_ = State::kPlaying;
}
OnStatusChanged();
OnStateChanged();
return X_ERROR_SUCCESS;
}
X_RESULT XXMPApp::XMPStop(uint32_t unk) {
assert_zero(unk);
XELOGD("XMPStop(%.8X)", unk);
status_ = Status::kStopped;
OnStatusChanged();
active_playlist_ = nullptr; // ?
active_song_index_ = 0;
state_ = State::kIdle;
OnStateChanged();
return X_ERROR_SUCCESS;
}
X_RESULT XXMPApp::XMPPause() {
XELOGD("XMPPause()");
if (status_ == Status::kPlaying) {
status_ = Status::kPaused;
if (state_ == State::kPlaying) {
state_ = State::kPaused;
}
OnStatusChanged();
OnStateChanged();
return X_ERROR_SUCCESS;
}
X_RESULT XXMPApp::XMPNext() {
XELOGD("XMPNext()");
status_ = Status::kPlaying;
OnStatusChanged();
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 XXMPApp::XMPPrevious() {
XELOGD("XMPPrevious()");
status_ = Status::kPlaying;
OnStatusChanged();
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 XXMPApp::OnStatusChanged() {
kernel_state_->BroadcastNotification(kMsgStatusChanged,
static_cast<uint32_t>(status_));
void XXMPApp::OnStateChanged() {
kernel_state_->BroadcastNotification(kMsgStateChanged,
static_cast<uint32_t>(state_));
}
X_RESULT XXMPApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
uint32_t buffer_length) {
// NOTE: buffer_length may be zero or valid.
// http://freestyledash.googlecode.com/svn-history/r1/trunk/Freestyle/Scenes/Media/Music/ScnMusic.cpp
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: {
assert_true(!buffer_length || buffer_length == 4);
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);
uint32_t xmp_client =
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 unk2 = poly::load_and_swap<uint32_t>(membase_ + buffer_ptr + 8);
uint32_t playback_mode =
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 =
poly::load_and_swap<uint32_t>(membase_ + buffer_ptr + 12);
assert_true(xmp_client == 0x00000002);
XELOGD("XMPSetState?(%.8X, %.8X, %.8X)", unk1, unk2, flags);
unknown_state1_ = unk1;
unknown_state2_ = unk2;
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(kMsgStateChanged, 0);
kernel_state_->BroadcastNotification(kMsgPlaybackBehaviorChanged, 0);
return X_ERROR_SUCCESS;
}
case 0x00070009: {
assert_true(!buffer_length || buffer_length == 8);
uint32_t xmp_client =
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
assert_true(xmp_client == 0x00000002);
return XMPGetStatus(status_ptr);
return XMPGetStatus(state_ptr);
}
case 0x0007000B: {
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>(
membase_ + buffer_ptr + 4); // out ptr to 4b - floating point
assert_true(xmp_client == 0x00000002);
XELOGD("XMPGetFloat?(%.8X)", float_ptr);
poly::store_and_swap<float>(membase_ + float_ptr, unknown_float_);
XELOGD("XMPGetVolume(%.8X)", float_ptr);
poly::store_and_swap<float>(membase_ + float_ptr, volume_);
return X_ERROR_SUCCESS;
}
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);
float float_value = poly::load_and_swap<float>(membase_ + buffer_ptr + 4);
assert_true(xmp_client == 0x00000002);
XELOGD("XMPSetFloat?(%g)", float_value);
unknown_float_ = float_value;
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 =
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: {
assert_true(!buffer_length || buffer_length == 12);
uint32_t xmp_client =
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 disabled =
uint32_t enabled =
poly::load_and_swap<uint32_t>(membase_ + buffer_ptr + 8);
assert_true(xmp_client == 0x00000002);
assert_zero(unk1);
XELOGD("XMPSetDisabled(%.8X, %.8X)", unk1, disabled);
disabled_ = disabled;
XELOGD("XMPSetEnabled(%.8X, %.8X)", unk1, enabled);
disabled_ = enabled;
if (disabled_) {
XMPStop(0);
}
kernel_state_->BroadcastNotification(kMsgDisableChanged, disabled_);
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>(
membase_ + buffer_ptr + 8); // out ptr to 4b - expect 1 (to skip)
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_ + 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;
}
case 0x00070029: {
assert_true(!buffer_length || buffer_length == 16);
uint32_t xmp_client =
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);
uint32_t unk2_ptr =
uint32_t repeat_mode_ptr =
poly::load_and_swap<uint32_t>(membase_ + buffer_ptr + 8);
uint32_t unk3_ptr =
poly::load_and_swap<uint32_t>(membase_ + buffer_ptr + 12);
assert_true(xmp_client == 0x00000002);
XELOGD("XMPGetState?(%.8X, %.8X, %.8X)", unk1_ptr, unk2_ptr, unk3_ptr);
poly::store_and_swap<uint32_t>(membase_ + unk1_ptr, unknown_state1_);
poly::store_and_swap<uint32_t>(membase_ + unk2_ptr, unknown_state2_);
XELOGD("XMPGetPlaybackBehavior(%.8X, %.8X, %.8X)", playback_mode_ptr,
repeat_mode_ptr, unk3_ptr);
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_);
return X_ERROR_SUCCESS;
}
case 0x0007002E: {
assert_true(!buffer_length || buffer_length == 12);
// 00000002 00000003 20049ce0
uint32_t xmp_client = poly::load_and_swap<uint32_t>(
membase_ + buffer_ptr + 0); // 0x00000002
uint32_t unk1 = poly::load_and_swap<uint32_t>(membase_ + buffer_ptr +
4); // 0x00000003
uint32_t unk_ptr =
// Query of size for XamAlloc - the result of the alloc is passed to
// 0x0007000D.
uint32_t xmp_client =
poly::load_and_swap<uint32_t>(membase_ + buffer_ptr + 0);
uint32_t song_count =
poly::load_and_swap<uint32_t>(membase_ + buffer_ptr + 4);
uint32_t size_ptr =
poly::load_and_swap<uint32_t>(membase_ + buffer_ptr + 8);
assert_true(xmp_client == 0x00000002);
//
break;
// We don't use the storage, so just fudge the number.
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",

View File

@ -10,6 +10,12 @@
#ifndef 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/kernel/app.h>
#include <xenia/kernel/kernel_state.h>
@ -18,18 +24,59 @@ namespace xe {
namespace kernel {
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 {
public:
enum class Status : uint32_t {
kStopped = 0,
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;
};
XXMPApp(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();
@ -40,18 +87,25 @@ class XXMPApp : public XApp {
uint32_t buffer_length) override;
private:
static const uint32_t kMsgStatusChanged = 0xA000001;
static const uint32_t kMsgStateChanged = 0xA000002;
static const uint32_t kMsgDisableChanged = 0xA000003;
static const uint32_t kMsgStateChanged = 0x0A000001;
static const uint32_t kMsgPlaybackBehaviorChanged = 0x0A000002;
static const uint32_t kMsgDisableChanged = 0x0A000003;
void OnStatusChanged();
void OnStateChanged();
Status status_;
State state_;
uint32_t disabled_;
uint32_t unknown_state1_;
uint32_t unknown_state2_;
PlaybackMode playback_mode_;
RepeatMode repeat_mode_;
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

View File

@ -36,7 +36,7 @@ void XNotifyListener::Initialize(uint64_t mask) {
void XNotifyListener::EnqueueNotification(XNotificationID id, uint32_t data) {
// 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;
}