2015-02-27 02:13:08 +00:00
|
|
|
// Copyright 2015 Citra Emulator Project
|
|
|
|
// Licensed under GPLv2 or any later version
|
|
|
|
// Refer to the license.txt file included.
|
|
|
|
|
2017-11-29 23:53:01 +00:00
|
|
|
#include <cinttypes>
|
2015-04-30 20:13:22 +00:00
|
|
|
#include "common/logging/log.h"
|
2017-05-21 04:41:11 +00:00
|
|
|
#include "core/file_sys/errors.h"
|
2015-05-06 05:15:46 +00:00
|
|
|
#include "core/file_sys/file_backend.h"
|
2015-02-27 02:13:08 +00:00
|
|
|
#include "core/hle/service/fs/archive.h"
|
2016-09-21 06:52:38 +00:00
|
|
|
#include "core/hle/service/ptm/ptm.h"
|
2016-12-08 10:17:19 +00:00
|
|
|
#include "core/hle/service/ptm/ptm_gets.h"
|
2015-02-27 02:13:08 +00:00
|
|
|
#include "core/hle/service/ptm/ptm_play.h"
|
2016-12-08 10:17:19 +00:00
|
|
|
#include "core/hle/service/ptm/ptm_sets.h"
|
2015-02-27 02:13:08 +00:00
|
|
|
#include "core/hle/service/ptm/ptm_sysm.h"
|
|
|
|
#include "core/hle/service/ptm/ptm_u.h"
|
2015-05-06 05:15:46 +00:00
|
|
|
#include "core/hle/service/service.h"
|
2016-09-18 00:38:01 +00:00
|
|
|
#include "core/settings.h"
|
2015-02-27 02:13:08 +00:00
|
|
|
|
|
|
|
namespace Service {
|
|
|
|
namespace PTM {
|
|
|
|
|
|
|
|
/// Values for the default gamecoin.dat file
|
2016-09-18 00:38:01 +00:00
|
|
|
static const GameCoin default_game_coin = {0x4F00, 42, 0, 0, 0, 2014, 12, 29};
|
2015-02-27 02:13:08 +00:00
|
|
|
|
|
|
|
/// Id of the SharedExtData archive used by the PTM process
|
|
|
|
static const std::vector<u8> ptm_shared_extdata_id = {0, 0, 0, 0, 0x0B, 0, 0, 0xF0, 0, 0, 0, 0};
|
|
|
|
|
2015-04-28 02:01:48 +00:00
|
|
|
static bool shell_open;
|
2015-02-27 02:13:08 +00:00
|
|
|
|
2015-04-28 02:01:48 +00:00
|
|
|
static bool battery_is_charging;
|
2015-02-27 02:13:08 +00:00
|
|
|
|
2017-05-17 21:12:59 +00:00
|
|
|
static bool pedometer_is_counting;
|
2015-04-30 20:13:22 +00:00
|
|
|
|
2017-09-29 12:09:10 +00:00
|
|
|
void GetAdapterState(Interface* self) {
|
2017-05-17 21:12:59 +00:00
|
|
|
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x5, 0, 0);
|
2015-04-30 20:13:22 +00:00
|
|
|
|
2017-05-17 21:12:59 +00:00
|
|
|
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
|
|
|
rb.Push(RESULT_SUCCESS);
|
|
|
|
rb.Push(battery_is_charging);
|
2015-04-30 20:13:22 +00:00
|
|
|
|
|
|
|
LOG_WARNING(Service_PTM, "(STUBBED) called");
|
2015-02-27 02:13:08 +00:00
|
|
|
}
|
|
|
|
|
2017-09-29 12:09:10 +00:00
|
|
|
void GetShellState(Interface* self) {
|
2017-05-17 21:12:59 +00:00
|
|
|
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x6, 0, 0);
|
2015-04-30 20:13:22 +00:00
|
|
|
|
2017-05-17 21:12:59 +00:00
|
|
|
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
|
|
|
rb.Push(RESULT_SUCCESS);
|
|
|
|
rb.Push(shell_open);
|
2015-02-27 02:13:08 +00:00
|
|
|
}
|
|
|
|
|
2017-09-29 12:09:10 +00:00
|
|
|
void GetBatteryLevel(Interface* self) {
|
2017-05-17 21:12:59 +00:00
|
|
|
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x7, 0, 0);
|
2015-04-30 20:13:22 +00:00
|
|
|
|
2017-05-17 21:12:59 +00:00
|
|
|
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
|
|
|
rb.Push(RESULT_SUCCESS);
|
|
|
|
rb.Push(static_cast<u32>(ChargeLevels::CompletelyFull)); // Set to a completely full battery
|
2015-04-30 20:13:22 +00:00
|
|
|
|
|
|
|
LOG_WARNING(Service_PTM, "(STUBBED) called");
|
|
|
|
}
|
|
|
|
|
2017-09-29 12:09:10 +00:00
|
|
|
void GetBatteryChargeState(Interface* self) {
|
2017-05-17 21:12:59 +00:00
|
|
|
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x8, 0, 0);
|
2015-04-30 20:13:22 +00:00
|
|
|
|
2017-05-17 21:12:59 +00:00
|
|
|
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
|
|
|
rb.Push(RESULT_SUCCESS);
|
|
|
|
rb.Push(battery_is_charging);
|
2015-04-30 20:13:22 +00:00
|
|
|
|
2017-05-17 21:12:59 +00:00
|
|
|
LOG_WARNING(Service_PTM, "(STUBBED) called");
|
|
|
|
}
|
|
|
|
|
2017-09-29 12:09:10 +00:00
|
|
|
void GetPedometerState(Interface* self) {
|
2017-05-17 21:12:59 +00:00
|
|
|
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x9, 0, 0);
|
|
|
|
|
|
|
|
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
|
|
|
rb.Push(RESULT_SUCCESS);
|
|
|
|
rb.Push(pedometer_is_counting);
|
2015-04-30 20:13:22 +00:00
|
|
|
|
|
|
|
LOG_WARNING(Service_PTM, "(STUBBED) called");
|
|
|
|
}
|
|
|
|
|
2017-09-29 12:09:10 +00:00
|
|
|
void GetStepHistory(Interface* self) {
|
|
|
|
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0xB, 3, 2);
|
|
|
|
|
|
|
|
u32 hours = rp.Pop<u32>();
|
|
|
|
u64 start_time = rp.Pop<u64>();
|
|
|
|
size_t steps_buff_size;
|
|
|
|
const VAddr steps_buff_addr = rp.PopMappedBuffer(&steps_buff_size);
|
|
|
|
ASSERT_MSG(sizeof(u16) * hours == steps_buff_size, "Buffer for steps count has incorrect size");
|
|
|
|
|
|
|
|
// Stub: set zero steps count for every hour
|
|
|
|
for (u32 i = 0; i < hours; ++i) {
|
|
|
|
const u16 steps_per_hour = 0;
|
|
|
|
Memory::Write16(steps_buff_addr + i * sizeof(u16), steps_per_hour);
|
|
|
|
}
|
|
|
|
|
|
|
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
|
|
|
rb.Push(RESULT_SUCCESS);
|
|
|
|
|
2017-12-03 09:52:42 +00:00
|
|
|
LOG_WARNING(Service_PTM, "(STUBBED) called, from time(raw): 0x%" PRIx64 ", for %u hours",
|
2017-11-29 23:53:01 +00:00
|
|
|
start_time, hours);
|
2017-09-29 12:09:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void GetTotalStepCount(Interface* self) {
|
2017-05-17 21:12:59 +00:00
|
|
|
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0xC, 0, 0);
|
2015-10-23 21:58:54 +00:00
|
|
|
|
2017-05-17 21:12:59 +00:00
|
|
|
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
|
|
|
rb.Push(RESULT_SUCCESS);
|
|
|
|
rb.Push<u32>(0);
|
2015-10-23 21:58:54 +00:00
|
|
|
|
|
|
|
LOG_WARNING(Service_PTM, "(STUBBED) called");
|
|
|
|
}
|
|
|
|
|
2017-09-29 12:09:10 +00:00
|
|
|
void GetSoftwareClosedFlag(Interface* self) {
|
2017-05-17 21:12:59 +00:00
|
|
|
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x80F, 0, 0);
|
2015-04-30 20:13:22 +00:00
|
|
|
|
2017-05-17 21:12:59 +00:00
|
|
|
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
|
|
|
rb.Push(RESULT_SUCCESS);
|
|
|
|
rb.Push(false);
|
2015-04-30 20:13:22 +00:00
|
|
|
|
|
|
|
LOG_WARNING(Service_PTM, "(STUBBED) called");
|
2015-02-27 02:13:08 +00:00
|
|
|
}
|
|
|
|
|
2016-12-28 16:11:14 +00:00
|
|
|
void CheckNew3DS(IPC::RequestBuilder& rb) {
|
2016-05-24 21:22:44 +00:00
|
|
|
const bool is_new_3ds = Settings::values.is_new_3ds;
|
2016-04-20 10:12:05 +00:00
|
|
|
|
|
|
|
if (is_new_3ds) {
|
2016-09-18 00:38:01 +00:00
|
|
|
LOG_CRITICAL(Service_PTM, "The option 'is_new_3ds' is enabled as part of the 'System' "
|
|
|
|
"settings. Citra does not fully support New 3DS emulation yet!");
|
2016-04-20 10:12:05 +00:00
|
|
|
}
|
|
|
|
|
2016-12-28 16:11:14 +00:00
|
|
|
rb.Push(RESULT_SUCCESS);
|
2016-12-30 14:54:40 +00:00
|
|
|
rb.Push(is_new_3ds);
|
2016-04-20 10:12:05 +00:00
|
|
|
|
|
|
|
LOG_WARNING(Service_PTM, "(STUBBED) called isNew3DS = 0x%08x", static_cast<u32>(is_new_3ds));
|
|
|
|
}
|
|
|
|
|
2017-09-29 12:09:10 +00:00
|
|
|
void CheckNew3DS(Interface* self) {
|
2017-03-18 10:47:40 +00:00
|
|
|
IPC::RequestBuilder rb(Kernel::GetCommandBuffer(), 0x40A, 0, 0); // 0x040A0000
|
2016-12-28 16:11:14 +00:00
|
|
|
CheckNew3DS(rb);
|
|
|
|
}
|
|
|
|
|
2015-03-08 01:54:16 +00:00
|
|
|
void Init() {
|
2016-12-08 10:20:12 +00:00
|
|
|
AddService(new PTM_Gets);
|
|
|
|
AddService(new PTM_Play);
|
2016-12-08 10:32:38 +00:00
|
|
|
AddService(new PTM_S);
|
2016-12-08 10:20:12 +00:00
|
|
|
AddService(new PTM_Sets);
|
|
|
|
AddService(new PTM_Sysm);
|
|
|
|
AddService(new PTM_U);
|
2015-02-27 02:13:08 +00:00
|
|
|
|
2015-04-28 02:01:48 +00:00
|
|
|
shell_open = true;
|
|
|
|
battery_is_charging = true;
|
2017-05-17 21:12:59 +00:00
|
|
|
pedometer_is_counting = false;
|
2015-04-28 02:01:48 +00:00
|
|
|
|
2016-09-18 00:38:01 +00:00
|
|
|
// Open the SharedExtSaveData archive 0xF000000B and create the gamecoin.dat file if it doesn't
|
|
|
|
// exist
|
2015-02-27 02:13:08 +00:00
|
|
|
FileSys::Path archive_path(ptm_shared_extdata_id);
|
2016-09-18 00:38:01 +00:00
|
|
|
auto archive_result =
|
|
|
|
Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SharedExtSaveData, archive_path);
|
2015-02-27 02:13:08 +00:00
|
|
|
// If the archive didn't exist, create the files inside
|
2017-05-21 04:41:11 +00:00
|
|
|
if (archive_result.Code() == FileSys::ERR_NOT_FORMATTED) {
|
2015-02-27 02:13:08 +00:00
|
|
|
// Format the archive to create the directories
|
2016-09-18 00:38:01 +00:00
|
|
|
Service::FS::FormatArchive(Service::FS::ArchiveIdCode::SharedExtSaveData,
|
|
|
|
FileSys::ArchiveFormatInfo(), archive_path);
|
2015-02-27 02:13:08 +00:00
|
|
|
// Open it again to get a valid archive now that the folder exists
|
2016-09-18 00:38:01 +00:00
|
|
|
archive_result =
|
|
|
|
Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SharedExtSaveData, archive_path);
|
2015-02-27 02:13:08 +00:00
|
|
|
ASSERT_MSG(archive_result.Succeeded(), "Could not open the PTM SharedExtSaveData archive!");
|
|
|
|
|
2016-10-20 01:43:43 +00:00
|
|
|
FileSys::Path gamecoin_path("/gamecoin.dat");
|
2017-03-25 11:22:07 +00:00
|
|
|
Service::FS::CreateFileInArchive(*archive_result, gamecoin_path, sizeof(GameCoin));
|
2015-02-27 02:13:08 +00:00
|
|
|
FileSys::Mode open_mode = {};
|
2016-02-11 17:41:15 +00:00
|
|
|
open_mode.write_flag.Assign(1);
|
2015-02-27 02:13:08 +00:00
|
|
|
// Open the file and write the default gamecoin information
|
2016-09-18 00:38:01 +00:00
|
|
|
auto gamecoin_result =
|
|
|
|
Service::FS::OpenFileFromArchive(*archive_result, gamecoin_path, open_mode);
|
2015-02-27 02:13:08 +00:00
|
|
|
if (gamecoin_result.Succeeded()) {
|
2017-06-19 02:03:15 +00:00
|
|
|
auto gamecoin = std::move(gamecoin_result).Unwrap();
|
2016-12-09 09:04:24 +00:00
|
|
|
gamecoin->backend->Write(0, sizeof(GameCoin), true,
|
2016-09-18 00:38:01 +00:00
|
|
|
reinterpret_cast<const u8*>(&default_game_coin));
|
2015-02-27 02:13:08 +00:00
|
|
|
gamecoin->backend->Close();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-19 01:01:46 +00:00
|
|
|
void Shutdown() {}
|
2015-02-27 02:13:08 +00:00
|
|
|
|
|
|
|
} // namespace PTM
|
|
|
|
} // namespace Service
|