diff --git a/src/poly/fs_win.cc b/src/poly/fs_win.cc index 2a414f2b5..8090d27f4 100644 --- a/src/poly/fs_win.cc +++ b/src/poly/fs_win.cc @@ -47,18 +47,25 @@ std::vector ListFiles(const std::wstring& path) { std::vector result; WIN32_FIND_DATA ffd; - HANDLE handle = FindFirstFile(path.c_str(), &ffd); + HANDLE handle = FindFirstFile((path + L"\\*").c_str(), &ffd); if (handle == INVALID_HANDLE_VALUE) { return result; } do { + if (std::wcscmp(ffd.cFileName, L".") == 0 || + std::wcscmp(ffd.cFileName, L"..") == 0) { + continue; + } FileInfo info; if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { info.type = FileInfo::Type::kDirectory; + info.total_size = 0; } else { info.type = FileInfo::Type::kFile; - info.total_size = (ffd.nFileSizeHigh * (MAXDWORD + 1)) + ffd.nFileSizeLow; + info.total_size = + (ffd.nFileSizeHigh * (size_t(MAXDWORD) + 1)) + ffd.nFileSizeLow; } + info.name = ffd.cFileName; result.push_back(info); } while (FindNextFile(handle, &ffd) != 0); FindClose(handle); diff --git a/src/xenia/kernel/content_manager.cc b/src/xenia/kernel/content_manager.cc index 43d0f1f1b..05f77ec7c 100644 --- a/src/xenia/kernel/content_manager.cc +++ b/src/xenia/kernel/content_manager.cc @@ -28,8 +28,7 @@ ContentPackage::ContentPackage(KernelState* kernel_state, std::string root_name, device_path_ = std::string("\\Device\\Content\\") + std::to_string(++content_device_id_) + "\\"; kernel_state_->file_system()->RegisterHostPathDevice(device_path_, - package_path, - false); + package_path, false); kernel_state_->file_system()->CreateSymbolicLink(root_name_ + ":", device_path_); } @@ -45,12 +44,12 @@ ContentManager::ContentManager(KernelState* kernel_state, ContentManager::~ContentManager() = default; -std::wstring ContentManager::ResolvePackagePath(const XCONTENT_DATA& data) { +std::wstring ContentManager::ResolvePackageRoot(uint32_t content_type) { wchar_t title_id[9] = L"00000000"; std::swprintf(title_id, 9, L"%.8X", kernel_state_->title_id()); std::wstring type_name; - switch (data.content_type) { + switch (content_type) { case 1: // Save games. type_name = L"00000001"; @@ -68,18 +67,47 @@ std::wstring ContentManager::ResolvePackagePath(const XCONTENT_DATA& data) { return nullptr; } + // Package root path: + // content_root/title_id/type_name/ + auto package_root = + poly::join_paths(root_path_, poly::join_paths(title_id, type_name)); + return package_root + L"\\"; +} + +std::wstring ContentManager::ResolvePackagePath(const XCONTENT_DATA& data) { // Content path: // content_root/title_id/type_name/data_file_name/ - std::wstring package_path = poly::join_paths( - root_path_, - poly::join_paths( - title_id, - poly::join_paths(type_name, poly::to_wstring(data.file_name)))); + auto package_root = ResolvePackageRoot(data.content_type); + auto package_path = + poly::join_paths(package_root, poly::to_wstring(data.file_name)); package_path += poly::path_separator; - return package_path; } +std::vector ContentManager::ListContent(uint32_t device_id, + uint32_t content_type) { + std::vector result; + + // Search path: + // content_root/title_id/type_name/* + auto package_root = ResolvePackageRoot(content_type); + auto file_infos = poly::fs::ListFiles(package_root); + for (const auto& file_info : file_infos) { + if (file_info.type != poly::fs::FileInfo::Type::kDirectory) { + // Directories only. + continue; + } + XCONTENT_DATA content_data; + content_data.device_id = device_id; + content_data.content_type = content_type; + content_data.display_name = file_info.name; + content_data.file_name = poly::to_string(file_info.name); + result.emplace_back(std::move(content_data)); + } + + return result; +} + std::unique_ptr ContentManager::ResolvePackage( std::string root_name, const XCONTENT_DATA& data) { auto package_path = ResolvePackagePath(data); @@ -174,6 +202,7 @@ X_RESULT ContentManager::GetContentThumbnail(const XCONTENT_DATA& data, auto file = _wfopen(thumb_path.c_str(), L"rb"); fseek(file, 0, SEEK_END); size_t file_len = ftell(file); + fseek(file, 0, SEEK_SET); buffer->resize(file_len); fread(const_cast(buffer->data()), 1, buffer->size(), file); fclose(file); diff --git a/src/xenia/kernel/content_manager.h b/src/xenia/kernel/content_manager.h index a88c46e28..45128aff5 100644 --- a/src/xenia/kernel/content_manager.h +++ b/src/xenia/kernel/content_manager.h @@ -26,10 +26,12 @@ namespace kernel { class KernelState; struct XCONTENT_DATA { + static const size_t kSize = 4 + 4 + 128 * 2 + 42 + 2; // = 306 + 2b padding uint32_t device_id; uint32_t content_type; std::wstring display_name; // 128 chars std::string file_name; + XCONTENT_DATA() = default; XCONTENT_DATA(const uint8_t* ptr) { device_id = poly::load_and_swap(ptr + 0); @@ -37,6 +39,13 @@ struct XCONTENT_DATA { display_name = poly::load_and_swap(ptr + 8); file_name = poly::load_and_swap(ptr + 8 + 128 * 2); } + + void Write(uint8_t* ptr) { + poly::store_and_swap(ptr + 0, device_id); + poly::store_and_swap(ptr + 4, content_type); + poly::store_and_swap(ptr + 8, display_name); + poly::store_and_swap(ptr + 8 + 128 * 2, file_name); + } }; class ContentPackage { @@ -56,6 +65,9 @@ class ContentManager { ContentManager(KernelState* kernel_state, std::wstring root_path); ~ContentManager(); + std::vector ListContent(uint32_t device_id, + uint32_t content_type); + std::unique_ptr ResolvePackage(std::string root_name, const XCONTENT_DATA& data); @@ -70,6 +82,7 @@ class ContentManager { X_RESULT DeleteContent(const XCONTENT_DATA& data); private: + std::wstring ResolvePackageRoot(uint32_t content_type); std::wstring ResolvePackagePath(const XCONTENT_DATA& data); KernelState* kernel_state_; diff --git a/src/xenia/kernel/objects/xenumerator.cc b/src/xenia/kernel/objects/xenumerator.cc index 21c0e51e2..c917a39a7 100644 --- a/src/xenia/kernel/objects/xenumerator.cc +++ b/src/xenia/kernel/objects/xenumerator.cc @@ -12,10 +12,14 @@ namespace xe { namespace kernel { -XEnumerator::XEnumerator(KernelState* kernel_state) - : XObject(kernel_state, kTypeEnumerator) {} +XEnumerator::XEnumerator(KernelState* kernel_state, size_t item_capacity, + size_t item_size) + : XObject(kernel_state, kTypeEnumerator), + item_capacity_(item_capacity), + item_size_(item_size) +{} -XEnumerator::~XEnumerator() {} +XEnumerator::~XEnumerator() = default; void XEnumerator::Initialize() {} diff --git a/src/xenia/kernel/objects/xenumerator.h b/src/xenia/kernel/objects/xenumerator.h index 2f6f32499..85612acbc 100644 --- a/src/xenia/kernel/objects/xenumerator.h +++ b/src/xenia/kernel/objects/xenumerator.h @@ -18,12 +18,46 @@ namespace kernel { class XEnumerator : public XObject { public: - XEnumerator(KernelState* kernel_state); + XEnumerator(KernelState* kernel_state, size_t item_capacity, + size_t item_size); virtual ~XEnumerator(); void Initialize(); + virtual uint32_t item_count() const = 0; + virtual void WriteItems(uint8_t* buffer) = 0; + + protected: + size_t item_capacity_; + size_t item_size_; +}; + +class XStaticEnumerator : public XEnumerator { + public: + XStaticEnumerator(KernelState* kernel_state, size_t item_capacity, + size_t item_size) + : XEnumerator(kernel_state, item_capacity, item_size), item_count_(0) { + buffer_.resize(item_capacity_ * item_size_); + } + + uint32_t item_count() const override { return item_count_; } + + uint8_t* AppendItem() { + if (item_count_ + 1 > item_capacity_) { + return nullptr; + } + auto ptr = const_cast(buffer_.data() + item_count_ * item_size_); + ++item_count_; + return ptr; + } + + void WriteItems(uint8_t* buffer) override { + std::memcpy(buffer, buffer_.data(), item_count_ * item_size_); + } + private: + uint32_t item_count_; + std::vector buffer_; }; } // namespace kernel diff --git a/src/xenia/kernel/xam_content.cc b/src/xenia/kernel/xam_content.cc index 9d1822ea8..913b9abdc 100644 --- a/src/xenia/kernel/xam_content.cc +++ b/src/xenia/kernel/xam_content.cc @@ -202,16 +202,31 @@ SHIM_CALL XamContentCreateEnumerator_shim(PPCContext* ppc_state, SHIM_SET_RETURN_32(X_E_INVALIDARG); return; } - - // 4 + 4 + 128*2 + 42 = 306 - if (buffer_size_ptr) { - uint32_t bp = SHIM_MEM_32(buffer_size_ptr); - SHIM_SET_MEM_32(buffer_size_ptr, item_count * 306); + if (!device_id) { + // 0 == whatever + device_id = dummy_device_info_.device_id; } - XEnumerator* e = new XEnumerator(state); + if (buffer_size_ptr) { + uint32_t bp = SHIM_MEM_32(buffer_size_ptr); + SHIM_SET_MEM_32(buffer_size_ptr, item_count * XCONTENT_DATA::kSize); + } + + auto e = new XStaticEnumerator(state, item_count, XCONTENT_DATA::kSize); e->Initialize(); + // Get all content data. + auto content_datas = + state->content_manager()->ListContent(device_id, content_type); + for (auto& content_data : content_datas) { + auto ptr = e->AppendItem(); + if (!ptr) { + // Too many items. + break; + } + content_data.Write(ptr); + } + SHIM_SET_MEM_32(handle_ptr, e->handle()); SHIM_SET_RETURN_32(X_ERROR_SUCCESS); diff --git a/src/xenia/kernel/xam_info.cc b/src/xenia/kernel/xam_info.cc index 1208d4de1..f741d111d 100644 --- a/src/xenia/kernel/xam_info.cc +++ b/src/xenia/kernel/xam_info.cc @@ -160,15 +160,17 @@ SHIM_CALL XamEnumerate_shim(PPCContext* ppc_state, KernelState* state) { return; } - // 0 items. + auto item_count = e->item_count(); + e->WriteItems(SHIM_MEM_ADDR(buffer_ptr)); + X_RESULT result; if (item_count_ptr) { assert_zero(overlapped_ptr); - SHIM_SET_MEM_32(item_count_ptr, 0); + SHIM_SET_MEM_32(item_count_ptr, item_count); result = X_ERROR_SUCCESS; } else if (overlapped_ptr) { assert_zero(item_count_ptr); - state->CompleteOverlappedImmediate(overlapped_ptr, 0, 0); + state->CompleteOverlappedImmediate(overlapped_ptr, 0, item_count); result = X_ERROR_IO_PENDING; } else { assert_always(); diff --git a/src/xenia/kernel/xam_user.cc b/src/xenia/kernel/xam_user.cc index 6cbcf28a4..690dfbc32 100644 --- a/src/xenia/kernel/xam_user.cc +++ b/src/xenia/kernel/xam_user.cc @@ -352,7 +352,7 @@ SHIM_CALL XamUserCreateAchievementEnumerator_shim(PPCContext* ppc_state, SHIM_SET_MEM_32(buffer_size_ptr, 64 * count); } - XEnumerator* e = new XEnumerator(state); + auto e = new XStaticEnumerator(state, count, 64); e->Initialize(); SHIM_SET_MEM_32(handle_ptr, e->handle());