forked from xenia-project/xenia
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Most of XamContent* methods, besides enumeration.
Progress on xenia-project#152.
- Loading branch information
Showing
25 changed files
with
849 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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_ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
Oops, something went wrong.