mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-11-26 06:54:15 +01:00
Implement full timezone service support
This serves as an extension to the initial time commit and combined they provide a complete implementation of everything application facing in time. psc:ITimeZoneService and glue:ITimeZoneService are used to convert between POSIX and calendar times according to the device location. Timezone binaries are used during the conversion, details of them can be read about in the previous commit. This is based off my own glue RE and Thog's time RE.
This commit is contained in:
parent
34cb0b49e8
commit
ba418976b0
@ -128,12 +128,14 @@ add_library(skyline SHARED
|
||||
${source_DIR}/skyline/services/timesrv/common.cpp
|
||||
${source_DIR}/skyline/services/timesrv/core.cpp
|
||||
${source_DIR}/skyline/services/timesrv/time_shared_memory.cpp
|
||||
${source_DIR}/skyline/services/timesrv/timezone_manager.cpp
|
||||
${source_DIR}/skyline/services/timesrv/time_manager_server.cpp
|
||||
${source_DIR}/skyline/services/timesrv/IStaticService.cpp
|
||||
${source_DIR}/skyline/services/timesrv/ISystemClock.cpp
|
||||
${source_DIR}/skyline/services/timesrv/ISteadyClock.cpp
|
||||
${source_DIR}/skyline/services/timesrv/ITimeZoneService.cpp
|
||||
${source_DIR}/skyline/services/glue/IStaticService.cpp
|
||||
${source_DIR}/skyline/services/glue/ITimeZoneService.cpp
|
||||
${source_DIR}/skyline/services/fssrv/IFileSystemProxy.cpp
|
||||
${source_DIR}/skyline/services/fssrv/IFileSystem.cpp
|
||||
${source_DIR}/skyline/services/fssrv/IFile.cpp
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include <unistd.h>
|
||||
#include <android/log.h>
|
||||
#include <android/asset_manager_jni.h>
|
||||
#include <sys/system_properties.h>
|
||||
#include "skyline/common.h"
|
||||
#include "skyline/common/signal.h"
|
||||
#include "skyline/common/settings.h"
|
||||
@ -23,6 +24,31 @@ std::weak_ptr<skyline::kernel::OS> OsWeak;
|
||||
std::weak_ptr<skyline::gpu::GPU> GpuWeak;
|
||||
std::weak_ptr<skyline::input::Input> InputWeak;
|
||||
|
||||
// https://cs.android.com/android/platform/superproject/+/master:bionic/libc/tzcode/bionic.cpp;l=43;drc=master;bpv=1;bpt=1
|
||||
static std::string GetTimeZoneName() {
|
||||
const char* nameEnv = getenv("TZ");
|
||||
if (nameEnv)
|
||||
return std::string(nameEnv);
|
||||
|
||||
char propBuf[PROP_VALUE_MAX];
|
||||
if (__system_property_get("persist.sys.timezone", propBuf)) {
|
||||
std::string nameProp(propBuf);
|
||||
|
||||
// Flip -/+, see https://cs.android.com/android/platform/superproject/+/master:bionic/libc/tzcode/bionic.cpp;l=53;drc=master;bpv=1;bpt=1
|
||||
if (nameProp.size() > 2) {
|
||||
if (nameProp[2] == '-')
|
||||
nameProp[2] = '+';
|
||||
else if (nameProp[2] == '+')
|
||||
nameProp[2] = '-';
|
||||
}
|
||||
|
||||
return nameProp;
|
||||
}
|
||||
|
||||
// Fallback to GMT
|
||||
return "GMT";
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_executeApplication(JNIEnv *env, jobject instance, jstring romUriJstring, jint romType, jint romFd, jint preferenceFd, jstring appFilesPathJstring, jobject assetManager) {
|
||||
skyline::signal::ScopedStackBlocker stackBlocker; // We do not want anything to unwind past JNI code as there are invalid stack frames which can lead to a segmentation fault
|
||||
Fps = FrameTime = 0;
|
||||
@ -39,7 +65,7 @@ extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_executeApplication(
|
||||
auto start{std::chrono::steady_clock::now()};
|
||||
|
||||
try {
|
||||
auto os{std::make_shared<skyline::kernel::OS>(jvmManager, logger, settings, std::string(appFilesPath), std::make_shared<skyline::vfs::AndroidAssetFileSystem>(AAssetManager_fromJava(env, assetManager)))};
|
||||
auto os{std::make_shared<skyline::kernel::OS>(jvmManager, logger, settings, std::string(appFilesPath), GetTimeZoneName(), std::make_shared<skyline::vfs::AndroidAssetFileSystem>(AAssetManager_fromJava(env, assetManager)))};
|
||||
OsWeak = os;
|
||||
GpuWeak = os->state.gpu;
|
||||
InputWeak = os->state.input;
|
||||
|
@ -13,7 +13,7 @@
|
||||
#include "os.h"
|
||||
|
||||
namespace skyline::kernel {
|
||||
OS::OS(std::shared_ptr<JvmManager> &jvmManager, std::shared_ptr<Logger> &logger, std::shared_ptr<Settings> &settings, std::string appFilesPath, std::shared_ptr<vfs::FileSystem> assetFileSystem) : state(this, jvmManager, settings, logger), appFilesPath(std::move(appFilesPath)), assetFileSystem(std::move(assetFileSystem)), serviceManager(state) {}
|
||||
OS::OS(std::shared_ptr<JvmManager> &jvmManager, std::shared_ptr<Logger> &logger, std::shared_ptr<Settings> &settings, std::string appFilesPath, std::string deviceTimeZone, std::shared_ptr<vfs::FileSystem> assetFileSystem) : state(this, jvmManager, settings, logger), appFilesPath(std::move(appFilesPath)), deviceTimeZone(std::move(deviceTimeZone)), assetFileSystem(std::move(assetFileSystem)), serviceManager(state) {}
|
||||
|
||||
void OS::Execute(int romFd, loader::RomFormat romType) {
|
||||
auto romFile{std::make_shared<vfs::OsBacking>(romFd)};
|
||||
|
@ -15,6 +15,7 @@ namespace skyline::kernel {
|
||||
public:
|
||||
DeviceState state;
|
||||
std::string appFilesPath; //!< The full path to the app's files directory
|
||||
std::string deviceTimeZone; //!< The timezone name (e.g. Europe/London)
|
||||
std::shared_ptr<vfs::FileSystem> assetFileSystem; //!< A filesystem to be used for accessing emulator assets (like tzdata)
|
||||
service::ServiceManager serviceManager;
|
||||
|
||||
@ -23,7 +24,7 @@ namespace skyline::kernel {
|
||||
* @param settings An instance of the Settings class
|
||||
* @param window The ANativeWindow object to draw the screen to
|
||||
*/
|
||||
OS(std::shared_ptr<JvmManager> &jvmManager, std::shared_ptr<Logger> &logger, std::shared_ptr<Settings> &settings, std::string appFilesPath, std::shared_ptr<vfs::FileSystem>);
|
||||
OS(std::shared_ptr<JvmManager> &jvmManager, std::shared_ptr<Logger> &logger, std::shared_ptr<Settings> &settings, std::string appFilesPath, std::string deviceTimeZone, std::shared_ptr<vfs::FileSystem>);
|
||||
|
||||
/**
|
||||
* @brief Execute a particular ROM file
|
||||
|
@ -2,12 +2,12 @@
|
||||
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
|
||||
#include <kernel/types/KProcess.h>
|
||||
#include <services/timesrv/IStaticService.h>
|
||||
#include <services/timesrv/results.h>
|
||||
#include "ITimeZoneService.h"
|
||||
#include "IStaticService.h"
|
||||
|
||||
namespace skyline::service::glue {
|
||||
IStaticService::IStaticService(const DeviceState &state, ServiceManager &manager, std::shared_ptr<timesrv::IStaticService> core, timesrv::StaticServicePermissions permissions) : BaseService(state, manager), core(std::move(core)), permissions(permissions) {}
|
||||
IStaticService::IStaticService(const DeviceState &state, ServiceManager &manager, std::shared_ptr<timesrv::IStaticService> core, timesrv::core::TimeServiceObject ×rvCore, timesrv::StaticServicePermissions permissions) : BaseService(state, manager), core(std::move(core)), timesrvCore(timesrvCore), permissions(permissions) {}
|
||||
|
||||
Result IStaticService::GetStandardUserSystemClock(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return core->GetStandardUserSystemClock(session, request, response);
|
||||
@ -20,9 +20,10 @@ namespace skyline::service::glue {
|
||||
Result IStaticService::GetStandardSteadyClock(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return core->GetStandardSteadyClock(session, request, response);
|
||||
}
|
||||
|
||||
Result IStaticService::GetTimeZoneService(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
// STUFFF
|
||||
return core->GetTimeZoneService(session, request, response);
|
||||
manager.RegisterService(std::make_shared<ITimeZoneService>(state, manager, core->GetTimeZoneService(state, manager), timesrvCore, true), session, response);
|
||||
return {};
|
||||
}
|
||||
|
||||
Result IStaticService::GetStandardLocalSystemClock(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
|
@ -14,10 +14,11 @@ namespace skyline::service::glue {
|
||||
class IStaticService : public BaseService {
|
||||
private:
|
||||
std::shared_ptr<timesrv::IStaticService> core;
|
||||
timesrv::core::TimeServiceObject ×rvCore;
|
||||
timesrv::StaticServicePermissions permissions;
|
||||
|
||||
public:
|
||||
IStaticService(const DeviceState &state, ServiceManager &manager, std::shared_ptr<timesrv::IStaticService> core, timesrv::StaticServicePermissions permissions);
|
||||
IStaticService(const DeviceState &state, ServiceManager &manager, std::shared_ptr<timesrv::IStaticService> core, timesrv::core::TimeServiceObject ×rvCore, timesrv::StaticServicePermissions permissions);
|
||||
|
||||
Result GetStandardUserSystemClock(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
|
102
app/src/main/cpp/skyline/services/glue/ITimeZoneService.cpp
Normal file
102
app/src/main/cpp/skyline/services/glue/ITimeZoneService.cpp
Normal file
@ -0,0 +1,102 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
|
||||
#include <os.h>
|
||||
#include <kernel/types/KProcess.h>
|
||||
#include <services/timesrv/common.h>
|
||||
#include <services/timesrv/results.h>
|
||||
#include <services/timesrv/ITimeZoneService.h>
|
||||
#include <services/timesrv/core.h>
|
||||
#include "ITimeZoneService.h"
|
||||
|
||||
namespace skyline::service::glue {
|
||||
ITimeZoneService::ITimeZoneService(const DeviceState &state, ServiceManager &manager, std::shared_ptr<timesrv::ITimeZoneService> core, timesrv::core::TimeServiceObject ×rvCore, bool writeable) : BaseService(state, manager), core(std::move(core)), timesrvCore(timesrvCore), locationNameUpdateEvent(std::make_shared<kernel::type::KEvent>(state, false)), writeable(writeable) {}
|
||||
|
||||
Result ITimeZoneService::GetDeviceLocationName(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return core->GetDeviceLocationName(session, request, response);
|
||||
}
|
||||
|
||||
Result ITimeZoneService::SetDeviceLocationName(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
if (!writeable)
|
||||
return timesrv::result::PermissionDenied;
|
||||
|
||||
auto locationName{span(request.Pop<timesrv::LocationName>()).as_string(true)};
|
||||
|
||||
// TODO Checked
|
||||
auto timeZoneBinaryFile{state.os->assetFileSystem->OpenFile(fmt::format("tzdata/zoneinfo/{}", locationName))};
|
||||
std::vector<u8> timeZoneBinaryBuffer(timeZoneBinaryFile->size);
|
||||
timeZoneBinaryFile->Read(timeZoneBinaryBuffer);
|
||||
auto ret{core->SetDeviceLocationNameWithTimeZoneBinary(span(locationName).as_string(true), timeZoneBinaryBuffer)};
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
locationNameUpdateEvent->Signal();
|
||||
return {};
|
||||
}
|
||||
|
||||
Result ITimeZoneService::GetTotalLocationNameCount(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return core->GetTotalLocationNameCount(session, request, response);
|
||||
}
|
||||
|
||||
Result ITimeZoneService::LoadLocationNameList(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
auto outList{request.outputBuf.at(0).cast<timesrv::LocationName>()};
|
||||
auto offset{request.Pop<u32>()};
|
||||
|
||||
outList.copy_from(span(timesrvCore.locationNameList).subspan(offset, outList.size()));
|
||||
|
||||
response.Push<u32>(outList.size());
|
||||
return {};
|
||||
}
|
||||
|
||||
Result ITimeZoneService::LoadTimeZoneRule(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
auto locationName{span(request.Pop<timesrv::LocationName>()).as_string(true)};
|
||||
|
||||
// TODO Checked
|
||||
auto timeZoneBinaryFile{state.os->assetFileSystem->OpenFile(fmt::format("tzdata/zoneinfo/{}", locationName))};
|
||||
std::vector<u8> timeZoneBinaryBuffer(timeZoneBinaryFile->size);
|
||||
timeZoneBinaryFile->Read(timeZoneBinaryBuffer);
|
||||
return core->ParseTimeZoneBinary(timeZoneBinaryBuffer, request.outputBuf.at(0));
|
||||
}
|
||||
|
||||
Result ITimeZoneService::GetTimeZoneRuleVersion(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return core->GetTimeZoneRuleVersion(session, request, response);
|
||||
}
|
||||
|
||||
Result ITimeZoneService::GetDeviceLocationNameAndUpdatedTime(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return timesrv::result::Unimplemented;
|
||||
}
|
||||
|
||||
Result ITimeZoneService::SetDeviceLocationNameWithTimeZoneBinary(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
if (!writeable)
|
||||
return timesrv::result::PermissionDenied;
|
||||
|
||||
return timesrv::result::Unimplemented;
|
||||
}
|
||||
|
||||
Result ITimeZoneService::ParseTimeZoneBinary(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return timesrv::result::Unimplemented;
|
||||
}
|
||||
|
||||
Result ITimeZoneService::GetDeviceLocationNameOperationEventReadableHandle(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
auto handle{state.process->InsertItem(locationNameUpdateEvent)};
|
||||
state.logger->Debug("Location Name Update Event Handle: 0x{:X}", handle);
|
||||
response.copyHandles.push_back(handle);
|
||||
return {};
|
||||
}
|
||||
|
||||
Result ITimeZoneService::ToCalendarTime(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return core->ToCalendarTime(session, request, response);
|
||||
}
|
||||
|
||||
Result ITimeZoneService::ToCalendarTimeWithMyRule(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return core->ToCalendarTimeWithMyRule(session, request, response);
|
||||
}
|
||||
|
||||
Result ITimeZoneService::ToPosixTime(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return core->ToPosixTime(session, request, response);
|
||||
}
|
||||
|
||||
Result ITimeZoneService::ToPosixTimeWithMyRule(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return core->ToPosixTimeWithMyRule(session, request, response);
|
||||
}
|
||||
}
|
74
app/src/main/cpp/skyline/services/glue/ITimeZoneService.h
Normal file
74
app/src/main/cpp/skyline/services/glue/ITimeZoneService.h
Normal file
@ -0,0 +1,74 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <kernel/types/KEvent.h>
|
||||
#include <services/serviceman.h>
|
||||
|
||||
namespace skyline::service::timesrv {
|
||||
class ITimeZoneService;
|
||||
|
||||
namespace core {
|
||||
struct TimeServiceObject;
|
||||
}
|
||||
}
|
||||
namespace skyline::service::glue {
|
||||
|
||||
|
||||
class ITimeZoneService : public BaseService {
|
||||
private:
|
||||
std::shared_ptr<timesrv::ITimeZoneService> core;
|
||||
timesrv::core::TimeServiceObject ×rvCore;
|
||||
std::shared_ptr<kernel::type::KEvent> locationNameUpdateEvent; //!< N uses a list here but a single event should be fine
|
||||
bool writeable; //!< If this instance is allowed to set the device timezone
|
||||
|
||||
public:
|
||||
ITimeZoneService(const DeviceState &state, ServiceManager &manager, std::shared_ptr<timesrv::ITimeZoneService> core, timesrv::core::TimeServiceObject ×rvCore, bool writeable);
|
||||
|
||||
Result GetDeviceLocationName(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result SetDeviceLocationName(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result GetTotalLocationNameCount(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result LoadLocationNameList(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result LoadTimeZoneRule(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result GetTimeZoneRuleVersion(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result GetDeviceLocationNameAndUpdatedTime(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result SetDeviceLocationNameWithTimeZoneBinary(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result ParseTimeZoneBinary(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result GetDeviceLocationNameOperationEventReadableHandle(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result ToCalendarTime(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result ToCalendarTimeWithMyRule(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result ToPosixTime(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result ToPosixTimeWithMyRule(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
SERVICE_DECL(
|
||||
SFUNC(0x0, ITimeZoneService, GetDeviceLocationName),
|
||||
SFUNC(0x1, ITimeZoneService, SetDeviceLocationName),
|
||||
SFUNC(0x2, ITimeZoneService, GetTotalLocationNameCount),
|
||||
SFUNC(0x3, ITimeZoneService, LoadLocationNameList),
|
||||
SFUNC(0x4, ITimeZoneService, LoadTimeZoneRule),
|
||||
SFUNC(0x5, ITimeZoneService, GetTimeZoneRuleVersion),
|
||||
SFUNC(0x6, ITimeZoneService, GetDeviceLocationNameAndUpdatedTime),
|
||||
SFUNC(0x7, ITimeZoneService, SetDeviceLocationNameWithTimeZoneBinary),
|
||||
SFUNC(0x8, ITimeZoneService, ParseTimeZoneBinary),
|
||||
SFUNC(0x9, ITimeZoneService, GetDeviceLocationNameOperationEventReadableHandle),
|
||||
SFUNC(0x64, ITimeZoneService, ToCalendarTime),
|
||||
SFUNC(0x65, ITimeZoneService, ToCalendarTimeWithMyRule),
|
||||
SFUNC(0xC9, ITimeZoneService, ToPosixTime),
|
||||
SFUNC(0xCA, ITimeZoneService, ToPosixTimeWithMyRule)
|
||||
)
|
||||
};
|
||||
}
|
@ -64,9 +64,9 @@ namespace skyline::service {
|
||||
SERVICE_CASE(hid::IHidServer, "hid")
|
||||
SERVICE_CASE(timesrv::IStaticService, "time:s", globalServiceState->timesrv, timesrv::constant::StaticServiceSystemPermissions) // Both of these would be registered after TimeServiceManager::Setup normally but we call that in the GlobalServiceState constructor so can just list them here directly
|
||||
SERVICE_CASE(timesrv::IStaticService, "time:su", globalServiceState->timesrv, timesrv::constant::StaticServiceSystemUpdatePermissions)
|
||||
SERVICE_CASE(glue::IStaticService, "time:a", globalServiceState->timesrv.managerServer.GetAdminStaticService(state, *this), timesrv::constant::StaticServiceAdminPermissions)
|
||||
SERVICE_CASE(glue::IStaticService, "time:r", globalServiceState->timesrv.managerServer.GetRepairStaticService(state, *this), timesrv::constant::StaticServiceRepairPermissions)
|
||||
SERVICE_CASE(glue::IStaticService, "time:u", globalServiceState->timesrv.managerServer.GetUserStaticService(state, *this), timesrv::constant::StaticServiceUserPermissions)
|
||||
SERVICE_CASE(glue::IStaticService, "time:a", globalServiceState->timesrv.managerServer.GetAdminStaticService(state, *this), globalServiceState->timesrv, timesrv::constant::StaticServiceAdminPermissions)
|
||||
SERVICE_CASE(glue::IStaticService, "time:r", globalServiceState->timesrv.managerServer.GetRepairStaticService(state, *this), globalServiceState->timesrv, timesrv::constant::StaticServiceRepairPermissions)
|
||||
SERVICE_CASE(glue::IStaticService, "time:u", globalServiceState->timesrv.managerServer.GetUserStaticService(state, *this), globalServiceState->timesrv, timesrv::constant::StaticServiceUserPermissions)
|
||||
SERVICE_CASE(fssrv::IFileSystemProxy, "fsp-srv")
|
||||
SERVICE_CASE(nvdrv::INvDrvServices, "nvdrv")
|
||||
SERVICE_CASE(nvdrv::INvDrvServices, "nvdrv:a")
|
||||
|
@ -27,11 +27,15 @@ namespace skyline::service::timesrv {
|
||||
return {};
|
||||
}
|
||||
|
||||
Result IStaticService::GetTimeZoneService(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
manager.RegisterService(std::make_shared<ITimeZoneService>(state, manager), session, response);
|
||||
Result IStaticService::GetTimeZoneServiceIpc(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
manager.RegisterService(std::make_shared<ITimeZoneService>(state, manager, core, permissions.writeTimezone), session, response);
|
||||
return {};
|
||||
}
|
||||
|
||||
std::shared_ptr<ITimeZoneService> IStaticService::GetTimeZoneService(const DeviceState &state, ServiceManager &manager) {
|
||||
return std::make_shared<ITimeZoneService>(state, manager, core, permissions.writeTimezone);
|
||||
}
|
||||
|
||||
Result IStaticService::GetStandardLocalSystemClock(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
manager.RegisterService(std::make_shared<ISystemClock>(state, manager, core.localSystemClock, permissions.writeLocalSystemClock, permissions.ignoreUninitializedChecks), session, response);
|
||||
return {};
|
||||
|
@ -46,6 +46,8 @@ namespace skyline::service::timesrv {
|
||||
|
||||
}
|
||||
|
||||
class ITimeZoneService;
|
||||
|
||||
namespace core {
|
||||
struct TimeServiceObject;
|
||||
}
|
||||
@ -70,7 +72,9 @@ namespace skyline::service::timesrv {
|
||||
|
||||
Result GetStandardSteadyClock(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result GetTimeZoneService(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
Result GetTimeZoneServiceIpc(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
std::shared_ptr<ITimeZoneService> GetTimeZoneService(const DeviceState &state, ServiceManager &manager);
|
||||
|
||||
Result GetStandardLocalSystemClock(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
@ -121,7 +125,7 @@ namespace skyline::service::timesrv {
|
||||
SFUNC(0x0, IStaticService, GetStandardUserSystemClock),
|
||||
SFUNC(0x1, IStaticService, GetStandardNetworkSystemClock),
|
||||
SFUNC(0x2, IStaticService, GetStandardSteadyClock),
|
||||
SFUNC(0x3, IStaticService, GetTimeZoneService),
|
||||
SFUNC(0x3, IStaticService, GetTimeZoneServiceIpc),
|
||||
SFUNC(0x4, IStaticService, GetStandardLocalSystemClock),
|
||||
SFUNC(0x5, IStaticService, GetEphemeralNetworkSystemClock),
|
||||
SFUNC(0x14, IStaticService, GetSharedMemoryNativeHandle),
|
||||
|
@ -2,32 +2,131 @@
|
||||
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
|
||||
#include "common.h"
|
||||
#include "core.h"
|
||||
#include "ITimeZoneService.h"
|
||||
|
||||
namespace skyline::service::timesrv {
|
||||
ITimeZoneService::ITimeZoneService(const DeviceState &state, ServiceManager &manager) : BaseService(state, manager) {}
|
||||
ITimeZoneService::ITimeZoneService(const DeviceState &state, ServiceManager &manager, core::TimeServiceObject &core, bool writeable) : BaseService(state, manager), core(core), writeable(writeable) {}
|
||||
|
||||
Result ITimeZoneService::GetDeviceLocationName(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
auto locationName{core.timeZoneManager.GetLocationName()};
|
||||
if (locationName)
|
||||
response.Push(*locationName);
|
||||
|
||||
return locationName;
|
||||
}
|
||||
|
||||
Result ITimeZoneService::SetDeviceLocationName(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
if (!writeable)
|
||||
return result::PermissionDenied;
|
||||
|
||||
return result::Unimplemented;
|
||||
}
|
||||
|
||||
Result ITimeZoneService::GetTotalLocationNameCount(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
auto count{core.timeZoneManager.GetLocationCount()};
|
||||
if (count)
|
||||
response.Push<i32>(*count);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
Result ITimeZoneService::LoadLocationNameList(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return result::Unimplemented;
|
||||
}
|
||||
|
||||
Result ITimeZoneService::LoadTimeZoneRule(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return result::Unimplemented;
|
||||
}
|
||||
|
||||
Result ITimeZoneService::GetTimeZoneRuleVersion(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
auto version{core.timeZoneManager.GetBinaryVersion()};
|
||||
if (version)
|
||||
response.Push(*version);
|
||||
|
||||
return version;
|
||||
}
|
||||
|
||||
Result ITimeZoneService::GetDeviceLocationNameAndUpdatedTime(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
auto locationName{core.timeZoneManager.GetLocationName()};
|
||||
if (!locationName)
|
||||
return locationName;
|
||||
|
||||
auto updateTime{core.timeZoneManager.GetUpdateTime()};
|
||||
if (!updateTime)
|
||||
return updateTime;
|
||||
|
||||
response.Push(*locationName);
|
||||
response.Push<u32>(0);
|
||||
response.Push(*updateTime);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
Result ITimeZoneService::SetDeviceLocationNameWithTimeZoneBinaryIpc(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
auto locationName{request.Pop<LocationName>()};
|
||||
|
||||
return SetDeviceLocationNameWithTimeZoneBinary(span(locationName).as_string(true), request.inputBuf.at(0));
|
||||
}
|
||||
|
||||
Result ITimeZoneService::SetDeviceLocationNameWithTimeZoneBinary(std::string_view locationName, span<u8> binary) {
|
||||
if (!writeable)
|
||||
return result::PermissionDenied;
|
||||
|
||||
return core.timeZoneManager.SetNewLocation(locationName, binary);
|
||||
}
|
||||
|
||||
Result ITimeZoneService::ParseTimeZoneBinaryIpc(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return core.timeZoneManager.ParseTimeZoneBinary(request.inputBuf.at(0), request.outputBuf.at(0));
|
||||
}
|
||||
|
||||
Result ITimeZoneService::ParseTimeZoneBinary(span<u8> binary, span<u8> rule) {
|
||||
return core.timeZoneManager.ParseTimeZoneBinary(binary, rule);
|
||||
}
|
||||
|
||||
Result ITimeZoneService::GetDeviceLocationNameOperationEventReadableHandle(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return result::Unimplemented;
|
||||
}
|
||||
|
||||
Result ITimeZoneService::ToCalendarTime(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
auto posixTime{request.Pop<PosixTime>()};
|
||||
auto calendarTime{core.timeZoneManager.ToCalendarTime(reinterpret_cast<tz_timezone_t>(request.inputBuf.at(0).data()), posixTime)};
|
||||
|
||||
if (calendarTime)
|
||||
response.Push(*calendarTime);
|
||||
|
||||
return calendarTime;
|
||||
}
|
||||
|
||||
Result ITimeZoneService::ToCalendarTimeWithMyRule(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
auto time{static_cast<time_t>(request.Pop<PosixTime>())};
|
||||
auto calender{*std::gmtime(&time)};
|
||||
auto posixTime{request.Pop<PosixTime>()};
|
||||
auto calendarTime{core.timeZoneManager.ToCalendarTimeWithMyRule(posixTime)};
|
||||
|
||||
CalendarTime calendarTime{
|
||||
.year = static_cast<u16>(calender.tm_year),
|
||||
.month = static_cast<u8>(calender.tm_mon + 1),
|
||||
.day = static_cast<u8>(calender.tm_hour),
|
||||
.minute = static_cast<u8>(calender.tm_min),
|
||||
.second = static_cast<u8>(calender.tm_sec),
|
||||
};
|
||||
response.Push(calendarTime);
|
||||
if (calendarTime)
|
||||
response.Push(*calendarTime);
|
||||
|
||||
CalendarAdditionalInfo calendarInfo{
|
||||
.dayOfWeek = static_cast<u32>(calender.tm_wday),
|
||||
.dayOfYear = static_cast<u32>(calender.tm_yday),
|
||||
.timezoneName = "GMT",
|
||||
.dst = static_cast<u32>(calender.tm_isdst),
|
||||
.gmtOffset = static_cast<i32>(calender.tm_gmtoff),
|
||||
};
|
||||
response.Push(calendarInfo);
|
||||
return calendarTime;
|
||||
}
|
||||
|
||||
Result ITimeZoneService::ToPosixTime(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
auto calendarTime{request.Pop<CalendarTime>()};
|
||||
auto posixTime{core.timeZoneManager.ToPosixTime(reinterpret_cast<tz_timezone_t>(request.inputBuf.at(0).data()), calendarTime)};
|
||||
if (!posixTime)
|
||||
return posixTime;
|
||||
|
||||
request.outputBuf.at(0).as<PosixTime>() = *posixTime;
|
||||
response.Push<u32>(1);
|
||||
return {};
|
||||
}
|
||||
|
||||
Result ITimeZoneService::ToPosixTimeWithMyRule(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
auto calendarTime{request.Pop<CalendarTime>()};
|
||||
auto posixTime{core.timeZoneManager.ToPosixTimeWithMyRule(calendarTime)};
|
||||
if (!posixTime)
|
||||
return posixTime;
|
||||
|
||||
request.outputBuf.at(0).as<PosixTime>() = *posixTime;
|
||||
response.Push<u32>(1);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
@ -6,22 +6,69 @@
|
||||
#include <services/serviceman.h>
|
||||
|
||||
namespace skyline::service::timesrv {
|
||||
namespace core {
|
||||
struct TimeServiceObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief ITimeZoneService is used to retrieve and set time
|
||||
* @brief ITimeZoneService is used to retrieve and set timezone info and convert between times and dates
|
||||
* @url https://switchbrew.org/wiki/PSC_services#ITimeZoneService
|
||||
*/
|
||||
class ITimeZoneService : public BaseService {
|
||||
public:
|
||||
ITimeZoneService(const DeviceState &state, ServiceManager &manager);
|
||||
private:
|
||||
core::TimeServiceObject &core;
|
||||
bool writeable; //!< If this instance is allowed to set the device timezone
|
||||
|
||||
public:
|
||||
ITimeZoneService(const DeviceState &state, ServiceManager &manager, core::TimeServiceObject &core, bool writeable);
|
||||
|
||||
Result GetDeviceLocationName(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result SetDeviceLocationName(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result GetTotalLocationNameCount(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result LoadLocationNameList(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result LoadTimeZoneRule(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result GetTimeZoneRuleVersion(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result GetDeviceLocationNameAndUpdatedTime(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result SetDeviceLocationNameWithTimeZoneBinaryIpc(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result SetDeviceLocationNameWithTimeZoneBinary(std::string_view locationName, span<u8> rule);
|
||||
|
||||
Result ParseTimeZoneBinaryIpc(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result ParseTimeZoneBinary(span<u8> binary, span<u8> rule);
|
||||
|
||||
Result GetDeviceLocationNameOperationEventReadableHandle(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result ToCalendarTime(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @brief Receives a u64 #PosixTime (https://switchbrew.org/wiki/PSC_services#PosixTime), and returns a #CalendarTime (https://switchbrew.org/wiki/PSC_services#CalendarTime), #CalendarAdditionalInfo
|
||||
* @url https://switchbrew.org/wiki/PSC_services#CalendarAdditionalInfo
|
||||
*/
|
||||
Result ToCalendarTimeWithMyRule(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result ToPosixTime(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result ToPosixTimeWithMyRule(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
SERVICE_DECL(
|
||||
SFUNC(0x65, ITimeZoneService, ToCalendarTimeWithMyRule)
|
||||
SFUNC(0x0, ITimeZoneService, GetDeviceLocationName),
|
||||
SFUNC(0x1, ITimeZoneService, SetDeviceLocationName),
|
||||
SFUNC(0x2, ITimeZoneService, GetTotalLocationNameCount),
|
||||
SFUNC(0x3, ITimeZoneService, LoadLocationNameList),
|
||||
SFUNC(0x4, ITimeZoneService, LoadTimeZoneRule),
|
||||
SFUNC(0x5, ITimeZoneService, GetTimeZoneRuleVersion),
|
||||
SFUNC(0x6, ITimeZoneService, GetDeviceLocationNameAndUpdatedTime),
|
||||
SFUNC(0x7, ITimeZoneService, SetDeviceLocationNameWithTimeZoneBinaryIpc),
|
||||
SFUNC(0x8, ITimeZoneService, ParseTimeZoneBinaryIpc),
|
||||
SFUNC(0x9, ITimeZoneService, GetDeviceLocationNameOperationEventReadableHandle),
|
||||
SFUNC(0x64, ITimeZoneService, ToCalendarTime),
|
||||
SFUNC(0x65, ITimeZoneService, ToCalendarTimeWithMyRule),
|
||||
SFUNC(0xC9, ITimeZoneService, ToPosixTime),
|
||||
SFUNC(0xCA, ITimeZoneService, ToPosixTimeWithMyRule)
|
||||
)
|
||||
};
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
namespace skyline::service::timesrv {
|
||||
using PosixTime = i64; //!< Unit for time in seconds since the epoch
|
||||
using LocationName = std::array<char, 0x18>;
|
||||
|
||||
/**
|
||||
* @brief Stores a quantity of time with nanosecond accuracy and provides helper functions to convert it to other units
|
||||
@ -97,12 +98,21 @@ namespace skyline::service::timesrv {
|
||||
struct CalendarAdditionalInfo {
|
||||
u32 dayOfWeek; //!< 0-6
|
||||
u32 dayOfYear; //!< 0-365
|
||||
std::array<u8, 8> timezoneName;
|
||||
std::array<char, 8> timeZoneName;
|
||||
u32 dst; //!< If DST is in effect or not
|
||||
i32 gmtOffset; //!< Offset of the time from GMT in seconds
|
||||
};
|
||||
static_assert(sizeof(CalendarAdditionalInfo) == 0x18);
|
||||
|
||||
/**
|
||||
* @brief Returned by ToCalendarTime and contains all details about a time
|
||||
*/
|
||||
struct FullCalendarTime {
|
||||
CalendarTime calendarTime;
|
||||
CalendarAdditionalInfo additionalInfo;
|
||||
};
|
||||
static_assert(sizeof(FullCalendarTime) == 0x20);
|
||||
|
||||
/**
|
||||
* @brief A snapshot of all clocks in the system
|
||||
*/
|
||||
|
@ -1,6 +1,9 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
|
||||
#include <os.h>
|
||||
#include <vfs/filesystem.h>
|
||||
#include <time.h>
|
||||
#include "core.h"
|
||||
#include "time_manager_server.h"
|
||||
|
||||
@ -220,7 +223,6 @@ namespace skyline::service::timesrv::core {
|
||||
}
|
||||
|
||||
TimeServiceObject::TimeServiceObject(const DeviceState &state) : timeSharedMemory(state), localSystemClockContextWriter(timeSharedMemory), networkSystemClockContextWriter(timeSharedMemory), localSystemClock(standardSteadyClock), networkSystemClock(standardSteadyClock), userSystemClock(state, standardSteadyClock, localSystemClock, networkSystemClock, timeSharedMemory), empheralSystemClock(tickBasedSteadyClock), managerServer(*this) {
|
||||
|
||||
// Setup time service:
|
||||
// A new rtc UUID is generated every time glue inits time
|
||||
auto rtcId{UUID::GenerateUuidV4()};
|
||||
@ -256,5 +258,42 @@ namespace skyline::service::timesrv::core {
|
||||
// Initialise the user system clock with automatic correction disabled as we don't emulate the automatic correction thread
|
||||
managerServer.SetupStandardUserSystemClock(false, SteadyClockTimePoint{.clockSourceId = UUID::GenerateUuidV4()});
|
||||
managerServer.SetupEphemeralSystemClock();
|
||||
|
||||
// Timezone init - normally done in glue
|
||||
|
||||
// Act as if we just updated the current timezone
|
||||
auto timezoneUpdateTime{standardSteadyClock.GetTimePoint()};
|
||||
if (!timezoneUpdateTime)
|
||||
throw exception("Failed to create a timezone updated timepoint!");
|
||||
|
||||
// TODO Checked
|
||||
auto timeZoneBinaryListFile{state.os->assetFileSystem->OpenFile("tzdata/binaryList.txt")};
|
||||
std::vector<u8> buffer(timeZoneBinaryListFile->size);
|
||||
timeZoneBinaryListFile->Read(buffer);
|
||||
|
||||
// Parse binaryList.txt into a vector
|
||||
auto prev{buffer.begin()};
|
||||
for (auto it{buffer.begin()}; it != buffer.end(); it++) {
|
||||
if (*it == '\n' && prev != it) {
|
||||
timesrv::LocationName name{};
|
||||
span(prev.base(), std::distance(prev, std::prev(it))).as_string().copy(name.data(), name.size());
|
||||
locationNameList.push_back(name);
|
||||
|
||||
if (std::next(it) != buffer.end())
|
||||
prev = std::next(it);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO Checked
|
||||
auto timeZoneBinaryVersionFile{state.os->assetFileSystem->OpenFile("tzdata/version.txt")};
|
||||
std::array<u8, 0x10> timeZoneBinaryVersion{};
|
||||
timeZoneBinaryVersionFile->Read(timeZoneBinaryVersion);
|
||||
|
||||
// TODO Checked
|
||||
auto timeZoneBinaryFile{state.os->assetFileSystem->OpenFile(fmt::format("tzdata/zoneinfo/{}", state.os->deviceTimeZone))};
|
||||
buffer.resize(timeZoneBinaryFile->size);
|
||||
timeZoneBinaryFile->Read(buffer);
|
||||
|
||||
managerServer.SetupTimeZoneManager(state.os->deviceTimeZone, *timezoneUpdateTime, locationNameList.size(), timeZoneBinaryVersion, buffer);
|
||||
}
|
||||
}
|
@ -8,8 +8,10 @@
|
||||
#include <kernel/types/KEvent.h>
|
||||
#include <kernel/types/KSharedMemory.h>
|
||||
#include <services/timesrv/common.h>
|
||||
#include <horizon_time.h>
|
||||
#include "time_shared_memory.h"
|
||||
#include "time_manager_server.h"
|
||||
#include "timezone_manager.h"
|
||||
#include "results.h"
|
||||
|
||||
namespace skyline::service::timesrv::core {
|
||||
@ -308,6 +310,9 @@ namespace skyline::service::timesrv::core {
|
||||
StandardUserSystemClockCore userSystemClock;
|
||||
EphemeralNetworkSystemClockCore empheralSystemClock;
|
||||
|
||||
TimeZoneManager timeZoneManager;
|
||||
std::vector<LocationName> locationNameList; //!< N stores in glue but we are fine putting it here
|
||||
|
||||
TimeManagerServer managerServer;
|
||||
|
||||
/**
|
||||
|
@ -11,5 +11,6 @@ namespace skyline::service::timesrv::result {
|
||||
constexpr Result ClockUninitialized(116, 103);
|
||||
constexpr Result InvalidComparison(116, 200);
|
||||
constexpr Result CompareOverflow(116, 201);
|
||||
constexpr Result RuleConversionFailed(116, 903);
|
||||
constexpr Result Unimplemented(116, 990);
|
||||
}
|
@ -33,6 +33,10 @@ namespace skyline::service::timesrv {
|
||||
return {};
|
||||
}
|
||||
|
||||
Result TimeManagerServer::SetupTimeZoneManager(std::string_view locationName, const SteadyClockTimePoint &updateTime, size_t locationCount, std::array<u8, 0x10> binaryVersion, span<u8> binary) {
|
||||
return core.timeZoneManager.Setup(locationName, updateTime, locationCount, binaryVersion, binary);
|
||||
}
|
||||
|
||||
Result TimeManagerServer::SetupStandardLocalSystemClock(const SystemClockContext &context, PosixTime posixTime) {
|
||||
core.localSystemClock.SetUpdateCallback(&core.localSystemClockContextWriter);
|
||||
core.localSystemClock.Setup(context, posixTime);
|
||||
|
@ -31,6 +31,9 @@ namespace skyline::service::timesrv {
|
||||
|
||||
Result SetupStandardSteadyClock(UUID rtcId, TimeSpanType rtcOffset, TimeSpanType internalOffset, TimeSpanType testOffset, bool rtcResetDetected);
|
||||
|
||||
|
||||
Result SetupTimeZoneManager(std::string_view locationName, const SteadyClockTimePoint &updateTime, size_t locationCount, std::array<u8, 0x10> binaryVersion, span<u8> binary);
|
||||
|
||||
Result SetupStandardLocalSystemClock(const SystemClockContext &context, PosixTime posixTime);
|
||||
|
||||
Result SetupStandardNetworkSystemClock(const SystemClockContext &context, TimeSpanType sufficientAccuracy);
|
||||
@ -42,11 +45,5 @@ namespace skyline::service::timesrv {
|
||||
std::shared_ptr<kernel::type::KEvent> GetStandardUserSystemClockAutomaticCorrectionEvent();
|
||||
|
||||
Result SetStandardSteadyClockRtcOffset(TimeSpanType rtcOffset);
|
||||
|
||||
/*
|
||||
Result SetupTimeZoneManager
|
||||
*/
|
||||
|
||||
|
||||
};
|
||||
}
|
||||
|
130
app/src/main/cpp/skyline/services/timesrv/timezone_manager.cpp
Normal file
130
app/src/main/cpp/skyline/services/timesrv/timezone_manager.cpp
Normal file
@ -0,0 +1,130 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
|
||||
#include "results.h"
|
||||
#include "timezone_manager.h"
|
||||
|
||||
namespace skyline::service::timesrv::core {
|
||||
Result TimeZoneManager::Setup(std::string_view pLocationName, const SteadyClockTimePoint &pUpdateTime, int pLocationCount, std::array<u8, 0x10> pBinaryVersion, span<u8> binary) {
|
||||
SetNewLocation(pLocationName, binary);
|
||||
SetUpdateTime(pUpdateTime);
|
||||
SetLocationCount(pLocationCount);
|
||||
SetBinaryVersion(pBinaryVersion);
|
||||
|
||||
MarkInitialized();
|
||||
return {};
|
||||
}
|
||||
|
||||
ResultValue<LocationName> TimeZoneManager::GetLocationName() {
|
||||
std::lock_guard lock(mutex);
|
||||
|
||||
if (!initialized)
|
||||
return result::ClockUninitialized;
|
||||
|
||||
return locationName;
|
||||
}
|
||||
|
||||
Result TimeZoneManager::SetNewLocation(std::string_view pLocationName, span<u8> binary) {
|
||||
std::lock_guard lock(mutex);
|
||||
|
||||
rule = tz_tzalloc(binary.data(), binary.size());
|
||||
if (!rule)
|
||||
return result::RuleConversionFailed;
|
||||
|
||||
span(locationName).copy_from(pLocationName);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
ResultValue<SteadyClockTimePoint> TimeZoneManager::GetUpdateTime() {
|
||||
std::lock_guard lock(mutex);
|
||||
|
||||
if (!initialized)
|
||||
return result::ClockUninitialized;
|
||||
|
||||
return updateTime;
|
||||
}
|
||||
|
||||
void TimeZoneManager::SetUpdateTime(const SteadyClockTimePoint &pUpdateTime) {
|
||||
std::lock_guard lock(mutex);
|
||||
updateTime = pUpdateTime;
|
||||
}
|
||||
|
||||
ResultValue<int> TimeZoneManager::GetLocationCount() {
|
||||
std::lock_guard lock(mutex);
|
||||
|
||||
if (!initialized)
|
||||
return result::ClockUninitialized;
|
||||
|
||||
return locationCount;
|
||||
}
|
||||
|
||||
void TimeZoneManager::SetLocationCount(int pLocationCount) {
|
||||
std::lock_guard lock(mutex);
|
||||
locationCount = pLocationCount;
|
||||
}
|
||||
|
||||
ResultValue<std::array<u8, 0x10>> TimeZoneManager::GetBinaryVersion() {
|
||||
std::lock_guard lock(mutex);
|
||||
|
||||
if (!initialized)
|
||||
return result::ClockUninitialized;
|
||||
|
||||
return binaryVersion;
|
||||
}
|
||||
|
||||
void TimeZoneManager::SetBinaryVersion(std::array<u8, 0x10> pBinaryVersion) {
|
||||
std::lock_guard lock(mutex);
|
||||
binaryVersion = pBinaryVersion;
|
||||
}
|
||||
|
||||
Result TimeZoneManager::ParseTimeZoneBinary(span<u8> binary, span<u8> ruleOut) {
|
||||
auto ruleObj{tz_tzalloc(binary.data(), binary.size())};
|
||||
if (!ruleObj)
|
||||
return result::RuleConversionFailed;
|
||||
|
||||
memcpy(ruleOut.data(), ruleObj, ruleOut.size_bytes());
|
||||
tz_tzfree(ruleObj);
|
||||
return {};
|
||||
}
|
||||
|
||||
ResultValue<FullCalendarTime> TimeZoneManager::ToCalendarTime(tz_timezone_t pRule, PosixTime posixTime) {
|
||||
struct tm tmp{};
|
||||
auto posixCalendarTime{tz_localtime_rz(pRule, &posixTime, &tmp)};
|
||||
if (!posixCalendarTime)
|
||||
return result::PermissionDenied; // Not the proper error here but *fine*
|
||||
|
||||
FullCalendarTime out{
|
||||
.calendarTime{
|
||||
.year = static_cast<u16>(posixCalendarTime->tm_year),
|
||||
.month = static_cast<u8>(posixCalendarTime->tm_mon + 1),
|
||||
.day = static_cast<u8>(posixCalendarTime->tm_mday),
|
||||
.hour = static_cast<u8>(posixCalendarTime->tm_hour),
|
||||
.minute = static_cast<u8>(posixCalendarTime->tm_min),
|
||||
.second = static_cast<u8>(posixCalendarTime->tm_sec),
|
||||
},
|
||||
.additionalInfo{
|
||||
.dayOfWeek = static_cast<u32>(posixCalendarTime->tm_wday),
|
||||
.dayOfYear = static_cast<u32>(posixCalendarTime->tm_yday),
|
||||
.dst = static_cast<u32>(posixCalendarTime->tm_isdst),
|
||||
.gmtOffset = static_cast<i32>(posixCalendarTime->tm_gmtoff),
|
||||
},
|
||||
};
|
||||
|
||||
std::string_view(posixCalendarTime->tm_zone).copy(out.additionalInfo.timeZoneName.data(), strlen(posixCalendarTime->tm_zone));
|
||||
return out;
|
||||
}
|
||||
|
||||
ResultValue<PosixTime> TimeZoneManager::ToPosixTime(tz_timezone_t pRule, CalendarTime calendarTime) {
|
||||
struct tm posixCalendarTime{
|
||||
.tm_year = calendarTime.year,
|
||||
.tm_mon = calendarTime.month - 1,
|
||||
.tm_mday = calendarTime.day,
|
||||
.tm_min = calendarTime.minute,
|
||||
.tm_sec = calendarTime.second,
|
||||
};
|
||||
|
||||
// Nintendo optionally two times here, presumably to deal with DST correction but we are probably fine without it
|
||||
return static_cast<PosixTime>(tz_mktime_z(pRule, &posixCalendarTime));
|
||||
}
|
||||
}
|
82
app/src/main/cpp/skyline/services/timesrv/timezone_manager.h
Normal file
82
app/src/main/cpp/skyline/services/timesrv/timezone_manager.h
Normal file
@ -0,0 +1,82 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <horizon_time.h>
|
||||
#include <common.h>
|
||||
#include "common.h"
|
||||
|
||||
namespace skyline::service::timesrv::core {
|
||||
/**
|
||||
* @brief TimeZoneManager handles converting POSIX times to calendar times and vice-versa by using a rule struct
|
||||
*/
|
||||
class TimeZoneManager {
|
||||
private:
|
||||
bool initialized{};
|
||||
std::mutex mutex;
|
||||
tz_timezone_t rule{}; //!< Rule corresponding to the timezone that is currently in use
|
||||
SteadyClockTimePoint updateTime{}; //!< Time when the rule was last updated
|
||||
int locationCount{}; //!< The number of possible timezone binary locations
|
||||
std::array<u8, 0x10> binaryVersion{}; //!< The version of the tzdata package
|
||||
LocationName locationName{}; //!< Name of the currently selected location
|
||||
|
||||
void MarkInitialized() {
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Initialises the manager, setting the initial timezone so it is ready for use by applications
|
||||
*/
|
||||
Result Setup(std::string_view pLocationName, const SteadyClockTimePoint &pUpdateTime, int pLocationCount, std::array<u8, 0x10> pBinaryVersion, span<u8> binary);
|
||||
|
||||
ResultValue<LocationName> GetLocationName();
|
||||
|
||||
/**
|
||||
* @brief Parses the given binary into rule and sets the appropriate location name
|
||||
*/
|
||||
Result SetNewLocation(std::string_view pLocationName, span<u8> binary);
|
||||
|
||||
ResultValue<SteadyClockTimePoint> GetUpdateTime();
|
||||
|
||||
void SetUpdateTime(const SteadyClockTimePoint &pUpdateTime);
|
||||
|
||||
ResultValue<int> GetLocationCount();
|
||||
|
||||
void SetLocationCount(int pLocationCount);
|
||||
|
||||
ResultValue<std::array<u8, 0x10>> GetBinaryVersion();
|
||||
|
||||
void SetBinaryVersion(std::array<u8, 0x10> pBinaryVersion);
|
||||
|
||||
/**
|
||||
* @brief Parses a raw TZIF2 file into a timezone rule that can be passed to other functions
|
||||
*/
|
||||
Result ParseTimeZoneBinary(span<u8> binary, span<u8> ruleOut);
|
||||
|
||||
/**
|
||||
* @brief Converts a POSIX time to a calendar time using the given rule
|
||||
*/
|
||||
ResultValue<FullCalendarTime> ToCalendarTime(tz_timezone_t pRule, PosixTime posixTime);
|
||||
|
||||
/**
|
||||
* @brief Converts a POSIX to a calendar time using the current location's rule
|
||||
*/
|
||||
ResultValue<FullCalendarTime> ToCalendarTimeWithMyRule(PosixTime posixTime) {
|
||||
return ToCalendarTime(rule, posixTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Converts a calendar time to a POSIX time using the given rule
|
||||
*/
|
||||
ResultValue<PosixTime> ToPosixTime(tz_timezone_t pRule, CalendarTime calendarTime);
|
||||
|
||||
/**
|
||||
* @brief Converts a calendar time to a POSIX time using the current location's rule
|
||||
*/
|
||||
ResultValue<PosixTime> ToPosixTimeWithMyRule(CalendarTime calendarTime) {
|
||||
return ToPosixTime(rule, calendarTime);
|
||||
}
|
||||
};
|
||||
}
|
Loading…
Reference in New Issue
Block a user