summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/audio_core/stream.cpp2
-rw-r--r--src/core/CMakeLists.txt5
-rw-r--r--src/core/core.cpp2
-rw-r--r--src/core/core_timing.cpp34
-rw-r--r--src/core/core_timing.h23
-rw-r--r--src/core/file_sys/nca_metadata.h6
-rw-r--r--src/core/file_sys/registered_cache.cpp9
-rw-r--r--src/core/file_sys/submission_package.cpp11
-rw-r--r--src/core/hle/kernel/thread.cpp6
-rw-r--r--src/core/hle/service/am/applets/web_browser.cpp4
-rw-r--r--src/core/hle/service/friend/errors.h12
-rw-r--r--src/core/hle/service/friend/friend.cpp115
-rw-r--r--src/core/hle/service/friend/friend.h1
-rw-r--r--src/core/hle/service/friend/interface.cpp2
-rw-r--r--src/core/hle/service/service.cpp2
-rw-r--r--src/core/hle/service/time/interface.cpp11
-rw-r--r--src/core/hle/service/time/interface.h5
-rw-r--r--src/core/hle/service/time/time.cpp115
-rw-r--r--src/core/hle/service/time/time.h11
-rw-r--r--src/core/hle/service/time/time_sharedmemory.cpp68
-rw-r--r--src/core/hle/service/time/time_sharedmemory.h74
-rw-r--r--src/core/loader/nsp.cpp3
-rw-r--r--src/core/loader/xci.cpp2
-rw-r--r--src/core/tools/freezer.cpp188
-rw-r--r--src/core/tools/freezer.h82
-rw-r--r--src/tests/core/core_timing.cpp20
-rw-r--r--src/video_core/rasterizer_cache.h3
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.cpp2
-rw-r--r--src/video_core/renderer_opengl/gl_global_cache.cpp1
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp76
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.h29
31 files changed, 768 insertions, 156 deletions
diff --git a/src/audio_core/stream.cpp b/src/audio_core/stream.cpp
index 982c7af2f..6a5f53a57 100644
--- a/src/audio_core/stream.cpp
+++ b/src/audio_core/stream.cpp
@@ -105,7 +105,7 @@ void Stream::PlayNextBuffer() {
sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples());
- core_timing.ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event, {});
+ core_timing.ScheduleEvent(GetBufferReleaseCycles(*active_buffer), release_event, {});
}
void Stream::ReleaseActiveBuffer() {
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 3f3fdbd55..30eb9d82e 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -271,6 +271,7 @@ add_library(core STATIC
hle/service/filesystem/fsp_srv.h
hle/service/fgm/fgm.cpp
hle/service/fgm/fgm.h
+ hle/service/friend/errors.h
hle/service/friend/friend.cpp
hle/service/friend/friend.h
hle/service/friend/interface.cpp
@@ -430,6 +431,8 @@ add_library(core STATIC
hle/service/time/interface.h
hle/service/time/time.cpp
hle/service/time/time.h
+ hle/service/time/time_sharedmemory.cpp
+ hle/service/time/time_sharedmemory.h
hle/service/usb/usb.cpp
hle/service/usb/usb.h
hle/service/vi/display/vi_display.cpp
@@ -477,6 +480,8 @@ add_library(core STATIC
settings.h
telemetry_session.cpp
telemetry_session.h
+ tools/freezer.cpp
+ tools/freezer.h
)
create_target_directory_groups(core)
diff --git a/src/core/core.cpp b/src/core/core.cpp
index df26eb109..262411db8 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -33,6 +33,7 @@
#include "core/reporter.h"
#include "core/settings.h"
#include "core/telemetry_session.h"
+#include "core/tools/freezer.h"
#include "file_sys/cheat_engine.h"
#include "file_sys/patch_manager.h"
#include "video_core/debug_utils/debug_utils.h"
@@ -300,6 +301,7 @@ struct System::Impl {
bool is_powered_on = false;
std::unique_ptr<FileSys::CheatEngine> cheat_engine;
+ std::unique_ptr<Tools::Freezer> memory_freezer;
/// Frontend applets
Service::AM::Applets::AppletManager applet_manager;
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp
index 41adb2302..a58f7b131 100644
--- a/src/core/core_timing.cpp
+++ b/src/core/core_timing.cpp
@@ -56,12 +56,12 @@ void CoreTiming::Initialize() {
}
void CoreTiming::Shutdown() {
- MoveEvents();
ClearPendingEvents();
UnregisterAllEvents();
}
EventType* CoreTiming::RegisterEvent(const std::string& name, TimedCallback callback) {
+ std::lock_guard guard{inner_mutex};
// check for existing type with same name.
// we want event type names to remain unique so that we can use them for serialization.
ASSERT_MSG(event_types.find(name) == event_types.end(),
@@ -82,6 +82,7 @@ void CoreTiming::UnregisterAllEvents() {
void CoreTiming::ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata) {
ASSERT(event_type != nullptr);
+ std::lock_guard guard{inner_mutex};
const s64 timeout = GetTicks() + cycles_into_future;
// If this event needs to be scheduled before the next advance(), force one early
@@ -93,12 +94,8 @@ void CoreTiming::ScheduleEvent(s64 cycles_into_future, const EventType* event_ty
std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
}
-void CoreTiming::ScheduleEventThreadsafe(s64 cycles_into_future, const EventType* event_type,
- u64 userdata) {
- ts_queue.Push(Event{global_timer + cycles_into_future, 0, userdata, event_type});
-}
-
void CoreTiming::UnscheduleEvent(const EventType* event_type, u64 userdata) {
+ std::lock_guard guard{inner_mutex};
const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) {
return e.type == event_type && e.userdata == userdata;
});
@@ -110,10 +107,6 @@ void CoreTiming::UnscheduleEvent(const EventType* event_type, u64 userdata) {
}
}
-void CoreTiming::UnscheduleEventThreadsafe(const EventType* event_type, u64 userdata) {
- unschedule_queue.Push(std::make_pair(event_type, userdata));
-}
-
u64 CoreTiming::GetTicks() const {
u64 ticks = static_cast<u64>(global_timer);
if (!is_global_timer_sane) {
@@ -135,6 +128,7 @@ void CoreTiming::ClearPendingEvents() {
}
void CoreTiming::RemoveEvent(const EventType* event_type) {
+ std::lock_guard guard{inner_mutex};
const auto itr = std::remove_if(event_queue.begin(), event_queue.end(),
[&](const Event& e) { return e.type == event_type; });
@@ -145,11 +139,6 @@ void CoreTiming::RemoveEvent(const EventType* event_type) {
}
}
-void CoreTiming::RemoveNormalAndThreadsafeEvent(const EventType* event_type) {
- MoveEvents();
- RemoveEvent(event_type);
-}
-
void CoreTiming::ForceExceptionCheck(s64 cycles) {
cycles = std::max<s64>(0, cycles);
if (downcount <= cycles) {
@@ -162,19 +151,8 @@ void CoreTiming::ForceExceptionCheck(s64 cycles) {
downcount = static_cast<int>(cycles);
}
-void CoreTiming::MoveEvents() {
- for (Event ev; ts_queue.Pop(ev);) {
- ev.fifo_order = event_fifo_id++;
- event_queue.emplace_back(std::move(ev));
- std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
- }
-}
-
void CoreTiming::Advance() {
- MoveEvents();
- for (std::pair<const EventType*, u64> ev; unschedule_queue.Pop(ev);) {
- UnscheduleEvent(ev.first, ev.second);
- }
+ std::unique_lock<std::mutex> guard(inner_mutex);
const int cycles_executed = slice_length - downcount;
global_timer += cycles_executed;
@@ -186,7 +164,9 @@ void CoreTiming::Advance() {
Event evt = std::move(event_queue.front());
std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>());
event_queue.pop_back();
+ inner_mutex.unlock();
evt.type->callback(evt.userdata, global_timer - evt.time);
+ inner_mutex.lock();
}
is_global_timer_sane = false;
diff --git a/src/core/core_timing.h b/src/core/core_timing.h
index 9d2efde37..161c7007d 100644
--- a/src/core/core_timing.h
+++ b/src/core/core_timing.h
@@ -6,6 +6,7 @@
#include <chrono>
#include <functional>
+#include <mutex>
#include <string>
#include <unordered_map>
#include <vector>
@@ -67,7 +68,7 @@ public:
///
EventType* RegisterEvent(const std::string& name, TimedCallback callback);
- /// Unregisters all registered events thus far.
+ /// Unregisters all registered events thus far. Note: not thread unsafe
void UnregisterAllEvents();
/// After the first Advance, the slice lengths and the downcount will be reduced whenever an
@@ -76,20 +77,10 @@ public:
/// Scheduling from a callback will not update the downcount until the Advance() completes.
void ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata = 0);
- /// This is to be called when outside of hle threads, such as the graphics thread, wants to
- /// schedule things to be executed on the main thread.
- ///
- /// @note This doesn't change slice_length and thus events scheduled by this might be
- /// called with a delay of up to MAX_SLICE_LENGTH
- void ScheduleEventThreadsafe(s64 cycles_into_future, const EventType* event_type,
- u64 userdata = 0);
-
void UnscheduleEvent(const EventType* event_type, u64 userdata);
- void UnscheduleEventThreadsafe(const EventType* event_type, u64 userdata);
/// We only permit one event of each type in the queue at a time.
void RemoveEvent(const EventType* event_type);
- void RemoveNormalAndThreadsafeEvent(const EventType* event_type);
void ForceExceptionCheck(s64 cycles);
@@ -120,7 +111,6 @@ private:
/// Clear all pending events. This should ONLY be done on exit.
void ClearPendingEvents();
- void MoveEvents();
s64 global_timer = 0;
s64 idled_cycles = 0;
@@ -143,14 +133,9 @@ private:
// remain stable regardless of rehashes/resizing.
std::unordered_map<std::string, EventType> event_types;
- // The queue for storing the events from other threads threadsafe until they will be added
- // to the event_queue by the emu thread
- Common::MPSCQueue<Event> ts_queue;
-
- // The queue for unscheduling the events from other threads threadsafe
- Common::MPSCQueue<std::pair<const EventType*, u64>> unschedule_queue;
-
EventType* ev_lost = nullptr;
+
+ std::mutex inner_mutex;
};
} // namespace Core::Timing
diff --git a/src/core/file_sys/nca_metadata.h b/src/core/file_sys/nca_metadata.h
index 84d5cd1e0..1f82fff0a 100644
--- a/src/core/file_sys/nca_metadata.h
+++ b/src/core/file_sys/nca_metadata.h
@@ -35,9 +35,9 @@ enum class ContentRecordType : u8 {
Program = 1,
Data = 2,
Control = 3,
- Manual = 4,
- Legal = 5,
- Patch = 6,
+ HtmlDocument = 4,
+ LegalInformation = 5,
+ DeltaFragment = 6,
};
struct ContentRecord {
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index 4608490e0..3725b10f7 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -99,7 +99,7 @@ ContentRecordType GetCRTypeFromNCAType(NCAContentType type) {
return ContentRecordType::Data;
case NCAContentType::Manual:
// TODO(DarkLordZach): Peek at NCA contents to differentiate Manual and Legal.
- return ContentRecordType::Manual;
+ return ContentRecordType::HtmlDocument;
default:
UNREACHABLE_MSG("Invalid NCAContentType={:02X}", static_cast<u8>(type));
}
@@ -397,8 +397,8 @@ InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_ex
});
if (meta_iter == ncas.end()) {
- LOG_ERROR(Loader, "The XCI you are attempting to install does not have a metadata NCA and "
- "is therefore malformed. Double check your encryption keys.");
+ LOG_ERROR(Loader, "The file you are attempting to install does not have a metadata NCA and "
+ "is therefore malformed. Check your encryption keys.");
return InstallResult::ErrorMetaFailed;
}
@@ -415,6 +415,9 @@ InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_ex
const auto cnmt_file = section0->GetFiles()[0];
const CNMT cnmt(cnmt_file);
for (const auto& record : cnmt.GetContentRecords()) {
+ // Ignore DeltaFragments, they are not useful to us
+ if (record.type == ContentRecordType::DeltaFragment)
+ continue;
const auto nca = GetNCAFromNSPForID(nsp, record.nca_id);
if (nca == nullptr)
return InstallResult::ErrorCopyFailed;
diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp
index d0428a457..8b3b14e25 100644
--- a/src/core/file_sys/submission_package.cpp
+++ b/src/core/file_sys/submission_package.cpp
@@ -248,10 +248,13 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) {
auto next_file = pfs->GetFile(fmt::format("{}.nca", id_string));
if (next_file == nullptr) {
- LOG_WARNING(Service_FS,
- "NCA with ID {}.nca is listed in content metadata, but cannot "
- "be found in PFS. NSP appears to be corrupted.",
- id_string);
+ if (rec.type != ContentRecordType::DeltaFragment) {
+ LOG_WARNING(Service_FS,
+ "NCA with ID {}.nca is listed in content metadata, but cannot "
+ "be found in PFS. NSP appears to be corrupted.",
+ id_string);
+ }
+
continue;
}
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index c73a40977..a055a5002 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -76,13 +76,13 @@ void Thread::WakeAfterDelay(s64 nanoseconds) {
// This function might be called from any thread so we have to be cautious and use the
// thread-safe version of ScheduleEvent.
const s64 cycles = Core::Timing::nsToCycles(std::chrono::nanoseconds{nanoseconds});
- Core::System::GetInstance().CoreTiming().ScheduleEventThreadsafe(
+ Core::System::GetInstance().CoreTiming().ScheduleEvent(
cycles, kernel.ThreadWakeupCallbackEventType(), callback_handle);
}
void Thread::CancelWakeupTimer() {
- Core::System::GetInstance().CoreTiming().UnscheduleEventThreadsafe(
- kernel.ThreadWakeupCallbackEventType(), callback_handle);
+ Core::System::GetInstance().CoreTiming().UnscheduleEvent(kernel.ThreadWakeupCallbackEventType(),
+ callback_handle);
}
static std::optional<s32> GetNextProcessorId(u64 mask) {
diff --git a/src/core/hle/service/am/applets/web_browser.cpp b/src/core/hle/service/am/applets/web_browser.cpp
index 2762e0653..f3c9fef0e 100644
--- a/src/core/hle/service/am/applets/web_browser.cpp
+++ b/src/core/hle/service/am/applets/web_browser.cpp
@@ -459,10 +459,10 @@ void WebBrowser::InitializeOffline() {
case OfflineWebSource::OfflineHtmlPage:
// While there is an AppID TLV field, in official SW this is always ignored.
title_id = 0;
- type = FileSys::ContentRecordType::Manual;
+ type = FileSys::ContentRecordType::HtmlDocument;
break;
case OfflineWebSource::ApplicationLegalInformation:
- type = FileSys::ContentRecordType::Legal;
+ type = FileSys::ContentRecordType::LegalInformation;
break;
case OfflineWebSource::SystemDataPage:
type = FileSys::ContentRecordType::Data;
diff --git a/src/core/hle/service/friend/errors.h b/src/core/hle/service/friend/errors.h
new file mode 100644
index 000000000..b3996e275
--- /dev/null
+++ b/src/core/hle/service/friend/errors.h
@@ -0,0 +1,12 @@
+// 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::Friend {
+
+constexpr ResultCode ERR_NO_NOTIFICATIONS{ErrorModule::Account, 15};
+}
diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp
index 5100e376c..dec541f2e 100644
--- a/src/core/hle/service/friend/friend.cpp
+++ b/src/core/hle/service/friend/friend.cpp
@@ -2,8 +2,13 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <queue>
#include "common/logging/log.h"
+#include "common/uuid.h"
#include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/readable_event.h"
+#include "core/hle/kernel/writable_event.h"
+#include "core/hle/service/friend/errors.h"
#include "core/hle/service/friend/friend.h"
#include "core/hle/service/friend/interface.h"
@@ -109,6 +114,105 @@ private:
}
};
+class INotificationService final : public ServiceFramework<INotificationService> {
+public:
+ INotificationService(Common::UUID uuid) : ServiceFramework("INotificationService"), uuid(uuid) {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, &INotificationService::GetEvent, "GetEvent"},
+ {1, &INotificationService::Clear, "Clear"},
+ {2, &INotificationService::Pop, "Pop"}
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+
+private:
+ void GetEvent(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_ACC, "called");
+
+ IPC::ResponseBuilder rb{ctx, 2, 1};
+ rb.Push(RESULT_SUCCESS);
+
+ if (!is_event_created) {
+ auto& kernel = Core::System::GetInstance().Kernel();
+ notification_event = Kernel::WritableEvent::CreateEventPair(
+ kernel, Kernel::ResetType::Manual, "INotificationService:NotifyEvent");
+ is_event_created = true;
+ }
+ rb.PushCopyObjects(notification_event.readable);
+ }
+
+ void Clear(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_ACC, "called");
+ while (!notifications.empty()) {
+ notifications.pop();
+ }
+ std::memset(&states, 0, sizeof(States));
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ void Pop(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_ACC, "called");
+
+ if (notifications.empty()) {
+ LOG_ERROR(Service_ACC, "No notifications in queue!");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERR_NO_NOTIFICATIONS);
+ return;
+ }
+
+ const auto notification = notifications.front();
+ notifications.pop();
+
+ switch (notification.notification_type) {
+ case NotificationTypes::HasUpdatedFriendsList:
+ states.has_updated_friends = false;
+ break;
+ case NotificationTypes::HasReceivedFriendRequest:
+ states.has_received_friend_request = false;
+ break;
+ default:
+ // HOS seems not have an error case for an unknown notification
+ LOG_WARNING(Service_ACC, "Unknown notification {:08X}",
+ static_cast<u32>(notification.notification_type));
+ break;
+ }
+
+ IPC::ResponseBuilder rb{ctx, 6};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushRaw<SizedNotificationInfo>(notification);
+ }
+
+ enum class NotificationTypes : u32 {
+ HasUpdatedFriendsList = 0x65,
+ HasReceivedFriendRequest = 0x1
+ };
+
+ struct SizedNotificationInfo {
+ NotificationTypes notification_type;
+ INSERT_PADDING_WORDS(
+ 1); // TODO(ogniK): This doesn't seem to be used within any IPC returns as of now
+ u64_le account_id;
+ };
+ static_assert(sizeof(SizedNotificationInfo) == 0x10,
+ "SizedNotificationInfo is an incorrect size");
+
+ struct States {
+ bool has_updated_friends;
+ bool has_received_friend_request;
+ };
+
+ Common::UUID uuid;
+ bool is_event_created = false;
+ Kernel::EventPair notification_event;
+ std::queue<SizedNotificationInfo> notifications;
+ States states{};
+};
+
void Module::Interface::CreateFriendService(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
@@ -116,6 +220,17 @@ void Module::Interface::CreateFriendService(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_ACC, "called");
}
+void Module::Interface::CreateNotificationService(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ auto uuid = rp.PopRaw<Common::UUID>();
+
+ LOG_DEBUG(Service_ACC, "called, uuid={}", uuid.Format());
+
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushIpcInterface<INotificationService>(uuid);
+}
+
Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
: ServiceFramework(name), module(std::move(module)) {}
diff --git a/src/core/hle/service/friend/friend.h b/src/core/hle/service/friend/friend.h
index e762840cb..38d05fa8e 100644
--- a/src/core/hle/service/friend/friend.h
+++ b/src/core/hle/service/friend/friend.h
@@ -16,6 +16,7 @@ public:
~Interface() override;
void CreateFriendService(Kernel::HLERequestContext& ctx);
+ void CreateNotificationService(Kernel::HLERequestContext& ctx);
protected:
std::shared_ptr<Module> module;
diff --git a/src/core/hle/service/friend/interface.cpp b/src/core/hle/service/friend/interface.cpp
index 5a6840af5..5b384f733 100644
--- a/src/core/hle/service/friend/interface.cpp
+++ b/src/core/hle/service/friend/interface.cpp
@@ -10,7 +10,7 @@ Friend::Friend(std::shared_ptr<Module> module, const char* name)
: Interface(std::move(module), name) {
static const FunctionInfo functions[] = {
{0, &Friend::CreateFriendService, "CreateFriendService"},
- {1, nullptr, "CreateNotificationService"},
+ {1, &Friend::CreateNotificationService, "CreateNotificationService"},
{2, nullptr, "CreateDaemonSuspendSessionService"},
};
RegisterHandlers(functions);
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index ec9d755b7..5fc7d3cab 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -249,7 +249,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system,
Sockets::InstallInterfaces(*sm);
SPL::InstallInterfaces(*sm);
SSL::InstallInterfaces(*sm);
- Time::InstallInterfaces(*sm);
+ Time::InstallInterfaces(system);
USB::InstallInterfaces(*sm);
VI::InstallInterfaces(*sm, nv_flinger);
WLAN::InstallInterfaces(*sm);
diff --git a/src/core/hle/service/time/interface.cpp b/src/core/hle/service/time/interface.cpp
index 8d122ae33..1030185e0 100644
--- a/src/core/hle/service/time/interface.cpp
+++ b/src/core/hle/service/time/interface.cpp
@@ -6,8 +6,9 @@
namespace Service::Time {
-Time::Time(std::shared_ptr<Module> time, const char* name)
- : Module::Interface(std::move(time), name) {
+Time::Time(std::shared_ptr<Module> time, std::shared_ptr<SharedMemory> shared_memory,
+ const char* name)
+ : Module::Interface(std::move(time), std::move(shared_memory), name) {
// clang-format off
static const FunctionInfo functions[] = {
{0, &Time::GetStandardUserSystemClock, "GetStandardUserSystemClock"},
@@ -16,12 +17,12 @@ Time::Time(std::shared_ptr<Module> time, const char* name)
{3, &Time::GetTimeZoneService, "GetTimeZoneService"},
{4, &Time::GetStandardLocalSystemClock, "GetStandardLocalSystemClock"},
{5, nullptr, "GetEphemeralNetworkSystemClock"},
- {20, nullptr, "GetSharedMemoryNativeHandle"},
+ {20, &Time::GetSharedMemoryNativeHandle, "GetSharedMemoryNativeHandle"},
{30, nullptr, "GetStandardNetworkClockOperationEventReadableHandle"},
{31, nullptr, "GetEphemeralNetworkClockOperationEventReadableHandle"},
{50, nullptr, "SetStandardSteadyClockInternalOffset"},
- {100, nullptr, "IsStandardUserSystemClockAutomaticCorrectionEnabled"},
- {101, nullptr, "SetStandardUserSystemClockAutomaticCorrectionEnabled"},
+ {100, &Time::IsStandardUserSystemClockAutomaticCorrectionEnabled, "IsStandardUserSystemClockAutomaticCorrectionEnabled"},
+ {101, &Time::SetStandardUserSystemClockAutomaticCorrectionEnabled, "SetStandardUserSystemClockAutomaticCorrectionEnabled"},
{102, nullptr, "GetStandardUserSystemClockInitialYear"},
{200, nullptr, "IsStandardNetworkSystemClockAccuracySufficient"},
{201, nullptr, "GetStandardUserSystemClockAutomaticCorrectionUpdatedTime"},
diff --git a/src/core/hle/service/time/interface.h b/src/core/hle/service/time/interface.h
index cd6b44dec..bdf0883e2 100644
--- a/src/core/hle/service/time/interface.h
+++ b/src/core/hle/service/time/interface.h
@@ -8,9 +8,12 @@
namespace Service::Time {
+class SharedMemory;
+
class Time final : public Module::Interface {
public:
- explicit Time(std::shared_ptr<Module> time, const char* name);
+ explicit Time(std::shared_ptr<Module> time, std::shared_ptr<SharedMemory> shared_memory,
+ const char* name);
~Time() override;
};
diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp
index 346bad80d..ae6446204 100644
--- a/src/core/hle/service/time/time.cpp
+++ b/src/core/hle/service/time/time.cpp
@@ -13,6 +13,7 @@
#include "core/hle/kernel/client_session.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"
namespace Service::Time {
@@ -61,9 +62,18 @@ static u64 CalendarToPosix(const CalendarTime& calendar_time,
return static_cast<u64>(epoch_time);
}
+enum class ClockContextType {
+ StandardSteady,
+ StandardUserSystem,
+ StandardNetworkSystem,
+ StandardLocalSystem,
+};
+
class ISystemClock final : public ServiceFramework<ISystemClock> {
public:
- ISystemClock() : ServiceFramework("ISystemClock") {
+ ISystemClock(std::shared_ptr<Service::Time::SharedMemory> shared_memory,
+ ClockContextType clock_type)
+ : ServiceFramework("ISystemClock"), shared_memory(shared_memory), clock_type(clock_type) {
static const FunctionInfo functions[] = {
{0, &ISystemClock::GetCurrentTime, "GetCurrentTime"},
{1, nullptr, "SetCurrentTime"},
@@ -72,6 +82,8 @@ public:
};
RegisterHandlers(functions);
+
+ UpdateSharedMemoryContext(system_clock_context);
}
private:
@@ -87,34 +99,63 @@ private:
void GetSystemClockContext(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_Time, "(STUBBED) called");
- SystemClockContext system_clock_ontext{};
+ // 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);
+
IPC::ResponseBuilder rb{ctx, (sizeof(SystemClockContext) / 4) + 2};
rb.Push(RESULT_SUCCESS);
- rb.PushRaw(system_clock_ontext);
+ 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;
};
class ISteadyClock final : public ServiceFramework<ISteadyClock> {
public:
- ISteadyClock() : ServiceFramework("ISteadyClock") {
+ ISteadyClock(std::shared_ptr<SharedMemory> shared_memory)
+ : ServiceFramework("ISteadyClock"), shared_memory(shared_memory) {
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& core_timing = Core::System::GetInstance().CoreTiming();
- const auto ms = Core::Timing::CyclesToMs(core_timing.GetTicks());
- const SteadyClockTimePoint steady_clock_time_point{static_cast<u64_le>(ms.count() / 1000),
- {}};
+ const auto time_point = GetCurrentTimePoint();
+ // TODO(ogniK): This should be updated periodically
+ shared_memory->SetStandardSteadyClockTimepoint(time_point);
+
IPC::ResponseBuilder rb{ctx, (sizeof(SteadyClockTimePoint) / 4) + 2};
rb.Push(RESULT_SUCCESS);
- rb.PushRaw(steady_clock_time_point);
+ rb.PushRaw(time_point);
}
+
+ SteadyClockTimePoint GetCurrentTimePoint() const {
+ const auto& core_timing = Core::System::GetInstance().CoreTiming();
+ const auto ms = Core::Timing::CyclesToMs(core_timing.GetTicks());
+ return {static_cast<u64_le>(ms.count() / 1000), {}};
+ }
+
+ std::shared_ptr<SharedMemory> shared_memory;
};
class ITimeZoneService final : public ServiceFramework<ITimeZoneService> {
@@ -233,7 +274,7 @@ void Module::Interface::GetStandardUserSystemClock(Kernel::HLERequestContext& ct
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<ISystemClock>();
+ rb.PushIpcInterface<ISystemClock>(shared_memory, ClockContextType::StandardUserSystem);
}
void Module::Interface::GetStandardNetworkSystemClock(Kernel::HLERequestContext& ctx) {
@@ -241,7 +282,7 @@ void Module::Interface::GetStandardNetworkSystemClock(Kernel::HLERequestContext&
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<ISystemClock>();
+ rb.PushIpcInterface<ISystemClock>(shared_memory, ClockContextType::StandardNetworkSystem);
}
void Module::Interface::GetStandardSteadyClock(Kernel::HLERequestContext& ctx) {
@@ -249,7 +290,7 @@ void Module::Interface::GetStandardSteadyClock(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<ISteadyClock>();
+ rb.PushIpcInterface<ISteadyClock>(shared_memory);
}
void Module::Interface::GetTimeZoneService(Kernel::HLERequestContext& ctx) {
@@ -265,7 +306,7 @@ void Module::Interface::GetStandardLocalSystemClock(Kernel::HLERequestContext& c
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<ISystemClock>();
+ rb.PushIpcInterface<ISystemClock>(shared_memory, ClockContextType::StandardLocalSystem);
}
void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
@@ -333,16 +374,52 @@ void Module::Interface::CalculateStandardUserSystemClockDifferenceByUser(
rb.PushRaw<u64>(difference);
}
-Module::Interface::Interface(std::shared_ptr<Module> time, const char* name)
- : ServiceFramework(name), time(std::move(time)) {}
+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);
+}
+
+Module::Interface::Interface(std::shared_ptr<Module> time,
+ std::shared_ptr<SharedMemory> shared_memory, const char* name)
+ : ServiceFramework(name), time(std::move(time)), shared_memory(std::move(shared_memory)) {}
Module::Interface::~Interface() = default;
-void InstallInterfaces(SM::ServiceManager& service_manager) {
+void InstallInterfaces(Core::System& system) {
auto time = std::make_shared<Module>();
- std::make_shared<Time>(time, "time:a")->InstallAsService(service_manager);
- std::make_shared<Time>(time, "time:s")->InstallAsService(service_manager);
- std::make_shared<Time>(time, "time:u")->InstallAsService(service_manager);
+ auto shared_mem = std::make_shared<SharedMemory>(system);
+
+ std::make_shared<Time>(time, shared_mem, "time:a")->InstallAsService(system.ServiceManager());
+ std::make_shared<Time>(time, shared_mem, "time:s")->InstallAsService(system.ServiceManager());
+ std::make_shared<Time>(std::move(time), shared_mem, "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 f11affe95..e0708f856 100644
--- a/src/core/hle/service/time/time.h
+++ b/src/core/hle/service/time/time.h
@@ -10,6 +10,8 @@
namespace Service::Time {
+class SharedMemory;
+
struct LocationName {
std::array<u8, 0x24> name;
};
@@ -77,7 +79,8 @@ class Module final {
public:
class Interface : public ServiceFramework<Interface> {
public:
- explicit Interface(std::shared_ptr<Module> time, const char* name);
+ explicit Interface(std::shared_ptr<Module> time,
+ std::shared_ptr<SharedMemory> shared_memory, const char* name);
~Interface() override;
void GetStandardUserSystemClock(Kernel::HLERequestContext& ctx);
@@ -87,13 +90,17 @@ public:
void GetStandardLocalSystemClock(Kernel::HLERequestContext& ctx);
void GetClockSnapshot(Kernel::HLERequestContext& ctx);
void CalculateStandardUserSystemClockDifferenceByUser(Kernel::HLERequestContext& ctx);
+ void GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx);
+ void IsStandardUserSystemClockAutomaticCorrectionEnabled(Kernel::HLERequestContext& ctx);
+ void SetStandardUserSystemClockAutomaticCorrectionEnabled(Kernel::HLERequestContext& ctx);
protected:
std::shared_ptr<Module> time;
+ std::shared_ptr<SharedMemory> shared_memory;
};
};
/// Registers all Time services with the specified service manager.
-void InstallInterfaces(SM::ServiceManager& service_manager);
+void InstallInterfaces(Core::System& system);
} // namespace Service::Time
diff --git a/src/core/hle/service/time/time_sharedmemory.cpp b/src/core/hle/service/time/time_sharedmemory.cpp
new file mode 100644
index 000000000..bfc81b83c
--- /dev/null
+++ b/src/core/hle/service/time/time_sharedmemory.cpp
@@ -0,0 +1,68 @@
+// 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/hle/service/time/time_sharedmemory.h"
+
+namespace Service::Time {
+const 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));
+}
+
+SharedMemory::~SharedMemory() = default;
+
+Kernel::SharedPtr<Kernel::SharedMemory> SharedMemory::GetSharedMemoryHolder() const {
+ return shared_memory_holder;
+}
+
+void SharedMemory::SetStandardSteadyClockTimepoint(const SteadyClockTimePoint& timepoint) {
+ shared_memory_format.standard_steady_clock_timepoint.StoreData(
+ shared_memory_holder->GetPointer(), timepoint);
+}
+
+void SharedMemory::SetStandardLocalSystemClockContext(const SystemClockContext& context) {
+ shared_memory_format.standard_local_system_clock_context.StoreData(
+ shared_memory_holder->GetPointer(), context);
+}
+
+void SharedMemory::SetStandardNetworkSystemClockContext(const SystemClockContext& context) {
+ shared_memory_format.standard_network_system_clock_context.StoreData(
+ shared_memory_holder->GetPointer(), context);
+}
+
+void SharedMemory::SetStandardUserSystemClockAutomaticCorrectionEnabled(bool 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());
+}
+
+} // namespace Service::Time
diff --git a/src/core/hle/service/time/time_sharedmemory.h b/src/core/hle/service/time/time_sharedmemory.h
new file mode 100644
index 000000000..cb8253541
--- /dev/null
+++ b/src/core/hle/service/time/time_sharedmemory.h
@@ -0,0 +1,74 @@
+// 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/kernel/shared_memory.h"
+#include "core/hle/service/time/time.h"
+
+namespace Service::Time {
+class SharedMemory {
+public:
+ explicit SharedMemory(Core::System& system);
+ ~SharedMemory();
+
+ // Return the shared memory handle
+ Kernel::SharedPtr<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");
+ u32_le read_attempt{};
+ std::array<T, 2> data{};
+
+ // These are not actually memory barriers at the moment as we don't have multicore and all
+ // HLE is mutexed. This will need to properly be implemented when we start updating the time
+ // points on threads. As of right now, we'll be updated both values synchronously and just
+ // incrementing the read_attempt to indicate that we waited.
+ void StoreData(u8* shared_memory, T data_to_store) {
+ std::memcpy(this, shared_memory + Offset, sizeof(*this));
+ read_attempt++;
+ data[read_attempt & 1] = data_to_store;
+ std::memcpy(shared_memory + Offset, this, sizeof(*this));
+ }
+
+ // For reading we're just going to read the last stored value. If there was no value stored
+ // it will just end up reading an empty value as intended.
+ T ReadData(u8* shared_memory) {
+ std::memcpy(this, shared_memory + Offset, sizeof(*this));
+ return data[(read_attempt - 1) & 1];
+ }
+ };
+
+ // 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<bool, 0xc8> standard_user_system_clock_automatic_correction;
+ u32_le format_version;
+ };
+ static_assert(sizeof(Format) == 0xd8, "Format is an invalid size");
+
+private:
+ Kernel::SharedPtr<Kernel::SharedMemory> shared_memory_holder{};
+ Core::System& system;
+ Format shared_memory_format{};
+};
+
+} // namespace Service::Time
diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp
index 3a22ec2c6..b1171ce65 100644
--- a/src/core/loader/nsp.cpp
+++ b/src/core/loader/nsp.cpp
@@ -168,7 +168,8 @@ ResultStatus AppLoader_NSP::ReadControlData(FileSys::NACP& nacp) {
}
ResultStatus AppLoader_NSP::ReadManualRomFS(FileSys::VirtualFile& file) {
- const auto nca = nsp->GetNCA(nsp->GetProgramTitleID(), FileSys::ContentRecordType::Manual);
+ const auto nca =
+ nsp->GetNCA(nsp->GetProgramTitleID(), FileSys::ContentRecordType::HtmlDocument);
if (nsp->GetStatus() != ResultStatus::Success || nca == nullptr)
return ResultStatus::ErrorNoRomFS;
file = nca->GetRomFS();
diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp
index a5c4d3688..5e8553db9 100644
--- a/src/core/loader/xci.cpp
+++ b/src/core/loader/xci.cpp
@@ -134,7 +134,7 @@ ResultStatus AppLoader_XCI::ReadControlData(FileSys::NACP& control) {
ResultStatus AppLoader_XCI::ReadManualRomFS(FileSys::VirtualFile& file) {
const auto nca = xci->GetSecurePartitionNSP()->GetNCA(xci->GetProgramTitleID(),
- FileSys::ContentRecordType::Manual);
+ FileSys::ContentRecordType::HtmlDocument);
if (xci->GetStatus() != ResultStatus::Success || nca == nullptr)
return ResultStatus::ErrorXCIMissingPartition;
file = nca->GetRomFS();
diff --git a/src/core/tools/freezer.cpp b/src/core/tools/freezer.cpp
new file mode 100644
index 000000000..17f050068
--- /dev/null
+++ b/src/core/tools/freezer.cpp
@@ -0,0 +1,188 @@
+// Copyright 2019 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/assert.h"
+#include "common/logging/log.h"
+#include "core/core.h"
+#include "core/core_timing.h"
+#include "core/core_timing_util.h"
+#include "core/memory.h"
+#include "core/tools/freezer.h"
+
+namespace Tools {
+
+namespace {
+
+constexpr s64 MEMORY_FREEZER_TICKS = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 60);
+
+u64 MemoryReadWidth(u32 width, VAddr addr) {
+ switch (width) {
+ case 1:
+ return Memory::Read8(addr);
+ case 2:
+ return Memory::Read16(addr);
+ case 4:
+ return Memory::Read32(addr);
+ case 8:
+ return Memory::Read64(addr);
+ default:
+ UNREACHABLE();
+ return 0;
+ }
+}
+
+void MemoryWriteWidth(u32 width, VAddr addr, u64 value) {
+ switch (width) {
+ case 1:
+ Memory::Write8(addr, static_cast<u8>(value));
+ break;
+ case 2:
+ Memory::Write16(addr, static_cast<u16>(value));
+ break;
+ case 4:
+ Memory::Write32(addr, static_cast<u32>(value));
+ break;
+ case 8:
+ Memory::Write64(addr, value);
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
+} // Anonymous namespace
+
+Freezer::Freezer(Core::Timing::CoreTiming& core_timing) : core_timing(core_timing) {
+ event = core_timing.RegisterEvent(
+ "MemoryFreezer::FrameCallback",
+ [this](u64 userdata, s64 cycles_late) { FrameCallback(userdata, cycles_late); });
+ core_timing.ScheduleEvent(MEMORY_FREEZER_TICKS, event);
+}
+
+Freezer::~Freezer() {
+ core_timing.UnscheduleEvent(event, 0);
+}
+
+void Freezer::SetActive(bool active) {
+ if (!this->active.exchange(active)) {
+ FillEntryReads();
+ core_timing.ScheduleEvent(MEMORY_FREEZER_TICKS, event);
+ LOG_DEBUG(Common_Memory, "Memory freezer activated!");
+ } else {
+ LOG_DEBUG(Common_Memory, "Memory freezer deactivated!");
+ }
+}
+
+bool Freezer::IsActive() const {
+ return active.load(std::memory_order_relaxed);
+}
+
+void Freezer::Clear() {
+ std::lock_guard lock{entries_mutex};
+
+ LOG_DEBUG(Common_Memory, "Clearing all frozen memory values.");
+
+ entries.clear();
+}
+
+u64 Freezer::Freeze(VAddr address, u32 width) {
+ std::lock_guard lock{entries_mutex};
+
+ const auto current_value = MemoryReadWidth(width, address);
+ entries.push_back({address, width, current_value});
+
+ LOG_DEBUG(Common_Memory,
+ "Freezing memory for address={:016X}, width={:02X}, current_value={:016X}", address,
+ width, current_value);
+
+ return current_value;
+}
+
+void Freezer::Unfreeze(VAddr address) {
+ std::lock_guard lock{entries_mutex};
+
+ LOG_DEBUG(Common_Memory, "Unfreezing memory for address={:016X}", address);
+
+ entries.erase(
+ std::remove_if(entries.begin(), entries.end(),
+ [&address](const Entry& entry) { return entry.address == address; }),
+ entries.end());
+}
+
+bool Freezer::IsFrozen(VAddr address) const {
+ std::lock_guard lock{entries_mutex};
+
+ return std::find_if(entries.begin(), entries.end(), [&address](const Entry& entry) {
+ return entry.address == address;
+ }) != entries.end();
+}
+
+void Freezer::SetFrozenValue(VAddr address, u64 value) {
+ std::lock_guard lock{entries_mutex};
+
+ const auto iter = std::find_if(entries.begin(), entries.end(), [&address](const Entry& entry) {
+ return entry.address == address;
+ });
+
+ if (iter == entries.end()) {
+ LOG_ERROR(Common_Memory,
+ "Tried to set freeze value for address={:016X} that is not frozen!", address);
+ return;
+ }
+
+ LOG_DEBUG(Common_Memory,
+ "Manually overridden freeze value for address={:016X}, width={:02X} to value={:016X}",
+ iter->address, iter->width, value);
+ iter->value = value;
+}
+
+std::optional<Freezer::Entry> Freezer::GetEntry(VAddr address) const {
+ std::lock_guard lock{entries_mutex};
+
+ const auto iter = std::find_if(entries.begin(), entries.end(), [&address](const Entry& entry) {
+ return entry.address == address;
+ });
+
+ if (iter == entries.end()) {
+ return std::nullopt;
+ }
+
+ return *iter;
+}
+
+std::vector<Freezer::Entry> Freezer::GetEntries() const {
+ std::lock_guard lock{entries_mutex};
+
+ return entries;
+}
+
+void Freezer::FrameCallback(u64 userdata, s64 cycles_late) {
+ if (!IsActive()) {
+ LOG_DEBUG(Common_Memory, "Memory freezer has been deactivated, ending callback events.");
+ return;
+ }
+
+ std::lock_guard lock{entries_mutex};
+
+ for (const auto& entry : entries) {
+ LOG_DEBUG(Common_Memory,
+ "Enforcing memory freeze at address={:016X}, value={:016X}, width={:02X}",
+ entry.address, entry.value, entry.width);
+ MemoryWriteWidth(entry.width, entry.address, entry.value);
+ }
+
+ core_timing.ScheduleEvent(MEMORY_FREEZER_TICKS - cycles_late, event);
+}
+
+void Freezer::FillEntryReads() {
+ std::lock_guard lock{entries_mutex};
+
+ LOG_DEBUG(Common_Memory, "Updating memory freeze entries to current values.");
+
+ for (auto& entry : entries) {
+ entry.value = MemoryReadWidth(entry.width, entry.address);
+ }
+}
+
+} // namespace Tools
diff --git a/src/core/tools/freezer.h b/src/core/tools/freezer.h
new file mode 100644
index 000000000..b58de5472
--- /dev/null
+++ b/src/core/tools/freezer.h
@@ -0,0 +1,82 @@
+// Copyright 2019 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <atomic>
+#include <mutex>
+#include <optional>
+#include <vector>
+#include "common/common_types.h"
+
+namespace Core::Timing {
+class CoreTiming;
+struct EventType;
+} // namespace Core::Timing
+
+namespace Tools {
+
+/**
+ * This class allows the user to prevent an application from writing new values to certain memory
+ * locations. This has a variety of uses when attempting to reverse a game.
+ *
+ * One example could be a cheat to prevent Mario from taking damage in SMO. One could freeze the
+ * memory address that the game uses to store Mario's health so when he takes damage (and the game
+ * tries to write the new health value to memory), the value won't change.
+ */
+class Freezer {
+public:
+ struct Entry {
+ VAddr address;
+ u32 width;
+ u64 value;
+ };
+
+ explicit Freezer(Core::Timing::CoreTiming& core_timing);
+ ~Freezer();
+
+ // Enables or disables the entire memory freezer.
+ void SetActive(bool active);
+
+ // Returns whether or not the freezer is active.
+ bool IsActive() const;
+
+ // Removes all entries from the freezer.
+ void Clear();
+
+ // Freezes a value to its current memory address. The value the memory is kept at will be the
+ // value that is read during this function. Width can be 1, 2, 4, or 8 (in bytes).
+ u64 Freeze(VAddr address, u32 width);
+
+ // Unfreezes the memory value at address. If the address isn't frozen, this is a no-op.
+ void Unfreeze(VAddr address);
+
+ // Returns whether or not the address is frozen.
+ bool IsFrozen(VAddr address) const;
+
+ // Sets the value that address should be frozen to. This doesn't change the width set by using
+ // Freeze(). If the value isn't frozen, this will not freeze it and is thus a no-op.
+ void SetFrozenValue(VAddr address, u64 value);
+
+ // Returns the entry corresponding to the address if the address is frozen, otherwise
+ // std::nullopt.
+ std::optional<Entry> GetEntry(VAddr address) const;
+
+ // Returns all the entries in the freezer, an empty vector means nothing is frozen.
+ std::vector<Entry> GetEntries() const;
+
+private:
+ void FrameCallback(u64 userdata, s64 cycles_late);
+ void FillEntryReads();
+
+ std::atomic_bool active{false};
+
+ mutable std::mutex entries_mutex;
+ std::vector<Entry> entries;
+
+ Core::Timing::EventType* event;
+ Core::Timing::CoreTiming& core_timing;
+};
+
+} // namespace Tools
diff --git a/src/tests/core/core_timing.cpp b/src/tests/core/core_timing.cpp
index 340d6a272..f8be8fd19 100644
--- a/src/tests/core/core_timing.cpp
+++ b/src/tests/core/core_timing.cpp
@@ -99,24 +99,24 @@ TEST_CASE("CoreTiming[Threadsave]", "[core]") {
core_timing.Advance();
// D -> B -> C -> A -> E
- core_timing.ScheduleEventThreadsafe(1000, cb_a, CB_IDS[0]);
- // Manually force since ScheduleEventThreadsafe doesn't call it
+ core_timing.ScheduleEvent(1000, cb_a, CB_IDS[0]);
+ // Manually force since ScheduleEvent doesn't call it
core_timing.ForceExceptionCheck(1000);
REQUIRE(1000 == core_timing.GetDowncount());
- core_timing.ScheduleEventThreadsafe(500, cb_b, CB_IDS[1]);
- // Manually force since ScheduleEventThreadsafe doesn't call it
+ core_timing.ScheduleEvent(500, cb_b, CB_IDS[1]);
+ // Manually force since ScheduleEvent doesn't call it
core_timing.ForceExceptionCheck(500);
REQUIRE(500 == core_timing.GetDowncount());
- core_timing.ScheduleEventThreadsafe(800, cb_c, CB_IDS[2]);
- // Manually force since ScheduleEventThreadsafe doesn't call it
+ core_timing.ScheduleEvent(800, cb_c, CB_IDS[2]);
+ // Manually force since ScheduleEvent doesn't call it
core_timing.ForceExceptionCheck(800);
REQUIRE(500 == core_timing.GetDowncount());
- core_timing.ScheduleEventThreadsafe(100, cb_d, CB_IDS[3]);
- // Manually force since ScheduleEventThreadsafe doesn't call it
+ core_timing.ScheduleEvent(100, cb_d, CB_IDS[3]);
+ // Manually force since ScheduleEvent doesn't call it
core_timing.ForceExceptionCheck(100);
REQUIRE(100 == core_timing.GetDowncount());
- core_timing.ScheduleEventThreadsafe(1200, cb_e, CB_IDS[4]);
- // Manually force since ScheduleEventThreadsafe doesn't call it
+ core_timing.ScheduleEvent(1200, cb_e, CB_IDS[4]);
+ // Manually force since ScheduleEvent doesn't call it
core_timing.ForceExceptionCheck(1200);
REQUIRE(100 == core_timing.GetDowncount());
diff --git a/src/video_core/rasterizer_cache.h b/src/video_core/rasterizer_cache.h
index 0c4ea1494..6de1597a2 100644
--- a/src/video_core/rasterizer_cache.h
+++ b/src/video_core/rasterizer_cache.h
@@ -169,6 +169,8 @@ protected:
object->MarkAsModified(false, *this);
}
+ std::recursive_mutex mutex;
+
private:
/// Returns a list of cached objects from the specified memory region, ordered by access time
std::vector<T> GetSortedObjectsFromRegion(CacheAddr addr, u64 size) {
@@ -208,5 +210,4 @@ private:
IntervalCache interval_cache; ///< Cache of objects
u64 modified_ticks{}; ///< Counter of cache state ticks, used for in-order flushing
VideoCore::RasterizerInterface& rasterizer;
- std::recursive_mutex mutex;
};
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
index 48b86f3bd..2b9bd142e 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
@@ -23,6 +23,7 @@ OGLBufferCache::OGLBufferCache(RasterizerOpenGL& rasterizer, std::size_t size)
GLintptr OGLBufferCache::UploadMemory(GPUVAddr gpu_addr, std::size_t size, std::size_t alignment,
bool cache) {
+ std::lock_guard lock{mutex};
auto& memory_manager = Core::System::GetInstance().GPU().MemoryManager();
// Cache management is a big overhead, so only cache entries with a given size.
@@ -62,6 +63,7 @@ GLintptr OGLBufferCache::UploadMemory(GPUVAddr gpu_addr, std::size_t size, std::
GLintptr OGLBufferCache::UploadHostMemory(const void* raw_pointer, std::size_t size,
std::size_t alignment) {
+ std::lock_guard lock{mutex};
AlignBuffer(alignment);
std::memcpy(buffer_ptr, raw_pointer, size);
const GLintptr uploaded_offset = buffer_offset;
diff --git a/src/video_core/renderer_opengl/gl_global_cache.cpp b/src/video_core/renderer_opengl/gl_global_cache.cpp
index ea4a593af..d5e385151 100644
--- a/src/video_core/renderer_opengl/gl_global_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_global_cache.cpp
@@ -76,6 +76,7 @@ GlobalRegionCacheOpenGL::GlobalRegionCacheOpenGL(RasterizerOpenGL& rasterizer)
GlobalRegion GlobalRegionCacheOpenGL::GetGlobalRegion(
const GLShader::GlobalMemoryEntry& global_region,
Tegra::Engines::Maxwell3D::Regs::ShaderStage stage) {
+ std::lock_guard lock{mutex};
auto& gpu{Core::System::GetInstance().GPU()};
auto& memory_manager{gpu.MemoryManager()};
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index ac8a9e6b7..2d78e2b60 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -221,44 +221,37 @@ std::set<GLenum> GetSupportedFormats() {
} // Anonymous namespace
-CachedShader::CachedShader(const Device& device, VAddr cpu_addr, u64 unique_identifier,
- Maxwell::ShaderProgram program_type, ShaderDiskCacheOpenGL& disk_cache,
- const PrecompiledPrograms& precompiled_programs,
- ProgramCode&& program_code, ProgramCode&& program_code_b, u8* host_ptr)
- : RasterizerCacheObject{host_ptr}, host_ptr{host_ptr}, cpu_addr{cpu_addr},
- unique_identifier{unique_identifier}, program_type{program_type}, disk_cache{disk_cache},
- precompiled_programs{precompiled_programs} {
- const std::size_t code_size{CalculateProgramSize(program_code)};
- const std::size_t code_size_b{program_code_b.empty() ? 0
- : CalculateProgramSize(program_code_b)};
- GLShader::ProgramResult program_result{
- CreateProgram(device, program_type, program_code, program_code_b)};
- if (program_result.first.empty()) {
+CachedShader::CachedShader(const ShaderParameters& params, Maxwell::ShaderProgram program_type,
+ GLShader::ProgramResult result)
+ : RasterizerCacheObject{params.host_ptr}, host_ptr{params.host_ptr}, cpu_addr{params.cpu_addr},
+ unique_identifier{params.unique_identifier}, program_type{program_type},
+ disk_cache{params.disk_cache}, precompiled_programs{params.precompiled_programs},
+ entries{result.second}, code{std::move(result.first)}, shader_length{entries.shader_length} {}
+
+Shader CachedShader::CreateStageFromMemory(const ShaderParameters& params,
+ Maxwell::ShaderProgram program_type,
+ ProgramCode&& program_code,
+ ProgramCode&& program_code_b) {
+ const auto code_size{CalculateProgramSize(program_code)};
+ const auto code_size_b{CalculateProgramSize(program_code_b)};
+ auto result{CreateProgram(params.device, program_type, program_code, program_code_b)};
+ if (result.first.empty()) {
// TODO(Rodrigo): Unimplemented shader stages hit here, avoid using these for now
- return;
+ return {};
}
- code = program_result.first;
- entries = program_result.second;
- shader_length = entries.shader_length;
+ params.disk_cache.SaveRaw(ShaderDiskCacheRaw(
+ params.unique_identifier, program_type, static_cast<u32>(code_size / sizeof(u64)),
+ static_cast<u32>(code_size_b / sizeof(u64)), std::move(program_code),
+ std::move(program_code_b)));
- const ShaderDiskCacheRaw raw(unique_identifier, program_type,
- static_cast<u32>(code_size / sizeof(u64)),
- static_cast<u32>(code_size_b / sizeof(u64)),
- std::move(program_code), std::move(program_code_b));
- disk_cache.SaveRaw(raw);
+ return std::make_shared<CachedShader>(params, program_type, std::move(result));
}
-CachedShader::CachedShader(VAddr cpu_addr, u64 unique_identifier,
- Maxwell::ShaderProgram program_type, ShaderDiskCacheOpenGL& disk_cache,
- const PrecompiledPrograms& precompiled_programs,
- GLShader::ProgramResult result, u8* host_ptr)
- : RasterizerCacheObject{host_ptr}, cpu_addr{cpu_addr}, unique_identifier{unique_identifier},
- program_type{program_type}, disk_cache{disk_cache}, precompiled_programs{
- precompiled_programs} {
- code = std::move(result.first);
- entries = result.second;
- shader_length = entries.shader_length;
+Shader CachedShader::CreateStageFromCache(const ShaderParameters& params,
+ Maxwell::ShaderProgram program_type,
+ GLShader::ProgramResult result) {
+ return std::make_shared<CachedShader>(params, program_type, std::move(result));
}
std::tuple<GLuint, BaseBindings> CachedShader::GetProgramHandle(GLenum primitive_mode,
@@ -570,18 +563,17 @@ Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) {
memory_manager.GetPointer(program_addr_b));
}
- const u64 unique_identifier = GetUniqueIdentifier(program, program_code, program_code_b);
- const VAddr cpu_addr{*memory_manager.GpuToCpuAddress(program_addr)};
+ const auto unique_identifier = GetUniqueIdentifier(program, program_code, program_code_b);
+ const auto cpu_addr{*memory_manager.GpuToCpuAddress(program_addr)};
+ const ShaderParameters params{disk_cache, precompiled_programs, device, cpu_addr,
+ host_ptr, unique_identifier};
+
const auto found = precompiled_shaders.find(unique_identifier);
- if (found != precompiled_shaders.end()) {
- // Create a shader from the cache
- shader = std::make_shared<CachedShader>(cpu_addr, unique_identifier, program, disk_cache,
- precompiled_programs, found->second, host_ptr);
+ if (found == precompiled_shaders.end()) {
+ shader = CachedShader::CreateStageFromMemory(params, program, std::move(program_code),
+ std::move(program_code_b));
} else {
- // Create a shader from guest memory
- shader = std::make_shared<CachedShader>(
- device, cpu_addr, unique_identifier, program, disk_cache, precompiled_programs,
- std::move(program_code), std::move(program_code_b), host_ptr);
+ shader = CachedShader::CreateStageFromCache(params, program, found->second);
}
Register(shader);
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h
index 09bd0761d..964f680bc 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.h
+++ b/src/video_core/renderer_opengl/gl_shader_cache.h
@@ -41,17 +41,27 @@ using Maxwell = Tegra::Engines::Maxwell3D::Regs;
using PrecompiledPrograms = std::unordered_map<ShaderDiskCacheUsage, CachedProgram>;
using PrecompiledShaders = std::unordered_map<u64, GLShader::ProgramResult>;
+struct ShaderParameters {
+ ShaderDiskCacheOpenGL& disk_cache;
+ const PrecompiledPrograms& precompiled_programs;
+ const Device& device;
+ VAddr cpu_addr;
+ u8* host_ptr;
+ u64 unique_identifier;
+};
+
class CachedShader final : public RasterizerCacheObject {
public:
- explicit CachedShader(const Device& device, VAddr cpu_addr, u64 unique_identifier,
- Maxwell::ShaderProgram program_type, ShaderDiskCacheOpenGL& disk_cache,
- const PrecompiledPrograms& precompiled_programs,
- ProgramCode&& program_code, ProgramCode&& program_code_b, u8* host_ptr);
+ explicit CachedShader(const ShaderParameters& params, Maxwell::ShaderProgram program_type,
+ GLShader::ProgramResult result);
- explicit CachedShader(VAddr cpu_addr, u64 unique_identifier,
- Maxwell::ShaderProgram program_type, ShaderDiskCacheOpenGL& disk_cache,
- const PrecompiledPrograms& precompiled_programs,
- GLShader::ProgramResult result, u8* host_ptr);
+ static Shader CreateStageFromMemory(const ShaderParameters& params,
+ Maxwell::ShaderProgram program_type,
+ ProgramCode&& program_code, ProgramCode&& program_code_b);
+
+ static Shader CreateStageFromCache(const ShaderParameters& params,
+ Maxwell::ShaderProgram program_type,
+ GLShader::ProgramResult result);
VAddr GetCpuAddr() const override {
return cpu_addr;
@@ -99,10 +109,9 @@ private:
ShaderDiskCacheOpenGL& disk_cache;
const PrecompiledPrograms& precompiled_programs;
- std::size_t shader_length{};
GLShader::ShaderEntries entries;
-
std::string code;
+ std::size_t shader_length{};
std::unordered_map<BaseBindings, CachedProgram> programs;
std::unordered_map<BaseBindings, GeometryPrograms> geometry_programs;