Merge branch 'master' of https://github.com/xenia-project/xenia into canary_experimental

This commit is contained in:
Gliniak
2022-07-17 21:27:52 +02:00
169 changed files with 39917 additions and 37685 deletions

View File

@@ -24,7 +24,8 @@ extern "C" int main(int argc, char** argv) {
// Initialize Android globals, including logging. Needs parsed cvars.
// TODO(Triang3l): Obtain the actual API level.
xe::InitializeAndroidAppFromMainThread(__ANDROID_API__, nullptr, nullptr);
xe::InitializeAndroidAppFromMainThread(__ANDROID_API__, nullptr, nullptr,
nullptr);
std::vector<std::string> args;
for (int n = 0; n < argc; n++) {

View File

@@ -7,9 +7,7 @@
******************************************************************************
*/
#include "cvar.h"
#include "utf8.h"
#include "xenia/base/cvar.h"
#define UTF_CPP_CPLUSPLUS 201703L
#include "third_party/utfcpp/source/utf8.h"
@@ -17,6 +15,7 @@
#include "xenia/base/console.h"
#include "xenia/base/logging.h"
#include "xenia/base/system.h"
#include "xenia/base/utf8.h"
namespace utfcpp = utf8;

View File

@@ -20,8 +20,13 @@
#include "third_party/fmt/include/fmt/format.h"
#include "xenia/base/assert.h"
#include "xenia/base/filesystem.h"
#include "xenia/base/platform.h"
#include "xenia/base/string_util.h"
#if XE_PLATFORM_ANDROID
#include <jni.h>
#endif // XE_PLATFORM_ANDROID
namespace cvar {
namespace toml {
@@ -56,6 +61,7 @@ class CommandVar : virtual public ICommandVar {
const std::string& description() const override;
void AddToLaunchOptions(cxxopts::Options* options) override;
void LoadFromLaunchOptions(cxxopts::ParseResult* result) override;
void SetCommandLineValue(T val);
T* current_value() { return current_value_; }
protected:
@@ -67,7 +73,6 @@ class CommandVar : virtual public ICommandVar {
T Convert(std::string val);
static std::string ToString(T val);
void SetValue(T val);
void SetCommandLineValue(T val);
void UpdateValue() override;
};
#pragma warning(push)
@@ -297,6 +302,9 @@ inline void AddCommandVar(ICommandVar* cv) {
void ParseLaunchArguments(int& argc, char**& argv,
const std::string_view positional_help,
const std::vector<std::string>& positional_options);
#if XE_PLATFORM_ANDROID
void ParseLaunchArgumentsFromAndroidBundle(jobject bundle);
#endif // XE_PLATFORM_ANDROID
template <typename T>
IConfigVar* define_configvar(const char* name, T* default_value,

View File

@@ -0,0 +1,212 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2022 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <jni.h>
#include "xenia/base/assert.h"
#include "xenia/base/cvar.h"
#include "xenia/base/filesystem.h"
#include "xenia/base/main_android.h"
namespace cvar {
void ParseLaunchArgumentsFromAndroidBundle(jobject bundle) {
if (!ConfigVars) {
return;
}
JNIEnv* jni_env = xe::GetAndroidThreadJniEnv();
if (!jni_env) {
return;
}
jclass bundle_class = jni_env->GetObjectClass(bundle);
if (!bundle_class) {
return;
}
bool bundle_methods_obtained = true;
jmethodID bundle_get_boolean =
jni_env->GetMethodID(bundle_class, "getBoolean", "(Ljava/lang/String;)Z");
bundle_methods_obtained &= (bundle_get_boolean != nullptr);
jmethodID bundle_get_double =
jni_env->GetMethodID(bundle_class, "getDouble", "(Ljava/lang/String;)D");
bundle_methods_obtained &= (bundle_get_double != nullptr);
jmethodID bundle_get_int =
jni_env->GetMethodID(bundle_class, "getInt", "(Ljava/lang/String;)I");
bundle_methods_obtained &= (bundle_get_int != nullptr);
jmethodID bundle_get_long =
jni_env->GetMethodID(bundle_class, "getLong", "(Ljava/lang/String;)J");
bundle_methods_obtained &= (bundle_get_long != nullptr);
jmethodID bundle_get_string = jni_env->GetMethodID(
bundle_class, "getString", "(Ljava/lang/String;)Ljava/lang/String;");
bundle_methods_obtained &= (bundle_get_string != nullptr);
jmethodID bundle_key_set_method_id =
jni_env->GetMethodID(bundle_class, "keySet", "()Ljava/util/Set;");
bundle_methods_obtained &= (bundle_key_set_method_id != nullptr);
if (!bundle_methods_obtained) {
jni_env->DeleteLocalRef(bundle_class);
return;
}
jobject key_set = jni_env->CallObjectMethod(bundle, bundle_key_set_method_id);
if (!key_set) {
jni_env->DeleteLocalRef(bundle_class);
return;
}
jclass set_class = jni_env->GetObjectClass(key_set);
if (!set_class) {
jni_env->DeleteLocalRef(key_set);
jni_env->DeleteLocalRef(bundle_class);
return;
}
bool set_methods_obtained = true;
jmethodID set_iterator_method_id =
jni_env->GetMethodID(set_class, "iterator", "()Ljava/util/Iterator;");
set_methods_obtained &= (set_iterator_method_id != nullptr);
if (!set_methods_obtained) {
jni_env->DeleteLocalRef(set_class);
jni_env->DeleteLocalRef(key_set);
jni_env->DeleteLocalRef(bundle_class);
return;
}
jobject key_set_iterator =
jni_env->CallObjectMethod(key_set, set_iterator_method_id);
if (!key_set_iterator) {
jni_env->DeleteLocalRef(set_class);
jni_env->DeleteLocalRef(key_set);
jni_env->DeleteLocalRef(bundle_class);
return;
}
jclass iterator_class = jni_env->GetObjectClass(key_set_iterator);
if (!iterator_class) {
jni_env->DeleteLocalRef(key_set_iterator);
jni_env->DeleteLocalRef(set_class);
jni_env->DeleteLocalRef(key_set);
jni_env->DeleteLocalRef(bundle_class);
return;
}
bool iterator_methods_obtained = true;
jmethodID iterator_has_next =
jni_env->GetMethodID(iterator_class, "hasNext", "()Z");
iterator_methods_obtained &= (iterator_has_next != nullptr);
jmethodID iterator_next =
jni_env->GetMethodID(iterator_class, "next", "()Ljava/lang/Object;");
iterator_methods_obtained &= (iterator_next != nullptr);
if (!iterator_methods_obtained) {
jni_env->DeleteLocalRef(iterator_class);
jni_env->DeleteLocalRef(key_set_iterator);
jni_env->DeleteLocalRef(set_class);
jni_env->DeleteLocalRef(key_set);
jni_env->DeleteLocalRef(bundle_class);
return;
}
while (jni_env->CallBooleanMethod(key_set_iterator, iterator_has_next)) {
jstring key = reinterpret_cast<jstring>(
jni_env->CallObjectMethod(key_set_iterator, iterator_next));
if (!key) {
continue;
}
const char* key_utf = jni_env->GetStringUTFChars(key, nullptr);
if (!key_utf) {
jni_env->DeleteLocalRef(key);
continue;
}
auto cvar_it = ConfigVars->find(key_utf);
jni_env->ReleaseStringUTFChars(key, key_utf);
// key_utf can't be used from now on.
if (cvar_it == ConfigVars->end()) {
jni_env->DeleteLocalRef(key);
continue;
}
IConfigVar* cvar = cvar_it->second;
auto cvar_bool = dynamic_cast<CommandVar<bool>*>(cvar);
if (cvar_bool) {
cvar_bool->SetCommandLineValue(
bool(jni_env->CallBooleanMethod(bundle, bundle_get_boolean, key)));
jni_env->DeleteLocalRef(key);
continue;
}
auto cvar_int32 = dynamic_cast<CommandVar<int32_t>*>(cvar);
if (cvar_int32) {
cvar_int32->SetCommandLineValue(
jni_env->CallIntMethod(bundle, bundle_get_int, key));
jni_env->DeleteLocalRef(key);
continue;
}
auto cvar_uint32 = dynamic_cast<CommandVar<uint32_t>*>(cvar);
if (cvar_uint32) {
cvar_uint32->SetCommandLineValue(
uint32_t(jni_env->CallIntMethod(bundle, bundle_get_int, key)));
jni_env->DeleteLocalRef(key);
continue;
}
auto cvar_uint64 = dynamic_cast<CommandVar<uint64_t>*>(cvar);
if (cvar_uint64) {
cvar_uint64->SetCommandLineValue(
uint64_t(jni_env->CallLongMethod(bundle, bundle_get_long, key)));
jni_env->DeleteLocalRef(key);
continue;
}
auto cvar_double = dynamic_cast<CommandVar<double>*>(cvar);
if (cvar_double) {
cvar_double->SetCommandLineValue(
jni_env->CallDoubleMethod(bundle, bundle_get_double, key));
jni_env->DeleteLocalRef(key);
continue;
}
auto cvar_string = dynamic_cast<CommandVar<std::string>*>(cvar);
if (cvar_string) {
jstring cvar_string_value = reinterpret_cast<jstring>(
jni_env->CallObjectMethod(bundle, bundle_get_string, key));
if (cvar_string_value) {
const char* cvar_string_value_utf =
jni_env->GetStringUTFChars(cvar_string_value, nullptr);
if (cvar_string_value_utf) {
cvar_string->SetCommandLineValue(cvar_string_value_utf);
jni_env->ReleaseStringUTFChars(cvar_string_value,
cvar_string_value_utf);
}
jni_env->DeleteLocalRef(cvar_string_value);
}
jni_env->DeleteLocalRef(key);
continue;
}
auto cvar_path = dynamic_cast<CommandVar<std::filesystem::path>*>(cvar);
if (cvar_path) {
jstring cvar_string_value = reinterpret_cast<jstring>(
jni_env->CallObjectMethod(bundle, bundle_get_string, key));
if (cvar_string_value) {
const char* cvar_string_value_utf =
jni_env->GetStringUTFChars(cvar_string_value, nullptr);
if (cvar_string_value_utf) {
cvar_path->SetCommandLineValue(xe::to_path(cvar_string_value_utf));
jni_env->ReleaseStringUTFChars(cvar_string_value,
cvar_string_value_utf);
}
jni_env->DeleteLocalRef(cvar_string_value);
}
jni_env->DeleteLocalRef(key);
continue;
}
assert_always("Unsupported type of cvar {}", cvar->name().c_str());
jni_env->DeleteLocalRef(key);
}
jni_env->DeleteLocalRef(iterator_class);
jni_env->DeleteLocalRef(key_set_iterator);
jni_env->DeleteLocalRef(set_class);
jni_env->DeleteLocalRef(key_set);
jni_env->DeleteLocalRef(bundle_class);
}
} // namespace cvar

View File

@@ -14,8 +14,10 @@
#include <iterator>
#include <memory>
#include <string>
#include <string_view>
#include <vector>
#include "xenia/base/platform.h"
#include "xenia/base/string.h"
namespace xe {
@@ -122,6 +124,14 @@ struct FileInfo {
bool GetInfo(const std::filesystem::path& path, FileInfo* out_info);
std::vector<FileInfo> ListFiles(const std::filesystem::path& path);
#if XE_PLATFORM_ANDROID
void AndroidInitialize();
void AndroidShutdown();
bool IsAndroidContentUri(const std::string_view source);
int OpenAndroidContentFileDescriptor(const std::string_view uri,
const char* mode);
#endif // XE_PLATFORM_ANDROID
} // namespace filesystem
} // namespace xe

View File

@@ -0,0 +1,278 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2022 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <android/log.h>
#include <jni.h>
#include <cstring>
#include "xenia/base/filesystem.h"
#include "xenia/base/main_android.h"
#include "xenia/base/math.h"
#include "xenia/base/string.h"
namespace xe {
namespace filesystem {
// Using Android logging because the file system may need to be initialized
// before logging.
static jobject android_content_resolver_;
static jclass android_content_resolver_class_;
static jmethodID android_content_resolver_open_file_descriptor_;
static jclass android_parcel_file_descriptor_class_;
static jmethodID android_parcel_file_descriptor_detach_fd_;
static jclass android_uri_class_;
static jmethodID android_uri_parse_;
static bool android_content_resolver_initialized_;
static void AndroidShutdownContentResolver() {
android_content_resolver_initialized_ = false;
android_uri_parse_ = nullptr;
android_parcel_file_descriptor_detach_fd_ = nullptr;
android_content_resolver_open_file_descriptor_ = nullptr;
JNIEnv* jni_env = GetAndroidThreadJniEnv();
if (jni_env) {
if (android_uri_class_) {
jni_env->DeleteGlobalRef(android_uri_class_);
}
if (android_parcel_file_descriptor_class_) {
jni_env->DeleteGlobalRef(
reinterpret_cast<jobject>(android_parcel_file_descriptor_class_));
}
if (android_content_resolver_class_) {
jni_env->DeleteGlobalRef(
reinterpret_cast<jobject>(android_content_resolver_class_));
}
if (android_content_resolver_) {
jni_env->DeleteGlobalRef(
reinterpret_cast<jobject>(android_content_resolver_));
}
}
android_uri_class_ = nullptr;
android_parcel_file_descriptor_class_ = nullptr;
android_content_resolver_class_ = nullptr;
android_content_resolver_ = nullptr;
}
static void AndroidInitializeContentResolver() {
JNIEnv* jni_env = GetAndroidThreadJniEnv();
if (!jni_env) {
return;
}
jobject application_context = GetAndroidApplicationContext();
if (!application_context) {
return;
}
{
jclass context_class = jni_env->GetObjectClass(application_context);
if (!context_class) {
__android_log_write(ANDROID_LOG_ERROR, "AndroidInitializeContentResolver",
"Failed to get the context class");
AndroidShutdownContentResolver();
return;
}
jmethodID context_get_content_resolver =
jni_env->GetMethodID(context_class, "getContentResolver",
"()Landroid/content/ContentResolver;");
if (!context_get_content_resolver) {
__android_log_write(
ANDROID_LOG_ERROR, "AndroidInitializeContentResolver",
"Failed to get the getContentResolver method of the context");
jni_env->DeleteLocalRef(reinterpret_cast<jobject>(context_class));
AndroidShutdownContentResolver();
return;
}
jobject content_resolver = jni_env->CallObjectMethod(
application_context, context_get_content_resolver);
jni_env->DeleteLocalRef(reinterpret_cast<jobject>(context_class));
if (!content_resolver) {
__android_log_write(ANDROID_LOG_ERROR, "AndroidInitializeContentResolver",
"Failed to get the content resolver");
AndroidShutdownContentResolver();
return;
}
android_content_resolver_ = jni_env->NewGlobalRef(content_resolver);
jni_env->DeleteLocalRef(content_resolver);
}
if (!android_content_resolver_) {
__android_log_write(
ANDROID_LOG_ERROR, "AndroidInitializeContentResolver",
"Failed to create a global reference to the content resolver");
AndroidShutdownContentResolver();
return;
}
{
jobject content_resolver_class =
jni_env->GetObjectClass(android_content_resolver_);
if (!content_resolver_class) {
__android_log_write(ANDROID_LOG_ERROR, "AndroidInitializeContentResolver",
"Failed to get the content resolver class");
AndroidShutdownContentResolver();
return;
}
android_content_resolver_class_ =
reinterpret_cast<jclass>(jni_env->NewGlobalRef(
reinterpret_cast<jobject>(content_resolver_class)));
jni_env->DeleteLocalRef(reinterpret_cast<jobject>(content_resolver_class));
}
if (!android_content_resolver_class_) {
__android_log_write(
ANDROID_LOG_ERROR, "AndroidInitializeContentResolver",
"Failed to create a global reference to the content resolver class");
AndroidShutdownContentResolver();
return;
}
android_content_resolver_open_file_descriptor_ = jni_env->GetMethodID(
android_content_resolver_class_, "openFileDescriptor",
"(Landroid/net/Uri;Ljava/lang/String;)Landroid/os/ParcelFileDescriptor;");
if (!android_content_resolver_open_file_descriptor_) {
__android_log_write(
ANDROID_LOG_ERROR, "AndroidInitializeContentResolver",
"Failed to get the openFileDescriptor method of the content resolver");
AndroidShutdownContentResolver();
return;
}
{
jclass parcel_file_descriptor_class =
jni_env->FindClass("android/os/ParcelFileDescriptor");
if (!parcel_file_descriptor_class) {
__android_log_write(ANDROID_LOG_ERROR, "AndroidInitializeContentResolver",
"Failed to get the parcel file descriptor class");
AndroidShutdownContentResolver();
return;
}
android_parcel_file_descriptor_class_ =
reinterpret_cast<jclass>(jni_env->NewGlobalRef(
reinterpret_cast<jobject>(parcel_file_descriptor_class)));
jni_env->DeleteLocalRef(
reinterpret_cast<jobject>(parcel_file_descriptor_class));
}
if (!android_parcel_file_descriptor_class_) {
__android_log_write(
ANDROID_LOG_ERROR, "AndroidInitializeContentResolver",
"Failed to create a global reference to the parcel file descriptor "
"class");
AndroidShutdownContentResolver();
return;
}
android_parcel_file_descriptor_detach_fd_ = jni_env->GetMethodID(
android_parcel_file_descriptor_class_, "detachFd", "()I");
if (!android_parcel_file_descriptor_detach_fd_) {
__android_log_write(
ANDROID_LOG_ERROR, "AndroidInitializeContentResolver",
"Failed to get the detachFd method of the parcel file descriptor");
AndroidShutdownContentResolver();
return;
}
{
jclass uri_class = jni_env->FindClass("android/net/Uri");
if (!uri_class) {
__android_log_write(ANDROID_LOG_ERROR, "AndroidInitializeContentResolver",
"Failed to get the URI class");
AndroidShutdownContentResolver();
return;
}
android_uri_class_ = reinterpret_cast<jclass>(
jni_env->NewGlobalRef(reinterpret_cast<jobject>(uri_class)));
jni_env->DeleteLocalRef(reinterpret_cast<jobject>(uri_class));
}
if (!android_uri_class_) {
__android_log_write(ANDROID_LOG_ERROR, "AndroidInitializeContentResolver",
"Failed to create a global reference to the URI class");
AndroidShutdownContentResolver();
return;
}
android_uri_parse_ = jni_env->GetStaticMethodID(
android_uri_class_, "parse", "(Ljava/lang/String;)Landroid/net/Uri;");
if (!android_uri_parse_) {
__android_log_write(ANDROID_LOG_ERROR, "AndroidInitializeContentResolver",
"Failed to get the parse method of the URI class");
AndroidShutdownContentResolver();
return;
}
android_content_resolver_initialized_ = true;
}
void AndroidInitialize() { AndroidInitializeContentResolver(); }
void AndroidShutdown() { AndroidShutdownContentResolver(); }
bool IsAndroidContentUri(const std::string_view source) {
// A URI schema is case-insensitive. Though just content: defines the schema,
// still including // in the comparison to distinguish from a file with a name
// starting from content: (as this is the main purpose of this code -
// separating URIs from file paths) more clearly.
static const char kContentSchema[] = "content://";
constexpr size_t kContentSchemaLength = xe::countof(kContentSchema) - 1;
return source.size() >= kContentSchemaLength &&
!xe_strncasecmp(source.data(), kContentSchema, kContentSchemaLength);
}
int OpenAndroidContentFileDescriptor(const std::string_view uri,
const char* mode) {
if (!android_content_resolver_initialized_) {
return -1;
}
JNIEnv* jni_env = GetAndroidThreadJniEnv();
if (!jni_env) {
return -1;
}
jobject uri_object;
{
jstring uri_string;
{
std::u16string uri_u16 = xe::to_utf16(uri);
uri_string = jni_env->NewString(
reinterpret_cast<const jchar*>(uri_u16.data()), uri_u16.size());
}
if (!uri_string) {
return -1;
}
uri_object = jni_env->CallStaticObjectMethod(
android_uri_class_, android_uri_parse_, uri_string);
jni_env->DeleteLocalRef(uri_string);
}
if (!uri_object) {
return -1;
}
jstring mode_string = jni_env->NewStringUTF(mode);
if (!mode_string) {
jni_env->DeleteLocalRef(uri_object);
return -1;
}
jobject parcel_file_descriptor = jni_env->CallObjectMethod(
android_content_resolver_, android_content_resolver_open_file_descriptor_,
uri_object, mode_string);
jni_env->DeleteLocalRef(mode_string);
jni_env->DeleteLocalRef(uri_object);
if (jni_env->ExceptionCheck()) {
jni_env->ExceptionClear();
return -1;
}
if (!parcel_file_descriptor) {
return -1;
}
int file_descriptor = jni_env->CallIntMethod(
parcel_file_descriptor, android_parcel_file_descriptor_detach_fd_);
jni_env->DeleteLocalRef(parcel_file_descriptor);
return file_descriptor;
}
} // namespace filesystem
} // namespace xe

View File

@@ -15,6 +15,8 @@
#include <cstdlib>
#include "xenia/base/assert.h"
#include "xenia/base/cvar.h"
#include "xenia/base/filesystem.h"
#include "xenia/base/logging.h"
#include "xenia/base/memory.h"
#include "xenia/base/system.h"
@@ -44,7 +46,8 @@ static void AndroidThreadJNIEnvDestructor(void* jni_env_pointer) {
void InitializeAndroidAppFromMainThread(int32_t api_level,
JNIEnv* main_thread_jni_env,
jobject application_context) {
jobject application_context,
jobject launch_arguments_bundle) {
if (android_initializations_++) {
// Already initialized for another component in the process.
return;
@@ -96,6 +99,13 @@ void InitializeAndroidAppFromMainThread(int32_t api_level,
// Logging uses threading.
xe::threading::AndroidInitialize();
xe::filesystem::AndroidInitialize();
// Initialize the cvars before logging.
if (launch_arguments_bundle) {
cvar::ParseLaunchArgumentsFromAndroidBundle(launch_arguments_bundle);
}
// Multiple apps can be launched within one process - don't pass the actual
// app name.
xe::InitializeLogging("xenia");
@@ -128,6 +138,8 @@ void ShutdownAndroidAppFromMainThread() {
xe::ShutdownLogging();
xe::filesystem::AndroidShutdown();
xe::threading::AndroidShutdown();
if (android_application_context_) {

View File

@@ -31,7 +31,8 @@ namespace xe {
// must be called in `main`, with a null main thread JNI environment.
void InitializeAndroidAppFromMainThread(int32_t api_level,
JNIEnv* main_thread_jni_env,
jobject application_context);
jobject application_context,
jobject launch_arguments_bundle);
void ShutdownAndroidAppFromMainThread();
// May be the minimum supported level if the initialization was done without a

View File

@@ -12,7 +12,9 @@
#include <filesystem>
#include <memory>
#include <string>
#include <string_view>
#include "xenia/base/platform.h"
namespace xe {
@@ -26,17 +28,25 @@ class MappedMemory {
static std::unique_ptr<MappedMemory> Open(const std::filesystem::path& path,
Mode mode, size_t offset = 0,
size_t length = 0);
#if XE_PLATFORM_ANDROID
static std::unique_ptr<MappedMemory> OpenForAndroidContentUri(
const std::string_view uri, Mode mode, size_t offset = 0,
size_t length = 0);
#endif // XE_PLATFORM_ANDROID
MappedMemory(const std::filesystem::path& path, Mode mode)
: path_(path), mode_(mode), data_(nullptr), size_(0) {}
MappedMemory(const std::filesystem::path& path, Mode mode, void* data,
size_t size)
: path_(path), mode_(mode), data_(data), size_(size) {}
MappedMemory() : data_(nullptr), size_(0) {}
MappedMemory(void* data, size_t size) : data_(data), size_(size) {}
MappedMemory(const MappedMemory& mapped_memory) = delete;
MappedMemory& operator=(const MappedMemory& mapped_memory) = delete;
MappedMemory(MappedMemory&& mapped_memory) = delete;
MappedMemory& operator=(MappedMemory&& mapped_memory) = delete;
virtual ~MappedMemory() = default;
std::unique_ptr<MappedMemory> Slice(Mode mode, size_t offset, size_t length) {
// The mapping is still backed by the object the slice was created from, a
// slice is not owning.
std::unique_ptr<MappedMemory> Slice(size_t offset, size_t length) {
return std::unique_ptr<MappedMemory>(
new MappedMemory(path_, mode, data() + offset, length));
new MappedMemory(data() + offset, length));
}
uint8_t* data() const { return reinterpret_cast<uint8_t*>(data_); }
@@ -50,8 +60,6 @@ class MappedMemory {
virtual bool Remap(size_t offset, size_t length) { return false; }
protected:
std::filesystem::path path_;
Mode mode_;
void* data_;
size_t size_;
};

View File

@@ -9,73 +9,126 @@
#include "xenia/base/mapped_memory.h"
#include <fcntl.h>
#include <sys/mman.h>
#include <cstdio>
#include <sys/stat.h>
#include <unistd.h>
#include <memory>
#include "xenia/base/string.h"
#include "xenia/base/filesystem.h"
#include "xenia/base/platform.h"
namespace xe {
class PosixMappedMemory : public MappedMemory {
public:
PosixMappedMemory(const std::filesystem::path& path, Mode mode)
: MappedMemory(path, mode), file_handle(nullptr) {}
PosixMappedMemory(void* data, size_t size, int file_descriptor)
: MappedMemory(data, size), file_descriptor_(file_descriptor) {}
~PosixMappedMemory() override {
if (data_) {
munmap(data_, size_);
munmap(data_, size());
}
if (file_handle) {
fclose(file_handle);
if (file_descriptor_ >= 0) {
close(file_descriptor_);
}
}
FILE* file_handle;
static std::unique_ptr<PosixMappedMemory> WrapFileDescriptor(
int file_descriptor, Mode mode, size_t offset = 0, size_t length = 0) {
int protection = 0;
switch (mode) {
case Mode::kRead:
protection |= PROT_READ;
break;
case Mode::kReadWrite:
protection |= PROT_READ | PROT_WRITE;
break;
}
size_t map_length = length;
if (!length) {
struct stat64 file_stat;
if (fstat64(file_descriptor, &file_stat)) {
close(file_descriptor);
return nullptr;
}
map_length = size_t(file_stat.st_size);
}
void* data =
mmap(0, map_length, protection, MAP_SHARED, file_descriptor, offset);
if (!data) {
close(file_descriptor);
return nullptr;
}
return std::make_unique<PosixMappedMemory>(data, map_length,
file_descriptor);
}
void Close(uint64_t truncate_size) override {
if (data_) {
munmap(data_, size());
data_ = nullptr;
}
if (file_descriptor_ >= 0) {
if (truncate_size) {
ftruncate64(file_descriptor_, off64_t(truncate_size));
}
close(file_descriptor_);
file_descriptor_ = -1;
}
}
void Flush() override { msync(data(), size(), MS_ASYNC); }
private:
int file_descriptor_;
};
std::unique_ptr<MappedMemory> MappedMemory::Open(
const std::filesystem::path& path, Mode mode, size_t offset,
size_t length) {
const char* mode_str;
int prot;
int open_flags = 0;
switch (mode) {
case Mode::kRead:
mode_str = "rb";
prot = PROT_READ;
open_flags |= O_RDONLY;
break;
case Mode::kReadWrite:
mode_str = "r+b";
prot = PROT_READ | PROT_WRITE;
open_flags |= O_RDWR;
break;
}
auto mm =
std::unique_ptr<PosixMappedMemory>(new PosixMappedMemory(path, mode));
mm->file_handle = fopen(path.c_str(), mode_str);
if (!mm->file_handle) {
int file_descriptor = open(path.c_str(), open_flags);
if (file_descriptor < 0) {
return nullptr;
}
size_t map_length;
map_length = length;
if (!length) {
fseeko(mm->file_handle, 0, SEEK_END);
map_length = ftello(mm->file_handle);
fseeko(mm->file_handle, 0, SEEK_SET);
}
mm->size_ = map_length;
mm->data_ =
mmap(0, map_length, prot, MAP_SHARED, fileno(mm->file_handle), offset);
if (!mm->data_) {
return nullptr;
}
return std::move(mm);
return PosixMappedMemory::WrapFileDescriptor(file_descriptor, mode, offset,
length);
}
#if XE_PLATFORM_ANDROID
std::unique_ptr<MappedMemory> MappedMemory::OpenForAndroidContentUri(
const std::string_view uri, Mode mode, size_t offset, size_t length) {
const char* open_mode = nullptr;
switch (mode) {
case Mode::kRead:
open_mode = "r";
break;
case Mode::kReadWrite:
open_mode = "rw";
break;
}
int file_descriptor =
xe::filesystem::OpenAndroidContentFileDescriptor(uri, open_mode);
if (file_descriptor < 0) {
return nullptr;
}
return PosixMappedMemory::WrapFileDescriptor(file_descriptor, mode, offset,
length);
}
#endif // XE_PLATFORM_ANDROID
std::unique_ptr<ChunkedMappedMemoryWriter> ChunkedMappedMemoryWriter::Open(
const std::filesystem::path& path, size_t chunk_size,
bool low_address_space) {

View File

@@ -32,9 +32,6 @@ class Win32MappedMemory : public MappedMemory {
// CreateFileMapping returns nullptr in case of failure.
static constexpr HANDLE kMappingHandleInvalid = nullptr;
Win32MappedMemory(const std::filesystem::path& path, Mode mode)
: MappedMemory(path, mode) {}
~Win32MappedMemory() override {
if (data_) {
UnmapViewOfFile(data_);
@@ -135,7 +132,7 @@ std::unique_ptr<MappedMemory> MappedMemory::Open(
offset & ~static_cast<size_t>(system_info.dwAllocationGranularity - 1);
const size_t aligned_length = length + (offset - aligned_offset);
auto mm = std::make_unique<Win32MappedMemory>(path, mode);
auto mm = std::make_unique<Win32MappedMemory>();
mm->view_access_ = view_access;
mm->file_handle = CreateFile(path.c_str(), file_access, file_share, nullptr,

View File

@@ -67,9 +67,6 @@
#endif
#if XE_PLATFORM_WIN32
#define strdup _strdup
#define strcasecmp _stricmp
#define strncasecmp _strnicmp
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX // Don't want windows.h including min/max macros.
#endif // XE_PLATFORM_WIN32

View File

@@ -9,9 +9,16 @@
#include "xenia/base/string.h"
#include <string.h>
#include <algorithm>
#include <locale>
#include "xenia/base/platform.h"
#if !XE_PLATFORM_WIN32
#include <strings.h>
#endif // !XE_PLATFORM_WIN32
#define UTF_CPP_CPLUSPLUS 201703L
#include "third_party/utfcpp/source/utf8.h"
@@ -19,6 +26,30 @@ namespace utfcpp = utf8;
namespace xe {
int xe_strcasecmp(const char* string1, const char* string2) {
#if XE_PLATFORM_WIN32
return _stricmp(string1, string2);
#else
return strcasecmp(string1, string2);
#endif // XE_PLATFORM_WIN32
}
int xe_strncasecmp(const char* string1, const char* string2, size_t count) {
#if XE_PLATFORM_WIN32
return _strnicmp(string1, string2, count);
#else
return strncasecmp(string1, string2, count);
#endif // XE_PLATFORM_WIN32
}
char* xe_strdup(const char* source) {
#if XE_PLATFORM_WIN32
return _strdup(source);
#else
return strdup(source);
#endif // XE_PLATFORM_WIN32
}
std::string to_utf8(const std::u16string_view source) {
return utfcpp::utf16to8(source);
}

View File

@@ -12,10 +12,14 @@
#include <string>
#include "utf8.h"
#include "xenia/base/utf8.h"
namespace xe {
int xe_strcasecmp(const char* string1, const char* string2);
int xe_strncasecmp(const char* string1, const char* string2, size_t count);
char* xe_strdup(const char* source);
std::string to_utf8(const std::u16string_view source);
std::u16string to_utf16(const std::string_view source);