Files
Xenia-Canary/src/xenia/kernel/xam/xam_content_device.cc
2022-01-09 14:16:37 -06:00

175 lines
6.2 KiB
C++

/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2022 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/kernel/xam/xam_content_device.h"
#include "xenia/base/logging.h"
#include "xenia/base/math.h"
#include "xenia/kernel/kernel_state.h"
#include "xenia/kernel/util/shim_utils.h"
#include "xenia/kernel/xam/xam_private.h"
#include "xenia/kernel/xenumerator.h"
#include "xenia/xbox.h"
namespace xe {
namespace kernel {
namespace xam {
// TODO(gibbed): real information.
//
// Until we expose real information about a HDD device, we
// claim there is 3GB free on a 4GB dummy HDD.
//
// There is a possibility that certain games are bugged in that
// they incorrectly only look at the lower 32-bits of free_bytes,
// when it is a 64-bit value. Which means any size above ~4GB
// will not be recognized properly.
#define ONE_GB (1024ull * 1024ull * 1024ull)
static const DummyDeviceInfo dummy_hdd_device_info_ = {
DummyDeviceId::HDD, DeviceType::HDD,
20ull * ONE_GB, // 20GB
3ull * ONE_GB, // 3GB, so it looks a little used.
u"Dummy HDD",
};
static const DummyDeviceInfo dummy_odd_device_info_ = {
DummyDeviceId::ODD, DeviceType::ODD,
7ull * ONE_GB, // 7GB (rough maximum)
0ull * ONE_GB, // read-only FS, so no free space
u"Dummy ODD",
};
static const DummyDeviceInfo* dummy_device_infos_[] = {
&dummy_hdd_device_info_,
&dummy_odd_device_info_,
};
#undef ONE_GB
const DummyDeviceInfo* GetDummyDeviceInfo(uint32_t device_id) {
const auto& begin = std::begin(dummy_device_infos_);
const auto& end = std::end(dummy_device_infos_);
auto it = std::find_if(begin, end, [device_id](const auto& item) {
return static_cast<uint32_t>(item->device_id) == device_id;
});
return it == end ? nullptr : *it;
}
dword_result_t XamContentGetDeviceName_entry(dword_t device_id,
lpu16string_t name_buffer,
dword_t name_capacity) {
auto device_info = GetDummyDeviceInfo(device_id);
if (device_info == nullptr) {
return X_ERROR_DEVICE_NOT_CONNECTED;
}
auto name = std::u16string(device_info->name);
if (name_capacity < name.size() + 1) {
return X_ERROR_INSUFFICIENT_BUFFER;
}
xe::string_util::copy_and_swap_truncating(name_buffer, name, name_capacity);
return X_ERROR_SUCCESS;
}
DECLARE_XAM_EXPORT1(XamContentGetDeviceName, kContent, kImplemented);
dword_result_t XamContentGetDeviceState_entry(dword_t device_id,
lpunknown_t overlapped_ptr) {
auto device_info = GetDummyDeviceInfo(device_id);
if (device_info == nullptr) {
if (overlapped_ptr) {
kernel_state()->CompleteOverlappedImmediateEx(
overlapped_ptr, X_ERROR_FUNCTION_FAILED, X_ERROR_DEVICE_NOT_CONNECTED,
0);
return X_ERROR_IO_PENDING;
} else {
return X_ERROR_DEVICE_NOT_CONNECTED;
}
}
if (overlapped_ptr) {
kernel_state()->CompleteOverlappedImmediate(overlapped_ptr,
X_ERROR_SUCCESS);
return X_ERROR_IO_PENDING;
} else {
return X_ERROR_SUCCESS;
}
}
DECLARE_XAM_EXPORT1(XamContentGetDeviceState, kContent, kStub);
typedef struct {
xe::be<uint32_t> device_id;
xe::be<uint32_t> device_type;
xe::be<uint64_t> total_bytes;
xe::be<uint64_t> free_bytes;
union {
xe::be<uint16_t> name[28];
char16_t name_chars[28];
};
} X_CONTENT_DEVICE_DATA;
static_assert_size(X_CONTENT_DEVICE_DATA, 0x50);
dword_result_t XamContentGetDeviceData_entry(
dword_t device_id, pointer_t<X_CONTENT_DEVICE_DATA> device_data) {
auto device_info = GetDummyDeviceInfo(device_id);
if (device_info == nullptr) {
return X_ERROR_DEVICE_NOT_CONNECTED;
}
device_data.Zero();
device_data->device_id = static_cast<uint32_t>(device_info->device_id);
device_data->device_type = static_cast<uint32_t>(device_info->device_type);
device_data->total_bytes = device_info->total_bytes;
device_data->free_bytes = device_info->free_bytes;
xe::string_util::copy_and_swap_truncating(
device_data->name_chars, device_info->name,
xe::countof(device_data->name_chars));
return X_ERROR_SUCCESS;
}
DECLARE_XAM_EXPORT1(XamContentGetDeviceData, kContent, kImplemented);
dword_result_t XamContentCreateDeviceEnumerator_entry(dword_t content_type,
dword_t content_flags,
dword_t max_count,
lpdword_t buffer_size_ptr,
lpdword_t handle_out) {
assert_not_null(handle_out);
if (buffer_size_ptr) {
*buffer_size_ptr = sizeof(X_CONTENT_DEVICE_DATA) * max_count;
}
auto e = make_object<XStaticEnumerator<X_CONTENT_DEVICE_DATA>>(kernel_state(),
max_count);
auto result = e->Initialize(0xFE, 0xFE, 0x2000A, 0x20009, 0);
if (XFAILED(result)) {
return result;
}
for (const auto& device_info : dummy_device_infos_) {
// Copy our dummy device into the enumerator
auto device_data = e->AppendItem();
assert_not_null(device_data);
if (device_data) {
device_data->device_id = static_cast<uint32_t>(device_info->device_id);
device_data->device_type =
static_cast<uint32_t>(device_info->device_type);
device_data->total_bytes = device_info->total_bytes;
device_data->free_bytes = device_info->free_bytes;
xe::string_util::copy_and_swap_truncating(
device_data->name_chars, device_info->name,
xe::countof(device_data->name_chars));
}
}
*handle_out = e->handle();
return X_ERROR_SUCCESS;
}
DECLARE_XAM_EXPORT1(XamContentCreateDeviceEnumerator, kNone, kImplemented);
} // namespace xam
} // namespace kernel
} // namespace xe
DECLARE_XAM_EMPTY_REGISTER_EXPORTS(ContentDevice);