[XConfig] Implementation of XConfig

This commit is contained in:
Gliniak
2026-05-17 21:30:10 +02:00
committed by Radosław Gliński
parent f88bfbe41e
commit 99ea6da18a
31 changed files with 2020 additions and 846 deletions

View File

@@ -0,0 +1,426 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2026 Xenia Canary. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/app/emulator_window.h"
#include "xenia/app/profile_dialogs.h"
#include "xenia/base/png_utils.h"
#include "xenia/base/system.h"
#include "xenia/kernel/util/shim_utils.h"
#include "xenia/kernel/xam/xam_ui.h"
#include "xenia/ui/file_picker.h"
#include "xenia/app/console_settings_dialog.h"
#include "third_party/fmt/include/fmt/format.h"
namespace xe {
namespace app {
static const std::map<uint32_t, std::string> kLanguageMap = {
{1, "English"}, {2, "Japanese"},
{3, "German"}, {4, "French"},
{5, "Spanish"}, {6, "Italian"},
{7, "Korean"}, {8, "Traditional Chinese"},
{9, "Portuguese"}, {10, "Simplified Chinese"},
{11, "Polish"}, {12, "Russian"}};
static const std::map<uint8_t, std::string> kCountryMap = {
{1, "AE"}, {2, "AL"}, {3, "AM"}, {4, "AR"}, {5, "AT"},
{6, "AU"}, {7, "AZ"}, {8, "BE"}, {9, "BG"}, {10, "BH"},
{11, "BN"}, {12, "BO"}, {13, "BR"}, {14, "BY"}, {15, "BZ"},
{16, "CA"}, {18, "CH"}, {19, "CL"}, {20, "CN"}, {21, "CO"},
{22, "CR"}, {23, "CZ"}, {24, "DE"}, {25, "DK"}, {26, "DO"},
{27, "DZ"}, {28, "EC"}, {29, "EE"}, {30, "EG"}, {31, "ES"},
{32, "FI"}, {33, "FO"}, {34, "FR"}, {35, "GB"}, {36, "GE"},
{37, "GR"}, {38, "GT"}, {39, "HK"}, {40, "HN"}, {41, "HR"},
{42, "HU"}, {43, "ID"}, {44, "IE"}, {45, "IL"}, {46, "IN"},
{47, "IQ"}, {48, "IR"}, {49, "IS"}, {50, "IT"}, {51, "JM"},
{52, "JO"}, {53, "JP"}, {54, "KE"}, {55, "KG"}, {56, "KR"},
{57, "KW"}, {58, "KZ"}, {59, "LB"}, {60, "LI"}, {61, "LT"},
{62, "LU"}, {63, "LV"}, {64, "LY"}, {65, "MA"}, {66, "MC"},
{67, "MK"}, {68, "MN"}, {69, "MO"}, {70, "MV"}, {71, "MX"},
{72, "MY"}, {73, "NI"}, {74, "NL"}, {75, "NO"}, {76, "NZ"},
{77, "OM"}, {78, "PA"}, {79, "PE"}, {80, "PH"}, {81, "PK"},
{82, "PL"}, {83, "PR"}, {84, "PT"}, {85, "PY"}, {86, "QA"},
{87, "RO"}, {88, "RU"}, {89, "SA"}, {90, "SE"}, {91, "SG"},
{92, "SI"}, {93, "SK"}, {95, "SV"}, {96, "SY"}, {97, "TH"},
{98, "TN"}, {99, "TR"}, {100, "TT"}, {101, "TW"}, {102, "UA"},
{103, "US"}, {104, "UY"}, {105, "UZ"}, {106, "VE"}, {107, "VN"},
{108, "YE"}, {109, "ZA"},
};
static const std::map<uint32_t, std::string> kAVRegion = {
{0x00400100, "NTSC"},
{0x00400200, "NTSC-J"},
{0x00400400, "PAL"},
{0x00800300, "PAL 50Hz"}};
template <typename T>
void DrawCombobox(ImGuiIO& io, std::string_view combobox_name,
const std::map<T, std::string>& options, xe::be<T>& value) {
const char* preview = "Unknown";
for (const auto& opt : options) {
if (opt.first == value.get()) {
preview = opt.second.data();
break;
}
}
if (ImGui::BeginCombo(combobox_name.data(), preview)) {
for (const auto& opt : options) {
const bool selected = (opt.first == value.get());
if (ImGui::Selectable(opt.second.data(), selected)) {
value = opt.first;
}
if (selected) {
ImGui::SetItemDefaultFocus();
}
}
ImGui::EndCombo();
}
}
void DrawResolutionCombobox(ImGuiIO& io,
const std::span<const kernel::Resolution> options,
xe::be<int32_t>& resolution,
xe::be<uint32_t>& video_flags) {
const char* preview = "Unknown";
for (const auto& opt : options) {
if (opt.to_host() == resolution.get()) {
preview = opt.name_.c_str();
break;
}
}
if (ImGui::BeginCombo("Resolution", preview)) {
for (const auto& opt : options) {
const bool selected = (opt.to_host() == resolution.get());
if (ImGui::Selectable(opt.name_.c_str(), selected)) {
resolution = opt.to_host();
if (opt.is_widescreen()) {
video_flags = video_flags | static_cast<uint32_t>(
kernel::X_VIDEO_FLAGS::Widescreen);
} else {
video_flags = video_flags & ~static_cast<uint32_t>(
kernel::X_VIDEO_FLAGS::Widescreen);
}
}
if (selected) {
ImGui::SetItemDefaultFocus();
}
}
ImGui::EndCombo();
}
}
void DrawTimezoneCombobox(ImGuiIO& io, kernel::XConfigData* xdata) {
const kernel::TimeZone current_tz = {
xdata->user.time_zone_bias, xdata->user.tz_std_name,
xdata->user.tz_dlt_name, xdata->user.tz_std_date,
xdata->user.tz_dlt_date, xdata->user.tz_std_bias,
xdata->user.tz_dlt_bias};
size_t index = 0x19; // Index of GMT+00
auto it = std::find(kernel::kTimezones.cbegin(), kernel::kTimezones.cend(),
current_tz);
if (it != kernel::kTimezones.cend()) {
index = std::distance(kernel::kTimezones.cbegin(), it);
}
if (ImGui::BeginCombo("Timezone", kernel::kTimezones[index].name.c_str())) {
for (size_t i = 0; i < kernel::kTimezones.size(); ++i) {
const bool is_selected =
(kernel::kTimezones[i] == kernel::kTimezones[index]);
if (ImGui::Selectable(kernel::kTimezones[i].name.c_str(), is_selected)) {
index = i;
// Update timezone values
const auto& tz = kernel::kTimezones[index];
xdata->user.time_zone_bias = tz.timezone_bias;
memcpy(xdata->user.tz_std_name.data(), tz.tz_std_name.data(), 4);
memcpy(xdata->user.tz_dlt_name.data(), tz.tz_dlt_name.data(), 4);
memcpy(xdata->user.tz_std_date.data(), tz.tz_std_date.data(), 4);
memcpy(xdata->user.tz_dlt_date.data(), tz.tz_dlt_date.data(), 4);
xdata->user.tz_std_bias = tz.tz_std_bias;
xdata->user.tz_dlt_bias = tz.tz_dlt_bias;
}
if (is_selected) {
ImGui::SetItemDefaultFocus();
}
}
ImGui::EndCombo();
}
}
template <typename T>
static bool FlagCheckbox(const char* label, xe::be<T>& field, T value) {
bool set = (field.get() & value) != 0;
if (ImGui::Checkbox(label, &set)) {
if (set) {
field = field.get() | value;
} else {
field = field.get() & ~value;
}
return true;
}
return false;
}
template <typename T>
static void ByteArray(const char* label, T* array, size_t count) {
for (size_t i = 0; i < count; i++) {
ImGui::SetNextItemWidth(ImGui::CalcTextSize("CC_").x);
ImGui::InputScalar(fmt::format("##{}{}", label, i).c_str(),
ImGuiDataType_U8, &array[i], nullptr, nullptr, "%02X");
if (i + 1 < count) {
ImGui::SameLine();
}
}
}
void GroupBox(const char* label, auto draw_fn) {
ImDrawList* draw_list = ImGui::GetWindowDrawList();
ImVec2 text_size = ImGui::CalcTextSize(label);
float border_offset = text_size.y * 0.5f;
ImGuiStyle& style = ImGui::GetStyle();
// Push content down so it doesn't overlap the label
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + border_offset);
ImGui::Indent(8.0f);
ImGui::BeginGroup();
ImGui::Dummy(ImVec2(ImGui::GetContentRegionAvail().x - 16.0f, 0));
ImGui::Spacing();
draw_fn();
ImGui::Spacing();
ImGui::EndGroup();
ImGui::Unindent(8.0f);
// NOW we know the rect
ImVec2 rect_min = ImGui::GetItemRectMin();
ImVec2 rect_max = ImGui::GetItemRectMax();
// Expand rect a bit for padding
rect_min.x -= 8.0f;
rect_max.x += 8.0f;
rect_max.y += style.ItemSpacing.y;
// Move the top of the border up to account for label offset
rect_min.y -= border_offset;
ImU32 border_color = ImGui::GetColorU32(ImGuiCol_Border);
float label_start = rect_min.x + 8.0f;
float label_end = label_start + text_size.x + 4.0f;
// Top edge with gap for label
draw_list->AddLine(ImVec2(rect_min.x, rect_min.y),
ImVec2(label_start, rect_min.y), border_color);
draw_list->AddLine(ImVec2(label_end, rect_min.y),
ImVec2(rect_max.x, rect_min.y), border_color);
// Left, right, bottom
draw_list->AddLine(rect_min, ImVec2(rect_min.x, rect_max.y), border_color);
draw_list->AddLine(ImVec2(rect_max.x, rect_min.y),
ImVec2(rect_max.x, rect_max.y), border_color);
draw_list->AddLine(ImVec2(rect_min.x, rect_max.y), rect_max, border_color);
// Label
draw_list->AddText(ImVec2(label_start + 2.0f, rect_min.y - border_offset),
ImGui::GetColorU32(ImGuiCol_Text), label);
ImGui::SetCursorPosY(rect_max.y + style.ItemSpacing.y);
}
void ConsoleSettingsDialog::OnDraw(ImGuiIO& io) {
ImGui::SetNextWindowPos(ImVec2(20, 20), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSize(ImVec2(20, 20), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowBgAlpha(0.90f);
bool dialog_open = true;
if (!ImGui::Begin("Console settings", &dialog_open,
ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_AlwaysAutoResize |
ImGuiWindowFlags_NoMove)) {
ImGui::End();
return;
}
if (!dialog_open) {
Close();
ImGui::End();
emulator_window_.ToggleConsoleSettingsDialog();
return;
}
if (ImGui::BeginTabBar("##Categories")) {
if (ImGui::BeginTabItem("User")) {
ImGui::BeginDisabled(emulator_window_.emulator()->is_title_open());
ImGui::Dummy(ImVec2(2.f, 2.f));
GroupBox("Time", [&]() {
DrawTimezoneCombobox(io, &xconfig_data_);
FlagCheckbox("Disable Daylight-Saving Time",
xconfig_data_.user.retail_flags,
static_cast<uint32_t>(kernel::X_RETAIL_FLAGS::DSTOff));
FlagCheckbox(
"24H Time", xconfig_data_.user.retail_flags,
static_cast<uint32_t>(kernel::X_RETAIL_FLAGS::TwentyFourHourClock));
});
GroupBox("Locale", [&]() {
DrawCombobox(io, "Language", kLanguageMap, xconfig_data_.user.language);
DrawCombobox(io, "Country", kCountryMap, xconfig_data_.user.country);
});
GroupBox("Profile", [&]() {
DrawCombobox(io, "Default Profile", profiles_,
xconfig_data_.user.default_profile);
FlagCheckbox("Parental Control",
xconfig_data_.user.parental_control_flags,
static_cast<uint8_t>(kernel::X_PC_FLAGS::PCEnabled));
});
ImGui::Dummy(ImVec2(2.f, 2.f));
GroupBox("Retail Options", [&]() {
FlagCheckbox("Dashboard Initialized", xconfig_data_.user.retail_flags,
static_cast<uint32_t>(
kernel::X_RETAIL_FLAGS::DashboardInitialized));
FlagCheckbox(
"IPTV Initialized", xconfig_data_.user.retail_flags,
static_cast<uint32_t>(kernel::X_RETAIL_FLAGS::IPTVEnabled));
FlagCheckbox(
"DVR Initialized", xconfig_data_.user.retail_flags,
static_cast<uint32_t>(kernel::X_RETAIL_FLAGS::IPTVDVREnabled));
FlagCheckbox(
"Kinect Initialized", xconfig_data_.user.retail_flags,
static_cast<uint32_t>(kernel::X_RETAIL_FLAGS::KinectInitialized));
});
ImGui::EndDisabled();
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("System")) {
ImGui::BeginDisabled(emulator_window_.emulator()->is_title_open());
ImGui::Dummy(ImVec2(2.f, 2.f));
GroupBox("Video Options", [&]() {
DrawCombobox(io, "AV Region", kAVRegion,
xconfig_data_.secured.av_region);
DrawResolutionCombobox(
io, {kernel::XVGAResolution.data(), kernel::XVGAResolution.size()},
xconfig_data_.user.av_pack_hdmi_sz, xconfig_data_.user.video_flags);
const auto res =
kernel::Resolution(xconfig_data_.user.av_pack_hdmi_sz.get());
ImGui::BeginDisabled(res.is_widescreen());
FlagCheckbox("Widescreen", xconfig_data_.user.video_flags,
static_cast<uint32_t>(kernel::X_VIDEO_FLAGS::Widescreen));
ImGui::EndDisabled();
});
GroupBox("Audio Options", [&]() {
FlagCheckbox("Mono", xconfig_data_.user.audio_flags,
static_cast<uint32_t>(kernel::X_AUDIO_FLAGS::AnalogMono));
FlagCheckbox(
"Dolby Pro Logic", xconfig_data_.user.audio_flags,
static_cast<uint32_t>(kernel::X_AUDIO_FLAGS::DolbyProLogic));
FlagCheckbox(
"Dolby Digital", xconfig_data_.user.audio_flags,
static_cast<uint32_t>(kernel::X_AUDIO_FLAGS::DolbyDigital));
FlagCheckbox("Dolby Digital WMA PRO", xconfig_data_.user.audio_flags,
static_cast<uint32_t>(
kernel::X_AUDIO_FLAGS::DolbyDigitalWithWMAPRO));
FlagCheckbox("Low Latency (unsupported)",
xconfig_data_.user.audio_flags,
static_cast<uint32_t>(kernel::X_AUDIO_FLAGS::LowLatency));
float volume = xconfig_data_.user.music_volume.get();
if (ImGui::SliderFloat("Audio player volume", &volume, 0.0f, 1.0f,
"%.2f")) {
xconfig_data_.user.music_volume = volume;
}
});
GroupBox("Network", [&]() {
if (ImGui::BeginTable("##NetworkTable", 2)) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("MAC Address: ");
ImGui::TableNextColumn();
ByteArray("mac", xconfig_data_.secured.mac_address.data(),
xconfig_data_.secured.mac_address.size());
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("Network ID: ");
ImGui::TableNextColumn();
ByteArray("netid", xconfig_data_.secured.online_network_id.data(),
xconfig_data_.secured.online_network_id.size());
ImGui::EndTable();
}
});
ImGui::EndDisabled();
ImGui::EndTabItem();
}
ImGui::EndTabBar();
}
ImGui::Dummy(ImVec2(2.f, 2.f));
// Bottom
ImGui::Separator();
ImGui::BeginDisabled(emulator_window_.emulator()->is_title_open());
if (ImGui::Button("Save")) {
SaveConfig();
save_confirmation_disappearance_ = ImGui::GetTime() + 3.0;
}
if (save_confirmation_disappearance_ > ImGui::GetTime()) {
ImGui::SameLine();
ImGui::Text("Settings Saved!");
}
ImGui::SameLine();
ImGui::SetCursorPosX(ImGui::GetCursorPosX() +
ImGui::GetContentRegionAvail().x - 55.f);
if (ImGui::Button("Reset", ImVec2(55.f, 0.0f))) {
xconfig_->SetDefaults();
xconfig_data_ = *xconfig_->GetXConfig();
save_confirmation_disappearance_ = ImGui::GetTime() + 3.0;
}
ImGui::EndDisabled();
ImGui::End();
}
void ConsoleSettingsDialog::SaveConfig() {
xconfig_->WriteXConfig(&xconfig_data_);
}
} // namespace app
} // namespace xe

View File

@@ -0,0 +1,62 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2026 Xenia Canary. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_APP_CONSOLE_SETTING_DIALOG_H_
#define XENIA_APP_CONSOLE_SETTING_DIALOG_H_
#include "xenia/ui/imgui_dialog.h"
#include "xenia/ui/imgui_drawer.h"
#include "xenia/xbox.h"
#include "xenia/kernel/xconfig.h"
namespace xe {
namespace app {
class EmulatorWindow;
class ConsoleSettingsDialog final : public ui::ImGuiDialog {
public:
ConsoleSettingsDialog(ui::ImGuiDrawer* imgui_drawer,
EmulatorWindow& emulator_window,
kernel::XConfig* xconfig)
: ui::ImGuiDialog(imgui_drawer),
emulator_window_(emulator_window),
xconfig_(xconfig),
xconfig_data_(*xconfig->GetXConfig()) {
const auto profiles = emulator_window.emulator()
->kernel_state()
->xam_state()
->profile_manager()
->GetAccounts();
for (const auto& [xuid, profile] : *profiles) {
profiles_.insert({xuid, profile.GetGamertagString()});
}
}
protected:
void OnDraw(ImGuiIO& io) override;
private:
void SaveConfig();
EmulatorWindow& emulator_window_;
kernel::XConfig* xconfig_ = nullptr;
kernel::XConfigData xconfig_data_{};
std::map<uint64_t, std::string> profiles_;
// UI specific variables
double save_confirmation_disappearance_ = 0.0;
};
} // namespace app
} // namespace xe
#endif

View File

@@ -19,6 +19,8 @@
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
#include "xenia/app/console_settings_dialog.h"
#include "xenia/base/assert.h"
#include "xenia/base/clock.h"
#include "xenia/base/cvar.h"
@@ -36,6 +38,7 @@
#include "xenia/kernel/xam/profile_manager.h"
#include "xenia/kernel/xam/xam_module.h"
#include "xenia/kernel/xam/xam_state.h"
#include "xenia/kernel/xconfig.h"
#include "xenia/ui/file_picker.h"
#include "xenia/ui/graphics_provider.h"
#include "xenia/ui/imgui_dialog.h"
@@ -725,7 +728,8 @@ void EmulatorWindow::XMPConfigDialog::OnDraw(ImGuiIO& io) {
volume_ =
emulator_window_.emulator_->audio_media_player()->GetVolume()->load();
if (ImGui::SliderFloat("Audio player volume", &volume_, 0.0f, 1.0f)) {
if (ImGui::SliderFloat("Audio player volume", &volume_, 0.0f, 1.0f,
"%.2f")) {
audio_player->SetVolume(volume_);
}
}
@@ -882,6 +886,15 @@ bool EmulatorWindow::Initialize() {
}
main_menu->AddChild(std::move(xmp_menu));
// Console menu
auto console_menu = MenuItem::Create(MenuItem::Type::kPopup, "&Console");
{
console_menu->AddChild(MenuItem::Create(
MenuItem::Type::kString, "&Open console settings", "",
std::bind(&EmulatorWindow::ToggleConsoleSettingsDialog, this)));
}
main_menu->AddChild(std::move(console_menu));
// Help menu.
auto help_menu = MenuItem::Create(MenuItem::Type::kPopup, "&Help");
{
@@ -1541,12 +1554,15 @@ void EmulatorWindow::GpuClearCaches() {
emulator()->graphics_system()->ClearCaches();
}
void EmulatorWindow::SetFullscreen(bool fullscreen) {
if (window_->IsFullscreen() == fullscreen) {
void EmulatorWindow::SetFullscreen(bool fullscreen_) {
if (window_->IsFullscreen() == fullscreen_) {
return;
}
window_->SetFullscreen(fullscreen);
window_->SetCursorVisibility(fullscreen
OVERRIDE_bool(fullscreen, fullscreen_);
window_->SetFullscreen(fullscreen_);
window_->SetCursorVisibility(fullscreen_
? ui::Window::CursorVisibility::kAutoHidden
: ui::Window::CursorVisibility::kVisible);
}
@@ -1596,6 +1612,20 @@ void EmulatorWindow::ToggleXMPConfigDialog() {
}
}
void EmulatorWindow::ToggleConsoleSettingsDialog() {
if (!console_settings_dialog_) {
console_settings_dialog_ =
std::unique_ptr<ConsoleSettingsDialog>(new ConsoleSettingsDialog(
imgui_drawer_.get(), *this, emulator_->kernel_state()->xconfig()));
} else {
if (console_settings_dialog_->IsClosing()) {
console_settings_dialog_.release();
} else {
console_settings_dialog_.reset();
}
}
}
void EmulatorWindow::ToggleControllerVibration() {
auto input_sys = emulator()->input_system();
if (input_sys) {
@@ -2341,6 +2371,10 @@ void EmulatorWindow::ClearDialogs() {
display_config_dialog_.reset();
}
if (console_settings_dialog_) {
console_settings_dialog_.reset();
}
imgui_drawer_.get()->ClearDialogs();
emulator_->kernel_state()->xam_state()->xam_dialogs_shown_ = 0;
}

View File

@@ -29,6 +29,8 @@
namespace xe {
namespace app {
class ConsoleSettingsDialog;
struct RecentTitleEntry {
std::string title_name;
std::filesystem::path path_to_file;
@@ -94,6 +96,8 @@ class EmulatorWindow {
void ToggleProfilesConfigDialog();
void ToggleXMPConfigDialog();
void ToggleConsoleSettingsDialog();
void SetHotkeysState(bool enabled) { disable_hotkeys_ = !enabled; }
// Types of button functions for hotkeys.
@@ -313,6 +317,7 @@ class EmulatorWindow {
bool initializing_shader_storage_ = false;
std::unique_ptr<DisplayConfigDialog> display_config_dialog_;
std::unique_ptr<ConsoleSettingsDialog> console_settings_dialog_;
// Storing pointers and toggling dialog state is useful for broadcasting
// messages back to guest.

View File

@@ -130,7 +130,8 @@ DECLARE_bool(debug);
DEFINE_bool(discord, true, "Enable Discord rich presence", "General");
DECLARE_bool(widescreen);
DECLARE_int32(window_size_x);
DECLARE_int32(window_size_y);
namespace xe {
namespace app {
@@ -538,12 +539,10 @@ bool EmulatorApp::OnInitialize() {
emulator_ =
std::make_unique<Emulator>("", storage_root, content_root, cache_root);
// Determine window size based on user setting.
auto res = xe::gpu::GraphicsSystem::GetInternalDisplayResolution();
// Main emulator display window.
emulator_window_ = EmulatorWindow::Create(emulator_.get(), app_context(),
res.first, res.second);
emulator_window_ =
EmulatorWindow::Create(emulator_.get(), app_context(),
cvars::window_size_x, cvars::window_size_y);
if (!emulator_window_) {
XELOGE("Failed to create the main emulator window");
return false;

View File

@@ -27,8 +27,6 @@ extern "C" {
} // extern "C"
DEFINE_bool(enable_xmp, true, "Enables Music Player playback.", "APU");
DEFINE_int32(xmp_default_volume, 70,
"Default music volume if game doesn't set it [0-100].", "APU");
namespace xe {
namespace apu {
@@ -246,7 +244,8 @@ void AudioMediaPlayer::Play() {
OnStateChanged();
if (volume_ == 0.0f) {
volume_ = cvars::xmp_default_volume / 100.0f;
volume_ = kernel_state_->xconfig()->ReadSetting<float>(
kernel::XCONFIG_USER_CATEGORY, kernel::XCONFIG_USER_MUSIC_VOLUME);
}
// Always apply the stored volume to the newly created driver

View File

@@ -84,8 +84,6 @@ DEFINE_bool(allow_game_relative_writes, false,
"generating test data to compare with original hardware. ",
"General");
DECLARE_int32(user_language);
DECLARE_bool(allow_plugins);
DEFINE_int32(priority_class, 0,
@@ -1555,8 +1553,9 @@ X_STATUS Emulator::CompleteLaunch(const std::filesystem::path& path,
kernel_state_->xam_state()->user_tracker()->AddTitleToPlayedList();
if (game_info_database_->IsValid()) {
title_name_ = game_info_database_->GetTitleName(
static_cast<XLanguage>(cvars::user_language));
title_name_ = game_info_database_->GetTitleName(static_cast<XLanguage>(
kernel_state_->xconfig()->ReadSetting<uint32_t>(
kernel::XCONFIG_USER_CATEGORY, kernel::XCONFIG_USER_LANGUAGE)));
XELOGI("Title name: {}", title_name_);
// Show achievments data

View File

@@ -23,34 +23,10 @@
#include "xenia/ui/window.h"
#include "xenia/ui/windowed_app_context.h"
DEFINE_uint32(internal_display_resolution, 8,
"Allow games that support different resolutions to render "
"in a specific resolution.\n"
"This is not guaranteed to work with all games or improve "
"performance.\n"
" 0=640x480\n"
" 1=640x576\n"
" 2=720x480\n"
" 3=720x576\n"
" 4=800x600\n"
" 5=848x480\n"
" 6=1024x768\n"
" 7=1152x864\n"
" 8=1280x720 (Default)\n"
" 9=1280x768\n"
" 10=1280x960\n"
" 11=1280x1024\n"
" 12=1360x768\n"
" 13=1440x900\n"
" 14=1680x1050\n"
" 15=1920x540\n"
" 16=1920x1080\n"
" 17=internal_display_resolution_x/y",
"Video");
DEFINE_uint32(internal_display_resolution_x, 1280,
DEFINE_uint32(custom_internal_display_resolution_x, 0,
"Custom width. See internal_display_resolution. Range 1-1920.",
"Video");
DEFINE_uint32(internal_display_resolution_y, 720,
DEFINE_uint32(custom_internal_display_resolution_y, 0,
"Custom height. See internal_display_resolution. Range 1-1080.\n",
"Video");
@@ -95,21 +71,6 @@ X_STATUS GraphicsSystem::Setup(cpu::Processor* processor,
scaled_aspect_x_ = 16;
scaled_aspect_y_ = 9;
auto custom_res_x = cvars::internal_display_resolution_x;
auto custom_res_y = cvars::internal_display_resolution_y;
if (!custom_res_x || custom_res_x > 1920 || !custom_res_y ||
custom_res_y > 1080) {
OVERRIDE_uint32(internal_display_resolution_x,
internal_display_resolution_entries[8].first);
OVERRIDE_uint32(internal_display_resolution_y,
internal_display_resolution_entries[8].second);
config::SaveConfig();
xe::FatalError(fmt::format(
"Invalid custom resolution specified: {}x{}\n"
"Width must be between 1-1920.\nHeight must be between 1-1080.",
custom_res_x, custom_res_y));
}
if (with_presentation && provider_) {
// Safe if either the UI thread call or the presenter creation fails.
if (app_context_) {
@@ -175,8 +136,15 @@ X_STATUS GraphicsSystem::Setup(cpu::Processor* processor,
#endif
while (frame_limiter_worker_running_) {
// If there is no title running then there is no need for guest
// frame limiter thread.
if (!kernel_state_->title_id()) {
xe::threading::Sleep(std::chrono::milliseconds(100));
continue;
}
register_file()->values[XE_GPU_REG_D1MODE_V_COUNTER] +=
GetInternalDisplayResolution().second;
GetResolution().second;
#if XE_PLATFORM_WIN32
if (cvars::vsync) {
@@ -454,14 +422,23 @@ bool GraphicsSystem::Restore(ByteStream* stream) {
return command_processor_->Restore(stream);
}
std::pair<uint16_t, uint16_t> GraphicsSystem::GetInternalDisplayResolution() {
if (cvars::internal_display_resolution >=
internal_display_resolution_entries.size()) {
return {cvars::internal_display_resolution_x,
cvars::internal_display_resolution_y};
std::pair<uint32_t, uint32_t> GraphicsSystem::GetResolution() const {
if (!kernel_state_) {
return {1280, 720};
}
return internal_display_resolution_entries
[cvars::internal_display_resolution];
if (cvars::custom_internal_display_resolution_x != 0 &&
cvars::custom_internal_display_resolution_y != 0) {
return {cvars::custom_internal_display_resolution_x,
cvars::custom_internal_display_resolution_y};
}
const auto resolution =
kernel::Resolution(kernel_state()->xconfig()->ReadSetting<uint32_t>(
kernel::XCONFIG_USER_CATEGORY,
kernel::XCONFIG_USER_AV_COMPOSITE_SCREENSZ));
return {resolution.width_, resolution.height_};
}
} // namespace gpu

View File

@@ -34,28 +34,6 @@ class Emulator;
namespace xe {
namespace gpu {
constexpr std::array<std::pair<uint16_t, uint16_t>, 17>
internal_display_resolution_entries = {{{640, 480},
{640, 576},
{720, 480},
{720, 576},
{800, 600},
{848, 480},
{1024, 768},
{1152, 864},
{1280, 720},
{1280, 768},
{1280, 960},
{1280, 1024},
{1360, 768},
{1440, 900},
{1680, 1050},
{1920, 540},
{1920, 1080}}};
constexpr std::array<std::pair<uint16_t, uint16_t>, 3>
driver_display_resolution = {{{1440, 900}, {1280, 720}, {1680, 1050}}};
class CommandProcessor;
class GraphicsSystem {
@@ -109,7 +87,7 @@ class GraphicsSystem {
bool Save(ByteStream* stream);
bool Restore(ByteStream* stream);
static std::pair<uint16_t, uint16_t> GetInternalDisplayResolution();
std::pair<uint32_t, uint32_t> GetResolution() const;
std::pair<uint32_t, uint32_t> GetScaledAspectRatio() const {
return {scaled_aspect_x_, scaled_aspect_y_};

View File

@@ -13,4 +13,4 @@ DEFINE_bool(headless, false,
"Don't display any UI, using defaults for prompts as needed.",
"UI");
DEFINE_bool(log_high_frequency_kernel_calls, false,
"Log kernel calls with the kHighFrequency tag.", "Kernel");
"Log kernel calls with the kHighFrequency tag.", "Logging");

View File

@@ -64,6 +64,8 @@ KernelState::KernelState(Emulator* emulator)
file_system_ = emulator->file_system();
xam_state_ = std::make_unique<xam::XamState>(emulator, this);
smc_ = std::make_unique<SystemManagementController>();
xconfig_ =
std::make_unique<XConfig>(emulator->storage_root() / "xconfig.settings");
InitializeKernelGuestGlobals();
kernel_version_ = KernelVersion(cvars::kernel_build_version);

View File

@@ -31,6 +31,7 @@
#include "xenia/kernel/xam/user_profile.h"
#include "xenia/kernel/xam/xam_state.h"
#include "xenia/kernel/xam/xdbf/spa_info.h"
#include "xenia/kernel/xconfig.h"
#include "xenia/kernel/xevent.h"
#include "xenia/vfs/virtual_file_system.h"
@@ -193,6 +194,8 @@ class KernelState {
XmpVolumePatch* xmp_volume_patch() const { return xmp_volume_patch_.get(); }
void InitXmpVolumePatch();
XConfig* xconfig() const { return xconfig_.get(); }
std::bitset<4> GetConnectedUsers() const;
// Access must be guarded by the global critical region.
@@ -350,6 +353,7 @@ class KernelState {
std::unique_ptr<xam::XamState> xam_state_;
std::unique_ptr<SystemManagementController> smc_;
std::unique_ptr<XmpVolumePatch> xmp_volume_patch_;
std::unique_ptr<XConfig> xconfig_;
KernelVersion kernel_version_;

View File

@@ -169,6 +169,16 @@ X_HRESULT XamApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
args->unk1.get(), args->unk2.get(), args->unk3.get());
return X_E_SUCCESS;
}
// Causes dashboard to correctly process language/region change. It does not
// contain any buffer.
case 0x8000000D: {
const bool is_pc_enabled =
(kernel_state_->xconfig()->ReadSetting<uint8_t>(
XCONFIG_USER_CATEGORY, XCONFIG_USER_PC_FLAGS) &
X_PC_FLAGS::PCEnabled) != 0;
return is_pc_enabled ? X_E_ACCESS_DENIED : X_E_SUCCESS;
}
}
XELOGE(
"Unimplemented XAM message app={:08X}, msg={:08X}, arg1={:08X}, "

View File

@@ -349,7 +349,8 @@ void UserTracker::UpdateTitleGpdFile() {
}
auto user_language = spa_data_->GetExistingLanguage(
static_cast<XLanguage>(cvars::user_language));
static_cast<XLanguage>(kernel_state()->xconfig()->ReadSetting<uint32_t>(
kernel::XCONFIG_USER_CATEGORY, kernel::XCONFIG_USER_LANGUAGE)));
// First add achievements because of lowest ID
for (const auto& entry : spa_data_->GetAchievements()) {

View File

@@ -14,8 +14,8 @@
DEFINE_bool(allow_avatar_initialization, false,
"Enable Avatar Initialization\n"
" Only set true when testing Avatar games. Certain games may\n"
" require kinect implementation.",
"Only set true when testing Avatar games. Certain games may crash "
"due to requirement of full avatar implementation.",
"Kernel");
namespace xe {

View File

@@ -22,6 +22,7 @@
#include "xenia/kernel/xboxkrnl/xboxkrnl_memory.h"
#include "xenia/kernel/xboxkrnl/xboxkrnl_modules.h"
#include "xenia/kernel/xboxkrnl/xboxkrnl_threading.h"
#include "xenia/kernel/xconfig.h"
#include "xenia/kernel/xenumerator.h"
#include "xenia/kernel/xthread.h"
#include "xenia/ui/imgui_dialog.h"
@@ -45,8 +46,6 @@ DEFINE_int32(avpack, 8,
" 7 = TV PAL-60\n"
" 8 = HDMI (default)",
"Video");
DECLARE_uint32(audio_flag);
DEFINE_bool(staging_mode, 0,
"Enables preview mode in dashboards to render debug information.",
"Kernel");
@@ -605,11 +604,11 @@ dword_result_t XGetAudioFlags_entry() {
return 2;
}
if (!cvars::audio_flag) {
return 0x10000 | 0x1;
}
const auto audio_flags = kernel_state()->xconfig()->ReadSetting<uint32_t>(
XCONFIG_USER_CATEGORY,
XCONFIG_USER_CATEGORY_ENTRIES::XCONFIG_USER_AUDIO_FLAGS);
return cvars::audio_flag;
return audio_flags ? audio_flags : 0x10000 | 0x1;
}
DECLARE_XAM_EXPORT1(XGetAudioFlags, kNone, kImplemented);
@@ -764,6 +763,21 @@ void GetSystemTimeAsFileTime_entry(lpqword_t time_ptr,
}
DECLARE_XAM_EXPORT1(GetSystemTimeAsFileTime, kNone, kImplemented);
dword_result_t XamIsIptvEnabled_entry() {
const bool iptv_enabled =
kernel_state()->xconfig()->ReadSetting<uint32_t>(
X_CONFIG_CATEGORY::XCONFIG_USER_CATEGORY, XCONFIG_USER_RETAIL_FLAGS) &
X_RETAIL_FLAGS::IPTVEnabled;
return !iptv_enabled ? X_E_FAIL : X_ERROR_SUCCESS;
}
DECLARE_XAM_EXPORT1(XamIsIptvEnabled, kNone, kImplemented);
dword_result_t XamLookupCommonStringByIndex_entry(dword_t string_index) {
return 0;
}
DECLARE_XAM_EXPORT1(XamLookupCommonStringByIndex, kNone, kImplemented);
} // namespace xam
} // namespace kernel
} // namespace xe

View File

@@ -21,8 +21,6 @@
#include "third_party/fmt/include/fmt/format.h"
#include "third_party/fmt/include/fmt/xchar.h"
DECLARE_int32(user_country);
DECLARE_int32(user_language);
// TODO(gibbed): put these forward decls in a header somewhere.
namespace xe {
@@ -205,17 +203,14 @@ uint8_t xeXamGetLocaleFromCountry(uint8_t id) {
// Helpers.
uint8_t xeXamGetLocaleEx(uint8_t max_country_id, uint8_t max_locale_id) {
// TODO(gibbed): rework when XConfig is cleanly implemented.
uint8_t country_id = static_cast<uint8_t>(cvars::user_country);
/*if (XSUCCEEDED(xboxkrnl::xeExGetXConfigSetting(
3, 14, &country_id, sizeof(country_id), nullptr))) {*/
uint8_t country_id = kernel_state()->xconfig()->ReadSetting<uint8_t>(
XCONFIG_USER_CATEGORY, XCONFIG_USER_COUNTRY);
if (country_id <= max_country_id) {
uint8_t locale_id = xeXamGetLocaleFromCountry(country_id);
if (locale_id <= max_locale_id) {
return locale_id;
}
}
/*}*/
// couldn't find locale, fallback from game region.
auto game_region = xeXGetGameRegion();
@@ -529,7 +524,9 @@ uint32_t xeXGetGameRegion() {
0x02FEu, 0x03FFu, 0x02FEu, 0x03FFu, 0x02FEu, 0x02FEu, 0xFFFFu, 0x03FFu,
0x03FFu, 0x03FFu, 0x03FFu, 0x02FEu, 0x03FFu, 0x03FFu, 0x02FEu, 0x00FFu,
0x03FFu, 0x03FFu, 0x03FFu, 0x03FFu, 0x03FFu, 0x03FFu, 0x03FFu};
auto country = static_cast<uint8_t>(cvars::user_country);
auto country = kernel_state()->xconfig()->ReadSetting<uint8_t>(
XCONFIG_USER_CATEGORY,
XCONFIG_USER_CATEGORY_ENTRIES::XCONFIG_USER_COUNTRY);
return country < xe::countof(table) ? table[country] : 0xFFFFu;
}
@@ -537,7 +534,11 @@ dword_result_t XGetGameRegion_entry() { return xeXGetGameRegion(); }
DECLARE_XAM_EXPORT1(XGetGameRegion, kNone, kStub);
XLanguage xeGetLanguage(bool extended_languages_support) {
auto desired_language = static_cast<XLanguage>(cvars::user_language);
auto desired_language =
static_cast<XLanguage>(kernel_state()->xconfig()->ReadSetting<uint32_t>(
XCONFIG_USER_CATEGORY,
XCONFIG_USER_CATEGORY_ENTRIES::XCONFIG_USER_LANGUAGE));
uint32_t region = xeXGetGameRegion();
auto max_languages = extended_languages_support ? XLanguage::kMaxLanguages
: XLanguage::kSChinese;
@@ -599,6 +600,24 @@ pointer_result_t XamGetLanguageTypefacePatch_entry(dword_t language) {
}
DECLARE_XAM_EXPORT1(XamGetLanguageTypefacePatch, kNone, kStub);
dword_result_t XamGetCountry_entry() {
return kernel_state()->xconfig()->ReadSetting<uint32_t>(
XCONFIG_USER_CATEGORY,
XCONFIG_USER_CATEGORY_ENTRIES::XCONFIG_USER_COUNTRY);
}
DECLARE_XAM_EXPORT1(XamGetCountry, kNone, kSketchy);
dword_result_t XamSetCountry_entry(dword_t country) {
const uint8_t country_real = country;
kernel_state()->xconfig()->WriteSetting(
XCONFIG_USER_CATEGORY,
XCONFIG_USER_CATEGORY_ENTRIES::XCONFIG_USER_COUNTRY, &country_real);
return 0;
}
DECLARE_XAM_EXPORT1(XamSetCountry, kNone, kSketchy);
} // namespace xam
} // namespace kernel
} // namespace xe

View File

@@ -19,12 +19,6 @@
#include "xenia/ui/windowed_app_context.h"
#include "xenia/xbox.h"
DEFINE_bool(allow_nui_initialization, false,
"Enable NUI initialization\n"
" Only set true when testing kinect games. Certain games may\n"
" require avatar implementation.",
"Kernel");
namespace xe {
namespace kernel {
namespace xam {
@@ -69,8 +63,14 @@ dword_result_t XamNuiGetDeviceStatus_entry(
*/
status_ptr.Zero();
status_ptr->status = cvars::allow_nui_initialization;
return cvars::allow_nui_initialization ? X_ERROR_SUCCESS : 0xC0050006;
const bool kinect_initialized =
kernel_state()->xconfig()->ReadSetting<uint32_t>(
X_CONFIG_CATEGORY::XCONFIG_USER_CATEGORY, XCONFIG_USER_RETAIL_FLAGS) &
X_RETAIL_FLAGS::KinectInitialized;
status_ptr->status = kinect_initialized;
return kinect_initialized ? X_ERROR_SUCCESS : 0xC0050006;
}
DECLARE_XAM_EXPORT1(XamNuiGetDeviceStatus, kNone, kStub);
@@ -195,8 +195,12 @@ dword_result_t XamNuiIsDeviceReady_entry() {
- 0x0004
- 0x0040
*/
uint16_t device_state = cvars::allow_nui_initialization ? 1 : 0;
return device_state >> 1 & 1;
const bool kinect_initialized =
kernel_state()->xconfig()->ReadSetting<uint32_t>(
X_CONFIG_CATEGORY::XCONFIG_USER_CATEGORY, XCONFIG_USER_RETAIL_FLAGS) &
X_RETAIL_FLAGS::KinectInitialized;
return kinect_initialized >> 1 & 1;
}
DECLARE_XAM_EXPORT1(XamNuiIsDeviceReady, kNone, kImplemented);

View File

@@ -988,7 +988,8 @@ DECLARE_XAM_EXPORT1(XamUserGetUserFlagsFromXUID, kUserProfiles, kImplemented);
dword_result_t XamUserGetOnlineLanguageFromXUID_entry(qword_t xuid) {
const auto& user = kernel_state()->xam_state()->GetUserProfile(xuid);
if (!user) {
return cvars::user_language;
return kernel_state()->xconfig()->ReadSetting<uint32_t>(
XCONFIG_USER_CATEGORY, XCONFIG_USER_LANGUAGE);
}
return user->GetLanguage();
}
@@ -998,7 +999,8 @@ DECLARE_XAM_EXPORT1(XamUserGetOnlineLanguageFromXUID, kUserProfiles,
dword_result_t XamUserGetOnlineCountryFromXUID_entry(qword_t xuid) {
const auto& user = kernel_state()->xam_state()->GetUserProfile(xuid);
if (!user) {
return cvars::user_country;
return kernel_state()->xconfig()->ReadSetting<uint8_t>(
XCONFIG_USER_CATEGORY, XCONFIG_USER_COUNTRY);
}
return user->GetCountry();
}

View File

@@ -21,7 +21,9 @@ namespace kernel {
namespace xboxkrnl {
dword_result_t XAudioGetSpeakerConfig_entry(lpdword_t config_ptr) {
*config_ptr = cvars::audio_flag;
kernel_state()->xconfig()->ReadSetting(
XCONFIG_USER_CATEGORY,
XCONFIG_USER_CATEGORY_ENTRIES::XCONFIG_USER_AUDIO_FLAGS, config_ptr);
return X_ERROR_SUCCESS;
}
DECLARE_XBOXKRNL_EXPORT1(XAudioGetSpeakerConfig, kAudio, kImplemented);

View File

@@ -12,7 +12,7 @@
#include "xenia/kernel/xboxkrnl/xboxkrnl_private.h"
DEFINE_bool(log_string_format_kernel_calls, false,
"Log kernel calls with the kHighFrequency tag.", "Logging");
"Log usage of print formatters like sprintf.", "Logging");
namespace xe {
namespace kernel {

View File

@@ -16,21 +16,10 @@
#include "xenia/kernel/util/shim_utils.h"
#include "xenia/kernel/xboxkrnl/xboxkrnl_private.h"
#include "xenia/kernel/xboxkrnl/xboxkrnl_rtl.h"
#include "xenia/kernel/xconfig.h"
#include "xenia/xbox.h"
DEFINE_int32(
video_standard, 1,
"Enables switching between different video signals.\n 1=NTSC\n "
"2=NTSC-J\n 3=PAL\n",
"Video");
DEFINE_bool(use_50Hz_mode, false, "Enables usage of PAL-50 mode.", "Video");
DEFINE_bool(interlaced, false, "Toggles interlaced mode.", "Video");
// TODO: This is stored in XConfig somewhere, probably in video flags.
DEFINE_bool(widescreen, true, "Toggles between 16:9 and 4:3 aspect ratio.",
"Video");
// BT.709 on modern monitors and TVs looks the closest to the Xbox 360 connected
// to an HDTV.
DEFINE_uint32(kernel_display_gamma_type, 2,
@@ -42,37 +31,22 @@ DEFINE_double(kernel_display_gamma_power, 2.22222233,
"Display gamma to use with kernel_display_gamma_type 3.",
"Kernel");
inline const static uint32_t GetVideoStandard() {
if (cvars::video_standard < 1 || cvars::video_standard > 3) {
return 1;
}
return cvars::video_standard;
}
inline const static float GetVideoRefreshRate() {
return cvars::use_50Hz_mode ? 50.0f : 60.0f;
}
inline const static std::pair<uint16_t, uint16_t> GetDisplayAspectRatio() {
if (cvars::widescreen) {
return {16, 9};
}
return {4, 3};
}
static std::pair<uint32_t, uint32_t> CalculateScaledAspectRatio(uint32_t fb_x,
uint32_t fb_y) {
static std::pair<uint32_t, uint32_t> CalculateScaledAspectRatio(
uint32_t fb_x, uint32_t fb_y, bool forced_widescreen) {
// Calculate the game's final aspect ratio as it would appear on a physical
// TV.
auto dar = GetDisplayAspectRatio();
uint32_t display_x = dar.first;
uint32_t display_y = dar.second;
const auto res = xe::kernel::Resolution(fb_x, fb_y);
auto res = xe::gpu::GraphicsSystem::GetInternalDisplayResolution();
uint32_t res_x = res.first;
uint32_t res_y = res.second;
uint32_t display_x = res.aspect_ratio().first;
uint32_t display_y = res.aspect_ratio().second;
if (forced_widescreen) {
display_x = 16;
display_y = 9;
}
uint32_t res_x = res.width_.get();
uint32_t res_y = res.height_.get();
uint32_t x_factor = std::gcd(fb_x, res_x);
res_x /= x_factor;
@@ -103,6 +77,47 @@ namespace xe {
namespace kernel {
namespace xboxkrnl {
bool IsWidescreen(KernelState* kernel_state, Resolution res) {
if (res.is_widescreen()) {
return true;
}
return kernel_state->xconfig()->ReadSetting<uint32_t>(
XCONFIG_USER_CATEGORY, XCONFIG_USER_VIDEO_FLAGS) &
X_VIDEO_FLAGS::Widescreen;
}
// Video standard only supports value from 1 to 3. PAL50 is not included here
// and PAL50 is converted to PAL with 50Hz in GetVideoRefreshRate.
X_AV_VIDEO_STANDARD GetVideoStandard(KernelState* kernel_state) {
auto av_region = static_cast<X_AV_VIDEO_STANDARD>(
(kernel_state->xconfig()->ReadSetting<uint32_t>(
XCONFIG_SECURED_CATEGORY, XCONFIG_SECURED_AV_REGION) &
0xFF00) >>
8);
if (av_region == X_AV_VIDEO_STANDARD::PAL_50) {
av_region = X_AV_VIDEO_STANDARD::PAL;
}
if (av_region < X_AV_VIDEO_STANDARD::NTSCM ||
av_region > X_AV_VIDEO_STANDARD::PAL_50) {
return X_AV_VIDEO_STANDARD::NTSCM;
}
return av_region;
}
float GetVideoRefreshRate(KernelState* kernel_state) {
const bool is_50Hz =
(kernel_state->xconfig()->ReadSetting<uint32_t>(
XCONFIG_SECURED_CATEGORY, XCONFIG_SECURED_AV_REGION) >>
23) &
0x1;
return !is_50Hz ? 60.0f : 50.0f;
}
// https://web.archive.org/web/20150805074003/https://www.tweakoz.com/orkid/
// http://www.tweakoz.com/orkid/dox/d3/d52/xb360init_8cpp_source.html
// https://github.com/Free60Project/xenosfb/
@@ -205,17 +220,25 @@ void VdQueryVideoMode(X_VIDEO_MODE* video_mode,
// TODO(benvanik): get info from actual display.
std::memset(video_mode, 0, sizeof(X_VIDEO_MODE));
auto display_res = gpu::GraphicsSystem::GetInternalDisplayResolution();
// Later calculate if resolution is widescreen or not and apply flag
// accordingly. Technically possible resolutions should depend on av_pack, but
// we can ignore it.
const auto resolution =
Resolution(kernel_state()->xconfig()->ReadSetting<uint32_t>(
XCONFIG_USER_CATEGORY, XCONFIG_USER_AV_COMPOSITE_SCREENSZ));
video_mode->display_width = display_res.first;
video_mode->display_height = display_res.second;
const auto is_widescreen = IsWidescreen(kernel_state(), resolution);
video_mode->display_width = resolution.width_.get();
video_mode->display_height = resolution.height_.get();
video_mode->is_interlaced = cvars::interlaced;
video_mode->is_widescreen = cvars::widescreen;
video_mode->is_widescreen = is_widescreen;
video_mode->is_hi_def = video_mode->display_width >= 0x500;
video_mode->refresh_rate = GetVideoRefreshRate();
video_mode->video_standard = GetVideoStandard();
video_mode->refresh_rate = GetVideoRefreshRate(kernel_state());
video_mode->video_standard =
static_cast<uint32_t>(GetVideoStandard(kernel_state()));
video_mode->pixel_rate = 0x8A;
video_mode->widescreen_flag = cvars::widescreen ? 0x01 : 0x03;
video_mode->widescreen_flag = is_widescreen ? 0x01 : 0x03;
}
void VdQueryRealVideoMode_entry(pointer_t<X_VIDEO_MODE> video_mode) {
@@ -370,7 +393,12 @@ dword_result_t VdInitializeScalerCommandBuffer_entry(
uint32_t fb_x = (scaled_output_wh >> 16) & 0xFFFF;
uint32_t fb_y = scaled_output_wh & 0xFFFF;
auto aspect = CalculateScaledAspectRatio(fb_x, fb_y);
const bool forced_widescreen =
kernel_state()->xconfig()->ReadSetting<uint32_t>(
XCONFIG_USER_CATEGORY, XCONFIG_USER_VIDEO_FLAGS) &
X_VIDEO_FLAGS::Widescreen;
auto aspect = CalculateScaledAspectRatio(fb_x, fb_y, forced_widescreen);
auto graphics_system = kernel_state()->emulator()->graphics_system();
graphics_system->SetScaledAspectRatio(aspect.first, aspect.second);

View File

@@ -7,295 +7,35 @@
******************************************************************************
*/
#include "xenia/kernel/xboxkrnl/xboxkrnl_xconfig.h"
#include "xenia/base/logging.h"
#include "xenia/kernel/kernel_state.h"
#include "xenia/kernel/util/shim_utils.h"
#include "xenia/kernel/xboxkrnl/xboxkrnl_private.h"
#include "xenia/kernel/xconfig.h"
#include "xenia/xbox.h"
DEFINE_int32(user_language, 1,
"User language ID.\n"
" 1=en 2=ja 3=de 4=fr 5=es 6=it 7=ko 8=zh\n"
" 9=pt 11=pl 12=ru 13=sv 14=tr 15=nb 16=nl 17=zh",
"XConfig");
DEFINE_int32(user_country, 103,
"User country ID.\n"
" 1=AE 2=AL 3=AM 4=AR 5=AT 6=AU 7=AZ 8=BE 9=BG\n"
" 10=BH 11=BN 12=BO 13=BR 14=BY 15=BZ 16=CA 18=CH 19=CL\n"
" 20=CN 21=CO 22=CR 23=CZ 24=DE 25=DK 26=DO 27=DZ 28=EC\n"
" 29=EE 30=EG 31=ES 32=FI 33=FO 34=FR 35=GB 36=GE 37=GR\n"
" 38=GT 39=HK 40=HN 41=HR 42=HU 43=ID 44=IE 45=IL 46=IN\n"
" 47=IQ 48=IR 49=IS 50=IT 51=JM 52=JO 53=JP 54=KE 55=KG\n"
" 56=KR 57=KW 58=KZ 59=LB 60=LI 61=LT 62=LU 63=LV 64=LY\n"
" 65=MA 66=MC 67=MK 68=MN 69=MO 70=MV 71=MX 72=MY 73=NI\n"
" 74=NL 75=NO 76=NZ 77=OM 78=PA 79=PE 80=PH 81=PK 82=PL\n"
" 83=PR 84=PT 85=PY 86=QA 87=RO 88=RU 89=SA 90=SE 91=SG\n"
" 92=SI 93=SK 95=SV 96=SY 97=TH 98=TN 99=TR 100=TT 101=TW\n"
" 102=UA 103=US 104=UY 105=UZ 106=VE 107=VN 108=YE 109=ZA\n",
"XConfig");
DEFINE_uint32(
audio_flag, 0x00010001,
"Audio Mode Analog.\n"
" 0x00000001 = Dolby Pro Logic\n"
" 0x00000002 = Analog Mono\n"
"Audio Mode Digital.\n"
" 0x00000000 = Digital Stereo (choose one of the above by itself)\n"
" 0x00010000 = Dolby Digital\n"
" 0x00030000 = Dolby Digital with WMA PRO\n"
"Special Flags.\n"
" 0x00000003 = Stereo Bypass\n"
" 0x80000000 = Low Latency\n"
" This Config requires you to pair an analog and digitial flag together\n"
" while digital stereo only requires an analog flag. Bonus flags are\n"
" optional. Ex) 0x00010001\n",
"XConfig");
DECLARE_bool(widescreen);
DECLARE_bool(use_50Hz_mode);
DECLARE_int32(video_standard);
DECLARE_uint32(internal_display_resolution);
namespace xe {
namespace kernel {
namespace xboxkrnl {
X_STATUS xeExGetXConfigSetting(X_CONFIG_CATEGORY category, uint16_t setting,
void* buffer, uint16_t buffer_size,
uint16_t* required_size) {
uint16_t setting_size = 0;
alignas(uint32_t) uint8_t value[4];
// TODO(benvanik): have real structs here that just get copied from.
// https://free60project.github.io/wiki/XConfig.html
// https://github.com/oukiar/freestyledash/blob/master/Freestyle/Tools/Generic/ExConfig.h
switch (category) {
case XCONFIG_SECURED_CATEGORY:
switch (setting) {
case XCONFIG_SECURED_AV_REGION:
setting_size = 4;
switch (cvars::video_standard) {
case 1: // NTSCM
xe::store_and_swap<uint32_t>(value, X_AV_REGION::NTSCM);
break;
case 2: // NTSCJ
xe::store_and_swap<uint32_t>(value, X_AV_REGION::NTSCJ);
break;
case 3: // PAL
xe::store_and_swap<uint32_t>(value, cvars::use_50Hz_mode
? X_AV_REGION::PAL_50
: X_AV_REGION::PAL);
break;
default:
xe::store_and_swap<uint32_t>(value, 0);
break;
}
break;
default:
XELOGW(
"An unimplemented setting 0x{:04X} in XCONFIG SECURED CATEGORY",
static_cast<uint16_t>(setting));
assert_unhandled_case(setting);
return X_STATUS_INVALID_PARAMETER_2;
}
break;
case XCONFIG_USER_CATEGORY:
switch (setting) {
case XCONFIG_USER_TIME_ZONE_BIAS:
case XCONFIG_USER_TIME_ZONE_STD_NAME:
case XCONFIG_USER_TIME_ZONE_DLT_NAME:
case XCONFIG_USER_TIME_ZONE_STD_DATE:
case XCONFIG_USER_TIME_ZONE_DLT_DATE:
case XCONFIG_USER_TIME_ZONE_STD_BIAS:
case XCONFIG_USER_TIME_ZONE_DLT_BIAS:
setting_size = 4;
// TODO(benvanik): get this value.
xe::store_and_swap<uint32_t>(value, 0);
break;
case XCONFIG_USER_LANGUAGE:
setting_size = 4;
xe::store_and_swap<uint32_t>(value, cvars::user_language);
break;
case XCONFIG_USER_VIDEO_FLAGS:
setting_size = 4;
xe::store_and_swap<uint32_t>(value, cvars::widescreen
? X_VIDEO_FLAGS::Widescreen
: X_VIDEO_FLAGS::RatioNormal);
break;
case XCONFIG_USER_AUDIO_FLAGS:
setting_size = 4;
xe::store_and_swap<uint32_t>(value, cvars::audio_flag);
break;
case XCONFIG_USER_RETAIL_FLAGS:
setting_size = 4;
// TODO(benvanik): get this value.
xe::store_and_swap<uint32_t>(value, 0x40);
break;
case XCONFIG_USER_COUNTRY:
setting_size = 1;
value[0] = static_cast<uint8_t>(cvars::user_country);
break;
case XCONFIG_USER_PC_FLAGS:
setting_size = 1;
// All related flags must be set for PC to function
// Both flags set even when PC are off
value[0] =
X_PC_FLAGS::XBLAllowed | X_PC_FLAGS::XBLMembershipCreationAllowed;
break;
case XCONFIG_USER_AV_COMPONENT_SCREENSZ:
setting_size = 4;
// int16_t* value[2];
if (XHDTVResolution.find(cvars::internal_display_resolution) !=
XHDTVResolution.cend()) {
xe::store_and_swap<int32_t>(
value, XHDTVResolution.at(cvars::internal_display_resolution));
} else {
XELOGW("Resolution not supported for AV Component");
xe::store_and_swap<int32_t>(value, 0);
}
break;
case XCONFIG_USER_AV_VGA_SCREENSZ:
setting_size = 4;
// int16_t* value[2];
if (XVGAResolution.find(cvars::internal_display_resolution) !=
XVGAResolution.cend()) {
xe::store_and_swap<int32_t>(
value, XVGAResolution.at(cvars::internal_display_resolution));
} else {
XELOGW("Resolution not supported for VGA");
xe::store_and_swap<int32_t>(value, 0);
}
break;
case XCONFIG_USER_PC_GAME:
setting_size = 4;
xe::store_and_swap<uint32_t>(value,
X_PC_GAMES_FLAGS::NoGameRestrictions);
break;
case XCONFIG_USER_PC_PASSWORD:
setting_size = 4;
std::memset(value, 0, 4);
break;
case XCONFIG_USER_PC_MOVIE:
setting_size = 4;
xe::store_and_swap<uint32_t>(value,
X_PC_MOVIE_FLAGS::NoMovieRestrictions);
break;
case XCONFIG_USER_PC_GAME_RATING:
setting_size = 4;
xe::store_and_swap<uint32_t>(value,
X_PC_GAME_RATING_FLAGS::DefaultGame);
break;
case XCONFIG_USER_PC_MOVIE_RATING:
setting_size = 4;
xe::store_and_swap<uint32_t>(value,
X_PC_MOVIE_RATING_FLAGS::DefaultMovie);
break;
case XCONFIG_USER_PC_HINT:
// ExSetXConfigSetting and ExGetXConfigSetting request size of 0x40
setting_size = 0x40;
store_and_swap<std::string>(value, "");
break;
case XCONFIG_USER_PC_HINT_ANSWER:
setting_size = 0x20;
store_and_swap<std::string>(value, "");
break;
case XCONFIG_USER_ARCADE_FLAGS:
setting_size = 4;
xe::store_and_swap<uint32_t>(value, X_ARCADE_FLAGS::AutoDownloadOff);
break;
case XCONFIG_USER_PC_VERSION:
setting_size = 4;
xe::store_and_swap<uint32_t>(value, X_PC_VERSION::VersionOne);
break;
case XCONFIG_USER_PC_TV:
setting_size = 4;
xe::store_and_swap<uint32_t>(value, X_PC_TV::NoTVRestrictions);
break;
case XCONFIG_USER_PC_TV_RATING:
setting_size = 4;
xe::store_and_swap<uint32_t>(value, X_PC_TV_RATING::DefaultTV);
break;
case XCONFIG_USER_PC_EXPLICIT_VIDEO:
setting_size = 4;
xe::store_and_swap<uint32_t>(
value, X_PC_EXPLICIT_VIDEO::ExplicitVideoAllowed);
break;
case XCONFIG_USER_PC_EXPLICIT_VIDEO_RATING:
setting_size = 4;
xe::store_and_swap<uint32_t>(
value, X_PC_EXPLICIT_VIDEO_RATING::ExplicitAllowed);
break;
case XCONFIG_USER_PC_UNRATED_VIDEO:
setting_size = 4;
xe::store_and_swap<uint32_t>(value,
X_PC_EXPLICIT_UNRATED::UnratedALL);
break;
case XCONFIG_USER_PC_UNRATED_VIDEO_RATING:
setting_size = 4;
xe::store_and_swap<uint32_t>(
value, X_PC_EXPLICIT_UNRATED_RATING::DefaultExplicitUnrated);
break;
case XCONFIG_USER_VIDEO_OUTPUT_BLACK_LEVELS:
setting_size = 4;
xe::store_and_swap<uint32_t>(value, X_BLACK_LEVEL::LevelNormal);
break;
default:
XELOGW("An unimplemented setting 0x{:04X} in XCONFIG USER CATEGORY",
static_cast<uint16_t>(setting));
assert_unhandled_case(setting);
return X_STATUS_INVALID_PARAMETER_2;
}
break;
case XCONFIG_CONSOLE_CATEGORY:
switch (setting) {
case XCONFIG_CONSOLE_SCREEN_SAVER:
setting_size = 2;
xe::store_and_swap<int16_t>(value, X_SCREENSAVER::ScreensaverOff);
break;
case XCONFIG_CONSOLE_AUTO_SHUT_OFF:
setting_size = 2;
xe::store_and_swap<int16_t>(value, X_AUTO_SHUTDOWN::AutoShutdownOff);
break;
case XCONFIG_CONSOLE_CAMERA_SETTINGS:
// Camera Flags are added together and last byte is always 0x1
setting_size = 4;
xe::store_and_swap<uint32_t>(value, X_CAMERA_FLAGS::AutoAll);
break;
case XCONFIG_CONSOLE_KEYBOARD_LAYOUT:
setting_size = 2;
xe::store_and_swap<int16_t>(value,
X_KEYBOARD_LAYOUT::KeyboardDefault);
break;
default:
XELOGW(
"An unimplemented setting 0x{:04X} in XCONFIG CONSOLE CATEGORY",
static_cast<uint16_t>(setting));
assert_unhandled_case(setting);
return X_STATUS_INVALID_PARAMETER_2;
}
break;
default:
XELOGW("An unimplemented category 0x{:04X}",
static_cast<uint16_t>(category));
assert_unhandled_case(category);
return X_STATUS_INVALID_PARAMETER_1;
X_STATUS xeExGetXConfigSetting(X_CONFIG_CATEGORY category,
const uint16_t setting_id, void* buffer,
uint16_t buffer_size, uint16_t* required_size) {
if (!buffer && buffer_size) {
return X_STATUS_INVALID_PARAMETER_3;
}
if (buffer) {
if (buffer_size < setting_size) {
if (buffer_size <
kernel_state()->xconfig()->GetSettingSize(category, setting_id)) {
return X_STATUS_BUFFER_TOO_SMALL;
}
std::memcpy(buffer, value, setting_size);
} else {
if (buffer_size) {
return X_STATUS_INVALID_PARAMETER_3;
}
kernel_state()->xconfig()->ReadSetting(category, setting_id, buffer);
}
if (required_size) {
*required_size = setting_size;
*required_size = static_cast<uint16_t>(
kernel_state()->xconfig()->GetSettingSize(category, setting_id));
}
return X_STATUS_SUCCESS;
@@ -325,7 +65,11 @@ dword_result_t ExSetXConfigSetting_entry(word_t category, word_t setting,
Handles settings the only have a single flag/value like
XCONFIG_USER_VIDEO_FLAGS to swap
*/
XELOGI("ExSetXConfigSetting: category: 0X{:04x}, setting: 0X{:04x}",
kernel_state()->xconfig()->WriteSetting(
static_cast<X_CONFIG_CATEGORY>(category.value()), setting.value(),
kernel_memory()->TranslateVirtual<void*>(buffer_ptr));
XELOGI("ExSetXConfigSetting: category: 0x{:04x}, setting: 0x{:04x}",
static_cast<uint16_t>(category), static_cast<uint16_t>(setting));
return X_STATUS_SUCCESS;
}
@@ -339,6 +83,14 @@ dword_result_t ExReadModifyWriteXConfigSettingUlong_entry(word_t category,
Handles settings with multiple flags like XCONFIG_USER_RETAIL_FLAGS and
XCONFIG_CONSOLE_RETAIL_EX_FLAGS
*/
uint32_t value = kernel_state()->xconfig()->ReadSetting<uint32_t>(
static_cast<X_CONFIG_CATEGORY>(category.value()), setting);
value = xe::byte_swap((value & bit_affected) | flag);
kernel_state()->xconfig()->WriteSetting(
static_cast<X_CONFIG_CATEGORY>(category.value()), setting, &value);
XELOGI(
"ExReadModifyWriteXConfigSettingUlong: category: 0x{:04x}, setting: "
"{:04x}, changed bits: 0X{:08x}, setting flag 0X{:08x}",

View File

@@ -1,395 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2025 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_KERNEL_XBOXKRNL_XBOXKRNL_XCONFIG_H_
#define XENIA_KERNEL_XBOXKRNL_XBOXKRNL_XCONFIG_H_
#include <cstdint>
#include <map>
namespace xe {
namespace kernel {
namespace xboxkrnl {
enum X_CONFIG_CATEGORY : uint16_t {
XCONFIG_STATIC_CATEGORY = 0x00,
XCONFIG_STATISTIC_CATEGORY = 0x01,
XCONFIG_SECURED_CATEGORY = 0x02,
XCONFIG_USER_CATEGORY = 0x03,
XCONFIG_XNET_MACHINE_ACCOUNT_CATEGORY = 0x04,
XCONFIG_XNET_PARAMETERS_CATEGORY = 0x05,
XCONFIG_MEDIA_CENTER_CATEGORY = 0x06,
XCONFIG_CONSOLE_CATEGORY = 0x07,
XCONFIG_DVD_CATEGORY = 0x08,
XCONFIG_IPTV_CATEGORY = 0x09,
XCONFIG_SYSTEM_CATEGORY = 0x0A,
XCONFIG_DEVKIT_CATEGORY = 0x0B
};
enum XCONFIG_STATIC_CATEGORY_ENTRIES : uint8_t {
XCONFIG_STATIC_FIRST_POWER_ON_DATE = 0x01,
XCONFIG_STATIC_SMC_CONFIG = 0x02
};
enum XCONFIG_STATISTIC_CATEGORY_ENTRIES : uint8_t {
XCONFIG_STATISTICS_XUID_MAC_ADDRESS = 0x01,
XCONFIG_STATISTICS_XUID_COUNT = 0x02,
XCONFIG_STATISTICS_ODD_FAILURES = 0x03,
XCONFIG_STATISTICS_HDD_SMART_DATA = 0x04,
XCONFIG_STATISTICS_UEM_ERRORS = 0x05,
XCONFIG_STATISTICS_FPM_ERRORS = 0x06,
XCONFIG_STATISTICS_LAST_REPORT_TIME = 0x07,
XCONFIG_STATISTICS_BUG_CHECK_DATA = 0x08,
XCONFIG_STATISTICS_TEMPERATURE = 0x09,
XCONFIG_STATISTICS_XEKEYS_WRITE_FAILURE = 0x0A
};
enum XCONFIG_SECURED_CATEGORY_ENTRIES : uint8_t {
XCONFIG_SECURED_MAC_ADDRESS = 0x01,
XCONFIG_SECURED_AV_REGION = 0x02,
XCONFIG_SECURED_GAME_REGION = 0x03,
XCONFIG_SECURED_DVD_REGION = 0x04,
XCONFIG_SECURED_RESET_KEY = 0x05,
XCONFIG_SECURED_SYSTEM_FLAGS = 0x06,
XCONFIG_SECURED_POWER_MODE = 0x07,
XCONFIG_SECURED_ONLINE_NETWORK_ID = 0x08,
XCONFIG_SECURED_POWER_VCS_CONTROL = 0x09
};
enum XCONFIG_USER_CATEGORY_ENTRIES : uint8_t {
XCONFIG_USER_TIME_ZONE_BIAS = 0x01,
XCONFIG_USER_TIME_ZONE_STD_NAME = 0x02,
XCONFIG_USER_TIME_ZONE_DLT_NAME = 0x03,
XCONFIG_USER_TIME_ZONE_STD_DATE = 0x04,
XCONFIG_USER_TIME_ZONE_DLT_DATE = 0x05,
XCONFIG_USER_TIME_ZONE_STD_BIAS = 0x06,
XCONFIG_USER_TIME_ZONE_DLT_BIAS = 0x07,
XCONFIG_USER_DEFAULT_PROFILE = 0x08,
XCONFIG_USER_LANGUAGE = 0x09,
XCONFIG_USER_VIDEO_FLAGS = 0x0A,
XCONFIG_USER_AUDIO_FLAGS = 0x0B,
XCONFIG_USER_RETAIL_FLAGS = 0x0C,
XCONFIG_USER_DEVKIT_FLAGS = 0x0D,
XCONFIG_USER_COUNTRY = 0x0E,
XCONFIG_USER_PC_FLAGS = 0x0F,
XCONFIG_USER_SMB_CONFIG = 0x10,
XCONFIG_USER_LIVE_PUID = 0x11,
XCONFIG_USER_LIVE_CREDENTIALS = 0x12,
XCONFIG_USER_AV_COMPOSITE_SCREENSZ = 0x13,
XCONFIG_USER_AV_COMPONENT_SCREENSZ = 0x14,
XCONFIG_USER_AV_VGA_SCREENSZ = 0x15,
XCONFIG_USER_PC_GAME = 0x16,
XCONFIG_USER_PC_PASSWORD = 0x17,
XCONFIG_USER_PC_MOVIE = 0x18,
XCONFIG_USER_PC_GAME_RATING = 0x19,
XCONFIG_USER_PC_MOVIE_RATING = 0x1A,
XCONFIG_USER_PC_HINT = 0x1B,
XCONFIG_USER_PC_HINT_ANSWER = 0x1C,
XCONFIG_USER_PC_OVERRIDE = 0x1D,
XCONFIG_USER_MUSIC_PLAYBACK_MODE = 0x1E,
XCONFIG_USER_MUSIC_VOLUME = 0x1F,
XCONFIG_USER_MUSIC_FLAGS = 0x20,
XCONFIG_USER_ARCADE_FLAGS = 0x21,
XCONFIG_USER_PC_VERSION = 0x22,
XCONFIG_USER_PC_TV = 0x23,
XCONFIG_USER_PC_TV_RATING = 0x24,
XCONFIG_USER_PC_EXPLICIT_VIDEO = 0x25,
XCONFIG_USER_PC_EXPLICIT_VIDEO_RATING = 0x26,
XCONFIG_USER_PC_UNRATED_VIDEO = 0x27,
XCONFIG_USER_PC_UNRATED_VIDEO_RATING = 0x28,
XCONFIG_USER_VIDEO_OUTPUT_BLACK_LEVELS = 0x29,
XCONFIG_USER_VIDEO_PLAYER_DISPLAY_MODE = 0x2A,
XCONFIG_USER_ALTERNATE_VIDEO_TIMING_ID = 0x2B,
XCONFIG_USER_VIDEO_DRIVER_OPTIONS = 0x2C,
XCONFIG_USER_MUSIC_UI_FLAGS = 0x2D,
XCONFIG_USER_VIDEO_MEDIA_SOURCE_TYPE = 0x2E,
XCONFIG_USER_MUSIC_MEDIA_SOURCE_TYPE = 0x2F,
XCONFIG_USER_PHOTO_MEDIA_SOURCE_TYPE = 0x30
};
enum XCONFIG_XNET_CATEGORY_ENTRIES : uint8_t { XCONFIG_XNET_DATA = 0x01 };
enum XCONFIG_MEDIA_CENTER_CATEGORY_ENTRIES : uint8_t {
XCONFIG_MEDIA_CENTER_MEDIA_PLAYER = 0x01,
XCONFIG_MEDIA_CENTER_XESLED_VERSION = 0x02,
XCONFIG_MEDIA_CENTER_XESLED_TRUST_SECRET = 0x03,
XCONFIG_MEDIA_CENTER_XESLED_TRUST_CODE = 0x04,
XCONFIG_MEDIA_CENTER_XESLED_HOST_ID = 0x05,
XCONFIG_MEDIA_CENTER_XESLED_KEY = 0x06,
XCONFIG_MEDIA_CENTER_XESLED_HOST_MAC_ADDRESS = 0x07,
XCONFIG_MEDIA_CENTER_SERVER_UUID = 0x08,
XCONFIG_MEDIA_CENTER_SERVER_NAME = 0x09,
XCONFIG_MEDIA_CENTER_SERVER_FLAG = 0x0A
};
enum XCONFIG_CONSOLE_CATEGORY_ENTRIES : uint8_t {
XCONFIG_CONSOLE_SCREEN_SAVER = 0x01,
XCONFIG_CONSOLE_AUTO_SHUT_OFF = 0x02,
XCONFIG_CONSOLE_WIRELESS_SETTINGS = 0x03,
XCONFIG_CONSOLE_CAMERA_SETTINGS = 0x04,
XCONFIG_CONSOLE_PLAYTIMERDATA = 0x05,
XCONFIG_CONSOLE_MEDIA_DISABLEAUTOLAUNCH = 0x06,
XCONFIG_CONSOLE_KEYBOARD_LAYOUT = 0x07,
XCONFIG_CONSOLE_PC_TITLE_EXEMPTIONS = 0x08,
XCONFIG_CONSOLE_NUI = 0x09,
XCONFIG_CONSOLE_VOICE = 0x0A,
XCONFIG_CONSOLE_RETAIL_EX_FLAGS = 0x0B,
XCONFIG_CONSOLE_DASH_FIRST_USE_TUTORIAL_FLAGS = 0x0C,
XCONFIG_CONSOLE_TV_DIAGONAL_SIZE_IN_CM = 0x0D,
XCONFIG_CONSOLE_NETWORKSTORAGEDEVICE_SERIALNUMBER = 0x0E,
XCONFIG_CONSOLE_DISCOVERABLE = 0x0F,
XCONFIG_CONSOLE_LIVE_TV_PROVIDER = 0x10
};
enum XCONFIG_DVD_CATEGORY_ENTRIES : uint8_t {
XCONFIG_DVD_VOLUME_ID = 0x01,
XCONFIG_DVD_BOOKMARK = 0x02
};
enum XCONFIG_IPTV_CATEGORY_ENTRIES : uint8_t {
XCONFIG_IPTV_SERVICE_PROVIDER_NAME = 0x01,
XCONFIG_IPTV_PROVISIONING_SERVER_URL = 0x02,
XCONFIG_IPTV_SUPPORT_INFO = 0x03,
XCONFIG_IPTV_BOOTSTRAP_SERVER_URL = 0x04
};
enum XCONFIG_SYSTEM_CATEGORY_ENTRIES : uint8_t {
XCONFIG_SYSTEM_ALARM_TIME = 0x01,
XCONFIG_SYSTEM_PREVIOUS_FLASH_VERSION = 0x02
};
enum XCONFIG_DEVKIT_CATEGORY_ENTRIES : uint8_t {
XCONFIG_DEVKIT_USBD_ROOT_HUB_PORT_DISABLE_MASK = 0x01,
XCONFIG_DEVKIT_XAM_FEATURE_ENABLE_DISABLE_MASK = 0x02,
XCONFIG_DEVKIT_KIOSK_ID = 0x03
};
// XCONFIG_SECURED_AV_REGION
enum X_AV_REGION : uint32_t {
NTSCM = 0x00400100,
NTSCJ = 0x00400200,
PAL = 0x00400400,
PAL_50 = 0x00800300,
};
// XCONFIG_USER_VIDEO_FLAGS
enum X_VIDEO_FLAGS : uint32_t {
RatioNormal = 0x00000000,
Widescreen = 0x00010000,
};
// XCONFIG_USER_AV_COMPONENT_SCREENSZ
const static std::map<uint32_t, int32_t> XHDTVResolution = {
{0, 0x028001E0}, // 480p
{8, 0x050002D0}, // 720p, always widescreen
{16, 0x07800438}, // 1080, interlaced added in 1888, always widescreen
};
// XCONFIG_USER_AV_COMPONENT_SCREENSZ
const static std::map<uint32_t, int32_t> XVGAResolution = {
// Existed since 1888
{0, 0x028001E0}, // 680x480
{5, 0x035001E0}, // 848x480
{6, 0x04000300}, // 1024x768
{8, 0x050002D0}, // 1280x720
{9, 0x05000300}, // 1280x768
{12, 0x05500300}, // 1360x768
// added in later Versions
{11, 0x05000400}, // 1280x1024
{13, 0x05A00384}, // 1440x900
{14, 0x0690041A}, // 1680x1050
{16, 0x07800438}, // 1920x1080
};
// XCONFIG_USER_AUDIO_FLAGS
enum X_AUDIO_FLAGS : uint32_t {
// Audio Mode Analog
DolbyProLogic = 0x00000001,
AnalogMono = 0x00000002,
// Audio Mode Digital
DigitalStereo = 0x00000000,
DolbyDigital = 0x00010000,
DolbyDigitalWithWMAPRO = 0x00030000,
// Special Flags
StereoBypass = 0x00000003,
LowLatency = 0x80000000,
};
// XCONFIG_USER_RETAIL_FLAGS
enum X_RETAIL_FLAGS : uint32_t {
// Clock
DSTOff = 0x00000002,
TwentyFourHourClock = 0x00000008,
// Startup
DashboardStartup = 0x00000080,
IPTVStartup = 0x00000800,
DiscStartup = 0x00002000,
MCXDownloaderStartup = 0x00020000,
// IPTV
IPTVEnabled = 0x00001000,
IPTVDVRENABLED = 0x00080000,
IPTVDisabled = 0x02000000,
// Kinect
KinectInitialized = 0x20000000,
KinectDisabled = 0x80000000,
// Other
DashboardInitialized = 0x00000040,
BackgroundDownloadOn = 0x00010000,
};
// XCONFIG_USER_PC_FLAGS
enum X_PC_FLAGS : uint8_t {
XBLAllowed = 0x01,
XBLMembershipCreationAllowed = 0x02,
XboxOneGameAllowed = 0x04,
PCEnabled = 0x80,
};
// XCONFIG_USER_PC_GAMES
enum X_PC_GAMES_FLAGS : uint32_t {
EarlyChildhoodMax = 0x00000000,
EveryoneMax = 0x00000002,
Everyone_10Max = 0x00000004,
TeenMax = 0x00000006,
MatureMax = 0x00000008,
NoGameRestrictions = 0x000000FF,
};
// XCONFIG_USER_PC_PASSWORD
enum X_PC_PASSWORD_FLAGS : uint32_t {
XButton = 0x00000001,
YButton = 0x00000002,
LeftButton = 0x00000003,
RightButton = 0x00000004,
UPButton = 0x00000005,
DownButton = 0x00000006,
LTButton = 0x00000009,
RTButton = 0x0000000A,
LBButton = 0x0000000B,
RBButton = 0x0000000C,
};
// XCONFIG_USER_PC_MOVIE
enum X_PC_MOVIE_FLAGS : uint32_t {
GeneralAudiences = 0x00000001,
ParentalGuidance = 0x00000003,
ParentalGuidance13 = 0x00000004,
Restricted = 0x00000006,
NoMovieRestrictions = 0x000000FF,
};
// XCONFIG_USER_PC_GAME_RATING
enum X_PC_GAME_RATING_FLAGS : uint32_t {
DefaultGame = 0x00000000,
};
// XCONFIG_USER_PC_MOVIE_RATING
enum X_PC_MOVIE_RATING_FLAGS : uint32_t {
DefaultMovie = 0x00000000,
};
// XCONFIG_USER_ARCADE_FLAGS
enum X_ARCADE_FLAGS : uint32_t {
AutoDownloadOff = 0x00000000,
AutoDownloadNewReleases = 0x00000001,
};
// XCONFIG_USER_PC_VERSION
enum X_PC_VERSION : uint32_t {
VersionOne = 0x00000001,
};
// XCONFIG_USER_PC_TV
enum X_PC_TV : uint32_t {
TVG = 0X00000006,
TVPG = 0X00000008,
TV14 = 0X0000000a,
TVMatureAudience = 0x0000000C,
NoTVRestrictions = 0X000000FF,
};
// XCONFIG_USER_PC_TV_RATING
enum X_PC_TV_RATING : uint32_t {
DefaultTV = 0X00000000,
};
// XCONFIG_USER_PC_EXPLICIT_VIDEO
enum X_PC_EXPLICIT_VIDEO : uint32_t {
ExplicitVideoAllowed = 0X000000FF,
ExplicitVideoBanned = 0X00000000,
};
// XCONFIG_USER_PC_EXPLICIT_VIDEO_RATING
enum X_PC_EXPLICIT_VIDEO_RATING : uint32_t {
ExplicitAllowed = 0X00000000,
};
// XCONFIG_USER_PC_UNRATED_VIDEO
enum X_PC_EXPLICIT_UNRATED : uint32_t {
ExplicitUnratedBanned = 0X00000000,
UnratedALL = 0X000000FF,
};
// XCONFIG_USER_PC_UNRATED_VIDEO_RATING
enum X_PC_EXPLICIT_UNRATED_RATING : uint32_t {
DefaultExplicitUnrated = 0X00000000,
};
// XCONFIG_USER_VIDEO_OUTPUT_BLACK_LEVELS
enum X_BLACK_LEVEL : uint32_t {
High = 0x00000100,
Intermediate = 0x00000200,
LevelNormal = 0x00000300,
};
// XCONFIG_CONSOLE_SCREENSAVER
enum X_SCREENSAVER : uint32_t {
ScreensaverOn = 0x000A,
ScreensaverOff = 0x1000,
};
// XCONFIG_CONSOLE_AUTO_SHUTDOWN
enum X_AUTO_SHUTDOWN : uint32_t {
AutoShutdownOff = 0x0000,
AutoShutdownOneHr = 0x003C,
AutoShutdownSixHr = 0x0168,
};
// XCONFIG_CONSOLE_CAMERA_SETTINGS
enum X_CAMERA_FLAGS : uint32_t {
// Room
RoomAuto = 0x00000000,
DarkWall = 0x00000001,
LightWall = 0x00000002,
// Lighting
LightingAuto = 0x00000000,
Incandescent = 0x00000004,
Flourescent = 0x00000008,
Daylight = 0x0000000C,
// Flourescent Anti-Flicker
AntiFlickerAuto = 0x00000000,
AntiFlickerOn = 0x00000001,
AntiFlickerOff = 0x00000002,
// Default
AutoAll = 0x00000001,
};
// XCONFIG_CONSOLE_KEYBOARD_LAYOUT
enum X_KEYBOARD_LAYOUT : uint16_t {
KeyboardDefault = 0x0000,
EnglishQWERTY = 0x0001,
};
} // namespace xboxkrnl
} // namespace kernel
} // namespace xe
#endif // XENIA_KERNEL_XBOXKRNL_XBOXKRNL_XCONFIG_H_

174
src/xenia/kernel/xconfig.cc Normal file
View File

@@ -0,0 +1,174 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2026 Xenia Canary. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/kernel/xconfig.h"
#include "xenia/base/logging.h"
#include "xenia/base/filesystem.h"
#include "xenia/base/byte_order.h"
#include "xenia/xbox.h"
#include <ranges>
namespace xe {
namespace kernel {
XConfig::XConfig(const std::filesystem::path& xconfig_path)
: file_path_(xconfig_path) {
if (!std::filesystem::exists(xconfig_path)) {
SetDefaults();
if (xe::filesystem::CreateEmptyFile(xconfig_path)) {
FlushToFile();
}
}
FILE* file = xe::filesystem::OpenFile(xconfig_path, "rb");
if (!file) {
SetDefaults();
return;
}
fread(&xconfig_data_, sizeof(XConfigData), 1, file);
fclose(file);
}
void XConfig::ReadSetting(const X_CONFIG_CATEGORY category,
const uint16_t setting_id, void* buffer) {
const auto setting = FindField(category, setting_id);
if (!setting) {
return;
}
std::lock_guard<xe_mutex> lock(lock_);
std::memcpy(buffer, CategoryBase(category) + setting->block_offset,
setting->size);
}
void XConfig::WriteSetting(const X_CONFIG_CATEGORY category,
const uint16_t setting_id, const void* buffer) {
const auto setting = FindField(category, setting_id);
if (!setting) {
return;
}
std::lock_guard<xe_mutex> lock(lock_);
std::memcpy(CategoryBase(category) + setting->block_offset, buffer,
setting->size);
FlushToFile();
}
uint16_t XConfig::GetSettingSize(const X_CONFIG_CATEGORY category,
const uint16_t setting_id) {
const auto setting = FindField(category, setting_id);
if (!setting) {
return 0;
}
return setting->size;
}
void XConfig::WriteXConfig(const XConfigData* data) {
xconfig_data_ = *data;
FlushToFile();
}
void XConfig::SetDefaults() {
xconfig_data_ = {};
xconfig_data_.secured.av_region = X_AV_REGION::NTSCM;
xconfig_data_.user.language = static_cast<uint32_t>(XLanguage::kEnglish);
xconfig_data_.user.country =
static_cast<uint8_t>(XOnlineCountry::kUnitedStates);
xconfig_data_.user.audio_flags = DolbyDigital | DolbyProLogic;
xconfig_data_.user.av_pack_hdmi_sz = XHDTVResolution.at(1).to_host();
xconfig_data_.user.av_pack_component_sz = XHDTVResolution.at(1).to_host();
xconfig_data_.user.av_pack_vga_sz = XVGAResolution.at(3).to_host();
xconfig_data_.user.retail_flags = DashboardInitialized;
xconfig_data_.user.video_flags = RatioNormal;
xconfig_data_.user.parental_control_flags =
XBLAllowed | XBLMembershipCreationAllowed;
xconfig_data_.user.parental_control_game = NoGameRestrictions;
xconfig_data_.user.music_volume = 0.7f;
const auto& tz = kTimezones[0x19];
xconfig_data_.user.time_zone_bias = tz.timezone_bias;
memcpy(xconfig_data_.user.tz_std_name.data(), tz.tz_std_name.data(), 4);
memcpy(xconfig_data_.user.tz_dlt_name.data(), tz.tz_dlt_name.data(), 4);
memcpy(xconfig_data_.user.tz_std_date.data(), tz.tz_std_date.data(), 4);
memcpy(xconfig_data_.user.tz_dlt_date.data(), tz.tz_dlt_date.data(), 4);
xconfig_data_.user.tz_std_bias = tz.tz_std_bias;
xconfig_data_.user.tz_dlt_bias = tz.tz_dlt_bias;
}
void XConfig::FlushToFile() {
if (!std::filesystem::exists(file_path_)) {
return;
}
FILE* file = xe::filesystem::OpenFile(file_path_, "wb");
if (!file) {
return;
}
fwrite(&xconfig_data_, 1, sizeof(XConfigData), file);
fclose(file);
}
const XConfig::FieldDescriptor* XConfig::FindField(X_CONFIG_CATEGORY category,
uint16_t setting) {
auto it = std::ranges::find_if(kFields, [&](const FieldDescriptor& f) {
return f.category == category && f.setting == setting;
});
return it == std::ranges::end(kFields) ? nullptr : &*it;
}
uint8_t* XConfig::CategoryBase(X_CONFIG_CATEGORY category) {
switch (category) {
case X_CONFIG_CATEGORY::XCONFIG_STATIC_CATEGORY:
return reinterpret_cast<uint8_t*>(&xconfig_data_.static_settings);
case X_CONFIG_CATEGORY::XCONFIG_STATISTIC_CATEGORY:
return reinterpret_cast<uint8_t*>(&xconfig_data_.statistic);
case X_CONFIG_CATEGORY::XCONFIG_SECURED_CATEGORY:
return reinterpret_cast<uint8_t*>(&xconfig_data_.secured);
case X_CONFIG_CATEGORY::XCONFIG_USER_CATEGORY:
return reinterpret_cast<uint8_t*>(&xconfig_data_.user);
case X_CONFIG_CATEGORY::XCONFIG_XNET_MACHINE_ACCOUNT_CATEGORY:
return reinterpret_cast<uint8_t*>(&xconfig_data_.xnet_machine_account);
case X_CONFIG_CATEGORY::XCONFIG_XNET_PARAMETERS_CATEGORY:
return reinterpret_cast<uint8_t*>(&xconfig_data_.xnet_parameters);
case X_CONFIG_CATEGORY::XCONFIG_MEDIA_CENTER_CATEGORY:
return reinterpret_cast<uint8_t*>(&xconfig_data_.media_center);
case X_CONFIG_CATEGORY::XCONFIG_CONSOLE_CATEGORY:
return reinterpret_cast<uint8_t*>(&xconfig_data_.console);
case X_CONFIG_CATEGORY::XCONFIG_DVD_CATEGORY:
return reinterpret_cast<uint8_t*>(&xconfig_data_.dvd);
case X_CONFIG_CATEGORY::XCONFIG_IPTV_CATEGORY:
return reinterpret_cast<uint8_t*>(&xconfig_data_.iptv);
case X_CONFIG_CATEGORY::XCONFIG_SYSTEM_CATEGORY:
return reinterpret_cast<uint8_t*>(&xconfig_data_.system);
}
return nullptr;
}
const uint8_t* XConfig::CategoryBase(X_CONFIG_CATEGORY category) const {
return const_cast<XConfig*>(this)->CategoryBase(category);
}
} // namespace kernel
} // namespace xe

1055
src/xenia/kernel/xconfig.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -16,6 +16,9 @@
#include "xenia/ui/imgui_drawer.h"
#include "xenia/ui/presenter.h"
DEFINE_int32(window_size_x, 1280, "Xenia window width", "UI");
DEFINE_int32(window_size_y, 720, "Xenia window height", "UI");
namespace xe {
namespace ui {
@@ -242,6 +245,7 @@ void Window::SetFullscreen(bool new_fullscreen) {
if (!CanApplyState()) {
return;
}
WindowDestructionReceiver destruction_receiver(this);
ApplyNewFullscreen();
if (destruction_receiver.IsWindowDestroyedOrStateInapplicable()) {
@@ -499,6 +503,7 @@ void Window::OnUsbDeviceChanged(
bool Window::OnActualSizeUpdate(
uint32_t new_physical_width, uint32_t new_physical_height,
WindowResizeAction cause_action,
WindowDestructionReceiver& destruction_receiver) {
if (actual_physical_width_ == new_physical_width &&
actual_physical_height_ == new_physical_height) {
@@ -508,6 +513,13 @@ bool Window::OnActualSizeUpdate(
actual_physical_height_ = new_physical_height;
// The listeners may reference the presenter, update the presenter first.
if (presenter_surface_) {
// Update variable only if window isn't in fullscreen mode.
if (!fullscreen_ && cause_action == WindowResizeAction::kManual) {
// Write new window size only if we know that window is present.
OVERRIDE_int32(window_size_x, SizeToLogical(new_physical_width));
OVERRIDE_int32(window_size_y, SizeToLogical(new_physical_height));
}
presenter_->OnSurfaceResizeFromUIThread();
}
UISetupEvent e(this);

View File

@@ -152,6 +152,12 @@ class Window {
kHidden,
};
enum class WindowResizeAction {
kManual,
kAutoMaximize,
kAutoMinimize,
};
static std::unique_ptr<Window> Create(WindowedAppContext& app_context,
const std::string_view title,
uint32_t desired_logical_width,
@@ -572,6 +578,7 @@ class Window {
// explicitly.
bool OnActualSizeUpdate(uint32_t new_physical_width,
uint32_t new_physical_height,
WindowResizeAction cause_action,
WindowDestructionReceiver& destruction_receiver);
void OnDesiredFullscreenUpdate(bool new_fullscreen) {
fullscreen_ = new_fullscreen;

View File

@@ -124,7 +124,7 @@ bool GTKWindow::OpenImpl() {
gtk_widget_get_allocation(drawing_area_, &drawing_area_allocation);
OnActualSizeUpdate(uint32_t(drawing_area_allocation.width),
uint32_t(drawing_area_allocation.height),
destruction_receiver);
WindowResizeAction::kManual, destruction_receiver);
if (destruction_receiver.IsWindowDestroyedOrClosed()) {
return true;
}
@@ -311,7 +311,7 @@ void GTKWindow::HandleSizeUpdate(
gtk_widget_get_allocation(drawing_area_, &drawing_area_allocation);
OnActualSizeUpdate(uint32_t(drawing_area_allocation.width),
uint32_t(drawing_area_allocation.height),
destruction_receiver);
WindowResizeAction::kManual, destruction_receiver);
if (destruction_receiver.IsWindowDestroyedOrClosed()) {
return;
}

View File

@@ -279,7 +279,7 @@ bool Win32Window::OpenImpl() {
if (GetClientRect(hwnd_, &shown_client_rect)) {
OnActualSizeUpdate(uint32_t(shown_client_rect.right),
uint32_t(shown_client_rect.bottom),
destruction_receiver);
WindowResizeAction::kManual, destruction_receiver);
if (destruction_receiver.IsWindowDestroyedOrClosed()) {
return true;
}
@@ -722,7 +722,7 @@ void Win32Window::ApplyFullscreenEntry(
}
void Win32Window::HandleSizeUpdate(
WindowDestructionReceiver& destruction_receiver) {
WindowDestructionReceiver& destruction_receiver, DWORD cause_action) {
if (!hwnd_) {
// Batched size update ended when the window has already been closed, for
// instance.
@@ -767,7 +767,10 @@ void Win32Window::HandleSizeUpdate(
RECT client_rect;
if (GetClientRect(hwnd_, &client_rect)) {
OnActualSizeUpdate(uint32_t(client_rect.right),
uint32_t(client_rect.bottom), destruction_receiver);
uint32_t(client_rect.bottom),
cause_action == 0 ? WindowResizeAction::kManual
: WindowResizeAction::kAutoMaximize,
destruction_receiver);
if (destruction_receiver.IsWindowDestroyedOrClosed()) {
return;
}
@@ -792,7 +795,7 @@ void Win32Window::EndBatchedSizeUpdate(
// handle the deferred messages twice.
if (batched_size_update_contained_wm_size_) {
batched_size_update_contained_wm_size_ = false;
HandleSizeUpdate(destruction_receiver);
HandleSizeUpdate(destruction_receiver, 0);
if (destruction_receiver.IsWindowDestroyed()) {
return;
}
@@ -1064,7 +1067,7 @@ LRESULT Win32Window::WndProc(HWND hWnd, UINT message, WPARAM wParam,
batched_size_update_contained_wm_size_ = true;
} else {
WindowDestructionReceiver destruction_receiver(this);
HandleSizeUpdate(destruction_receiver);
HandleSizeUpdate(destruction_receiver, wParam);
if (destruction_receiver.IsWindowDestroyedOrClosed()) {
break;
}

View File

@@ -76,7 +76,8 @@ class Win32Window : public Window {
void ApplyFullscreenEntry(WindowDestructionReceiver& destruction_receiver);
void HandleSizeUpdate(WindowDestructionReceiver& destruction_receiver);
void HandleSizeUpdate(WindowDestructionReceiver& destruction_receiver,
DWORD cause_action);
// For updating multiple factors that may influence the window size at once,
// without handling WM_SIZE multiple times (that may not only result in wasted
// handling, but also in the state potentially changed to an inconsistent one