Most of XamContent* methods, besides enumeration.

Progress on #152.
This commit is contained in:
Ben Vanik 2015-02-12 14:16:43 -08:00
parent 53eaeff690
commit dc731f6a31
25 changed files with 849 additions and 14 deletions

1
.gitignore vendored
View File

@ -79,6 +79,7 @@ build-test/
.vagrant
attic/
content/
third_party/binutils/binutils-2.24.tar.gz
third_party/binutils/bin/
third_party/binutils/powerpc-none-elf/

41
src/poly/fs.h Normal file
View File

@ -0,0 +1,41 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef POLY_FS_H_
#define POLY_FS_H_
#include <string>
#include "poly/config.h"
#include "poly/string.h"
namespace poly {
namespace fs {
bool PathExists(const std::wstring& path);
bool CreateFolder(const std::wstring& path);
bool DeleteFolder(const std::wstring& path);
struct FileInfo {
enum class Type {
kFile,
kDirectory,
};
Type type;
std::wstring name;
size_t total_size;
};
std::vector<FileInfo> ListFiles(const std::wstring& path);
} // namespace fs
} // namespace poly
#endif // POLY_FS_H_

70
src/poly/fs_win.cc Normal file
View File

@ -0,0 +1,70 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "poly/fs.h"
#include <shellapi.h>
#include <string>
#include "poly/platform.h"
namespace poly {
namespace fs {
bool PathExists(const std::wstring& path) {
DWORD attrib = GetFileAttributes(path.c_str());
return attrib != INVALID_FILE_ATTRIBUTES;
}
bool CreateFolder(const std::wstring& path) {
wchar_t folder[MAX_PATH] = {0};
auto end = std::wcschr(path.c_str(), L'\\');
while (end) {
wcsncpy(folder, path.c_str(), end - path.c_str() + 1);
CreateDirectory(folder, NULL);
end = wcschr(++end, L'\\');
}
return PathExists(path);
}
bool DeleteFolder(const std::wstring& path) {
auto double_null_path = path + L"\0";
SHFILEOPSTRUCT op = {0};
op.wFunc = FO_DELETE;
op.pFrom = double_null_path.c_str();
op.fFlags = FOF_NO_UI;
return SHFileOperation(&op) == 0;
}
std::vector<FileInfo> ListFiles(const std::wstring& path) {
std::vector<FileInfo> result;
WIN32_FIND_DATA ffd;
HANDLE handle = FindFirstFile(path.c_str(), &ffd);
if (handle == INVALID_HANDLE_VALUE) {
return result;
}
do {
FileInfo info;
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
info.type = FileInfo::Type::kDirectory;
} else {
info.type = FileInfo::Type::kFile;
info.total_size = (ffd.nFileSizeHigh * (MAXDWORD + 1)) + ffd.nFileSizeLow;
}
result.push_back(info);
} while (FindNextFile(handle, &ffd) != 0);
FindClose(handle);
return result;
}
} // namespace fs
} // namespace poly

View File

@ -8,6 +8,7 @@
'delegate.h',
'config.h',
'cxx_compat.h',
'fs.h',
'logging.cc',
'logging.h',
'main.h',
@ -45,6 +46,7 @@
['OS == "win"', {
'sources': [
'debugging_win.cc',
'fs_win.cc',
'main_win.cc',
'mapped_memory_win.cc',
'threading_win.cc',

View File

@ -0,0 +1,214 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/kernel/content_manager.h"
#include <string>
#include "poly/fs.h"
#include "xenia/kernel/xobject.h"
namespace xe {
namespace kernel {
static const wchar_t* kThumbnailFileName = L"__thumbnail.png";
static int content_device_id_ = 0;
ContentPackage::ContentPackage(KernelState* kernel_state, std::string root_name,
const XCONTENT_DATA& data,
std::wstring package_path)
: kernel_state_(kernel_state), root_name_(std::move(root_name)) {
device_path_ = std::string("\\Device\\Content\\") +
std::to_string(++content_device_id_) + "\\";
kernel_state_->file_system()->RegisterHostPathDevice(device_path_,
package_path,
false);
kernel_state_->file_system()->CreateSymbolicLink(root_name_ + ":",
device_path_);
}
ContentPackage::~ContentPackage() {
kernel_state_->file_system()->DeleteSymbolicLink(root_name_ + ":");
// TODO(benvanik): unregister device.
}
ContentManager::ContentManager(KernelState* kernel_state,
std::wstring root_path)
: kernel_state_(kernel_state), root_path_(std::move(root_path)) {}
ContentManager::~ContentManager() = default;
std::wstring ContentManager::ResolvePackagePath(const XCONTENT_DATA& data) {
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) {
case 1:
// Save games.
type_name = L"00000001";
break;
case 2:
// DLC from the marketplace.
type_name = L"00000002";
break;
case 3:
// Publisher content?
type_name = L"00000003";
break;
default:
assert_unhandled_case(data.content_type);
return nullptr;
}
// 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))));
package_path += poly::path_separator;
return package_path;
}
std::unique_ptr<ContentPackage> ContentManager::ResolvePackage(
std::string root_name, const XCONTENT_DATA& data) {
auto package_path = ResolvePackagePath(data);
if (!poly::fs::PathExists(package_path)) {
return nullptr;
}
std::lock_guard<std::recursive_mutex> lock(content_mutex_);
auto package = std::make_unique<ContentPackage>(kernel_state_, root_name,
data, package_path);
return package;
}
bool ContentManager::ContentExists(const XCONTENT_DATA& data) {
auto path = ResolvePackagePath(data);
return poly::fs::PathExists(path);
}
X_RESULT ContentManager::CreateContent(std::string root_name,
const XCONTENT_DATA& data) {
std::lock_guard<std::recursive_mutex> lock(content_mutex_);
if (open_packages_.count(root_name)) {
// Already content open with this root name.
return X_ERROR_INVALID_NAME;
}
auto package_path = ResolvePackagePath(data);
if (poly::fs::PathExists(package_path)) {
// Exists, must not!
return X_ERROR_ALREADY_EXISTS;
}
if (!poly::fs::CreateFolder(package_path)) {
return X_ERROR_ACCESS_DENIED;
}
auto package = ResolvePackage(root_name, data);
assert_not_null(package);
open_packages_.insert({root_name, package.release()});
return X_ERROR_SUCCESS;
}
X_RESULT ContentManager::OpenContent(std::string root_name,
const XCONTENT_DATA& data) {
std::lock_guard<std::recursive_mutex> lock(content_mutex_);
if (open_packages_.count(root_name)) {
// Already content open with this root name.
return X_ERROR_INVALID_NAME;
}
auto package_path = ResolvePackagePath(data);
if (!poly::fs::PathExists(package_path)) {
// Does not exist, must be created.
return X_ERROR_FILE_NOT_FOUND;
}
// Open package.
auto package = ResolvePackage(root_name, data);
assert_not_null(package);
open_packages_.insert({root_name, package.release()});
return X_ERROR_SUCCESS;
}
X_RESULT ContentManager::CloseContent(std::string root_name) {
std::lock_guard<std::recursive_mutex> lock(content_mutex_);
auto it = open_packages_.find(root_name);
if (it == open_packages_.end()) {
return X_ERROR_FILE_NOT_FOUND;
}
auto package = it->second;
open_packages_.erase(it);
delete package;
return X_ERROR_SUCCESS;
}
X_RESULT ContentManager::GetContentThumbnail(const XCONTENT_DATA& data,
std::vector<uint8_t>* buffer) {
std::lock_guard<std::recursive_mutex> lock(content_mutex_);
auto package_path = ResolvePackagePath(data);
auto thumb_path = poly::join_paths(package_path, kThumbnailFileName);
if (poly::fs::PathExists(thumb_path)) {
auto file = _wfopen(thumb_path.c_str(), L"rb");
fseek(file, 0, SEEK_END);
size_t file_len = ftell(file);
buffer->resize(file_len);
fread(const_cast<uint8_t*>(buffer->data()), 1, buffer->size(), file);
fclose(file);
return X_ERROR_SUCCESS;
} else {
return X_ERROR_FILE_NOT_FOUND;
}
}
X_RESULT ContentManager::SetContentThumbnail(const XCONTENT_DATA& data,
std::vector<uint8_t> buffer) {
std::lock_guard<std::recursive_mutex> lock(content_mutex_);
auto package_path = ResolvePackagePath(data);
if (poly::fs::PathExists(package_path)) {
auto thumb_path = poly::join_paths(package_path, kThumbnailFileName);
auto file = _wfopen(thumb_path.c_str(), L"wb");
fwrite(buffer.data(), 1, buffer.size(), file);
fclose(file);
return X_ERROR_SUCCESS;
} else {
return X_ERROR_FILE_NOT_FOUND;
}
}
X_RESULT ContentManager::DeleteContent(const XCONTENT_DATA& data) {
std::lock_guard<std::recursive_mutex> lock(content_mutex_);
auto package_path = ResolvePackagePath(data);
if (poly::fs::PathExists(package_path)) {
poly::fs::DeleteFolder(package_path);
return X_ERROR_SUCCESS;
} else {
return X_ERROR_FILE_NOT_FOUND;
}
}
} // namespace kernel
} // namespace xe

View File

@ -0,0 +1,85 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_KERNEL_CONTENT_MANAGER_H_
#define XENIA_KERNEL_CONTENT_MANAGER_H_
#include <memory>
#include <mutex>
#include <string>
#include <unordered_map>
#include <vector>
#include "poly/memory.h"
#include "xenia/common.h"
#include "xenia/xbox.h"
namespace xe {
namespace kernel {
class KernelState;
struct XCONTENT_DATA {
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<uint32_t>(ptr + 0);
content_type = poly::load_and_swap<uint32_t>(ptr + 4);
display_name = poly::load_and_swap<std::wstring>(ptr + 8);
file_name = poly::load_and_swap<std::string>(ptr + 8 + 128 * 2);
}
};
class ContentPackage {
public:
ContentPackage(KernelState* kernel_state, std::string root_name,
const XCONTENT_DATA& data, std::wstring package_path);
~ContentPackage();
private:
KernelState* kernel_state_;
std::string root_name_;
std::string device_path_;
};
class ContentManager {
public:
ContentManager(KernelState* kernel_state, std::wstring root_path);
~ContentManager();
std::unique_ptr<ContentPackage> ResolvePackage(std::string root_name,
const XCONTENT_DATA& data);
bool ContentExists(const XCONTENT_DATA& data);
X_RESULT CreateContent(std::string root_name, const XCONTENT_DATA& data);
X_RESULT OpenContent(std::string root_name, const XCONTENT_DATA& data);
X_RESULT CloseContent(std::string root_name);
X_RESULT GetContentThumbnail(const XCONTENT_DATA& data,
std::vector<uint8_t>* buffer);
X_RESULT SetContentThumbnail(const XCONTENT_DATA& data,
std::vector<uint8_t> buffer);
X_RESULT DeleteContent(const XCONTENT_DATA& data);
private:
std::wstring ResolvePackagePath(const XCONTENT_DATA& data);
KernelState* kernel_state_;
std::wstring root_path_;
std::recursive_mutex content_mutex_;
std::unordered_map<std::string, ContentPackage*> open_packages_;
};
} // namespace kernel
} // namespace xe
#endif // XENIA_KERNEL_CONTENT_MANAGER_H_

View File

@ -27,6 +27,8 @@ class Device {
const std::string& path() const { return path_; }
virtual bool is_read_only() const { return true; }
virtual std::unique_ptr<Entry> ResolvePath(const char* path) = 0;
virtual X_STATUS QueryVolume(XVolumeInfo* out_info, size_t length);

View File

@ -17,8 +17,8 @@ namespace kernel {
namespace fs {
HostPathDevice::HostPathDevice(const std::string& path,
const std::wstring& local_path)
: Device(path), local_path_(local_path) {}
const std::wstring& local_path, bool read_only)
: Device(path), local_path_(local_path), read_only_(read_only) {}
HostPathDevice::~HostPathDevice() {}

View File

@ -21,13 +21,17 @@ namespace fs {
class HostPathDevice : public Device {
public:
HostPathDevice(const std::string& path, const std::wstring& local_path);
HostPathDevice(const std::string& path, const std::wstring& local_path,
bool read_only);
~HostPathDevice() override;
bool is_read_only() const { return read_only_; }
std::unique_ptr<Entry> ResolvePath(const char* path) override;
private:
std::wstring local_path_;
bool read_only_;
};
} // namespace fs

View File

@ -140,7 +140,7 @@ X_STATUS HostPathEntry::Open(KernelState* kernel_state, Mode mode, bool async,
DWORD desired_access =
mode == Mode::READ ? GENERIC_READ : (GENERIC_READ | GENERIC_WRITE);
DWORD share_mode = FILE_SHARE_READ;
DWORD creation_disposition = OPEN_EXISTING;
DWORD creation_disposition = mode == Mode::READ ? OPEN_EXISTING : OPEN_ALWAYS;
DWORD flags_and_attributes = async ? FILE_FLAG_OVERLAPPED : 0;
HANDLE file =
CreateFile(local_path_.c_str(), desired_access, share_mode, NULL,

View File

@ -69,6 +69,23 @@ X_STATUS HostPathFile::ReadSync(void* buffer, size_t buffer_length,
}
}
X_STATUS HostPathFile::WriteSync(const void* buffer, size_t buffer_length,
size_t byte_offset,
size_t* out_bytes_written) {
OVERLAPPED overlapped;
overlapped.Pointer = (PVOID)byte_offset;
overlapped.hEvent = NULL;
DWORD bytes_written = 0;
BOOL wrote = WriteFile(file_handle_, buffer, (DWORD)buffer_length,
&bytes_written, &overlapped);
if (wrote) {
*out_bytes_written = bytes_written;
return X_STATUS_SUCCESS;
} else {
return X_STATUS_END_OF_FILE;
}
}
} // namespace fs
} // namespace kernel
} // namespace xe

View File

@ -41,6 +41,8 @@ class HostPathFile : public XFile {
protected:
X_STATUS ReadSync(void* buffer, size_t buffer_length, size_t byte_offset,
size_t* out_bytes_read) override;
X_STATUS WriteSync(const void* buffer, size_t buffer_length,
size_t byte_offset, size_t* out_bytes_written) override;
private:
HostPathEntry* entry_;

View File

@ -29,6 +29,10 @@ Entry::Entry(Device* device, const std::string& path)
Entry::~Entry() = default;
bool Entry::is_read_only() const {
return device_->is_read_only();
}
} // namespace fs
} // namespace kernel
} // namespace xe

View File

@ -61,6 +61,8 @@ class Entry {
const std::string& absolute_path() const { return absolute_path_; }
const std::string& name() const { return name_; }
bool is_read_only() const;
virtual X_STATUS QueryInfo(XFileInfo* out_info) = 0;
virtual X_STATUS QueryDirectory(XDirectoryInfo* out_info, size_t length,
const char* file_name, bool restart) = 0;

View File

@ -74,7 +74,7 @@ int FileSystem::InitializeFromPath(fs::FileSystemType type,
// Register the local directory in the virtual filesystem.
int result_code = RegisterHostPathDevice(
"\\Device\\Harddisk1\\Partition0", parent_path);
"\\Device\\Harddisk1\\Partition0", parent_path, true);
if (result_code) {
XELOGE("Unable to mount local directory");
return result_code;
@ -108,8 +108,9 @@ int FileSystem::RegisterDevice(const std::string& path, Device* device) {
}
int FileSystem::RegisterHostPathDevice(const std::string& path,
const std::wstring& local_path) {
Device* device = new HostPathDevice(path, local_path);
const std::wstring& local_path,
bool read_only) {
Device* device = new HostPathDevice(path, local_path, read_only);
return RegisterDevice(path, device);
}

View File

@ -40,7 +40,8 @@ class FileSystem {
int RegisterDevice(const std::string& path, Device* device);
int RegisterHostPathDevice(const std::string& path,
const std::wstring& local_path);
const std::wstring& local_path,
bool read_only);
int RegisterDiscImageDevice(const std::string& path,
const std::wstring& local_path);
int RegisterSTFSContainerDevice(const std::string& path,

View File

@ -9,6 +9,8 @@
#include "xenia/kernel/kernel_state.h"
#include <gflags/gflags.h>
#include "xenia/emulator.h"
#include "xenia/kernel/dispatcher.h"
#include "xenia/kernel/xam_module.h"
@ -22,6 +24,9 @@
#include "xenia/kernel/objects/xthread.h"
#include "xenia/kernel/objects/xuser_module.h"
DEFINE_string(content_root, "content",
"Root path for content (save/etc) storage.");
namespace xe {
namespace kernel {
@ -45,6 +50,10 @@ KernelState::KernelState(Emulator* emulator)
app_manager_ = std::make_unique<XAppManager>();
user_profile_ = std::make_unique<UserProfile>();
auto content_root = poly::to_wstring(FLAGS_content_root);
content_root = poly::to_absolute_path(content_root);
content_manager_ = std::make_unique<ContentManager>(this, content_root);
object_table_ = new ObjectTable();
assert_null(shared_kernel_state_);
@ -70,6 +79,11 @@ KernelState::~KernelState() {
KernelState* KernelState::shared() { return shared_kernel_state_; }
uint32_t KernelState::title_id() const {
assert_not_null(executable_module_);
return executable_module_->xex_header()->execution_info.title_id;
}
void KernelState::RegisterModule(XModule* module) {}
void KernelState::UnregisterModule(XModule* module) {}

View File

@ -16,6 +16,7 @@
#include "xenia/common.h"
#include "xenia/export_resolver.h"
#include "xenia/kernel/app.h"
#include "xenia/kernel/content_manager.h"
#include "xenia/kernel/fs/filesystem.h"
#include "xenia/kernel/object_table.h"
#include "xenia/kernel/user_profile.h"
@ -49,11 +50,14 @@ class KernelState {
Memory* memory() const { return memory_; }
cpu::Processor* processor() const { return processor_; }
fs::FileSystem* file_system() const { return file_system_; }
uint32_t title_id() const;
Dispatcher* dispatcher() const { return dispatcher_; }
XAppManager* app_manager() const { return app_manager_.get(); }
UserProfile* user_profile() const { return user_profile_.get(); }
ContentManager* content_manager() const { return content_manager_.get(); }
ObjectTable* object_table() const { return object_table_; }
std::mutex& object_mutex() { return object_mutex_; }
@ -90,6 +94,7 @@ class KernelState {
std::unique_ptr<XAppManager> app_manager_;
std::unique_ptr<UserProfile> user_profile_;
std::unique_ptr<ContentManager> content_manager_;
ObjectTable* object_table_;
std::mutex object_mutex_;

View File

@ -53,5 +53,19 @@ X_STATUS XFile::Read(void* buffer, size_t buffer_length, size_t byte_offset,
return result;
}
X_STATUS XFile::Write(const void* buffer, size_t buffer_length,
size_t byte_offset, size_t* out_bytes_written) {
if (byte_offset == -1) {
// Write from current position.
byte_offset = position_;
}
X_STATUS result =
WriteSync(buffer, buffer_length, byte_offset, out_bytes_written);
if (XSUCCEEDED(result)) {
position_ += *out_bytes_written;
}
return result;
}
} // namespace kernel
} // namespace xe

View File

@ -146,12 +146,19 @@ class XFile : public XObject {
X_STATUS Read(void* buffer, size_t buffer_length, size_t byte_offset,
XAsyncRequest* request);
X_STATUS Write(const void* buffer, size_t buffer_length, size_t byte_offset,
size_t* out_bytes_written);
virtual void* GetWaitHandle();
protected:
XFile(KernelState* kernel_state, fs::Mode mode);
virtual X_STATUS ReadSync(void* buffer, size_t buffer_length,
size_t byte_offset, size_t* out_bytes_read) = 0;
virtual X_STATUS WriteSync(const void* buffer, size_t buffer_length,
size_t byte_offset, size_t* out_bytes_written) {
return X_STATUS_ACCESS_DENIED;
}
private:
fs::Mode mode_;

View File

@ -5,6 +5,8 @@
'app.h',
'async_request.cc',
'async_request.h',
'content_manager.cc',
'content_manager.h',
'dispatcher.cc',
'dispatcher.h',
'kernel.h',

View File

@ -57,7 +57,7 @@ SHIM_CALL XamShowDeviceSelectorUI_shim(PPCContext* ppc_state,
uint32_t device_id_ptr = SHIM_GET_ARG_32(4);
uint32_t overlapped_ptr = SHIM_GET_ARG_32(5);
XELOGD("XamShowDeviceSelectorUI(%.8X, %.8X, %.8X, %.8X, %.8X, %.8X)",
XELOGD("XamShowDeviceSelectorUI(%d, %.8X, %.8X, %.8X, %.8X, %.8X)",
user_index, content_type, content_flags, total_requested,
device_id_ptr, overlapped_ptr);
@ -159,6 +159,29 @@ SHIM_CALL XamContentGetDeviceData_shim(PPCContext* ppc_state,
SHIM_SET_RETURN_32(X_ERROR_SUCCESS);
}
SHIM_CALL XamContentResolve_shim(PPCContext* ppc_state, KernelState* state) {
uint32_t user_index = SHIM_GET_ARG_32(0);
uint32_t content_data_ptr = SHIM_GET_ARG_32(1);
uint32_t buffer_ptr = SHIM_GET_ARG_32(2);
uint32_t buffer_size = SHIM_GET_ARG_32(3);
uint32_t unk1 = SHIM_GET_ARG_32(4); // 1
uint32_t unk2 = SHIM_GET_ARG_32(5); // 0
uint32_t unk3 = SHIM_GET_ARG_32(6); // 0
auto content_data = XCONTENT_DATA(SHIM_MEM_ADDR(content_data_ptr));
XELOGD("XamContentResolve(%d, %.8X, %.8X, %d, %.8X, %.8X, %.8X)", user_index,
content_data_ptr, buffer_ptr, buffer_size, unk1, unk2, unk3);
// Result of buffer_ptr is sent to RtlInitAnsiString.
// buffer_size is usually 260 (max path).
// Games expect zero if resolve was successful.
assert_always();
XELOGW("XamContentResolve unimplemented!");
SHIM_SET_RETURN_32(X_ERROR_NOT_FOUND);
}
// http://gameservice.googlecode.com/svn-history/r14/trunk/ContentManager.cpp
SHIM_CALL XamContentCreateEnumerator_shim(PPCContext* ppc_state,
KernelState* state) {
@ -170,7 +193,7 @@ SHIM_CALL XamContentCreateEnumerator_shim(PPCContext* ppc_state,
uint32_t buffer_size_ptr = SHIM_GET_ARG_32(5);
uint32_t handle_ptr = SHIM_GET_ARG_32(6);
XELOGD("XamContentCreateEnumerator(%.8X, %.8X, %.8X, %.8X, %.8X, %.8X, %.8X)",
XELOGD("XamContentCreateEnumerator(%d, %.8X, %.8X, %.8X, %.8X, %.8X, %.8X)",
user_index, device_id, content_type, content_flags, item_count,
buffer_size_ptr, handle_ptr);
@ -194,6 +217,227 @@ SHIM_CALL XamContentCreateEnumerator_shim(PPCContext* ppc_state,
SHIM_SET_RETURN_32(X_ERROR_SUCCESS);
}
SHIM_CALL XamContentCreate_shim(PPCContext* ppc_state, KernelState* state) {
// uint32_t user_index = SHIM_GET_ARG_32(0);
// uint32_t content_data_ptr = SHIM_GET_ARG_32(1);
XELOGD("XamContentCreate unimplemented");
assert_always();
}
SHIM_CALL XamContentCreateEx_shim(PPCContext* ppc_state, KernelState* state) {
uint32_t user_index = SHIM_GET_ARG_32(0);
uint32_t root_name_ptr = SHIM_GET_ARG_32(1);
uint32_t content_data_ptr = SHIM_GET_ARG_32(2);
uint32_t flags = SHIM_GET_ARG_32(3);
uint32_t disposition_ptr = SHIM_GET_ARG_32(4);
uint32_t license_mask_ptr = SHIM_GET_ARG_32(5);
uint32_t cache_size = SHIM_GET_ARG_32(6);
uint64_t content_size = SHIM_GET_ARG_64(7);
uint32_t sp = (uint32_t)ppc_state->r[1];
uint32_t overlapped_ptr = SHIM_MEM_32(sp + 0x54);
auto root_name =
poly::load_and_swap<std::string>(SHIM_MEM_ADDR(root_name_ptr));
auto content_data = XCONTENT_DATA(SHIM_MEM_ADDR(content_data_ptr));
XELOGD(
"XamContentCreateEx(%d, %.8X(%s), %.8X, %.8X, %.8X, %.8X, %.8X, %.8llX, "
"%.8X)",
user_index, root_name_ptr, root_name.c_str(), content_data_ptr, flags,
disposition_ptr, license_mask_ptr, cache_size, content_size,
overlapped_ptr);
assert_zero(license_mask_ptr);
X_RESULT result = X_ERROR_INVALID_PARAMETER;
auto content_manager = state->content_manager();
bool create = false;
bool open = false;
switch (flags & 0xF) {
case 1: // CREATE_NEW
// Fail if exists.
if (content_manager->ContentExists(content_data)) {
result = X_ERROR_ALREADY_EXISTS;
} else {
create = true;
}
break;
case 2: // CREATE_ALWAYS
// Overwrite existing, if any.
if (content_manager->ContentExists(content_data)) {
content_manager->DeleteContent(content_data);
create = true;
} else {
create = true;
}
break;
case 3: // OPEN_EXISTING
// Open only if exists.
if (!content_manager->ContentExists(content_data)) {
result = X_ERROR_FILE_NOT_FOUND;
} else {
open = true;
}
break;
case 4: // OPEN_ALWAYS
// Create if needed.
if (!content_manager->ContentExists(content_data)) {
create = true;
} else {
open = true;
}
break;
case 5: // TRUNCATE_EXISTING
// Fail if doesn't exist, if does exist delete and recreate.
if (!content_manager->ContentExists(content_data)) {
result = X_ERROR_FILE_NOT_FOUND;
} else {
content_manager->DeleteContent(content_data);
create = true;
}
break;
default:
assert_unhandled_case(flags & 0xF);
break;
}
if (disposition_ptr) {
if (overlapped_ptr) {
// If async always set to zero, but don't set to a real value.
SHIM_SET_MEM_32(disposition_ptr, 0);
} else {
SHIM_SET_MEM_32(disposition_ptr, create ? 1 : 2);
}
}
if (create) {
result = content_manager->CreateContent(root_name, content_data);
} else if (open) {
result = content_manager->OpenContent(root_name, content_data);
}
if (overlapped_ptr) {
state->CompleteOverlappedImmediate(overlapped_ptr, result);
SHIM_SET_RETURN_32(X_ERROR_IO_PENDING);
} else {
SHIM_SET_RETURN_32(result);
}
}
SHIM_CALL XamContentClose_shim(PPCContext* ppc_state, KernelState* state) {
uint32_t root_name_ptr = SHIM_GET_ARG_32(0);
uint32_t overlapped_ptr = SHIM_GET_ARG_32(1);
auto root_name =
poly::load_and_swap<std::string>(SHIM_MEM_ADDR(root_name_ptr));
XELOGD("XamContentClose(%.8X(%s), %.8X)", root_name_ptr, root_name.c_str(),
overlapped_ptr);
// Closes a previously opened root from XamContentCreate*.
auto result = state->content_manager()->CloseContent(root_name);
if (overlapped_ptr) {
state->CompleteOverlappedImmediate(overlapped_ptr, result);
SHIM_SET_RETURN_32(X_ERROR_IO_PENDING);
} else {
SHIM_SET_RETURN_32(result);
}
}
SHIM_CALL XamContentGetThumbnail_shim(PPCContext* ppc_state,
KernelState* state) {
uint32_t user_index = SHIM_GET_ARG_32(0);
uint32_t content_data_ptr = SHIM_GET_ARG_32(1);
uint32_t buffer_ptr = SHIM_GET_ARG_32(2);
uint32_t buffer_size_ptr = SHIM_GET_ARG_32(3);
uint32_t overlapped_ptr = SHIM_GET_ARG_32(4);
assert_not_zero(buffer_size_ptr);
uint32_t buffer_size = SHIM_MEM_32(buffer_size_ptr);
auto content_data = XCONTENT_DATA(SHIM_MEM_ADDR(content_data_ptr));
XELOGD("XamContentGetThumbnail(%d, %.8X, %.8X, %.8X(%d), %.8X)", user_index,
content_data_ptr, buffer_ptr, buffer_size_ptr, buffer_size,
overlapped_ptr);
// Get thumbnail (if it exists).
std::vector<uint8_t> buffer;
auto result =
state->content_manager()->GetContentThumbnail(content_data, &buffer);
SHIM_SET_MEM_32(buffer_size_ptr, uint32_t(buffer.size()));
if (XSUCCEEDED(result)) {
// Write data, if we were given a pointer.
// This may have just been a size query.
if (buffer_ptr) {
if (buffer_size < buffer.size()) {
// Dest buffer too small.
result = X_ERROR_INSUFFICIENT_BUFFER;
} else {
// Copy data.
std::memcpy(SHIM_MEM_ADDR(buffer_ptr), buffer.data(), buffer.size());
}
}
}
if (overlapped_ptr) {
state->CompleteOverlappedImmediate(overlapped_ptr, result);
SHIM_SET_RETURN_32(X_ERROR_IO_PENDING);
} else {
SHIM_SET_RETURN_32(result);
}
}
SHIM_CALL XamContentSetThumbnail_shim(PPCContext* ppc_state,
KernelState* state) {
uint32_t user_index = SHIM_GET_ARG_32(0);
uint32_t content_data_ptr = SHIM_GET_ARG_32(1);
uint32_t buffer_ptr = SHIM_GET_ARG_32(2);
uint32_t buffer_size = SHIM_GET_ARG_32(3);
uint32_t overlapped_ptr = SHIM_GET_ARG_32(4);
auto content_data = XCONTENT_DATA(SHIM_MEM_ADDR(content_data_ptr));
XELOGD("XamContentSetThumbnail(%d, %.8X, %.8X, %d, %.8X)", user_index,
content_data_ptr, buffer_ptr, buffer_size, overlapped_ptr);
// Buffer is PNG data.
auto buffer = std::vector<uint8_t>(SHIM_MEM_ADDR(buffer_ptr),
SHIM_MEM_ADDR(buffer_ptr) + buffer_size);
auto result = state->content_manager()->SetContentThumbnail(
content_data, std::move(buffer));
if (overlapped_ptr) {
state->CompleteOverlappedImmediate(overlapped_ptr, result);
SHIM_SET_RETURN_32(X_ERROR_IO_PENDING);
} else {
SHIM_SET_RETURN_32(result);
}
}
SHIM_CALL XamContentDelete_shim(PPCContext* ppc_state, KernelState* state) {
uint32_t user_index = SHIM_GET_ARG_32(0);
uint32_t content_data_ptr = SHIM_GET_ARG_32(1);
uint32_t overlapped_ptr = SHIM_GET_ARG_32(2);
auto content_data = XCONTENT_DATA(SHIM_MEM_ADDR(content_data_ptr));
XELOGD("XamContentDelete(%d, %.8X, %.8X)", user_index, content_data_ptr,
overlapped_ptr);
auto result = state->content_manager()->DeleteContent(content_data);
if (overlapped_ptr) {
state->CompleteOverlappedImmediate(overlapped_ptr, result);
SHIM_SET_RETURN_32(X_ERROR_IO_PENDING);
} else {
SHIM_SET_RETURN_32(result);
}
}
} // namespace kernel
} // namespace xe
@ -204,5 +448,12 @@ void xe::kernel::xam::RegisterContentExports(ExportResolver* export_resolver,
SHIM_SET_MAPPING("xam.xex", XamContentGetDeviceName, state);
SHIM_SET_MAPPING("xam.xex", XamContentGetDeviceState, state);
SHIM_SET_MAPPING("xam.xex", XamContentGetDeviceData, state);
SHIM_SET_MAPPING("xam.xex", XamContentResolve, state);
SHIM_SET_MAPPING("xam.xex", XamContentCreateEnumerator, state);
SHIM_SET_MAPPING("xam.xex", XamContentCreate, state);
SHIM_SET_MAPPING("xam.xex", XamContentCreateEx, state);
SHIM_SET_MAPPING("xam.xex", XamContentClose, state);
SHIM_SET_MAPPING("xam.xex", XamContentGetThumbnail, state);
SHIM_SET_MAPPING("xam.xex", XamContentSetThumbnail, state);
SHIM_SET_MAPPING("xam.xex", XamContentDelete, state);
}

View File

@ -103,8 +103,11 @@ X_STATUS NtCreateFile(PPCContext* ppc_state, KernelState* state,
if (creation_disposition != FileDisposition::X_FILE_OPEN ||
desired_access & FileAccess::X_GENERIC_WRITE ||
desired_access & FileAccess::X_GENERIC_ALL) {
// We don't support any write modes.
XELOGW("Attempted to open the file/dir for create/write");
if (entry->is_read_only()) {
// We don't support any write modes.
XELOGW("Attempted to open the file/dir for create/write");
desired_access = FileAccess::X_GENERIC_READ;
}
}
XFile* file = nullptr;
@ -113,7 +116,10 @@ X_STATUS NtCreateFile(PPCContext* ppc_state, KernelState* state,
info = X_FILE_DOES_NOT_EXIST;
} else {
// Open the file/directory.
result = fs->Open(std::move(entry), state, fs::Mode::READ,
result = fs->Open(std::move(entry), state,
desired_access == FileAccess::X_GENERIC_READ
? fs::Mode::READ
: fs::Mode::READ_WRITE,
false, // TODO(benvanik): pick async mode, if needed.
&file);
}
@ -294,6 +300,92 @@ SHIM_CALL NtReadFile_shim(PPCContext* ppc_state, KernelState* state) {
SHIM_SET_RETURN_32(result);
}
SHIM_CALL NtWriteFile_shim(PPCContext* ppc_state, KernelState* state) {
uint32_t file_handle = SHIM_GET_ARG_32(0);
uint32_t event_handle = SHIM_GET_ARG_32(1);
uint32_t apc_routine_ptr = SHIM_GET_ARG_32(2);
uint32_t apc_context = SHIM_GET_ARG_32(3);
uint32_t io_status_block_ptr = SHIM_GET_ARG_32(4);
uint32_t buffer = SHIM_GET_ARG_32(5);
uint32_t buffer_length = SHIM_GET_ARG_32(6);
uint32_t byte_offset_ptr = SHIM_GET_ARG_32(7);
size_t byte_offset = byte_offset_ptr ? SHIM_MEM_64(byte_offset_ptr) : 0;
XELOGD("NtWriteFile(%.8X, %.8X, %.8X, %.8X, %.8X, %.8X, %d, %.8X(%d))",
file_handle, event_handle, apc_routine_ptr, apc_context,
io_status_block_ptr, buffer, buffer_length, byte_offset_ptr,
byte_offset);
// Async not supported yet.
assert_zero(apc_routine_ptr);
X_STATUS result = X_STATUS_SUCCESS;
uint32_t info = 0;
// Grab event to signal.
XEvent* ev = NULL;
bool signal_event = false;
if (event_handle) {
result = state->object_table()->GetObject(event_handle, (XObject**)&ev);
}
// Grab file.
XFile* file = NULL;
if (XSUCCEEDED(result)) {
result = state->object_table()->GetObject(file_handle, (XObject**)&file);
}
// Execute write.
if (XSUCCEEDED(result)) {
// Reset event before we begin.
if (ev) {
ev->Reset();
}
// TODO(benvanik): async path.
if (true) {
// Synchronous request.
if (!byte_offset_ptr || byte_offset == 0xFFFFFFFFfffffffe) {
// FILE_USE_FILE_POINTER_POSITION
byte_offset = -1;
}
// Write now.
size_t bytes_written = 0;
result = file->Write(SHIM_MEM_ADDR(buffer), buffer_length, byte_offset,
&bytes_written);
if (XSUCCEEDED(result)) {
info = (int32_t)bytes_written;
}
// Mark that we should signal the event now. We do this after
// we have written the info out.
signal_event = true;
} else {
// X_STATUS_PENDING if not returning immediately.
result = X_STATUS_PENDING;
info = 0;
}
}
if (io_status_block_ptr) {
SHIM_SET_MEM_32(io_status_block_ptr, result); // Status
SHIM_SET_MEM_32(io_status_block_ptr + 4, info); // Information
}
if (file) {
file->Release();
}
if (ev) {
if (signal_event) {
ev->Set(0, false);
}
ev->Release();
}
SHIM_SET_RETURN_32(result);
}
SHIM_CALL NtSetInformationFile_shim(PPCContext* ppc_state, KernelState* state) {
uint32_t file_handle = SHIM_GET_ARG_32(0);
uint32_t io_status_block_ptr = SHIM_GET_ARG_32(1);
@ -629,6 +721,7 @@ void xe::kernel::xboxkrnl::RegisterIoExports(ExportResolver* export_resolver,
SHIM_SET_MAPPING("xboxkrnl.exe", NtCreateFile, state);
SHIM_SET_MAPPING("xboxkrnl.exe", NtOpenFile, state);
SHIM_SET_MAPPING("xboxkrnl.exe", NtReadFile, state);
SHIM_SET_MAPPING("xboxkrnl.exe", NtWriteFile, state);
SHIM_SET_MAPPING("xboxkrnl.exe", NtQueryInformationFile, state);
SHIM_SET_MAPPING("xboxkrnl.exe", NtSetInformationFile, state);
SHIM_SET_MAPPING("xboxkrnl.exe", NtQueryFullAttributesFile, state);

View File

@ -295,7 +295,7 @@ SHIM_CALL VdInitializeScalerCommandBuffer_shim(PPCContext* ppc_state,
// arg8 is in stack!
uint32_t sp = (uint32_t)ppc_state->r[1];
// Points to the first 80000000h where the memcpy sources from.
uint32_t dest_ptr = SHIM_MEM_32(sp + 0x64);
uint32_t dest_ptr = SHIM_MEM_32(sp + 0x54);
XELOGD(
"VdInitializeScalerCommandBuffer(%.8X, %.8X, %.8X, %.8X, %.8X, %.8X, "

View File

@ -76,14 +76,17 @@ typedef uint32_t X_RESULT;
#define X_FACILITY_WIN32 7
#define X_RESULT_FROM_WIN32(x) x //((X_RESULT)(x) <= 0 ? ((X_RESULT)(x)) : ((X_RESULT) (((x) & 0x0000FFFF) | (X_FACILITY_WIN32 << 16) | 0x80000000)))
#define X_ERROR_SUCCESS X_RESULT_FROM_WIN32(0x00000000L)
#define X_ERROR_FILE_NOT_FOUND X_RESULT_FROM_WIN32(0x00000002L)
#define X_ERROR_ACCESS_DENIED X_RESULT_FROM_WIN32(0x00000005L)
#define X_ERROR_INVALID_HANDLE X_RESULT_FROM_WIN32(0x00000006L)
#define X_ERROR_NO_MORE_FILES X_RESULT_FROM_WIN32(0x00000018L)
#define X_ERROR_INVALID_PARAMETER X_RESULT_FROM_WIN32(0x00000057L)
#define X_ERROR_IO_PENDING X_RESULT_FROM_WIN32(0x000003E5L)
#define X_ERROR_INSUFFICIENT_BUFFER X_RESULT_FROM_WIN32(0x0000007AL)
#define X_ERROR_INVALID_NAME X_RESULT_FROM_WIN32(0x0000007BL)
#define X_ERROR_BAD_ARGUMENTS X_RESULT_FROM_WIN32(0x000000A0L)
#define X_ERROR_BUSY X_RESULT_FROM_WIN32(0x000000AAL)
#define X_ERROR_ALREADY_EXISTS X_RESULT_FROM_WIN32(0x000000B7L)
#define X_ERROR_DEVICE_NOT_CONNECTED X_RESULT_FROM_WIN32(0x0000048FL)
#define X_ERROR_NOT_FOUND X_RESULT_FROM_WIN32(0x00000490L)
#define X_ERROR_CANCELLED X_RESULT_FROM_WIN32(0x000004C7L)