parent
53eaeff690
commit
dc731f6a31
|
@ -79,6 +79,7 @@ build-test/
|
||||||
|
|
||||||
.vagrant
|
.vagrant
|
||||||
attic/
|
attic/
|
||||||
|
content/
|
||||||
third_party/binutils/binutils-2.24.tar.gz
|
third_party/binutils/binutils-2.24.tar.gz
|
||||||
third_party/binutils/bin/
|
third_party/binutils/bin/
|
||||||
third_party/binutils/powerpc-none-elf/
|
third_party/binutils/powerpc-none-elf/
|
||||||
|
|
|
@ -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_
|
|
@ -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
|
|
@ -8,6 +8,7 @@
|
||||||
'delegate.h',
|
'delegate.h',
|
||||||
'config.h',
|
'config.h',
|
||||||
'cxx_compat.h',
|
'cxx_compat.h',
|
||||||
|
'fs.h',
|
||||||
'logging.cc',
|
'logging.cc',
|
||||||
'logging.h',
|
'logging.h',
|
||||||
'main.h',
|
'main.h',
|
||||||
|
@ -45,6 +46,7 @@
|
||||||
['OS == "win"', {
|
['OS == "win"', {
|
||||||
'sources': [
|
'sources': [
|
||||||
'debugging_win.cc',
|
'debugging_win.cc',
|
||||||
|
'fs_win.cc',
|
||||||
'main_win.cc',
|
'main_win.cc',
|
||||||
'mapped_memory_win.cc',
|
'mapped_memory_win.cc',
|
||||||
'threading_win.cc',
|
'threading_win.cc',
|
||||||
|
|
|
@ -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
|
|
@ -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_
|
|
@ -27,6 +27,8 @@ class Device {
|
||||||
|
|
||||||
const std::string& path() const { return path_; }
|
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 std::unique_ptr<Entry> ResolvePath(const char* path) = 0;
|
||||||
|
|
||||||
virtual X_STATUS QueryVolume(XVolumeInfo* out_info, size_t length);
|
virtual X_STATUS QueryVolume(XVolumeInfo* out_info, size_t length);
|
||||||
|
|
|
@ -17,8 +17,8 @@ namespace kernel {
|
||||||
namespace fs {
|
namespace fs {
|
||||||
|
|
||||||
HostPathDevice::HostPathDevice(const std::string& path,
|
HostPathDevice::HostPathDevice(const std::string& path,
|
||||||
const std::wstring& local_path)
|
const std::wstring& local_path, bool read_only)
|
||||||
: Device(path), local_path_(local_path) {}
|
: Device(path), local_path_(local_path), read_only_(read_only) {}
|
||||||
|
|
||||||
HostPathDevice::~HostPathDevice() {}
|
HostPathDevice::~HostPathDevice() {}
|
||||||
|
|
||||||
|
|
|
@ -21,13 +21,17 @@ namespace fs {
|
||||||
|
|
||||||
class HostPathDevice : public Device {
|
class HostPathDevice : public Device {
|
||||||
public:
|
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;
|
~HostPathDevice() override;
|
||||||
|
|
||||||
|
bool is_read_only() const { return read_only_; }
|
||||||
|
|
||||||
std::unique_ptr<Entry> ResolvePath(const char* path) override;
|
std::unique_ptr<Entry> ResolvePath(const char* path) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::wstring local_path_;
|
std::wstring local_path_;
|
||||||
|
bool read_only_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace fs
|
} // namespace fs
|
||||||
|
|
|
@ -140,7 +140,7 @@ X_STATUS HostPathEntry::Open(KernelState* kernel_state, Mode mode, bool async,
|
||||||
DWORD desired_access =
|
DWORD desired_access =
|
||||||
mode == Mode::READ ? GENERIC_READ : (GENERIC_READ | GENERIC_WRITE);
|
mode == Mode::READ ? GENERIC_READ : (GENERIC_READ | GENERIC_WRITE);
|
||||||
DWORD share_mode = FILE_SHARE_READ;
|
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;
|
DWORD flags_and_attributes = async ? FILE_FLAG_OVERLAPPED : 0;
|
||||||
HANDLE file =
|
HANDLE file =
|
||||||
CreateFile(local_path_.c_str(), desired_access, share_mode, NULL,
|
CreateFile(local_path_.c_str(), desired_access, share_mode, NULL,
|
||||||
|
|
|
@ -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 fs
|
||||||
} // namespace kernel
|
} // namespace kernel
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
|
@ -41,6 +41,8 @@ class HostPathFile : public XFile {
|
||||||
protected:
|
protected:
|
||||||
X_STATUS ReadSync(void* buffer, size_t buffer_length, size_t byte_offset,
|
X_STATUS ReadSync(void* buffer, size_t buffer_length, size_t byte_offset,
|
||||||
size_t* out_bytes_read) override;
|
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:
|
private:
|
||||||
HostPathEntry* entry_;
|
HostPathEntry* entry_;
|
||||||
|
|
|
@ -29,6 +29,10 @@ Entry::Entry(Device* device, const std::string& path)
|
||||||
|
|
||||||
Entry::~Entry() = default;
|
Entry::~Entry() = default;
|
||||||
|
|
||||||
|
bool Entry::is_read_only() const {
|
||||||
|
return device_->is_read_only();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace fs
|
} // namespace fs
|
||||||
} // namespace kernel
|
} // namespace kernel
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
|
@ -61,6 +61,8 @@ class Entry {
|
||||||
const std::string& absolute_path() const { return absolute_path_; }
|
const std::string& absolute_path() const { return absolute_path_; }
|
||||||
const std::string& name() const { return name_; }
|
const std::string& name() const { return name_; }
|
||||||
|
|
||||||
|
bool is_read_only() const;
|
||||||
|
|
||||||
virtual X_STATUS QueryInfo(XFileInfo* out_info) = 0;
|
virtual X_STATUS QueryInfo(XFileInfo* out_info) = 0;
|
||||||
virtual X_STATUS QueryDirectory(XDirectoryInfo* out_info, size_t length,
|
virtual X_STATUS QueryDirectory(XDirectoryInfo* out_info, size_t length,
|
||||||
const char* file_name, bool restart) = 0;
|
const char* file_name, bool restart) = 0;
|
||||||
|
|
|
@ -74,7 +74,7 @@ int FileSystem::InitializeFromPath(fs::FileSystemType type,
|
||||||
|
|
||||||
// Register the local directory in the virtual filesystem.
|
// Register the local directory in the virtual filesystem.
|
||||||
int result_code = RegisterHostPathDevice(
|
int result_code = RegisterHostPathDevice(
|
||||||
"\\Device\\Harddisk1\\Partition0", parent_path);
|
"\\Device\\Harddisk1\\Partition0", parent_path, true);
|
||||||
if (result_code) {
|
if (result_code) {
|
||||||
XELOGE("Unable to mount local directory");
|
XELOGE("Unable to mount local directory");
|
||||||
return result_code;
|
return result_code;
|
||||||
|
@ -108,8 +108,9 @@ int FileSystem::RegisterDevice(const std::string& path, Device* device) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int FileSystem::RegisterHostPathDevice(const std::string& path,
|
int FileSystem::RegisterHostPathDevice(const std::string& path,
|
||||||
const std::wstring& local_path) {
|
const std::wstring& local_path,
|
||||||
Device* device = new HostPathDevice(path, local_path);
|
bool read_only) {
|
||||||
|
Device* device = new HostPathDevice(path, local_path, read_only);
|
||||||
return RegisterDevice(path, device);
|
return RegisterDevice(path, device);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,8 @@ class FileSystem {
|
||||||
|
|
||||||
int RegisterDevice(const std::string& path, Device* device);
|
int RegisterDevice(const std::string& path, Device* device);
|
||||||
int RegisterHostPathDevice(const std::string& path,
|
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,
|
int RegisterDiscImageDevice(const std::string& path,
|
||||||
const std::wstring& local_path);
|
const std::wstring& local_path);
|
||||||
int RegisterSTFSContainerDevice(const std::string& path,
|
int RegisterSTFSContainerDevice(const std::string& path,
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
|
|
||||||
#include "xenia/kernel/kernel_state.h"
|
#include "xenia/kernel/kernel_state.h"
|
||||||
|
|
||||||
|
#include <gflags/gflags.h>
|
||||||
|
|
||||||
#include "xenia/emulator.h"
|
#include "xenia/emulator.h"
|
||||||
#include "xenia/kernel/dispatcher.h"
|
#include "xenia/kernel/dispatcher.h"
|
||||||
#include "xenia/kernel/xam_module.h"
|
#include "xenia/kernel/xam_module.h"
|
||||||
|
@ -22,6 +24,9 @@
|
||||||
#include "xenia/kernel/objects/xthread.h"
|
#include "xenia/kernel/objects/xthread.h"
|
||||||
#include "xenia/kernel/objects/xuser_module.h"
|
#include "xenia/kernel/objects/xuser_module.h"
|
||||||
|
|
||||||
|
DEFINE_string(content_root, "content",
|
||||||
|
"Root path for content (save/etc) storage.");
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace kernel {
|
namespace kernel {
|
||||||
|
|
||||||
|
@ -45,6 +50,10 @@ KernelState::KernelState(Emulator* emulator)
|
||||||
app_manager_ = std::make_unique<XAppManager>();
|
app_manager_ = std::make_unique<XAppManager>();
|
||||||
user_profile_ = std::make_unique<UserProfile>();
|
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();
|
object_table_ = new ObjectTable();
|
||||||
|
|
||||||
assert_null(shared_kernel_state_);
|
assert_null(shared_kernel_state_);
|
||||||
|
@ -70,6 +79,11 @@ KernelState::~KernelState() {
|
||||||
|
|
||||||
KernelState* KernelState::shared() { return shared_kernel_state_; }
|
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::RegisterModule(XModule* module) {}
|
||||||
|
|
||||||
void KernelState::UnregisterModule(XModule* module) {}
|
void KernelState::UnregisterModule(XModule* module) {}
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include "xenia/common.h"
|
#include "xenia/common.h"
|
||||||
#include "xenia/export_resolver.h"
|
#include "xenia/export_resolver.h"
|
||||||
#include "xenia/kernel/app.h"
|
#include "xenia/kernel/app.h"
|
||||||
|
#include "xenia/kernel/content_manager.h"
|
||||||
#include "xenia/kernel/fs/filesystem.h"
|
#include "xenia/kernel/fs/filesystem.h"
|
||||||
#include "xenia/kernel/object_table.h"
|
#include "xenia/kernel/object_table.h"
|
||||||
#include "xenia/kernel/user_profile.h"
|
#include "xenia/kernel/user_profile.h"
|
||||||
|
@ -49,11 +50,14 @@ class KernelState {
|
||||||
Memory* memory() const { return memory_; }
|
Memory* memory() const { return memory_; }
|
||||||
cpu::Processor* processor() const { return processor_; }
|
cpu::Processor* processor() const { return processor_; }
|
||||||
fs::FileSystem* file_system() const { return file_system_; }
|
fs::FileSystem* file_system() const { return file_system_; }
|
||||||
|
|
||||||
|
uint32_t title_id() const;
|
||||||
|
|
||||||
Dispatcher* dispatcher() const { return dispatcher_; }
|
Dispatcher* dispatcher() const { return dispatcher_; }
|
||||||
|
|
||||||
XAppManager* app_manager() const { return app_manager_.get(); }
|
XAppManager* app_manager() const { return app_manager_.get(); }
|
||||||
UserProfile* user_profile() const { return user_profile_.get(); }
|
UserProfile* user_profile() const { return user_profile_.get(); }
|
||||||
|
ContentManager* content_manager() const { return content_manager_.get(); }
|
||||||
|
|
||||||
ObjectTable* object_table() const { return object_table_; }
|
ObjectTable* object_table() const { return object_table_; }
|
||||||
std::mutex& object_mutex() { return object_mutex_; }
|
std::mutex& object_mutex() { return object_mutex_; }
|
||||||
|
@ -90,6 +94,7 @@ class KernelState {
|
||||||
|
|
||||||
std::unique_ptr<XAppManager> app_manager_;
|
std::unique_ptr<XAppManager> app_manager_;
|
||||||
std::unique_ptr<UserProfile> user_profile_;
|
std::unique_ptr<UserProfile> user_profile_;
|
||||||
|
std::unique_ptr<ContentManager> content_manager_;
|
||||||
|
|
||||||
ObjectTable* object_table_;
|
ObjectTable* object_table_;
|
||||||
std::mutex object_mutex_;
|
std::mutex object_mutex_;
|
||||||
|
|
|
@ -53,5 +53,19 @@ X_STATUS XFile::Read(void* buffer, size_t buffer_length, size_t byte_offset,
|
||||||
return result;
|
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 kernel
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
|
@ -146,12 +146,19 @@ class XFile : public XObject {
|
||||||
X_STATUS Read(void* buffer, size_t buffer_length, size_t byte_offset,
|
X_STATUS Read(void* buffer, size_t buffer_length, size_t byte_offset,
|
||||||
XAsyncRequest* request);
|
XAsyncRequest* request);
|
||||||
|
|
||||||
|
X_STATUS Write(const void* buffer, size_t buffer_length, size_t byte_offset,
|
||||||
|
size_t* out_bytes_written);
|
||||||
|
|
||||||
virtual void* GetWaitHandle();
|
virtual void* GetWaitHandle();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
XFile(KernelState* kernel_state, fs::Mode mode);
|
XFile(KernelState* kernel_state, fs::Mode mode);
|
||||||
virtual X_STATUS ReadSync(void* buffer, size_t buffer_length,
|
virtual X_STATUS ReadSync(void* buffer, size_t buffer_length,
|
||||||
size_t byte_offset, size_t* out_bytes_read) = 0;
|
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:
|
private:
|
||||||
fs::Mode mode_;
|
fs::Mode mode_;
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
'app.h',
|
'app.h',
|
||||||
'async_request.cc',
|
'async_request.cc',
|
||||||
'async_request.h',
|
'async_request.h',
|
||||||
|
'content_manager.cc',
|
||||||
|
'content_manager.h',
|
||||||
'dispatcher.cc',
|
'dispatcher.cc',
|
||||||
'dispatcher.h',
|
'dispatcher.h',
|
||||||
'kernel.h',
|
'kernel.h',
|
||||||
|
|
|
@ -57,7 +57,7 @@ SHIM_CALL XamShowDeviceSelectorUI_shim(PPCContext* ppc_state,
|
||||||
uint32_t device_id_ptr = SHIM_GET_ARG_32(4);
|
uint32_t device_id_ptr = SHIM_GET_ARG_32(4);
|
||||||
uint32_t overlapped_ptr = SHIM_GET_ARG_32(5);
|
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,
|
user_index, content_type, content_flags, total_requested,
|
||||||
device_id_ptr, overlapped_ptr);
|
device_id_ptr, overlapped_ptr);
|
||||||
|
|
||||||
|
@ -159,6 +159,29 @@ SHIM_CALL XamContentGetDeviceData_shim(PPCContext* ppc_state,
|
||||||
SHIM_SET_RETURN_32(X_ERROR_SUCCESS);
|
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
|
// http://gameservice.googlecode.com/svn-history/r14/trunk/ContentManager.cpp
|
||||||
SHIM_CALL XamContentCreateEnumerator_shim(PPCContext* ppc_state,
|
SHIM_CALL XamContentCreateEnumerator_shim(PPCContext* ppc_state,
|
||||||
KernelState* 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 buffer_size_ptr = SHIM_GET_ARG_32(5);
|
||||||
uint32_t handle_ptr = SHIM_GET_ARG_32(6);
|
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,
|
user_index, device_id, content_type, content_flags, item_count,
|
||||||
buffer_size_ptr, handle_ptr);
|
buffer_size_ptr, handle_ptr);
|
||||||
|
|
||||||
|
@ -194,6 +217,227 @@ SHIM_CALL XamContentCreateEnumerator_shim(PPCContext* ppc_state,
|
||||||
SHIM_SET_RETURN_32(X_ERROR_SUCCESS);
|
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 kernel
|
||||||
} // namespace xe
|
} // 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", XamContentGetDeviceName, state);
|
||||||
SHIM_SET_MAPPING("xam.xex", XamContentGetDeviceState, state);
|
SHIM_SET_MAPPING("xam.xex", XamContentGetDeviceState, state);
|
||||||
SHIM_SET_MAPPING("xam.xex", XamContentGetDeviceData, 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", 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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -103,8 +103,11 @@ X_STATUS NtCreateFile(PPCContext* ppc_state, KernelState* state,
|
||||||
if (creation_disposition != FileDisposition::X_FILE_OPEN ||
|
if (creation_disposition != FileDisposition::X_FILE_OPEN ||
|
||||||
desired_access & FileAccess::X_GENERIC_WRITE ||
|
desired_access & FileAccess::X_GENERIC_WRITE ||
|
||||||
desired_access & FileAccess::X_GENERIC_ALL) {
|
desired_access & FileAccess::X_GENERIC_ALL) {
|
||||||
// We don't support any write modes.
|
if (entry->is_read_only()) {
|
||||||
XELOGW("Attempted to open the file/dir for create/write");
|
// 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;
|
XFile* file = nullptr;
|
||||||
|
@ -113,7 +116,10 @@ X_STATUS NtCreateFile(PPCContext* ppc_state, KernelState* state,
|
||||||
info = X_FILE_DOES_NOT_EXIST;
|
info = X_FILE_DOES_NOT_EXIST;
|
||||||
} else {
|
} else {
|
||||||
// Open the file/directory.
|
// 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.
|
false, // TODO(benvanik): pick async mode, if needed.
|
||||||
&file);
|
&file);
|
||||||
}
|
}
|
||||||
|
@ -294,6 +300,92 @@ SHIM_CALL NtReadFile_shim(PPCContext* ppc_state, KernelState* state) {
|
||||||
SHIM_SET_RETURN_32(result);
|
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) {
|
SHIM_CALL NtSetInformationFile_shim(PPCContext* ppc_state, KernelState* state) {
|
||||||
uint32_t file_handle = SHIM_GET_ARG_32(0);
|
uint32_t file_handle = SHIM_GET_ARG_32(0);
|
||||||
uint32_t io_status_block_ptr = SHIM_GET_ARG_32(1);
|
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", NtCreateFile, state);
|
||||||
SHIM_SET_MAPPING("xboxkrnl.exe", NtOpenFile, state);
|
SHIM_SET_MAPPING("xboxkrnl.exe", NtOpenFile, state);
|
||||||
SHIM_SET_MAPPING("xboxkrnl.exe", NtReadFile, 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", NtQueryInformationFile, state);
|
||||||
SHIM_SET_MAPPING("xboxkrnl.exe", NtSetInformationFile, state);
|
SHIM_SET_MAPPING("xboxkrnl.exe", NtSetInformationFile, state);
|
||||||
SHIM_SET_MAPPING("xboxkrnl.exe", NtQueryFullAttributesFile, state);
|
SHIM_SET_MAPPING("xboxkrnl.exe", NtQueryFullAttributesFile, state);
|
||||||
|
|
|
@ -295,7 +295,7 @@ SHIM_CALL VdInitializeScalerCommandBuffer_shim(PPCContext* ppc_state,
|
||||||
// arg8 is in stack!
|
// arg8 is in stack!
|
||||||
uint32_t sp = (uint32_t)ppc_state->r[1];
|
uint32_t sp = (uint32_t)ppc_state->r[1];
|
||||||
// Points to the first 80000000h where the memcpy sources from.
|
// 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(
|
XELOGD(
|
||||||
"VdInitializeScalerCommandBuffer(%.8X, %.8X, %.8X, %.8X, %.8X, %.8X, "
|
"VdInitializeScalerCommandBuffer(%.8X, %.8X, %.8X, %.8X, %.8X, %.8X, "
|
||||||
|
|
|
@ -76,14 +76,17 @@ typedef uint32_t X_RESULT;
|
||||||
#define X_FACILITY_WIN32 7
|
#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_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_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_ACCESS_DENIED X_RESULT_FROM_WIN32(0x00000005L)
|
||||||
#define X_ERROR_INVALID_HANDLE X_RESULT_FROM_WIN32(0x00000006L)
|
#define X_ERROR_INVALID_HANDLE X_RESULT_FROM_WIN32(0x00000006L)
|
||||||
#define X_ERROR_NO_MORE_FILES X_RESULT_FROM_WIN32(0x00000018L)
|
#define X_ERROR_NO_MORE_FILES X_RESULT_FROM_WIN32(0x00000018L)
|
||||||
#define X_ERROR_INVALID_PARAMETER X_RESULT_FROM_WIN32(0x00000057L)
|
#define X_ERROR_INVALID_PARAMETER X_RESULT_FROM_WIN32(0x00000057L)
|
||||||
#define X_ERROR_IO_PENDING X_RESULT_FROM_WIN32(0x000003E5L)
|
#define X_ERROR_IO_PENDING X_RESULT_FROM_WIN32(0x000003E5L)
|
||||||
#define X_ERROR_INSUFFICIENT_BUFFER X_RESULT_FROM_WIN32(0x0000007AL)
|
#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_BAD_ARGUMENTS X_RESULT_FROM_WIN32(0x000000A0L)
|
||||||
#define X_ERROR_BUSY X_RESULT_FROM_WIN32(0x000000AAL)
|
#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_DEVICE_NOT_CONNECTED X_RESULT_FROM_WIN32(0x0000048FL)
|
||||||
#define X_ERROR_NOT_FOUND X_RESULT_FROM_WIN32(0x00000490L)
|
#define X_ERROR_NOT_FOUND X_RESULT_FROM_WIN32(0x00000490L)
|
||||||
#define X_ERROR_CANCELLED X_RESULT_FROM_WIN32(0x000004C7L)
|
#define X_ERROR_CANCELLED X_RESULT_FROM_WIN32(0x000004C7L)
|
||||||
|
|
Loading…
Reference in New Issue