summaryrefslogtreecommitdiffstats
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/CMakeLists.txt40
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.cpp5
-rw-r--r--src/core/arm/exclusive_monitor.cpp14
-rw-r--r--src/core/arm/exclusive_monitor.h9
-rw-r--r--src/core/arm/unicorn/arm_unicorn.cpp3
-rw-r--r--src/core/core.cpp75
-rw-r--r--src/core/core.h10
-rw-r--r--src/core/core_cpu.cpp127
-rw-r--r--src/core/core_cpu.h120
-rw-r--r--src/core/core_manager.cpp70
-rw-r--r--src/core/core_manager.h63
-rw-r--r--src/core/cpu_core_manager.cpp152
-rw-r--r--src/core/cpu_core_manager.h62
-rw-r--r--src/core/cpu_manager.cpp81
-rw-r--r--src/core/cpu_manager.h50
-rw-r--r--src/core/file_sys/system_archive/system_archive.cpp3
-rw-r--r--src/core/file_sys/system_archive/time_zone_binary.cpp657
-rw-r--r--src/core/file_sys/system_archive/time_zone_binary.h14
-rw-r--r--src/core/gdbstub/gdbstub.cpp2
-rw-r--r--src/core/hle/kernel/address_arbiter.cpp1
-rw-r--r--src/core/hle/kernel/kernel.cpp52
-rw-r--r--src/core/hle/kernel/kernel.h19
-rw-r--r--src/core/hle/kernel/physical_core.cpp51
-rw-r--r--src/core/hle/kernel/physical_core.h77
-rw-r--r--src/core/hle/kernel/physical_memory.h5
-rw-r--r--src/core/hle/kernel/process.cpp4
-rw-r--r--src/core/hle/kernel/scheduler.cpp1
-rw-r--r--src/core/hle/kernel/svc.cpp2
-rw-r--r--src/core/hle/kernel/thread.cpp3
-rw-r--r--src/core/hle/kernel/vm_manager.cpp37
-rw-r--r--src/core/hle/kernel/wait_object.cpp3
-rw-r--r--src/core/hle/service/acc/acc.cpp2
-rw-r--r--src/core/hle/service/acc/profile_manager.cpp14
-rw-r--r--src/core/hle/service/acc/profile_manager.h27
-rw-r--r--src/core/hle/service/friend/friend.cpp2
-rw-r--r--src/core/hle/service/mii/mii_manager.h156
-rw-r--r--src/core/hle/service/sockets/bsd.cpp48
-rw-r--r--src/core/hle/service/sockets/bsd.h4
-rw-r--r--src/core/hle/service/time/clock_types.h103
-rw-r--r--src/core/hle/service/time/ephemeral_network_system_clock_context_writer.h16
-rw-r--r--src/core/hle/service/time/ephemeral_network_system_clock_core.h17
-rw-r--r--src/core/hle/service/time/errors.h22
-rw-r--r--src/core/hle/service/time/interface.cpp19
-rw-r--r--src/core/hle/service/time/interface.h11
-rw-r--r--src/core/hle/service/time/local_system_clock_context_writer.h28
-rw-r--r--src/core/hle/service/time/network_system_clock_context_writer.h28
-rw-r--r--src/core/hle/service/time/standard_local_system_clock_core.h17
-rw-r--r--src/core/hle/service/time/standard_network_system_clock_core.h46
-rw-r--r--src/core/hle/service/time/standard_steady_clock_core.cpp26
-rw-r--r--src/core/hle/service/time/standard_steady_clock_core.h42
-rw-r--r--src/core/hle/service/time/standard_user_system_clock_core.cpp77
-rw-r--r--src/core/hle/service/time/standard_user_system_clock_core.h57
-rw-r--r--src/core/hle/service/time/steady_clock_core.h55
-rw-r--r--src/core/hle/service/time/system_clock_context_update_callback.cpp55
-rw-r--r--src/core/hle/service/time/system_clock_context_update_callback.h43
-rw-r--r--src/core/hle/service/time/system_clock_core.cpp72
-rw-r--r--src/core/hle/service/time/system_clock_core.h71
-rw-r--r--src/core/hle/service/time/tick_based_steady_clock_core.cpp24
-rw-r--r--src/core/hle/service/time/tick_based_steady_clock_core.h29
-rw-r--r--src/core/hle/service/time/time.cpp474
-rw-r--r--src/core/hle/service/time/time.h101
-rw-r--r--src/core/hle/service/time/time_manager.cpp137
-rw-r--r--src/core/hle/service/time/time_manager.h117
-rw-r--r--src/core/hle/service/time/time_sharedmemory.cpp53
-rw-r--r--src/core/hle/service/time/time_sharedmemory.h35
-rw-r--r--src/core/hle/service/time/time_zone_content_manager.cpp125
-rw-r--r--src/core/hle/service/time/time_zone_content_manager.h46
-rw-r--r--src/core/hle/service/time/time_zone_manager.cpp1039
-rw-r--r--src/core/hle/service/time/time_zone_manager.h54
-rw-r--r--src/core/hle/service/time/time_zone_service.cpp170
-rw-r--r--src/core/hle/service/time/time_zone_service.h31
-rw-r--r--src/core/hle/service/time/time_zone_types.h87
-rw-r--r--src/core/loader/elf.cpp3
-rw-r--r--src/core/loader/kip.cpp5
-rw-r--r--src/core/loader/nso.cpp21
-rw-r--r--src/core/loader/nso.h2
-rw-r--r--src/core/memory.cpp11
-rw-r--r--src/core/memory.h16
-rw-r--r--src/core/settings.h3
79 files changed, 4359 insertions, 1094 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 7fd226050..d342cafe0 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -15,14 +15,14 @@ add_library(core STATIC
constants.h
core.cpp
core.h
- core_cpu.cpp
- core_cpu.h
+ core_manager.cpp
+ core_manager.h
core_timing.cpp
core_timing.h
core_timing_util.cpp
core_timing_util.h
- cpu_core_manager.cpp
- cpu_core_manager.h
+ cpu_manager.cpp
+ cpu_manager.h
crypto/aes_util.cpp
crypto/aes_util.h
crypto/encryption_layer.cpp
@@ -96,6 +96,8 @@ add_library(core STATIC
file_sys/system_archive/system_archive.h
file_sys/system_archive/system_version.cpp
file_sys/system_archive/system_version.h
+ file_sys/system_archive/time_zone_binary.cpp
+ file_sys/system_archive/time_zone_binary.h
file_sys/vfs.cpp
file_sys/vfs.h
file_sys/vfs_concat.cpp
@@ -156,6 +158,8 @@ add_library(core STATIC
hle/kernel/mutex.h
hle/kernel/object.cpp
hle/kernel/object.h
+ hle/kernel/physical_core.cpp
+ hle/kernel/physical_core.h
hle/kernel/process.cpp
hle/kernel/process.h
hle/kernel/process_capability.cpp
@@ -461,12 +465,40 @@ add_library(core STATIC
hle/service/spl/spl.h
hle/service/ssl/ssl.cpp
hle/service/ssl/ssl.h
+ hle/service/time/clock_types.h
+ hle/service/time/ephemeral_network_system_clock_context_writer.h
+ hle/service/time/ephemeral_network_system_clock_core.h
+ hle/service/time/errors.h
hle/service/time/interface.cpp
hle/service/time/interface.h
+ hle/service/time/local_system_clock_context_writer.h
+ hle/service/time/network_system_clock_context_writer.h
+ hle/service/time/standard_local_system_clock_core.h
+ hle/service/time/standard_network_system_clock_core.h
+ hle/service/time/standard_steady_clock_core.cpp
+ hle/service/time/standard_steady_clock_core.h
+ hle/service/time/standard_user_system_clock_core.cpp
+ hle/service/time/standard_user_system_clock_core.h
+ hle/service/time/steady_clock_core.h
+ hle/service/time/system_clock_context_update_callback.cpp
+ hle/service/time/system_clock_context_update_callback.h
+ hle/service/time/system_clock_core.cpp
+ hle/service/time/system_clock_core.h
+ hle/service/time/tick_based_steady_clock_core.cpp
+ hle/service/time/tick_based_steady_clock_core.h
hle/service/time/time.cpp
hle/service/time/time.h
+ hle/service/time/time_manager.cpp
+ hle/service/time/time_manager.h
hle/service/time/time_sharedmemory.cpp
hle/service/time/time_sharedmemory.h
+ hle/service/time/time_zone_content_manager.cpp
+ hle/service/time/time_zone_content_manager.h
+ hle/service/time/time_zone_manager.cpp
+ hle/service/time/time_zone_manager.h
+ hle/service/time/time_zone_service.cpp
+ hle/service/time/time_zone_service.h
+ hle/service/time/time_zone_types.h
hle/service/usb/usb.cpp
hle/service/usb/usb.h
hle/service/vi/display/vi_display.cpp
diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp
index e825c0526..791640a3a 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic.cpp
@@ -10,11 +10,12 @@
#include "common/microprofile.h"
#include "core/arm/dynarmic/arm_dynarmic.h"
#include "core/core.h"
-#include "core/core_cpu.h"
+#include "core/core_manager.h"
#include "core/core_timing.h"
#include "core/core_timing_util.h"
#include "core/gdbstub/gdbstub.h"
#include "core/hle/kernel/process.h"
+#include "core/hle/kernel/scheduler.h"
#include "core/hle/kernel/svc.h"
#include "core/hle/kernel/vm_manager.h"
#include "core/memory.h"
@@ -87,7 +88,7 @@ public:
if (GDBStub::IsServerEnabled()) {
parent.jit->HaltExecution();
parent.SetPC(pc);
- Kernel::Thread* thread = Kernel::GetCurrentThread();
+ Kernel::Thread* const thread = parent.system.CurrentScheduler().GetCurrentThread();
parent.SaveContext(thread->GetContext());
GDBStub::Break();
GDBStub::SendTrap(thread, 5);
diff --git a/src/core/arm/exclusive_monitor.cpp b/src/core/arm/exclusive_monitor.cpp
index abd59ff4b..94570e520 100644
--- a/src/core/arm/exclusive_monitor.cpp
+++ b/src/core/arm/exclusive_monitor.cpp
@@ -2,10 +2,24 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#ifdef ARCHITECTURE_x86_64
+#include "core/arm/dynarmic/arm_dynarmic.h"
+#endif
#include "core/arm/exclusive_monitor.h"
+#include "core/memory.h"
namespace Core {
ExclusiveMonitor::~ExclusiveMonitor() = default;
+std::unique_ptr<Core::ExclusiveMonitor> MakeExclusiveMonitor(Memory::Memory& memory,
+ std::size_t num_cores) {
+#ifdef ARCHITECTURE_x86_64
+ return std::make_unique<Core::DynarmicExclusiveMonitor>(memory, num_cores);
+#else
+ // TODO(merry): Passthrough exclusive monitor
+ return nullptr;
+#endif
+}
+
} // namespace Core
diff --git a/src/core/arm/exclusive_monitor.h b/src/core/arm/exclusive_monitor.h
index f59aca667..4ef418b90 100644
--- a/src/core/arm/exclusive_monitor.h
+++ b/src/core/arm/exclusive_monitor.h
@@ -4,8 +4,14 @@
#pragma once
+#include <memory>
+
#include "common/common_types.h"
+namespace Memory {
+class Memory;
+}
+
namespace Core {
class ExclusiveMonitor {
@@ -22,4 +28,7 @@ public:
virtual bool ExclusiveWrite128(std::size_t core_index, VAddr vaddr, u128 value) = 0;
};
+std::unique_ptr<Core::ExclusiveMonitor> MakeExclusiveMonitor(Memory::Memory& memory,
+ std::size_t num_cores);
+
} // namespace Core
diff --git a/src/core/arm/unicorn/arm_unicorn.cpp b/src/core/arm/unicorn/arm_unicorn.cpp
index 48182c99a..f99ad5802 100644
--- a/src/core/arm/unicorn/arm_unicorn.cpp
+++ b/src/core/arm/unicorn/arm_unicorn.cpp
@@ -9,6 +9,7 @@
#include "core/arm/unicorn/arm_unicorn.h"
#include "core/core.h"
#include "core/core_timing.h"
+#include "core/hle/kernel/scheduler.h"
#include "core/hle/kernel/svc.h"
namespace Core {
@@ -177,7 +178,7 @@ void ARM_Unicorn::ExecuteInstructions(std::size_t num_instructions) {
uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address);
}
- Kernel::Thread* thread = Kernel::GetCurrentThread();
+ Kernel::Thread* const thread = system.CurrentScheduler().GetCurrentThread();
SaveContext(thread->GetContext());
if (last_bkpt_hit || GDBStub::IsMemoryBreak() || GDBStub::GetCpuStepFlag()) {
last_bkpt_hit = false;
diff --git a/src/core/core.cpp b/src/core/core.cpp
index d697b80ef..c53d122be 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -11,9 +11,9 @@
#include "common/string_util.h"
#include "core/arm/exclusive_monitor.h"
#include "core/core.h"
-#include "core/core_cpu.h"
+#include "core/core_manager.h"
#include "core/core_timing.h"
-#include "core/cpu_core_manager.h"
+#include "core/cpu_manager.h"
#include "core/file_sys/bis_factory.h"
#include "core/file_sys/card_image.h"
#include "core/file_sys/mode.h"
@@ -28,6 +28,7 @@
#include "core/hardware_interrupt_manager.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/physical_core.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/scheduler.h"
#include "core/hle/kernel/thread.h"
@@ -113,16 +114,25 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
struct System::Impl {
explicit Impl(System& system)
: kernel{system}, fs_controller{system}, memory{system},
- cpu_core_manager{system}, reporter{system}, applet_manager{system} {}
+ cpu_manager{system}, reporter{system}, applet_manager{system} {}
- Cpu& CurrentCpuCore() {
- return cpu_core_manager.GetCurrentCore();
+ CoreManager& CurrentCoreManager() {
+ return cpu_manager.GetCurrentCoreManager();
+ }
+
+ Kernel::PhysicalCore& CurrentPhysicalCore() {
+ const auto index = cpu_manager.GetActiveCoreIndex();
+ return kernel.PhysicalCore(index);
+ }
+
+ Kernel::PhysicalCore& GetPhysicalCore(std::size_t index) {
+ return kernel.PhysicalCore(index);
}
ResultStatus RunLoop(bool tight_loop) {
status = ResultStatus::Success;
- cpu_core_manager.RunLoop(tight_loop);
+ cpu_manager.RunLoop(tight_loop);
return status;
}
@@ -131,8 +141,8 @@ struct System::Impl {
LOG_DEBUG(HW_Memory, "initialized OK");
core_timing.Initialize();
- cpu_core_manager.Initialize();
kernel.Initialize();
+ cpu_manager.Initialize();
const auto current_time = std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now().time_since_epoch());
@@ -205,7 +215,6 @@ struct System::Impl {
// Main process has been loaded and been made current.
// Begin GPU and CPU execution.
gpu_core->Start();
- cpu_core_manager.StartThreads();
// Initialize cheat engine
if (cheat_engine) {
@@ -272,7 +281,7 @@ struct System::Impl {
gpu_core.reset();
// Close all CPU/threading state
- cpu_core_manager.Shutdown();
+ cpu_manager.Shutdown();
// Shutdown kernel and core timing
kernel.Shutdown();
@@ -342,7 +351,7 @@ struct System::Impl {
std::unique_ptr<Tegra::GPU> gpu_core;
std::unique_ptr<Hardware::InterruptManager> interrupt_manager;
Memory::Memory memory;
- CpuCoreManager cpu_core_manager;
+ CpuManager cpu_manager;
bool is_powered_on = false;
bool exit_lock = false;
@@ -377,12 +386,12 @@ struct System::Impl {
System::System() : impl{std::make_unique<Impl>(*this)} {}
System::~System() = default;
-Cpu& System::CurrentCpuCore() {
- return impl->CurrentCpuCore();
+CoreManager& System::CurrentCoreManager() {
+ return impl->CurrentCoreManager();
}
-const Cpu& System::CurrentCpuCore() const {
- return impl->CurrentCpuCore();
+const CoreManager& System::CurrentCoreManager() const {
+ return impl->CurrentCoreManager();
}
System::ResultStatus System::RunLoop(bool tight_loop) {
@@ -394,7 +403,7 @@ System::ResultStatus System::SingleStep() {
}
void System::InvalidateCpuInstructionCaches() {
- impl->cpu_core_manager.InvalidateAllInstructionCaches();
+ impl->kernel.InvalidateAllInstructionCaches();
}
System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath) {
@@ -406,13 +415,11 @@ bool System::IsPoweredOn() const {
}
void System::PrepareReschedule() {
- CurrentCpuCore().PrepareReschedule();
+ impl->CurrentPhysicalCore().Stop();
}
void System::PrepareReschedule(const u32 core_index) {
- if (core_index < GlobalScheduler().CpuCoresCount()) {
- CpuCore(core_index).PrepareReschedule();
- }
+ impl->kernel.PrepareReschedule(core_index);
}
PerfStatsResults System::GetAndResetPerfStats() {
@@ -428,31 +435,31 @@ const TelemetrySession& System::TelemetrySession() const {
}
ARM_Interface& System::CurrentArmInterface() {
- return CurrentCpuCore().ArmInterface();
+ return impl->CurrentPhysicalCore().ArmInterface();
}
const ARM_Interface& System::CurrentArmInterface() const {
- return CurrentCpuCore().ArmInterface();
+ return impl->CurrentPhysicalCore().ArmInterface();
}
std::size_t System::CurrentCoreIndex() const {
- return CurrentCpuCore().CoreIndex();
+ return impl->cpu_manager.GetActiveCoreIndex();
}
Kernel::Scheduler& System::CurrentScheduler() {
- return CurrentCpuCore().Scheduler();
+ return impl->CurrentPhysicalCore().Scheduler();
}
const Kernel::Scheduler& System::CurrentScheduler() const {
- return CurrentCpuCore().Scheduler();
+ return impl->CurrentPhysicalCore().Scheduler();
}
Kernel::Scheduler& System::Scheduler(std::size_t core_index) {
- return CpuCore(core_index).Scheduler();
+ return impl->GetPhysicalCore(core_index).Scheduler();
}
const Kernel::Scheduler& System::Scheduler(std::size_t core_index) const {
- return CpuCore(core_index).Scheduler();
+ return impl->GetPhysicalCore(core_index).Scheduler();
}
/// Gets the global scheduler
@@ -474,28 +481,28 @@ const Kernel::Process* System::CurrentProcess() const {
}
ARM_Interface& System::ArmInterface(std::size_t core_index) {
- return CpuCore(core_index).ArmInterface();
+ return impl->GetPhysicalCore(core_index).ArmInterface();
}
const ARM_Interface& System::ArmInterface(std::size_t core_index) const {
- return CpuCore(core_index).ArmInterface();
+ return impl->GetPhysicalCore(core_index).ArmInterface();
}
-Cpu& System::CpuCore(std::size_t core_index) {
- return impl->cpu_core_manager.GetCore(core_index);
+CoreManager& System::GetCoreManager(std::size_t core_index) {
+ return impl->cpu_manager.GetCoreManager(core_index);
}
-const Cpu& System::CpuCore(std::size_t core_index) const {
+const CoreManager& System::GetCoreManager(std::size_t core_index) const {
ASSERT(core_index < NUM_CPU_CORES);
- return impl->cpu_core_manager.GetCore(core_index);
+ return impl->cpu_manager.GetCoreManager(core_index);
}
ExclusiveMonitor& System::Monitor() {
- return impl->cpu_core_manager.GetExclusiveMonitor();
+ return impl->kernel.GetExclusiveMonitor();
}
const ExclusiveMonitor& System::Monitor() const {
- return impl->cpu_core_manager.GetExclusiveMonitor();
+ return impl->kernel.GetExclusiveMonitor();
}
Memory::Memory& System::Memory() {
diff --git a/src/core/core.h b/src/core/core.h
index e240c5c58..e69d68fcf 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -93,7 +93,7 @@ class Memory;
namespace Core {
class ARM_Interface;
-class Cpu;
+class CoreManager;
class ExclusiveMonitor;
class FrameLimiter;
class PerfStats;
@@ -218,10 +218,10 @@ public:
const ARM_Interface& ArmInterface(std::size_t core_index) const;
/// Gets a CPU interface to the CPU core with the specified index
- Cpu& CpuCore(std::size_t core_index);
+ CoreManager& GetCoreManager(std::size_t core_index);
/// Gets a CPU interface to the CPU core with the specified index
- const Cpu& CpuCore(std::size_t core_index) const;
+ const CoreManager& GetCoreManager(std::size_t core_index) const;
/// Gets a reference to the exclusive monitor
ExclusiveMonitor& Monitor();
@@ -364,10 +364,10 @@ private:
System();
/// Returns the currently running CPU core
- Cpu& CurrentCpuCore();
+ CoreManager& CurrentCoreManager();
/// Returns the currently running CPU core
- const Cpu& CurrentCpuCore() const;
+ const CoreManager& CurrentCoreManager() const;
/**
* Initialize the emulated system.
diff --git a/src/core/core_cpu.cpp b/src/core/core_cpu.cpp
deleted file mode 100644
index 630cd4feb..000000000
--- a/src/core/core_cpu.cpp
+++ /dev/null
@@ -1,127 +0,0 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include <condition_variable>
-#include <mutex>
-
-#include "common/logging/log.h"
-#ifdef ARCHITECTURE_x86_64
-#include "core/arm/dynarmic/arm_dynarmic.h"
-#endif
-#include "core/arm/exclusive_monitor.h"
-#include "core/arm/unicorn/arm_unicorn.h"
-#include "core/core.h"
-#include "core/core_cpu.h"
-#include "core/core_timing.h"
-#include "core/hle/kernel/scheduler.h"
-#include "core/hle/kernel/thread.h"
-#include "core/hle/lock.h"
-#include "core/settings.h"
-
-namespace Core {
-
-void CpuBarrier::NotifyEnd() {
- std::unique_lock lock{mutex};
- end = true;
- condition.notify_all();
-}
-
-bool CpuBarrier::Rendezvous() {
- if (!Settings::values.use_multi_core) {
- // Meaningless when running in single-core mode
- return true;
- }
-
- if (!end) {
- std::unique_lock lock{mutex};
-
- --cores_waiting;
- if (!cores_waiting) {
- cores_waiting = NUM_CPU_CORES;
- condition.notify_all();
- return true;
- }
-
- condition.wait(lock);
- return true;
- }
-
- return false;
-}
-
-Cpu::Cpu(System& system, ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_barrier,
- std::size_t core_index)
- : cpu_barrier{cpu_barrier}, global_scheduler{system.GlobalScheduler()},
- core_timing{system.CoreTiming()}, core_index{core_index} {
-#ifdef ARCHITECTURE_x86_64
- arm_interface = std::make_unique<ARM_Dynarmic>(system, exclusive_monitor, core_index);
-#else
- arm_interface = std::make_unique<ARM_Unicorn>(system);
- LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");
-#endif
-
- scheduler = std::make_unique<Kernel::Scheduler>(system, *arm_interface, core_index);
-}
-
-Cpu::~Cpu() = default;
-
-std::unique_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor(
- [[maybe_unused]] Memory::Memory& memory, [[maybe_unused]] std::size_t num_cores) {
-#ifdef ARCHITECTURE_x86_64
- return std::make_unique<DynarmicExclusiveMonitor>(memory, num_cores);
-#else
- // TODO(merry): Passthrough exclusive monitor
- return nullptr;
-#endif
-}
-
-void Cpu::RunLoop(bool tight_loop) {
- // Wait for all other CPU cores to complete the previous slice, such that they run in lock-step
- if (!cpu_barrier.Rendezvous()) {
- // If rendezvous failed, session has been killed
- return;
- }
-
- Reschedule();
-
- // If we don't have a currently active thread then don't execute instructions,
- // instead advance to the next event and try to yield to the next thread
- if (Kernel::GetCurrentThread() == nullptr) {
- LOG_TRACE(Core, "Core-{} idling", core_index);
- core_timing.Idle();
- } else {
- if (tight_loop) {
- arm_interface->Run();
- } else {
- arm_interface->Step();
- }
- // We are stopping a run, exclusive state must be cleared
- arm_interface->ClearExclusiveState();
- }
- core_timing.Advance();
-
- Reschedule();
-}
-
-void Cpu::SingleStep() {
- return RunLoop(false);
-}
-
-void Cpu::PrepareReschedule() {
- arm_interface->PrepareReschedule();
-}
-
-void Cpu::Reschedule() {
- // Lock the global kernel mutex when we manipulate the HLE state
- std::lock_guard lock(HLE::g_hle_lock);
-
- global_scheduler.SelectThread(core_index);
- scheduler->TryDoContextSwitch();
-}
-
-void Cpu::Shutdown() {
- scheduler->Shutdown();
-}
-
-} // namespace Core
diff --git a/src/core/core_cpu.h b/src/core/core_cpu.h
deleted file mode 100644
index 78f5021a2..000000000
--- a/src/core/core_cpu.h
+++ /dev/null
@@ -1,120 +0,0 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include <atomic>
-#include <condition_variable>
-#include <cstddef>
-#include <memory>
-#include <mutex>
-#include "common/common_types.h"
-
-namespace Kernel {
-class GlobalScheduler;
-class Scheduler;
-} // namespace Kernel
-
-namespace Core {
-class System;
-}
-
-namespace Core::Timing {
-class CoreTiming;
-}
-
-namespace Memory {
-class Memory;
-}
-
-namespace Core {
-
-class ARM_Interface;
-class ExclusiveMonitor;
-
-constexpr unsigned NUM_CPU_CORES{4};
-
-class CpuBarrier {
-public:
- bool IsAlive() const {
- return !end;
- }
-
- void NotifyEnd();
-
- bool Rendezvous();
-
-private:
- unsigned cores_waiting{NUM_CPU_CORES};
- std::mutex mutex;
- std::condition_variable condition;
- std::atomic<bool> end{};
-};
-
-class Cpu {
-public:
- Cpu(System& system, ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_barrier,
- std::size_t core_index);
- ~Cpu();
-
- void RunLoop(bool tight_loop = true);
-
- void SingleStep();
-
- void PrepareReschedule();
-
- ARM_Interface& ArmInterface() {
- return *arm_interface;
- }
-
- const ARM_Interface& ArmInterface() const {
- return *arm_interface;
- }
-
- Kernel::Scheduler& Scheduler() {
- return *scheduler;
- }
-
- const Kernel::Scheduler& Scheduler() const {
- return *scheduler;
- }
-
- bool IsMainCore() const {
- return core_index == 0;
- }
-
- std::size_t CoreIndex() const {
- return core_index;
- }
-
- void Shutdown();
-
- /**
- * Creates an exclusive monitor to handle exclusive reads/writes.
- *
- * @param memory The current memory subsystem that the monitor may wish
- * to keep track of.
- *
- * @param num_cores The number of cores to assume about the CPU.
- *
- * @returns The constructed exclusive monitor instance, or nullptr if the current
- * CPU backend is unable to use an exclusive monitor.
- */
- static std::unique_ptr<ExclusiveMonitor> MakeExclusiveMonitor(Memory::Memory& memory,
- std::size_t num_cores);
-
-private:
- void Reschedule();
-
- std::unique_ptr<ARM_Interface> arm_interface;
- CpuBarrier& cpu_barrier;
- Kernel::GlobalScheduler& global_scheduler;
- std::unique_ptr<Kernel::Scheduler> scheduler;
- Timing::CoreTiming& core_timing;
-
- std::atomic<bool> reschedule_pending = false;
- std::size_t core_index;
-};
-
-} // namespace Core
diff --git a/src/core/core_manager.cpp b/src/core/core_manager.cpp
new file mode 100644
index 000000000..8eacf92dd
--- /dev/null
+++ b/src/core/core_manager.cpp
@@ -0,0 +1,70 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <condition_variable>
+#include <mutex>
+
+#include "common/logging/log.h"
+#ifdef ARCHITECTURE_x86_64
+#include "core/arm/dynarmic/arm_dynarmic.h"
+#endif
+#include "core/arm/exclusive_monitor.h"
+#include "core/arm/unicorn/arm_unicorn.h"
+#include "core/core.h"
+#include "core/core_manager.h"
+#include "core/core_timing.h"
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/physical_core.h"
+#include "core/hle/kernel/scheduler.h"
+#include "core/hle/kernel/thread.h"
+#include "core/hle/lock.h"
+#include "core/settings.h"
+
+namespace Core {
+
+CoreManager::CoreManager(System& system, std::size_t core_index)
+ : global_scheduler{system.GlobalScheduler()}, physical_core{system.Kernel().PhysicalCore(
+ core_index)},
+ core_timing{system.CoreTiming()}, core_index{core_index} {}
+
+CoreManager::~CoreManager() = default;
+
+void CoreManager::RunLoop(bool tight_loop) {
+ Reschedule();
+
+ // If we don't have a currently active thread then don't execute instructions,
+ // instead advance to the next event and try to yield to the next thread
+ if (Kernel::GetCurrentThread() == nullptr) {
+ LOG_TRACE(Core, "Core-{} idling", core_index);
+ core_timing.Idle();
+ } else {
+ if (tight_loop) {
+ physical_core.Run();
+ } else {
+ physical_core.Step();
+ }
+ }
+ core_timing.Advance();
+
+ Reschedule();
+}
+
+void CoreManager::SingleStep() {
+ return RunLoop(false);
+}
+
+void CoreManager::PrepareReschedule() {
+ physical_core.Stop();
+}
+
+void CoreManager::Reschedule() {
+ // Lock the global kernel mutex when we manipulate the HLE state
+ std::lock_guard lock(HLE::g_hle_lock);
+
+ global_scheduler.SelectThread(core_index);
+
+ physical_core.Scheduler().TryDoContextSwitch();
+}
+
+} // namespace Core
diff --git a/src/core/core_manager.h b/src/core/core_manager.h
new file mode 100644
index 000000000..b14e723d7
--- /dev/null
+++ b/src/core/core_manager.h
@@ -0,0 +1,63 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <atomic>
+#include <cstddef>
+#include <memory>
+#include "common/common_types.h"
+
+namespace Kernel {
+class GlobalScheduler;
+class PhysicalCore;
+} // namespace Kernel
+
+namespace Core {
+class System;
+}
+
+namespace Core::Timing {
+class CoreTiming;
+}
+
+namespace Memory {
+class Memory;
+}
+
+namespace Core {
+
+constexpr unsigned NUM_CPU_CORES{4};
+
+class CoreManager {
+public:
+ CoreManager(System& system, std::size_t core_index);
+ ~CoreManager();
+
+ void RunLoop(bool tight_loop = true);
+
+ void SingleStep();
+
+ void PrepareReschedule();
+
+ bool IsMainCore() const {
+ return core_index == 0;
+ }
+
+ std::size_t CoreIndex() const {
+ return core_index;
+ }
+
+private:
+ void Reschedule();
+
+ Kernel::GlobalScheduler& global_scheduler;
+ Kernel::PhysicalCore& physical_core;
+ Timing::CoreTiming& core_timing;
+
+ std::atomic<bool> reschedule_pending = false;
+ std::size_t core_index;
+};
+
+} // namespace Core
diff --git a/src/core/cpu_core_manager.cpp b/src/core/cpu_core_manager.cpp
deleted file mode 100644
index f04a34133..000000000
--- a/src/core/cpu_core_manager.cpp
+++ /dev/null
@@ -1,152 +0,0 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include "common/assert.h"
-#include "core/arm/exclusive_monitor.h"
-#include "core/core.h"
-#include "core/core_cpu.h"
-#include "core/core_timing.h"
-#include "core/cpu_core_manager.h"
-#include "core/gdbstub/gdbstub.h"
-#include "core/settings.h"
-
-namespace Core {
-namespace {
-void RunCpuCore(const System& system, Cpu& cpu_state) {
- while (system.IsPoweredOn()) {
- cpu_state.RunLoop(true);
- }
-}
-} // Anonymous namespace
-
-CpuCoreManager::CpuCoreManager(System& system) : system{system} {}
-CpuCoreManager::~CpuCoreManager() = default;
-
-void CpuCoreManager::Initialize() {
- barrier = std::make_unique<CpuBarrier>();
- exclusive_monitor = Cpu::MakeExclusiveMonitor(system.Memory(), cores.size());
-
- for (std::size_t index = 0; index < cores.size(); ++index) {
- cores[index] = std::make_unique<Cpu>(system, *exclusive_monitor, *barrier, index);
- }
-}
-
-void CpuCoreManager::StartThreads() {
- // Create threads for CPU cores 1-3, and build thread_to_cpu map
- // CPU core 0 is run on the main thread
- thread_to_cpu[std::this_thread::get_id()] = cores[0].get();
- if (!Settings::values.use_multi_core) {
- return;
- }
-
- for (std::size_t index = 0; index < core_threads.size(); ++index) {
- core_threads[index] = std::make_unique<std::thread>(RunCpuCore, std::cref(system),
- std::ref(*cores[index + 1]));
- thread_to_cpu[core_threads[index]->get_id()] = cores[index + 1].get();
- }
-}
-
-void CpuCoreManager::Shutdown() {
- barrier->NotifyEnd();
- if (Settings::values.use_multi_core) {
- for (auto& thread : core_threads) {
- thread->join();
- thread.reset();
- }
- }
-
- thread_to_cpu.clear();
- for (auto& cpu_core : cores) {
- cpu_core->Shutdown();
- cpu_core.reset();
- }
-
- exclusive_monitor.reset();
- barrier.reset();
-}
-
-Cpu& CpuCoreManager::GetCore(std::size_t index) {
- return *cores.at(index);
-}
-
-const Cpu& CpuCoreManager::GetCore(std::size_t index) const {
- return *cores.at(index);
-}
-
-ExclusiveMonitor& CpuCoreManager::GetExclusiveMonitor() {
- return *exclusive_monitor;
-}
-
-const ExclusiveMonitor& CpuCoreManager::GetExclusiveMonitor() const {
- return *exclusive_monitor;
-}
-
-Cpu& CpuCoreManager::GetCurrentCore() {
- if (Settings::values.use_multi_core) {
- const auto& search = thread_to_cpu.find(std::this_thread::get_id());
- ASSERT(search != thread_to_cpu.end());
- ASSERT(search->second);
- return *search->second;
- }
-
- // Otherwise, use single-threaded mode active_core variable
- return *cores[active_core];
-}
-
-const Cpu& CpuCoreManager::GetCurrentCore() const {
- if (Settings::values.use_multi_core) {
- const auto& search = thread_to_cpu.find(std::this_thread::get_id());
- ASSERT(search != thread_to_cpu.end());
- ASSERT(search->second);
- return *search->second;
- }
-
- // Otherwise, use single-threaded mode active_core variable
- return *cores[active_core];
-}
-
-void CpuCoreManager::RunLoop(bool tight_loop) {
- // Update thread_to_cpu in case Core 0 is run from a different host thread
- thread_to_cpu[std::this_thread::get_id()] = cores[0].get();
-
- if (GDBStub::IsServerEnabled()) {
- GDBStub::HandlePacket();
-
- // If the loop is halted and we want to step, use a tiny (1) number of instructions to
- // execute. Otherwise, get out of the loop function.
- if (GDBStub::GetCpuHaltFlag()) {
- if (GDBStub::GetCpuStepFlag()) {
- tight_loop = false;
- } else {
- return;
- }
- }
- }
-
- auto& core_timing = system.CoreTiming();
- core_timing.ResetRun();
- bool keep_running{};
- do {
- keep_running = false;
- for (active_core = 0; active_core < NUM_CPU_CORES; ++active_core) {
- core_timing.SwitchContext(active_core);
- if (core_timing.CanCurrentContextRun()) {
- cores[active_core]->RunLoop(tight_loop);
- }
- keep_running |= core_timing.CanCurrentContextRun();
- }
- } while (keep_running);
-
- if (GDBStub::IsServerEnabled()) {
- GDBStub::SetCpuStepFlag(false);
- }
-}
-
-void CpuCoreManager::InvalidateAllInstructionCaches() {
- for (auto& cpu : cores) {
- cpu->ArmInterface().ClearInstructionCache();
- }
-}
-
-} // namespace Core
diff --git a/src/core/cpu_core_manager.h b/src/core/cpu_core_manager.h
deleted file mode 100644
index 2cbbf8216..000000000
--- a/src/core/cpu_core_manager.h
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include <array>
-#include <map>
-#include <memory>
-#include <thread>
-
-namespace Core {
-
-class Cpu;
-class CpuBarrier;
-class ExclusiveMonitor;
-class System;
-
-class CpuCoreManager {
-public:
- explicit CpuCoreManager(System& system);
- CpuCoreManager(const CpuCoreManager&) = delete;
- CpuCoreManager(CpuCoreManager&&) = delete;
-
- ~CpuCoreManager();
-
- CpuCoreManager& operator=(const CpuCoreManager&) = delete;
- CpuCoreManager& operator=(CpuCoreManager&&) = delete;
-
- void Initialize();
- void StartThreads();
- void Shutdown();
-
- Cpu& GetCore(std::size_t index);
- const Cpu& GetCore(std::size_t index) const;
-
- Cpu& GetCurrentCore();
- const Cpu& GetCurrentCore() const;
-
- ExclusiveMonitor& GetExclusiveMonitor();
- const ExclusiveMonitor& GetExclusiveMonitor() const;
-
- void RunLoop(bool tight_loop);
-
- void InvalidateAllInstructionCaches();
-
-private:
- static constexpr std::size_t NUM_CPU_CORES = 4;
-
- std::unique_ptr<ExclusiveMonitor> exclusive_monitor;
- std::unique_ptr<CpuBarrier> barrier;
- std::array<std::unique_ptr<Cpu>, NUM_CPU_CORES> cores;
- std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> core_threads;
- std::size_t active_core{}; ///< Active core, only used in single thread mode
-
- /// Map of guest threads to CPU cores
- std::map<std::thread::id, Cpu*> thread_to_cpu;
-
- System& system;
-};
-
-} // namespace Core
diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp
new file mode 100644
index 000000000..70ddbdcca
--- /dev/null
+++ b/src/core/cpu_manager.cpp
@@ -0,0 +1,81 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/arm/exclusive_monitor.h"
+#include "core/core.h"
+#include "core/core_manager.h"
+#include "core/core_timing.h"
+#include "core/cpu_manager.h"
+#include "core/gdbstub/gdbstub.h"
+
+namespace Core {
+
+CpuManager::CpuManager(System& system) : system{system} {}
+CpuManager::~CpuManager() = default;
+
+void CpuManager::Initialize() {
+ for (std::size_t index = 0; index < core_managers.size(); ++index) {
+ core_managers[index] = std::make_unique<CoreManager>(system, index);
+ }
+}
+
+void CpuManager::Shutdown() {
+ for (auto& cpu_core : core_managers) {
+ cpu_core.reset();
+ }
+}
+
+CoreManager& CpuManager::GetCoreManager(std::size_t index) {
+ return *core_managers.at(index);
+}
+
+const CoreManager& CpuManager::GetCoreManager(std::size_t index) const {
+ return *core_managers.at(index);
+}
+
+CoreManager& CpuManager::GetCurrentCoreManager() {
+ // Otherwise, use single-threaded mode active_core variable
+ return *core_managers[active_core];
+}
+
+const CoreManager& CpuManager::GetCurrentCoreManager() const {
+ // Otherwise, use single-threaded mode active_core variable
+ return *core_managers[active_core];
+}
+
+void CpuManager::RunLoop(bool tight_loop) {
+ if (GDBStub::IsServerEnabled()) {
+ GDBStub::HandlePacket();
+
+ // If the loop is halted and we want to step, use a tiny (1) number of instructions to
+ // execute. Otherwise, get out of the loop function.
+ if (GDBStub::GetCpuHaltFlag()) {
+ if (GDBStub::GetCpuStepFlag()) {
+ tight_loop = false;
+ } else {
+ return;
+ }
+ }
+ }
+
+ auto& core_timing = system.CoreTiming();
+ core_timing.ResetRun();
+ bool keep_running{};
+ do {
+ keep_running = false;
+ for (active_core = 0; active_core < NUM_CPU_CORES; ++active_core) {
+ core_timing.SwitchContext(active_core);
+ if (core_timing.CanCurrentContextRun()) {
+ core_managers[active_core]->RunLoop(tight_loop);
+ }
+ keep_running |= core_timing.CanCurrentContextRun();
+ }
+ } while (keep_running);
+
+ if (GDBStub::IsServerEnabled()) {
+ GDBStub::SetCpuStepFlag(false);
+ }
+}
+
+} // namespace Core
diff --git a/src/core/cpu_manager.h b/src/core/cpu_manager.h
new file mode 100644
index 000000000..feb619e1b
--- /dev/null
+++ b/src/core/cpu_manager.h
@@ -0,0 +1,50 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include <memory>
+
+namespace Core {
+
+class CoreManager;
+class System;
+
+class CpuManager {
+public:
+ explicit CpuManager(System& system);
+ CpuManager(const CpuManager&) = delete;
+ CpuManager(CpuManager&&) = delete;
+
+ ~CpuManager();
+
+ CpuManager& operator=(const CpuManager&) = delete;
+ CpuManager& operator=(CpuManager&&) = delete;
+
+ void Initialize();
+ void Shutdown();
+
+ CoreManager& GetCoreManager(std::size_t index);
+ const CoreManager& GetCoreManager(std::size_t index) const;
+
+ CoreManager& GetCurrentCoreManager();
+ const CoreManager& GetCurrentCoreManager() const;
+
+ std::size_t GetActiveCoreIndex() const {
+ return active_core;
+ }
+
+ void RunLoop(bool tight_loop);
+
+private:
+ static constexpr std::size_t NUM_CPU_CORES = 4;
+
+ std::array<std::unique_ptr<CoreManager>, NUM_CPU_CORES> core_managers;
+ std::size_t active_core{}; ///< Active core, only used in single thread mode
+
+ System& system;
+};
+
+} // namespace Core
diff --git a/src/core/file_sys/system_archive/system_archive.cpp b/src/core/file_sys/system_archive/system_archive.cpp
index e93d100a5..a6696024e 100644
--- a/src/core/file_sys/system_archive/system_archive.cpp
+++ b/src/core/file_sys/system_archive/system_archive.cpp
@@ -9,6 +9,7 @@
#include "core/file_sys/system_archive/shared_font.h"
#include "core/file_sys/system_archive/system_archive.h"
#include "core/file_sys/system_archive/system_version.h"
+#include "core/file_sys/system_archive/time_zone_binary.h"
namespace FileSys::SystemArchive {
@@ -38,7 +39,7 @@ constexpr std::array<SystemArchiveDescriptor, SYSTEM_ARCHIVE_COUNT> SYSTEM_ARCHI
{0x010000000000080B, "LocalNews", nullptr},
{0x010000000000080C, "Eula", nullptr},
{0x010000000000080D, "UrlBlackList", nullptr},
- {0x010000000000080E, "TimeZoneBinary", nullptr},
+ {0x010000000000080E, "TimeZoneBinary", &TimeZoneBinary},
{0x010000000000080F, "CertStoreCruiser", nullptr},
{0x0100000000000810, "FontNintendoExtension", &FontNintendoExtension},
{0x0100000000000811, "FontStandard", &FontStandard},
diff --git a/src/core/file_sys/system_archive/time_zone_binary.cpp b/src/core/file_sys/system_archive/time_zone_binary.cpp
new file mode 100644
index 000000000..9806bd197
--- /dev/null
+++ b/src/core/file_sys/system_archive/time_zone_binary.cpp
@@ -0,0 +1,657 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/swap.h"
+#include "core/file_sys/system_archive/time_zone_binary.h"
+#include "core/file_sys/vfs_vector.h"
+#include "core/hle/service/time/time_zone_types.h"
+
+namespace FileSys::SystemArchive {
+
+static constexpr std::array<u8, 9633> LOCATION_NAMES{
+ 0x43, 0x45, 0x54, 0x0d, 0x0a, 0x43, 0x53, 0x54, 0x36, 0x43, 0x44, 0x54, 0x0d, 0x0a, 0x43, 0x75,
+ 0x62, 0x61, 0x0d, 0x0a, 0x45, 0x45, 0x54, 0x0d, 0x0a, 0x45, 0x67, 0x79, 0x70, 0x74, 0x0d, 0x0a,
+ 0x45, 0x69, 0x72, 0x65, 0x0d, 0x0a, 0x45, 0x53, 0x54, 0x0d, 0x0a, 0x45, 0x53, 0x54, 0x35, 0x45,
+ 0x44, 0x54, 0x0d, 0x0a, 0x47, 0x42, 0x0d, 0x0a, 0x47, 0x42, 0x2d, 0x45, 0x69, 0x72, 0x65, 0x0d,
+ 0x0a, 0x47, 0x4d, 0x54, 0x0d, 0x0a, 0x47, 0x4d, 0x54, 0x2b, 0x30, 0x0d, 0x0a, 0x47, 0x4d, 0x54,
+ 0x2d, 0x30, 0x0d, 0x0a, 0x47, 0x4d, 0x54, 0x30, 0x0d, 0x0a, 0x47, 0x72, 0x65, 0x65, 0x6e, 0x77,
+ 0x69, 0x63, 0x68, 0x0d, 0x0a, 0x48, 0x6f, 0x6e, 0x67, 0x6b, 0x6f, 0x6e, 0x67, 0x0d, 0x0a, 0x48,
+ 0x53, 0x54, 0x0d, 0x0a, 0x49, 0x63, 0x65, 0x6c, 0x61, 0x6e, 0x64, 0x0d, 0x0a, 0x49, 0x72, 0x61,
+ 0x6e, 0x0d, 0x0a, 0x49, 0x73, 0x72, 0x61, 0x65, 0x6c, 0x0d, 0x0a, 0x4a, 0x61, 0x6d, 0x61, 0x69,
+ 0x63, 0x61, 0x0d, 0x0a, 0x4a, 0x61, 0x70, 0x61, 0x6e, 0x0d, 0x0a, 0x4b, 0x77, 0x61, 0x6a, 0x61,
+ 0x6c, 0x65, 0x69, 0x6e, 0x0d, 0x0a, 0x4c, 0x69, 0x62, 0x79, 0x61, 0x0d, 0x0a, 0x4d, 0x45, 0x54,
+ 0x0d, 0x0a, 0x4d, 0x53, 0x54, 0x0d, 0x0a, 0x4d, 0x53, 0x54, 0x37, 0x4d, 0x44, 0x54, 0x0d, 0x0a,
+ 0x4e, 0x61, 0x76, 0x61, 0x6a, 0x6f, 0x0d, 0x0a, 0x4e, 0x5a, 0x0d, 0x0a, 0x4e, 0x5a, 0x2d, 0x43,
+ 0x48, 0x41, 0x54, 0x0d, 0x0a, 0x50, 0x6f, 0x6c, 0x61, 0x6e, 0x64, 0x0d, 0x0a, 0x50, 0x6f, 0x72,
+ 0x74, 0x75, 0x67, 0x61, 0x6c, 0x0d, 0x0a, 0x50, 0x52, 0x43, 0x0d, 0x0a, 0x50, 0x53, 0x54, 0x38,
+ 0x50, 0x44, 0x54, 0x0d, 0x0a, 0x52, 0x4f, 0x43, 0x0d, 0x0a, 0x52, 0x4f, 0x4b, 0x0d, 0x0a, 0x53,
+ 0x69, 0x6e, 0x67, 0x61, 0x70, 0x6f, 0x72, 0x65, 0x0d, 0x0a, 0x54, 0x75, 0x72, 0x6b, 0x65, 0x79,
+ 0x0d, 0x0a, 0x55, 0x43, 0x54, 0x0d, 0x0a, 0x55, 0x6e, 0x69, 0x76, 0x65, 0x72, 0x73, 0x61, 0x6c,
+ 0x0d, 0x0a, 0x55, 0x54, 0x43, 0x0d, 0x0a, 0x57, 0x2d, 0x53, 0x55, 0x0d, 0x0a, 0x57, 0x45, 0x54,
+ 0x0d, 0x0a, 0x5a, 0x75, 0x6c, 0x75, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41,
+ 0x62, 0x69, 0x64, 0x6a, 0x61, 0x6e, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41,
+ 0x63, 0x63, 0x72, 0x61, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x64, 0x64,
+ 0x69, 0x73, 0x5f, 0x41, 0x62, 0x61, 0x62, 0x61, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61,
+ 0x2f, 0x41, 0x6c, 0x67, 0x69, 0x65, 0x72, 0x73, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61,
+ 0x2f, 0x41, 0x73, 0x6d, 0x61, 0x72, 0x61, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f,
+ 0x41, 0x73, 0x6d, 0x65, 0x72, 0x61, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x42,
+ 0x61, 0x6d, 0x61, 0x6b, 0x6f, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x42, 0x61,
+ 0x6e, 0x67, 0x75, 0x69, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x42, 0x61, 0x6e,
+ 0x6a, 0x75, 0x6c, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x42, 0x69, 0x73, 0x73,
+ 0x61, 0x75, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x42, 0x6c, 0x61, 0x6e, 0x74,
+ 0x79, 0x72, 0x65, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x42, 0x72, 0x61, 0x7a,
+ 0x7a, 0x61, 0x76, 0x69, 0x6c, 0x6c, 0x65, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f,
+ 0x42, 0x75, 0x6a, 0x75, 0x6d, 0x62, 0x75, 0x72, 0x61, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63,
+ 0x61, 0x2f, 0x43, 0x61, 0x69, 0x72, 0x6f, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f,
+ 0x43, 0x61, 0x73, 0x61, 0x62, 0x6c, 0x61, 0x6e, 0x63, 0x61, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69,
+ 0x63, 0x61, 0x2f, 0x43, 0x65, 0x75, 0x74, 0x61, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61,
+ 0x2f, 0x43, 0x6f, 0x6e, 0x61, 0x6b, 0x72, 0x79, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61,
+ 0x2f, 0x44, 0x61, 0x6b, 0x61, 0x72, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x44,
+ 0x61, 0x72, 0x5f, 0x65, 0x73, 0x5f, 0x53, 0x61, 0x6c, 0x61, 0x61, 0x6d, 0x0d, 0x0a, 0x41, 0x66,
+ 0x72, 0x69, 0x63, 0x61, 0x2f, 0x44, 0x6a, 0x69, 0x62, 0x6f, 0x75, 0x74, 0x69, 0x0d, 0x0a, 0x41,
+ 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x44, 0x6f, 0x75, 0x61, 0x6c, 0x61, 0x0d, 0x0a, 0x41, 0x66,
+ 0x72, 0x69, 0x63, 0x61, 0x2f, 0x45, 0x6c, 0x5f, 0x41, 0x61, 0x69, 0x75, 0x6e, 0x0d, 0x0a, 0x41,
+ 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x46, 0x72, 0x65, 0x65, 0x74, 0x6f, 0x77, 0x6e, 0x0d, 0x0a,
+ 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x47, 0x61, 0x62, 0x6f, 0x72, 0x6f, 0x6e, 0x65, 0x0d,
+ 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x48, 0x61, 0x72, 0x61, 0x72, 0x65, 0x0d, 0x0a,
+ 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4a, 0x6f, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x73, 0x62,
+ 0x75, 0x72, 0x67, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4a, 0x75, 0x62, 0x61,
+ 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4b, 0x61, 0x6d, 0x70, 0x61, 0x6c, 0x61,
+ 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4b, 0x68, 0x61, 0x72, 0x74, 0x6f, 0x75,
+ 0x6d, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4b, 0x69, 0x67, 0x61, 0x6c, 0x69,
+ 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4b, 0x69, 0x6e, 0x73, 0x68, 0x61, 0x73,
+ 0x61, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4c, 0x61, 0x67, 0x6f, 0x73, 0x0d,
+ 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4c, 0x69, 0x62, 0x72, 0x65, 0x76, 0x69, 0x6c,
+ 0x6c, 0x65, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4c, 0x6f, 0x6d, 0x65, 0x0d,
+ 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4c, 0x75, 0x61, 0x6e, 0x64, 0x61, 0x0d, 0x0a,
+ 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4c, 0x75, 0x62, 0x75, 0x6d, 0x62, 0x61, 0x73, 0x68,
+ 0x69, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4c, 0x75, 0x73, 0x61, 0x6b, 0x61,
+ 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x61, 0x6c, 0x61, 0x62, 0x6f, 0x0d,
+ 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x61, 0x70, 0x75, 0x74, 0x6f, 0x0d, 0x0a,
+ 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x61, 0x73, 0x65, 0x72, 0x75, 0x0d, 0x0a, 0x41,
+ 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x62, 0x61, 0x62, 0x61, 0x6e, 0x65, 0x0d, 0x0a, 0x41,
+ 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x6f, 0x67, 0x61, 0x64, 0x69, 0x73, 0x68, 0x75, 0x0d,
+ 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x6f, 0x6e, 0x72, 0x6f, 0x76, 0x69, 0x61,
+ 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4e, 0x61, 0x69, 0x72, 0x6f, 0x62, 0x69,
+ 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4e, 0x64, 0x6a, 0x61, 0x6d, 0x65, 0x6e,
+ 0x61, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4e, 0x69, 0x61, 0x6d, 0x65, 0x79,
+ 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4e, 0x6f, 0x75, 0x61, 0x6b, 0x63, 0x68,
+ 0x6f, 0x74, 0x74, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4f, 0x75, 0x61, 0x67,
+ 0x61, 0x64, 0x6f, 0x75, 0x67, 0x6f, 0x75, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f,
+ 0x50, 0x6f, 0x72, 0x74, 0x6f, 0x2d, 0x4e, 0x6f, 0x76, 0x6f, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69,
+ 0x63, 0x61, 0x2f, 0x53, 0x61, 0x6f, 0x5f, 0x54, 0x6f, 0x6d, 0x65, 0x0d, 0x0a, 0x41, 0x66, 0x72,
+ 0x69, 0x63, 0x61, 0x2f, 0x54, 0x69, 0x6d, 0x62, 0x75, 0x6b, 0x74, 0x75, 0x0d, 0x0a, 0x41, 0x66,
+ 0x72, 0x69, 0x63, 0x61, 0x2f, 0x54, 0x72, 0x69, 0x70, 0x6f, 0x6c, 0x69, 0x0d, 0x0a, 0x41, 0x66,
+ 0x72, 0x69, 0x63, 0x61, 0x2f, 0x54, 0x75, 0x6e, 0x69, 0x73, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69,
+ 0x63, 0x61, 0x2f, 0x57, 0x69, 0x6e, 0x64, 0x68, 0x6f, 0x65, 0x6b, 0x0d, 0x0a, 0x41, 0x6d, 0x65,
+ 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x64, 0x61, 0x6b, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69,
+ 0x63, 0x61, 0x2f, 0x41, 0x6e, 0x63, 0x68, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x0d, 0x0a, 0x41, 0x6d,
+ 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x6e, 0x67, 0x75, 0x69, 0x6c, 0x6c, 0x61, 0x0d, 0x0a,
+ 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x6e, 0x74, 0x69, 0x67, 0x75, 0x61, 0x0d,
+ 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x72, 0x61, 0x67, 0x75, 0x61, 0x69,
+ 0x6e, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x72, 0x75, 0x62,
+ 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x73, 0x75, 0x6e, 0x63,
+ 0x69, 0x6f, 0x6e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x74, 0x69,
+ 0x6b, 0x6f, 0x6b, 0x61, 0x6e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41,
+ 0x74, 0x6b, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x42, 0x61, 0x68,
+ 0x69, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x42, 0x61, 0x68, 0x69,
+ 0x61, 0x5f, 0x42, 0x61, 0x6e, 0x64, 0x65, 0x72, 0x61, 0x73, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72,
+ 0x69, 0x63, 0x61, 0x2f, 0x42, 0x61, 0x72, 0x62, 0x61, 0x64, 0x6f, 0x73, 0x0d, 0x0a, 0x41, 0x6d,
+ 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x42, 0x65, 0x6c, 0x65, 0x6d, 0x0d, 0x0a, 0x41, 0x6d, 0x65,
+ 0x72, 0x69, 0x63, 0x61, 0x2f, 0x42, 0x65, 0x6c, 0x69, 0x7a, 0x65, 0x0d, 0x0a, 0x41, 0x6d, 0x65,
+ 0x72, 0x69, 0x63, 0x61, 0x2f, 0x42, 0x6c, 0x61, 0x6e, 0x63, 0x2d, 0x53, 0x61, 0x62, 0x6c, 0x6f,
+ 0x6e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x42, 0x6f, 0x61, 0x5f, 0x56,
+ 0x69, 0x73, 0x74, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x42, 0x6f,
+ 0x67, 0x6f, 0x74, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x42, 0x6f,
+ 0x69, 0x73, 0x65, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x42, 0x75, 0x65,
+ 0x6e, 0x6f, 0x73, 0x5f, 0x41, 0x69, 0x72, 0x65, 0x73, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69,
+ 0x63, 0x61, 0x2f, 0x43, 0x61, 0x6d, 0x62, 0x72, 0x69, 0x64, 0x67, 0x65, 0x5f, 0x42, 0x61, 0x79,
+ 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x43, 0x61, 0x6d, 0x70, 0x6f, 0x5f,
+ 0x47, 0x72, 0x61, 0x6e, 0x64, 0x65, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f,
+ 0x43, 0x61, 0x6e, 0x63, 0x75, 0x6e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f,
+ 0x43, 0x61, 0x72, 0x61, 0x63, 0x61, 0x73, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61,
+ 0x2f, 0x43, 0x61, 0x74, 0x61, 0x6d, 0x61, 0x72, 0x63, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72,
+ 0x69, 0x63, 0x61, 0x2f, 0x43, 0x61, 0x79, 0x65, 0x6e, 0x6e, 0x65, 0x0d, 0x0a, 0x41, 0x6d, 0x65,
+ 0x72, 0x69, 0x63, 0x61, 0x2f, 0x43, 0x61, 0x79, 0x6d, 0x61, 0x6e, 0x0d, 0x0a, 0x41, 0x6d, 0x65,
+ 0x72, 0x69, 0x63, 0x61, 0x2f, 0x43, 0x68, 0x69, 0x63, 0x61, 0x67, 0x6f, 0x0d, 0x0a, 0x41, 0x6d,
+ 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x43, 0x68, 0x69, 0x68, 0x75, 0x61, 0x68, 0x75, 0x61, 0x0d,
+ 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x43, 0x6f, 0x72, 0x61, 0x6c, 0x5f, 0x48,
+ 0x61, 0x72, 0x62, 0x6f, 0x75, 0x72, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f,
+ 0x43, 0x6f, 0x72, 0x64, 0x6f, 0x62, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61,
+ 0x2f, 0x43, 0x6f, 0x73, 0x74, 0x61, 0x5f, 0x52, 0x69, 0x63, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65,
+ 0x72, 0x69, 0x63, 0x61, 0x2f, 0x43, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x6e, 0x0d, 0x0a, 0x41, 0x6d,
+ 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x43, 0x75, 0x69, 0x61, 0x62, 0x61, 0x0d, 0x0a, 0x41, 0x6d,
+ 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x43, 0x75, 0x72, 0x61, 0x63, 0x61, 0x6f, 0x0d, 0x0a, 0x41,
+ 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x44, 0x61, 0x6e, 0x6d, 0x61, 0x72, 0x6b, 0x73, 0x68,
+ 0x61, 0x76, 0x6e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x44, 0x61, 0x77,
+ 0x73, 0x6f, 0x6e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x44, 0x61, 0x77,
+ 0x73, 0x6f, 0x6e, 0x5f, 0x43, 0x72, 0x65, 0x65, 0x6b, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69,
+ 0x63, 0x61, 0x2f, 0x44, 0x65, 0x6e, 0x76, 0x65, 0x72, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69,
+ 0x63, 0x61, 0x2f, 0x44, 0x65, 0x74, 0x72, 0x6f, 0x69, 0x74, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72,
+ 0x69, 0x63, 0x61, 0x2f, 0x44, 0x6f, 0x6d, 0x69, 0x6e, 0x69, 0x63, 0x61, 0x0d, 0x0a, 0x41, 0x6d,
+ 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x45, 0x64, 0x6d, 0x6f, 0x6e, 0x74, 0x6f, 0x6e, 0x0d, 0x0a,
+ 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x45, 0x69, 0x72, 0x75, 0x6e, 0x65, 0x70, 0x65,
+ 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x45, 0x6c, 0x5f, 0x53, 0x61, 0x6c,
+ 0x76, 0x61, 0x64, 0x6f, 0x72, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x45,
+ 0x6e, 0x73, 0x65, 0x6e, 0x61, 0x64, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61,
+ 0x2f, 0x46, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x65, 0x7a, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72,
+ 0x69, 0x63, 0x61, 0x2f, 0x46, 0x6f, 0x72, 0x74, 0x5f, 0x4e, 0x65, 0x6c, 0x73, 0x6f, 0x6e, 0x0d,
+ 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x46, 0x6f, 0x72, 0x74, 0x5f, 0x57, 0x61,
+ 0x79, 0x6e, 0x65, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x47, 0x6c, 0x61,
+ 0x63, 0x65, 0x5f, 0x42, 0x61, 0x79, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f,
+ 0x47, 0x6f, 0x64, 0x74, 0x68, 0x61, 0x62, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61,
+ 0x2f, 0x47, 0x6f, 0x6f, 0x73, 0x65, 0x5f, 0x42, 0x61, 0x79, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72,
+ 0x69, 0x63, 0x61, 0x2f, 0x47, 0x72, 0x61, 0x6e, 0x64, 0x5f, 0x54, 0x75, 0x72, 0x6b, 0x0d, 0x0a,
+ 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x47, 0x72, 0x65, 0x6e, 0x61, 0x64, 0x61, 0x0d,
+ 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x47, 0x75, 0x61, 0x64, 0x65, 0x6c, 0x6f,
+ 0x75, 0x70, 0x65, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x47, 0x75, 0x61,
+ 0x74, 0x65, 0x6d, 0x61, 0x6c, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f,
+ 0x47, 0x75, 0x61, 0x79, 0x61, 0x71, 0x75, 0x69, 0x6c, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69,
+ 0x63, 0x61, 0x2f, 0x47, 0x75, 0x79, 0x61, 0x6e, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69,
+ 0x63, 0x61, 0x2f, 0x48, 0x61, 0x6c, 0x69, 0x66, 0x61, 0x78, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72,
+ 0x69, 0x63, 0x61, 0x2f, 0x48, 0x61, 0x76, 0x61, 0x6e, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72,
+ 0x69, 0x63, 0x61, 0x2f, 0x48, 0x65, 0x72, 0x6d, 0x6f, 0x73, 0x69, 0x6c, 0x6c, 0x6f, 0x0d, 0x0a,
+ 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x49, 0x6e, 0x64, 0x69, 0x61, 0x6e, 0x61, 0x70,
+ 0x6f, 0x6c, 0x69, 0x73, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x49, 0x6e,
+ 0x75, 0x76, 0x69, 0x6b, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x49, 0x71,
+ 0x61, 0x6c, 0x75, 0x69, 0x74, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4a,
+ 0x61, 0x6d, 0x61, 0x69, 0x63, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f,
+ 0x4a, 0x75, 0x6a, 0x75, 0x79, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4a,
+ 0x75, 0x6e, 0x65, 0x61, 0x75, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4b,
+ 0x6e, 0x6f, 0x78, 0x5f, 0x49, 0x4e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f,
+ 0x4b, 0x72, 0x61, 0x6c, 0x65, 0x6e, 0x64, 0x69, 0x6a, 0x6b, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72,
+ 0x69, 0x63, 0x61, 0x2f, 0x4c, 0x61, 0x5f, 0x50, 0x61, 0x7a, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72,
+ 0x69, 0x63, 0x61, 0x2f, 0x4c, 0x69, 0x6d, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63,
+ 0x61, 0x2f, 0x4c, 0x6f, 0x73, 0x5f, 0x41, 0x6e, 0x67, 0x65, 0x6c, 0x65, 0x73, 0x0d, 0x0a, 0x41,
+ 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4c, 0x6f, 0x75, 0x69, 0x73, 0x76, 0x69, 0x6c, 0x6c,
+ 0x65, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4c, 0x6f, 0x77, 0x65, 0x72,
+ 0x5f, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x65, 0x73, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63,
+ 0x61, 0x2f, 0x4d, 0x61, 0x63, 0x65, 0x69, 0x6f, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63,
+ 0x61, 0x2f, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x75, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69,
+ 0x63, 0x61, 0x2f, 0x4d, 0x61, 0x6e, 0x61, 0x75, 0x73, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69,
+ 0x63, 0x61, 0x2f, 0x4d, 0x61, 0x72, 0x69, 0x67, 0x6f, 0x74, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72,
+ 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x61, 0x72, 0x74, 0x69, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x0d, 0x0a,
+ 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x61, 0x74, 0x61, 0x6d, 0x6f, 0x72, 0x6f,
+ 0x73, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x61, 0x7a, 0x61, 0x74,
+ 0x6c, 0x61, 0x6e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x65, 0x6e,
+ 0x64, 0x6f, 0x7a, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x65,
+ 0x6e, 0x6f, 0x6d, 0x69, 0x6e, 0x65, 0x65, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61,
+ 0x2f, 0x4d, 0x65, 0x72, 0x69, 0x64, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61,
+ 0x2f, 0x4d, 0x65, 0x74, 0x6c, 0x61, 0x6b, 0x61, 0x74, 0x6c, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65,
+ 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x65, 0x78, 0x69, 0x63, 0x6f, 0x5f, 0x43, 0x69, 0x74, 0x79,
+ 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x69, 0x71, 0x75, 0x65, 0x6c,
+ 0x6f, 0x6e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x6f, 0x6e, 0x63,
+ 0x74, 0x6f, 0x6e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x6f, 0x6e,
+ 0x74, 0x65, 0x72, 0x72, 0x65, 0x79, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f,
+ 0x4d, 0x6f, 0x6e, 0x74, 0x65, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72,
+ 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x6f, 0x6e, 0x74, 0x72, 0x65, 0x61, 0x6c, 0x0d, 0x0a, 0x41, 0x6d,
+ 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x6f, 0x6e, 0x74, 0x73, 0x65, 0x72, 0x72, 0x61, 0x74,
+ 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4e, 0x61, 0x73, 0x73, 0x61, 0x75,
+ 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4e, 0x65, 0x77, 0x5f, 0x59, 0x6f,
+ 0x72, 0x6b, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4e, 0x69, 0x70, 0x69,
+ 0x67, 0x6f, 0x6e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4e, 0x6f, 0x6d,
+ 0x65, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4e, 0x6f, 0x72, 0x6f, 0x6e,
+ 0x68, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4f, 0x6a, 0x69, 0x6e,
+ 0x61, 0x67, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x50, 0x61, 0x6e,
+ 0x61, 0x6d, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x50, 0x61, 0x6e,
+ 0x67, 0x6e, 0x69, 0x72, 0x74, 0x75, 0x6e, 0x67, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63,
+ 0x61, 0x2f, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x61, 0x72, 0x69, 0x62, 0x6f, 0x0d, 0x0a, 0x41, 0x6d,
+ 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x50, 0x68, 0x6f, 0x65, 0x6e, 0x69, 0x78, 0x0d, 0x0a, 0x41,
+ 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x50, 0x6f, 0x72, 0x74, 0x2d, 0x61, 0x75, 0x2d, 0x50,
+ 0x72, 0x69, 0x6e, 0x63, 0x65, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x50,
+ 0x6f, 0x72, 0x74, 0x6f, 0x5f, 0x41, 0x63, 0x72, 0x65, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69,
+ 0x63, 0x61, 0x2f, 0x50, 0x6f, 0x72, 0x74, 0x6f, 0x5f, 0x56, 0x65, 0x6c, 0x68, 0x6f, 0x0d, 0x0a,
+ 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x50, 0x6f, 0x72, 0x74, 0x5f, 0x6f, 0x66, 0x5f,
+ 0x53, 0x70, 0x61, 0x69, 0x6e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x50,
+ 0x75, 0x65, 0x72, 0x74, 0x6f, 0x5f, 0x52, 0x69, 0x63, 0x6f, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72,
+ 0x69, 0x63, 0x61, 0x2f, 0x50, 0x75, 0x6e, 0x74, 0x61, 0x5f, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x73,
+ 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x52, 0x61, 0x69, 0x6e, 0x79, 0x5f,
+ 0x52, 0x69, 0x76, 0x65, 0x72, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x52,
+ 0x61, 0x6e, 0x6b, 0x69, 0x6e, 0x5f, 0x49, 0x6e, 0x6c, 0x65, 0x74, 0x0d, 0x0a, 0x41, 0x6d, 0x65,
+ 0x72, 0x69, 0x63, 0x61, 0x2f, 0x52, 0x65, 0x63, 0x69, 0x66, 0x65, 0x0d, 0x0a, 0x41, 0x6d, 0x65,
+ 0x72, 0x69, 0x63, 0x61, 0x2f, 0x52, 0x65, 0x67, 0x69, 0x6e, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65,
+ 0x72, 0x69, 0x63, 0x61, 0x2f, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x65, 0x0d, 0x0a, 0x41,
+ 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x52, 0x69, 0x6f, 0x5f, 0x42, 0x72, 0x61, 0x6e, 0x63,
+ 0x6f, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x52, 0x6f, 0x73, 0x61, 0x72,
+ 0x69, 0x6f, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x53, 0x61, 0x6e, 0x74,
+ 0x61, 0x72, 0x65, 0x6d, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x53, 0x61,
+ 0x6e, 0x74, 0x61, 0x5f, 0x49, 0x73, 0x61, 0x62, 0x65, 0x6c, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72,
+ 0x69, 0x63, 0x61, 0x2f, 0x53, 0x61, 0x6e, 0x74, 0x69, 0x61, 0x67, 0x6f, 0x0d, 0x0a, 0x41, 0x6d,
+ 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x53, 0x61, 0x6e, 0x74, 0x6f, 0x5f, 0x44, 0x6f, 0x6d, 0x69,
+ 0x6e, 0x67, 0x6f, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x53, 0x61, 0x6f,
+ 0x5f, 0x50, 0x61, 0x75, 0x6c, 0x6f, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f,
+ 0x53, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x62, 0x79, 0x73, 0x75, 0x6e, 0x64, 0x0d, 0x0a, 0x41, 0x6d,
+ 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x53, 0x68, 0x69, 0x70, 0x72, 0x6f, 0x63, 0x6b, 0x0d, 0x0a,
+ 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x53, 0x69, 0x74, 0x6b, 0x61, 0x0d, 0x0a, 0x41,
+ 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x53, 0x74, 0x5f, 0x42, 0x61, 0x72, 0x74, 0x68, 0x65,
+ 0x6c, 0x65, 0x6d, 0x79, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x53, 0x74,
+ 0x5f, 0x4a, 0x6f, 0x68, 0x6e, 0x73, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f,
+ 0x53, 0x74, 0x5f, 0x4b, 0x69, 0x74, 0x74, 0x73, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63,
+ 0x61, 0x2f, 0x53, 0x74, 0x5f, 0x4c, 0x75, 0x63, 0x69, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72,
+ 0x69, 0x63, 0x61, 0x2f, 0x53, 0x74, 0x5f, 0x54, 0x68, 0x6f, 0x6d, 0x61, 0x73, 0x0d, 0x0a, 0x41,
+ 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x53, 0x74, 0x5f, 0x56, 0x69, 0x6e, 0x63, 0x65, 0x6e,
+ 0x74, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x53, 0x77, 0x69, 0x66, 0x74,
+ 0x5f, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63,
+ 0x61, 0x2f, 0x54, 0x65, 0x67, 0x75, 0x63, 0x69, 0x67, 0x61, 0x6c, 0x70, 0x61, 0x0d, 0x0a, 0x41,
+ 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x54, 0x68, 0x75, 0x6c, 0x65, 0x0d, 0x0a, 0x41, 0x6d,
+ 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x54, 0x68, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x5f, 0x42, 0x61,
+ 0x79, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x54, 0x69, 0x6a, 0x75, 0x61,
+ 0x6e, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x54, 0x6f, 0x72, 0x6f,
+ 0x6e, 0x74, 0x6f, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x54, 0x6f, 0x72,
+ 0x74, 0x6f, 0x6c, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x56, 0x61,
+ 0x6e, 0x63, 0x6f, 0x75, 0x76, 0x65, 0x72, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61,
+ 0x2f, 0x56, 0x69, 0x72, 0x67, 0x69, 0x6e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61,
+ 0x2f, 0x57, 0x68, 0x69, 0x74, 0x65, 0x68, 0x6f, 0x72, 0x73, 0x65, 0x0d, 0x0a, 0x41, 0x6d, 0x65,
+ 0x72, 0x69, 0x63, 0x61, 0x2f, 0x57, 0x69, 0x6e, 0x6e, 0x69, 0x70, 0x65, 0x67, 0x0d, 0x0a, 0x41,
+ 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x59, 0x61, 0x6b, 0x75, 0x74, 0x61, 0x74, 0x0d, 0x0a,
+ 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x59, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6b, 0x6e,
+ 0x69, 0x66, 0x65, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x72, 0x67,
+ 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x61, 0x2f, 0x42, 0x75, 0x65, 0x6e, 0x6f, 0x73, 0x5f, 0x41, 0x69,
+ 0x72, 0x65, 0x73, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x72, 0x67,
+ 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x61, 0x2f, 0x43, 0x61, 0x74, 0x61, 0x6d, 0x61, 0x72, 0x63, 0x61,
+ 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x72, 0x67, 0x65, 0x6e, 0x74,
+ 0x69, 0x6e, 0x61, 0x2f, 0x43, 0x6f, 0x6d, 0x6f, 0x64, 0x52, 0x69, 0x76, 0x61, 0x64, 0x61, 0x76,
+ 0x69, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x72, 0x67, 0x65,
+ 0x6e, 0x74, 0x69, 0x6e, 0x61, 0x2f, 0x43, 0x6f, 0x72, 0x64, 0x6f, 0x62, 0x61, 0x0d, 0x0a, 0x41,
+ 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x72, 0x67, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x61,
+ 0x2f, 0x4a, 0x75, 0x6a, 0x75, 0x79, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f,
+ 0x41, 0x72, 0x67, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x61, 0x2f, 0x4c, 0x61, 0x5f, 0x52, 0x69, 0x6f,
+ 0x6a, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x72, 0x67, 0x65,
+ 0x6e, 0x74, 0x69, 0x6e, 0x61, 0x2f, 0x4d, 0x65, 0x6e, 0x64, 0x6f, 0x7a, 0x61, 0x0d, 0x0a, 0x41,
+ 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x72, 0x67, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x61,
+ 0x2f, 0x52, 0x69, 0x6f, 0x5f, 0x47, 0x61, 0x6c, 0x6c, 0x65, 0x67, 0x6f, 0x73, 0x0d, 0x0a, 0x41,
+ 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x72, 0x67, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x61,
+ 0x2f, 0x53, 0x61, 0x6c, 0x74, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f,
+ 0x41, 0x72, 0x67, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x61, 0x2f, 0x53, 0x61, 0x6e, 0x5f, 0x4a, 0x75,
+ 0x61, 0x6e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x72, 0x67, 0x65,
+ 0x6e, 0x74, 0x69, 0x6e, 0x61, 0x2f, 0x53, 0x61, 0x6e, 0x5f, 0x4c, 0x75, 0x69, 0x73, 0x0d, 0x0a,
+ 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x72, 0x67, 0x65, 0x6e, 0x74, 0x69, 0x6e,
+ 0x61, 0x2f, 0x54, 0x75, 0x63, 0x75, 0x6d, 0x61, 0x6e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69,
+ 0x63, 0x61, 0x2f, 0x41, 0x72, 0x67, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x61, 0x2f, 0x55, 0x73, 0x68,
+ 0x75, 0x61, 0x69, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x49, 0x6e,
+ 0x64, 0x69, 0x61, 0x6e, 0x61, 0x2f, 0x49, 0x6e, 0x64, 0x69, 0x61, 0x6e, 0x61, 0x70, 0x6f, 0x6c,
+ 0x69, 0x73, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x49, 0x6e, 0x64, 0x69,
+ 0x61, 0x6e, 0x61, 0x2f, 0x4b, 0x6e, 0x6f, 0x78, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63,
+ 0x61, 0x2f, 0x49, 0x6e, 0x64, 0x69, 0x61, 0x6e, 0x61, 0x2f, 0x4d, 0x61, 0x72, 0x65, 0x6e, 0x67,
+ 0x6f, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x49, 0x6e, 0x64, 0x69, 0x61,
+ 0x6e, 0x61, 0x2f, 0x50, 0x65, 0x74, 0x65, 0x72, 0x73, 0x62, 0x75, 0x72, 0x67, 0x0d, 0x0a, 0x41,
+ 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x49, 0x6e, 0x64, 0x69, 0x61, 0x6e, 0x61, 0x2f, 0x54,
+ 0x65, 0x6c, 0x6c, 0x5f, 0x43, 0x69, 0x74, 0x79, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63,
+ 0x61, 0x2f, 0x49, 0x6e, 0x64, 0x69, 0x61, 0x6e, 0x61, 0x2f, 0x56, 0x65, 0x76, 0x61, 0x79, 0x0d,
+ 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x49, 0x6e, 0x64, 0x69, 0x61, 0x6e, 0x61,
+ 0x2f, 0x56, 0x69, 0x6e, 0x63, 0x65, 0x6e, 0x6e, 0x65, 0x73, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72,
+ 0x69, 0x63, 0x61, 0x2f, 0x49, 0x6e, 0x64, 0x69, 0x61, 0x6e, 0x61, 0x2f, 0x57, 0x69, 0x6e, 0x61,
+ 0x6d, 0x61, 0x63, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4b, 0x65, 0x6e,
+ 0x74, 0x75, 0x63, 0x6b, 0x79, 0x2f, 0x4c, 0x6f, 0x75, 0x69, 0x73, 0x76, 0x69, 0x6c, 0x6c, 0x65,
+ 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4b, 0x65, 0x6e, 0x74, 0x75, 0x63,
+ 0x6b, 0x79, 0x2f, 0x4d, 0x6f, 0x6e, 0x74, 0x69, 0x63, 0x65, 0x6c, 0x6c, 0x6f, 0x0d, 0x0a, 0x41,
+ 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4e, 0x6f, 0x72, 0x74, 0x68, 0x5f, 0x44, 0x61, 0x6b,
+ 0x6f, 0x74, 0x61, 0x2f, 0x42, 0x65, 0x75, 0x6c, 0x61, 0x68, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72,
+ 0x69, 0x63, 0x61, 0x2f, 0x4e, 0x6f, 0x72, 0x74, 0x68, 0x5f, 0x44, 0x61, 0x6b, 0x6f, 0x74, 0x61,
+ 0x2f, 0x43, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61,
+ 0x2f, 0x4e, 0x6f, 0x72, 0x74, 0x68, 0x5f, 0x44, 0x61, 0x6b, 0x6f, 0x74, 0x61, 0x2f, 0x4e, 0x65,
+ 0x77, 0x5f, 0x53, 0x61, 0x6c, 0x65, 0x6d, 0x0d, 0x0a, 0x41, 0x6e, 0x74, 0x61, 0x72, 0x63, 0x74,
+ 0x69, 0x63, 0x61, 0x2f, 0x43, 0x61, 0x73, 0x65, 0x79, 0x0d, 0x0a, 0x41, 0x6e, 0x74, 0x61, 0x72,
+ 0x63, 0x74, 0x69, 0x63, 0x61, 0x2f, 0x44, 0x61, 0x76, 0x69, 0x73, 0x0d, 0x0a, 0x41, 0x6e, 0x74,
+ 0x61, 0x72, 0x63, 0x74, 0x69, 0x63, 0x61, 0x2f, 0x44, 0x75, 0x6d, 0x6f, 0x6e, 0x74, 0x44, 0x55,
+ 0x72, 0x76, 0x69, 0x6c, 0x6c, 0x65, 0x0d, 0x0a, 0x41, 0x6e, 0x74, 0x61, 0x72, 0x63, 0x74, 0x69,
+ 0x63, 0x61, 0x2f, 0x4d, 0x61, 0x63, 0x71, 0x75, 0x61, 0x72, 0x69, 0x65, 0x0d, 0x0a, 0x41, 0x6e,
+ 0x74, 0x61, 0x72, 0x63, 0x74, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x61, 0x77, 0x73, 0x6f, 0x6e, 0x0d,
+ 0x0a, 0x41, 0x6e, 0x74, 0x61, 0x72, 0x63, 0x74, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x63, 0x4d, 0x75,
+ 0x72, 0x64, 0x6f, 0x0d, 0x0a, 0x41, 0x6e, 0x74, 0x61, 0x72, 0x63, 0x74, 0x69, 0x63, 0x61, 0x2f,
+ 0x50, 0x61, 0x6c, 0x6d, 0x65, 0x72, 0x0d, 0x0a, 0x41, 0x6e, 0x74, 0x61, 0x72, 0x63, 0x74, 0x69,
+ 0x63, 0x61, 0x2f, 0x52, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x61, 0x0d, 0x0a, 0x41, 0x6e, 0x74, 0x61,
+ 0x72, 0x63, 0x74, 0x69, 0x63, 0x61, 0x2f, 0x53, 0x6f, 0x75, 0x74, 0x68, 0x5f, 0x50, 0x6f, 0x6c,
+ 0x65, 0x0d, 0x0a, 0x41, 0x6e, 0x74, 0x61, 0x72, 0x63, 0x74, 0x69, 0x63, 0x61, 0x2f, 0x53, 0x79,
+ 0x6f, 0x77, 0x61, 0x0d, 0x0a, 0x41, 0x6e, 0x74, 0x61, 0x72, 0x63, 0x74, 0x69, 0x63, 0x61, 0x2f,
+ 0x54, 0x72, 0x6f, 0x6c, 0x6c, 0x0d, 0x0a, 0x41, 0x6e, 0x74, 0x61, 0x72, 0x63, 0x74, 0x69, 0x63,
+ 0x61, 0x2f, 0x56, 0x6f, 0x73, 0x74, 0x6f, 0x6b, 0x0d, 0x0a, 0x41, 0x72, 0x63, 0x74, 0x69, 0x63,
+ 0x2f, 0x4c, 0x6f, 0x6e, 0x67, 0x79, 0x65, 0x61, 0x72, 0x62, 0x79, 0x65, 0x6e, 0x0d, 0x0a, 0x41,
+ 0x73, 0x69, 0x61, 0x2f, 0x41, 0x64, 0x65, 0x6e, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x41,
+ 0x6c, 0x6d, 0x61, 0x74, 0x79, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x41, 0x6d, 0x6d, 0x61,
+ 0x6e, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x41, 0x6e, 0x61, 0x64, 0x79, 0x72, 0x0d, 0x0a,
+ 0x41, 0x73, 0x69, 0x61, 0x2f, 0x41, 0x71, 0x74, 0x61, 0x75, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61,
+ 0x2f, 0x41, 0x71, 0x74, 0x6f, 0x62, 0x65, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x41, 0x73,
+ 0x68, 0x67, 0x61, 0x62, 0x61, 0x74, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x41, 0x73, 0x68,
+ 0x6b, 0x68, 0x61, 0x62, 0x61, 0x64, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x41, 0x74, 0x79,
+ 0x72, 0x61, 0x75, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x42, 0x61, 0x67, 0x68, 0x64, 0x61,
+ 0x64, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x42, 0x61, 0x68, 0x72, 0x61, 0x69, 0x6e, 0x0d,
+ 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x42, 0x61, 0x6b, 0x75, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61,
+ 0x2f, 0x42, 0x61, 0x6e, 0x67, 0x6b, 0x6f, 0x6b, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x42,
+ 0x61, 0x72, 0x6e, 0x61, 0x75, 0x6c, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x42, 0x65, 0x69,
+ 0x72, 0x75, 0x74, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x42, 0x69, 0x73, 0x68, 0x6b, 0x65,
+ 0x6b, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x42, 0x72, 0x75, 0x6e, 0x65, 0x69, 0x0d, 0x0a,
+ 0x41, 0x73, 0x69, 0x61, 0x2f, 0x43, 0x61, 0x6c, 0x63, 0x75, 0x74, 0x74, 0x61, 0x0d, 0x0a, 0x41,
+ 0x73, 0x69, 0x61, 0x2f, 0x43, 0x68, 0x69, 0x74, 0x61, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f,
+ 0x43, 0x68, 0x6f, 0x69, 0x62, 0x61, 0x6c, 0x73, 0x61, 0x6e, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61,
+ 0x2f, 0x43, 0x68, 0x6f, 0x6e, 0x67, 0x71, 0x69, 0x6e, 0x67, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61,
+ 0x2f, 0x43, 0x68, 0x75, 0x6e, 0x67, 0x6b, 0x69, 0x6e, 0x67, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61,
+ 0x2f, 0x43, 0x6f, 0x6c, 0x6f, 0x6d, 0x62, 0x6f, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x44,
+ 0x61, 0x63, 0x63, 0x61, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x44, 0x61, 0x6d, 0x61, 0x73,
+ 0x63, 0x75, 0x73, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x44, 0x68, 0x61, 0x6b, 0x61, 0x0d,
+ 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x44, 0x69, 0x6c, 0x69, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61,
+ 0x2f, 0x44, 0x75, 0x62, 0x61, 0x69, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x44, 0x75, 0x73,
+ 0x68, 0x61, 0x6e, 0x62, 0x65, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x46, 0x61, 0x6d, 0x61,
+ 0x67, 0x75, 0x73, 0x74, 0x61, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x47, 0x61, 0x7a, 0x61,
+ 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x48, 0x61, 0x72, 0x62, 0x69, 0x6e, 0x0d, 0x0a, 0x41,
+ 0x73, 0x69, 0x61, 0x2f, 0x48, 0x65, 0x62, 0x72, 0x6f, 0x6e, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61,
+ 0x2f, 0x48, 0x6f, 0x6e, 0x67, 0x5f, 0x4b, 0x6f, 0x6e, 0x67, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61,
+ 0x2f, 0x48, 0x6f, 0x76, 0x64, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x48, 0x6f, 0x5f, 0x43,
+ 0x68, 0x69, 0x5f, 0x4d, 0x69, 0x6e, 0x68, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x49, 0x72,
+ 0x6b, 0x75, 0x74, 0x73, 0x6b, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x49, 0x73, 0x74, 0x61,
+ 0x6e, 0x62, 0x75, 0x6c, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4a, 0x61, 0x6b, 0x61, 0x72,
+ 0x74, 0x61, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4a, 0x61, 0x79, 0x61, 0x70, 0x75, 0x72,
+ 0x61, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4a, 0x65, 0x72, 0x75, 0x73, 0x61, 0x6c, 0x65,
+ 0x6d, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4b, 0x61, 0x62, 0x75, 0x6c, 0x0d, 0x0a, 0x41,
+ 0x73, 0x69, 0x61, 0x2f, 0x4b, 0x61, 0x6d, 0x63, 0x68, 0x61, 0x74, 0x6b, 0x61, 0x0d, 0x0a, 0x41,
+ 0x73, 0x69, 0x61, 0x2f, 0x4b, 0x61, 0x72, 0x61, 0x63, 0x68, 0x69, 0x0d, 0x0a, 0x41, 0x73, 0x69,
+ 0x61, 0x2f, 0x4b, 0x61, 0x73, 0x68, 0x67, 0x61, 0x72, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f,
+ 0x4b, 0x61, 0x74, 0x68, 0x6d, 0x61, 0x6e, 0x64, 0x75, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f,
+ 0x4b, 0x61, 0x74, 0x6d, 0x61, 0x6e, 0x64, 0x75, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4b,
+ 0x68, 0x61, 0x6e, 0x64, 0x79, 0x67, 0x61, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4b, 0x6f,
+ 0x6c, 0x6b, 0x61, 0x74, 0x61, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4b, 0x72, 0x61, 0x73,
+ 0x6e, 0x6f, 0x79, 0x61, 0x72, 0x73, 0x6b, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4b, 0x75,
+ 0x61, 0x6c, 0x61, 0x5f, 0x4c, 0x75, 0x6d, 0x70, 0x75, 0x72, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61,
+ 0x2f, 0x4b, 0x75, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4b,
+ 0x75, 0x77, 0x61, 0x69, 0x74, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4d, 0x61, 0x63, 0x61,
+ 0x6f, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4d, 0x61, 0x63, 0x61, 0x75, 0x0d, 0x0a, 0x41,
+ 0x73, 0x69, 0x61, 0x2f, 0x4d, 0x61, 0x67, 0x61, 0x64, 0x61, 0x6e, 0x0d, 0x0a, 0x41, 0x73, 0x69,
+ 0x61, 0x2f, 0x4d, 0x61, 0x6b, 0x61, 0x73, 0x73, 0x61, 0x72, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61,
+ 0x2f, 0x4d, 0x61, 0x6e, 0x69, 0x6c, 0x61, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4d, 0x75,
+ 0x73, 0x63, 0x61, 0x74, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4e, 0x69, 0x63, 0x6f, 0x73,
+ 0x69, 0x61, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4e, 0x6f, 0x76, 0x6f, 0x6b, 0x75, 0x7a,
+ 0x6e, 0x65, 0x74, 0x73, 0x6b, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4e, 0x6f, 0x76, 0x6f,
+ 0x73, 0x69, 0x62, 0x69, 0x72, 0x73, 0x6b, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4f, 0x6d,
+ 0x73, 0x6b, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4f, 0x72, 0x61, 0x6c, 0x0d, 0x0a, 0x41,
+ 0x73, 0x69, 0x61, 0x2f, 0x50, 0x68, 0x6e, 0x6f, 0x6d, 0x5f, 0x50, 0x65, 0x6e, 0x68, 0x0d, 0x0a,
+ 0x41, 0x73, 0x69, 0x61, 0x2f, 0x50, 0x6f, 0x6e, 0x74, 0x69, 0x61, 0x6e, 0x61, 0x6b, 0x0d, 0x0a,
+ 0x41, 0x73, 0x69, 0x61, 0x2f, 0x50, 0x79, 0x6f, 0x6e, 0x67, 0x79, 0x61, 0x6e, 0x67, 0x0d, 0x0a,
+ 0x41, 0x73, 0x69, 0x61, 0x2f, 0x51, 0x61, 0x74, 0x61, 0x72, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61,
+ 0x2f, 0x51, 0x79, 0x7a, 0x79, 0x6c, 0x6f, 0x72, 0x64, 0x61, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61,
+ 0x2f, 0x52, 0x61, 0x6e, 0x67, 0x6f, 0x6f, 0x6e, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x52,
+ 0x69, 0x79, 0x61, 0x64, 0x68, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x53, 0x61, 0x69, 0x67,
+ 0x6f, 0x6e, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x53, 0x61, 0x6b, 0x68, 0x61, 0x6c, 0x69,
+ 0x6e, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x53, 0x61, 0x6d, 0x61, 0x72, 0x6b, 0x61, 0x6e,
+ 0x64, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x53, 0x65, 0x6f, 0x75, 0x6c, 0x0d, 0x0a, 0x41,
+ 0x73, 0x69, 0x61, 0x2f, 0x53, 0x68, 0x61, 0x6e, 0x67, 0x68, 0x61, 0x69, 0x0d, 0x0a, 0x41, 0x73,
+ 0x69, 0x61, 0x2f, 0x53, 0x69, 0x6e, 0x67, 0x61, 0x70, 0x6f, 0x72, 0x65, 0x0d, 0x0a, 0x41, 0x73,
+ 0x69, 0x61, 0x2f, 0x53, 0x72, 0x65, 0x64, 0x6e, 0x65, 0x6b, 0x6f, 0x6c, 0x79, 0x6d, 0x73, 0x6b,
+ 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x54, 0x61, 0x69, 0x70, 0x65, 0x69, 0x0d, 0x0a, 0x41,
+ 0x73, 0x69, 0x61, 0x2f, 0x54, 0x61, 0x73, 0x68, 0x6b, 0x65, 0x6e, 0x74, 0x0d, 0x0a, 0x41, 0x73,
+ 0x69, 0x61, 0x2f, 0x54, 0x62, 0x69, 0x6c, 0x69, 0x73, 0x69, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61,
+ 0x2f, 0x54, 0x65, 0x68, 0x72, 0x61, 0x6e, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x54, 0x65,
+ 0x6c, 0x5f, 0x41, 0x76, 0x69, 0x76, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x54, 0x68, 0x69,
+ 0x6d, 0x62, 0x75, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x54, 0x68, 0x69, 0x6d, 0x70, 0x68,
+ 0x75, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x54, 0x6f, 0x6b, 0x79, 0x6f, 0x0d, 0x0a, 0x41,
+ 0x73, 0x69, 0x61, 0x2f, 0x54, 0x6f, 0x6d, 0x73, 0x6b, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f,
+ 0x55, 0x6a, 0x75, 0x6e, 0x67, 0x5f, 0x50, 0x61, 0x6e, 0x64, 0x61, 0x6e, 0x67, 0x0d, 0x0a, 0x41,
+ 0x73, 0x69, 0x61, 0x2f, 0x55, 0x6c, 0x61, 0x61, 0x6e, 0x62, 0x61, 0x61, 0x74, 0x61, 0x72, 0x0d,
+ 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x55, 0x6c, 0x61, 0x6e, 0x5f, 0x42, 0x61, 0x74, 0x6f, 0x72,
+ 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x55, 0x72, 0x75, 0x6d, 0x71, 0x69, 0x0d, 0x0a, 0x41,
+ 0x73, 0x69, 0x61, 0x2f, 0x55, 0x73, 0x74, 0x2d, 0x4e, 0x65, 0x72, 0x61, 0x0d, 0x0a, 0x41, 0x73,
+ 0x69, 0x61, 0x2f, 0x56, 0x69, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6e, 0x65, 0x0d, 0x0a, 0x41, 0x73,
+ 0x69, 0x61, 0x2f, 0x56, 0x6c, 0x61, 0x64, 0x69, 0x76, 0x6f, 0x73, 0x74, 0x6f, 0x6b, 0x0d, 0x0a,
+ 0x41, 0x73, 0x69, 0x61, 0x2f, 0x59, 0x61, 0x6b, 0x75, 0x74, 0x73, 0x6b, 0x0d, 0x0a, 0x41, 0x73,
+ 0x69, 0x61, 0x2f, 0x59, 0x61, 0x6e, 0x67, 0x6f, 0x6e, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f,
+ 0x59, 0x65, 0x6b, 0x61, 0x74, 0x65, 0x72, 0x69, 0x6e, 0x62, 0x75, 0x72, 0x67, 0x0d, 0x0a, 0x41,
+ 0x73, 0x69, 0x61, 0x2f, 0x59, 0x65, 0x72, 0x65, 0x76, 0x61, 0x6e, 0x0d, 0x0a, 0x41, 0x74, 0x6c,
+ 0x61, 0x6e, 0x74, 0x69, 0x63, 0x2f, 0x41, 0x7a, 0x6f, 0x72, 0x65, 0x73, 0x0d, 0x0a, 0x41, 0x74,
+ 0x6c, 0x61, 0x6e, 0x74, 0x69, 0x63, 0x2f, 0x42, 0x65, 0x72, 0x6d, 0x75, 0x64, 0x61, 0x0d, 0x0a,
+ 0x41, 0x74, 0x6c, 0x61, 0x6e, 0x74, 0x69, 0x63, 0x2f, 0x43, 0x61, 0x6e, 0x61, 0x72, 0x79, 0x0d,
+ 0x0a, 0x41, 0x74, 0x6c, 0x61, 0x6e, 0x74, 0x69, 0x63, 0x2f, 0x43, 0x61, 0x70, 0x65, 0x5f, 0x56,
+ 0x65, 0x72, 0x64, 0x65, 0x0d, 0x0a, 0x41, 0x74, 0x6c, 0x61, 0x6e, 0x74, 0x69, 0x63, 0x2f, 0x46,
+ 0x61, 0x65, 0x72, 0x6f, 0x65, 0x0d, 0x0a, 0x41, 0x74, 0x6c, 0x61, 0x6e, 0x74, 0x69, 0x63, 0x2f,
+ 0x46, 0x61, 0x72, 0x6f, 0x65, 0x0d, 0x0a, 0x41, 0x74, 0x6c, 0x61, 0x6e, 0x74, 0x69, 0x63, 0x2f,
+ 0x4a, 0x61, 0x6e, 0x5f, 0x4d, 0x61, 0x79, 0x65, 0x6e, 0x0d, 0x0a, 0x41, 0x74, 0x6c, 0x61, 0x6e,
+ 0x74, 0x69, 0x63, 0x2f, 0x4d, 0x61, 0x64, 0x65, 0x69, 0x72, 0x61, 0x0d, 0x0a, 0x41, 0x74, 0x6c,
+ 0x61, 0x6e, 0x74, 0x69, 0x63, 0x2f, 0x52, 0x65, 0x79, 0x6b, 0x6a, 0x61, 0x76, 0x69, 0x6b, 0x0d,
+ 0x0a, 0x41, 0x74, 0x6c, 0x61, 0x6e, 0x74, 0x69, 0x63, 0x2f, 0x53, 0x6f, 0x75, 0x74, 0x68, 0x5f,
+ 0x47, 0x65, 0x6f, 0x72, 0x67, 0x69, 0x61, 0x0d, 0x0a, 0x41, 0x74, 0x6c, 0x61, 0x6e, 0x74, 0x69,
+ 0x63, 0x2f, 0x53, 0x74, 0x61, 0x6e, 0x6c, 0x65, 0x79, 0x0d, 0x0a, 0x41, 0x74, 0x6c, 0x61, 0x6e,
+ 0x74, 0x69, 0x63, 0x2f, 0x53, 0x74, 0x5f, 0x48, 0x65, 0x6c, 0x65, 0x6e, 0x61, 0x0d, 0x0a, 0x41,
+ 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f, 0x41, 0x43, 0x54, 0x0d, 0x0a, 0x41, 0x75,
+ 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f, 0x41, 0x64, 0x65, 0x6c, 0x61, 0x69, 0x64, 0x65,
+ 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f, 0x42, 0x72, 0x69, 0x73,
+ 0x62, 0x61, 0x6e, 0x65, 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f,
+ 0x42, 0x72, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x48, 0x69, 0x6c, 0x6c, 0x0d, 0x0a, 0x41, 0x75, 0x73,
+ 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f, 0x43, 0x61, 0x6e, 0x62, 0x65, 0x72, 0x72, 0x61, 0x0d,
+ 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f, 0x43, 0x75, 0x72, 0x72, 0x69,
+ 0x65, 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f, 0x44, 0x61, 0x72,
+ 0x77, 0x69, 0x6e, 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f, 0x45,
+ 0x75, 0x63, 0x6c, 0x61, 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f,
+ 0x48, 0x6f, 0x62, 0x61, 0x72, 0x74, 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69,
+ 0x61, 0x2f, 0x4c, 0x48, 0x49, 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61,
+ 0x2f, 0x4c, 0x69, 0x6e, 0x64, 0x65, 0x6d, 0x61, 0x6e, 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72,
+ 0x61, 0x6c, 0x69, 0x61, 0x2f, 0x4c, 0x6f, 0x72, 0x64, 0x5f, 0x48, 0x6f, 0x77, 0x65, 0x0d, 0x0a,
+ 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f, 0x4d, 0x65, 0x6c, 0x62, 0x6f, 0x75,
+ 0x72, 0x6e, 0x65, 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f, 0x4e,
+ 0x6f, 0x72, 0x74, 0x68, 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f,
+ 0x4e, 0x53, 0x57, 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f, 0x50,
+ 0x65, 0x72, 0x74, 0x68, 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f,
+ 0x51, 0x75, 0x65, 0x65, 0x6e, 0x73, 0x6c, 0x61, 0x6e, 0x64, 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74,
+ 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f, 0x53, 0x6f, 0x75, 0x74, 0x68, 0x0d, 0x0a, 0x41, 0x75, 0x73,
+ 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f, 0x53, 0x79, 0x64, 0x6e, 0x65, 0x79, 0x0d, 0x0a, 0x41,
+ 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f, 0x54, 0x61, 0x73, 0x6d, 0x61, 0x6e, 0x69,
+ 0x61, 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f, 0x56, 0x69, 0x63,
+ 0x74, 0x6f, 0x72, 0x69, 0x61, 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61,
+ 0x2f, 0x57, 0x65, 0x73, 0x74, 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61,
+ 0x2f, 0x59, 0x61, 0x6e, 0x63, 0x6f, 0x77, 0x69, 0x6e, 0x6e, 0x61, 0x0d, 0x0a, 0x42, 0x72, 0x61,
+ 0x7a, 0x69, 0x6c, 0x2f, 0x41, 0x63, 0x72, 0x65, 0x0d, 0x0a, 0x42, 0x72, 0x61, 0x7a, 0x69, 0x6c,
+ 0x2f, 0x44, 0x65, 0x4e, 0x6f, 0x72, 0x6f, 0x6e, 0x68, 0x61, 0x0d, 0x0a, 0x42, 0x72, 0x61, 0x7a,
+ 0x69, 0x6c, 0x2f, 0x45, 0x61, 0x73, 0x74, 0x0d, 0x0a, 0x42, 0x72, 0x61, 0x7a, 0x69, 0x6c, 0x2f,
+ 0x57, 0x65, 0x73, 0x74, 0x0d, 0x0a, 0x43, 0x61, 0x6e, 0x61, 0x64, 0x61, 0x2f, 0x41, 0x74, 0x6c,
+ 0x61, 0x6e, 0x74, 0x69, 0x63, 0x0d, 0x0a, 0x43, 0x61, 0x6e, 0x61, 0x64, 0x61, 0x2f, 0x43, 0x65,
+ 0x6e, 0x74, 0x72, 0x61, 0x6c, 0x0d, 0x0a, 0x43, 0x61, 0x6e, 0x61, 0x64, 0x61, 0x2f, 0x45, 0x61,
+ 0x73, 0x74, 0x2d, 0x53, 0x61, 0x73, 0x6b, 0x61, 0x74, 0x63, 0x68, 0x65, 0x77, 0x61, 0x6e, 0x0d,
+ 0x0a, 0x43, 0x61, 0x6e, 0x61, 0x64, 0x61, 0x2f, 0x45, 0x61, 0x73, 0x74, 0x65, 0x72, 0x6e, 0x0d,
+ 0x0a, 0x43, 0x61, 0x6e, 0x61, 0x64, 0x61, 0x2f, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x61, 0x69, 0x6e,
+ 0x0d, 0x0a, 0x43, 0x61, 0x6e, 0x61, 0x64, 0x61, 0x2f, 0x4e, 0x65, 0x77, 0x66, 0x6f, 0x75, 0x6e,
+ 0x64, 0x6c, 0x61, 0x6e, 0x64, 0x0d, 0x0a, 0x43, 0x61, 0x6e, 0x61, 0x64, 0x61, 0x2f, 0x50, 0x61,
+ 0x63, 0x69, 0x66, 0x69, 0x63, 0x0d, 0x0a, 0x43, 0x61, 0x6e, 0x61, 0x64, 0x61, 0x2f, 0x53, 0x61,
+ 0x73, 0x6b, 0x61, 0x74, 0x63, 0x68, 0x65, 0x77, 0x61, 0x6e, 0x0d, 0x0a, 0x43, 0x61, 0x6e, 0x61,
+ 0x64, 0x61, 0x2f, 0x59, 0x75, 0x6b, 0x6f, 0x6e, 0x0d, 0x0a, 0x43, 0x68, 0x69, 0x6c, 0x65, 0x2f,
+ 0x43, 0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x0d, 0x0a, 0x43, 0x68, 0x69,
+ 0x6c, 0x65, 0x2f, 0x45, 0x61, 0x73, 0x74, 0x65, 0x72, 0x49, 0x73, 0x6c, 0x61, 0x6e, 0x64, 0x0d,
+ 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d,
+ 0x54, 0x2b, 0x30, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2b, 0x31, 0x0d, 0x0a,
+ 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2b, 0x31, 0x30, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f,
+ 0x47, 0x4d, 0x54, 0x2b, 0x31, 0x31, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2b,
+ 0x31, 0x32, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2b, 0x32, 0x0d, 0x0a, 0x45,
+ 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2b, 0x33, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d,
+ 0x54, 0x2b, 0x34, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2b, 0x35, 0x0d, 0x0a,
+ 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2b, 0x36, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47,
+ 0x4d, 0x54, 0x2b, 0x37, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2b, 0x38, 0x0d,
+ 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2b, 0x39, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f,
+ 0x47, 0x4d, 0x54, 0x2d, 0x30, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2d, 0x31,
+ 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2d, 0x31, 0x30, 0x0d, 0x0a, 0x45, 0x74,
+ 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2d, 0x31, 0x31, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d,
+ 0x54, 0x2d, 0x31, 0x32, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2d, 0x31, 0x33,
+ 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2d, 0x31, 0x34, 0x0d, 0x0a, 0x45, 0x74,
+ 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2d, 0x32, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54,
+ 0x2d, 0x33, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2d, 0x34, 0x0d, 0x0a, 0x45,
+ 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2d, 0x35, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d,
+ 0x54, 0x2d, 0x36, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2d, 0x37, 0x0d, 0x0a,
+ 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2d, 0x38, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47,
+ 0x4d, 0x54, 0x2d, 0x39, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x30, 0x0d, 0x0a,
+ 0x45, 0x74, 0x63, 0x2f, 0x47, 0x72, 0x65, 0x65, 0x6e, 0x77, 0x69, 0x63, 0x68, 0x0d, 0x0a, 0x45,
+ 0x74, 0x63, 0x2f, 0x55, 0x43, 0x54, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x55, 0x6e, 0x69, 0x76,
+ 0x65, 0x72, 0x73, 0x61, 0x6c, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x55, 0x54, 0x43, 0x0d, 0x0a,
+ 0x45, 0x74, 0x63, 0x2f, 0x5a, 0x75, 0x6c, 0x75, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65,
+ 0x2f, 0x41, 0x6d, 0x73, 0x74, 0x65, 0x72, 0x64, 0x61, 0x6d, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f,
+ 0x70, 0x65, 0x2f, 0x41, 0x6e, 0x64, 0x6f, 0x72, 0x72, 0x61, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f,
+ 0x70, 0x65, 0x2f, 0x41, 0x73, 0x74, 0x72, 0x61, 0x6b, 0x68, 0x61, 0x6e, 0x0d, 0x0a, 0x45, 0x75,
+ 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x41, 0x74, 0x68, 0x65, 0x6e, 0x73, 0x0d, 0x0a, 0x45, 0x75, 0x72,
+ 0x6f, 0x70, 0x65, 0x2f, 0x42, 0x65, 0x6c, 0x66, 0x61, 0x73, 0x74, 0x0d, 0x0a, 0x45, 0x75, 0x72,
+ 0x6f, 0x70, 0x65, 0x2f, 0x42, 0x65, 0x6c, 0x67, 0x72, 0x61, 0x64, 0x65, 0x0d, 0x0a, 0x45, 0x75,
+ 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x42, 0x65, 0x72, 0x6c, 0x69, 0x6e, 0x0d, 0x0a, 0x45, 0x75, 0x72,
+ 0x6f, 0x70, 0x65, 0x2f, 0x42, 0x72, 0x61, 0x74, 0x69, 0x73, 0x6c, 0x61, 0x76, 0x61, 0x0d, 0x0a,
+ 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x42, 0x72, 0x75, 0x73, 0x73, 0x65, 0x6c, 0x73, 0x0d,
+ 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x42, 0x75, 0x63, 0x68, 0x61, 0x72, 0x65, 0x73,
+ 0x74, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x42, 0x75, 0x64, 0x61, 0x70, 0x65,
+ 0x73, 0x74, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x42, 0x75, 0x73, 0x69, 0x6e,
+ 0x67, 0x65, 0x6e, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x43, 0x68, 0x69, 0x73,
+ 0x69, 0x6e, 0x61, 0x75, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x43, 0x6f, 0x70,
+ 0x65, 0x6e, 0x68, 0x61, 0x67, 0x65, 0x6e, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f,
+ 0x44, 0x75, 0x62, 0x6c, 0x69, 0x6e, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x47,
+ 0x69, 0x62, 0x72, 0x61, 0x6c, 0x74, 0x61, 0x72, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65,
+ 0x2f, 0x47, 0x75, 0x65, 0x72, 0x6e, 0x73, 0x65, 0x79, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70,
+ 0x65, 0x2f, 0x48, 0x65, 0x6c, 0x73, 0x69, 0x6e, 0x6b, 0x69, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f,
+ 0x70, 0x65, 0x2f, 0x49, 0x73, 0x6c, 0x65, 0x5f, 0x6f, 0x66, 0x5f, 0x4d, 0x61, 0x6e, 0x0d, 0x0a,
+ 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x49, 0x73, 0x74, 0x61, 0x6e, 0x62, 0x75, 0x6c, 0x0d,
+ 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x4a, 0x65, 0x72, 0x73, 0x65, 0x79, 0x0d, 0x0a,
+ 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x4b, 0x61, 0x6c, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x72,
+ 0x61, 0x64, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x4b, 0x69, 0x65, 0x76, 0x0d,
+ 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x4b, 0x69, 0x72, 0x6f, 0x76, 0x0d, 0x0a, 0x45,
+ 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x4c, 0x69, 0x73, 0x62, 0x6f, 0x6e, 0x0d, 0x0a, 0x45, 0x75,
+ 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x4c, 0x6a, 0x75, 0x62, 0x6c, 0x6a, 0x61, 0x6e, 0x61, 0x0d, 0x0a,
+ 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x4c, 0x6f, 0x6e, 0x64, 0x6f, 0x6e, 0x0d, 0x0a, 0x45,
+ 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x4c, 0x75, 0x78, 0x65, 0x6d, 0x62, 0x6f, 0x75, 0x72, 0x67,
+ 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x4d, 0x61, 0x64, 0x72, 0x69, 0x64, 0x0d,
+ 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x4d, 0x61, 0x6c, 0x74, 0x61, 0x0d, 0x0a, 0x45,
+ 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x4d, 0x61, 0x72, 0x69, 0x65, 0x68, 0x61, 0x6d, 0x6e, 0x0d,
+ 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x4d, 0x69, 0x6e, 0x73, 0x6b, 0x0d, 0x0a, 0x45,
+ 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x4d, 0x6f, 0x6e, 0x61, 0x63, 0x6f, 0x0d, 0x0a, 0x45, 0x75,
+ 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x4d, 0x6f, 0x73, 0x63, 0x6f, 0x77, 0x0d, 0x0a, 0x45, 0x75, 0x72,
+ 0x6f, 0x70, 0x65, 0x2f, 0x4e, 0x69, 0x63, 0x6f, 0x73, 0x69, 0x61, 0x0d, 0x0a, 0x45, 0x75, 0x72,
+ 0x6f, 0x70, 0x65, 0x2f, 0x4f, 0x73, 0x6c, 0x6f, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65,
+ 0x2f, 0x50, 0x61, 0x72, 0x69, 0x73, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x50,
+ 0x6f, 0x64, 0x67, 0x6f, 0x72, 0x69, 0x63, 0x61, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65,
+ 0x2f, 0x50, 0x72, 0x61, 0x67, 0x75, 0x65, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f,
+ 0x52, 0x69, 0x67, 0x61, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x52, 0x6f, 0x6d,
+ 0x65, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x53, 0x61, 0x6d, 0x61, 0x72, 0x61,
+ 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x53, 0x61, 0x6e, 0x5f, 0x4d, 0x61, 0x72,
+ 0x69, 0x6e, 0x6f, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x53, 0x61, 0x72, 0x61,
+ 0x6a, 0x65, 0x76, 0x6f, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x53, 0x61, 0x72,
+ 0x61, 0x74, 0x6f, 0x76, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x53, 0x69, 0x6d,
+ 0x66, 0x65, 0x72, 0x6f, 0x70, 0x6f, 0x6c, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f,
+ 0x53, 0x6b, 0x6f, 0x70, 0x6a, 0x65, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x53,
+ 0x6f, 0x66, 0x69, 0x61, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x53, 0x74, 0x6f,
+ 0x63, 0x6b, 0x68, 0x6f, 0x6c, 0x6d, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x54,
+ 0x61, 0x6c, 0x6c, 0x69, 0x6e, 0x6e, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x54,
+ 0x69, 0x72, 0x61, 0x6e, 0x65, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x54, 0x69,
+ 0x72, 0x61, 0x73, 0x70, 0x6f, 0x6c, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x55,
+ 0x6c, 0x79, 0x61, 0x6e, 0x6f, 0x76, 0x73, 0x6b, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65,
+ 0x2f, 0x55, 0x7a, 0x68, 0x67, 0x6f, 0x72, 0x6f, 0x64, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70,
+ 0x65, 0x2f, 0x56, 0x61, 0x64, 0x75, 0x7a, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f,
+ 0x56, 0x61, 0x74, 0x69, 0x63, 0x61, 0x6e, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f,
+ 0x56, 0x69, 0x65, 0x6e, 0x6e, 0x61, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x56,
+ 0x69, 0x6c, 0x6e, 0x69, 0x75, 0x73, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x56,
+ 0x6f, 0x6c, 0x67, 0x6f, 0x67, 0x72, 0x61, 0x64, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65,
+ 0x2f, 0x57, 0x61, 0x72, 0x73, 0x61, 0x77, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f,
+ 0x5a, 0x61, 0x67, 0x72, 0x65, 0x62, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x5a,
+ 0x61, 0x70, 0x6f, 0x72, 0x6f, 0x7a, 0x68, 0x79, 0x65, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70,
+ 0x65, 0x2f, 0x5a, 0x75, 0x72, 0x69, 0x63, 0x68, 0x0d, 0x0a, 0x49, 0x6e, 0x64, 0x69, 0x61, 0x6e,
+ 0x2f, 0x41, 0x6e, 0x74, 0x61, 0x6e, 0x61, 0x6e, 0x61, 0x72, 0x69, 0x76, 0x6f, 0x0d, 0x0a, 0x49,
+ 0x6e, 0x64, 0x69, 0x61, 0x6e, 0x2f, 0x43, 0x68, 0x61, 0x67, 0x6f, 0x73, 0x0d, 0x0a, 0x49, 0x6e,
+ 0x64, 0x69, 0x61, 0x6e, 0x2f, 0x43, 0x68, 0x72, 0x69, 0x73, 0x74, 0x6d, 0x61, 0x73, 0x0d, 0x0a,
+ 0x49, 0x6e, 0x64, 0x69, 0x61, 0x6e, 0x2f, 0x43, 0x6f, 0x63, 0x6f, 0x73, 0x0d, 0x0a, 0x49, 0x6e,
+ 0x64, 0x69, 0x61, 0x6e, 0x2f, 0x43, 0x6f, 0x6d, 0x6f, 0x72, 0x6f, 0x0d, 0x0a, 0x49, 0x6e, 0x64,
+ 0x69, 0x61, 0x6e, 0x2f, 0x4b, 0x65, 0x72, 0x67, 0x75, 0x65, 0x6c, 0x65, 0x6e, 0x0d, 0x0a, 0x49,
+ 0x6e, 0x64, 0x69, 0x61, 0x6e, 0x2f, 0x4d, 0x61, 0x68, 0x65, 0x0d, 0x0a, 0x49, 0x6e, 0x64, 0x69,
+ 0x61, 0x6e, 0x2f, 0x4d, 0x61, 0x6c, 0x64, 0x69, 0x76, 0x65, 0x73, 0x0d, 0x0a, 0x49, 0x6e, 0x64,
+ 0x69, 0x61, 0x6e, 0x2f, 0x4d, 0x61, 0x75, 0x72, 0x69, 0x74, 0x69, 0x75, 0x73, 0x0d, 0x0a, 0x49,
+ 0x6e, 0x64, 0x69, 0x61, 0x6e, 0x2f, 0x4d, 0x61, 0x79, 0x6f, 0x74, 0x74, 0x65, 0x0d, 0x0a, 0x49,
+ 0x6e, 0x64, 0x69, 0x61, 0x6e, 0x2f, 0x52, 0x65, 0x75, 0x6e, 0x69, 0x6f, 0x6e, 0x0d, 0x0a, 0x4d,
+ 0x65, 0x78, 0x69, 0x63, 0x6f, 0x2f, 0x42, 0x61, 0x6a, 0x61, 0x4e, 0x6f, 0x72, 0x74, 0x65, 0x0d,
+ 0x0a, 0x4d, 0x65, 0x78, 0x69, 0x63, 0x6f, 0x2f, 0x42, 0x61, 0x6a, 0x61, 0x53, 0x75, 0x72, 0x0d,
+ 0x0a, 0x4d, 0x65, 0x78, 0x69, 0x63, 0x6f, 0x2f, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x0d,
+ 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x41, 0x70, 0x69, 0x61, 0x0d, 0x0a, 0x50,
+ 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x41, 0x75, 0x63, 0x6b, 0x6c, 0x61, 0x6e, 0x64, 0x0d,
+ 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x42, 0x6f, 0x75, 0x67, 0x61, 0x69, 0x6e,
+ 0x76, 0x69, 0x6c, 0x6c, 0x65, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x43,
+ 0x68, 0x61, 0x74, 0x68, 0x61, 0x6d, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f,
+ 0x43, 0x68, 0x75, 0x75, 0x6b, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x45,
+ 0x61, 0x73, 0x74, 0x65, 0x72, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x45,
+ 0x66, 0x61, 0x74, 0x65, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x45, 0x6e,
+ 0x64, 0x65, 0x72, 0x62, 0x75, 0x72, 0x79, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63,
+ 0x2f, 0x46, 0x61, 0x6b, 0x61, 0x6f, 0x66, 0x6f, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69,
+ 0x63, 0x2f, 0x46, 0x69, 0x6a, 0x69, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f,
+ 0x46, 0x75, 0x6e, 0x61, 0x66, 0x75, 0x74, 0x69, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69,
+ 0x63, 0x2f, 0x47, 0x61, 0x6c, 0x61, 0x70, 0x61, 0x67, 0x6f, 0x73, 0x0d, 0x0a, 0x50, 0x61, 0x63,
+ 0x69, 0x66, 0x69, 0x63, 0x2f, 0x47, 0x61, 0x6d, 0x62, 0x69, 0x65, 0x72, 0x0d, 0x0a, 0x50, 0x61,
+ 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x47, 0x75, 0x61, 0x64, 0x61, 0x6c, 0x63, 0x61, 0x6e, 0x61,
+ 0x6c, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x47, 0x75, 0x61, 0x6d, 0x0d,
+ 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x48, 0x6f, 0x6e, 0x6f, 0x6c, 0x75, 0x6c,
+ 0x75, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x4a, 0x6f, 0x68, 0x6e, 0x73,
+ 0x74, 0x6f, 0x6e, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x4b, 0x69, 0x72,
+ 0x69, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x69, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63,
+ 0x2f, 0x4b, 0x6f, 0x73, 0x72, 0x61, 0x65, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63,
+ 0x2f, 0x4b, 0x77, 0x61, 0x6a, 0x61, 0x6c, 0x65, 0x69, 0x6e, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69,
+ 0x66, 0x69, 0x63, 0x2f, 0x4d, 0x61, 0x6a, 0x75, 0x72, 0x6f, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69,
+ 0x66, 0x69, 0x63, 0x2f, 0x4d, 0x61, 0x72, 0x71, 0x75, 0x65, 0x73, 0x61, 0x73, 0x0d, 0x0a, 0x50,
+ 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x4d, 0x69, 0x64, 0x77, 0x61, 0x79, 0x0d, 0x0a, 0x50,
+ 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x4e, 0x61, 0x75, 0x72, 0x75, 0x0d, 0x0a, 0x50, 0x61,
+ 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x4e, 0x69, 0x75, 0x65, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69,
+ 0x66, 0x69, 0x63, 0x2f, 0x4e, 0x6f, 0x72, 0x66, 0x6f, 0x6c, 0x6b, 0x0d, 0x0a, 0x50, 0x61, 0x63,
+ 0x69, 0x66, 0x69, 0x63, 0x2f, 0x4e, 0x6f, 0x75, 0x6d, 0x65, 0x61, 0x0d, 0x0a, 0x50, 0x61, 0x63,
+ 0x69, 0x66, 0x69, 0x63, 0x2f, 0x50, 0x61, 0x67, 0x6f, 0x5f, 0x50, 0x61, 0x67, 0x6f, 0x0d, 0x0a,
+ 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x50, 0x61, 0x6c, 0x61, 0x75, 0x0d, 0x0a, 0x50,
+ 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x50, 0x69, 0x74, 0x63, 0x61, 0x69, 0x72, 0x6e, 0x0d,
+ 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x50, 0x6f, 0x68, 0x6e, 0x70, 0x65, 0x69,
+ 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x50, 0x6f, 0x6e, 0x61, 0x70, 0x65,
+ 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x50, 0x6f, 0x72, 0x74, 0x5f, 0x4d,
+ 0x6f, 0x72, 0x65, 0x73, 0x62, 0x79, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f,
+ 0x52, 0x61, 0x72, 0x6f, 0x74, 0x6f, 0x6e, 0x67, 0x61, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66,
+ 0x69, 0x63, 0x2f, 0x53, 0x61, 0x69, 0x70, 0x61, 0x6e, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66,
+ 0x69, 0x63, 0x2f, 0x53, 0x61, 0x6d, 0x6f, 0x61, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69,
+ 0x63, 0x2f, 0x54, 0x61, 0x68, 0x69, 0x74, 0x69, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69,
+ 0x63, 0x2f, 0x54, 0x61, 0x72, 0x61, 0x77, 0x61, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69,
+ 0x63, 0x2f, 0x54, 0x6f, 0x6e, 0x67, 0x61, 0x74, 0x61, 0x70, 0x75, 0x0d, 0x0a, 0x50, 0x61, 0x63,
+ 0x69, 0x66, 0x69, 0x63, 0x2f, 0x54, 0x72, 0x75, 0x6b, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66,
+ 0x69, 0x63, 0x2f, 0x57, 0x61, 0x6b, 0x65, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63,
+ 0x2f, 0x57, 0x61, 0x6c, 0x6c, 0x69, 0x73, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63,
+ 0x2f, 0x59, 0x61, 0x70, 0x0d, 0x0a, 0x55, 0x53, 0x2f, 0x41, 0x6c, 0x61, 0x73, 0x6b, 0x61, 0x0d,
+ 0x0a, 0x55, 0x53, 0x2f, 0x41, 0x6c, 0x65, 0x75, 0x74, 0x69, 0x61, 0x6e, 0x0d, 0x0a, 0x55, 0x53,
+ 0x2f, 0x41, 0x72, 0x69, 0x7a, 0x6f, 0x6e, 0x61, 0x0d, 0x0a, 0x55, 0x53, 0x2f, 0x43, 0x65, 0x6e,
+ 0x74, 0x72, 0x61, 0x6c, 0x0d, 0x0a, 0x55, 0x53, 0x2f, 0x45, 0x61, 0x73, 0x74, 0x2d, 0x49, 0x6e,
+ 0x64, 0x69, 0x61, 0x6e, 0x61, 0x0d, 0x0a, 0x55, 0x53, 0x2f, 0x45, 0x61, 0x73, 0x74, 0x65, 0x72,
+ 0x6e, 0x0d, 0x0a, 0x55, 0x53, 0x2f, 0x48, 0x61, 0x77, 0x61, 0x69, 0x69, 0x0d, 0x0a, 0x55, 0x53,
+ 0x2f, 0x49, 0x6e, 0x64, 0x69, 0x61, 0x6e, 0x61, 0x2d, 0x53, 0x74, 0x61, 0x72, 0x6b, 0x65, 0x0d,
+ 0x0a, 0x55, 0x53, 0x2f, 0x4d, 0x69, 0x63, 0x68, 0x69, 0x67, 0x61, 0x6e, 0x0d, 0x0a, 0x55, 0x53,
+ 0x2f, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x0d, 0x0a, 0x55, 0x53, 0x2f, 0x50, 0x61,
+ 0x63, 0x69, 0x66, 0x69, 0x63, 0x0d, 0x0a, 0x55, 0x53, 0x2f, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69,
+ 0x63, 0x2d, 0x4e, 0x65, 0x77, 0x0d, 0x0a, 0x55, 0x53, 0x2f, 0x53, 0x61, 0x6d, 0x6f, 0x61, 0x0d,
+ 0x0a};
+
+static VirtualFile GenerateDefaultTimeZoneFile() {
+ struct {
+ s64_be at;
+ INSERT_PADDING_BYTES(7);
+ std::array<char, 4> time_zone_chars;
+ INSERT_PADDING_BYTES(2);
+ std::array<char, 6> time_zone_name;
+ } time_zone_info{};
+
+ const VirtualFile file{std::make_shared<VectorVfsFile>(
+ std::vector<u8>(sizeof(Service::Time::TimeZone::TzifHeader) + sizeof(time_zone_info)),
+ "GMT")};
+
+ Service::Time::TimeZone::TzifHeader header{};
+ header.magic = 0x545a6966;
+ header.version = 0x32;
+ header.ttis_gmt_count = 0x1;
+ header.ttis_std_count = 0x1;
+ header.time_count = 0x1;
+ header.type_count = 0x1;
+ header.char_count = 0x4;
+ file->WriteObject(header, 0);
+
+ time_zone_info.at = 0xf8;
+ time_zone_info.time_zone_chars = {'G', 'M', 'T', '\0'};
+ time_zone_info.time_zone_name = {'\n', 'G', 'M', 'T', '0', '\n'};
+ file->WriteObject(time_zone_info, sizeof(Service::Time::TimeZone::TzifHeader));
+
+ return file;
+}
+
+VirtualDir TimeZoneBinary() {
+ const std::vector<VirtualDir> root_dirs{std::make_shared<VectorVfsDirectory>(
+ std::vector<VirtualFile>{GenerateDefaultTimeZoneFile()}, std::vector<VirtualDir>{},
+ "zoneinfo")};
+ const std::vector<VirtualFile> root_files{
+ std::make_shared<ArrayVfsFile<LOCATION_NAMES.size()>>(LOCATION_NAMES, "binaryList.txt")};
+ return std::make_shared<VectorVfsDirectory>(root_files, root_dirs, "data");
+}
+
+} // namespace FileSys::SystemArchive
diff --git a/src/core/file_sys/system_archive/time_zone_binary.h b/src/core/file_sys/system_archive/time_zone_binary.h
new file mode 100644
index 000000000..ed2b78227
--- /dev/null
+++ b/src/core/file_sys/system_archive/time_zone_binary.h
@@ -0,0 +1,14 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <string>
+#include "core/file_sys/vfs_types.h"
+
+namespace FileSys::SystemArchive {
+
+VirtualDir TimeZoneBinary();
+
+} // namespace FileSys::SystemArchive
diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp
index 37cb28848..67e95999d 100644
--- a/src/core/gdbstub/gdbstub.cpp
+++ b/src/core/gdbstub/gdbstub.cpp
@@ -35,7 +35,7 @@
#include "common/swap.h"
#include "core/arm/arm_interface.h"
#include "core/core.h"
-#include "core/core_cpu.h"
+#include "core/core_manager.h"
#include "core/gdbstub/gdbstub.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/scheduler.h"
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp
index db189c8e3..2ea3dcb61 100644
--- a/src/core/hle/kernel/address_arbiter.cpp
+++ b/src/core/hle/kernel/address_arbiter.cpp
@@ -8,7 +8,6 @@
#include "common/assert.h"
#include "common/common_types.h"
#include "core/core.h"
-#include "core/core_cpu.h"
#include "core/hle/kernel/address_arbiter.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/scheduler.h"
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 1d0783bd3..edd4c4259 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -3,13 +3,15 @@
// Refer to the license.txt file included.
#include <atomic>
+#include <functional>
#include <memory>
#include <mutex>
#include <utility>
#include "common/assert.h"
#include "common/logging/log.h"
-
+#include "core/arm/arm_interface.h"
+#include "core/arm/exclusive_monitor.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/core_timing_util.h"
@@ -17,6 +19,7 @@
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/physical_core.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/kernel/scheduler.h"
@@ -98,6 +101,7 @@ struct KernelCore::Impl {
void Initialize(KernelCore& kernel) {
Shutdown();
+ InitializePhysicalCores();
InitializeSystemResourceLimit(kernel);
InitializeThreads();
InitializePreemption();
@@ -121,6 +125,21 @@ struct KernelCore::Impl {
global_scheduler.Shutdown();
named_ports.clear();
+
+ for (auto& core : cores) {
+ core.Shutdown();
+ }
+ cores.clear();
+
+ exclusive_monitor.reset();
+ }
+
+ void InitializePhysicalCores() {
+ exclusive_monitor =
+ Core::MakeExclusiveMonitor(system.Memory(), global_scheduler.CpuCoresCount());
+ for (std::size_t i = 0; i < global_scheduler.CpuCoresCount(); i++) {
+ cores.emplace_back(system, i, *exclusive_monitor);
+ }
}
// Creates the default system resource limit
@@ -186,6 +205,9 @@ struct KernelCore::Impl {
/// the ConnectToPort SVC.
NamedPortTable named_ports;
+ std::unique_ptr<Core::ExclusiveMonitor> exclusive_monitor;
+ std::vector<Kernel::PhysicalCore> cores;
+
// System context
Core::System& system;
};
@@ -240,6 +262,34 @@ const Kernel::GlobalScheduler& KernelCore::GlobalScheduler() const {
return impl->global_scheduler;
}
+Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) {
+ return impl->cores[id];
+}
+
+const Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) const {
+ return impl->cores[id];
+}
+
+Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() {
+ return *impl->exclusive_monitor;
+}
+
+const Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() const {
+ return *impl->exclusive_monitor;
+}
+
+void KernelCore::InvalidateAllInstructionCaches() {
+ for (std::size_t i = 0; i < impl->global_scheduler.CpuCoresCount(); i++) {
+ PhysicalCore(i).ArmInterface().ClearInstructionCache();
+ }
+}
+
+void KernelCore::PrepareReschedule(std::size_t id) {
+ if (id < impl->global_scheduler.CpuCoresCount()) {
+ impl->cores[id].Stop();
+ }
+}
+
void KernelCore::AddNamedPort(std::string name, std::shared_ptr<ClientPort> port) {
impl->named_ports.emplace(std::move(name), std::move(port));
}
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 3bf0068ed..fccffaf3a 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -11,8 +11,9 @@
#include "core/hle/kernel/object.h"
namespace Core {
+class ExclusiveMonitor;
class System;
-}
+} // namespace Core
namespace Core::Timing {
class CoreTiming;
@@ -25,6 +26,7 @@ class AddressArbiter;
class ClientPort;
class GlobalScheduler;
class HandleTable;
+class PhysicalCore;
class Process;
class ResourceLimit;
class Thread;
@@ -84,6 +86,21 @@ public:
/// Gets the sole instance of the global scheduler
const Kernel::GlobalScheduler& GlobalScheduler() const;
+ /// Gets the an instance of the respective physical CPU core.
+ Kernel::PhysicalCore& PhysicalCore(std::size_t id);
+
+ /// Gets the an instance of the respective physical CPU core.
+ const Kernel::PhysicalCore& PhysicalCore(std::size_t id) const;
+
+ /// Stops execution of 'id' core, in order to reschedule a new thread.
+ void PrepareReschedule(std::size_t id);
+
+ Core::ExclusiveMonitor& GetExclusiveMonitor();
+
+ const Core::ExclusiveMonitor& GetExclusiveMonitor() const;
+
+ void InvalidateAllInstructionCaches();
+
/// Adds a port to the named port table
void AddNamedPort(std::string name, std::shared_ptr<ClientPort> port);
diff --git a/src/core/hle/kernel/physical_core.cpp b/src/core/hle/kernel/physical_core.cpp
new file mode 100644
index 000000000..9303dd273
--- /dev/null
+++ b/src/core/hle/kernel/physical_core.cpp
@@ -0,0 +1,51 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/logging/log.h"
+#include "core/arm/arm_interface.h"
+#ifdef ARCHITECTURE_x86_64
+#include "core/arm/dynarmic/arm_dynarmic.h"
+#endif
+#include "core/arm/exclusive_monitor.h"
+#include "core/arm/unicorn/arm_unicorn.h"
+#include "core/core.h"
+#include "core/hle/kernel/physical_core.h"
+#include "core/hle/kernel/scheduler.h"
+#include "core/hle/kernel/thread.h"
+
+namespace Kernel {
+
+PhysicalCore::PhysicalCore(Core::System& system, std::size_t id,
+ Core::ExclusiveMonitor& exclusive_monitor)
+ : core_index{id} {
+#ifdef ARCHITECTURE_x86_64
+ arm_interface = std::make_unique<Core::ARM_Dynarmic>(system, exclusive_monitor, core_index);
+#else
+ arm_interface = std::make_shared<Core::ARM_Unicorn>(system);
+ LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");
+#endif
+
+ scheduler = std::make_unique<Kernel::Scheduler>(system, *arm_interface, core_index);
+}
+
+PhysicalCore::~PhysicalCore() = default;
+
+void PhysicalCore::Run() {
+ arm_interface->Run();
+ arm_interface->ClearExclusiveState();
+}
+
+void PhysicalCore::Step() {
+ arm_interface->Step();
+}
+
+void PhysicalCore::Stop() {
+ arm_interface->PrepareReschedule();
+}
+
+void PhysicalCore::Shutdown() {
+ scheduler->Shutdown();
+}
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/physical_core.h b/src/core/hle/kernel/physical_core.h
new file mode 100644
index 000000000..4c32c0f1b
--- /dev/null
+++ b/src/core/hle/kernel/physical_core.h
@@ -0,0 +1,77 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <cstddef>
+#include <memory>
+
+namespace Kernel {
+class Scheduler;
+} // namespace Kernel
+
+namespace Core {
+class ARM_Interface;
+class ExclusiveMonitor;
+class System;
+} // namespace Core
+
+namespace Kernel {
+
+class PhysicalCore {
+public:
+ PhysicalCore(Core::System& system, std::size_t id, Core::ExclusiveMonitor& exclusive_monitor);
+ ~PhysicalCore();
+
+ PhysicalCore(const PhysicalCore&) = delete;
+ PhysicalCore& operator=(const PhysicalCore&) = delete;
+
+ PhysicalCore(PhysicalCore&&) = default;
+ PhysicalCore& operator=(PhysicalCore&&) = default;
+
+ /// Execute current jit state
+ void Run();
+ /// Execute a single instruction in current jit.
+ void Step();
+ /// Stop JIT execution/exit
+ void Stop();
+
+ // Shutdown this physical core.
+ void Shutdown();
+
+ Core::ARM_Interface& ArmInterface() {
+ return *arm_interface;
+ }
+
+ const Core::ARM_Interface& ArmInterface() const {
+ return *arm_interface;
+ }
+
+ bool IsMainCore() const {
+ return core_index == 0;
+ }
+
+ bool IsSystemCore() const {
+ return core_index == 3;
+ }
+
+ std::size_t CoreIndex() const {
+ return core_index;
+ }
+
+ Kernel::Scheduler& Scheduler() {
+ return *scheduler;
+ }
+
+ const Kernel::Scheduler& Scheduler() const {
+ return *scheduler;
+ }
+
+private:
+ std::size_t core_index;
+ std::unique_ptr<Core::ARM_Interface> arm_interface;
+ std::unique_ptr<Kernel::Scheduler> scheduler;
+};
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/physical_memory.h b/src/core/hle/kernel/physical_memory.h
index 090565310..b689e8e8b 100644
--- a/src/core/hle/kernel/physical_memory.h
+++ b/src/core/hle/kernel/physical_memory.h
@@ -14,6 +14,9 @@ namespace Kernel {
// - Second to ensure all host backing memory used is aligned to 256 bytes due
// to strict alignment restrictions on GPU memory.
-using PhysicalMemory = std::vector<u8, Common::AlignmentAllocator<u8, 256>>;
+using PhysicalMemoryVector = std::vector<u8, Common::AlignmentAllocator<u8, 256>>;
+class PhysicalMemory final : public PhysicalMemoryVector {
+ using PhysicalMemoryVector::PhysicalMemoryVector;
+};
} // namespace Kernel
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index 12ea4ebe3..b9035a0be 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -317,6 +317,8 @@ void Process::FreeTLSRegion(VAddr tls_address) {
}
void Process::LoadModule(CodeSet module_, VAddr base_addr) {
+ code_memory_size += module_.memory.size();
+
const auto memory = std::make_shared<PhysicalMemory>(std::move(module_.memory));
const auto MapSegment = [&](const CodeSet::Segment& segment, VMAPermission permissions,
@@ -332,8 +334,6 @@ void Process::LoadModule(CodeSet module_, VAddr base_addr) {
MapSegment(module_.CodeSegment(), VMAPermission::ReadExecute, MemoryState::Code);
MapSegment(module_.RODataSegment(), VMAPermission::Read, MemoryState::CodeData);
MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeData);
-
- code_memory_size += module_.memory.size();
}
Process::Process(Core::System& system)
diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp
index d36fcd7d9..eb196a690 100644
--- a/src/core/hle/kernel/scheduler.cpp
+++ b/src/core/hle/kernel/scheduler.cpp
@@ -14,7 +14,6 @@
#include "common/logging/log.h"
#include "core/arm/arm_interface.h"
#include "core/core.h"
-#include "core/core_cpu.h"
#include "core/core_timing.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index dbcdb0b88..1d99bf7a2 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -15,7 +15,7 @@
#include "common/string_util.h"
#include "core/arm/exclusive_monitor.h"
#include "core/core.h"
-#include "core/core_cpu.h"
+#include "core/core_manager.h"
#include "core/core_timing.h"
#include "core/core_timing_util.h"
#include "core/hle/kernel/address_arbiter.h"
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index e84e5ce0d..e965b5b04 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -13,7 +13,6 @@
#include "common/thread_queue_list.h"
#include "core/arm/arm_interface.h"
#include "core/core.h"
-#include "core/core_cpu.h"
#include "core/core_timing.h"
#include "core/core_timing_util.h"
#include "core/hle/kernel/errors.h"
@@ -356,7 +355,7 @@ void Thread::SetActivity(ThreadActivity value) {
// Set status if not waiting
if (status == ThreadStatus::Ready || status == ThreadStatus::Running) {
SetStatus(ThreadStatus::Paused);
- Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule();
+ kernel.PrepareReschedule(processor_id);
}
} else if (status == ThreadStatus::Paused) {
// Ready to reschedule
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp
index a9a20ef76..0b3500fce 100644
--- a/src/core/hle/kernel/vm_manager.cpp
+++ b/src/core/hle/kernel/vm_manager.cpp
@@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include <algorithm>
+#include <cstring>
#include <iterator>
#include <utility>
#include "common/alignment.h"
@@ -269,18 +270,9 @@ ResultVal<VAddr> VMManager::SetHeapSize(u64 size) {
// If necessary, expand backing vector to cover new heap extents in
// the case of allocating. Otherwise, shrink the backing memory,
// if a smaller heap has been requested.
- const u64 old_heap_size = GetCurrentHeapSize();
- if (size > old_heap_size) {
- const u64 alloc_size = size - old_heap_size;
-
- heap_memory->insert(heap_memory->end(), alloc_size, 0);
- RefreshMemoryBlockMappings(heap_memory.get());
- } else if (size < old_heap_size) {
- heap_memory->resize(size);
- heap_memory->shrink_to_fit();
-
- RefreshMemoryBlockMappings(heap_memory.get());
- }
+ heap_memory->resize(size);
+ heap_memory->shrink_to_fit();
+ RefreshMemoryBlockMappings(heap_memory.get());
heap_end = heap_region_base + size;
ASSERT(GetCurrentHeapSize() == heap_memory->size());
@@ -752,24 +744,20 @@ void VMManager::MergeAdjacentVMA(VirtualMemoryArea& left, const VirtualMemoryAre
// Always merge allocated memory blocks, even when they don't share the same backing block.
if (left.type == VMAType::AllocatedMemoryBlock &&
(left.backing_block != right.backing_block || left.offset + left.size != right.offset)) {
- const auto right_begin = right.backing_block->begin() + right.offset;
- const auto right_end = right_begin + right.size;
// Check if we can save work.
if (left.offset == 0 && left.size == left.backing_block->size()) {
// Fast case: left is an entire backing block.
- left.backing_block->insert(left.backing_block->end(), right_begin, right_end);
+ left.backing_block->resize(left.size + right.size);
+ std::memcpy(left.backing_block->data() + left.size,
+ right.backing_block->data() + right.offset, right.size);
} else {
// Slow case: make a new memory block for left and right.
- const auto left_begin = left.backing_block->begin() + left.offset;
- const auto left_end = left_begin + left.size;
- const auto left_size = static_cast<std::size_t>(std::distance(left_begin, left_end));
- const auto right_size = static_cast<std::size_t>(std::distance(right_begin, right_end));
-
auto new_memory = std::make_shared<PhysicalMemory>();
- new_memory->reserve(left_size + right_size);
- new_memory->insert(new_memory->end(), left_begin, left_end);
- new_memory->insert(new_memory->end(), right_begin, right_end);
+ new_memory->resize(left.size + right.size);
+ std::memcpy(new_memory->data(), left.backing_block->data() + left.offset, left.size);
+ std::memcpy(new_memory->data() + left.size, right.backing_block->data() + right.offset,
+ right.size);
left.backing_block = std::move(new_memory);
left.offset = 0;
@@ -792,8 +780,7 @@ void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) {
memory.UnmapRegion(page_table, vma.base, vma.size);
break;
case VMAType::AllocatedMemoryBlock:
- memory.MapMemoryRegion(page_table, vma.base, vma.size,
- vma.backing_block->data() + vma.offset);
+ memory.MapMemoryRegion(page_table, vma.base, vma.size, *vma.backing_block, vma.offset);
break;
case VMAType::BackingMemory:
memory.MapMemoryRegion(page_table, vma.base, vma.size, vma.backing_memory);
diff --git a/src/core/hle/kernel/wait_object.cpp b/src/core/hle/kernel/wait_object.cpp
index 745f2c4e8..a0c806e8f 100644
--- a/src/core/hle/kernel/wait_object.cpp
+++ b/src/core/hle/kernel/wait_object.cpp
@@ -7,7 +7,6 @@
#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/core.h"
-#include "core/core_cpu.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/process.h"
@@ -96,7 +95,7 @@ void WaitObject::WakeupWaitingThread(std::shared_ptr<Thread> thread) {
}
if (resume) {
thread->ResumeFromWait();
- Core::System::GetInstance().PrepareReschedule(thread->GetProcessorID());
+ kernel.PrepareReschedule(thread->GetProcessorID());
}
}
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index 7e3e311fb..cfac8ca9a 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -211,7 +211,7 @@ protected:
}
ProfileManager& profile_manager;
- Common::UUID user_id; ///< The user id this profile refers to.
+ Common::UUID user_id{Common::INVALID_UUID}; ///< The user id this profile refers to.
};
class IProfile final : public IProfileCommon {
diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp
index 3e756e59e..eb8c81645 100644
--- a/src/core/hle/service/acc/profile_manager.cpp
+++ b/src/core/hle/service/acc/profile_manager.cpp
@@ -16,17 +16,17 @@ namespace Service::Account {
using Common::UUID;
struct UserRaw {
- UUID uuid;
- UUID uuid2;
- u64 timestamp;
- ProfileUsername username;
- ProfileData extra_data;
+ UUID uuid{Common::INVALID_UUID};
+ UUID uuid2{Common::INVALID_UUID};
+ u64 timestamp{};
+ ProfileUsername username{};
+ ProfileData extra_data{};
};
static_assert(sizeof(UserRaw) == 0xC8, "UserRaw has incorrect size.");
struct ProfileDataRaw {
INSERT_PADDING_BYTES(0x10);
- std::array<UserRaw, MAX_USERS> users;
+ std::array<UserRaw, MAX_USERS> users{};
};
static_assert(sizeof(ProfileDataRaw) == 0x650, "ProfileDataRaw has incorrect size.");
@@ -238,7 +238,7 @@ UserIDArray ProfileManager::GetOpenUsers() const {
std::transform(profiles.begin(), profiles.end(), output.begin(), [](const ProfileInfo& p) {
if (p.is_open)
return p.user_uuid;
- return UUID{};
+ return UUID{Common::INVALID_UUID};
});
std::stable_partition(output.begin(), output.end(), [](const UUID& uuid) { return uuid; });
return output;
diff --git a/src/core/hle/service/acc/profile_manager.h b/src/core/hle/service/acc/profile_manager.h
index 5a6d28925..5310637a6 100644
--- a/src/core/hle/service/acc/profile_manager.h
+++ b/src/core/hle/service/acc/profile_manager.h
@@ -13,9 +13,10 @@
#include "core/hle/result.h"
namespace Service::Account {
-constexpr std::size_t MAX_USERS = 8;
-constexpr std::size_t profile_username_size = 32;
+constexpr std::size_t MAX_USERS{8};
+constexpr std::size_t profile_username_size{32};
+
using ProfileUsername = std::array<u8, profile_username_size>;
using UserIDArray = std::array<Common::UUID, MAX_USERS>;
@@ -23,8 +24,8 @@ using UserIDArray = std::array<Common::UUID, MAX_USERS>;
/// TODO: RE this structure
struct ProfileData {
INSERT_PADDING_WORDS(1);
- u32 icon_id;
- u8 bg_color_id;
+ u32 icon_id{};
+ u8 bg_color_id{};
INSERT_PADDING_BYTES(0x7);
INSERT_PADDING_BYTES(0x10);
INSERT_PADDING_BYTES(0x60);
@@ -34,17 +35,17 @@ static_assert(sizeof(ProfileData) == 0x80, "ProfileData structure has incorrect
/// This holds general information about a users profile. This is where we store all the information
/// based on a specific user
struct ProfileInfo {
- Common::UUID user_uuid;
- ProfileUsername username;
- u64 creation_time;
- ProfileData data; // TODO(ognik): Work out what this is
- bool is_open;
+ Common::UUID user_uuid{Common::INVALID_UUID};
+ ProfileUsername username{};
+ u64 creation_time{};
+ ProfileData data{}; // TODO(ognik): Work out what this is
+ bool is_open{};
};
struct ProfileBase {
- Common::UUID user_uuid;
- u64_le timestamp;
- ProfileUsername username;
+ Common::UUID user_uuid{Common::INVALID_UUID};
+ u64_le timestamp{};
+ ProfileUsername username{};
// Zero out all the fields to make the profile slot considered "Empty"
void Invalidate() {
@@ -101,7 +102,7 @@ private:
bool RemoveProfileAtIndex(std::size_t index);
std::array<ProfileInfo, MAX_USERS> profiles{};
- std::size_t user_count = 0;
+ std::size_t user_count{};
Common::UUID last_opened_user{Common::INVALID_UUID};
};
diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp
index 219176c31..6aadb3ea8 100644
--- a/src/core/hle/service/friend/friend.cpp
+++ b/src/core/hle/service/friend/friend.cpp
@@ -241,7 +241,7 @@ private:
bool has_received_friend_request;
};
- Common::UUID uuid;
+ Common::UUID uuid{Common::INVALID_UUID};
Kernel::EventPair notification_event;
std::queue<SizedNotificationInfo> notifications;
States states{};
diff --git a/src/core/hle/service/mii/mii_manager.h b/src/core/hle/service/mii/mii_manager.h
index 38ad78a0d..fc742816a 100644
--- a/src/core/hle/service/mii/mii_manager.h
+++ b/src/core/hle/service/mii/mii_manager.h
@@ -10,13 +10,13 @@
namespace Service::Mii {
-constexpr std::size_t MAX_MIIS = 100;
-constexpr u32 INVALID_INDEX = 0xFFFFFFFF;
+constexpr std::size_t MAX_MIIS{100};
+constexpr u32 INVALID_INDEX{0xFFFFFFFF};
struct RandomParameters {
- u32 unknown_1;
- u32 unknown_2;
- u32 unknown_3;
+ u32 unknown_1{};
+ u32 unknown_2{};
+ u32 unknown_3{};
};
static_assert(sizeof(RandomParameters) == 0xC, "RandomParameters has incorrect size.");
@@ -30,57 +30,57 @@ enum class Source : u32 {
std::ostream& operator<<(std::ostream& os, Source source);
struct MiiInfo {
- Common::UUID uuid;
- std::array<char16_t, 11> name;
- u8 font_region;
- u8 favorite_color;
- u8 gender;
- u8 height;
- u8 weight;
- u8 mii_type;
- u8 mii_region;
- u8 face_type;
- u8 face_color;
- u8 face_wrinkle;
- u8 face_makeup;
- u8 hair_type;
- u8 hair_color;
- bool hair_flip;
- u8 eye_type;
- u8 eye_color;
- u8 eye_scale;
- u8 eye_aspect_ratio;
- u8 eye_rotate;
- u8 eye_x;
- u8 eye_y;
- u8 eyebrow_type;
- u8 eyebrow_color;
- u8 eyebrow_scale;
- u8 eyebrow_aspect_ratio;
- u8 eyebrow_rotate;
- u8 eyebrow_x;
- u8 eyebrow_y;
- u8 nose_type;
- u8 nose_scale;
- u8 nose_y;
- u8 mouth_type;
- u8 mouth_color;
- u8 mouth_scale;
- u8 mouth_aspect_ratio;
- u8 mouth_y;
- u8 facial_hair_color;
- u8 beard_type;
- u8 mustache_type;
- u8 mustache_scale;
- u8 mustache_y;
- u8 glasses_type;
- u8 glasses_color;
- u8 glasses_scale;
- u8 glasses_y;
- u8 mole_type;
- u8 mole_scale;
- u8 mole_x;
- u8 mole_y;
+ Common::UUID uuid{Common::INVALID_UUID};
+ std::array<char16_t, 11> name{};
+ u8 font_region{};
+ u8 favorite_color{};
+ u8 gender{};
+ u8 height{};
+ u8 weight{};
+ u8 mii_type{};
+ u8 mii_region{};
+ u8 face_type{};
+ u8 face_color{};
+ u8 face_wrinkle{};
+ u8 face_makeup{};
+ u8 hair_type{};
+ u8 hair_color{};
+ bool hair_flip{};
+ u8 eye_type{};
+ u8 eye_color{};
+ u8 eye_scale{};
+ u8 eye_aspect_ratio{};
+ u8 eye_rotate{};
+ u8 eye_x{};
+ u8 eye_y{};
+ u8 eyebrow_type{};
+ u8 eyebrow_color{};
+ u8 eyebrow_scale{};
+ u8 eyebrow_aspect_ratio{};
+ u8 eyebrow_rotate{};
+ u8 eyebrow_x{};
+ u8 eyebrow_y{};
+ u8 nose_type{};
+ u8 nose_scale{};
+ u8 nose_y{};
+ u8 mouth_type{};
+ u8 mouth_color{};
+ u8 mouth_scale{};
+ u8 mouth_aspect_ratio{};
+ u8 mouth_y{};
+ u8 facial_hair_color{};
+ u8 beard_type{};
+ u8 mustache_type{};
+ u8 mustache_scale{};
+ u8 mustache_y{};
+ u8 glasses_type{};
+ u8 glasses_color{};
+ u8 glasses_scale{};
+ u8 glasses_y{};
+ u8 mole_type{};
+ u8 mole_scale{};
+ u8 mole_x{};
+ u8 mole_y{};
INSERT_PADDING_BYTES(1);
std::u16string Name() const;
@@ -94,14 +94,14 @@ bool operator!=(const MiiInfo& lhs, const MiiInfo& rhs);
#pragma pack(push, 4)
struct MiiInfoElement {
- MiiInfo info;
- Source source;
+ MiiInfo info{};
+ Source source{};
};
static_assert(sizeof(MiiInfoElement) == 0x5C, "MiiInfoElement has incorrect size.");
struct MiiStoreBitFields {
union {
- u32 word_0;
+ u32 word_0{};
BitField<24, 8, u32> hair_type;
BitField<23, 1, u32> mole_type;
@@ -112,7 +112,7 @@ struct MiiStoreBitFields {
};
union {
- u32 word_1;
+ u32 word_1{};
BitField<31, 1, u32> gender;
BitField<24, 7, u32> eye_color;
@@ -122,7 +122,7 @@ struct MiiStoreBitFields {
};
union {
- u32 word_2;
+ u32 word_2{};
BitField<31, 1, u32> mii_type;
BitField<24, 7, u32> glasses_color;
@@ -135,7 +135,7 @@ struct MiiStoreBitFields {
};
union {
- u32 word_3;
+ u32 word_3{};
BitField<29, 3, u32> mustache_type;
BitField<24, 5, u32> eyebrow_type;
@@ -148,7 +148,7 @@ struct MiiStoreBitFields {
};
union {
- u32 word_4;
+ u32 word_4{};
BitField<29, 3, u32> eye_rotate;
BitField<24, 5, u32> mustache_y;
@@ -160,7 +160,7 @@ struct MiiStoreBitFields {
};
union {
- u32 word_5;
+ u32 word_5{};
BitField<24, 5, u32> glasses_type;
BitField<20, 4, u32> face_type;
@@ -172,7 +172,7 @@ struct MiiStoreBitFields {
};
union {
- u32 word_6;
+ u32 word_6{};
BitField<28, 4, u32> eyebrow_rotate;
BitField<24, 4, u32> eyebrow_scale;
@@ -192,30 +192,30 @@ struct MiiStoreData {
// This corresponds to the above structure MiiStoreBitFields. I did it like this because the
// BitField<> type makes this (and any thing that contains it) not trivially copyable, which is
// not suitable for our uses.
- std::array<u8, 0x1C> data;
+ std::array<u8, 0x1C> data{};
static_assert(sizeof(MiiStoreBitFields) == sizeof(data), "data field has incorrect size.");
- std::array<char16_t, 10> name;
- Common::UUID uuid;
- u16 crc_1;
- u16 crc_2;
+ std::array<char16_t, 10> name{};
+ Common::UUID uuid{Common::INVALID_UUID};
+ u16 crc_1{};
+ u16 crc_2{};
std::u16string Name() const;
};
static_assert(sizeof(MiiStoreData) == 0x44, "MiiStoreData has incorrect size.");
struct MiiStoreDataElement {
- MiiStoreData data;
- Source source;
+ MiiStoreData data{};
+ Source source{};
};
static_assert(sizeof(MiiStoreDataElement) == 0x48, "MiiStoreDataElement has incorrect size.");
struct MiiDatabase {
- u32 magic; // 'NFDB'
- std::array<MiiStoreData, MAX_MIIS> miis;
+ u32 magic{}; // 'NFDB'
+ std::array<MiiStoreData, MAX_MIIS> miis{};
INSERT_PADDING_BYTES(1);
- u8 count;
- u16 crc;
+ u8 count{};
+ u16 crc{};
};
static_assert(sizeof(MiiDatabase) == 0x1A98, "MiiDatabase has incorrect size.");
#pragma pack(pop)
@@ -266,8 +266,8 @@ private:
void EnsureDatabasePartition();
MiiDatabase database;
- bool updated_flag = false;
- bool is_test_mode_enabled = false;
+ bool updated_flag{};
+ bool is_test_mode_enabled{};
};
}; // namespace Service::Mii
diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp
index 884ad173b..f67fab2f9 100644
--- a/src/core/hle/service/sockets/bsd.cpp
+++ b/src/core/hle/service/sockets/bsd.cpp
@@ -42,6 +42,26 @@ void BSD::Socket(Kernel::HLERequestContext& ctx) {
rb.Push<u32>(0); // bsd errno
}
+void BSD::Select(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 4};
+
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<u32>(0); // ret
+ rb.Push<u32>(0); // bsd errno
+}
+
+void BSD::Bind(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 4};
+
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<u32>(0); // ret
+ rb.Push<u32>(0); // bsd errno
+}
+
void BSD::Connect(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called");
@@ -52,6 +72,26 @@ void BSD::Connect(Kernel::HLERequestContext& ctx) {
rb.Push<u32>(0); // bsd errno
}
+void BSD::Listen(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 4};
+
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<u32>(0); // ret
+ rb.Push<u32>(0); // bsd errno
+}
+
+void BSD::SetSockOpt(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 4};
+
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<u32>(0); // ret
+ rb.Push<u32>(0); // bsd errno
+}
+
void BSD::SendTo(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called");
@@ -80,7 +120,7 @@ BSD::BSD(const char* name) : ServiceFramework(name) {
{2, &BSD::Socket, "Socket"},
{3, nullptr, "SocketExempt"},
{4, nullptr, "Open"},
- {5, nullptr, "Select"},
+ {5, &BSD::Select, "Select"},
{6, nullptr, "Poll"},
{7, nullptr, "Sysctl"},
{8, nullptr, "Recv"},
@@ -88,15 +128,15 @@ BSD::BSD(const char* name) : ServiceFramework(name) {
{10, nullptr, "Send"},
{11, &BSD::SendTo, "SendTo"},
{12, nullptr, "Accept"},
- {13, nullptr, "Bind"},
+ {13, &BSD::Bind, "Bind"},
{14, &BSD::Connect, "Connect"},
{15, nullptr, "GetPeerName"},
{16, nullptr, "GetSockName"},
{17, nullptr, "GetSockOpt"},
- {18, nullptr, "Listen"},
+ {18, &BSD::Listen, "Listen"},
{19, nullptr, "Ioctl"},
{20, nullptr, "Fcntl"},
- {21, nullptr, "SetSockOpt"},
+ {21, &BSD::SetSockOpt, "SetSockOpt"},
{22, nullptr, "Shutdown"},
{23, nullptr, "ShutdownAllSockets"},
{24, nullptr, "Write"},
diff --git a/src/core/hle/service/sockets/bsd.h b/src/core/hle/service/sockets/bsd.h
index 0fe0e65c6..3098e3baf 100644
--- a/src/core/hle/service/sockets/bsd.h
+++ b/src/core/hle/service/sockets/bsd.h
@@ -18,7 +18,11 @@ private:
void RegisterClient(Kernel::HLERequestContext& ctx);
void StartMonitoring(Kernel::HLERequestContext& ctx);
void Socket(Kernel::HLERequestContext& ctx);
+ void Select(Kernel::HLERequestContext& ctx);
+ void Bind(Kernel::HLERequestContext& ctx);
void Connect(Kernel::HLERequestContext& ctx);
+ void Listen(Kernel::HLERequestContext& ctx);
+ void SetSockOpt(Kernel::HLERequestContext& ctx);
void SendTo(Kernel::HLERequestContext& ctx);
void Close(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/time/clock_types.h b/src/core/hle/service/time/clock_types.h
new file mode 100644
index 000000000..72e1921ec
--- /dev/null
+++ b/src/core/hle/service/time/clock_types.h
@@ -0,0 +1,103 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+#include "common/uuid.h"
+#include "core/hle/service/time/errors.h"
+#include "core/hle/service/time/time_zone_types.h"
+
+namespace Service::Time::Clock {
+
+/// https://switchbrew.org/wiki/Glue_services#SteadyClockTimePoint
+struct SteadyClockTimePoint {
+ s64 time_point;
+ Common::UUID clock_source_id;
+
+ ResultCode GetSpanBetween(SteadyClockTimePoint other, s64& span) const {
+ span = 0;
+
+ if (clock_source_id != other.clock_source_id) {
+ return ERROR_TIME_MISMATCH;
+ }
+
+ span = other.time_point - time_point;
+
+ return RESULT_SUCCESS;
+ }
+
+ static SteadyClockTimePoint GetRandom() {
+ return {0, Common::UUID::Generate()};
+ }
+};
+static_assert(sizeof(SteadyClockTimePoint) == 0x18, "SteadyClockTimePoint is incorrect size");
+static_assert(std::is_trivially_copyable_v<SteadyClockTimePoint>,
+ "SteadyClockTimePoint must be trivially copyable");
+
+struct SteadyClockContext {
+ u64 internal_offset;
+ Common::UUID steady_time_point;
+};
+static_assert(sizeof(SteadyClockContext) == 0x18, "SteadyClockContext is incorrect size");
+static_assert(std::is_trivially_copyable_v<SteadyClockContext>,
+ "SteadyClockContext must be trivially copyable");
+
+struct SystemClockContext {
+ s64 offset;
+ SteadyClockTimePoint steady_time_point;
+};
+static_assert(sizeof(SystemClockContext) == 0x20, "SystemClockContext is incorrect size");
+static_assert(std::is_trivially_copyable_v<SystemClockContext>,
+ "SystemClockContext must be trivially copyable");
+
+/// https://switchbrew.org/wiki/Glue_services#TimeSpanType
+struct TimeSpanType {
+ s64 nanoseconds{};
+ static constexpr s64 ns_per_second{1000000000ULL};
+
+ s64 ToSeconds() const {
+ return nanoseconds / ns_per_second;
+ }
+
+ static TimeSpanType FromSeconds(s64 seconds) {
+ return {seconds * ns_per_second};
+ }
+
+ static TimeSpanType FromTicks(u64 ticks, u64 frequency) {
+ return FromSeconds(static_cast<s64>(ticks) / static_cast<s64>(frequency));
+ }
+};
+static_assert(sizeof(TimeSpanType) == 8, "TimeSpanType is incorrect size");
+
+struct ClockSnapshot {
+ SystemClockContext user_context{};
+ SystemClockContext network_context{};
+ s64 user_time{};
+ s64 network_time{};
+ TimeZone::CalendarTime user_calendar_time{};
+ TimeZone::CalendarTime network_calendar_time{};
+ TimeZone::CalendarAdditionalInfo user_calendar_additional_time{};
+ TimeZone::CalendarAdditionalInfo network_calendar_additional_time{};
+ SteadyClockTimePoint steady_clock_time_point{};
+ TimeZone::LocationName location_name{};
+ u8 is_automatic_correction_enabled{};
+ u8 type{};
+ INSERT_PADDING_BYTES(0x2);
+
+ static ResultCode GetCurrentTime(s64& current_time,
+ const SteadyClockTimePoint& steady_clock_time_point,
+ const SystemClockContext& context) {
+ if (steady_clock_time_point.clock_source_id != context.steady_time_point.clock_source_id) {
+ current_time = 0;
+ return ERROR_TIME_MISMATCH;
+ }
+ current_time = steady_clock_time_point.time_point + context.offset;
+ return RESULT_SUCCESS;
+ }
+};
+static_assert(sizeof(ClockSnapshot) == 0xD0, "ClockSnapshot is incorrect size");
+
+} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/ephemeral_network_system_clock_context_writer.h b/src/core/hle/service/time/ephemeral_network_system_clock_context_writer.h
new file mode 100644
index 000000000..42893e3f6
--- /dev/null
+++ b/src/core/hle/service/time/ephemeral_network_system_clock_context_writer.h
@@ -0,0 +1,16 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/time/system_clock_context_update_callback.h"
+
+namespace Service::Time::Clock {
+
+class EphemeralNetworkSystemClockContextWriter final : public SystemClockContextUpdateCallback {
+public:
+ EphemeralNetworkSystemClockContextWriter() : SystemClockContextUpdateCallback{} {}
+};
+
+} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/ephemeral_network_system_clock_core.h b/src/core/hle/service/time/ephemeral_network_system_clock_core.h
new file mode 100644
index 000000000..4c6cdef86
--- /dev/null
+++ b/src/core/hle/service/time/ephemeral_network_system_clock_core.h
@@ -0,0 +1,17 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/time/system_clock_core.h"
+
+namespace Service::Time::Clock {
+
+class EphemeralNetworkSystemClockCore final : public SystemClockCore {
+public:
+ explicit EphemeralNetworkSystemClockCore(SteadyClockCore& steady_clock_core)
+ : SystemClockCore{steady_clock_core} {}
+};
+
+} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/errors.h b/src/core/hle/service/time/errors.h
new file mode 100644
index 000000000..8501a3e8c
--- /dev/null
+++ b/src/core/hle/service/time/errors.h
@@ -0,0 +1,22 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/result.h"
+
+namespace Service::Time {
+
+constexpr ResultCode ERROR_PERMISSION_DENIED{ErrorModule::Time, 1};
+constexpr ResultCode ERROR_TIME_MISMATCH{ErrorModule::Time, 102};
+constexpr ResultCode ERROR_UNINITIALIZED_CLOCK{ErrorModule::Time, 103};
+constexpr ResultCode ERROR_TIME_NOT_FOUND{ErrorModule::Time, 200};
+constexpr ResultCode ERROR_OVERFLOW{ErrorModule::Time, 201};
+constexpr ResultCode ERROR_LOCATION_NAME_TOO_LONG{ErrorModule::Time, 801};
+constexpr ResultCode ERROR_OUT_OF_RANGE{ErrorModule::Time, 902};
+constexpr ResultCode ERROR_TIME_ZONE_CONVERSION_FAILED{ErrorModule::Time, 903};
+constexpr ResultCode ERROR_TIME_ZONE_NOT_FOUND{ErrorModule::Time, 989};
+constexpr ResultCode ERROR_NOT_IMPLEMENTED{ErrorModule::Time, 990};
+
+} // namespace Service::Time
diff --git a/src/core/hle/service/time/interface.cpp b/src/core/hle/service/time/interface.cpp
index bc74f1e1d..1660bbdb8 100644
--- a/src/core/hle/service/time/interface.cpp
+++ b/src/core/hle/service/time/interface.cpp
@@ -1,4 +1,4 @@
-// Copyright 2018 yuzu emulator team
+// Copyright 2019 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@@ -6,9 +6,8 @@
namespace Service::Time {
-Time::Time(std::shared_ptr<Module> time, std::shared_ptr<SharedMemory> shared_memory,
- Core::System& system, const char* name)
- : Module::Interface(std::move(time), std::move(shared_memory), system, name) {
+Time::Time(std::shared_ptr<Module> module, Core::System& system, const char* name)
+ : Module::Interface(std::move(module), system, name) {
// clang-format off
static const FunctionInfo functions[] = {
{0, &Time::GetStandardUserSystemClock, "GetStandardUserSystemClock"},
@@ -22,15 +21,15 @@ Time::Time(std::shared_ptr<Module> time, std::shared_ptr<SharedMemory> shared_me
{31, nullptr, "GetEphemeralNetworkClockOperationEventReadableHandle"},
{50, nullptr, "SetStandardSteadyClockInternalOffset"},
{51, nullptr, "GetStandardSteadyClockRtcValue"},
- {100, &Time::IsStandardUserSystemClockAutomaticCorrectionEnabled, "IsStandardUserSystemClockAutomaticCorrectionEnabled"},
- {101, &Time::SetStandardUserSystemClockAutomaticCorrectionEnabled, "SetStandardUserSystemClockAutomaticCorrectionEnabled"},
+ {100, nullptr, "IsStandardUserSystemClockAutomaticCorrectionEnabled"},
+ {101, nullptr, "SetStandardUserSystemClockAutomaticCorrectionEnabled"},
{102, nullptr, "GetStandardUserSystemClockInitialYear"},
- {200, nullptr, "IsStandardNetworkSystemClockAccuracySufficient"},
+ {200, &Time::IsStandardNetworkSystemClockAccuracySufficient, "IsStandardNetworkSystemClockAccuracySufficient"},
{201, nullptr, "GetStandardUserSystemClockAutomaticCorrectionUpdatedTime"},
- {300, nullptr, "CalculateMonotonicSystemClockBaseTimePoint"},
+ {300, &Time::CalculateMonotonicSystemClockBaseTimePoint, "CalculateMonotonicSystemClockBaseTimePoint"},
{400, &Time::GetClockSnapshot, "GetClockSnapshot"},
- {401, nullptr, "GetClockSnapshotFromSystemClockContext"},
- {500, &Time::CalculateStandardUserSystemClockDifferenceByUser, "CalculateStandardUserSystemClockDifferenceByUser"},
+ {401, &Time::GetClockSnapshotFromSystemClockContext, "GetClockSnapshotFromSystemClockContext"},
+ {500, nullptr, "CalculateStandardUserSystemClockDifferenceByUser"},
{501, nullptr, "CalculateSpanBetween"},
};
// clang-format on
diff --git a/src/core/hle/service/time/interface.h b/src/core/hle/service/time/interface.h
index 5c63a07f4..4f49e1f07 100644
--- a/src/core/hle/service/time/interface.h
+++ b/src/core/hle/service/time/interface.h
@@ -1,4 +1,4 @@
-// Copyright 2018 yuzu emulator team
+// Copyright 2019 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@@ -6,14 +6,15 @@
#include "core/hle/service/time/time.h"
-namespace Service::Time {
+namespace Core {
+class System;
+}
-class SharedMemory;
+namespace Service::Time {
class Time final : public Module::Interface {
public:
- explicit Time(std::shared_ptr<Module> time, std::shared_ptr<SharedMemory> shared_memory,
- Core::System& system, const char* name);
+ explicit Time(std::shared_ptr<Module> time, Core::System& system, const char* name);
~Time() override;
};
diff --git a/src/core/hle/service/time/local_system_clock_context_writer.h b/src/core/hle/service/time/local_system_clock_context_writer.h
new file mode 100644
index 000000000..7050844c6
--- /dev/null
+++ b/src/core/hle/service/time/local_system_clock_context_writer.h
@@ -0,0 +1,28 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/time/errors.h"
+#include "core/hle/service/time/system_clock_context_update_callback.h"
+#include "core/hle/service/time/time_sharedmemory.h"
+
+namespace Service::Time::Clock {
+
+class LocalSystemClockContextWriter final : public SystemClockContextUpdateCallback {
+public:
+ explicit LocalSystemClockContextWriter(SharedMemory& shared_memory)
+ : SystemClockContextUpdateCallback{}, shared_memory{shared_memory} {}
+
+protected:
+ ResultCode Update() override {
+ shared_memory.UpdateLocalSystemClockContext(context);
+ return RESULT_SUCCESS;
+ }
+
+private:
+ SharedMemory& shared_memory;
+};
+
+} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/network_system_clock_context_writer.h b/src/core/hle/service/time/network_system_clock_context_writer.h
new file mode 100644
index 000000000..94d8788ff
--- /dev/null
+++ b/src/core/hle/service/time/network_system_clock_context_writer.h
@@ -0,0 +1,28 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/time/errors.h"
+#include "core/hle/service/time/system_clock_context_update_callback.h"
+#include "core/hle/service/time/time_sharedmemory.h"
+
+namespace Service::Time::Clock {
+
+class NetworkSystemClockContextWriter final : public SystemClockContextUpdateCallback {
+public:
+ explicit NetworkSystemClockContextWriter(SharedMemory& shared_memory)
+ : SystemClockContextUpdateCallback{}, shared_memory{shared_memory} {}
+
+protected:
+ ResultCode Update() override {
+ shared_memory.UpdateNetworkSystemClockContext(context);
+ return RESULT_SUCCESS;
+ }
+
+private:
+ SharedMemory& shared_memory;
+};
+
+} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/standard_local_system_clock_core.h b/src/core/hle/service/time/standard_local_system_clock_core.h
new file mode 100644
index 000000000..8c1882eb1
--- /dev/null
+++ b/src/core/hle/service/time/standard_local_system_clock_core.h
@@ -0,0 +1,17 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/time/system_clock_core.h"
+
+namespace Service::Time::Clock {
+
+class StandardLocalSystemClockCore final : public SystemClockCore {
+public:
+ explicit StandardLocalSystemClockCore(SteadyClockCore& steady_clock_core)
+ : SystemClockCore{steady_clock_core} {}
+};
+
+} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/standard_network_system_clock_core.h b/src/core/hle/service/time/standard_network_system_clock_core.h
new file mode 100644
index 000000000..3f505c37c
--- /dev/null
+++ b/src/core/hle/service/time/standard_network_system_clock_core.h
@@ -0,0 +1,46 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/time/clock_types.h"
+#include "core/hle/service/time/steady_clock_core.h"
+#include "core/hle/service/time/system_clock_core.h"
+
+namespace Core {
+class System;
+}
+
+namespace Service::Time::Clock {
+
+class StandardNetworkSystemClockCore final : public SystemClockCore {
+public:
+ explicit StandardNetworkSystemClockCore(SteadyClockCore& steady_clock_core)
+ : SystemClockCore{steady_clock_core} {}
+
+ void SetStandardNetworkClockSufficientAccuracy(TimeSpanType value) {
+ standard_network_clock_sufficient_accuracy = value;
+ }
+
+ bool IsStandardNetworkSystemClockAccuracySufficient(Core::System& system) {
+ SystemClockContext context{};
+ if (GetClockContext(system, context) != RESULT_SUCCESS) {
+ return {};
+ }
+
+ s64 span{};
+ if (context.steady_time_point.GetSpanBetween(
+ GetSteadyClockCore().GetCurrentTimePoint(system), span) != RESULT_SUCCESS) {
+ return {};
+ }
+
+ return TimeSpanType{span}.nanoseconds <
+ standard_network_clock_sufficient_accuracy.nanoseconds;
+ }
+
+private:
+ TimeSpanType standard_network_clock_sufficient_accuracy{};
+};
+
+} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/standard_steady_clock_core.cpp b/src/core/hle/service/time/standard_steady_clock_core.cpp
new file mode 100644
index 000000000..ca1a783fc
--- /dev/null
+++ b/src/core/hle/service/time/standard_steady_clock_core.cpp
@@ -0,0 +1,26 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/core.h"
+#include "core/core_timing.h"
+#include "core/core_timing_util.h"
+#include "core/hle/service/time/standard_steady_clock_core.h"
+
+namespace Service::Time::Clock {
+
+TimeSpanType StandardSteadyClockCore::GetCurrentRawTimePoint(Core::System& system) {
+ const TimeSpanType ticks_time_span{TimeSpanType::FromTicks(
+ Core::Timing::CpuCyclesToClockCycles(system.CoreTiming().GetTicks()),
+ Core::Timing::CNTFREQ)};
+ TimeSpanType raw_time_point{setup_value.nanoseconds + ticks_time_span.nanoseconds};
+
+ if (raw_time_point.nanoseconds < cached_raw_time_point.nanoseconds) {
+ raw_time_point.nanoseconds = cached_raw_time_point.nanoseconds;
+ }
+
+ cached_raw_time_point = raw_time_point;
+ return raw_time_point;
+}
+
+} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/standard_steady_clock_core.h b/src/core/hle/service/time/standard_steady_clock_core.h
new file mode 100644
index 000000000..f56f3fd95
--- /dev/null
+++ b/src/core/hle/service/time/standard_steady_clock_core.h
@@ -0,0 +1,42 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/time/clock_types.h"
+#include "core/hle/service/time/steady_clock_core.h"
+
+namespace Core {
+class System;
+}
+
+namespace Service::Time::Clock {
+
+class StandardSteadyClockCore final : public SteadyClockCore {
+public:
+ SteadyClockTimePoint GetTimePoint(Core::System& system) override {
+ return {GetCurrentRawTimePoint(system).ToSeconds(), GetClockSourceId()};
+ }
+
+ TimeSpanType GetInternalOffset() const override {
+ return internal_offset;
+ }
+
+ void SetInternalOffset(TimeSpanType value) override {
+ internal_offset = value;
+ }
+
+ TimeSpanType GetCurrentRawTimePoint(Core::System& system) override;
+
+ void SetSetupValue(TimeSpanType value) {
+ setup_value = value;
+ }
+
+private:
+ TimeSpanType setup_value{};
+ TimeSpanType internal_offset{};
+ TimeSpanType cached_raw_time_point{};
+};
+
+} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/standard_user_system_clock_core.cpp b/src/core/hle/service/time/standard_user_system_clock_core.cpp
new file mode 100644
index 000000000..8af17091c
--- /dev/null
+++ b/src/core/hle/service/time/standard_user_system_clock_core.cpp
@@ -0,0 +1,77 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/assert.h"
+#include "core/core.h"
+#include "core/hle/kernel/writable_event.h"
+#include "core/hle/service/time/standard_local_system_clock_core.h"
+#include "core/hle/service/time/standard_network_system_clock_core.h"
+#include "core/hle/service/time/standard_user_system_clock_core.h"
+
+namespace Service::Time::Clock {
+
+StandardUserSystemClockCore::StandardUserSystemClockCore(
+ StandardLocalSystemClockCore& local_system_clock_core,
+ StandardNetworkSystemClockCore& network_system_clock_core, Core::System& system)
+ : SystemClockCore(local_system_clock_core.GetSteadyClockCore()),
+ local_system_clock_core{local_system_clock_core},
+ network_system_clock_core{network_system_clock_core}, auto_correction_enabled{},
+ auto_correction_time{SteadyClockTimePoint::GetRandom()},
+ auto_correction_event{Kernel::WritableEvent::CreateEventPair(
+ system.Kernel(), "StandardUserSystemClockCore:AutoCorrectionEvent")} {}
+
+ResultCode StandardUserSystemClockCore::SetAutomaticCorrectionEnabled(Core::System& system,
+ bool value) {
+ if (const ResultCode result{ApplyAutomaticCorrection(system, value)};
+ result != RESULT_SUCCESS) {
+ return result;
+ }
+
+ auto_correction_enabled = value;
+
+ return RESULT_SUCCESS;
+}
+
+ResultCode StandardUserSystemClockCore::GetClockContext(Core::System& system,
+ SystemClockContext& context) const {
+ if (const ResultCode result{ApplyAutomaticCorrection(system, false)};
+ result != RESULT_SUCCESS) {
+ return result;
+ }
+
+ return local_system_clock_core.GetClockContext(system, context);
+}
+
+ResultCode StandardUserSystemClockCore::Flush(const SystemClockContext& context) {
+ UNREACHABLE();
+ return ERROR_NOT_IMPLEMENTED;
+}
+
+ResultCode StandardUserSystemClockCore::SetClockContext(const SystemClockContext& context) {
+ UNREACHABLE();
+ return ERROR_NOT_IMPLEMENTED;
+}
+
+ResultCode StandardUserSystemClockCore::ApplyAutomaticCorrection(Core::System& system,
+ bool value) const {
+ if (auto_correction_enabled == value) {
+ return RESULT_SUCCESS;
+ }
+
+ if (!network_system_clock_core.IsClockSetup(system)) {
+ return ERROR_UNINITIALIZED_CLOCK;
+ }
+
+ SystemClockContext context{};
+ if (const ResultCode result{network_system_clock_core.GetClockContext(system, context)};
+ result != RESULT_SUCCESS) {
+ return result;
+ }
+
+ local_system_clock_core.SetClockContext(context);
+
+ return RESULT_SUCCESS;
+}
+
+} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/standard_user_system_clock_core.h b/src/core/hle/service/time/standard_user_system_clock_core.h
new file mode 100644
index 000000000..ef3d468b7
--- /dev/null
+++ b/src/core/hle/service/time/standard_user_system_clock_core.h
@@ -0,0 +1,57 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/kernel/writable_event.h"
+#include "core/hle/service/time/clock_types.h"
+#include "core/hle/service/time/system_clock_core.h"
+
+namespace Core {
+class System;
+}
+
+namespace Service::Time::Clock {
+
+class StandardLocalSystemClockCore;
+class StandardNetworkSystemClockCore;
+
+class StandardUserSystemClockCore final : public SystemClockCore {
+public:
+ StandardUserSystemClockCore(StandardLocalSystemClockCore& local_system_clock_core,
+ StandardNetworkSystemClockCore& network_system_clock_core,
+ Core::System& system);
+
+ ResultCode SetAutomaticCorrectionEnabled(Core::System& system, bool value);
+
+ ResultCode GetClockContext(Core::System& system, SystemClockContext& context) const override;
+
+ bool IsAutomaticCorrectionEnabled() const {
+ return auto_correction_enabled;
+ }
+
+ void SetAutomaticCorrectionUpdatedTime(SteadyClockTimePoint steady_clock_time_point) {
+ auto_correction_time = steady_clock_time_point;
+ }
+
+protected:
+ ResultCode Flush(const SystemClockContext& context) override;
+
+ ResultCode SetClockContext(const SystemClockContext&) override;
+
+ ResultCode ApplyAutomaticCorrection(Core::System& system, bool value) const;
+
+ const SteadyClockTimePoint& GetAutomaticCorrectionUpdatedTime() const {
+ return auto_correction_time;
+ }
+
+private:
+ StandardLocalSystemClockCore& local_system_clock_core;
+ StandardNetworkSystemClockCore& network_system_clock_core;
+ bool auto_correction_enabled{};
+ SteadyClockTimePoint auto_correction_time;
+ Kernel::EventPair auto_correction_event;
+};
+
+} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/steady_clock_core.h b/src/core/hle/service/time/steady_clock_core.h
new file mode 100644
index 000000000..84af3d105
--- /dev/null
+++ b/src/core/hle/service/time/steady_clock_core.h
@@ -0,0 +1,55 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/uuid.h"
+#include "core/hle/service/time/clock_types.h"
+
+namespace Core {
+class System;
+}
+
+namespace Service::Time::Clock {
+
+class SteadyClockCore {
+public:
+ SteadyClockCore() = default;
+
+ const Common::UUID& GetClockSourceId() const {
+ return clock_source_id;
+ }
+
+ void SetClockSourceId(const Common::UUID& value) {
+ clock_source_id = value;
+ }
+
+ virtual TimeSpanType GetInternalOffset() const = 0;
+
+ virtual void SetInternalOffset(TimeSpanType internal_offset) = 0;
+
+ virtual SteadyClockTimePoint GetTimePoint(Core::System& system) = 0;
+
+ virtual TimeSpanType GetCurrentRawTimePoint(Core::System& system) = 0;
+
+ SteadyClockTimePoint GetCurrentTimePoint(Core::System& system) {
+ SteadyClockTimePoint result{GetTimePoint(system)};
+ result.time_point += GetInternalOffset().ToSeconds();
+ return result;
+ }
+
+ bool IsInitialized() const {
+ return is_initialized;
+ }
+
+ void MarkAsInitialized() {
+ is_initialized = true;
+ }
+
+private:
+ Common::UUID clock_source_id{Common::UUID::Generate()};
+ bool is_initialized{};
+};
+
+} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/system_clock_context_update_callback.cpp b/src/core/hle/service/time/system_clock_context_update_callback.cpp
new file mode 100644
index 000000000..5cdb80703
--- /dev/null
+++ b/src/core/hle/service/time/system_clock_context_update_callback.cpp
@@ -0,0 +1,55 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/kernel/writable_event.h"
+#include "core/hle/service/time/errors.h"
+#include "core/hle/service/time/system_clock_context_update_callback.h"
+
+namespace Service::Time::Clock {
+
+SystemClockContextUpdateCallback::SystemClockContextUpdateCallback() = default;
+SystemClockContextUpdateCallback::~SystemClockContextUpdateCallback() = default;
+
+bool SystemClockContextUpdateCallback::NeedUpdate(const SystemClockContext& value) const {
+ if (has_context) {
+ return context.offset != value.offset ||
+ context.steady_time_point.clock_source_id != value.steady_time_point.clock_source_id;
+ }
+
+ return true;
+}
+
+void SystemClockContextUpdateCallback::RegisterOperationEvent(
+ std::shared_ptr<Kernel::WritableEvent>&& writable_event) {
+ operation_event_list.emplace_back(std::move(writable_event));
+}
+
+void SystemClockContextUpdateCallback::BroadcastOperationEvent() {
+ for (const auto& writable_event : operation_event_list) {
+ writable_event->Signal();
+ }
+}
+
+ResultCode SystemClockContextUpdateCallback::Update(const SystemClockContext& value) {
+ ResultCode result{RESULT_SUCCESS};
+
+ if (NeedUpdate(value)) {
+ context = value;
+ has_context = true;
+
+ result = Update();
+
+ if (result == RESULT_SUCCESS) {
+ BroadcastOperationEvent();
+ }
+ }
+
+ return result;
+}
+
+ResultCode SystemClockContextUpdateCallback::Update() {
+ return RESULT_SUCCESS;
+}
+
+} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/system_clock_context_update_callback.h b/src/core/hle/service/time/system_clock_context_update_callback.h
new file mode 100644
index 000000000..6260de6c3
--- /dev/null
+++ b/src/core/hle/service/time/system_clock_context_update_callback.h
@@ -0,0 +1,43 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <vector>
+
+#include "core/hle/service/time/clock_types.h"
+
+namespace Kernel {
+class WritableEvent;
+}
+
+namespace Service::Time::Clock {
+
+// Parts of this implementation were based on Ryujinx (https://github.com/Ryujinx/Ryujinx/pull/783).
+// This code was released under public domain.
+
+class SystemClockContextUpdateCallback {
+public:
+ SystemClockContextUpdateCallback();
+ ~SystemClockContextUpdateCallback();
+
+ bool NeedUpdate(const SystemClockContext& value) const;
+
+ void RegisterOperationEvent(std::shared_ptr<Kernel::WritableEvent>&& writable_event);
+
+ void BroadcastOperationEvent();
+
+ ResultCode Update(const SystemClockContext& value);
+
+protected:
+ virtual ResultCode Update();
+
+ SystemClockContext context{};
+
+private:
+ bool has_context{};
+ std::vector<std::shared_ptr<Kernel::WritableEvent>> operation_event_list;
+};
+
+} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/system_clock_core.cpp b/src/core/hle/service/time/system_clock_core.cpp
new file mode 100644
index 000000000..1a3ab8cfa
--- /dev/null
+++ b/src/core/hle/service/time/system_clock_core.cpp
@@ -0,0 +1,72 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/service/time/steady_clock_core.h"
+#include "core/hle/service/time/system_clock_context_update_callback.h"
+#include "core/hle/service/time/system_clock_core.h"
+
+namespace Service::Time::Clock {
+
+SystemClockCore::SystemClockCore(SteadyClockCore& steady_clock_core)
+ : steady_clock_core{steady_clock_core}, is_initialized{} {
+ context.steady_time_point.clock_source_id = steady_clock_core.GetClockSourceId();
+}
+
+SystemClockCore ::~SystemClockCore() = default;
+
+ResultCode SystemClockCore::GetCurrentTime(Core::System& system, s64& posix_time) const {
+ posix_time = 0;
+
+ const SteadyClockTimePoint current_time_point{steady_clock_core.GetCurrentTimePoint(system)};
+
+ SystemClockContext clock_context{};
+ if (const ResultCode result{GetClockContext(system, clock_context)}; result != RESULT_SUCCESS) {
+ return result;
+ }
+
+ if (current_time_point.clock_source_id != clock_context.steady_time_point.clock_source_id) {
+ return ERROR_TIME_MISMATCH;
+ }
+
+ posix_time = clock_context.offset + current_time_point.time_point;
+
+ return RESULT_SUCCESS;
+}
+
+ResultCode SystemClockCore::SetCurrentTime(Core::System& system, s64 posix_time) {
+ const SteadyClockTimePoint current_time_point{steady_clock_core.GetCurrentTimePoint(system)};
+ const SystemClockContext clock_context{posix_time - current_time_point.time_point,
+ current_time_point};
+
+ if (const ResultCode result{SetClockContext(clock_context)}; result != RESULT_SUCCESS) {
+ return result;
+ }
+ return Flush(clock_context);
+}
+
+ResultCode SystemClockCore::Flush(const SystemClockContext& context) {
+ if (!system_clock_context_update_callback) {
+ return RESULT_SUCCESS;
+ }
+ return system_clock_context_update_callback->Update(context);
+}
+
+ResultCode SystemClockCore::SetSystemClockContext(const SystemClockContext& context) {
+ if (const ResultCode result{SetClockContext(context)}; result != RESULT_SUCCESS) {
+ return result;
+ }
+ return Flush(context);
+}
+
+bool SystemClockCore::IsClockSetup(Core::System& system) const {
+ SystemClockContext value{};
+ if (GetClockContext(system, value) == RESULT_SUCCESS) {
+ const SteadyClockTimePoint steady_clock_time_point{
+ steady_clock_core.GetCurrentTimePoint(system)};
+ return steady_clock_time_point.clock_source_id == value.steady_time_point.clock_source_id;
+ }
+ return {};
+}
+
+} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/system_clock_core.h b/src/core/hle/service/time/system_clock_core.h
new file mode 100644
index 000000000..54407a6c5
--- /dev/null
+++ b/src/core/hle/service/time/system_clock_core.h
@@ -0,0 +1,71 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+#include "core/hle/service/time/clock_types.h"
+
+namespace Core {
+class System;
+}
+
+namespace Service::Time::Clock {
+
+class SteadyClockCore;
+class SystemClockContextUpdateCallback;
+
+// Parts of this implementation were based on Ryujinx (https://github.com/Ryujinx/Ryujinx/pull/783).
+// This code was released under public domain.
+
+class SystemClockCore {
+public:
+ explicit SystemClockCore(SteadyClockCore& steady_clock_core);
+ ~SystemClockCore();
+
+ SteadyClockCore& GetSteadyClockCore() const {
+ return steady_clock_core;
+ }
+
+ ResultCode GetCurrentTime(Core::System& system, s64& posix_time) const;
+
+ ResultCode SetCurrentTime(Core::System& system, s64 posix_time);
+
+ virtual ResultCode GetClockContext([[maybe_unused]] Core::System& system,
+ SystemClockContext& value) const {
+ value = context;
+ return RESULT_SUCCESS;
+ }
+
+ virtual ResultCode SetClockContext(const SystemClockContext& value) {
+ context = value;
+ return RESULT_SUCCESS;
+ }
+
+ virtual ResultCode Flush(const SystemClockContext& context);
+
+ void SetUpdateCallbackInstance(std::shared_ptr<SystemClockContextUpdateCallback> callback) {
+ system_clock_context_update_callback = std::move(callback);
+ }
+
+ ResultCode SetSystemClockContext(const SystemClockContext& context);
+
+ bool IsInitialized() const {
+ return is_initialized;
+ }
+
+ void MarkAsInitialized() {
+ is_initialized = true;
+ }
+
+ bool IsClockSetup(Core::System& system) const;
+
+private:
+ SteadyClockCore& steady_clock_core;
+ SystemClockContext context{};
+ bool is_initialized{};
+ std::shared_ptr<SystemClockContextUpdateCallback> system_clock_context_update_callback;
+};
+
+} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/tick_based_steady_clock_core.cpp b/src/core/hle/service/time/tick_based_steady_clock_core.cpp
new file mode 100644
index 000000000..c77b98189
--- /dev/null
+++ b/src/core/hle/service/time/tick_based_steady_clock_core.cpp
@@ -0,0 +1,24 @@
+// Copyright 2020 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/core.h"
+#include "core/core_timing.h"
+#include "core/core_timing_util.h"
+#include "core/hle/service/time/tick_based_steady_clock_core.h"
+
+namespace Service::Time::Clock {
+
+SteadyClockTimePoint TickBasedSteadyClockCore::GetTimePoint(Core::System& system) {
+ const TimeSpanType ticks_time_span{TimeSpanType::FromTicks(
+ Core::Timing::CpuCyclesToClockCycles(system.CoreTiming().GetTicks()),
+ Core::Timing::CNTFREQ)};
+
+ return {ticks_time_span.ToSeconds(), GetClockSourceId()};
+}
+
+TimeSpanType TickBasedSteadyClockCore::GetCurrentRawTimePoint(Core::System& system) {
+ return TimeSpanType::FromSeconds(GetTimePoint(system).time_point);
+}
+
+} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/tick_based_steady_clock_core.h b/src/core/hle/service/time/tick_based_steady_clock_core.h
new file mode 100644
index 000000000..1a5a53fd7
--- /dev/null
+++ b/src/core/hle/service/time/tick_based_steady_clock_core.h
@@ -0,0 +1,29 @@
+// Copyright 2020 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/time/clock_types.h"
+#include "core/hle/service/time/steady_clock_core.h"
+
+namespace Core {
+class System;
+}
+
+namespace Service::Time::Clock {
+
+class TickBasedSteadyClockCore final : public SteadyClockCore {
+public:
+ TimeSpanType GetInternalOffset() const override {
+ return {};
+ }
+
+ void SetInternalOffset(TimeSpanType internal_offset) override {}
+
+ SteadyClockTimePoint GetTimePoint(Core::System& system) override;
+
+ TimeSpanType GetCurrentRawTimePoint(Core::System& system) override;
+};
+
+} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp
index 6ee77c5f9..8ef4efcef 100644
--- a/src/core/hle/service/time/time.cpp
+++ b/src/core/hle/service/time/time.cpp
@@ -1,9 +1,7 @@
-// Copyright 2018 yuzu emulator team
+// Copyright 2019 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <chrono>
-#include <ctime>
#include "common/logging/log.h"
#include "core/core.h"
#include "core/core_timing.h"
@@ -11,429 +9,321 @@
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/client_session.h"
+#include "core/hle/kernel/scheduler.h"
#include "core/hle/service/time/interface.h"
#include "core/hle/service/time/time.h"
#include "core/hle/service/time/time_sharedmemory.h"
-#include "core/settings.h"
+#include "core/hle/service/time/time_zone_service.h"
namespace Service::Time {
-static std::chrono::seconds GetSecondsSinceEpoch() {
- return std::chrono::duration_cast<std::chrono::seconds>(
- std::chrono::system_clock::now().time_since_epoch()) +
- Settings::values.custom_rtc_differential;
-}
-
-static void PosixToCalendar(u64 posix_time, CalendarTime& calendar_time,
- CalendarAdditionalInfo& additional_info,
- [[maybe_unused]] const TimeZoneRule& /*rule*/) {
- const std::time_t time(posix_time);
- const std::tm* tm = std::localtime(&time);
- if (tm == nullptr) {
- calendar_time = {};
- additional_info = {};
- return;
- }
- calendar_time.year = static_cast<u16_le>(tm->tm_year + 1900);
- calendar_time.month = static_cast<u8>(tm->tm_mon + 1);
- calendar_time.day = static_cast<u8>(tm->tm_mday);
- calendar_time.hour = static_cast<u8>(tm->tm_hour);
- calendar_time.minute = static_cast<u8>(tm->tm_min);
- calendar_time.second = static_cast<u8>(tm->tm_sec);
-
- additional_info.day_of_week = tm->tm_wday;
- additional_info.day_of_year = tm->tm_yday;
- std::memcpy(additional_info.name.data(), "UTC", sizeof("UTC"));
- additional_info.utc_offset = 0;
-}
-
-static u64 CalendarToPosix(const CalendarTime& calendar_time,
- [[maybe_unused]] const TimeZoneRule& /*rule*/) {
- std::tm time{};
- time.tm_year = calendar_time.year - 1900;
- time.tm_mon = calendar_time.month - 1;
- time.tm_mday = calendar_time.day;
-
- time.tm_hour = calendar_time.hour;
- time.tm_min = calendar_time.minute;
- time.tm_sec = calendar_time.second;
-
- std::time_t epoch_time = std::mktime(&time);
- return static_cast<u64>(epoch_time);
-}
-
-enum class ClockContextType {
- StandardSteady,
- StandardUserSystem,
- StandardNetworkSystem,
- StandardLocalSystem,
-};
-
class ISystemClock final : public ServiceFramework<ISystemClock> {
public:
- ISystemClock(std::shared_ptr<Service::Time::SharedMemory> shared_memory,
- ClockContextType clock_type)
- : ServiceFramework("ISystemClock"), shared_memory(shared_memory), clock_type(clock_type) {
+ ISystemClock(Clock::SystemClockCore& clock_core)
+ : ServiceFramework("ISystemClock"), clock_core{clock_core} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &ISystemClock::GetCurrentTime, "GetCurrentTime"},
{1, nullptr, "SetCurrentTime"},
- {2, &ISystemClock::GetSystemClockContext, "GetSystemClockContext"},
+ {2, &ISystemClock::GetSystemClockContext, "GetSystemClockContext"},
{3, nullptr, "SetSystemClockContext"},
{4, nullptr, "GetOperationEventReadableHandle"},
};
// clang-format on
RegisterHandlers(functions);
- UpdateSharedMemoryContext(system_clock_context);
}
private:
void GetCurrentTime(Kernel::HLERequestContext& ctx) {
- const s64 time_since_epoch{GetSecondsSinceEpoch().count()};
LOG_DEBUG(Service_Time, "called");
+ if (!clock_core.IsInitialized()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_UNINITIALIZED_CLOCK);
+ return;
+ }
+
+ s64 posix_time{};
+ if (const ResultCode result{
+ clock_core.GetCurrentTime(Core::System::GetInstance(), posix_time)};
+ result != RESULT_SUCCESS) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+ return;
+ }
+
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
- rb.Push<u64>(time_since_epoch);
+ rb.Push<s64>(posix_time);
}
void GetSystemClockContext(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_Time, "(STUBBED) called");
+ LOG_DEBUG(Service_Time, "called");
+
+ if (!clock_core.IsInitialized()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_UNINITIALIZED_CLOCK);
+ return;
+ }
- // TODO(ogniK): This should be updated periodically however since we have it stubbed we'll
- // only update when we get a new context
- UpdateSharedMemoryContext(system_clock_context);
+ Clock::SystemClockContext system_clock_context{};
+ if (const ResultCode result{
+ clock_core.GetClockContext(Core::System::GetInstance(), system_clock_context)};
+ result != RESULT_SUCCESS) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+ return;
+ }
- IPC::ResponseBuilder rb{ctx, (sizeof(SystemClockContext) / 4) + 2};
+ IPC::ResponseBuilder rb{ctx, sizeof(Clock::SystemClockContext) / 4 + 2};
rb.Push(RESULT_SUCCESS);
rb.PushRaw(system_clock_context);
}
- void UpdateSharedMemoryContext(const SystemClockContext& clock_context) {
- switch (clock_type) {
- case ClockContextType::StandardLocalSystem:
- shared_memory->SetStandardLocalSystemClockContext(clock_context);
- break;
- case ClockContextType::StandardNetworkSystem:
- shared_memory->SetStandardNetworkSystemClockContext(clock_context);
- break;
- }
- }
-
- SystemClockContext system_clock_context{};
- std::shared_ptr<Service::Time::SharedMemory> shared_memory;
- ClockContextType clock_type;
+ Clock::SystemClockCore& clock_core;
};
class ISteadyClock final : public ServiceFramework<ISteadyClock> {
public:
- ISteadyClock(std::shared_ptr<SharedMemory> shared_memory, Core::System& system)
- : ServiceFramework("ISteadyClock"), shared_memory(shared_memory), system(system) {
+ ISteadyClock(Clock::SteadyClockCore& clock_core)
+ : ServiceFramework("ISteadyClock"), clock_core{clock_core} {
static const FunctionInfo functions[] = {
{0, &ISteadyClock::GetCurrentTimePoint, "GetCurrentTimePoint"},
};
RegisterHandlers(functions);
-
- shared_memory->SetStandardSteadyClockTimepoint(GetCurrentTimePoint());
}
private:
void GetCurrentTimePoint(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called");
- const auto time_point = GetCurrentTimePoint();
- // TODO(ogniK): This should be updated periodically
- shared_memory->SetStandardSteadyClockTimepoint(time_point);
+ if (!clock_core.IsInitialized()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_UNINITIALIZED_CLOCK);
+ return;
+ }
- IPC::ResponseBuilder rb{ctx, (sizeof(SteadyClockTimePoint) / 4) + 2};
+ const Clock::SteadyClockTimePoint time_point{
+ clock_core.GetCurrentTimePoint(Core::System::GetInstance())};
+ IPC::ResponseBuilder rb{ctx, (sizeof(Clock::SteadyClockTimePoint) / 4) + 2};
rb.Push(RESULT_SUCCESS);
rb.PushRaw(time_point);
}
- SteadyClockTimePoint GetCurrentTimePoint() const {
- const auto& core_timing = system.CoreTiming();
- const auto ms = Core::Timing::CyclesToMs(core_timing.GetTicks());
- return {static_cast<u64_le>(ms.count() / 1000), {}};
- }
-
- std::shared_ptr<SharedMemory> shared_memory;
- Core::System& system;
+ Clock::SteadyClockCore& clock_core;
};
-class ITimeZoneService final : public ServiceFramework<ITimeZoneService> {
-public:
- ITimeZoneService() : ServiceFramework("ITimeZoneService") {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, &ITimeZoneService::GetDeviceLocationName, "GetDeviceLocationName"},
- {1, nullptr, "SetDeviceLocationName"},
- {2, &ITimeZoneService::GetTotalLocationNameCount, "GetTotalLocationNameCount"},
- {3, nullptr, "LoadLocationNameList"},
- {4, &ITimeZoneService::LoadTimeZoneRule, "LoadTimeZoneRule"},
- {5, nullptr, "GetTimeZoneRuleVersion"},
- {6, nullptr, "GetDeviceLocationNameAndUpdatedTime"},
- {7, nullptr, "SetDeviceLocationNameWithTimeZoneRule"},
- {8, nullptr, "ParseTimeZoneBinary"},
- {20, nullptr, "GetDeviceLocationNameOperationEventReadableHandle"},
- {100, &ITimeZoneService::ToCalendarTime, "ToCalendarTime"},
- {101, &ITimeZoneService::ToCalendarTimeWithMyRule, "ToCalendarTimeWithMyRule"},
- {201, &ITimeZoneService::ToPosixTime, "ToPosixTime"},
- {202, &ITimeZoneService::ToPosixTimeWithMyRule, "ToPosixTimeWithMyRule"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
- }
+ResultCode Module::Interface::GetClockSnapshotFromSystemClockContextInternal(
+ Kernel::Thread* thread, Clock::SystemClockContext user_context,
+ Clock::SystemClockContext network_context, u8 type, Clock::ClockSnapshot& clock_snapshot) {
-private:
- LocationName location_name{"UTC"};
- TimeZoneRule my_time_zone_rule{};
+ auto& time_manager{module->GetTimeManager()};
- void GetDeviceLocationName(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Time, "called");
+ clock_snapshot.is_automatic_correction_enabled =
+ time_manager.GetStandardUserSystemClockCore().IsAutomaticCorrectionEnabled();
+ clock_snapshot.user_context = user_context;
+ clock_snapshot.network_context = network_context;
- IPC::ResponseBuilder rb{ctx, (sizeof(LocationName) / 4) + 2};
- rb.Push(RESULT_SUCCESS);
- rb.PushRaw(location_name);
+ if (const ResultCode result{
+ time_manager.GetTimeZoneContentManager().GetTimeZoneManager().GetDeviceLocationName(
+ clock_snapshot.location_name)};
+ result != RESULT_SUCCESS) {
+ return result;
}
- void GetTotalLocationNameCount(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_Time, "(STUBBED) called");
-
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(RESULT_SUCCESS);
- rb.Push<u32>(0);
+ const auto current_time_point{
+ time_manager.GetStandardSteadyClockCore().GetCurrentTimePoint(Core::System::GetInstance())};
+ if (const ResultCode result{Clock::ClockSnapshot::GetCurrentTime(
+ clock_snapshot.user_time, current_time_point, clock_snapshot.user_context)};
+ result != RESULT_SUCCESS) {
+ return result;
}
- void LoadTimeZoneRule(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_Time, "(STUBBED) called");
-
- ctx.WriteBuffer(&my_time_zone_rule, sizeof(TimeZoneRule));
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(RESULT_SUCCESS);
+ TimeZone::CalendarInfo userCalendarInfo{};
+ if (const ResultCode result{
+ time_manager.GetTimeZoneContentManager().GetTimeZoneManager().ToCalendarTimeWithMyRules(
+ clock_snapshot.user_time, userCalendarInfo)};
+ result != RESULT_SUCCESS) {
+ return result;
}
- void ToCalendarTime(Kernel::HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const u64 posix_time = rp.Pop<u64>();
- LOG_WARNING(Service_Time, "(STUBBED) called, posix_time=0x{:016X}", posix_time);
-
- TimeZoneRule time_zone_rule{};
- auto buffer = ctx.ReadBuffer();
- std::memcpy(&time_zone_rule, buffer.data(), buffer.size());
+ clock_snapshot.user_calendar_time = userCalendarInfo.time;
+ clock_snapshot.user_calendar_additional_time = userCalendarInfo.additiona_info;
- CalendarTime calendar_time{2018, 1, 1, 0, 0, 0};
- CalendarAdditionalInfo additional_info{};
-
- PosixToCalendar(posix_time, calendar_time, additional_info, time_zone_rule);
-
- IPC::ResponseBuilder rb{ctx, 10};
- rb.Push(RESULT_SUCCESS);
- rb.PushRaw(calendar_time);
- rb.PushRaw(additional_info);
- }
-
- void ToCalendarTimeWithMyRule(Kernel::HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const u64 posix_time = rp.Pop<u64>();
- LOG_WARNING(Service_Time, "(STUBBED) called, posix_time=0x{:016X}", posix_time);
-
- CalendarTime calendar_time{2018, 1, 1, 0, 0, 0};
- CalendarAdditionalInfo additional_info{};
-
- PosixToCalendar(posix_time, calendar_time, additional_info, my_time_zone_rule);
-
- IPC::ResponseBuilder rb{ctx, 10};
- rb.Push(RESULT_SUCCESS);
- rb.PushRaw(calendar_time);
- rb.PushRaw(additional_info);
+ if (Clock::ClockSnapshot::GetCurrentTime(clock_snapshot.network_time, current_time_point,
+ clock_snapshot.network_context) != RESULT_SUCCESS) {
+ clock_snapshot.network_time = 0;
}
- void ToPosixTime(Kernel::HLERequestContext& ctx) {
- // TODO(ogniK): Figure out how to handle multiple times
- LOG_WARNING(Service_Time, "(STUBBED) called");
-
- IPC::RequestParser rp{ctx};
- auto calendar_time = rp.PopRaw<CalendarTime>();
- auto posix_time = CalendarToPosix(calendar_time, {});
-
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(RESULT_SUCCESS);
- rb.PushRaw<u32>(1); // Amount of times we're returning
- ctx.WriteBuffer(&posix_time, sizeof(u64));
+ TimeZone::CalendarInfo networkCalendarInfo{};
+ if (const ResultCode result{
+ time_manager.GetTimeZoneContentManager().GetTimeZoneManager().ToCalendarTimeWithMyRules(
+ clock_snapshot.network_time, networkCalendarInfo)};
+ result != RESULT_SUCCESS) {
+ return result;
}
- void ToPosixTimeWithMyRule(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_Time, "(STUBBED) called");
-
- IPC::RequestParser rp{ctx};
- auto calendar_time = rp.PopRaw<CalendarTime>();
- auto posix_time = CalendarToPosix(calendar_time, {});
+ clock_snapshot.network_calendar_time = networkCalendarInfo.time;
+ clock_snapshot.network_calendar_additional_time = networkCalendarInfo.additiona_info;
+ clock_snapshot.type = type;
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(RESULT_SUCCESS);
- rb.PushRaw<u32>(1); // Amount of times we're returning
- ctx.WriteBuffer(&posix_time, sizeof(u64));
- }
-};
+ return RESULT_SUCCESS;
+}
void Module::Interface::GetStandardUserSystemClock(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called");
-
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<ISystemClock>(shared_memory, ClockContextType::StandardUserSystem);
+ rb.PushIpcInterface<ISystemClock>(module->GetTimeManager().GetStandardUserSystemClockCore());
}
void Module::Interface::GetStandardNetworkSystemClock(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called");
-
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<ISystemClock>(shared_memory, ClockContextType::StandardNetworkSystem);
+ rb.PushIpcInterface<ISystemClock>(module->GetTimeManager().GetStandardNetworkSystemClockCore());
}
void Module::Interface::GetStandardSteadyClock(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called");
-
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<ISteadyClock>(shared_memory, system);
+ rb.PushIpcInterface<ISteadyClock>(module->GetTimeManager().GetStandardSteadyClockCore());
}
void Module::Interface::GetTimeZoneService(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called");
-
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<ITimeZoneService>();
+ rb.PushIpcInterface<ITimeZoneService>(module->GetTimeManager().GetTimeZoneContentManager());
}
void Module::Interface::GetStandardLocalSystemClock(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called");
-
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<ISystemClock>(shared_memory, ClockContextType::StandardLocalSystem);
+ rb.PushIpcInterface<ISystemClock>(module->GetTimeManager().GetStandardLocalSystemClockCore());
}
-void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
+void Module::Interface::IsStandardNetworkSystemClockAccuracySufficient(
+ Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called");
+ auto& clock_core{module->GetTimeManager().GetStandardNetworkSystemClockCore()};
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<u32>(clock_core.IsStandardNetworkSystemClockAccuracySufficient(system));
+}
- IPC::RequestParser rp{ctx};
- const auto initial_type = rp.PopRaw<u8>();
+void Module::Interface::CalculateMonotonicSystemClockBaseTimePoint(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_Time, "called");
- const s64 time_since_epoch{GetSecondsSinceEpoch().count()};
- const std::time_t time(time_since_epoch);
- const std::tm* tm = std::localtime(&time);
- if (tm == nullptr) {
- LOG_ERROR(Service_Time, "tm is a nullptr");
+ auto& steady_clock_core{module->GetTimeManager().GetStandardSteadyClockCore()};
+ if (!steady_clock_core.IsInitialized()) {
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(RESULT_UNKNOWN); // TODO(ogniK): Find appropriate error code
+ rb.Push(ERROR_UNINITIALIZED_CLOCK);
return;
}
- const auto& core_timing = system.CoreTiming();
- const auto ms = Core::Timing::CyclesToMs(core_timing.GetTicks());
- const SteadyClockTimePoint steady_clock_time_point{static_cast<u64_le>(ms.count() / 1000), {}};
-
- CalendarTime calendar_time{};
- calendar_time.year = static_cast<u16_le>(tm->tm_year + 1900);
- calendar_time.month = static_cast<u8>(tm->tm_mon + 1);
- calendar_time.day = static_cast<u8>(tm->tm_mday);
- calendar_time.hour = static_cast<u8>(tm->tm_hour);
- calendar_time.minute = static_cast<u8>(tm->tm_min);
- calendar_time.second = static_cast<u8>(tm->tm_sec);
+ IPC::RequestParser rp{ctx};
+ const auto context{rp.PopRaw<Clock::SystemClockContext>()};
+ const auto current_time_point{
+ steady_clock_core.GetCurrentTimePoint(Core::System::GetInstance())};
+
+ if (current_time_point.clock_source_id == context.steady_time_point.clock_source_id) {
+ const auto ticks{Clock::TimeSpanType::FromTicks(
+ Core::Timing::CpuCyclesToClockCycles(system.CoreTiming().GetTicks()),
+ Core::Timing::CNTFREQ)};
+ const s64 base_time_point{context.offset + current_time_point.time_point -
+ ticks.ToSeconds()};
+ IPC::ResponseBuilder rb{ctx, (sizeof(s64) / 4) + 2};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushRaw(base_time_point);
+ return;
+ }
- ClockSnapshot clock_snapshot{};
- clock_snapshot.system_posix_time = time_since_epoch;
- clock_snapshot.network_posix_time = time_since_epoch;
- clock_snapshot.system_calendar_time = calendar_time;
- clock_snapshot.network_calendar_time = calendar_time;
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_TIME_MISMATCH);
+}
- CalendarAdditionalInfo additional_info{};
- PosixToCalendar(time_since_epoch, calendar_time, additional_info, {});
+void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_Time, "called");
+ IPC::RequestParser rp{ctx};
+ const auto type{rp.PopRaw<u8>()};
- clock_snapshot.system_calendar_info = additional_info;
- clock_snapshot.network_calendar_info = additional_info;
+ Clock::SystemClockContext user_context{};
+ if (const ResultCode result{
+ module->GetTimeManager().GetStandardUserSystemClockCore().GetClockContext(
+ Core::System::GetInstance(), user_context)};
+ result != RESULT_SUCCESS) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+ return;
+ }
+ Clock::SystemClockContext network_context{};
+ if (const ResultCode result{
+ module->GetTimeManager().GetStandardNetworkSystemClockCore().GetClockContext(
+ Core::System::GetInstance(), network_context)};
+ result != RESULT_SUCCESS) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+ return;
+ }
- clock_snapshot.steady_clock_timepoint = steady_clock_time_point;
- clock_snapshot.location_name = LocationName{"UTC"};
- clock_snapshot.clock_auto_adjustment_enabled = 1;
- clock_snapshot.type = initial_type;
+ Clock::ClockSnapshot clock_snapshot{};
+ if (const ResultCode result{GetClockSnapshotFromSystemClockContextInternal(
+ &ctx.GetThread(), user_context, network_context, type, clock_snapshot)};
+ result != RESULT_SUCCESS) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+ return;
+ }
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- ctx.WriteBuffer(&clock_snapshot, sizeof(ClockSnapshot));
+ ctx.WriteBuffer(&clock_snapshot, sizeof(Clock::ClockSnapshot));
}
-void Module::Interface::CalculateStandardUserSystemClockDifferenceByUser(
- Kernel::HLERequestContext& ctx) {
+void Module::Interface::GetClockSnapshotFromSystemClockContext(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called");
-
IPC::RequestParser rp{ctx};
- const auto snapshot_a = rp.PopRaw<ClockSnapshot>();
- const auto snapshot_b = rp.PopRaw<ClockSnapshot>();
- const u64 difference =
- snapshot_b.user_clock_context.offset - snapshot_a.user_clock_context.offset;
+ const auto type{rp.PopRaw<u8>()};
+ rp.AlignWithPadding();
+
+ const Clock::SystemClockContext user_context{rp.PopRaw<Clock::SystemClockContext>()};
+ const Clock::SystemClockContext network_context{rp.PopRaw<Clock::SystemClockContext>()};
- IPC::ResponseBuilder rb{ctx, 4};
+ Clock::ClockSnapshot clock_snapshot{};
+ if (const ResultCode result{GetClockSnapshotFromSystemClockContextInternal(
+ &ctx.GetThread(), user_context, network_context, type, clock_snapshot)};
+ result != RESULT_SUCCESS) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+ return;
+ }
+
+ IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- rb.PushRaw<u64>(difference);
+ ctx.WriteBuffer(&clock_snapshot, sizeof(Clock::ClockSnapshot));
}
void Module::Interface::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called");
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushCopyObjects(shared_memory->GetSharedMemoryHolder());
-}
-
-void Module::Interface::IsStandardUserSystemClockAutomaticCorrectionEnabled(
- Kernel::HLERequestContext& ctx) {
- // ogniK(TODO): When clock contexts are implemented, the value should be read from the context
- // instead of our shared memory holder
- LOG_DEBUG(Service_Time, "called");
-
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(RESULT_SUCCESS);
- rb.Push<u8>(shared_memory->GetStandardUserSystemClockAutomaticCorrectionEnabled());
-}
-
-void Module::Interface::SetStandardUserSystemClockAutomaticCorrectionEnabled(
- Kernel::HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const auto enabled = rp.Pop<u8>();
-
- LOG_WARNING(Service_Time, "(PARTIAL IMPLEMENTATION) called");
-
- // TODO(ogniK): Update clock contexts and correct timespans
-
- shared_memory->SetStandardUserSystemClockAutomaticCorrectionEnabled(enabled > 0);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(RESULT_SUCCESS);
+ rb.PushCopyObjects(module->GetTimeManager().GetSharedMemory().GetSharedMemoryHolder());
}
-Module::Interface::Interface(std::shared_ptr<Module> time,
- std::shared_ptr<SharedMemory> shared_memory, Core::System& system,
- const char* name)
- : ServiceFramework(name), time(std::move(time)), shared_memory(std::move(shared_memory)),
- system(system) {}
+Module::Interface::Interface(std::shared_ptr<Module> module, Core::System& system, const char* name)
+ : ServiceFramework(name), module{std::move(module)}, system{system} {}
Module::Interface::~Interface() = default;
void InstallInterfaces(Core::System& system) {
- auto time = std::make_shared<Module>();
- auto shared_mem = std::make_shared<SharedMemory>(system);
-
- std::make_shared<Time>(time, shared_mem, system, "time:a")
- ->InstallAsService(system.ServiceManager());
- std::make_shared<Time>(time, shared_mem, system, "time:s")
- ->InstallAsService(system.ServiceManager());
- std::make_shared<Time>(std::move(time), shared_mem, system, "time:u")
- ->InstallAsService(system.ServiceManager());
+ auto module{std::make_shared<Module>(system)};
+ std::make_shared<Time>(module, system, "time:a")->InstallAsService(system.ServiceManager());
+ std::make_shared<Time>(module, system, "time:s")->InstallAsService(system.ServiceManager());
+ std::make_shared<Time>(module, system, "time:u")->InstallAsService(system.ServiceManager());
}
} // namespace Service::Time
diff --git a/src/core/hle/service/time/time.h b/src/core/hle/service/time/time.h
index c32d32860..aadc2df60 100644
--- a/src/core/hle/service/time/time.h
+++ b/src/core/hle/service/time/time.h
@@ -4,84 +4,23 @@
#pragma once
-#include <array>
-#include "common/common_funcs.h"
#include "core/hle/service/service.h"
+#include "core/hle/service/time/clock_types.h"
+#include "core/hle/service/time/time_manager.h"
-namespace Service::Time {
-
-class SharedMemory;
-
-struct LocationName {
- std::array<u8, 0x24> name;
-};
-static_assert(sizeof(LocationName) == 0x24, "LocationName is incorrect size");
-
-struct CalendarTime {
- u16_le year;
- u8 month; // Starts at 1
- u8 day; // Starts at 1
- u8 hour;
- u8 minute;
- u8 second;
-};
-static_assert(sizeof(CalendarTime) == 0x8, "CalendarTime structure has incorrect size");
-
-struct CalendarAdditionalInfo {
- u32_le day_of_week;
- u32_le day_of_year;
- std::array<u8, 8> name;
- u8 is_dst;
- s32_le utc_offset;
-};
-static_assert(sizeof(CalendarAdditionalInfo) == 0x18,
- "CalendarAdditionalInfo structure has incorrect size");
-
-// TODO(mailwl) RE this structure
-struct TimeZoneRule {
- INSERT_PADDING_BYTES(0x4000);
-};
-
-struct SteadyClockTimePoint {
- using SourceID = std::array<u8, 16>;
+namespace Core {
+class System;
+}
- u64_le value;
- SourceID source_id;
-};
-static_assert(sizeof(SteadyClockTimePoint) == 0x18, "SteadyClockTimePoint is incorrect size");
-
-struct SystemClockContext {
- u64_le offset;
- SteadyClockTimePoint time_point;
-};
-static_assert(sizeof(SystemClockContext) == 0x20,
- "SystemClockContext structure has incorrect size");
-
-struct ClockSnapshot {
- SystemClockContext user_clock_context;
- SystemClockContext network_clock_context;
- s64_le system_posix_time;
- s64_le network_posix_time;
- CalendarTime system_calendar_time;
- CalendarTime network_calendar_time;
- CalendarAdditionalInfo system_calendar_info;
- CalendarAdditionalInfo network_calendar_info;
- SteadyClockTimePoint steady_clock_timepoint;
- LocationName location_name;
- u8 clock_auto_adjustment_enabled;
- u8 type;
- u8 version;
- INSERT_PADDING_BYTES(1);
-};
-static_assert(sizeof(ClockSnapshot) == 0xd0, "ClockSnapshot is an invalid size");
+namespace Service::Time {
class Module final {
public:
+ Module(Core::System& system) : time_manager{system} {}
+
class Interface : public ServiceFramework<Interface> {
public:
- explicit Interface(std::shared_ptr<Module> time,
- std::shared_ptr<SharedMemory> shared_memory, Core::System& system,
- const char* name);
+ explicit Interface(std::shared_ptr<Module> module, Core::System& system, const char* name);
~Interface() override;
void GetStandardUserSystemClock(Kernel::HLERequestContext& ctx);
@@ -89,17 +28,29 @@ public:
void GetStandardSteadyClock(Kernel::HLERequestContext& ctx);
void GetTimeZoneService(Kernel::HLERequestContext& ctx);
void GetStandardLocalSystemClock(Kernel::HLERequestContext& ctx);
+ void IsStandardNetworkSystemClockAccuracySufficient(Kernel::HLERequestContext& ctx);
+ void CalculateMonotonicSystemClockBaseTimePoint(Kernel::HLERequestContext& ctx);
void GetClockSnapshot(Kernel::HLERequestContext& ctx);
- void CalculateStandardUserSystemClockDifferenceByUser(Kernel::HLERequestContext& ctx);
+ void GetClockSnapshotFromSystemClockContext(Kernel::HLERequestContext& ctx);
void GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx);
- void IsStandardUserSystemClockAutomaticCorrectionEnabled(Kernel::HLERequestContext& ctx);
- void SetStandardUserSystemClockAutomaticCorrectionEnabled(Kernel::HLERequestContext& ctx);
+
+ private:
+ ResultCode GetClockSnapshotFromSystemClockContextInternal(
+ Kernel::Thread* thread, Clock::SystemClockContext user_context,
+ Clock::SystemClockContext network_context, u8 type,
+ Clock::ClockSnapshot& cloc_snapshot);
protected:
- std::shared_ptr<Module> time;
- std::shared_ptr<SharedMemory> shared_memory;
+ std::shared_ptr<Module> module;
Core::System& system;
};
+
+ TimeManager& GetTimeManager() {
+ return time_manager;
+ }
+
+private:
+ TimeManager time_manager;
};
/// Registers all Time services with the specified service manager.
diff --git a/src/core/hle/service/time/time_manager.cpp b/src/core/hle/service/time/time_manager.cpp
new file mode 100644
index 000000000..9d6c55865
--- /dev/null
+++ b/src/core/hle/service/time/time_manager.cpp
@@ -0,0 +1,137 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <chrono>
+#include <ctime>
+
+#include "core/hle/service/time/ephemeral_network_system_clock_context_writer.h"
+#include "core/hle/service/time/local_system_clock_context_writer.h"
+#include "core/hle/service/time/network_system_clock_context_writer.h"
+#include "core/hle/service/time/time_manager.h"
+#include "core/settings.h"
+
+namespace Service::Time {
+
+constexpr Clock::TimeSpanType standard_network_clock_accuracy{0x0009356907420000ULL};
+
+static std::chrono::seconds GetSecondsSinceEpoch() {
+ return std::chrono::duration_cast<std::chrono::seconds>(
+ std::chrono::system_clock::now().time_since_epoch()) +
+ Settings::values.custom_rtc_differential;
+}
+
+static s64 GetExternalRtcValue() {
+ return GetSecondsSinceEpoch().count();
+}
+
+TimeManager::TimeManager(Core::System& system)
+ : shared_memory{system}, standard_local_system_clock_core{standard_steady_clock_core},
+ standard_network_system_clock_core{standard_steady_clock_core},
+ standard_user_system_clock_core{standard_local_system_clock_core,
+ standard_network_system_clock_core, system},
+ ephemeral_network_system_clock_core{tick_based_steady_clock_core},
+ local_system_clock_context_writer{
+ std::make_shared<Clock::LocalSystemClockContextWriter>(shared_memory)},
+ network_system_clock_context_writer{
+ std::make_shared<Clock::NetworkSystemClockContextWriter>(shared_memory)},
+ ephemeral_network_system_clock_context_writer{
+ std::make_shared<Clock::EphemeralNetworkSystemClockContextWriter>()},
+ time_zone_content_manager{*this, system} {
+
+ const auto system_time{Clock::TimeSpanType::FromSeconds(GetExternalRtcValue())};
+ SetupStandardSteadyClock(system, Common::UUID::Generate(), system_time, {}, {});
+ SetupStandardLocalSystemClock(system, {}, system_time.ToSeconds());
+ SetupStandardNetworkSystemClock({}, standard_network_clock_accuracy);
+ SetupStandardUserSystemClock(system, {}, Clock::SteadyClockTimePoint::GetRandom());
+ SetupEphemeralNetworkSystemClock();
+}
+
+TimeManager::~TimeManager() = default;
+
+void TimeManager::SetupTimeZoneManager(std::string location_name,
+ Clock::SteadyClockTimePoint time_zone_updated_time_point,
+ std::size_t total_location_name_count,
+ u128 time_zone_rule_version,
+ FileSys::VirtualFile& vfs_file) {
+ if (time_zone_content_manager.GetTimeZoneManager().SetDeviceLocationNameWithTimeZoneRule(
+ location_name, vfs_file) != RESULT_SUCCESS) {
+ UNREACHABLE();
+ return;
+ }
+
+ time_zone_content_manager.GetTimeZoneManager().SetUpdatedTime(time_zone_updated_time_point);
+ time_zone_content_manager.GetTimeZoneManager().SetTotalLocationNameCount(
+ total_location_name_count);
+ time_zone_content_manager.GetTimeZoneManager().SetTimeZoneRuleVersion(time_zone_rule_version);
+ time_zone_content_manager.GetTimeZoneManager().MarkAsInitialized();
+}
+
+void TimeManager::SetupStandardSteadyClock(Core::System& system, Common::UUID clock_source_id,
+ Clock::TimeSpanType setup_value,
+ Clock::TimeSpanType internal_offset,
+ bool is_rtc_reset_detected) {
+ standard_steady_clock_core.SetClockSourceId(clock_source_id);
+ standard_steady_clock_core.SetSetupValue(setup_value);
+ standard_steady_clock_core.SetInternalOffset(internal_offset);
+ standard_steady_clock_core.MarkAsInitialized();
+
+ const auto current_time_point{standard_steady_clock_core.GetCurrentRawTimePoint(system)};
+ shared_memory.SetupStandardSteadyClock(system, clock_source_id, current_time_point);
+}
+
+void TimeManager::SetupStandardLocalSystemClock(Core::System& system,
+ Clock::SystemClockContext clock_context,
+ s64 posix_time) {
+ standard_local_system_clock_core.SetUpdateCallbackInstance(local_system_clock_context_writer);
+
+ const auto current_time_point{
+ standard_local_system_clock_core.GetSteadyClockCore().GetCurrentTimePoint(system)};
+ if (current_time_point.clock_source_id == clock_context.steady_time_point.clock_source_id) {
+ standard_local_system_clock_core.SetSystemClockContext(clock_context);
+ } else {
+ if (standard_local_system_clock_core.SetCurrentTime(system, posix_time) != RESULT_SUCCESS) {
+ UNREACHABLE();
+ return;
+ }
+ }
+
+ standard_local_system_clock_core.MarkAsInitialized();
+}
+
+void TimeManager::SetupStandardNetworkSystemClock(Clock::SystemClockContext clock_context,
+ Clock::TimeSpanType sufficient_accuracy) {
+ standard_network_system_clock_core.SetUpdateCallbackInstance(
+ network_system_clock_context_writer);
+
+ if (standard_network_system_clock_core.SetSystemClockContext(clock_context) != RESULT_SUCCESS) {
+ UNREACHABLE();
+ return;
+ }
+
+ standard_network_system_clock_core.SetStandardNetworkClockSufficientAccuracy(
+ sufficient_accuracy);
+ standard_network_system_clock_core.MarkAsInitialized();
+}
+
+void TimeManager::SetupStandardUserSystemClock(
+ Core::System& system, bool is_automatic_correction_enabled,
+ Clock::SteadyClockTimePoint steady_clock_time_point) {
+ if (standard_user_system_clock_core.SetAutomaticCorrectionEnabled(
+ system, is_automatic_correction_enabled) != RESULT_SUCCESS) {
+ UNREACHABLE();
+ return;
+ }
+
+ standard_user_system_clock_core.SetAutomaticCorrectionUpdatedTime(steady_clock_time_point);
+ standard_user_system_clock_core.MarkAsInitialized();
+ shared_memory.SetAutomaticCorrectionEnabled(is_automatic_correction_enabled);
+}
+
+void TimeManager::SetupEphemeralNetworkSystemClock() {
+ ephemeral_network_system_clock_core.SetUpdateCallbackInstance(
+ ephemeral_network_system_clock_context_writer);
+ ephemeral_network_system_clock_core.MarkAsInitialized();
+}
+
+} // namespace Service::Time
diff --git a/src/core/hle/service/time/time_manager.h b/src/core/hle/service/time/time_manager.h
new file mode 100644
index 000000000..8e65f0d22
--- /dev/null
+++ b/src/core/hle/service/time/time_manager.h
@@ -0,0 +1,117 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+#include "core/file_sys/vfs_types.h"
+#include "core/hle/service/time/clock_types.h"
+#include "core/hle/service/time/ephemeral_network_system_clock_core.h"
+#include "core/hle/service/time/standard_local_system_clock_core.h"
+#include "core/hle/service/time/standard_network_system_clock_core.h"
+#include "core/hle/service/time/standard_steady_clock_core.h"
+#include "core/hle/service/time/standard_user_system_clock_core.h"
+#include "core/hle/service/time/tick_based_steady_clock_core.h"
+#include "core/hle/service/time/time_sharedmemory.h"
+#include "core/hle/service/time/time_zone_content_manager.h"
+
+namespace Service::Time {
+
+namespace Clock {
+class EphemeralNetworkSystemClockContextWriter;
+class LocalSystemClockContextWriter;
+class NetworkSystemClockContextWriter;
+} // namespace Clock
+
+// Parts of this implementation were based on Ryujinx (https://github.com/Ryujinx/Ryujinx/pull/783).
+// This code was released under public domain.
+
+class TimeManager final {
+public:
+ explicit TimeManager(Core::System& system);
+ ~TimeManager();
+
+ Clock::StandardSteadyClockCore& GetStandardSteadyClockCore() {
+ return standard_steady_clock_core;
+ }
+
+ const Clock::StandardSteadyClockCore& GetStandardSteadyClockCore() const {
+ return standard_steady_clock_core;
+ }
+
+ Clock::StandardLocalSystemClockCore& GetStandardLocalSystemClockCore() {
+ return standard_local_system_clock_core;
+ }
+
+ const Clock::StandardLocalSystemClockCore& GetStandardLocalSystemClockCore() const {
+ return standard_local_system_clock_core;
+ }
+
+ Clock::StandardNetworkSystemClockCore& GetStandardNetworkSystemClockCore() {
+ return standard_network_system_clock_core;
+ }
+
+ const Clock::StandardNetworkSystemClockCore& GetStandardNetworkSystemClockCore() const {
+ return standard_network_system_clock_core;
+ }
+
+ Clock::StandardUserSystemClockCore& GetStandardUserSystemClockCore() {
+ return standard_user_system_clock_core;
+ }
+
+ const Clock::StandardUserSystemClockCore& GetStandardUserSystemClockCore() const {
+ return standard_user_system_clock_core;
+ }
+
+ TimeZone::TimeZoneContentManager& GetTimeZoneContentManager() {
+ return time_zone_content_manager;
+ }
+
+ const TimeZone::TimeZoneContentManager& GetTimeZoneContentManager() const {
+ return time_zone_content_manager;
+ }
+
+ SharedMemory& GetSharedMemory() {
+ return shared_memory;
+ }
+
+ const SharedMemory& GetSharedMemory() const {
+ return shared_memory;
+ }
+
+ void SetupTimeZoneManager(std::string location_name,
+ Clock::SteadyClockTimePoint time_zone_updated_time_point,
+ std::size_t total_location_name_count, u128 time_zone_rule_version,
+ FileSys::VirtualFile& vfs_file);
+
+private:
+ void SetupStandardSteadyClock(Core::System& system, Common::UUID clock_source_id,
+ Clock::TimeSpanType setup_value,
+ Clock::TimeSpanType internal_offset, bool is_rtc_reset_detected);
+ void SetupStandardLocalSystemClock(Core::System& system,
+ Clock::SystemClockContext clock_context, s64 posix_time);
+ void SetupStandardNetworkSystemClock(Clock::SystemClockContext clock_context,
+ Clock::TimeSpanType sufficient_accuracy);
+ void SetupStandardUserSystemClock(Core::System& system, bool is_automatic_correction_enabled,
+ Clock::SteadyClockTimePoint steady_clock_time_point);
+ void SetupEphemeralNetworkSystemClock();
+
+ SharedMemory shared_memory;
+
+ Clock::StandardSteadyClockCore standard_steady_clock_core;
+ Clock::TickBasedSteadyClockCore tick_based_steady_clock_core;
+ Clock::StandardLocalSystemClockCore standard_local_system_clock_core;
+ Clock::StandardNetworkSystemClockCore standard_network_system_clock_core;
+ Clock::StandardUserSystemClockCore standard_user_system_clock_core;
+ Clock::EphemeralNetworkSystemClockCore ephemeral_network_system_clock_core;
+
+ std::shared_ptr<Clock::LocalSystemClockContextWriter> local_system_clock_context_writer;
+ std::shared_ptr<Clock::NetworkSystemClockContextWriter> network_system_clock_context_writer;
+ std::shared_ptr<Clock::EphemeralNetworkSystemClockContextWriter>
+ ephemeral_network_system_clock_context_writer;
+
+ TimeZone::TimeZoneContentManager time_zone_content_manager;
+};
+
+} // namespace Service::Time
diff --git a/src/core/hle/service/time/time_sharedmemory.cpp b/src/core/hle/service/time/time_sharedmemory.cpp
index 4035f5072..9b03191bf 100644
--- a/src/core/hle/service/time/time_sharedmemory.cpp
+++ b/src/core/hle/service/time/time_sharedmemory.cpp
@@ -3,20 +3,21 @@
// Refer to the license.txt file included.
#include "core/core.h"
+#include "core/core_timing.h"
+#include "core/core_timing_util.h"
+#include "core/hle/service/time/clock_types.h"
+#include "core/hle/service/time/steady_clock_core.h"
#include "core/hle/service/time/time_sharedmemory.h"
namespace Service::Time {
-const std::size_t SHARED_MEMORY_SIZE = 0x1000;
+
+static constexpr std::size_t SHARED_MEMORY_SIZE{0x1000};
SharedMemory::SharedMemory(Core::System& system) : system(system) {
shared_memory_holder = Kernel::SharedMemory::Create(
system.Kernel(), nullptr, SHARED_MEMORY_SIZE, Kernel::MemoryPermission::ReadWrite,
Kernel::MemoryPermission::Read, 0, Kernel::MemoryRegion::BASE, "Time:SharedMemory");
-
- // Seems static from 1.0.0 -> 8.1.0. Specific games seem to check this value and crash
- // if it's set to anything else
- shared_memory_format.format_version = 14;
- std::memcpy(shared_memory_holder->GetPointer(), &shared_memory_format, sizeof(Format));
+ std::memset(shared_memory_holder->GetPointer(), 0, SHARED_MEMORY_SIZE);
}
SharedMemory::~SharedMemory() = default;
@@ -25,44 +26,32 @@ std::shared_ptr<Kernel::SharedMemory> SharedMemory::GetSharedMemoryHolder() cons
return shared_memory_holder;
}
-void SharedMemory::SetStandardSteadyClockTimepoint(const SteadyClockTimePoint& timepoint) {
+void SharedMemory::SetupStandardSteadyClock(Core::System& system,
+ const Common::UUID& clock_source_id,
+ Clock::TimeSpanType current_time_point) {
+ const Clock::TimeSpanType ticks_time_span{Clock::TimeSpanType::FromTicks(
+ Core::Timing::CpuCyclesToClockCycles(system.CoreTiming().GetTicks()),
+ Core::Timing::CNTFREQ)};
+ const Clock::SteadyClockContext context{
+ static_cast<u64>(current_time_point.nanoseconds - ticks_time_span.nanoseconds),
+ clock_source_id};
shared_memory_format.standard_steady_clock_timepoint.StoreData(
- shared_memory_holder->GetPointer(), timepoint);
+ shared_memory_holder->GetPointer(), context);
}
-void SharedMemory::SetStandardLocalSystemClockContext(const SystemClockContext& context) {
+void SharedMemory::UpdateLocalSystemClockContext(const Clock::SystemClockContext& context) {
shared_memory_format.standard_local_system_clock_context.StoreData(
shared_memory_holder->GetPointer(), context);
}
-void SharedMemory::SetStandardNetworkSystemClockContext(const SystemClockContext& context) {
+void SharedMemory::UpdateNetworkSystemClockContext(const Clock::SystemClockContext& context) {
shared_memory_format.standard_network_system_clock_context.StoreData(
shared_memory_holder->GetPointer(), context);
}
-void SharedMemory::SetStandardUserSystemClockAutomaticCorrectionEnabled(bool enabled) {
+void SharedMemory::SetAutomaticCorrectionEnabled(bool is_enabled) {
shared_memory_format.standard_user_system_clock_automatic_correction.StoreData(
- shared_memory_holder->GetPointer(), enabled);
-}
-
-SteadyClockTimePoint SharedMemory::GetStandardSteadyClockTimepoint() {
- return shared_memory_format.standard_steady_clock_timepoint.ReadData(
- shared_memory_holder->GetPointer());
-}
-
-SystemClockContext SharedMemory::GetStandardLocalSystemClockContext() {
- return shared_memory_format.standard_local_system_clock_context.ReadData(
- shared_memory_holder->GetPointer());
-}
-
-SystemClockContext SharedMemory::GetStandardNetworkSystemClockContext() {
- return shared_memory_format.standard_network_system_clock_context.ReadData(
- shared_memory_holder->GetPointer());
-}
-
-bool SharedMemory::GetStandardUserSystemClockAutomaticCorrectionEnabled() {
- return shared_memory_format.standard_user_system_clock_automatic_correction.ReadData(
- shared_memory_holder->GetPointer());
+ shared_memory_holder->GetPointer(), is_enabled);
}
} // namespace Service::Time
diff --git a/src/core/hle/service/time/time_sharedmemory.h b/src/core/hle/service/time/time_sharedmemory.h
index 904a96430..5976b2046 100644
--- a/src/core/hle/service/time/time_sharedmemory.h
+++ b/src/core/hle/service/time/time_sharedmemory.h
@@ -5,11 +5,14 @@
#pragma once
#include "common/common_types.h"
+#include "common/uuid.h"
#include "core/hle/kernel/shared_memory.h"
-#include "core/hle/service/time/time.h"
+#include "core/hle/kernel/thread.h"
+#include "core/hle/service/time/clock_types.h"
namespace Service::Time {
-class SharedMemory {
+
+class SharedMemory final {
public:
explicit SharedMemory(Core::System& system);
~SharedMemory();
@@ -17,22 +20,10 @@ public:
// Return the shared memory handle
std::shared_ptr<Kernel::SharedMemory> GetSharedMemoryHolder() const;
- // Set memory barriers in shared memory and update them
- void SetStandardSteadyClockTimepoint(const SteadyClockTimePoint& timepoint);
- void SetStandardLocalSystemClockContext(const SystemClockContext& context);
- void SetStandardNetworkSystemClockContext(const SystemClockContext& context);
- void SetStandardUserSystemClockAutomaticCorrectionEnabled(bool enabled);
-
- // Pull from memory barriers in the shared memory
- SteadyClockTimePoint GetStandardSteadyClockTimepoint();
- SystemClockContext GetStandardLocalSystemClockContext();
- SystemClockContext GetStandardNetworkSystemClockContext();
- bool GetStandardUserSystemClockAutomaticCorrectionEnabled();
-
// TODO(ogniK): We have to properly simulate memory barriers, how are we going to do this?
template <typename T, std::size_t Offset>
struct MemoryBarrier {
- static_assert(std::is_trivially_constructible_v<T>, "T must be trivially constructable");
+ static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable");
u32_le read_attempt{};
std::array<T, 2> data{};
@@ -57,16 +48,22 @@ public:
// Shared memory format
struct Format {
- MemoryBarrier<SteadyClockTimePoint, 0x0> standard_steady_clock_timepoint;
- MemoryBarrier<SystemClockContext, 0x38> standard_local_system_clock_context;
- MemoryBarrier<SystemClockContext, 0x80> standard_network_system_clock_context;
+ MemoryBarrier<Clock::SteadyClockContext, 0x0> standard_steady_clock_timepoint;
+ MemoryBarrier<Clock::SystemClockContext, 0x38> standard_local_system_clock_context;
+ MemoryBarrier<Clock::SystemClockContext, 0x80> standard_network_system_clock_context;
MemoryBarrier<bool, 0xc8> standard_user_system_clock_automatic_correction;
u32_le format_version;
};
static_assert(sizeof(Format) == 0xd8, "Format is an invalid size");
+ void SetupStandardSteadyClock(Core::System& system, const Common::UUID& clock_source_id,
+ Clock::TimeSpanType currentTimePoint);
+ void UpdateLocalSystemClockContext(const Clock::SystemClockContext& context);
+ void UpdateNetworkSystemClockContext(const Clock::SystemClockContext& context);
+ void SetAutomaticCorrectionEnabled(bool is_enabled);
+
private:
- std::shared_ptr<Kernel::SharedMemory> shared_memory_holder{};
+ std::shared_ptr<Kernel::SharedMemory> shared_memory_holder;
Core::System& system;
Format shared_memory_format{};
};
diff --git a/src/core/hle/service/time/time_zone_content_manager.cpp b/src/core/hle/service/time/time_zone_content_manager.cpp
new file mode 100644
index 000000000..57b1a2bca
--- /dev/null
+++ b/src/core/hle/service/time/time_zone_content_manager.cpp
@@ -0,0 +1,125 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <sstream>
+
+#include "common/logging/log.h"
+#include "core/core.h"
+#include "core/file_sys/content_archive.h"
+#include "core/file_sys/nca_metadata.h"
+#include "core/file_sys/registered_cache.h"
+#include "core/file_sys/romfs.h"
+#include "core/file_sys/system_archive/system_archive.h"
+#include "core/hle/service/filesystem/filesystem.h"
+#include "core/hle/service/time/time_manager.h"
+#include "core/hle/service/time/time_zone_content_manager.h"
+
+namespace Service::Time::TimeZone {
+
+constexpr u64 time_zone_binary_titleid{0x010000000000080E};
+
+static FileSys::VirtualDir GetTimeZoneBinary(Core::System& system) {
+ const auto* nand{system.GetFileSystemController().GetSystemNANDContents()};
+ const auto nca{nand->GetEntry(time_zone_binary_titleid, FileSys::ContentRecordType::Data)};
+
+ FileSys::VirtualFile romfs;
+ if (nca) {
+ romfs = nca->GetRomFS();
+ }
+
+ if (!romfs) {
+ romfs = FileSys::SystemArchive::SynthesizeSystemArchive(time_zone_binary_titleid);
+ }
+
+ if (!romfs) {
+ LOG_ERROR(Service_Time, "Failed to find or synthesize {:016X!}", time_zone_binary_titleid);
+ return {};
+ }
+
+ return FileSys::ExtractRomFS(romfs);
+}
+
+static std::vector<std::string> BuildLocationNameCache(Core::System& system) {
+ const FileSys::VirtualDir extracted_romfs{GetTimeZoneBinary(system)};
+ if (!extracted_romfs) {
+ LOG_ERROR(Service_Time, "Failed to extract RomFS for {:016X}!", time_zone_binary_titleid);
+ return {};
+ }
+
+ const FileSys::VirtualFile binary_list{extracted_romfs->GetFile("binaryList.txt")};
+ if (!binary_list) {
+ LOG_ERROR(Service_Time, "{:016X} has no file binaryList.txt!", time_zone_binary_titleid);
+ return {};
+ }
+
+ std::vector<char> raw_data(binary_list->GetSize());
+ binary_list->ReadBytes<char>(raw_data.data(), binary_list->GetSize());
+
+ std::stringstream data_stream{raw_data.data()};
+ std::string name;
+ std::vector<std::string> location_name_cache;
+ while (std::getline(data_stream, name)) {
+ name.pop_back(); // Remove carriage return
+ location_name_cache.emplace_back(std::move(name));
+ }
+ return location_name_cache;
+}
+
+TimeZoneContentManager::TimeZoneContentManager(TimeManager& time_manager, Core::System& system)
+ : system{system}, location_name_cache{BuildLocationNameCache(system)} {
+ if (FileSys::VirtualFile vfs_file; GetTimeZoneInfoFile("GMT", vfs_file) == RESULT_SUCCESS) {
+ const auto time_point{
+ time_manager.GetStandardSteadyClockCore().GetCurrentTimePoint(system)};
+ time_manager.SetupTimeZoneManager("GMT", time_point, location_name_cache.size(), {},
+ vfs_file);
+ } else {
+ time_zone_manager.MarkAsInitialized();
+ }
+}
+
+ResultCode TimeZoneContentManager::LoadTimeZoneRule(TimeZoneRule& rules,
+ const std::string& location_name) const {
+ FileSys::VirtualFile vfs_file;
+ if (const ResultCode result{GetTimeZoneInfoFile(location_name, vfs_file)};
+ result != RESULT_SUCCESS) {
+ return result;
+ }
+
+ return time_zone_manager.ParseTimeZoneRuleBinary(rules, vfs_file);
+}
+
+bool TimeZoneContentManager::IsLocationNameValid(const std::string& location_name) const {
+ return std::find(location_name_cache.begin(), location_name_cache.end(), location_name) !=
+ location_name_cache.end();
+}
+
+ResultCode TimeZoneContentManager::GetTimeZoneInfoFile(const std::string& location_name,
+ FileSys::VirtualFile& vfs_file) const {
+ if (!IsLocationNameValid(location_name)) {
+ return ERROR_TIME_NOT_FOUND;
+ }
+
+ const FileSys::VirtualDir extracted_romfs{GetTimeZoneBinary(system)};
+ if (!extracted_romfs) {
+ LOG_ERROR(Service_Time, "Failed to extract RomFS for {:016X}!", time_zone_binary_titleid);
+ return ERROR_TIME_NOT_FOUND;
+ }
+
+ const FileSys::VirtualDir zoneinfo_dir{extracted_romfs->GetSubdirectory("zoneinfo")};
+ if (!zoneinfo_dir) {
+ LOG_ERROR(Service_Time, "{:016X} has no directory zoneinfo!", time_zone_binary_titleid);
+ return ERROR_TIME_NOT_FOUND;
+ }
+
+ vfs_file = zoneinfo_dir->GetFile(location_name);
+ if (!vfs_file) {
+ LOG_ERROR(Service_Time, "{:016X} has no file \"{}\"!", time_zone_binary_titleid,
+ location_name);
+ return ERROR_TIME_NOT_FOUND;
+ }
+
+ return RESULT_SUCCESS;
+}
+
+} // namespace Service::Time::TimeZone
diff --git a/src/core/hle/service/time/time_zone_content_manager.h b/src/core/hle/service/time/time_zone_content_manager.h
new file mode 100644
index 000000000..4f302c3b9
--- /dev/null
+++ b/src/core/hle/service/time/time_zone_content_manager.h
@@ -0,0 +1,46 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include "core/hle/service/time/time_zone_manager.h"
+
+namespace Core {
+class System;
+}
+
+namespace Service::Time {
+class TimeManager;
+}
+
+namespace Service::Time::TimeZone {
+
+class TimeZoneContentManager final {
+public:
+ TimeZoneContentManager(TimeManager& time_manager, Core::System& system);
+
+ TimeZoneManager& GetTimeZoneManager() {
+ return time_zone_manager;
+ }
+
+ const TimeZoneManager& GetTimeZoneManager() const {
+ return time_zone_manager;
+ }
+
+ ResultCode LoadTimeZoneRule(TimeZoneRule& rules, const std::string& location_name) const;
+
+private:
+ bool IsLocationNameValid(const std::string& location_name) const;
+ ResultCode GetTimeZoneInfoFile(const std::string& location_name,
+ FileSys::VirtualFile& vfs_file) const;
+
+ Core::System& system;
+ TimeZoneManager time_zone_manager;
+ const std::vector<std::string> location_name_cache;
+};
+
+} // namespace Service::Time::TimeZone
diff --git a/src/core/hle/service/time/time_zone_manager.cpp b/src/core/hle/service/time/time_zone_manager.cpp
new file mode 100644
index 000000000..07b553a43
--- /dev/null
+++ b/src/core/hle/service/time/time_zone_manager.cpp
@@ -0,0 +1,1039 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <climits>
+
+#include "common/assert.h"
+#include "common/logging/log.h"
+#include "core/file_sys/content_archive.h"
+#include "core/file_sys/nca_metadata.h"
+#include "core/file_sys/registered_cache.h"
+#include "core/file_sys/romfs.h"
+#include "core/file_sys/system_archive/system_archive.h"
+#include "core/hle/service/time/time_zone_manager.h"
+
+namespace Service::Time::TimeZone {
+
+static constexpr s32 epoch_year{1970};
+static constexpr s32 year_base{1900};
+static constexpr s32 epoch_week_day{4};
+static constexpr s32 seconds_per_minute{60};
+static constexpr s32 minutes_per_hour{60};
+static constexpr s32 hours_per_day{24};
+static constexpr s32 days_per_week{7};
+static constexpr s32 days_per_normal_year{365};
+static constexpr s32 days_per_leap_year{366};
+static constexpr s32 months_per_year{12};
+static constexpr s32 seconds_per_hour{seconds_per_minute * minutes_per_hour};
+static constexpr s32 seconds_per_day{seconds_per_hour * hours_per_day};
+static constexpr s32 years_per_repeat{400};
+static constexpr s64 average_seconds_per_year{31556952};
+static constexpr s64 seconds_per_repeat{years_per_repeat * average_seconds_per_year};
+
+struct Rule {
+ enum class Type : u32 { JulianDay, DayOfYear, MonthNthDayOfWeek };
+ Type rule_type{};
+ s32 day{};
+ s32 week{};
+ s32 month{};
+ s32 transition_time{};
+};
+
+struct CalendarTimeInternal {
+ s64 year{};
+ s8 month{};
+ s8 day{};
+ s8 hour{};
+ s8 minute{};
+ s8 second{};
+ int Compare(const CalendarTimeInternal& other) const {
+ if (year != other.year) {
+ if (year < other.year) {
+ return -1;
+ }
+ return 1;
+ }
+ if (month != other.month) {
+ return month - other.month;
+ }
+ if (day != other.day) {
+ return day - other.day;
+ }
+ if (hour != other.hour) {
+ return hour - other.hour;
+ }
+ if (minute != other.minute) {
+ return minute - other.minute;
+ }
+ if (second != other.second) {
+ return second - other.second;
+ }
+ return {};
+ }
+};
+
+template <typename TResult, typename TOperand>
+static bool SafeAdd(TResult& result, TOperand op) {
+ result = result + op;
+ return true;
+}
+
+template <typename TResult, typename TUnit, typename TBase>
+static bool SafeNormalize(TResult& result, TUnit& unit, TBase base) {
+ TUnit delta{};
+ if (unit >= 0) {
+ delta = unit / base;
+ } else {
+ delta = -1 - (-1 - unit) / base;
+ }
+ unit -= delta * base;
+ return SafeAdd(result, delta);
+}
+
+template <typename T>
+static constexpr bool IsLeapYear(T year) {
+ return ((year) % 4) == 0 && (((year) % 100) != 0 || ((year) % 400) == 0);
+}
+
+template <typename T>
+static constexpr T GetYearLengthInDays(T year) {
+ return IsLeapYear(year) ? days_per_leap_year : days_per_normal_year;
+}
+
+static constexpr s64 GetLeapDaysFromYearPositive(s64 year) {
+ return year / 4 - year / 100 + year / years_per_repeat;
+}
+
+static constexpr s64 GetLeapDaysFromYear(s64 year) {
+ if (year < 0) {
+ return -1 - GetLeapDaysFromYearPositive(-1 - year);
+ } else {
+ return GetLeapDaysFromYearPositive(year);
+ }
+}
+
+static constexpr int GetMonthLength(bool is_leap_year, int month) {
+ constexpr std::array<int, 12> month_lengths{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+ constexpr std::array<int, 12> month_lengths_leap{31, 29, 31, 30, 31, 30,
+ 31, 31, 30, 31, 30, 31};
+ return is_leap_year ? month_lengths_leap[month] : month_lengths[month];
+}
+
+static constexpr bool IsDigit(char value) {
+ return value >= '0' && value <= '9';
+}
+
+static constexpr int GetQZName(const char* name, int offset, char delimiter) {
+ while (name[offset] != '\0' && name[offset] != delimiter) {
+ offset++;
+ }
+ return offset;
+}
+
+static constexpr int GetTZName(const char* name, int offset) {
+ for (char value{name[offset]};
+ value != '\0' && !IsDigit(value) && value != ',' && value != '-' && value != '+';
+ offset++) {
+ value = name[offset];
+ }
+ return offset;
+}
+
+static constexpr bool GetInteger(const char* name, int& offset, int& value, int min, int max) {
+ value = 0;
+ char temp{name[offset]};
+ if (!IsDigit(temp)) {
+ return {};
+ }
+ do {
+ value = value * 10 + (temp - '0');
+ if (value > max) {
+ return {};
+ }
+ temp = name[offset];
+ } while (IsDigit(temp));
+
+ return value >= min;
+}
+
+static constexpr bool GetSeconds(const char* name, int& offset, int& seconds) {
+ seconds = 0;
+ int value{};
+ if (!GetInteger(name, offset, value, 0, hours_per_day * days_per_week - 1)) {
+ return {};
+ }
+ seconds = value * seconds_per_hour;
+
+ if (name[offset] == ':') {
+ offset++;
+ if (!GetInteger(name, offset, value, 0, minutes_per_hour - 1)) {
+ return {};
+ }
+ seconds += value * seconds_per_minute;
+ if (name[offset] == ':') {
+ offset++;
+ if (!GetInteger(name, offset, value, 0, seconds_per_minute)) {
+ return {};
+ }
+ seconds += value;
+ }
+ }
+ return true;
+}
+
+static constexpr bool GetOffset(const char* name, int& offset, int& value) {
+ bool is_negative{};
+ if (name[offset] == '-') {
+ is_negative = true;
+ offset++;
+ } else if (name[offset] == '+') {
+ offset++;
+ }
+ if (!GetSeconds(name, offset, value)) {
+ return {};
+ }
+ if (is_negative) {
+ value = -value;
+ }
+ return true;
+}
+
+static constexpr bool GetRule(const char* name, int& position, Rule& rule) {
+ bool is_valid{};
+ if (name[position] == 'J') {
+ position++;
+ rule.rule_type = Rule::Type::JulianDay;
+ is_valid = GetInteger(name, position, rule.day, 1, days_per_normal_year);
+ } else if (name[position] == 'M') {
+ position++;
+ rule.rule_type = Rule::Type::MonthNthDayOfWeek;
+ is_valid = GetInteger(name, position, rule.month, 1, months_per_year);
+ if (!is_valid) {
+ return {};
+ }
+ if (name[position++] != '.') {
+ return {};
+ }
+ is_valid = GetInteger(name, position, rule.week, 1, 5);
+ if (!is_valid) {
+ return {};
+ }
+ if (name[position++] != '.') {
+ return {};
+ }
+ is_valid = GetInteger(name, position, rule.day, 0, days_per_week - 1);
+ } else if (isdigit(name[position])) {
+ rule.rule_type = Rule::Type::DayOfYear;
+ is_valid = GetInteger(name, position, rule.day, 0, days_per_leap_year - 1);
+ } else {
+ return {};
+ }
+ if (!is_valid) {
+ return {};
+ }
+ if (name[position] == '/') {
+ position++;
+ return GetOffset(name, position, rule.transition_time);
+ } else {
+ rule.transition_time = 2 * seconds_per_hour;
+ }
+ return true;
+}
+
+static constexpr int TransitionTime(int year, Rule rule, int offset) {
+ int value{};
+ switch (rule.rule_type) {
+ case Rule::Type::JulianDay:
+ value = (rule.day - 1) * seconds_per_day;
+ if (IsLeapYear(year) && rule.day >= 60) {
+ value += seconds_per_day;
+ }
+ break;
+ case Rule::Type::DayOfYear:
+ value = rule.day * seconds_per_day;
+ break;
+ case Rule::Type::MonthNthDayOfWeek: {
+ // Use Zeller's Congruence (https://en.wikipedia.org/wiki/Zeller%27s_congruence) to
+ // calculate the day of the week for any Julian or Gregorian calendar date.
+ const int m1{(rule.month + 9) % 12 + 1};
+ const int yy0{(rule.month <= 2) ? (year - 1) : year};
+ const int yy1{yy0 / 100};
+ const int yy2{yy0 % 100};
+ int day_of_week{((26 * m1 - 2) / 10 + 1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7};
+
+ if (day_of_week < 0) {
+ day_of_week += days_per_week;
+ }
+ int day{rule.day - day_of_week};
+ if (day < 0) {
+ day += days_per_week;
+ }
+ for (int i{1}; i < rule.week; i++) {
+ if (day + days_per_week >= GetMonthLength(IsLeapYear(year), rule.month - 1)) {
+ break;
+ }
+ day += days_per_week;
+ }
+
+ value = day * seconds_per_day;
+ for (int index{}; index < rule.month - 1; ++index) {
+ value += GetMonthLength(IsLeapYear(year), index) * seconds_per_day;
+ }
+ break;
+ }
+ default:
+ UNREACHABLE();
+ }
+ return value + rule.transition_time + offset;
+}
+
+static bool ParsePosixName(const char* name, TimeZoneRule& rule) {
+ constexpr char default_rule[]{",M4.1.0,M10.5.0"};
+ const char* std_name{name};
+ int std_len{};
+ int offset{};
+ int std_offset{};
+
+ if (name[offset] == '<') {
+ offset++;
+ std_name = name + offset;
+ const int std_name_offset{offset};
+ offset = GetQZName(name, offset, '>');
+ if (name[offset] != '>') {
+ return {};
+ }
+ std_len = offset - std_name_offset;
+ offset++;
+ } else {
+ offset = GetTZName(name, offset);
+ std_len = offset;
+ }
+ if (!std_len) {
+ return {};
+ }
+ if (!GetOffset(name, offset, std_offset)) {
+ return {};
+ }
+
+ int char_count{std_len + 1};
+ int dest_len{};
+ int dest_offset{};
+ const char* dest_name{name + offset};
+ if (rule.chars.size() < char_count) {
+ return {};
+ }
+
+ if (name[offset] != '\0') {
+ if (name[offset] == '<') {
+ dest_name = name + (++offset);
+ const int dest_name_offset{offset};
+ offset = GetQZName(name, offset, '>');
+ if (name[offset] != '>') {
+ return {};
+ }
+ dest_len = offset - dest_name_offset;
+ offset++;
+ } else {
+ dest_name = name + (offset);
+ offset = GetTZName(name, offset);
+ dest_len = offset;
+ }
+ if (dest_len == 0) {
+ return {};
+ }
+ char_count += dest_len + 1;
+ if (rule.chars.size() < char_count) {
+ return {};
+ }
+ if (name[offset] != '\0' && name[offset] != ',' && name[offset] != ';') {
+ if (!GetOffset(name, offset, dest_offset)) {
+ return {};
+ }
+ } else {
+ dest_offset = std_offset - seconds_per_hour;
+ }
+ if (name[offset] == '\0') {
+ name = default_rule;
+ offset = 0;
+ }
+ if (name[offset] == ',' || name[offset] == ';') {
+ offset++;
+
+ Rule start{};
+ if (!GetRule(name, offset, start)) {
+ return {};
+ }
+ if (name[offset++] != ',') {
+ return {};
+ }
+
+ Rule end{};
+ if (!GetRule(name, offset, end)) {
+ return {};
+ }
+ if (name[offset] != '\0') {
+ return {};
+ }
+
+ rule.type_count = 2;
+ rule.ttis[0].gmt_offset = -dest_offset;
+ rule.ttis[0].is_dst = true;
+ rule.ttis[0].abbreviation_list_index = std_len + 1;
+ rule.ttis[1].gmt_offset = -std_offset;
+ rule.ttis[1].is_dst = false;
+ rule.ttis[1].abbreviation_list_index = 0;
+ rule.default_type = 0;
+
+ s64 jan_first{};
+ int time_count{};
+ int jan_offset{};
+ int year_beginning{epoch_year};
+ do {
+ const int year_seconds{GetYearLengthInDays(year_beginning - 1) * seconds_per_day};
+ year_beginning--;
+ if (!SafeAdd(jan_first, -year_seconds)) {
+ jan_offset = -year_seconds;
+ break;
+ }
+ } while (epoch_year - years_per_repeat / 2 < year_beginning);
+
+ int year_limit{year_beginning + years_per_repeat + 1};
+ int year{};
+ for (year = year_beginning; year < year_limit; year++) {
+ int start_time{TransitionTime(year, start, std_offset)};
+ int end_time{TransitionTime(year, end, dest_offset)};
+ const int year_seconds{GetYearLengthInDays(year) * seconds_per_day};
+ const bool is_reversed{end_time < start_time};
+ if (is_reversed) {
+ int swap{start_time};
+ start_time = end_time;
+ end_time = swap;
+ }
+
+ if (is_reversed ||
+ (start_time < end_time &&
+ (end_time - start_time < (year_seconds + (std_offset - dest_offset))))) {
+ if (rule.ats.size() - 2 < time_count) {
+ break;
+ }
+
+ rule.ats[time_count] = jan_first;
+ if (SafeAdd(rule.ats[time_count], jan_offset + start_time)) {
+ rule.types[time_count++] = is_reversed ? 1 : 0;
+ } else if (jan_offset != 0) {
+ rule.default_type = is_reversed ? 1 : 0;
+ }
+
+ rule.ats[time_count] = jan_first;
+ if (SafeAdd(rule.ats[time_count], jan_offset + end_time)) {
+ rule.types[time_count++] = is_reversed ? 0 : 1;
+ year_limit = year + years_per_repeat + 1;
+ } else if (jan_offset != 0) {
+ rule.default_type = is_reversed ? 0 : 1;
+ }
+ }
+ if (!SafeAdd(jan_first, jan_offset + year_seconds)) {
+ break;
+ }
+ jan_offset = 0;
+ }
+ rule.time_count = time_count;
+ if (time_count == 0) {
+ rule.type_count = 1;
+ } else if (years_per_repeat < year - year_beginning) {
+ rule.go_back = true;
+ rule.go_ahead = true;
+ }
+ } else {
+ if (name[offset] == '\0') {
+ return {};
+ }
+
+ s64 their_std_offset{};
+ for (int index{}; index < rule.time_count; ++index) {
+ const s8 type{rule.types[index]};
+ if (rule.ttis[type].is_standard_time_daylight) {
+ their_std_offset = -rule.ttis[type].gmt_offset;
+ }
+ }
+
+ s64 their_offset{their_std_offset};
+ for (int index{}; index < rule.time_count; ++index) {
+ const s8 type{rule.types[index]};
+ rule.types[index] = rule.ttis[type].is_dst ? 1 : 0;
+ if (!rule.ttis[type].is_gmt) {
+ if (!rule.ttis[type].is_standard_time_daylight) {
+ rule.ats[index] += dest_offset - their_std_offset;
+ } else {
+ rule.ats[index] += std_offset - their_std_offset;
+ }
+ }
+ their_offset = -rule.ttis[type].gmt_offset;
+ if (!rule.ttis[type].is_dst) {
+ their_std_offset = their_offset;
+ }
+ }
+ rule.ttis[0].gmt_offset = -std_offset;
+ rule.ttis[0].is_dst = false;
+ rule.ttis[0].abbreviation_list_index = 0;
+ rule.ttis[1].gmt_offset = -dest_offset;
+ rule.ttis[1].is_dst = true;
+ rule.ttis[1].abbreviation_list_index = std_len + 1;
+ rule.type_count = 2;
+ rule.default_type = 0;
+ }
+ } else {
+ // Default is standard time
+ rule.type_count = 1;
+ rule.time_count = 0;
+ rule.default_type = 0;
+ rule.ttis[0].gmt_offset = -std_offset;
+ rule.ttis[0].is_dst = false;
+ rule.ttis[0].abbreviation_list_index = 0;
+ }
+
+ rule.char_count = char_count;
+ for (int index{}; index < std_len; ++index) {
+ rule.chars[index] = std_name[index];
+ }
+
+ rule.chars[std_len++] = '\0';
+ if (dest_len != 0) {
+ for (int index{}; index < dest_len; ++index) {
+ rule.chars[std_len + index] = dest_name[index];
+ }
+ rule.chars[std_len + dest_len] = '\0';
+ }
+
+ return true;
+}
+
+static bool ParseTimeZoneBinary(TimeZoneRule& time_zone_rule, FileSys::VirtualFile& vfs_file) {
+ TzifHeader header{};
+ if (vfs_file->ReadObject<TzifHeader>(&header) != sizeof(TzifHeader)) {
+ return {};
+ }
+
+ constexpr s32 time_zone_max_leaps{50};
+ constexpr s32 time_zone_max_chars{50};
+ if (!(0 <= header.leap_count && header.leap_count < time_zone_max_leaps &&
+ 0 < header.type_count && header.type_count < time_zone_rule.ttis.size() &&
+ 0 <= header.time_count && header.time_count < time_zone_rule.ats.size() &&
+ 0 <= header.char_count && header.char_count < time_zone_max_chars &&
+ (header.ttis_std_count == header.type_count || header.ttis_std_count == 0) &&
+ (header.ttis_gmt_count == header.type_count || header.ttis_gmt_count == 0))) {
+ return {};
+ }
+ time_zone_rule.time_count = header.time_count;
+ time_zone_rule.type_count = header.type_count;
+ time_zone_rule.char_count = header.char_count;
+
+ int time_count{};
+ u64 read_offset = sizeof(TzifHeader);
+ for (int index{}; index < time_zone_rule.time_count; ++index) {
+ s64_be at{};
+ vfs_file->ReadObject<s64_be>(&at, read_offset);
+ time_zone_rule.types[index] = 1;
+ if (time_count != 0 && at <= time_zone_rule.ats[time_count - 1]) {
+ if (at < time_zone_rule.ats[time_count - 1]) {
+ return {};
+ }
+ time_zone_rule.types[index - 1] = 0;
+ time_count--;
+ }
+ time_zone_rule.ats[time_count++] = at;
+ read_offset += sizeof(s64_be);
+ }
+ time_count = 0;
+ for (int index{}; index < time_zone_rule.time_count; ++index) {
+ const u8 type{*vfs_file->ReadByte(read_offset)};
+ read_offset += sizeof(u8);
+ if (time_zone_rule.time_count <= type) {
+ return {};
+ }
+ if (time_zone_rule.types[index] != 0) {
+ time_zone_rule.types[time_count++] = type;
+ }
+ }
+ time_zone_rule.time_count = time_count;
+ for (int index{}; index < time_zone_rule.type_count; ++index) {
+ TimeTypeInfo& ttis{time_zone_rule.ttis[index]};
+ u32_be gmt_offset{};
+ vfs_file->ReadObject<u32_be>(&gmt_offset, read_offset);
+ read_offset += sizeof(u32_be);
+ ttis.gmt_offset = gmt_offset;
+
+ const u8 dst{*vfs_file->ReadByte(read_offset)};
+ read_offset += sizeof(u8);
+ if (dst >= 2) {
+ return {};
+ }
+ ttis.is_dst = dst != 0;
+
+ const s32 abbreviation_list_index{*vfs_file->ReadByte(read_offset)};
+ read_offset += sizeof(u8);
+ if (abbreviation_list_index >= time_zone_rule.char_count) {
+ return {};
+ }
+ ttis.abbreviation_list_index = abbreviation_list_index;
+ }
+
+ vfs_file->ReadArray(time_zone_rule.chars.data(), time_zone_rule.char_count, read_offset);
+ time_zone_rule.chars[time_zone_rule.char_count] = '\0';
+ read_offset += time_zone_rule.char_count;
+ for (int index{}; index < time_zone_rule.type_count; ++index) {
+ if (header.ttis_std_count == 0) {
+ time_zone_rule.ttis[index].is_standard_time_daylight = false;
+ } else {
+ const u8 dst{*vfs_file->ReadByte(read_offset)};
+ read_offset += sizeof(u8);
+ if (dst >= 2) {
+ return {};
+ }
+ time_zone_rule.ttis[index].is_standard_time_daylight = dst != 0;
+ }
+ }
+
+ for (int index{}; index < time_zone_rule.type_count; ++index) {
+ if (header.ttis_std_count == 0) {
+ time_zone_rule.ttis[index].is_gmt = false;
+ } else {
+ const u8 dst{*vfs_file->ReadByte(read_offset)};
+ read_offset += sizeof(u8);
+ if (dst >= 2) {
+ return {};
+ }
+ time_zone_rule.ttis[index].is_gmt = dst != 0;
+ }
+ }
+
+ const u64 position{(read_offset - sizeof(TzifHeader))};
+ const std::size_t bytes_read{vfs_file->GetSize() - sizeof(TzifHeader) - position};
+ if (bytes_read < 0) {
+ return {};
+ }
+ constexpr s32 time_zone_name_max{255};
+ if (bytes_read > (time_zone_name_max + 1)) {
+ return {};
+ }
+
+ std::array<char, time_zone_name_max + 1> temp_name{};
+ vfs_file->ReadArray(temp_name.data(), bytes_read, read_offset);
+ if (bytes_read > 2 && temp_name[0] == '\n' && temp_name[bytes_read - 1] == '\n' &&
+ time_zone_rule.type_count + 2 <= time_zone_rule.ttis.size()) {
+ temp_name[bytes_read - 1] = '\0';
+
+ std::array<char, time_zone_name_max> name{};
+ std::memcpy(name.data(), temp_name.data() + 1, bytes_read - 1);
+
+ TimeZoneRule temp_rule;
+ if (ParsePosixName(name.data(), temp_rule)) {
+ UNIMPLEMENTED();
+ }
+ }
+ if (time_zone_rule.type_count == 0) {
+ return {};
+ }
+ if (time_zone_rule.time_count > 1) {
+ UNIMPLEMENTED();
+ }
+
+ s32 default_type{};
+
+ for (default_type = 0; default_type < time_zone_rule.time_count; default_type++) {
+ if (time_zone_rule.types[default_type] == 0) {
+ break;
+ }
+ }
+
+ default_type = default_type < time_zone_rule.time_count ? -1 : 0;
+ if (default_type < 0 && time_zone_rule.time_count > 0 &&
+ time_zone_rule.ttis[time_zone_rule.types[0]].is_dst) {
+ default_type = time_zone_rule.types[0];
+ while (--default_type >= 0) {
+ if (!time_zone_rule.ttis[default_type].is_dst) {
+ break;
+ }
+ }
+ }
+ if (default_type < 0) {
+ default_type = 0;
+ while (time_zone_rule.ttis[default_type].is_dst) {
+ if (++default_type >= time_zone_rule.type_count) {
+ default_type = 0;
+ break;
+ }
+ }
+ }
+ time_zone_rule.default_type = default_type;
+ return true;
+}
+
+static ResultCode CreateCalendarTime(s64 time, int gmt_offset, CalendarTimeInternal& calendar_time,
+ CalendarAdditionalInfo& calendar_additional_info) {
+ s64 year{epoch_year};
+ s64 time_days{time / seconds_per_day};
+ s64 remaining_seconds{time % seconds_per_day};
+ while (time_days < 0 || time_days >= GetYearLengthInDays(year)) {
+ s64 delta = time_days / days_per_leap_year;
+ if (!delta) {
+ delta = time_days < 0 ? -1 : 1;
+ }
+ s64 new_year{year};
+ if (!SafeAdd(new_year, delta)) {
+ return ERROR_OUT_OF_RANGE;
+ }
+ time_days -= (new_year - year) * days_per_normal_year;
+ time_days -= GetLeapDaysFromYear(new_year - 1) - GetLeapDaysFromYear(year - 1);
+ year = new_year;
+ }
+
+ s64 day_of_year{time_days};
+ remaining_seconds += gmt_offset;
+ while (remaining_seconds < 0) {
+ remaining_seconds += seconds_per_day;
+ day_of_year--;
+ }
+
+ while (remaining_seconds >= seconds_per_day) {
+ remaining_seconds -= seconds_per_day;
+ day_of_year++;
+ }
+
+ while (day_of_year < 0) {
+ if (!SafeAdd(year, -1)) {
+ return ERROR_OUT_OF_RANGE;
+ }
+ day_of_year += GetYearLengthInDays(year);
+ }
+
+ while (day_of_year >= GetYearLengthInDays(year)) {
+ day_of_year -= GetYearLengthInDays(year);
+ if (!SafeAdd(year, 1)) {
+ return ERROR_OUT_OF_RANGE;
+ }
+ }
+
+ calendar_time.year = year;
+ calendar_additional_info.day_of_year = static_cast<u32>(day_of_year);
+ s64 day_of_week{
+ (epoch_week_day +
+ ((year - epoch_year) % days_per_week) * (days_per_normal_year % days_per_week) +
+ GetLeapDaysFromYear(year - 1) - GetLeapDaysFromYear(epoch_year - 1) + day_of_year) %
+ days_per_week};
+ if (day_of_week < 0) {
+ day_of_week += days_per_week;
+ }
+
+ calendar_additional_info.day_of_week = static_cast<u32>(day_of_week);
+ calendar_time.hour = static_cast<s8>((remaining_seconds / seconds_per_hour) % seconds_per_hour);
+ remaining_seconds %= seconds_per_hour;
+ calendar_time.minute = static_cast<s8>(remaining_seconds / seconds_per_minute);
+ calendar_time.second = static_cast<s8>(remaining_seconds % seconds_per_minute);
+
+ for (calendar_time.month = 0;
+ day_of_year >= GetMonthLength(IsLeapYear(year), calendar_time.month);
+ ++calendar_time.month) {
+ day_of_year -= GetMonthLength(IsLeapYear(year), calendar_time.month);
+ }
+
+ calendar_time.day = static_cast<s8>(day_of_year + 1);
+ calendar_additional_info.is_dst = false;
+ calendar_additional_info.gmt_offset = gmt_offset;
+
+ return RESULT_SUCCESS;
+}
+
+static ResultCode ToCalendarTimeInternal(const TimeZoneRule& rules, s64 time,
+ CalendarTimeInternal& calendar_time,
+ CalendarAdditionalInfo& calendar_additional_info) {
+ if ((rules.go_ahead && time < rules.ats[0]) ||
+ (rules.go_back && time > rules.ats[rules.time_count - 1])) {
+ s64 seconds{};
+ if (time < rules.ats[0]) {
+ seconds = rules.ats[0] - time;
+ } else {
+ seconds = time - rules.ats[rules.time_count - 1];
+ }
+ seconds--;
+
+ const s64 years{(seconds / seconds_per_repeat + 1) * years_per_repeat};
+ seconds = years * average_seconds_per_year;
+
+ s64 new_time{time};
+ if (time < rules.ats[0]) {
+ new_time += seconds;
+ } else {
+ new_time -= seconds;
+ }
+ if (new_time < rules.ats[0] && new_time > rules.ats[rules.time_count - 1]) {
+ return ERROR_TIME_NOT_FOUND;
+ }
+ if (const ResultCode result{
+ ToCalendarTimeInternal(rules, new_time, calendar_time, calendar_additional_info)};
+ result != RESULT_SUCCESS) {
+ return result;
+ }
+ if (time < rules.ats[0]) {
+ calendar_time.year -= years;
+ } else {
+ calendar_time.year += years;
+ }
+
+ return RESULT_SUCCESS;
+ }
+
+ s32 tti_index{};
+ if (rules.time_count == 0 || time < rules.ats[0]) {
+ tti_index = rules.default_type;
+ } else {
+ s32 low{1};
+ s32 high{rules.time_count};
+ while (low < high) {
+ s32 mid{(low + high) >> 1};
+ if (time < rules.ats[mid]) {
+ high = mid;
+ } else {
+ low = mid + 1;
+ }
+ }
+ tti_index = rules.types[low - 1];
+ }
+
+ if (const ResultCode result{CreateCalendarTime(time, rules.ttis[tti_index].gmt_offset,
+ calendar_time, calendar_additional_info)};
+ result != RESULT_SUCCESS) {
+ return result;
+ }
+
+ calendar_additional_info.is_dst = rules.ttis[tti_index].is_dst;
+ const char* time_zone{&rules.chars[rules.ttis[tti_index].abbreviation_list_index]};
+ for (int index{}; time_zone[index] != '\0'; ++index) {
+ calendar_additional_info.timezone_name[index] = time_zone[index];
+ }
+ return RESULT_SUCCESS;
+}
+
+static ResultCode ToCalendarTimeImpl(const TimeZoneRule& rules, s64 time, CalendarInfo& calendar) {
+ CalendarTimeInternal calendar_time{};
+ const ResultCode result{
+ ToCalendarTimeInternal(rules, time, calendar_time, calendar.additiona_info)};
+ calendar.time.year = static_cast<s16>(calendar_time.year);
+ calendar.time.month = calendar_time.month + 1; // Internal impl. uses 0-indexed month
+ calendar.time.day = calendar_time.day;
+ calendar.time.hour = calendar_time.hour;
+ calendar.time.minute = calendar_time.minute;
+ calendar.time.second = calendar_time.second;
+ return result;
+}
+
+TimeZoneManager::TimeZoneManager() = default;
+TimeZoneManager::~TimeZoneManager() = default;
+
+ResultCode TimeZoneManager::ToCalendarTime(const TimeZoneRule& rules, s64 time,
+ CalendarInfo& calendar) const {
+ return ToCalendarTimeImpl(rules, time, calendar);
+}
+
+ResultCode TimeZoneManager::SetDeviceLocationNameWithTimeZoneRule(const std::string& location_name,
+ FileSys::VirtualFile& vfs_file) {
+ TimeZoneRule rule{};
+ if (ParseTimeZoneBinary(rule, vfs_file)) {
+ device_location_name = location_name;
+ time_zone_rule = rule;
+ return RESULT_SUCCESS;
+ }
+ return ERROR_TIME_ZONE_CONVERSION_FAILED;
+}
+
+ResultCode TimeZoneManager::SetUpdatedTime(const Clock::SteadyClockTimePoint& value) {
+ time_zone_update_time_point = value;
+ return RESULT_SUCCESS;
+}
+
+ResultCode TimeZoneManager::ToCalendarTimeWithMyRules(s64 time, CalendarInfo& calendar) const {
+ if (is_initialized) {
+ return ToCalendarTime(time_zone_rule, time, calendar);
+ } else {
+ return ERROR_UNINITIALIZED_CLOCK;
+ }
+}
+
+ResultCode TimeZoneManager::ParseTimeZoneRuleBinary(TimeZoneRule& rules,
+ FileSys::VirtualFile& vfs_file) const {
+ if (!ParseTimeZoneBinary(rules, vfs_file)) {
+ return ERROR_TIME_ZONE_CONVERSION_FAILED;
+ }
+ return RESULT_SUCCESS;
+}
+
+ResultCode TimeZoneManager::ToPosixTime(const TimeZoneRule& rules,
+ const CalendarTime& calendar_time, s64& posix_time) const {
+ posix_time = 0;
+
+ CalendarTimeInternal internal_time{};
+ internal_time.year = calendar_time.year;
+ internal_time.month = calendar_time.month - 1; // Internal impl. uses 0-indexed month
+ internal_time.day = calendar_time.day;
+ internal_time.hour = calendar_time.hour;
+ internal_time.minute = calendar_time.minute;
+ internal_time.second = calendar_time.second;
+
+ s32 hour{internal_time.hour};
+ s32 minute{internal_time.minute};
+ if (!SafeNormalize(hour, minute, minutes_per_hour)) {
+ return ERROR_OVERFLOW;
+ }
+ internal_time.minute = static_cast<s8>(minute);
+
+ s32 day{internal_time.day};
+ if (!SafeNormalize(day, hour, hours_per_day)) {
+ return ERROR_OVERFLOW;
+ }
+ internal_time.day = static_cast<s8>(day);
+ internal_time.hour = static_cast<s8>(hour);
+
+ s64 year{internal_time.year};
+ s64 month{internal_time.month};
+ if (!SafeNormalize(year, month, months_per_year)) {
+ return ERROR_OVERFLOW;
+ }
+ internal_time.month = static_cast<s8>(month);
+
+ if (!SafeAdd(year, year_base)) {
+ return ERROR_OVERFLOW;
+ }
+
+ while (day <= 0) {
+ if (!SafeAdd(year, -1)) {
+ return ERROR_OVERFLOW;
+ }
+ s64 temp_year{year};
+ if (1 < internal_time.month) {
+ ++temp_year;
+ }
+ day += static_cast<s32>(GetYearLengthInDays(temp_year));
+ }
+
+ while (day > days_per_leap_year) {
+ s64 temp_year{year};
+ if (1 < internal_time.month) {
+ temp_year++;
+ }
+ day -= static_cast<s32>(GetYearLengthInDays(temp_year));
+ if (!SafeAdd(year, 1)) {
+ return ERROR_OVERFLOW;
+ }
+ }
+
+ while (true) {
+ const s32 month_length{GetMonthLength(IsLeapYear(year), internal_time.month)};
+ if (day <= month_length) {
+ break;
+ }
+ day -= month_length;
+ internal_time.month++;
+ if (internal_time.month >= months_per_year) {
+ internal_time.month = 0;
+ if (!SafeAdd(year, 1)) {
+ return ERROR_OVERFLOW;
+ }
+ }
+ }
+ internal_time.day = static_cast<s8>(day);
+
+ if (!SafeAdd(year, -year_base)) {
+ return ERROR_OVERFLOW;
+ }
+ internal_time.year = year;
+
+ s32 saved_seconds{};
+ if (internal_time.second >= 0 && internal_time.second < seconds_per_minute) {
+ saved_seconds = 0;
+ } else if (year + year_base < epoch_year) {
+ s32 second{internal_time.second};
+ if (!SafeAdd(second, 1 - seconds_per_minute)) {
+ return ERROR_OVERFLOW;
+ }
+ saved_seconds = second;
+ internal_time.second = 1 - seconds_per_minute;
+ } else {
+ saved_seconds = internal_time.second;
+ internal_time.second = 0;
+ }
+
+ s64 low{LLONG_MIN};
+ s64 high{LLONG_MAX};
+ while (true) {
+ s64 pivot{low / 2 + high / 2};
+ if (pivot < low) {
+ pivot = low;
+ } else if (pivot > high) {
+ pivot = high;
+ }
+ s32 direction{};
+ CalendarTimeInternal candidate_calendar_time{};
+ CalendarAdditionalInfo unused{};
+ if (ToCalendarTimeInternal(rules, pivot, candidate_calendar_time, unused) !=
+ RESULT_SUCCESS) {
+ if (pivot > 0) {
+ direction = 1;
+ } else {
+ direction = -1;
+ }
+ } else {
+ direction = candidate_calendar_time.Compare(internal_time);
+ }
+ if (!direction) {
+ const s64 time_result{pivot + saved_seconds};
+ if ((time_result < pivot) != (saved_seconds < 0)) {
+ return ERROR_OVERFLOW;
+ }
+ posix_time = time_result;
+ break;
+ } else {
+ if (pivot == low) {
+ if (pivot == LLONG_MAX) {
+ return ERROR_TIME_NOT_FOUND;
+ }
+ pivot++;
+ low++;
+ } else if (pivot == high) {
+ if (pivot == LLONG_MIN) {
+ return ERROR_TIME_NOT_FOUND;
+ }
+ pivot--;
+ high--;
+ }
+ if (low > high) {
+ return ERROR_TIME_NOT_FOUND;
+ }
+ if (direction > 0) {
+ high = pivot;
+ } else {
+ low = pivot;
+ }
+ }
+ }
+ return RESULT_SUCCESS;
+}
+
+ResultCode TimeZoneManager::ToPosixTimeWithMyRule(const CalendarTime& calendar_time,
+ s64& posix_time) const {
+ if (is_initialized) {
+ return ToPosixTime(time_zone_rule, calendar_time, posix_time);
+ }
+ posix_time = 0;
+ return ERROR_UNINITIALIZED_CLOCK;
+}
+
+ResultCode TimeZoneManager::GetDeviceLocationName(LocationName& value) const {
+ if (!is_initialized) {
+ return ERROR_UNINITIALIZED_CLOCK;
+ }
+ std::memcpy(value.data(), device_location_name.c_str(), device_location_name.size());
+ return RESULT_SUCCESS;
+}
+
+} // namespace Service::Time::TimeZone
diff --git a/src/core/hle/service/time/time_zone_manager.h b/src/core/hle/service/time/time_zone_manager.h
new file mode 100644
index 000000000..aaab0a1e0
--- /dev/null
+++ b/src/core/hle/service/time/time_zone_manager.h
@@ -0,0 +1,54 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <string>
+
+#include "common/common_types.h"
+#include "core/file_sys/vfs_types.h"
+#include "core/hle/service/time/clock_types.h"
+#include "core/hle/service/time/time_zone_types.h"
+
+namespace Service::Time::TimeZone {
+
+class TimeZoneManager final {
+public:
+ TimeZoneManager();
+ ~TimeZoneManager();
+
+ void SetTotalLocationNameCount(std::size_t value) {
+ total_location_name_count = value;
+ }
+
+ void SetTimeZoneRuleVersion(const u128& value) {
+ time_zone_rule_version = value;
+ }
+
+ void MarkAsInitialized() {
+ is_initialized = true;
+ }
+
+ ResultCode SetDeviceLocationNameWithTimeZoneRule(const std::string& location_name,
+ FileSys::VirtualFile& vfs_file);
+ ResultCode SetUpdatedTime(const Clock::SteadyClockTimePoint& value);
+ ResultCode GetDeviceLocationName(TimeZone::LocationName& value) const;
+ ResultCode ToCalendarTime(const TimeZoneRule& rules, s64 time, CalendarInfo& calendar) const;
+ ResultCode ToCalendarTimeWithMyRules(s64 time, CalendarInfo& calendar) const;
+ ResultCode ParseTimeZoneRuleBinary(TimeZoneRule& rules, FileSys::VirtualFile& vfs_file) const;
+ ResultCode ToPosixTime(const TimeZoneRule& rules, const CalendarTime& calendar_time,
+ s64& posix_time) const;
+ ResultCode ToPosixTimeWithMyRule(const CalendarTime& calendar_time, s64& posix_time) const;
+
+private:
+ bool is_initialized{};
+ TimeZoneRule time_zone_rule{};
+ std::string device_location_name{"GMT"};
+ u128 time_zone_rule_version{};
+ std::size_t total_location_name_count{};
+ Clock::SteadyClockTimePoint time_zone_update_time_point{
+ Clock::SteadyClockTimePoint::GetRandom()};
+};
+
+} // namespace Service::Time::TimeZone
diff --git a/src/core/hle/service/time/time_zone_service.cpp b/src/core/hle/service/time/time_zone_service.cpp
new file mode 100644
index 000000000..db57ae069
--- /dev/null
+++ b/src/core/hle/service/time/time_zone_service.cpp
@@ -0,0 +1,170 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/logging/log.h"
+#include "core/hle/ipc_helpers.h"
+#include "core/hle/service/time/time_zone_content_manager.h"
+#include "core/hle/service/time/time_zone_service.h"
+#include "core/hle/service/time/time_zone_types.h"
+
+namespace Service::Time {
+
+ITimeZoneService ::ITimeZoneService(TimeZone::TimeZoneContentManager& time_zone_content_manager)
+ : ServiceFramework("ITimeZoneService"), time_zone_content_manager{time_zone_content_manager} {
+ static const FunctionInfo functions[] = {
+ {0, &ITimeZoneService::GetDeviceLocationName, "GetDeviceLocationName"},
+ {1, nullptr, "SetDeviceLocationName"},
+ {2, nullptr, "GetTotalLocationNameCount"},
+ {3, nullptr, "LoadLocationNameList"},
+ {4, &ITimeZoneService::LoadTimeZoneRule, "LoadTimeZoneRule"},
+ {5, nullptr, "GetTimeZoneRuleVersion"},
+ {100, &ITimeZoneService::ToCalendarTime, "ToCalendarTime"},
+ {101, &ITimeZoneService::ToCalendarTimeWithMyRule, "ToCalendarTimeWithMyRule"},
+ {201, &ITimeZoneService::ToPosixTime, "ToPosixTime"},
+ {202, &ITimeZoneService::ToPosixTimeWithMyRule, "ToPosixTimeWithMyRule"},
+ };
+ RegisterHandlers(functions);
+}
+
+void ITimeZoneService::GetDeviceLocationName(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_Time, "called");
+
+ TimeZone::LocationName location_name{};
+ if (const ResultCode result{
+ time_zone_content_manager.GetTimeZoneManager().GetDeviceLocationName(location_name)};
+ result != RESULT_SUCCESS) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+ return;
+ }
+
+ IPC::ResponseBuilder rb{ctx, (sizeof(location_name) / 4) + 2};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushRaw(location_name);
+}
+
+void ITimeZoneService::LoadTimeZoneRule(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto raw_location_name{rp.PopRaw<std::array<u8, 0x24>>()};
+
+ std::string location_name;
+ for (const auto& byte : raw_location_name) {
+ // Strip extra bytes
+ if (byte == '\0') {
+ break;
+ }
+ location_name.push_back(byte);
+ }
+
+ LOG_DEBUG(Service_Time, "called, location_name={}", location_name);
+
+ TimeZone::TimeZoneRule time_zone_rule{};
+ if (const ResultCode result{
+ time_zone_content_manager.LoadTimeZoneRule(time_zone_rule, location_name)};
+ result != RESULT_SUCCESS) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+ return;
+ }
+
+ std::vector<u8> time_zone_rule_outbuffer(sizeof(TimeZone::TimeZoneRule));
+ std::memcpy(time_zone_rule_outbuffer.data(), &time_zone_rule, sizeof(TimeZone::TimeZoneRule));
+ ctx.WriteBuffer(time_zone_rule_outbuffer);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
+
+void ITimeZoneService::ToCalendarTime(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto posix_time{rp.Pop<s64>()};
+
+ LOG_DEBUG(Service_Time, "called, posix_time=0x{:016X}", posix_time);
+
+ TimeZone::TimeZoneRule time_zone_rule{};
+ const auto buffer{ctx.ReadBuffer()};
+ std::memcpy(&time_zone_rule, buffer.data(), buffer.size());
+
+ TimeZone::CalendarInfo calendar_info{};
+ if (const ResultCode result{time_zone_content_manager.GetTimeZoneManager().ToCalendarTime(
+ time_zone_rule, posix_time, calendar_info)};
+ result != RESULT_SUCCESS) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+ return;
+ }
+
+ IPC::ResponseBuilder rb{ctx, 2 + (sizeof(TimeZone::CalendarInfo) / 4)};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushRaw(calendar_info);
+}
+
+void ITimeZoneService::ToCalendarTimeWithMyRule(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto posix_time{rp.Pop<s64>()};
+
+ LOG_DEBUG(Service_Time, "called, posix_time=0x{:016X}", posix_time);
+
+ TimeZone::CalendarInfo calendar_info{};
+ if (const ResultCode result{
+ time_zone_content_manager.GetTimeZoneManager().ToCalendarTimeWithMyRules(
+ posix_time, calendar_info)};
+ result != RESULT_SUCCESS) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+ return;
+ }
+
+ IPC::ResponseBuilder rb{ctx, 2 + (sizeof(TimeZone::CalendarInfo) / 4)};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushRaw(calendar_info);
+}
+
+void ITimeZoneService::ToPosixTime(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_Time, "called");
+
+ IPC::RequestParser rp{ctx};
+ const auto calendar_time{rp.PopRaw<TimeZone::CalendarTime>()};
+ TimeZone::TimeZoneRule time_zone_rule{};
+ std::memcpy(&time_zone_rule, ctx.ReadBuffer().data(), sizeof(TimeZone::TimeZoneRule));
+
+ s64 posix_time{};
+ if (const ResultCode result{time_zone_content_manager.GetTimeZoneManager().ToPosixTime(
+ time_zone_rule, calendar_time, posix_time)};
+ result != RESULT_SUCCESS) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+ return;
+ }
+
+ // TODO(bunnei): Handle multiple times
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushRaw<u32>(1); // Number of times we're returning
+ ctx.WriteBuffer(&posix_time, sizeof(s64));
+}
+
+void ITimeZoneService::ToPosixTimeWithMyRule(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_Time, "called");
+
+ IPC::RequestParser rp{ctx};
+ const auto calendar_time{rp.PopRaw<TimeZone::CalendarTime>()};
+
+ s64 posix_time{};
+ if (const ResultCode result{
+ time_zone_content_manager.GetTimeZoneManager().ToPosixTimeWithMyRule(calendar_time,
+ posix_time)};
+ result != RESULT_SUCCESS) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+ return;
+ }
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushRaw<u32>(1); // Number of times we're returning
+ ctx.WriteBuffer(&posix_time, sizeof(s64));
+}
+
+} // namespace Service::Time
diff --git a/src/core/hle/service/time/time_zone_service.h b/src/core/hle/service/time/time_zone_service.h
new file mode 100644
index 000000000..cb495748b
--- /dev/null
+++ b/src/core/hle/service/time/time_zone_service.h
@@ -0,0 +1,31 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+namespace Service::Time {
+
+namespace TimeZone {
+class TimeZoneContentManager;
+}
+
+class ITimeZoneService final : public ServiceFramework<ITimeZoneService> {
+public:
+ explicit ITimeZoneService(TimeZone::TimeZoneContentManager& time_zone_manager);
+
+private:
+ void GetDeviceLocationName(Kernel::HLERequestContext& ctx);
+ void LoadTimeZoneRule(Kernel::HLERequestContext& ctx);
+ void ToCalendarTime(Kernel::HLERequestContext& ctx);
+ void ToCalendarTimeWithMyRule(Kernel::HLERequestContext& ctx);
+ void ToPosixTime(Kernel::HLERequestContext& ctx);
+ void ToPosixTimeWithMyRule(Kernel::HLERequestContext& ctx);
+
+private:
+ TimeZone::TimeZoneContentManager& time_zone_content_manager;
+};
+
+} // namespace Service::Time
diff --git a/src/core/hle/service/time/time_zone_types.h b/src/core/hle/service/time/time_zone_types.h
new file mode 100644
index 000000000..9be15b53e
--- /dev/null
+++ b/src/core/hle/service/time/time_zone_types.h
@@ -0,0 +1,87 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+#include "common/swap.h"
+
+namespace Service::Time::TimeZone {
+
+using LocationName = std::array<char, 0x24>;
+
+/// https://switchbrew.org/wiki/Glue_services#ttinfo
+struct TimeTypeInfo {
+ s32 gmt_offset{};
+ u8 is_dst{};
+ INSERT_PADDING_BYTES(3);
+ s32 abbreviation_list_index{};
+ u8 is_standard_time_daylight{};
+ u8 is_gmt{};
+ INSERT_PADDING_BYTES(2);
+};
+static_assert(sizeof(TimeTypeInfo) == 0x10, "TimeTypeInfo is incorrect size");
+
+/// https://switchbrew.org/wiki/Glue_services#TimeZoneRule
+struct TimeZoneRule {
+ s32 time_count{};
+ s32 type_count{};
+ s32 char_count{};
+ u8 go_back{};
+ u8 go_ahead{};
+ INSERT_PADDING_BYTES(2);
+ std::array<s64, 1000> ats{};
+ std::array<s8, 1000> types{};
+ std::array<TimeTypeInfo, 128> ttis{};
+ std::array<char, 512> chars{};
+ s32 default_type{};
+ INSERT_PADDING_BYTES(0x12C4);
+};
+static_assert(sizeof(TimeZoneRule) == 0x4000, "TimeZoneRule is incorrect size");
+
+/// https://switchbrew.org/wiki/Glue_services#CalendarAdditionalInfo
+struct CalendarAdditionalInfo {
+ u32 day_of_week{};
+ u32 day_of_year{};
+ std::array<char, 8> timezone_name;
+ u32 is_dst{};
+ s32 gmt_offset{};
+};
+static_assert(sizeof(CalendarAdditionalInfo) == 0x18, "CalendarAdditionalInfo is incorrect size");
+
+/// https://switchbrew.org/wiki/Glue_services#CalendarTime
+struct CalendarTime {
+ s16 year{};
+ s8 month{};
+ s8 day{};
+ s8 hour{};
+ s8 minute{};
+ s8 second{};
+ INSERT_PADDING_BYTES(1);
+};
+static_assert(sizeof(CalendarTime) == 0x8, "CalendarTime is incorrect size");
+
+struct CalendarInfo {
+ CalendarTime time{};
+ CalendarAdditionalInfo additiona_info{};
+};
+static_assert(sizeof(CalendarInfo) == 0x20, "CalendarInfo is incorrect size");
+
+struct TzifHeader {
+ u32_be magic{};
+ u8 version{};
+ INSERT_PADDING_BYTES(15);
+ s32_be ttis_gmt_count{};
+ s32_be ttis_std_count{};
+ s32_be leap_count{};
+ s32_be time_count{};
+ s32_be type_count{};
+ s32_be char_count{};
+};
+static_assert(sizeof(TzifHeader) == 0x2C, "TzifHeader is incorrect size");
+
+} // namespace Service::Time::TimeZone
diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp
index f1795fdd6..8908e5328 100644
--- a/src/core/loader/elf.cpp
+++ b/src/core/loader/elf.cpp
@@ -335,7 +335,8 @@ Kernel::CodeSet ElfReader::LoadInto(VAddr vaddr) {
codeset_segment->addr = segment_addr;
codeset_segment->size = aligned_size;
- memcpy(&program_image[current_image_position], GetSegmentPtr(i), p->p_filesz);
+ std::memcpy(program_image.data() + current_image_position, GetSegmentPtr(i),
+ p->p_filesz);
current_image_position += aligned_size;
}
}
diff --git a/src/core/loader/kip.cpp b/src/core/loader/kip.cpp
index 474b55cb1..092103abe 100644
--- a/src/core/loader/kip.cpp
+++ b/src/core/loader/kip.cpp
@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <cstring>
#include "core/file_sys/kernel_executable.h"
#include "core/file_sys/program_metadata.h"
#include "core/gdbstub/gdbstub.h"
@@ -76,8 +77,8 @@ AppLoader::LoadResult AppLoader_KIP::Load(Kernel::Process& process) {
segment.addr = offset;
segment.offset = offset;
segment.size = PageAlignSize(static_cast<u32>(data.size()));
- program_image.resize(offset);
- program_image.insert(program_image.end(), data.begin(), data.end());
+ program_image.resize(offset + data.size());
+ std::memcpy(program_image.data() + offset, data.data(), data.size());
};
load_segment(codeset.CodeSegment(), kip->GetTextSection(), kip->GetTextOffset());
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index f629892ae..044067a5b 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include <cinttypes>
+#include <cstring>
#include <vector>
#include "common/common_funcs.h"
@@ -96,15 +97,21 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
if (nso_header.IsSegmentCompressed(i)) {
data = DecompressSegment(data, nso_header.segments[i]);
}
- program_image.resize(nso_header.segments[i].location);
- program_image.insert(program_image.end(), data.begin(), data.end());
+ program_image.resize(nso_header.segments[i].location +
+ PageAlignSize(static_cast<u32>(data.size())));
+ std::memcpy(program_image.data() + nso_header.segments[i].location, data.data(),
+ data.size());
codeset.segments[i].addr = nso_header.segments[i].location;
codeset.segments[i].offset = nso_header.segments[i].location;
codeset.segments[i].size = PageAlignSize(static_cast<u32>(data.size()));
}
- if (should_pass_arguments && !Settings::values.program_args.empty()) {
- const auto arg_data = Settings::values.program_args;
+ if (should_pass_arguments) {
+ std::vector<u8> arg_data{Settings::values.program_args.begin(),
+ Settings::values.program_args.end()};
+ if (arg_data.empty()) {
+ arg_data.resize(NSO_ARGUMENT_DEFAULT_SIZE);
+ }
codeset.DataSegment().size += NSO_ARGUMENT_DATA_ALLOCATION_SIZE;
NSOArgumentHeader args_header{
NSO_ARGUMENT_DATA_ALLOCATION_SIZE, static_cast<u32_le>(arg_data.size()), {}};
@@ -139,12 +146,12 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
std::vector<u8> pi_header;
pi_header.insert(pi_header.begin(), reinterpret_cast<u8*>(&nso_header),
reinterpret_cast<u8*>(&nso_header) + sizeof(NSOHeader));
- pi_header.insert(pi_header.begin() + sizeof(NSOHeader), program_image.begin(),
- program_image.end());
+ pi_header.insert(pi_header.begin() + sizeof(NSOHeader), program_image.data(),
+ program_image.data() + program_image.size());
pi_header = pm->PatchNSO(pi_header, file.GetName());
- std::copy(pi_header.begin() + sizeof(NSOHeader), pi_header.end(), program_image.begin());
+ std::copy(pi_header.begin() + sizeof(NSOHeader), pi_header.end(), program_image.data());
}
// Apply cheats if they exist and the program has a valid title ID
diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h
index 58cbe162d..d2d600cd9 100644
--- a/src/core/loader/nso.h
+++ b/src/core/loader/nso.h
@@ -56,6 +56,8 @@ static_assert(sizeof(NSOHeader) == 0x100, "NSOHeader has incorrect size.");
static_assert(std::is_trivially_copyable_v<NSOHeader>, "NSOHeader must be trivially copyable.");
constexpr u64 NSO_ARGUMENT_DATA_ALLOCATION_SIZE = 0x9000;
+// NOTE: Official software default argument state is unverified.
+constexpr u64 NSO_ARGUMENT_DEFAULT_SIZE = 1;
struct NSOArgumentHeader {
u32_le allocated_size;
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 3c2a29d9b..f0888327f 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -14,6 +14,7 @@
#include "common/swap.h"
#include "core/arm/arm_interface.h"
#include "core/core.h"
+#include "core/hle/kernel/physical_memory.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/vm_manager.h"
#include "core/memory.h"
@@ -38,6 +39,11 @@ struct Memory::Impl {
system.ArmInterface(3).PageTableChanged(*current_page_table, address_space_width);
}
+ void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size,
+ Kernel::PhysicalMemory& memory, VAddr offset) {
+ MapMemoryRegion(page_table, base, size, memory.data() + offset);
+ }
+
void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, u8* target) {
ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size);
ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base);
@@ -601,6 +607,11 @@ void Memory::SetCurrentPageTable(Kernel::Process& process) {
impl->SetCurrentPageTable(process);
}
+void Memory::MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size,
+ Kernel::PhysicalMemory& memory, VAddr offset) {
+ impl->MapMemoryRegion(page_table, base, size, memory, offset);
+}
+
void Memory::MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, u8* target) {
impl->MapMemoryRegion(page_table, base, size, target);
}
diff --git a/src/core/memory.h b/src/core/memory.h
index 1428a6d60..8913a9da4 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -19,8 +19,9 @@ class System;
}
namespace Kernel {
+class PhysicalMemory;
class Process;
-}
+} // namespace Kernel
namespace Memory {
@@ -66,6 +67,19 @@ public:
void SetCurrentPageTable(Kernel::Process& process);
/**
+ * Maps an physical buffer onto a region of the emulated process address space.
+ *
+ * @param page_table The page table of the emulated process.
+ * @param base The address to start mapping at. Must be page-aligned.
+ * @param size The amount of bytes to map. Must be page-aligned.
+ * @param memory Physical buffer with the memory backing the mapping. Must be of length
+ * at least `size + offset`.
+ * @param offset The offset within the physical memory. Must be page-aligned.
+ */
+ void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size,
+ Kernel::PhysicalMemory& memory, VAddr offset);
+
+ /**
* Maps an allocated buffer onto a region of the emulated process address space.
*
* @param page_table The page table of the emulated process.
diff --git a/src/core/settings.h b/src/core/settings.h
index 9c98a9287..421e76f5f 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -401,6 +401,9 @@ struct Values {
std::string motion_device;
TouchscreenInput touchscreen;
std::atomic_bool is_device_reload_pending{true};
+ std::string udp_input_address;
+ u16 udp_input_port;
+ u8 udp_pad_index;
// Core
bool use_multi_core;