diff options
Diffstat (limited to 'src/core')
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; |