/** ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** * Copyright 2021 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ #include "xenia/kernel/xam/apps/xam_app.h" #include "xenia/base/logging.h" #include "xenia/kernel/kernel_state.h" #include "xenia/kernel/xam/xam_content_device.h" #include "xenia/kernel/xenumerator.h" /* Notes: - Messages ids that start with 0x00021xxx are UI calls - Messages ids that start with 0x00023xxx are used for the user profile - Messages ids that start with 0x0002Bxxx are used by the Kinect device usually for camera related functions - Messages ids that start with 0x0002Cxxx are used by the XamNuiIdentity functions */ namespace xe { namespace kernel { namespace xam { namespace apps { XamApp::XamApp(KernelState* kernel_state) : App(kernel_state, 0xFE) {} X_HRESULT XamApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr, uint32_t buffer_length) { // NOTE: buffer_length may be zero or valid. auto buffer = memory_->TranslateVirtual(buffer_ptr); switch (message) { case 0x0002000E: { X_ENUMERATE_PARAM* data_ptr = reinterpret_cast(buffer); XELOGD( "XEnumerateCrossTitle({:04X}, {:04X}, {:04X}, {:04X}, {}, {}, " "{:04X})", data_ptr->user_index.get(), data_ptr->flags.get(), data_ptr->private_enum_structure_ptr.get(), data_ptr->buffer_ptr.get(), data_ptr->buffer_size.get(), data_ptr->items_requested.get(), data_ptr->items_returned_ptr.get()); if (!data_ptr->buffer_ptr || !data_ptr->private_enum_structure_ptr) { return X_E_INVALIDARG; } auto enum_struct = memory_->TranslateVirtual( data_ptr->private_enum_structure_ptr); auto e = kernel_state_->object_table()->LookupObject( enum_struct->handle); if (!e) { return X_E_INVALIDARG; } assert_true(enum_struct->magic == kXObjSignature); XCONTENT_CROSS_TITLE_DATA cross_title_data = {}; uint8_t* cross_title_data_ptr = reinterpret_cast(&cross_title_data); uint32_t item_count = 0; X_RESULT result = e->WriteItems(cross_title_data_ptr, data_ptr->buffer_size, &item_count); XCONTENT_DATA_INTERNAL* content_data_ptr = memory_->TranslateVirtual( data_ptr->buffer_ptr); assert_true(data_ptr->buffer_size == sizeof(XCONTENT_DATA_INTERNAL)); std::memset(content_data_ptr, 0, data_ptr->buffer_size); if (!result) { content_data_ptr->device_id = cross_title_data.content_data.device_id; content_data_ptr->content_type = cross_title_data.content_data.content_type; content_data_ptr->set_display_name( cross_title_data.content_data.display_name()); content_data_ptr->set_file_name( cross_title_data.content_data.file_name()); content_data_ptr->padding[0] = content_data_ptr->padding[1] = 0; content_data_ptr->title_id = cross_title_data.title_id; } result = X_HRESULT_FROM_WIN32(result); xe::be* items_returned_ptr = memory_->TranslateVirtual*>( data_ptr->items_returned_ptr); *items_returned_ptr = item_count; return result; } case 0x00020021: { struct XContentQueryVolumeDeviceType { char root_name[64]; xe::be is_title_process; xe::be device_type_ptr; xe::be overlapped_ptr; }* data = reinterpret_cast(buffer); assert_true(buffer_length == sizeof(XContentQueryVolumeDeviceType)); xe::be* device_type_ptr = memory_->TranslateVirtual*>( static_cast(data->device_type_ptr.get())); switch (kernel_state_->deployment_type_) { case XDeploymentType::kDownload: case XDeploymentType::kInstalledToHDD: { *device_type_ptr = DeviceType::HDD; } break; case XDeploymentType::kOpticalDisc: { *device_type_ptr = DeviceType::ODD; } break; default: { *device_type_ptr = DeviceType::Invalid; } break; } XELOGD("XContentQueryVolumeDeviceType('{}', {:08X}, {:08X}, {:08X})", data->root_name, static_cast(data->is_title_process.get()), static_cast(data->device_type_ptr.get()), static_cast(data->overlapped_ptr.get())); return X_E_SUCCESS; } case 0x00021012: { uint32_t enabled = xe::load_and_swap(buffer); XELOGD("XEnableGuestSignin: {}", enabled ? "true" : "false"); return X_E_SUCCESS; } case 0x00022005: { struct XTITLE_GET_DEPLOYMENT_TYPE { xe::be deployment_type_ptr; xe::be overlapped_ptr; }* data = reinterpret_cast(buffer); assert_true(!buffer_length || buffer_length == sizeof(XTITLE_GET_DEPLOYMENT_TYPE)); auto deployment_type = memory_->TranslateVirtual(data->deployment_type_ptr); *deployment_type = static_cast(kernel_state_->deployment_type_); XELOGD("XTitleGetDeploymentType({:08X}, {:08X}", data->deployment_type_ptr.get(), data->overlapped_ptr.get()); return X_E_SUCCESS; } case 0x0002B003: { // Games used in: // 4D5309C9 // It only receives buffer struct { xe::be unk1; xe::be unk2; xe::be unk3; }* args = memory_->TranslateVirtual(buffer_ptr); XELOGD("XamUnk2B003({:016X}, {:016X}, {:016X}), unimplemented", 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( 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}, " "arg2={:08X}", app_id(), message, buffer_ptr, buffer_length); return X_E_FAIL; } } // namespace apps } // namespace xam } // namespace kernel } // namespace xe