v0.9.0 (v1.0 beta) (#338)

* move aurora to the aurora-wiicompiled submodule (#315)

* Update settings_overlay.cpp (#257)

* Simplify controller settings menu (#317)

* simplify controller settings menu

* platform pairing hint

* build for x86-64-v2 on older linux cpus (#319)

* Real Wii Remote support over Bluetooth (#323)

* pass real wii remotes through emulated bluetooth

* build bluetooth tests on macos ci

* address review comments

* cancel replaced bluetooth reads

* Run GX on its own thread (#325)

* NTSC-U and NTSC-J support (#326)

* add ntsc-u and ntsc-j support

Co-authored-by: DarthMDev <mgracer48@yahoo.com>
Co-authored-by: rooklz <rookieb@muzo-works.com>

* hash inc files and require project for shards

* port remaining stale rr us hooks

* drop map report generation

* drop one-off region porters

---------

Co-authored-by: DarthMDev <mgracer48@yahoo.com>
Co-authored-by: rooklz <rookieb@muzo-works.com>

* Keyboard input for friend code entry (#327)

* keyboard input for friend code entry

* add new guest imports to synthetic test

* replace per-address region table with range map (#328)

* paste friend codes from clipboard (#329)

* Verify dependency archive hashes (#330)

* verify dependency archive hashes

* verify cached cppwinrt tool

* check dependency path types

* Check CPU support before building (#331)

* check cpu support before building

* build for x86-64-v2 on windows and intel macs

* bump aurora for fog fsel fix

* add vsync option (#333)

* Add exe icons (#334)

* add exe icons

* rebuild icon resource on ico change

* drop icon rebuild tracking

* Fix bugs found in forks (#336)

* port fixes found in forks

* fill whole disk id

* dump kamek overlay on crash (#335)

* bump aurora and version to 0.9.0

---------

Co-authored-by: DarthMDev <mgracer48@yahoo.com>
Co-authored-by: rooklz <rookieb@muzo-works.com>
This commit is contained in:
patchzyy
2026-10-23 18:00:55 +02:00
committed by GitHub
parent 4c166a7eb1
commit ac99413d45
467 changed files with 72272 additions and 65657 deletions
+6 -6
View File
@@ -157,14 +157,14 @@ public:
#define MKW_DETAIL_CAT(a, b) a##b
#define MKW_DETAIL_MAKE_UNIQUE(a, b) MKW_DETAIL_CAT(a, b)
// Three hand-written registration macros. Generated code never registers per function; every
// translated function reaches the registry via the bulk BulkTranslatedFunctionRecord tables
// in the translator's *_registration TUs.
// Three hand-written registration macros, given the PAL address. Generated code never registers
// per function; every translated function reaches the registry via the bulk
// BulkTranslatedFunctionRecord tables in the translator's *_registration TUs.
#define REGISTER_TRANSLATED_FUNCTION(address, fn) \
static AbiTrampoline<decltype(fn)> MKW_DETAIL_MAKE_UNIQUE(_abi_trampoline_, __COUNTER__)(address, #fn, fn, FunctionKind::BaseTranslated, false, kPpcAllNonvolatileFprMask, 0, 0, nullptr, KnownTranslatedCpuCall<address>::kMustRemainDynamicallyDispatchable)
static AbiTrampoline<decltype(fn)> MKW_DETAIL_MAKE_UNIQUE(_abi_trampoline_, __COUNTER__)(::GuestRegion::Translate(address), #fn, fn, FunctionKind::BaseTranslated, false, kPpcAllNonvolatileFprMask, 0, 0, nullptr, KnownTranslatedCpuCall<address>::kMustRemainDynamicallyDispatchable)
#define REGISTER_NATIVE_FUNCTION(address, fn) \
static AbiTrampoline<decltype(fn)> MKW_DETAIL_MAKE_UNIQUE(_abi_native_trampoline_, __COUNTER__)(address, #fn, fn, FunctionKind::Native, false, kPpcAllNonvolatileFprMask, 0, 0, &AbiRawCpuThunk<&fn>::Invoke)
static AbiTrampoline<decltype(fn)> MKW_DETAIL_MAKE_UNIQUE(_abi_native_trampoline_, __COUNTER__)(::GuestRegion::Translate(address), #fn, fn, FunctionKind::Native, false, kPpcAllNonvolatileFprMask, 0, 0, &AbiRawCpuThunk<&fn>::Invoke)
#define REGISTER_NATIVE_FUNCTION_AS(address, fn, pretty_name) \
static AbiTrampoline<decltype(fn)> MKW_DETAIL_MAKE_UNIQUE(_abi_native_trampoline_named_, __COUNTER__)(address, pretty_name, fn, FunctionKind::Native, false, kPpcAllNonvolatileFprMask, 0, 0, &AbiRawCpuThunk<&fn>::Invoke)
static AbiTrampoline<decltype(fn)> MKW_DETAIL_MAKE_UNIQUE(_abi_native_trampoline_named_, __COUNTER__)(::GuestRegion::Translate(address), pretty_name, fn, FunctionKind::Native, false, kPpcAllNonvolatileFprMask, 0, 0, &AbiRawCpuThunk<&fn>::Invoke)
+31
View File
@@ -1,7 +1,9 @@
#pragma once
#include "friend_code_keyboard.h"
#include "settings_overlay.h"
#include "runtime_config.h"
#include "gx_worker.h"
#include <aurora/aurora.h>
#include <aurora/event.h>
@@ -117,6 +119,7 @@ inline void ProcessAuroraEvents(const AuroraEvent* events) {
g_mkwDynamicAspectSurfacePending.store(true, std::memory_order_release);
}
settings_overlay::HandleEvents(events);
FriendCodeKeyboard::HandleEvents(events);
}
inline void ApplyPendingMkwDynamicAspectSurface() {
@@ -131,14 +134,42 @@ inline void ApplyPendingMkwDynamicAspectSurface() {
UpdateMkwDynamicAspectSurface(surfaceWidth, surfaceHeight);
}
void GxWorkerBeginFrame();
void GxWorkerEndFrame(AuroraImguiFrame* imgui);
void SetAuroraFrameGxBegun(bool begun);
inline bool BeginAuroraFrame() {
if (GxWorker::ShouldDefer()) {
// The window half can touch the surface and render targets the worker draws into.
GxWorker::Sync();
if (!aurora_begin_frame_window()) {
return false;
}
GxWorker::Submit([] { GxWorkerBeginFrame(); });
ApplyPendingMkwDynamicAspectSurface();
return true;
}
if (!aurora_begin_frame()) {
return false;
}
SetAuroraFrameGxBegun(true);
ApplyPendingMkwDynamicAspectSurface();
return true;
}
// Ends the frame; with the GX worker, the game thread joins it here so the next frame never
// overlaps guest memory the worker may still read.
inline void EndAuroraFrame() {
if (GxWorker::ShouldDefer()) {
AuroraImguiFrame* imgui = aurora_end_frame_window();
GxWorker::Submit([imgui] { GxWorkerEndFrame(imgui); });
GxWorker::Sync();
return;
}
aurora_end_frame();
SetAuroraFrameGxBegun(false);
}
// Poll Aurora events and update cached window/framebuffer dimensions.
inline void UpdateAuroraAndProcessEvents() {
ProcessAuroraEvents(aurora_update());
+19
View File
@@ -0,0 +1,19 @@
#pragma once
// HCI and L2CAP fields are little-endian.
#include <cstdint>
#include <vector>
namespace Bluetooth {
inline uint16_t Le16(const uint8_t* p) {
return static_cast<uint16_t>(p[0] | (p[1] << 8));
}
inline void PutLe16(std::vector<uint8_t>& out, uint16_t v) {
out.push_back(static_cast<uint8_t>(v));
out.push_back(static_cast<uint8_t>(v >> 8));
}
} // namespace Bluetooth
+153
View File
@@ -0,0 +1,153 @@
#pragma once
// Emulated Bluetooth controller behind the Wii's internal USB module
// (/dev/usb/oh1/57e/305). The game's own Broadcom stack (BTE), WUD, WPAD and KPAD
// talk HCI to it; each of the four virtual Wii Remotes it hosts is backed by a
// physical remote on the PC (see RemoteHost), whose HID reports are passed
// through unchanged. Pure host code: no guest memory, so it is unit-testable.
#include <array>
#include <cstddef>
#include <cstdint>
#include <deque>
#include <memory>
#include <vector>
namespace Bluetooth {
// Bluetooth device address in HCI (least significant byte first) order.
using BdAddr = std::array<uint8_t, 6>;
inline constexpr size_t kRemoteSlots = 4;
enum class LinkState : uint8_t {
Down, // no baseband connection to the game
Connecting, // baseband up, HID channels being opened
Ready, // HID control and interrupt channels open
};
// The physical side of the virtual remotes. Called from the thread that drives
// the Controller; implementations synchronize with their own I/O threads.
class RemoteHost {
public:
virtual ~RemoteHost() = default;
// A physical remote is attached to `slot` and can carry a connection.
virtual bool IsAttached(size_t slot) const = 0;
// True once each time the remote on `slot` asks to connect (it was just
// attached, or a button was pressed while it had no connection).
virtual bool ConsumeWake(size_t slot) = 0;
// Next input report (report id first, no HID header), false when none.
virtual bool PopInputReport(size_t slot, std::vector<uint8_t>& report) = 0;
// Output report from the game (report id first, no HID header).
virtual void SendOutputReport(size_t slot, const uint8_t* report, size_t size) = 0;
virtual void OnLinkStateChanged(size_t slot, LinkState state) = 0;
// The game ended a working connection; a real remote powers off here.
virtual void OnGuestDisconnect(size_t slot) = 0;
// The game is searching for remotes (its 1+2 / reconnect screens).
virtual void OnGuestInquiry(bool active) = 0;
// "Nintendo RVL-CNT-01" or "Nintendo RVL-CNT-01-TR".
virtual const char* DeviceName(size_t slot) const = 0;
};
class RemoteLink;
class Controller {
public:
explicit Controller(RemoteHost& host);
~Controller();
Controller(const Controller&) = delete;
Controller& operator=(const Controller&) = delete;
// The guest opened / closed the USB device.
void Open(uint64_t nowUs);
void Close();
bool IsOpen() const { return m_open; }
// Raw HCI command packet (opcode first, no H4 byte).
void ExecuteCommand(const uint8_t* packet, size_t size, uint64_t nowUs);
// Raw ACL packet from the guest (handle first).
void ReceiveAcl(const uint8_t* packet, size_t size, uint64_t nowUs);
// Advances timers, connection attempts, input forwarding and flow control.
void Tick(uint64_t nowUs);
bool HasEvent() const { return !m_events.empty(); }
bool PopEvent(std::vector<uint8_t>& event);
// ACL data is only handed out once every queued event has been delivered,
// so the guest always sees a connection before data on it.
bool PopAcl(std::vector<uint8_t>& packet);
size_t AclQueued() const { return m_acl.size(); }
LinkState SlotLinkState(size_t slot) const;
bool PageScanEnabled() const { return (m_scanEnable & 0x02) != 0; }
bool InquiryActive() const { return m_inquiry.active; }
static BdAddr LocalAddress();
static BdAddr RemoteAddress(size_t slot);
static uint16_t ConnectionHandle(size_t slot);
// Used by RemoteLink.
void QueueEvent(std::vector<uint8_t> event);
void QueueAcl(uint16_t handle, const std::vector<uint8_t>& l2capFrame);
RemoteHost& Host() { return m_host; }
uint32_t PageTimeoutUs() const { return static_cast<uint32_t>(m_pageTimeoutSlots) * 625u; }
private:
struct StoredKey {
BdAddr addr{};
std::array<uint8_t, 16> key{};
};
struct Inquiry {
bool active = false;
uint64_t endUs = 0;
uint8_t maxResponses = 0;
uint8_t reported = 0;
std::array<bool, kRemoteSlots> slotReported{};
};
struct Reassembly {
std::vector<uint8_t> frame;
size_t expected = 0;
};
// A reply the controller sends after a delay (page / name request timeouts).
struct PendingEvent {
uint64_t dueUs = 0;
std::vector<uint8_t> event;
};
void ResetState(bool keepStoredKeys);
void CommandComplete(uint16_t opcode, const std::vector<uint8_t>& params);
void CommandStatus(uint16_t opcode, uint8_t status);
void TickInquiry(uint64_t nowUs);
void FinishInquiry();
void SendNumberOfCompletedPackets();
int SlotForAddress(const BdAddr& addr) const;
int SlotForHandle(uint16_t handle) const;
const StoredKey* FindStoredKey(const BdAddr& addr) const;
void HandleLinkControl(uint16_t opcode, const uint8_t* p, size_t n, uint64_t nowUs);
void HandleLinkPolicy(uint16_t opcode, const uint8_t* p, size_t n);
void HandleControllerBaseband(uint16_t opcode, const uint8_t* p, size_t n, uint64_t nowUs);
void HandleInformational(uint16_t opcode, const uint8_t* p, size_t n);
void HandleStatusParameters(uint16_t opcode, const uint8_t* p, size_t n);
void HandleVendor(uint16_t opcode, const uint8_t* p, size_t n);
void UnknownCommand(uint16_t opcode);
RemoteHost& m_host;
bool m_open = false;
uint8_t m_scanEnable = 0;
uint16_t m_pageTimeoutSlots = 0x2000;
uint8_t m_inquiryMode = 0;
std::array<uint8_t, 3> m_classOfDevice{};
std::array<char, 248> m_localName{};
Inquiry m_inquiry;
bool m_hostInquiryNotified = false;
std::vector<StoredKey> m_storedKeys;
std::array<std::unique_ptr<RemoteLink>, kRemoteSlots> m_links;
std::array<uint16_t, kRemoteSlots> m_completedPackets{};
std::array<Reassembly, kRemoteSlots> m_reassembly;
std::array<bool, kRemoteSlots> m_authPending{};
std::vector<PendingEvent> m_pending;
std::deque<std::vector<uint8_t>> m_events;
std::deque<std::vector<uint8_t>> m_acl;
};
} // namespace Bluetooth
+107
View File
@@ -0,0 +1,107 @@
#pragma once
// One emulated Wii Remote as seen from the controller: its baseband connection,
// L2CAP channels (SDP, HID control, HID interrupt) and the HID transaction layer
// that carries the physical remote's reports.
#include "bluetooth/bt_controller.h"
#include <cstddef>
#include <cstdint>
#include <vector>
namespace Bluetooth {
class RemoteLink {
public:
enum class Baseband : uint8_t {
Idle,
Requesting, // Connection Request sent, waiting for the guest to accept
Paging, // the guest paged this remote (Create Connection)
Connected,
};
RemoteLink(Controller& controller, size_t slot);
// Drops every connection without telling the guest (controller reset/close).
void Reset();
void Tick(uint64_t nowUs);
// Remote side wants a connection and should be offered to the guest's inquiry.
bool Discoverable() const;
bool ConnectWanted() const { return m_connectWanted; }
// Guest accepted / rejected this remote's Connection Request.
bool IsRequesting() const { return m_baseband == Baseband::Requesting; }
void OnAccepted(uint8_t role);
void OnRejected(uint8_t reason, uint64_t nowUs);
// Guest paged this remote. Returns false when the remote cannot answer.
bool OnPaged(uint64_t nowUs);
// Baseband went down. `byGuest` = the guest's HCI Disconnect.
void OnDisconnected(bool byGuest, uint64_t nowUs);
// The physical remote vanished while connected: tells the guest.
void DropFromRemote(uint8_t reason);
void ReceiveL2cap(const uint8_t* frame, size_t size);
Baseband BasebandState() const { return m_baseband; }
LinkState State() const { return m_state; }
private:
struct Channel {
uint16_t psm = 0;
uint16_t localCid = 0;
uint16_t remoteCid = 0;
uint16_t remoteMtu = 672;
bool weInitiated = false;
bool connected = false; // connection response success seen/sent
bool configSent = false; // our Configuration Request is out
bool localConfigured = false; // the guest accepted our configuration
bool remoteConfigured = false; // we accepted the guest's configuration
uint8_t connectIdent = 0;
uint8_t configIdent = 0;
uint64_t requestUs = 0;
std::vector<uint8_t> pendingOptions; // continued Configuration Request
bool Open() const { return connected && localConfigured && remoteConfigured; }
};
Channel* FindByLocal(uint16_t cid);
Channel* FindByPsm(uint16_t psm);
uint16_t AllocateCid() const;
uint8_t NextIdent();
void SendSignal(uint8_t code, uint8_t ident, const std::vector<uint8_t>& data);
void SendData(const Channel& channel, const std::vector<uint8_t>& payload);
void RequestChannel(uint16_t psm, uint64_t nowUs);
void SendConfigRequest(Channel& channel);
void HandleSignalling(const uint8_t* data, size_t size);
void HandleConnectionRequest(uint8_t ident, const uint8_t* p, size_t n);
void HandleConnectionResponse(uint8_t ident, const uint8_t* p, size_t n);
void HandleConfigurationRequest(uint8_t ident, const uint8_t* p, size_t n);
void HandleConfigurationResponse(uint8_t ident, const uint8_t* p, size_t n);
void HandleDisconnectionRequest(uint8_t ident, const uint8_t* p, size_t n);
void HandleDisconnectionResponse(const uint8_t* p, size_t n);
void HandleChannelData(Channel& channel, const uint8_t* data, size_t size);
void HandleHidControl(Channel& channel, const uint8_t* data, size_t size);
void UpdateState();
void SetState(LinkState state);
void ForwardInput();
void SendInput(const Channel& interrupt, const std::vector<uint8_t>& report);
void ClearConnection();
Controller& m_controller;
size_t m_slot;
Baseband m_baseband = Baseband::Idle;
LinkState m_state = LinkState::Down;
bool m_connectWanted = false;
bool m_guestInitiated = false;
bool m_everReady = false;
uint8_t m_ident = 0;
uint64_t m_stateSinceUs = 0;
uint64_t m_retryAtUs = 0;
uint64_t m_nowUs = 0;
std::vector<Channel> m_channels;
std::vector<uint8_t> m_report;
};
} // namespace Bluetooth
+19
View File
@@ -0,0 +1,19 @@
#pragma once
// SDP server of an emulated Wii Remote: the HID service record (0x00010000)
// and the Device ID record (0x00010001) a real RVL-CNT-01 publishes.
#include <cstddef>
#include <cstdint>
#include <vector>
namespace Bluetooth::Sdp {
// The Wii Remote's HID report descriptor, as the game also embeds it.
const std::vector<uint8_t>& WiiRemoteHidDescriptor();
// Answers one SDP request PDU. `maxResponse` bounds the PDU returned (the
// guest's L2CAP MTU on the SDP channel).
std::vector<uint8_t> HandleRequest(const uint8_t* pdu, size_t size, size_t maxResponse);
} // namespace Bluetooth::Sdp
+8
View File
@@ -0,0 +1,8 @@
#pragma once
#include <aurora/event.h>
// Lets the keyboard type into Retro Rewind's friend code entry page.
namespace FriendCodeKeyboard {
void HandleEvents(const AuroraEvent* events) noexcept;
} // namespace FriendCodeKeyboard
+1 -1
View File
@@ -1,4 +1,4 @@
#pragma once
// Bump whenever guest_abi/, isa/ or ppc_isa_memory.h changes what translated code compiles against.
#define MKW_GUEST_ABI_VERSION 1
#define MKW_GUEST_ABI_VERSION 2
+6 -7
View File
@@ -2,6 +2,7 @@
#include "guest_abi/cpu_context.h"
#include "guest_abi/exec_guard.h"
#include "guest_abi/guest_region.h"
#include "isa/ppc_isa_config.h"
#include "isa/ppc_isa_cr.h"
@@ -24,7 +25,7 @@ uint32_t FilterScnRendererPathMaskOption(uint32_t pathMask) noexcept;
// (used for the path mask filtering in ScnRenderer::createPath: depth of
// field is always removed, bloom when the user disabled it)
inline void ApplyRuntimeCallOptions(uint32_t target, CpuContext* ctx) {
if (target == 0x8023BD38u) {
if (::GuestRegion::Is<0x8023BD38u>(target)) {
// ScnRenderer::createPath receives the post-processing path mask in r4.
ctx->gpr[4] = FilterScnRendererPathMaskOption(ctx->gpr[4]);
}
@@ -242,13 +243,11 @@ private:
std::array<uint32_t, 18> saved_;
};
// Guest address of OSLoadContext, which models rfi-style context restoration
// and intentionally replaces the full guest register file instead of returning
// like a normal ABI call. It can never be guarded.
inline constexpr uint32_t kOSLoadContextAddress = 0x801A1F58u;
// OSLoadContext models rfi-style context restoration and intentionally replaces
// the full guest register file instead of returning like a normal ABI call. It
// can never be guarded.
inline bool ShouldPreserveNonvolatileGprsForRawCpuCall(const TranslatedFunctionInfo* info) noexcept {
return info->kind == FunctionKind::Native && info->address != kOSLoadContextAddress;
return info->kind == FunctionKind::Native && info->address != MKW_GADDR(801A1F58);
}
inline uint32_t NonvolatileFprGuardMaskFor(const TranslatedFunctionInfo* info) noexcept {
+166
View File
@@ -0,0 +1,166 @@
#pragma once
// Guest addresses are named by their PAL (RMCP01) address. Which address that identity has
// depends on the region the game was translated from, which runtime/game supplies at link
// time, so one compiled runtime serves every region.
//
// MKW_GADDR(801A9E84) address of that identity in the running region
// MKW_GADDR_CT(801A9E84) same, as a constant expression; translated code only
//
// guest_versions.inc maps PAL ranges to the other regions. A PAL address outside every range
// of a region does not compile.
#include <cstddef>
#include <cstdint>
namespace GuestRegion {
enum class Id : uint8_t {
Pal = 0,
NtscU = 1,
NtscJ = 2,
};
inline constexpr size_t kCount = 3;
struct Row {
uint32_t address[kCount];
constexpr uint32_t In(Id region) const { return address[static_cast<size_t>(region)]; }
};
struct Range {
Id region;
uint32_t first;
uint32_t last;
int32_t delta;
};
inline constexpr Range kRanges[] = {
#define MKW_REGION_RANGE(region, first, last, delta) {Id::region, 0x##first##u, 0x##last##u, delta},
#include "guest_abi/guest_versions.inc"
#undef MKW_REGION_RANGE
};
inline constexpr size_t kRangeCount = sizeof(kRanges) / sizeof(kRanges[0]);
constexpr bool Precedes(Id region, uint32_t address, const Range& range) {
return region < range.region || (region == range.region && address < range.first);
}
constexpr bool RangesAreOrdered() {
for (size_t i = 0; i < kRangeCount; ++i) {
const Range& range = kRanges[i];
if (range.region == Id::Pal || range.first > range.last) {
return false;
}
if (i != 0 && !Precedes(kRanges[i - 1].region, kRanges[i - 1].last, range)) {
return false;
}
}
return true;
}
static_assert(RangesAreOrdered(), "guest_versions.inc ranges must be sorted by region and PAL address without overlaps");
struct Mapping {
bool found;
uint32_t address;
};
constexpr Mapping Map(uint32_t pal, Id region) {
if (region == Id::Pal) {
return {true, pal};
}
size_t lo = 0;
size_t hi = kRangeCount;
while (lo < hi) {
const size_t mid = lo + (hi - lo) / 2;
if (Precedes(region, pal, kRanges[mid])) {
hi = mid;
} else {
lo = mid + 1;
}
}
if (lo == 0) {
return {false, 0};
}
const Range& range = kRanges[lo - 1];
if (range.region != region || pal > range.last) {
return {false, 0};
}
return {true, static_cast<uint32_t>(static_cast<int64_t>(pal) + range.delta)};
}
constexpr Row RowOf(uint32_t pal) {
const Mapping ntscU = Map(pal, Id::NtscU);
const Mapping ntscJ = Map(pal, Id::NtscJ);
return ntscU.found && ntscJ.found ? Row{{pal, ntscU.address, ntscJ.address}}
: throw "PAL address outside guest_abi/guest_versions.inc";
}
} // namespace GuestRegion
// Defined by runtime/game for the translated region.
extern "C" const uint8_t kMkwGuestRegion;
namespace GuestRegion {
inline Id Current() noexcept { return static_cast<Id>(kMkwGuestRegion); }
template <uint32_t Pal>
inline uint32_t Address() noexcept {
constexpr Row row = RowOf(Pal);
if constexpr (row.address[1] == Pal && row.address[2] == Pal) {
return Pal;
} else {
return row.In(Current());
}
}
// Folds to false at compile time for a constant target that no region uses for Pal.
template <uint32_t Pal>
inline bool Is(uint32_t target) noexcept {
constexpr Row row = RowOf(Pal);
return (target == row.address[0] || target == row.address[1] || target == row.address[2]) &&
target == row.In(Current());
}
[[noreturn]] void FatalUnknownAddress(uint32_t pal) noexcept;
// For hand-written registrations, which name the PAL address.
inline uint32_t Translate(uint32_t pal) noexcept {
const Mapping mapping = Map(pal, Current());
if (!mapping.found) {
FatalUnknownAddress(pal);
}
return mapping.address;
}
struct Facts {
const char* gameId;
char letter;
uint32_t gameCode;
uint32_t viTvFormat;
uint32_t mem1ArenaLo;
// setting.txt defaults for a new NAND
const char* area;
const char* model;
const char* productCode;
const char* video;
const char* gameRegion;
};
inline constexpr Facts kFacts[kCount] = {
{"RMCP01", 'P', 0x524D4350u, 1u, 0x80399180u, "EUR", "RVL-001(EUR)", "LEH", "PAL", "EU"},
{"RMCE01", 'E', 0x524D4345u, 0u, 0x80394E00u, "USA", "RVL-001(USA)", "LU", "NTSC", "US"},
{"RMCJ01", 'J', 0x524D434Au, 0u, 0x80398B00u, "JPN", "RVL-001(JPN)", "LJ", "NTSC", "JP"},
};
inline const Facts& CurrentFacts() noexcept { return kFacts[static_cast<size_t>(Current())]; }
} // namespace GuestRegion
#define MKW_GADDR(pal) (::GuestRegion::Address<0x##pal##u>())
#if defined(MKW_TRANSLATED_GUEST_REGION)
#define MKW_GADDR_CT(pal) \
(::GuestRegion::RowOf(0x##pal##u).In(static_cast<::GuestRegion::Id>(MKW_TRANSLATED_GUEST_REGION)))
#endif
+416
View File
@@ -0,0 +1,416 @@
// PAL address ranges -> address in each other region, in the style of Retro Rewind's
// versions.txt: MKW_REGION_RANGE(region, first PAL, last PAL (inclusive), delta).
// PAL maps to itself. Code ranges come from mkw-sp's port.py chunk table (MIT, Pablo Stebler),
// data ranges from Retro Rewind's versions.txt plus the verified data_addresses.txt anchors.
// An address outside every range has no known counterpart and fails to compile.
MKW_REGION_RANGE(NtscU, 80000000, 8000802F, +0x0)
MKW_REGION_RANGE(NtscU, 8000808C, 8000ADBB, -0x40)
MKW_REGION_RANGE(NtscU, 8000AF24, 8000B6B3, -0xA0)
MKW_REGION_RANGE(NtscU, 8000B6B4, 8000C173, +0x15994)
MKW_REGION_RANGE(NtscU, 8000C174, 80021BA7, -0xB60)
MKW_REGION_RANGE(NtscU, 80021BA8, 80225F1B, -0xA0)
MKW_REGION_RANGE(NtscU, 802261F8, 802402DF, -0x384)
MKW_REGION_RANGE(NtscU, 802402E0, 80240E17, +0x3F1C)
MKW_REGION_RANGE(NtscU, 80240E18, 80244DD3, -0xEBC)
MKW_REGION_RANGE(NtscU, 80244DDA, 80244EBF, -0xA0)
MKW_REGION_RANGE(NtscU, 80244EC0, 80244ECF, -0x80)
MKW_REGION_RANGE(NtscU, 802572BF, 802575DF, -0x320)
MKW_REGION_RANGE(NtscU, 80257E64, 80258184, -0x320)
MKW_REGION_RANGE(NtscU, 80276054, 8029CEFB, -0x4340)
MKW_REGION_RANGE(NtscU, 802A3D60, 802A407F, -0x4368)
MKW_REGION_RANGE(NtscU, 802A4080, 80386F27, -0x4380)
MKW_REGION_RANGE(NtscU, 80386F48, 80386F8F, -0x4388)
MKW_REGION_RANGE(NtscU, 80386FA0, 80388BE3, -0x4380)
MKW_REGION_RANGE(NtscU, 80388DD0, 80388DF7, -0x4388)
MKW_REGION_RANGE(NtscU, 80389140, 8038917B, -0x4380)
MKW_REGION_RANGE(NtscU, 80399200, 80399400, -0x4380)
MKW_REGION_RANGE(NtscU, 805103B4, 80510A8F, -0x4380)
MKW_REGION_RANGE(NtscU, 80510B84, 8052D297, -0x4474)
MKW_REGION_RANGE(NtscU, 8052D298, 8052D96B, +0x22568)
MKW_REGION_RANGE(NtscU, 8052D96C, 8053D97B, -0x4B48)
MKW_REGION_RANGE(NtscU, 8053D97C, 8053E36F, +0x12558)
MKW_REGION_RANGE(NtscU, 8053E370, 8054FB2B, -0x553C)
MKW_REGION_RANGE(NtscU, 8054FB2C, 80550547, +0xD9C)
MKW_REGION_RANGE(NtscU, 80550548, 805537CB, -0x5F58)
MKW_REGION_RANGE(NtscU, 80553894, 8055572B, -0x6020)
MKW_REGION_RANGE(NtscU, 8055572C, 8056AB6B, -0x4380)
MKW_REGION_RANGE(NtscU, 8056AB6C, 8056B63B, +0x4357C)
MKW_REGION_RANGE(NtscU, 8056B63C, 8057402F, -0x4E50)
MKW_REGION_RANGE(NtscU, 80574030, 805758AB, +0x3AB88)
MKW_REGION_RANGE(NtscU, 80575A44, 80583F2B, -0x6864)
MKW_REGION_RANGE(NtscU, 80583F2C, 8059B5A3, -0x6824)
MKW_REGION_RANGE(NtscU, 8059B5A4, 8059EAA3, +0x14FE8)
MKW_REGION_RANGE(NtscU, 8059EAA4, 805A0557, -0x9D24)
MKW_REGION_RANGE(NtscU, 805A068C, 805A1477, +0x1343C)
MKW_REGION_RANGE(NtscU, 805A1488, 805A1863, +0x1342C)
MKW_REGION_RANGE(NtscU, 805A1864, 805A9EDB, -0xB038)
MKW_REGION_RANGE(NtscU, 805A9EDC, 805AB573, -0xB030)
MKW_REGION_RANGE(NtscU, 805AB574, 805ABC8F, -0xAF70)
MKW_REGION_RANGE(NtscU, 805ABC90, 805ADD97, -0xAF68)
MKW_REGION_RANGE(NtscU, 805ADD98, 805B900F, -0xAF28)
MKW_REGION_RANGE(NtscU, 805B9010, 805B92FF, -0x4380)
MKW_REGION_RANGE(NtscU, 805B9300, 805BAB3F, +0x664F4)
MKW_REGION_RANGE(NtscU, 805BAB40, 805BB2B7, -0x5BC0)
MKW_REGION_RANGE(NtscU, 805BB2B8, 805BD2D7, +0x65D7C)
MKW_REGION_RANGE(NtscU, 805BD2D8, 805BD2E3, -0x697C)
MKW_REGION_RANGE(NtscU, 805BD2E4, 805BD39B, +0x65D70)
MKW_REGION_RANGE(NtscU, 805BD39C, 805BE5FF, -0x7CA4)
MKW_REGION_RANGE(NtscU, 805BE600, 805BE61B, -0x7C98)
MKW_REGION_RANGE(NtscU, 805BE66C, 805BE7F3, +0x64AA0)
MKW_REGION_RANGE(NtscU, 805BE7F4, 805BE84B, -0x7124)
MKW_REGION_RANGE(NtscU, 805BE84C, 805BED67, +0x64A48)
MKW_REGION_RANGE(NtscU, 805BED68, 805BED73, -0x7948)
MKW_REGION_RANGE(NtscU, 805BED74, 805BF2D7, +0x64A3C)
MKW_REGION_RANGE(NtscU, 805BF2D8, 805BF2DB, -0x613C)
MKW_REGION_RANGE(NtscU, 805BF3CC, 805BFE1B, -0x89FC)
MKW_REGION_RANGE(NtscU, 805BFE1C, 805C00BF, -0x89F0)
MKW_REGION_RANGE(NtscU, 805C00C0, 805C1B33, -0x8998)
MKW_REGION_RANGE(NtscU, 805C1B34, 805C35F7, -0x8994)
MKW_REGION_RANGE(NtscU, 805C35F8, 805C3BCB, +0x60810)
MKW_REGION_RANGE(NtscU, 805C3BCC, 805C4767, -0x8F68)
MKW_REGION_RANGE(NtscU, 805C4768, 805C579B, +0x5FC74)
MKW_REGION_RANGE(NtscU, 805C57B0, 805CADDB, -0x9FB0)
MKW_REGION_RANGE(NtscU, 805CADDC, 805CC0A7, +0x5A648)
MKW_REGION_RANGE(NtscU, 805CC1B4, 805CD94B, +0x5A5A4)
MKW_REGION_RANGE(NtscU, 805CD94C, 805D1887, -0xCB20)
MKW_REGION_RANGE(NtscU, 805D1888, 805D1E83, +0x56668)
MKW_REGION_RANGE(NtscU, 805D1E84, 805D7F77, -0xD11C)
MKW_REGION_RANGE(NtscU, 805D7F78, 805DBFDB, +0x50574)
MKW_REGION_RANGE(NtscU, 805DBFDC, 805DC033, +0x2A620)
MKW_REGION_RANGE(NtscU, 805DC034, 805DE837, +0x5051C)
MKW_REGION_RANGE(NtscU, 805DE838, 805DE843, +0x5C00)
MKW_REGION_RANGE(NtscU, 805DE844, 805E0C37, +0x50510)
MKW_REGION_RANGE(NtscU, 805E0C38, 805E6B4F, -0x15DDC)
MKW_REGION_RANGE(NtscU, 805E6B50, 805E745F, +0x4A5F8)
MKW_REGION_RANGE(NtscU, 805E7460, 805EA77F, -0x166EC)
MKW_REGION_RANGE(NtscU, 805EA780, 805EEB67, +0x472D8)
MKW_REGION_RANGE(NtscU, 805EEB68, 805F2D23, -0x1AAD4)
MKW_REGION_RANGE(NtscU, 805F2D24, 805F8B33, +0x4311C)
MKW_REGION_RANGE(NtscU, 805F8B34, 805FB5BB, -0x208E4)
MKW_REGION_RANGE(NtscU, 805FB5BC, 805FB81F, +0x40694)
MKW_REGION_RANGE(NtscU, 805FB820, 805FD517, -0x20B48)
MKW_REGION_RANGE(NtscU, 805FD518, 806012AF, +0x3E99C)
MKW_REGION_RANGE(NtscU, 806012B0, 80608D17, -0x248E0)
MKW_REGION_RANGE(NtscU, 80608D18, 8060A72B, -0x248D4)
MKW_REGION_RANGE(NtscU, 8060A72C, 8061186B, +0x35520)
MKW_REGION_RANGE(NtscU, 8061186C, 80616843, -0x2BA14)
MKW_REGION_RANGE(NtscU, 80616844, 8061AE6B, +0x30548)
MKW_REGION_RANGE(NtscU, 8061AE6C, 8061E897, -0x3003C)
MKW_REGION_RANGE(NtscU, 8061E898, 8061F6E7, +0x2CB1C)
MKW_REGION_RANGE(NtscU, 8061F6E8, 80620CB3, -0x30E8C)
MKW_REGION_RANGE(NtscU, 80620D7C, 80634F47, -0x30EB4)
MKW_REGION_RANGE(NtscU, 80634F48, 80637493, -0x30E98)
MKW_REGION_RANGE(NtscU, 80637494, 80637513, -0x30DF0)
MKW_REGION_RANGE(NtscU, 80637514, 80637A2F, +0x14CF0)
MKW_REGION_RANGE(NtscU, 80637AC0, 8063BCF7, -0x313A0)
MKW_REGION_RANGE(NtscU, 8063BE40, 806453E3, -0x31420)
MKW_REGION_RANGE(NtscU, 806453E4, 806467D3, +0x73A8)
MKW_REGION_RANGE(NtscU, 806467D4, 8064A3F3, -0x32810)
MKW_REGION_RANGE(NtscU, 8064A3F4, 8064AEE3, +0x3788)
MKW_REGION_RANGE(NtscU, 8064AEF8, 80651BEB, -0x33314)
MKW_REGION_RANGE(NtscU, 80651BEC, 806522FF, -0x3580)
MKW_REGION_RANGE(NtscU, 80652300, 80653207, -0x33A14)
MKW_REGION_RANGE(NtscU, 80653208, 8065B353, -0x4488)
MKW_REGION_RANGE(NtscU, 8065B4E8, 8065C0EB, -0x461C)
MKW_REGION_RANGE(NtscU, 8065C0EC, 8065FA13, +0xC88)
MKW_REGION_RANGE(NtscU, 8065FA4C, 8065FE4B, -0x7F7C)
MKW_REGION_RANGE(NtscU, 8065FE4C, 80662777, -0x7F3C)
MKW_REGION_RANGE(NtscU, 80662778, 80663193, -0x20E4)
MKW_REGION_RANGE(NtscU, 80663194, 80665537, -0x8958)
MKW_REGION_RANGE(NtscU, 80665538, 80668813, +0xDC50)
MKW_REGION_RANGE(NtscU, 80668814, 8067906B, -0x7764)
MKW_REGION_RANGE(NtscU, 8067906C, 8067937F, -0x2C08)
MKW_REGION_RANGE(NtscU, 80679380, 8067ABFF, -0x7A78)
MKW_REGION_RANGE(NtscU, 8067AC00, 806BF4C7, -0x4488)
MKW_REGION_RANGE(NtscU, 806BF4C8, 806BFB13, +0x22BE0)
MKW_REGION_RANGE(NtscU, 806BFB14, 806C050B, -0x4AD4)
MKW_REGION_RANGE(NtscU, 806C050C, 806C35A7, +0x221E8)
MKW_REGION_RANGE(NtscU, 806C35A8, 806C3AA3, -0x7B70)
MKW_REGION_RANGE(NtscU, 806C3AA4, 806C4ED3, +0x21CEC)
MKW_REGION_RANGE(NtscU, 806C4EF0, 806C63A7, +0x21CD0)
MKW_REGION_RANGE(NtscU, 806C63B0, 806C6B43, +0x21CC8)
MKW_REGION_RANGE(NtscU, 806C6B44, 806CCE8F, -0xABF4)
MKW_REGION_RANGE(NtscU, 806CCE90, 806CE81F, +0x1B97C)
MKW_REGION_RANGE(NtscU, 806CE828, 806D02BB, -0xC58C)
MKW_REGION_RANGE(NtscU, 806D02BC, 806D28F3, +0x19EE0)
MKW_REGION_RANGE(NtscU, 806D2908, 806D5C5B, -0xEBD0)
MKW_REGION_RANGE(NtscU, 806D5C60, 806D5ED7, +0x16B74)
MKW_REGION_RANGE(NtscU, 806D5ED8, 806DA913, -0xEE40)
MKW_REGION_RANGE(NtscU, 806DA914, 806DB183, +0x12144)
MKW_REGION_RANGE(NtscU, 806DB184, 806DDA83, -0xF6B0)
MKW_REGION_RANGE(NtscU, 806DDA84, 806DEB3F, +0xF844)
MKW_REGION_RANGE(NtscU, 806DEB40, 806DF7CF, -0x1076C)
MKW_REGION_RANGE(NtscU, 806DF7D0, 806DFD13, +0xEBB4)
MKW_REGION_RANGE(NtscU, 806DFD14, 806E3A8B, -0x10CB0)
MKW_REGION_RANGE(NtscU, 806E3A8C, 806E3E1F, +0xAE3C)
MKW_REGION_RANGE(NtscU, 806E3E20, 806E95AF, -0x11044)
MKW_REGION_RANGE(NtscU, 806E95B0, 806EC7BF, +0x56AC)
MKW_REGION_RANGE(NtscU, 806EC7C0, 806ED53B, -0x1425C)
MKW_REGION_RANGE(NtscU, 806ED53C, 806F62FB, -0x14254)
MKW_REGION_RANGE(NtscU, 806F62FC, 806F7697, -0x4488)
MKW_REGION_RANGE(NtscU, 806F77C4, 806F7A53, +0x1C2B4)
MKW_REGION_RANGE(NtscU, 806F7AA8, 806F821F, +0x1C260)
MKW_REGION_RANGE(NtscU, 806F8220, 806F8933, -0x5010)
MKW_REGION_RANGE(NtscU, 806F8934, 806FA3FB, -0x4FBC)
MKW_REGION_RANGE(NtscU, 806FA3FC, 806FE23F, -0x4F68)
MKW_REGION_RANGE(NtscU, 806FE240, 806FE9DF, -0x4F10)
MKW_REGION_RANGE(NtscU, 806FE9E0, 807001A3, +0x15AA0)
MKW_REGION_RANGE(NtscU, 80700230, 80700473, +0x15A14)
MKW_REGION_RANGE(NtscU, 80700474, 8070E7B7, -0x69A4)
MKW_REGION_RANGE(NtscU, 8070E7B8, 8070F8B7, +0x76D0)
MKW_REGION_RANGE(NtscU, 8070F8B8, 807179C3, -0x7AA4)
MKW_REGION_RANGE(NtscU, 807179C4, 80717E33, -0x7A18)
MKW_REGION_RANGE(NtscU, 80717E34, 807182E7, -0xEAC)
MKW_REGION_RANGE(NtscU, 807182E8, 8071B86B, -0x7ECC)
MKW_REGION_RANGE(NtscU, 8071B86C, 80726573, -0x4430)
MKW_REGION_RANGE(NtscU, 80726574, 8072820B, +0x16068)
MKW_REGION_RANGE(NtscU, 8072821C, 807285C7, +0x16058)
MKW_REGION_RANGE(NtscU, 807285CC, 80729337, +0x16054)
MKW_REGION_RANGE(NtscU, 80729350, 80729B87, -0x720C)
MKW_REGION_RANGE(NtscU, 80729B88, 8072A893, -0x7204)
MKW_REGION_RANGE(NtscU, 8072A894, 8072B95B, -0x71FC)
MKW_REGION_RANGE(NtscU, 8072B95C, 8072DE63, -0x71EC)
MKW_REGION_RANGE(NtscU, 8072DFCC, 8072FF5F, +0x113C4)
MKW_REGION_RANGE(NtscU, 8072FF78, 80730197, +0x113AC)
MKW_REGION_RANGE(NtscU, 80730198, 80730A7F, -0x93B0)
MKW_REGION_RANGE(NtscU, 80730B40, 8073195F, +0x10A04)
MKW_REGION_RANGE(NtscU, 80731960, 80735947, -0xA1B8)
MKW_REGION_RANGE(NtscU, 80735948, 80738DB7, +0xCA1C)
MKW_REGION_RANGE(NtscU, 80738DB8, 8073C54B, -0xD628)
MKW_REGION_RANGE(NtscU, 8073C54C, 8073EDEF, +0x9288)
MKW_REGION_RANGE(NtscU, 8073EDF0, 8074C4A7, -0xFECC)
MKW_REGION_RANGE(NtscU, 8074C4A8, 8074D5B7, +0x20A04)
MKW_REGION_RANGE(NtscU, 8074D5B8, 807519C7, -0x5540)
MKW_REGION_RANGE(NtscU, 807519C8, 80754103, +0x1C5F4)
MKW_REGION_RANGE(NtscU, 80754104, 80758BDB, -0x7C7C)
MKW_REGION_RANGE(NtscU, 80758BDC, 8075DB23, +0x17B1C)
MKW_REGION_RANGE(NtscU, 8075DB3C, 8075E78B, -0xCBDC)
MKW_REGION_RANGE(NtscU, 8075E78C, 8075EAFB, +0x16ECC)
MKW_REGION_RANGE(NtscU, 8075EAFC, 80765C93, -0xCF4C)
MKW_REGION_RANGE(NtscU, 80765C94, 807678F3, +0xFD34)
MKW_REGION_RANGE(NtscU, 807678F4, 80768D1F, -0xEBAC)
MKW_REGION_RANGE(NtscU, 80768D20, 807693EB, +0xE908)
MKW_REGION_RANGE(NtscU, 8076960C, 8076C85B, +0xE85C)
MKW_REGION_RANGE(NtscU, 8076C85C, 8076EBDB, -0x126E8)
MKW_REGION_RANGE(NtscU, 8076EBE0, 8076F2DB, +0xC4D8)
MKW_REGION_RANGE(NtscU, 8076F2DC, 807726C3, -0x12DE8)
MKW_REGION_RANGE(NtscU, 80772704, 80773C13, +0x90B4)
MKW_REGION_RANGE(NtscU, 80773C1C, 8077439B, -0x14340)
MKW_REGION_RANGE(NtscU, 807743A0, 807787EF, -0x14294)
MKW_REGION_RANGE(NtscU, 807787F0, 8077902B, +0x4520)
MKW_REGION_RANGE(NtscU, 8077902C, 8077CE87, -0x14AD0)
MKW_REGION_RANGE(NtscU, 8077CEC8, 8077DF23, +0x684)
MKW_REGION_RANGE(NtscU, 8077DF24, 807829D7, -0x15B2C)
MKW_REGION_RANGE(NtscU, 807829D8, 80787D83, -0x4430)
MKW_REGION_RANGE(NtscU, 80787D84, 8078C95F, +0x48598)
MKW_REGION_RANGE(NtscU, 8078C960, 807A81B3, -0x900C)
MKW_REGION_RANGE(NtscU, 807A81B4, 807A9D6F, +0x2CDC0)
MKW_REGION_RANGE(NtscU, 807A9E50, 807A9E53, +0x2CCAC)
MKW_REGION_RANGE(NtscU, 807A9EB8, 807AF13F, -0xACA8)
MKW_REGION_RANGE(NtscU, 807AF140, 807B2EF7, +0x27A54)
MKW_REGION_RANGE(NtscU, 807B2EF8, 807D976B, -0xEA60)
MKW_REGION_RANGE(NtscU, 807D976C, 807D9B7F, +0x11E0)
MKW_REGION_RANGE(NtscU, 807D9B98, 807DA5BF, -0xEE8C)
MKW_REGION_RANGE(NtscU, 807DA5C0, 807DBCCB, +0x7B8)
MKW_REGION_RANGE(NtscU, 807DBCCC, 807DC8C7, -0x10598)
MKW_REGION_RANGE(NtscU, 807DC950, 807E093B, -0x10620)
MKW_REGION_RANGE(NtscU, 807E093C, 807E251F, +0x5BDB4)
MKW_REGION_RANGE(NtscU, 807E259C, 807E560F, -0x6090)
MKW_REGION_RANGE(NtscU, 807E5654, 807E6413, -0x6070)
MKW_REGION_RANGE(NtscU, 807E6414, 807E9C43, +0x57F28)
MKW_REGION_RANGE(NtscU, 807E9C50, 807EDD97, -0x98AC)
MKW_REGION_RANGE(NtscU, 807EDD98, 807EE23B, +0x53DD4)
MKW_REGION_RANGE(NtscU, 807EE250, 807EE467, -0x9D64)
MKW_REGION_RANGE(NtscU, 807EE474, 807EEA13, +0x53B9C)
MKW_REGION_RANGE(NtscU, 807EEA14, 807EF9F3, -0xA310)
MKW_REGION_RANGE(NtscU, 807EFD0C, 807F76EB, -0xA628)
MKW_REGION_RANGE(NtscU, 807F76EC, 807F7BA3, +0x4B1FC)
MKW_REGION_RANGE(NtscU, 807F7BC4, 807F890B, -0xAB00)
MKW_REGION_RANGE(NtscU, 807F8968, 807F927F, +0x4A438)
MKW_REGION_RANGE(NtscU, 807F9280, 807F9387, -0xB3F0)
MKW_REGION_RANGE(NtscU, 807F9580, 807FAB57, +0x4A320)
MKW_REGION_RANGE(NtscU, 807FAB58, 807FEB67, -0xCBC0)
MKW_REGION_RANGE(NtscU, 807FEB68, 807FFADF, +0x46310)
MKW_REGION_RANGE(NtscU, 807FFB20, 80805A0B, -0xDB78)
MKW_REGION_RANGE(NtscU, 80805A0C, 8080761B, +0x403E4)
MKW_REGION_RANGE(NtscU, 8080761C, 80809447, -0xF748)
MKW_REGION_RANGE(NtscU, 80809448, 8080AD1F, +0x3E5B8)
MKW_REGION_RANGE(NtscU, 8080AD20, 80811E47, -0x11020)
MKW_REGION_RANGE(NtscU, 80811E48, 80813BD3, +0x37490)
MKW_REGION_RANGE(NtscU, 80813BD4, 8081E283, -0x12DAC)
MKW_REGION_RANGE(NtscU, 8081E284, 8081EFEB, +0x2CDE0)
MKW_REGION_RANGE(NtscU, 8081EFEC, 8082E53F, -0x13B14)
MKW_REGION_RANGE(NtscU, 8082E540, 8082E853, +0x1D88C)
MKW_REGION_RANGE(NtscU, 8082E854, 8082F407, -0x13E28)
MKW_REGION_RANGE(NtscU, 8082F408, 8083349F, +0x1CCD8)
MKW_REGION_RANGE(NtscU, 808334E0, 80833AFF, -0x17F00)
MKW_REGION_RANGE(NtscU, 80833B00, 80838E5F, -0x17EF8)
MKW_REGION_RANGE(NtscU, 80838E60, 8083B0CB, +0x17358)
MKW_REGION_RANGE(NtscU, 8083B0CC, 8083CB43, -0x1A178)
MKW_REGION_RANGE(NtscU, 8083CB44, 8083D42B, +0x158E0)
MKW_REGION_RANGE(NtscU, 8083D42C, 8084233F, -0x1AA60)
MKW_REGION_RANGE(NtscU, 80842340, 808447AB, +0x109CC)
MKW_REGION_RANGE(NtscU, 808447AC, 8084A99F, -0x1CED8)
MKW_REGION_RANGE(NtscU, 8084A9A0, 8084D0DB, +0xA7E4)
MKW_REGION_RANGE(NtscU, 8084D0DC, 80851D2B, -0x1F614)
MKW_REGION_RANGE(NtscU, 80851D38, 80852C5F, +0x5B88)
MKW_REGION_RANGE(NtscU, 80852C60, 80853CA3, -0x20548)
MKW_REGION_RANGE(NtscU, 80853CA4, 808551EB, +0x4B50)
MKW_REGION_RANGE(NtscU, 808551EC, 8085C3CB, -0x21A90)
MKW_REGION_RANGE(NtscU, 8085C3CC, 8085E673, -0x2690)
MKW_REGION_RANGE(NtscU, 8085E674, 8085F0AB, -0x23D24)
MKW_REGION_RANGE(NtscU, 8085F0AC, 8085FFD3, -0x30C8)
MKW_REGION_RANGE(NtscU, 8085FFD4, 80860F2B, -0x24C4C)
MKW_REGION_RANGE(NtscU, 80860F2C, 80862E23, -0x4020)
MKW_REGION_RANGE(NtscU, 80863234, 8086708B, -0x4430)
MKW_REGION_RANGE(NtscU, 8086708C, 808676DF, -0x2354)
MKW_REGION_RANGE(NtscU, 808676E0, 808697BB, -0x4A84)
MKW_REGION_RANGE(NtscU, 808697BC, 8086A253, -0x4430)
MKW_REGION_RANGE(NtscU, 8086A254, 8086C097, +0xC4A0)
MKW_REGION_RANGE(NtscU, 8086C108, 8086C987, +0xC430)
MKW_REGION_RANGE(NtscU, 8086CA40, 80872CA3, +0xC378)
MKW_REGION_RANGE(NtscU, 80872CA4, 808739AF, -0xCE80)
MKW_REGION_RANGE(NtscU, 808739B0, 80875453, -0xCDC8)
MKW_REGION_RANGE(NtscU, 80875454, 8088344B, -0xCD58)
MKW_REGION_RANGE(NtscU, 8088344C, 8088F70F, -0x4430)
MKW_REGION_RANGE(NtscU, 8088FDB8, 8088FDBB, -0x4458)
MKW_REGION_RANGE(NtscU, 80890030, 80890215, -0x4470)
MKW_REGION_RANGE(NtscU, 80890490, 80890977, -0x4578)
MKW_REGION_RANGE(NtscU, 808913C0, 808913C3, -0x4448)
MKW_REGION_RANGE(NtscU, 80891750, 8089175F, -0x44D0)
MKW_REGION_RANGE(NtscU, 80891A28, 80891C28, -0x4518)
MKW_REGION_RANGE(NtscU, 80892F2C, 80892F96, -0x1B0)
MKW_REGION_RANGE(NtscU, 80893217, 8089321D, -0x4A30)
MKW_REGION_RANGE(NtscU, 808944DE, 80894769, -0x50E8)
MKW_REGION_RANGE(NtscU, 80894B98, 80894B9C, -0x1330)
MKW_REGION_RANGE(NtscU, 80895238, 808952B7, -0x58F8)
MKW_REGION_RANGE(NtscU, 80895A08, 80895C8C, -0x1930)
MKW_REGION_RANGE(NtscU, 80895CBC, 80895CF0, -0x5FA8)
MKW_REGION_RANGE(NtscU, 8089647A, 808966D4, -0x1CA0)
MKW_REGION_RANGE(NtscU, 80896820, 80897040, -0x6678)
MKW_REGION_RANGE(NtscU, 80897C63, 80897C67, -0x6DA0)
MKW_REGION_RANGE(NtscU, 80897DD3, 80897DEF, -0x6DA8)
MKW_REGION_RANGE(NtscU, 80898955, 80898959, -0x7388)
MKW_REGION_RANGE(NtscU, 80899692, 8089973A, -0x7730)
MKW_REGION_RANGE(NtscU, 808998B3, 808998CA, -0x3FB0)
MKW_REGION_RANGE(NtscU, 80899A36, 80899EEF, -0x7890)
MKW_REGION_RANGE(NtscU, 8089A194, 8089A1B7, -0x46D0)
MKW_REGION_RANGE(NtscU, 808A1D80, 808A1E6F, -0x4A08)
MKW_REGION_RANGE(NtscU, 808A22E0, 808A22EF, -0x46D0)
MKW_REGION_RANGE(NtscU, 808A4E00, 808A56FF, -0x49C0)
MKW_REGION_RANGE(NtscU, 808A587C, 808A5897, -0x2B20)
MKW_REGION_RANGE(NtscU, 808A5A30, 808A5DC3, -0x4AD8)
MKW_REGION_RANGE(NtscU, 808A7DE0, 808A85F3, -0x4CD8)
MKW_REGION_RANGE(NtscU, 808A98D0, 808A98DF, -0x5728)
MKW_REGION_RANGE(NtscU, 808A9AF8, 808A9AFF, +0x780)
MKW_REGION_RANGE(NtscU, 808A9C0A, 808A9CC8, -0x58C8)
MKW_REGION_RANGE(NtscU, 808A9FEC, 808AA1AF, -0x5928)
MKW_REGION_RANGE(NtscU, 808AAA04, 808AAA10, -0x5980)
MKW_REGION_RANGE(NtscU, 808ACB3F, 808ACB43, -0x5F10)
MKW_REGION_RANGE(NtscU, 808ACFDF, 808AD02B, -0x60C0)
MKW_REGION_RANGE(NtscU, 808AD3C4, 808AD3C7, -0x60C0)
MKW_REGION_RANGE(NtscU, 808AD48E, 808AD492, -0x2980)
MKW_REGION_RANGE(NtscU, 808AD9F7, 808AD9FC, -0x6320)
MKW_REGION_RANGE(NtscU, 808ADCBC, 808ADD2C, -0x2FA8)
MKW_REGION_RANGE(NtscU, 808AEB45, 808AEB55, -0x68A8)
MKW_REGION_RANGE(NtscU, 808AF8E0, 808AF95F, -0x6AC0)
MKW_REGION_RANGE(NtscU, 808AFC18, 808AFC1B, -0x46B8)
MKW_REGION_RANGE(NtscU, 808B30C8, 808B3108, -0x46B0)
MKW_REGION_RANGE(NtscU, 808B35A0, 808B35AF, -0x46B0)
MKW_REGION_RANGE(NtscU, 808B3984, 808B3987, -0x4850)
MKW_REGION_RANGE(NtscU, 808B3A90, 808B3A9F, -0x4850)
MKW_REGION_RANGE(NtscU, 808B4C20, 808B4DEF, -0x46B0)
MKW_REGION_RANGE(NtscU, 808B54B8, 808B550F, -0x4738)
MKW_REGION_RANGE(NtscU, 808B59F4, 808B5CCF, -0x48C0)
MKW_REGION_RANGE(NtscU, 808B5CD8, 808B5CDB, -0x48BC)
MKW_REGION_RANGE(NtscU, 808BB034, 808BB097, -0x60F8)
MKW_REGION_RANGE(NtscU, 808BBEC8, 808BE908, -0x6430)
MKW_REGION_RANGE(NtscU, 808BFAF0, 808BFAFF, -0x65B0)
MKW_REGION_RANGE(NtscU, 808C069C, 808C06FF, -0x66B0)
MKW_REGION_RANGE(NtscU, 808CB550, 808CB553, -0x5508)
MKW_REGION_RANGE(NtscU, 808CB700, 808CB70F, -0x5508)
MKW_REGION_RANGE(NtscU, 808D1DD0, 808D1E2B, -0x3A98)
MKW_REGION_RANGE(NtscU, 808D30D8, 808D324F, -0x4908)
MKW_REGION_RANGE(NtscU, 808D3698, 808D369B, +0x1AB0)
MKW_REGION_RANGE(NtscU, 808D36CC, 808D36CF, +0x1AB0)
MKW_REGION_RANGE(NtscU, 808D36D4, 808D36D7, +0x1AB0)
MKW_REGION_RANGE(NtscU, 808D3744, 808D3747, +0x1AB0)
MKW_REGION_RANGE(NtscU, 808D374C, 808D374F, +0x1AB0)
MKW_REGION_RANGE(NtscU, 808D3C78, 808D3D1B, +0x1610)
MKW_REGION_RANGE(NtscU, 808D3E14, 808D3F2F, -0x4F68)
MKW_REGION_RANGE(NtscU, 808D9A58, 808D9A98, -0x60F0)
MKW_REGION_RANGE(NtscU, 808D9BC8, 808D9C5F, -0x36C0)
MKW_REGION_RANGE(NtscU, 808D9DB0, 808D9F97, -0x6220)
MKW_REGION_RANGE(NtscU, 808DA070, 808DA07F, -0x6220)
MKW_REGION_RANGE(NtscU, 808DA318, 808DA367, -0x63B8)
MKW_REGION_RANGE(NtscU, 808DA5DC, 808DA63F, -0x64C0)
MKW_REGION_RANGE(NtscU, 808DAD60, 808DAD6F, -0x6778)
MKW_REGION_RANGE(NtscU, 808DC540, 808DC54F, -0x4138)
MKW_REGION_RANGE(NtscU, 808E2BBC, 808E2BBE, +0x0)
MKW_REGION_RANGE(NtscU, 809BD6E0, 809C186B, -0x47C0)
MKW_REGION_RANGE(NtscU, 809C1874, 809C1877, -0x47C0)
MKW_REGION_RANGE(NtscU, 809C18F8, 809C18FB, -0x47E8)
MKW_REGION_RANGE(NtscU, 809C1918, 809C191B, -0x47E8)
MKW_REGION_RANGE(NtscU, 809C1980, 809C198F, -0x4610)
MKW_REGION_RANGE(NtscU, 809C19A0, 809C19BB, -0x4820)
MKW_REGION_RANGE(NtscU, 809C1E38, 809C1E3B, -0x4930)
MKW_REGION_RANGE(NtscU, 809C1F50, 809C20EF, -0x47C0)
MKW_REGION_RANGE(NtscU, 809C20F0, 809C20F7, -0x47C8)
MKW_REGION_RANGE(NtscU, 809C20F8, 809C20FB, -0x47A8)
MKW_REGION_RANGE(NtscU, 809C2100, 809C2107, -0x47D0)
MKW_REGION_RANGE(NtscU, 809C2108, 809C210B, -0x47B0)
MKW_REGION_RANGE(NtscU, 809C210C, 809C210F, -0x47D0)
MKW_REGION_RANGE(NtscU, 809C2110, 809C211B, -0x47D8)
MKW_REGION_RANGE(NtscU, 809C21D0, 809C21D3, -0x47C0)
MKW_REGION_RANGE(NtscU, 809C21D4, 809C21D7, +0xB840)
MKW_REGION_RANGE(NtscU, 809C21D8, 809C21DB, -0x47C0)
MKW_REGION_RANGE(NtscU, 809C2328, 809C232B, -0x47C8)
MKW_REGION_RANGE(NtscU, 809C26B8, 809C26BB, -0x4700)
MKW_REGION_RANGE(NtscU, 809C27F0, 809C289B, -0x4C40)
MKW_REGION_RANGE(NtscU, 809C28B8, 809C28BB, -0x47C0)
MKW_REGION_RANGE(NtscU, 809C2BE8, 809C2BEB, -0x4850)
MKW_REGION_RANGE(NtscU, 809C2CB8, 809C2CBB, -0x47E0)
MKW_REGION_RANGE(NtscU, 809C2EF0, 809C2EF3, -0x47C0)
MKW_REGION_RANGE(NtscU, 809C2F38, 809C361B, -0x47F8)
MKW_REGION_RANGE(NtscU, 809C3670, 809C38CF, -0x4808)
MKW_REGION_RANGE(NtscU, 809C4330, 809C4333, -0x4840)
MKW_REGION_RANGE(NtscU, 809C4680, 809C4683, -0x48C0)
MKW_REGION_RANGE(NtscU, 809C4730, 809C474F, -0x47B0)
MKW_REGION_RANGE(NtscU, 809C496C, 809C496F, -0x4820)
MKW_REGION_RANGE(NtscJ, 80000000, 80008023, +0x0)
MKW_REGION_RANGE(NtscJ, 800080E8, 8000ADBF, -0xA4)
MKW_REGION_RANGE(NtscJ, 8000AF08, 80021BAB, -0xDC)
MKW_REGION_RANGE(NtscJ, 80021BAC, 80244EBF, -0xE0)
MKW_REGION_RANGE(NtscJ, 802572BF, 802575DF, -0x660)
MKW_REGION_RANGE(NtscJ, 80257F40, 80258184, -0x660)
MKW_REGION_RANGE(NtscJ, 80276054, 802A407F, -0x660)
MKW_REGION_RANGE(NtscJ, 802A4080, 8038917B, -0x680)
MKW_REGION_RANGE(NtscJ, 80399200, 80399400, -0x680)
MKW_REGION_RANGE(NtscJ, 805103B4, 805CC0A7, -0x680)
MKW_REGION_RANGE(NtscJ, 805CC1B4, 805FA33B, -0x724)
MKW_REGION_RANGE(NtscJ, 805FA344, 805FF6E7, -0x724)
MKW_REGION_RANGE(NtscJ, 805FFD70, 806003E7, -0x848)
MKW_REGION_RANGE(NtscJ, 80600C78, 80620CB3, -0x88C)
MKW_REGION_RANGE(NtscJ, 80620D7C, 80637A23, -0x8B4)
MKW_REGION_RANGE(NtscJ, 80637A80, 8063BCF7, -0x914)
MKW_REGION_RANGE(NtscJ, 8063BD28, 8063BD2F, -0x920)
MKW_REGION_RANGE(NtscJ, 8063BD38, 8063BD3F, -0x920)
MKW_REGION_RANGE(NtscJ, 8063BE40, 8088F70F, -0x994)
MKW_REGION_RANGE(NtscJ, 8088F720, 8088FDBB, -0x9A0)
MKW_REGION_RANGE(NtscJ, 80890208, 80890977, -0x9B0)
MKW_REGION_RANGE(NtscJ, 808913C0, 808913C3, -0x9B0)
MKW_REGION_RANGE(NtscJ, 80891750, 8089175F, -0x9B0)
MKW_REGION_RANGE(NtscJ, 80891A60, 80891A6F, -0x9B0)
MKW_REGION_RANGE(NtscJ, 80891B28, 80891C28, -0x9B0)
MKW_REGION_RANGE(NtscJ, 80892F2C, 80897DEF, -0x9B0)
MKW_REGION_RANGE(NtscJ, 80898955, 80898959, -0xE60)
MKW_REGION_RANGE(NtscJ, 80899692, 808B5CCF, -0xEA0)
MKW_REGION_RANGE(NtscJ, 808B5CD8, 808B5CDB, -0xE9C)
MKW_REGION_RANGE(NtscJ, 808BB034, 808BB097, -0xEB0)
MKW_REGION_RANGE(NtscJ, 808BBEC8, 808DA617, -0xEB0)
MKW_REGION_RANGE(NtscJ, 808DC540, 808DC54F, -0xEB0)
MKW_REGION_RANGE(NtscJ, 808E2BBC, 808E2BBE, +0x0)
MKW_REGION_RANGE(NtscJ, 809BD6E8, 809C474B, -0xFA0)
MKW_REGION_RANGE(NtscJ, 809C496C, 809C496F, -0xFA0)
+43 -37
View File
@@ -44,56 +44,62 @@ extern "C" void GX__SetBlendMode_8017277c(uint32_t t, uint32_t s, uint32_t d, ui
extern "C" void GX__LoadPosMtxImm_8017310c(uint32_t ma, uint32_t id);
extern "C" void GX__SetCurrentMtx_80173214(uint32_t id);
// Keyed by the translated region's addresses, so only translated code sees them;
// runtime calls resolve through the registry.
#if defined(MKW_TRANSLATED_GUEST_REGION)
#define MKW_KNOWN_TYPED_NATIVE(addr, fn, ...) \
template <> struct KnownTypedNativeCpuCall<addr> { \
template <> struct KnownTypedNativeCpuCall<MKW_GADDR_CT(addr)> { \
static constexpr bool kAvailable = true; \
static void Invoke(CpuContext* cpu) { (void)cpu; fn(__VA_ARGS__); } \
}
MKW_KNOWN_TYPED_NATIVE(0x8016D3A4u, GX__SetVtxDesc_8016d3a4, cpu->gpr[3], cpu->gpr[4]);
MKW_KNOWN_TYPED_NATIVE(0x8016DC34u, GX__ClearVtxDesc_8016dc34);
MKW_KNOWN_TYPED_NATIVE(0x8016DC68u, GX__SetVtxAttrFmt_8016dc68, cpu->gpr[3], cpu->gpr[4], cpu->gpr[5], cpu->gpr[6], cpu->gpr[7]);
MKW_KNOWN_TYPED_NATIVE(0x8016E37Cu, GX__SetTexCoordGen2_8016e37c, cpu->gpr[3], cpu->gpr[4], cpu->gpr[5], cpu->gpr[6], cpu->gpr[7], cpu->gpr[8]);
MKW_KNOWN_TYPED_NATIVE(0x8016E5A4u, GX__SetNumTexGens_8016e5a4, cpu->gpr[3]);
MKW_KNOWN_TYPED_NATIVE(0x8016EAB0u, GX__DrawDone_8016eab0);
MKW_KNOWN_TYPED_NATIVE(0x8016F0F0u, GX__Begin_8016f0f0, cpu->gpr[3], cpu->gpr[4], cpu->gpr[5]);
MKW_KNOWN_TYPED_NATIVE(0x8016F314u, GX__SetLineWidth_8016f314, cpu->gpr[3], cpu->gpr[4]);
MKW_KNOWN_TYPED_NATIVE(0x8016F348u, GX__SetPointSize_8016f348, cpu->gpr[3], cpu->gpr[4]);
MKW_KNOWN_TYPED_NATIVE(0x8016F3B8u, GX__SetCullMode_8016f3b8, cpu->gpr[3]);
MKW_KNOWN_TYPED_NATIVE(0x8017054Cu, GX__SetNumChans_8017054c, cpu->gpr[3]);
MKW_KNOWN_TYPED_NATIVE(0x80170570u, GX__SetChanCtrl_80170570, cpu->gpr[3], cpu->gpr[4], cpu->gpr[5], cpu->gpr[6], cpu->gpr[7], cpu->gpr[8], cpu->gpr[9]);
MKW_KNOWN_TYPED_NATIVE(0x801707F8u, GX__InitTexObj_801707f8, cpu->gpr[3], cpu->gpr[4], cpu->gpr[5], cpu->gpr[6], cpu->gpr[7], cpu->gpr[8], cpu->gpr[9], cpu->gpr[10]);
MKW_KNOWN_TYPED_NATIVE(0x80170A4Cu, GX__InitTexObjLOD_80170a4c, cpu->gpr[3], cpu->gpr[4], cpu->gpr[5], static_cast<float>(cpu->fpr[1].d), static_cast<float>(cpu->fpr[2].d), static_cast<float>(cpu->fpr[3].d), cpu->gpr[6], cpu->gpr[7], cpu->gpr[8]);
MKW_KNOWN_TYPED_NATIVE(0x80171110u, GX__InvalidateTexAll_80171110);
MKW_KNOWN_TYPED_NATIVE(0x80170F2Cu, GX__LoadTexObj_80170f2c, cpu->gpr[3], cpu->gpr[4]);
MKW_KNOWN_TYPED_NATIVE(0x80171B38u, GX__SetNumIndStages_80171b38, cpu->gpr[3]);
MKW_KNOWN_TYPED_NATIVE(0x80171B58u, GX__SetTevDirect_80171b58, cpu->gpr[3]);
MKW_KNOWN_TYPED_NATIVE(0x80171CE0u, GX__SetTevColorIn_80171ce0, cpu->gpr[3], cpu->gpr[4], cpu->gpr[5], cpu->gpr[6], cpu->gpr[7]);
MKW_KNOWN_TYPED_NATIVE(0x80171D20u, GX__SetTevAlphaIn_80171d20, cpu->gpr[3], cpu->gpr[4], cpu->gpr[5], cpu->gpr[6], cpu->gpr[7]);
MKW_KNOWN_TYPED_NATIVE(0x80171D60u, GX__SetTevColorOp_80171d60, cpu->gpr[3], cpu->gpr[4], cpu->gpr[5], cpu->gpr[6], cpu->gpr[7], cpu->gpr[8]);
MKW_KNOWN_TYPED_NATIVE(0x80171DB8u, GX__SetTevAlphaOp_80171db8, cpu->gpr[3], cpu->gpr[4], cpu->gpr[5], cpu->gpr[6], cpu->gpr[7], cpu->gpr[8]);
MKW_KNOWN_TYPED_NATIVE(0x80171E70u, GX__SetTevColorS10_80171e70, cpu->gpr[3], cpu->gpr[4]);
MKW_KNOWN_TYPED_NATIVE(0x80171ED4u, GX__SetTevKColor_80171ed4, cpu->gpr[3], cpu->gpr[4]);
MKW_KNOWN_TYPED_NATIVE(0x80171F30u, GX__SetTevKColorSel_80171f30, cpu->gpr[3], cpu->gpr[4]);
MKW_KNOWN_TYPED_NATIVE(0x80171F80u, GX__SetTevKAlphaSel_80171f80, cpu->gpr[3], cpu->gpr[4]);
MKW_KNOWN_TYPED_NATIVE(0x80171FD0u, GX__SetTevSwapMode_80171fd0, cpu->gpr[3], cpu->gpr[4], cpu->gpr[5]);
MKW_KNOWN_TYPED_NATIVE(0x8017200Cu, GX__SetTevSwapModeTable_8017200c, cpu->gpr[3], cpu->gpr[4], cpu->gpr[5], cpu->gpr[6], cpu->gpr[7]);
MKW_KNOWN_TYPED_NATIVE(0x8017214Cu, GX__SetTevOrder_8017214c, cpu->gpr[3], cpu->gpr[4], cpu->gpr[5], cpu->gpr[6]);
MKW_KNOWN_TYPED_NATIVE(0x801722A8u, GX__SetNumTevStages_801722a8, cpu->gpr[3]);
MKW_KNOWN_TYPED_NATIVE(0x8017277Cu, GX__SetBlendMode_8017277c, cpu->gpr[3], cpu->gpr[4], cpu->gpr[5], cpu->gpr[6]);
MKW_KNOWN_TYPED_NATIVE(0x8017310Cu, GX__LoadPosMtxImm_8017310c, cpu->gpr[3], cpu->gpr[4]);
MKW_KNOWN_TYPED_NATIVE(0x80173214u, GX__SetCurrentMtx_80173214, cpu->gpr[3]);
MKW_KNOWN_TYPED_NATIVE(8016D3A4, GX__SetVtxDesc_8016d3a4, cpu->gpr[3], cpu->gpr[4]);
MKW_KNOWN_TYPED_NATIVE(8016DC34, GX__ClearVtxDesc_8016dc34);
MKW_KNOWN_TYPED_NATIVE(8016DC68, GX__SetVtxAttrFmt_8016dc68, cpu->gpr[3], cpu->gpr[4], cpu->gpr[5], cpu->gpr[6], cpu->gpr[7]);
MKW_KNOWN_TYPED_NATIVE(8016E37C, GX__SetTexCoordGen2_8016e37c, cpu->gpr[3], cpu->gpr[4], cpu->gpr[5], cpu->gpr[6], cpu->gpr[7], cpu->gpr[8]);
MKW_KNOWN_TYPED_NATIVE(8016E5A4, GX__SetNumTexGens_8016e5a4, cpu->gpr[3]);
MKW_KNOWN_TYPED_NATIVE(8016EAB0, GX__DrawDone_8016eab0);
MKW_KNOWN_TYPED_NATIVE(8016F0F0, GX__Begin_8016f0f0, cpu->gpr[3], cpu->gpr[4], cpu->gpr[5]);
MKW_KNOWN_TYPED_NATIVE(8016F314, GX__SetLineWidth_8016f314, cpu->gpr[3], cpu->gpr[4]);
MKW_KNOWN_TYPED_NATIVE(8016F348, GX__SetPointSize_8016f348, cpu->gpr[3], cpu->gpr[4]);
MKW_KNOWN_TYPED_NATIVE(8016F3B8, GX__SetCullMode_8016f3b8, cpu->gpr[3]);
MKW_KNOWN_TYPED_NATIVE(8017054C, GX__SetNumChans_8017054c, cpu->gpr[3]);
MKW_KNOWN_TYPED_NATIVE(80170570, GX__SetChanCtrl_80170570, cpu->gpr[3], cpu->gpr[4], cpu->gpr[5], cpu->gpr[6], cpu->gpr[7], cpu->gpr[8], cpu->gpr[9]);
MKW_KNOWN_TYPED_NATIVE(801707F8, GX__InitTexObj_801707f8, cpu->gpr[3], cpu->gpr[4], cpu->gpr[5], cpu->gpr[6], cpu->gpr[7], cpu->gpr[8], cpu->gpr[9], cpu->gpr[10]);
MKW_KNOWN_TYPED_NATIVE(80170A4C, GX__InitTexObjLOD_80170a4c, cpu->gpr[3], cpu->gpr[4], cpu->gpr[5], static_cast<float>(cpu->fpr[1].d), static_cast<float>(cpu->fpr[2].d), static_cast<float>(cpu->fpr[3].d), cpu->gpr[6], cpu->gpr[7], cpu->gpr[8]);
MKW_KNOWN_TYPED_NATIVE(80171110, GX__InvalidateTexAll_80171110);
MKW_KNOWN_TYPED_NATIVE(80170F2C, GX__LoadTexObj_80170f2c, cpu->gpr[3], cpu->gpr[4]);
MKW_KNOWN_TYPED_NATIVE(80171B38, GX__SetNumIndStages_80171b38, cpu->gpr[3]);
MKW_KNOWN_TYPED_NATIVE(80171B58, GX__SetTevDirect_80171b58, cpu->gpr[3]);
MKW_KNOWN_TYPED_NATIVE(80171CE0, GX__SetTevColorIn_80171ce0, cpu->gpr[3], cpu->gpr[4], cpu->gpr[5], cpu->gpr[6], cpu->gpr[7]);
MKW_KNOWN_TYPED_NATIVE(80171D20, GX__SetTevAlphaIn_80171d20, cpu->gpr[3], cpu->gpr[4], cpu->gpr[5], cpu->gpr[6], cpu->gpr[7]);
MKW_KNOWN_TYPED_NATIVE(80171D60, GX__SetTevColorOp_80171d60, cpu->gpr[3], cpu->gpr[4], cpu->gpr[5], cpu->gpr[6], cpu->gpr[7], cpu->gpr[8]);
MKW_KNOWN_TYPED_NATIVE(80171DB8, GX__SetTevAlphaOp_80171db8, cpu->gpr[3], cpu->gpr[4], cpu->gpr[5], cpu->gpr[6], cpu->gpr[7], cpu->gpr[8]);
MKW_KNOWN_TYPED_NATIVE(80171E70, GX__SetTevColorS10_80171e70, cpu->gpr[3], cpu->gpr[4]);
MKW_KNOWN_TYPED_NATIVE(80171ED4, GX__SetTevKColor_80171ed4, cpu->gpr[3], cpu->gpr[4]);
MKW_KNOWN_TYPED_NATIVE(80171F30, GX__SetTevKColorSel_80171f30, cpu->gpr[3], cpu->gpr[4]);
MKW_KNOWN_TYPED_NATIVE(80171F80, GX__SetTevKAlphaSel_80171f80, cpu->gpr[3], cpu->gpr[4]);
MKW_KNOWN_TYPED_NATIVE(80171FD0, GX__SetTevSwapMode_80171fd0, cpu->gpr[3], cpu->gpr[4], cpu->gpr[5]);
MKW_KNOWN_TYPED_NATIVE(8017200C, GX__SetTevSwapModeTable_8017200c, cpu->gpr[3], cpu->gpr[4], cpu->gpr[5], cpu->gpr[6], cpu->gpr[7]);
MKW_KNOWN_TYPED_NATIVE(8017214C, GX__SetTevOrder_8017214c, cpu->gpr[3], cpu->gpr[4], cpu->gpr[5], cpu->gpr[6]);
MKW_KNOWN_TYPED_NATIVE(801722A8, GX__SetNumTevStages_801722a8, cpu->gpr[3]);
MKW_KNOWN_TYPED_NATIVE(8017277C, GX__SetBlendMode_8017277c, cpu->gpr[3], cpu->gpr[4], cpu->gpr[5], cpu->gpr[6]);
MKW_KNOWN_TYPED_NATIVE(8017310C, GX__LoadPosMtxImm_8017310c, cpu->gpr[3], cpu->gpr[4]);
MKW_KNOWN_TYPED_NATIVE(80173214, GX__SetCurrentMtx_80173214, cpu->gpr[3]);
#undef MKW_KNOWN_TYPED_NATIVE
#define MKW_KNOWN_NATIVE_CPU_CALL(addr, fn) \
template <> struct KnownNativeCpuCall<addr> { \
template <> struct KnownNativeCpuCall<MKW_GADDR_CT(addr)> { \
static constexpr bool kAvailable = true; \
static constexpr uint32_t kNonvolatileFprWriteMask = 0; \
static constexpr void (*Entry)(CpuContext*) = &fn; \
}
MKW_KNOWN_NATIVE_CPU_CALL(0x800847C0u, nw4r__lyt__detail__DrawQuad_800847c0);
MKW_KNOWN_NATIVE_CPU_CALL(0x80084D20u, nw4r__lyt__detail__DrawQuad_80084d20);
MKW_KNOWN_NATIVE_CPU_CALL(800847C0, nw4r__lyt__detail__DrawQuad_800847c0);
MKW_KNOWN_NATIVE_CPU_CALL(80084D20, nw4r__lyt__detail__DrawQuad_80084d20);
#undef MKW_KNOWN_NATIVE_CPU_CALL
#endif
+21
View File
@@ -0,0 +1,21 @@
#pragma once
// Base translations the runtime calls directly, named by PAL identity and resolved through the
// function registry on first use.
// MKW_GUEST_FUNC(801A0620)(ctx);
#include "guest_abi/cpu_context.h"
#include "guest_abi/guest_region.h"
namespace GuestFunctions {
using Entry = void (*)(CpuContext*);
// The base translation at a guest address, also where a native or mod overrides it.
Entry BaseTranslation(uint32_t address);
} // namespace GuestFunctions
#define MKW_GUEST_FUNC(pal) \
(*[] { \
static const ::GuestFunctions::Entry entry = ::GuestFunctions::BaseTranslation(MKW_GADDR(pal)); \
return entry; \
}())
+114
View File
@@ -0,0 +1,114 @@
#pragma once
#include <atomic>
#include <cstddef>
#include <cstdint>
#include <new>
#include <type_traits>
#include <utility>
// Dedicated GX thread (Dolphin "dual core"): the game thread records GX HLE calls in order and
// the worker executes them. While active, GX work runs on the worker except inside inline scopes,
// which run on the game thread after the worker has caught up.
namespace GxWorker {
namespace detail {
inline constexpr size_t kAlign = 16;
extern std::atomic<bool> g_active;
extern constinit thread_local bool t_onWorker;
extern constinit thread_local int t_inlineDepth;
extern bool g_fifoPending;
void* Reserve(size_t payloadSize, void (*run)(void*));
void Commit();
void FlushFifo();
} // namespace detail
// Starts the worker unless [video] gx_worker disables it. Call once Aurora is up.
void Start();
void Stop();
// Stops without running queued work; bounded, for fatal exits.
void Abandon();
inline bool Active() noexcept { return detail::g_active.load(std::memory_order_relaxed); }
inline bool OnWorker() noexcept { return detail::t_onWorker; }
// True when a GX entry point on this thread must be recorded instead of executed.
inline bool ShouldDefer() noexcept {
return Active() && !detail::t_onWorker && detail::t_inlineDepth == 0;
}
template <typename F>
void Submit(F&& fn) {
using Fn = std::decay_t<F>;
static_assert(alignof(Fn) <= detail::kAlign, "over-aligned GX command");
if (detail::g_fifoPending) {
detail::FlushFifo();
}
void* storage = detail::Reserve(sizeof(Fn), [](void* p) {
struct Destroy {
Fn* f;
~Destroy() { f->~Fn(); }
} guard{static_cast<Fn*>(p)};
(*guard.f)();
});
new (storage) Fn(std::forward<F>(fn));
detail::Commit();
}
// Records fn when deferring, otherwise runs it now. Read call-time guest memory before calling.
template <typename F>
void Run(F&& fn) {
if (ShouldDefer()) {
Submit(std::forward<F>(fn));
} else {
fn();
}
}
// Gather-pipe writes, coalesced into one record per run of consecutive writes and replayed
// with their original sizes.
void FifoWrite(uint32_t value, uint32_t sizeBytes);
void FifoBurst(const uint8_t* data, uint32_t sizeBytes);
// Called about every millisecond while the game thread waits in Sync.
void SetWaitCallback(void (*callback)());
// Blocks until every recorded command has executed.
void Sync();
// GX entry points run inline on the game thread while inside (after a Sync), for work whose
// results the guest needs immediately.
void BeginInline();
void EndInline();
class InlineScope {
public:
InlineScope() { BeginInline(); }
~InlineScope() { EndInline(); }
InlineScope(const InlineScope&) = delete;
InlineScope& operator=(const InlineScope&) = delete;
};
} // namespace GxWorker
// Records the enclosing call for the GX worker and returns, or falls through when it should run
// here (worker thread, inline scope, worker disabled).
#define GX_DEFER_CALL(fn, ...) \
do { \
if (GxWorker::ShouldDefer()) { \
GxWorker::Submit([=] { fn(__VA_ARGS__); }); \
return; \
} \
} while (false)
#define GX_DEFER_CALL0(fn) \
do { \
if (GxWorker::ShouldDefer()) { \
GxWorker::Submit([] { fn(); }); \
return; \
} \
} while (false)
// Runs the rest of the enclosing function on this thread once the worker has caught up.
#define GX_SYNC_INLINE() GxWorker::InlineScope gxInlineScope_
+22
View File
@@ -0,0 +1,22 @@
#pragma once
// IOS side of the emulated Bluetooth module (/dev/usb/oh1/57e/305): routes the
// guest's USB control / bulk / interrupt transfers to Bluetooth::Controller and
// completes the held event and ACL reads from the alarm pump.
#include <cstdint>
struct CpuContext;
// Returns the fd for the Bluetooth device path, 0 when `path` is not ours.
int32_t Bluetooth_HLE_OpenDevice(const char* path);
bool Bluetooth_HLE_IsFd(uint32_t fd);
// Cancels the held reads (their callbacks are queued) and closes the device.
int32_t Bluetooth_HLE_Close(uint32_t fd);
// True when the request completed now with `result`; false when it is held
// until data arrives (its callback is queued later by the pump).
bool Bluetooth_HLE_IoctlvAsync(uint32_t fd, uint32_t cmd, uint32_t numIn, uint32_t numOut, uint32_t vectorPtr,
uint32_t callback, uint32_t callbackArg, int32_t& result);
int32_t Bluetooth_HLE_Ioctlv(uint32_t fd, uint32_t cmd, uint32_t numIn, uint32_t numOut, uint32_t vectorPtr);
// Runs the controller and completes held reads. Emulation thread only.
bool Bluetooth_HLE_ProcessCompletions(CpuContext* cpu);
@@ -41,42 +41,3 @@ inline GuestStatus Encode(const Fields& fields)
}
} // namespace PadStatusContract
namespace WpadContract {
inline constexpr std::size_t kChannelCount = 4;
inline constexpr int32_t kStatusDisabled = 0;
inline constexpr int32_t kStatusReady = 3;
inline constexpr int32_t kErrorNoController = -1;
inline constexpr int32_t kErrorNotReady = -2;
inline constexpr int32_t kErrorBadChannel = -6;
inline constexpr int32_t kExtensionCore = 0;
class State {
public:
void Initialize() { m_initialized = true; }
bool IsInitialized() const { return m_initialized; }
int32_t GetLibraryStatus() const { return m_initialized ? kStatusReady : kStatusDisabled; }
int32_t GetDataFormat(uint32_t chan) const
{
if (chan >= kChannelCount) {
return kErrorBadChannel;
}
return m_initialized ? 0 : kErrorNotReady;
}
int32_t SetDataFormat(uint32_t chan, int32_t format) const
{
(void)format;
if (chan >= kChannelCount) {
return kErrorBadChannel;
}
return m_initialized ? kErrorNoController : kErrorNotReady;
}
private:
bool m_initialized = false;
};
} // namespace WpadContract
+10
View File
@@ -0,0 +1,10 @@
#pragma once
#include <cstdint>
// HW_GPIOB_OUT, the GPIO outputs the PPC owns. WPAD sets bit 8 to power the
// sensor bar; there is nothing behind it on a PC, so the register only latches.
inline constexpr uint32_t kHollywoodGpioBOut = 0xCD0000C0u;
bool HollywoodGpio_Read32(uint32_t address, uint32_t& value);
bool HollywoodGpio_Write32(uint32_t address, uint32_t value);
+4 -2
View File
@@ -38,10 +38,12 @@ extern "C" void OS_HLE_EndDeferredGuestCallbacks();
// renaming requires updating Translator.Cli/Program.cs, RuntimeNativeGuestEffectAnalyzer.cs,
// TranslatedBuildShardEmitter.cs and RuntimeNativeFunctionAbiProvider.cs together.
// addr_hex is the PAL address (see guest_abi/guest_region.h). The translator reads arg_list as
// the guest ABI, so the unused wrapper keeps it checked against the implementation.
#define PPC_NATIVE_OVERRIDE(addr_hex, name, ret_type, arg_list, call_list) \
extern "C" ret_type func_##addr_hex arg_list { return name call_list; } \
[[maybe_unused]] static ret_type native_signature_##addr_hex arg_list { return name call_list; } \
REGISTER_NATIVE_FUNCTION(0x##addr_hex, name)
#define PPC_NATIVE_OVERRIDE_VOID(addr_hex, name, arg_list, call_list) \
extern "C" void func_##addr_hex arg_list { name call_list; } \
[[maybe_unused]] static void native_signature_##addr_hex arg_list { name call_list; } \
REGISTER_NATIVE_FUNCTION(0x##addr_hex, name)
+4 -1
View File
@@ -1,6 +1,7 @@
#pragma once
#include "runtime_config.h"
#include "guest_abi/guest_region.h"
#include "nand_settings.h"
#include "runtime_log.h"
#include "system_bridge.h"
@@ -184,7 +185,9 @@ inline std::filesystem::path DiscoverNandRootPath() {
static const auto root = [] {
const auto resolved = ResolveNandRootPath();
std::string error;
if (!RuntimeNandSettings::Ensure(resolved, error)) {
const auto& facts = ::GuestRegion::CurrentFacts();
if (!RuntimeNandSettings::Ensure(resolved, error,
{facts.area, facts.model, facts.productCode, facts.video, facts.gameRegion})) {
FailNandRoot(error.c_str(), RuntimeNandSettings::FilePath(resolved));
}
return resolved;
+25 -8
View File
@@ -88,10 +88,20 @@ inline std::string GenerateSerial(std::time_t now) {
return std::string(9 - digits.size(), '0') + digits;
}
// This recompilation targets the European disc. These are Dolphin's PAL boot
// defaults; an existing setting.txt always takes precedence, in every region.
inline std::optional<std::array<uint8_t, 256>> EncodeNew(const std::string& serial) {
const Settings identity{{"SERNO", serial}, {"CODE", "LEH"}, {"AREA", "EUR"}, {"GAME", "EU"}};
// Console identity for a new setting.txt; Dolphin's boot defaults for the game's region.
struct BootDefaults {
std::string area = "EUR";
std::string model = "RVL-001(EUR)";
std::string code = "LEH";
std::string video = "PAL";
std::string game = "EU";
};
// An existing setting.txt always takes precedence, in every region.
inline std::optional<std::array<uint8_t, 256>> EncodeNew(
const std::string& serial,
const BootDefaults& defaults = BootDefaults{}) {
const Settings identity{{"SERNO", serial}, {"CODE", defaults.code}, {"AREA", defaults.area}, {"GAME", defaults.game}};
if (!HasIdentity(identity)) {
return std::nullopt;
}
@@ -102,9 +112,15 @@ inline std::optional<std::array<uint8_t, 256>> EncodeNew(const std::string& seri
bytes[position++] = static_cast<uint8_t>(value) ^ static_cast<uint8_t>(key);
key = (key << 1) | (key >> 31);
};
for (const std::string& line : {std::string("AREA=EUR\r\n"), std::string("MODEL=RVL-001(EUR)\r\n"),
std::string("DVD=0\r\n"), std::string("MPCH=0x7FFE\r\n"), std::string("CODE=LEH\r\n"),
"SERNO=" + serial + "\r\n", std::string("VIDEO=PAL\r\n"), std::string("GAME=EU\r\n")}) {
for (const std::string& line : {
"AREA=" + defaults.area + "\r\n",
"MODEL=" + defaults.model + "\r\n",
std::string("DVD=0\r\n"),
std::string("MPCH=0x7FFE\r\n"),
"CODE=" + defaults.code + "\r\n",
"SERNO=" + serial + "\r\n",
"VIDEO=" + defaults.video + "\r\n",
"GAME=" + defaults.game + "\r\n"}) {
for (;;) {
if (position + line.size() > bytes.size()) {
return std::nullopt;
@@ -146,6 +162,7 @@ inline std::optional<std::filesystem::path> CreateScratchDirectory(
// Never replace an existing file, including an unreadable or damaged one.
// Publish a complete file atomically so simultaneous launches use one identity.
inline bool Ensure(const std::filesystem::path& root, std::string& error,
const BootDefaults& defaults,
std::time_t now = std::time(nullptr)) {
const auto path = FilePath(root);
std::error_code ec;
@@ -163,7 +180,7 @@ inline bool Ensure(const std::filesystem::path& root, std::string& error,
return false;
}
const auto bytes = EncodeNew(GenerateSerial(now));
const auto bytes = EncodeNew(GenerateSerial(now), defaults);
if (!bytes) {
error = "Cannot initialize NAND settings: invalid system clock";
return false;
+81 -81
View File
@@ -44,6 +44,8 @@ struct RuntimeUserConfig {
std::optional<uint32_t> frameInterpolationFps;
std::optional<bool> metalFxSpatialUpscaling;
std::optional<bool> skipUnreadyPipelines;
std::optional<bool> vsync;
std::optional<bool> gxWorker;
std::optional<bool> disableCopyFilter;
std::optional<bool> textureReplacements;
std::optional<bool> textureDumps;
@@ -57,26 +59,16 @@ struct RuntimeUserConfig {
std::optional<bool> audioMuted;
std::optional<bool> audioMixWorker;
std::optional<bool> attenuateMusicWhenMediaPlays;
// Real Wii Remotes (with or without Nunchuk / Classic Controller) and Wii U Pro
// Controllers paired over Bluetooth, driven by SDL's HIDAPI Wii driver. The driver
// is opt-in on SDL's side, so this decides whether the runtime turns it on.
// Real Wii Remotes passed through to the game's own Bluetooth stack. Off hands
// Nintendo controllers (e.g. the Wii U Pro Controller) to SDL as plain gamepads.
std::optional<bool> wiiRemotes;
// Keep re-enumerating Bluetooth HID devices while no Wii controller is connected
// (Dolphin's "continuous scanning"), so a remote that dropped or was switched on
// after launch shows up without restarting.
std::optional<bool> wiiContinuousScan;
// Accelerometer zero-point correction for the Bluetooth Wii Remote, in g and in
// SDL's sensor frame (x right, y out of the button face, z towards the user).
// SDL's Wii driver falls back to a nominal zero point when its read of the
// remote's calibration block times out (common over Bluetooth), so this is
// measured in the overlay with the remote at rest. Stored per remote under
// [controller.wii_accel_offsets]; the old shared
// wii_accel_offset_* keys are only detected, to report that they are ignored.
std::map<std::string, std::array<double, 3>> wiiAccelOffsets;
bool hasLegacyWiiAccelOffset = false;
// Debugging aid: append every KPAD sample of the Bluetooth remote (raw and
// corrected accelerometer, buttons) to wii_accel_trace.csv next to Config.toml.
std::optional<bool> wiiAccelTrace;
// Look for remotes in pairing mode (1+2 / SYNC) while none is connected.
std::optional<bool> wiiAutoPair;
// The console's Wii Remote settings (SYSCONF BT.BAR, BT.SENS, BT.SPKV, BT.MOT).
std::optional<bool> wiiSensorBarAbove;
std::optional<int32_t> wiiIrSensitivity;
std::optional<int32_t> wiiSpeakerVolume;
std::optional<bool> wiiRumble;
std::optional<bool> networkEnabled;
std::optional<bool> discordPresenceEnabled;
// The application ID of the WiiCompiled Discord application. This is only
@@ -285,6 +277,9 @@ inline void EnsureConfigFile() {
"display_mode = \"windowed\"\n"
"graphics_api = \"auto\"\n"
"skip_unready_pipelines = true\n"
"vsync = false\n"
"# Runs GX command processing on its own thread. Read once, at startup.\n"
"gx_worker = true\n"
"disable_copy_filter = true\n"
"show_fps = true\n"
"# Dolphin-style custom textures. When enabled, the renderer indexes\n"
@@ -382,28 +377,6 @@ inline void AppendOverlayRoots(RuntimeUserConfig& config, const std::string& roo
}
// Reads every supported setting out of a parsed Config.toml document.
// [x, y, z] of finite numbers (integers allowed), else nullopt.
inline std::optional<std::array<double, 3>> ParseAccelOffset(const toml::value& value) {
if (!value.is_array() || value.as_array().size() != 3) {
return std::nullopt;
}
std::array<double, 3> offset{};
for (size_t i = 0; i < 3; ++i) {
const toml::value& element = value.as_array()[i];
if (element.is_floating()) {
offset[i] = element.as_floating();
} else if (element.is_integer()) {
offset[i] = static_cast<double>(element.as_integer());
} else {
return std::nullopt;
}
if (!std::isfinite(offset[i])) {
return std::nullopt;
}
}
return offset;
}
inline RuntimeUserConfig ParseConfigDocument(const toml::value& document) {
RuntimeUserConfig config;
@@ -427,13 +400,6 @@ inline RuntimeUserConfig ParseConfigDocument(const toml::value& document) {
config.controllerExpressions[key] = value.as_string();
}
}
if (section->contains("wii_accel_offsets") && section->at("wii_accel_offsets").is_table()) {
for (const auto& [key, value] : section->at("wii_accel_offsets").as_table()) {
if (const auto offset = ParseAccelOffset(value)) {
config.wiiAccelOffsets[key] = *offset;
}
}
}
}
config.widescreen = FindConfigValue<bool>(document, "video", "widescreen");
@@ -471,6 +437,8 @@ inline RuntimeUserConfig ParseConfigDocument(const toml::value& document) {
config.metalFxSpatialUpscaling =
FindConfigValue<bool>(document, "video", "metalfx_spatial_upscaling");
config.skipUnreadyPipelines = FindConfigValue<bool>(document, "video", "skip_unready_pipelines");
config.vsync = FindConfigValue<bool>(document, "video", "vsync");
config.gxWorker = FindConfigValue<bool>(document, "video", "gx_worker");
config.disableCopyFilter = FindConfigValue<bool>(document, "video", "disable_copy_filter");
config.showFps = FindConfigValue<bool>(document, "video", "show_fps");
config.textureReplacements = FindConfigValue<bool>(document, "video", "texture_replacements");
@@ -494,11 +462,17 @@ inline RuntimeUserConfig ParseConfigDocument(const toml::value& document) {
config.attenuateMusicWhenMediaPlays =
FindConfigValue<bool>(document, "audio", "attenuate_music_when_media_plays");
config.wiiRemotes = FindConfigValue<bool>(document, "controller", "wii_remotes");
config.wiiContinuousScan = FindConfigValue<bool>(document, "controller", "wii_continuous_scan");
for (const char* key : {"wii_accel_offset_x", "wii_accel_offset_y", "wii_accel_offset_z"}) {
config.hasLegacyWiiAccelOffset |= FindConfigValue<double>(document, "controller", key).value_or(0.0) != 0.0;
config.wiiAutoPair = FindConfigValue<bool>(document, "controller", "wii_auto_pair");
if (const auto bar = FindConfigValue<std::string>(document, "controller", "wii_sensor_bar")) {
if (*bar == "above" || *bar == "below") config.wiiSensorBarAbove = *bar == "above";
}
config.wiiAccelTrace = FindConfigValue<bool>(document, "controller", "wii_accel_trace");
if (const auto value = FindConfigInt(document, "controller", "wii_ir_sensitivity"); value && *value >= 1 && *value <= 5) {
config.wiiIrSensitivity = *value;
}
if (const auto value = FindConfigInt(document, "controller", "wii_speaker_volume"); value && *value >= 0 && *value <= 127) {
config.wiiSpeakerVolume = *value;
}
config.wiiRumble = FindConfigValue<bool>(document, "controller", "wii_rumble");
config.networkEnabled = FindConfigValue<bool>(document, "network", "enabled");
config.discordPresenceEnabled = FindConfigValue<bool>(document, "discord", "enabled");
config.discordClientId = FindConfigValue<std::string>(document, "discord", "client_id");
@@ -669,6 +643,11 @@ inline bool SetSkipUnreadyPipelines(bool value) {
return WriteSetting("video", "skip_unready_pipelines", value ? "true" : "false");
}
inline bool SetVsync(bool value) {
Mutable().vsync = value;
return WriteSetting("video", "vsync", value ? "true" : "false");
}
inline bool SetDisableCopyFilter(bool value) {
Mutable().disableCopyFilter = value;
return WriteSetting("video", "disable_copy_filter", value ? "true" : "false");
@@ -844,56 +823,70 @@ inline bool AudioMixWorkerEnabled(bool fallback = true) {
return Get().audioMixWorker.value_or(fallback);
}
inline bool GxWorkerEnabled(bool fallback = true) {
return Get().gxWorker.value_or(fallback);
}
// Whether background music should duck automatically for other media playback.
inline bool AttenuateMusicWhenMediaPlays(bool fallback = false) {
return Get().attenuateMusicWhenMediaPlays.value_or(fallback);
}
// Bluetooth Wii Remotes / Wii U Pro Controllers. Read once before SDL's joystick
// subsystem comes up, so a change only takes effect on the next launch.
inline bool WiiRemotesEnabled(bool fallback = true) {
// Bluetooth Wii Remotes. Read once before SDL's joystick subsystem comes up, so a
// change only takes effect on the next launch.
inline bool WiiRemotesEnabled(bool fallback = false) {
return Get().wiiRemotes.value_or(fallback);
}
// Persists the Bluetooth Wii Remote driver switch.
inline bool SetWiiRemotesEnabled(bool value) {
Mutable().wiiRemotes = value;
return WriteSetting("controller", "wii_remotes", value ? "true" : "false");
}
// Whether to keep rescanning Bluetooth while no Wii controller is connected.
inline bool WiiContinuousScanEnabled(bool fallback = false) {
return Get().wiiContinuousScan.value_or(fallback);
inline bool WiiAutoPairEnabled(bool fallback = true) {
return Get().wiiAutoPair.value_or(fallback);
}
// Persists the continuous scanning switch.
inline bool SetWiiContinuousScanEnabled(bool value) {
Mutable().wiiContinuousScan = value;
return WriteSetting("controller", "wii_continuous_scan", value ? "true" : "false");
inline bool SetWiiAutoPairEnabled(bool value) {
Mutable().wiiAutoPair = value;
return WriteSetting("controller", "wii_auto_pair", value ? "true" : "false");
}
// Stored correction of the remote identified by `remoteKey`, if it has one.
inline std::optional<std::array<double, 3>> WiiAccelOffsetFor(const std::string& remoteKey) {
const auto it = Get().wiiAccelOffsets.find(remoteKey);
if (it == Get().wiiAccelOffsets.end()) {
return std::nullopt;
}
return it->second;
// Defaults are the console's.
inline bool WiiSensorBarAbove(bool fallback = false) {
return Get().wiiSensorBarAbove.value_or(fallback);
}
// Whether to write the per-frame accelerometer trace (off unless asked for).
inline bool WiiAccelTraceEnabled(bool fallback = false) {
return Get().wiiAccelTrace.value_or(fallback);
inline bool SetWiiSensorBarAbove(bool value) {
Mutable().wiiSensorBarAbove = value;
return WriteSetting("controller", "wii_sensor_bar", value ? "\"above\"" : "\"below\"");
}
// Persists the accelerometer correction of one remote. `remoteKey` must be a bare TOML key.
inline bool SetWiiAccelOffsetFor(const std::string& remoteKey, const std::array<double, 3>& offset) {
Mutable().wiiAccelOffsets[remoteKey] = offset;
// Always float literals, so a whole-number offset does not come back as a TOML integer.
std::ostringstream formatted;
formatted << std::fixed << std::setprecision(4) << '[' << offset[0] << ", " << offset[1] << ", " << offset[2]
<< ']';
return WriteSetting("controller.wii_accel_offsets", remoteKey, formatted.str());
inline int32_t WiiIrSensitivity(int32_t fallback = 3) {
return Get().wiiIrSensitivity.value_or(fallback);
}
inline bool SetWiiIrSensitivity(int32_t value) {
Mutable().wiiIrSensitivity = value;
return WriteSetting("controller", "wii_ir_sensitivity", std::to_string(value));
}
inline int32_t WiiSpeakerVolume(int32_t fallback = 0x59) {
return Get().wiiSpeakerVolume.value_or(fallback);
}
inline bool SetWiiSpeakerVolume(int32_t value) {
Mutable().wiiSpeakerVolume = value;
return WriteSetting("controller", "wii_speaker_volume", std::to_string(value));
}
inline bool WiiRumbleEnabled(bool fallback = true) {
return Get().wiiRumble.value_or(fallback);
}
inline bool SetWiiRumbleEnabled(bool value) {
Mutable().wiiRumble = value;
return WriteSetting("controller", "wii_rumble", value ? "true" : "false");
}
// Target frame rate for frame interpolation, or 0 to disable it.
@@ -906,6 +899,10 @@ inline bool SkipUnreadyPipelines(bool fallback = true) {
return Get().skipUnreadyPipelines.value_or(fallback);
}
inline bool Vsync(bool fallback = false) {
return Get().vsync.value_or(fallback);
}
inline bool DisableCopyFilter(bool fallback = true) {
return Get().disableCopyFilter.value_or(fallback);
}
@@ -1018,6 +1015,9 @@ inline void LogLoadedConfig() {
if (config.skipUnreadyPipelines) {
std::cout << " skip_unready_pipelines=" << (*config.skipUnreadyPipelines ? "true" : "false");
}
if (config.vsync) {
std::cout << " vsync=" << (*config.vsync ? "true" : "false");
}
if (config.disableCopyFilter) {
std::cout << " disable_copy_filter=" << (*config.disableCopyFilter ? "true" : "false");
}
+2 -1
View File
@@ -84,7 +84,8 @@ public:
// Crash-path helpers shared by the centralized reporter in main.cpp.
// DumpCrashHeuristics prints plain-language "likely cause" hints derived
// from the register state; WriteGuestMemorySnapshot writes MEM1 to
// `mem1Path` and MEM2 to `mem1Path + ".mem2"`, logging outcomes to `os`.
// `mem1Path`, MEM2 to `mem1Path + ".mem2"` and the Kamek overlay to
// `mem1Path + ".kamek"`, logging outcomes to `os`.
static void DumpCrashHeuristics(std::ostream& os, const struct CpuContext* cpu,
const uint32_t* missingGuestTarget);
static void WriteGuestMemorySnapshot(std::ostream& os, const std::filesystem::path& mem1Path);
-111
View File
@@ -1,111 +0,0 @@
#pragma once
#include <array>
#include <cstdint>
#include <optional>
struct PADStatus;
// Real Wii Remotes paired over Bluetooth.
//
// SDL 3 ships a HIDAPI driver for them (Wii Remote alone, with Nunchuk, with a
// Classic Controller, and the Wii U Pro Controller) that exposes each as a
// regular SDL gamepad; it is off by default on SDL's side and ConfigureSdlHints
// turns it on. A remote, alone or with a Nunchuk or Classic Controller, is
// handed to the game as a real Wii Remote with that extension: the KPAD/WPAD
// HLE builds a KPADStatus (and the WPADCLStatus behind KPADGetUnifiedWpadStatus)
// from it every frame (see ReadKpadSample), so the game's own code does wheelies,
// tricks, Wii Wheel steering and the Classic Controller layout, and plugging an
// extension in or out mid-game switches control scheme like on the console. Only
// the Wii U Pro Controller, which has no Wii-era equivalent, goes through
// aurora's PAD layer as a GameCube pad.
namespace WiiRemoteInput {
enum class Kind : uint8_t {
NotWii,
Remote, // Wii Remote with no extension
RemoteWithNunchuk,
RemoteWithClassic,
WiiUPro,
};
// Must run before SDL's joystick subsystem is initialized (aurora does that
// inside aurora_initialize); SDL only consults the hint on its first device scan.
void ConfigureSdlHints(bool enabled);
// Classifies a gamepad by the name SDL's Wii driver reports for it.
Kind KindForName(const char* gamepadName);
// Classification of the SDL gamepad currently assigned to a game port.
Kind KindForPort(uint32_t port);
const char* KindLabel(Kind kind);
// One frame of a Wii Remote in the units KPAD uses.
struct KpadSample {
uint32_t hold = 0; // WPAD button bits (WPAD_BUTTON_*), Nunchuk C/Z included
float acc[3] = {}; // remote accelerometer in g, KPAD frame (rest: y = -1)
bool hasNunchuk = false;
float stick[2] = {}; // Nunchuk stick, -1..1, +y up
float nunchukAcc[3] = {};
bool hasClassic = false;
uint32_t clHold = 0; // WPAD_CL_BUTTON_* bits
float clLStick[2] = {}; // Classic sticks, -1..1, +y up
float clRStick[2] = {};
int16_t clLStickRaw[2] = {}; // as WPADCLStatus reports them: -512..511, centre 0, +y up
int16_t clRStickRaw[2] = {};
uint8_t clTriggerL = 0; // 0..255; SDL only exposes the digital click
uint8_t clTriggerR = 0;
};
// What the game should see on `chan`: the controller SDL has there right now,
// or, for a few seconds after a remote vanished, the kind it had. SDL's driver
// destroys and re-creates the joystick when an extension is plugged in or out,
// and the console never disconnects for that, so the gap is papered over with
// neutral input instead of a "communications interrupted" prompt.
Kind EffectiveKind(uint32_t chan);
// True when the game reads `chan` through KPAD: a Wii Remote alone, with a
// Nunchuk or with a Classic Controller (live or within the swap grace period).
bool IsRemoteChannel(uint32_t chan);
// Reads the current state of the remote on `chan`; false when IsRemoteChannel
// is false. During the swap grace period the sample is neutral.
bool ReadKpadSample(uint32_t chan, KpadSample& sample);
// Remote accelerometer for the overlay readout: the SDL sample in g after the
// zero-point correction (x right across the face, y out of the button face, z
// towards the user) and the KPAD vector built from it. False when the port has
// no remote or SDL has not delivered a sample yet.
bool ReadAccelDebug(uint32_t chan, float sdlG[3], float kpadAcc[3]);
// Residual zero-point trim on top of SDL's factory-calibrated decode, stored per
// physical remote under [controller.wii_accel_offsets]. Expects the remote lying
// still, buttons up; Poll() averages about a second and stores it for that remote.
void StartAccelCalibration(uint32_t chan);
// Forgets the stored correction of the remote on `chan`.
void ClearAccelCalibration(uint32_t chan);
bool IsAccelCalibrating(uint32_t chan);
// 0..1 while a calibration is collecting samples.
float AccelCalibrationProgress();
// Correction in effect for the remote on `chan` (zeros when uncalibrated), or
// nullopt when the port has no remote.
std::optional<std::array<double, 3>> ReadAccelOffset(uint32_t chan);
// Outcome of the last calibration attempt on `chan` for the overlay, or nullptr.
const char* AccelCalibrationMessage(uint32_t chan);
// Reports "no controller" on the GameCube side for every port served through
// KPAD, so the game never sees the same remote twice. Runs after aurora's PADRead.
void HideRemotesFromPad(PADStatus* statuses, uint32_t count);
// Dolphin-style continuous scanning. SDL's HIDAPI Wii driver drops a remote on
// a failed Bluetooth read or an extension change and never re-adds it on its
// own. Poll() runs once per PADRead and, while no Wii controller is present,
// periodically forces SDL to re-enumerate HIDAPI by toggling the driver hint.
void Poll();
// Forces one re-enumeration right now (settings overlay "Rescan now").
void RescanNow();
// True while Poll() is looking for a remote (no Wii controller connected).
bool IsScanning();
// True where looking means periodic rescans; elsewhere Poll() waits for hotplug.
bool PeriodicRescanEnabled();
// Rescans issued since a Wii controller was last seen.
uint32_t ScanCount();
} // namespace WiiRemoteInput
+24
View File
@@ -0,0 +1,24 @@
#pragma once
// The Wii's extension controller encryption: the game writes a random key to
// the extension, which then XORs and offsets every byte it reports.
#include <array>
#include <cstddef>
#include <cstdint>
#include <optional>
namespace Wiimote::ExtCrypto {
struct Tables {
std::array<uint8_t, 8> ft{};
std::array<uint8_t, 8> sb{};
void Encrypt(uint8_t* data, uint32_t address, size_t length) const;
void Decrypt(uint8_t* data, uint32_t address, size_t length) const;
};
// Tables for the key the game wrote to registers 0x40..0x4F, or nullopt when it
// does not come from the first-party key schedule.
std::optional<Tables> FromKey(const std::array<uint8_t, 16>& keyData);
} // namespace Wiimote::ExtCrypto
+40
View File
@@ -0,0 +1,40 @@
#pragma once
// Follows the game's conversation with a remote's extension (type, calibration,
// encryption key) so its data can be put at rest while the overlay owns input.
#include "wiimote_ext_crypto.h"
#include <array>
#include <cstddef>
#include <cstdint>
#include <optional>
#include <vector>
namespace Wiimote {
class ExtensionState {
public:
void ObserveOutput(const uint8_t* report, size_t size);
void ObserveInput(const uint8_t* report, size_t size);
// Centres the sticks and releases the buttons of the extension data in a
// data report, leaving its motion data alone.
void Neutralize(std::vector<uint8_t>& report) const;
private:
enum class Type : uint8_t { Unknown, Nunchuk, Classic };
void Reset();
uint8_t Calibration(size_t index, uint8_t fallback) const;
Type m_type = Type::Unknown;
uint8_t m_format = 0;
bool m_encrypted = false;
std::array<uint8_t, 16> m_key{};
std::optional<ExtCrypto::Tables> m_tables;
std::array<uint8_t, 16> m_calibration{};
bool m_haveCalibration = false;
uint32_t m_readAddress = 0;
};
} // namespace Wiimote
+52
View File
@@ -0,0 +1,52 @@
#pragma once
// Physical Wii Remotes on the PC. Each one is attached to one of the four
// virtual remotes of the emulated Bluetooth controller and its HID reports are
// passed through unchanged, so the game's own WPAD/KPAD drive it (IR, speaker,
// rumble, extensions, battery).
#include "bluetooth/bt_controller.h"
#include <cstddef>
#include <string>
namespace Wiimote {
struct SlotInfo {
bool attached = false;
// The game disconnected it and the platform could not power it off; any
// button wakes it up again.
bool asleep = false;
Bluetooth::LinkState link = Bluetooth::LinkState::Down;
std::string model; // "Wii Remote" / "Wii Remote Plus"
std::string extension; // empty when none
int battery = -1; // 0..4 like the game's battery bars, -1 unknown
};
struct PairingInfo {
bool supported = false; // this platform can discover and pair remotes itself
bool searching = false;
std::string status; // last pairing outcome, for the overlay
};
// Must run before SDL's joystick subsystem starts (aurora_initialize).
void ConfigureSdlHints(bool enabled);
// Starts device scanning when Wii Remotes are enabled.
void Initialize();
// Powers the remotes off, like a console shutting down, and stops the threads.
void Shutdown();
Bluetooth::RemoteHost& Host();
SlotInfo Slot(size_t slot);
PairingInfo Pairing();
// The console's SYNC button: look for remotes in pairing mode for a while.
void StartPairing();
// Ends the game's connection to the remote on `slot` and powers it off.
void Disconnect(size_t slot);
// Setup problems worth showing (kernel driver, missing permissions), else empty.
std::string PlatformNotice();
// Connected remotes waiting for a free player slot.
size_t WaitingRemotes();
} // namespace Wiimote
+43
View File
@@ -0,0 +1,43 @@
#pragma once
// Host Bluetooth services the Wii Remote manager needs from the OS: finding and
// pairing remotes in pairing mode, naming a HID device, and dropping a remote's
// connection (which powers it off, as a console does).
#include <atomic>
#include <string>
struct SDL_hid_device_info;
namespace Wiimote::Platform {
struct Capabilities {
bool pairing = false;
bool powerOff = false;
};
Capabilities Query();
// One search for remotes in pairing mode (red SYNC or 1+2); pairs and connects
// each one found. Blocks for a few seconds. Returns the number connected.
int DiscoverAndPair(const std::atomic<bool>& cancel, std::string& status);
// Disconnects the remote; it switches itself off. False when not possible.
bool PowerOff(const std::string& address);
// Bluetooth address of a HID device as 12 lowercase hex digits, or empty.
std::string AddressFor(const SDL_hid_device_info& info);
// Bluetooth name ("Nintendo RVL-CNT-01-TR", ...) or the HID product string.
std::string NameFor(const std::string& address, const SDL_hid_device_info& info);
// False only when the OS knows the remote is not connected right now.
bool MaybeConnected(const std::string& address);
// False when the OS driver would fight over the device (Linux hid-wiimote).
bool Usable(const SDL_hid_device_info& info);
// Setup problems worth showing in the overlay, else empty.
std::string Notice();
} // namespace Wiimote::Platform