mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-09-18 15:32:06 +00:00
Merge branch 'master' of https://github.com/crediar/dolphin
This commit is contained in:
commit
fdf5df80f8
83 changed files with 960 additions and 443 deletions
File diff suppressed because one or more lines are too long
|
@ -59,9 +59,12 @@ C202F310 00000003
|
|||
$16:9 Aspect Ratio Fix - Centered HUD with letterboxing
|
||||
04199598 4E800020
|
||||
0441F6D8 3FE38E39
|
||||
$No Letterbox [Ralf]
|
||||
0400E3D4 48000070
|
||||
|
||||
[Gecko_RetroAchievements_Verified]
|
||||
$16:9 Aspect Ratio Fix - Normal HUD
|
||||
$16:9 Aspect Ratio Fix - Centered HUD
|
||||
$16:9 Aspect Ratio Fix - Stretched HUD
|
||||
$16:9 Aspect Ratio Fix - Centered HUD with letterboxing
|
||||
$No Letterbox
|
||||
|
|
|
@ -32,9 +32,12 @@ C202F3F8 00000003
|
|||
$16:9 Aspect Ratio Fix - Centered HUD with letterboxing
|
||||
0419B274 4E800020
|
||||
0442C158 3FE38E39
|
||||
$No Letterbox [Ralf]
|
||||
0400E598 48000070
|
||||
|
||||
[Gecko_RetroAchievements_Verified]
|
||||
$16:9 Aspect Ratio Fix - Normal HUD
|
||||
$16:9 Aspect Ratio Fix - Centered HUD
|
||||
$16:9 Aspect Ratio Fix - Stretched HUD
|
||||
$16:9 Aspect Ratio Fix - Centered HUD with letterboxing
|
||||
$No Letterbox
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# GC6E01, GC6P01 - Pokemon Colosseum
|
||||
# GC6E01, GC6P01, GC6J01 - Pokemon Colosseum
|
||||
|
||||
[Core]
|
||||
# Values set here will override the main Dolphin settings.
|
||||
|
@ -13,5 +13,5 @@
|
|||
SafeTextureCacheColorSamples = 0
|
||||
# Many areas of the game have unused vertexes, especially with cutscenes
|
||||
# involving Shadow Pokémon, such as the purification cutscene.
|
||||
# CPU Cull ends up greatly boosting performance for these cases.
|
||||
# CPU Cull ends up greatly boosting performance for these cases.
|
||||
CPUCull = True
|
||||
|
|
|
@ -36,9 +36,12 @@ $16:9 Widescreen
|
|||
04005308 4809E62C
|
||||
040A3930 4BF619D0
|
||||
0447E724 3FAAAAAB
|
||||
$60 FPS [Nerdzilla]
|
||||
04005D98 38600000
|
||||
|
||||
[Patches_RetroAchievements_Verified]
|
||||
$Allow Memory Card saving with Savestates
|
||||
|
||||
[AR_RetroAchievements_Verified]
|
||||
$16:9 Widescreen
|
||||
$60 FPS [Nerdzilla]
|
||||
|
|
|
@ -30,27 +30,11 @@ $Allow Memory Card saving with Savestates
|
|||
$Allow Memory Card saving with Savestates
|
||||
|
||||
[ActionReplay]
|
||||
$16:9 Widescreen
|
||||
04261AC0 000034E0
|
||||
04261AC4 000034E4
|
||||
F6000001 80008180
|
||||
FFA01090 93E10024
|
||||
D2000000 00000003
|
||||
3DC03FAA 61CEAAAB
|
||||
91C20000 C2620000
|
||||
EFB300B2 00000000
|
||||
E0000000 80008000
|
||||
F6000001 80008180
|
||||
FF601090 7C7F1B78
|
||||
D2000000 00000004
|
||||
3DC03FAA 61CEAAAB
|
||||
91C20004 C2220004
|
||||
EF7100B2 39C00000
|
||||
60000000 00000000
|
||||
E0000000 80008000
|
||||
$60 FPS [Nerdzilla]
|
||||
04005CFC 38600000
|
||||
|
||||
[Patches_RetroAchievements_Verified]
|
||||
$Allow Memory Card saving with Savestates
|
||||
|
||||
[AR_RetroAchievements_Verified]
|
||||
$16:9 Widescreen
|
||||
$60 FPS [Nerdzilla]
|
||||
|
|
|
@ -36,9 +36,12 @@ $16:9 Widescreen [Ralf]
|
|||
04005308 480A1C68
|
||||
040A6F6C 4BF5E394
|
||||
044CBCA4 3FAAAAAB
|
||||
$60 FPS [Nerdzilla]
|
||||
04005E90 38600000
|
||||
|
||||
[Patches_RetroAchievements_Verified]
|
||||
$Allow Memory Card saving with Savestates
|
||||
|
||||
[AR_RetroAchievements_Verified]
|
||||
$16:9 Widescreen [Ralf]
|
||||
$60 FPS [Nerdzilla]
|
||||
|
|
17
Data/Sys/GameSettings/GD7PB2.ini
Normal file
17
Data/Sys/GameSettings/GD7PB2.ini
Normal file
|
@ -0,0 +1,17 @@
|
|||
# GD7PB2 - Dragon Ball Z: Budokai
|
||||
|
||||
[Core]
|
||||
# Values set here will override the main Dolphin settings.
|
||||
|
||||
[OnFrame]
|
||||
# Add memory patches to be applied every frame here.
|
||||
|
||||
[ActionReplay]
|
||||
# Add action replay cheats here.
|
||||
|
||||
[Gecko]
|
||||
$Deinterlacing Fix
|
||||
0044E9A8 00000000
|
||||
|
||||
[Gecko_RetroAchievements_Verified]
|
||||
$Deinterlacing Fix
|
|
@ -101,23 +101,28 @@ C2434EBC 00000002
|
|||
3DC03F36 91DC00CC
|
||||
60000000 00000000
|
||||
$60 FPS
|
||||
04423540 3BC00001 # `setFramerate`: force 60 FPS
|
||||
0451D804 40100000 # Map translation speed 1
|
||||
C2312B04 00000002 # Map translation speed 2
|
||||
3C003F80 9001FFFC
|
||||
C121FFFC 00000000
|
||||
C242DF28 00000004 # Set to 30 FPS when cutscene starts
|
||||
3C608042 6063352C
|
||||
7C6903A6 806D9AEC
|
||||
38800002 4E800421
|
||||
801F01F0 00000000
|
||||
C242E040 00000004 # Set to 60 FPS when cutscene ends
|
||||
3C608042 6063352C
|
||||
7C6903A6 806D9AEC
|
||||
38800001 4E800421
|
||||
801F01F0 00000000
|
||||
0414B778 38800001 # `BaseGameSection::init`, force `setFramerate` argument to 60 FPS (2P mode doesn't start with a cutscene)
|
||||
04513C5C 40100000 # Map translation speed 1
|
||||
04513C60 3F800000 # Map translation speed 2
|
||||
0451D7B0 3C75C28F # Map scale speed
|
||||
04520628 3F911111 # Cutscene delta time
|
||||
0452062C 20000000
|
||||
C2010654 00000002 # Double cutscene sections' "frame duration"
|
||||
5484083C 909F002C
|
||||
60000000 00000000
|
||||
C242ED28 00000002 # Cutscene: objects spawn/despawn timing
|
||||
3C003F00 9001FFFC
|
||||
C001FFFC 00000000
|
||||
C24374DC 00000002 # Cutscene: trigger `startFadeblack` a frame earlier (fixes black screen in Day 1 cutscene)
|
||||
806D9AEC C0230054
|
||||
FC020840 00000000
|
||||
04514128 3ECCCCCD # Final floor animation
|
||||
04516140 3F000000 # Pay dept animation
|
||||
04520078 3EB33333 # Ready-go animation
|
||||
04410FA4 388000C8 # Win/Lose reason duration 1
|
||||
04410FAC 386000FA # Win/Lose reason duration 2
|
||||
04520224 3F000000 # Win/Lose animation
|
||||
044106DC 38800078 # Win/Lose duration 1
|
||||
044106E4 38600168 # Win/Lose duration 2
|
||||
|
||||
[Gecko_RetroAchievements_Verified]
|
||||
$16:9 Widescreen
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
$16:9 Widescreen
|
||||
043438A8 3FAF0000
|
||||
043486A8 406CB800
|
||||
$60Hz
|
||||
$60 FPS
|
||||
040B66F8 60000000
|
||||
0402F0E4 38600002
|
||||
0402F1CC 60000000
|
||||
|
@ -16,4 +16,4 @@ $60Hz
|
|||
|
||||
[AR_RetroAchievements_Verified]
|
||||
$16:9 Widescreen
|
||||
$60Hz
|
||||
$60 FPS
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
$16:9 Widescreen
|
||||
04343EC8 3FAF0000
|
||||
04348D30 406CB800
|
||||
$60Hz
|
||||
$60 FPS
|
||||
040B6F08 60000000
|
||||
0402F130 38600002
|
||||
0402F218 60000000
|
||||
|
@ -16,4 +16,4 @@ $60Hz
|
|||
|
||||
[AR_RetroAchievements_Verified]
|
||||
$16:9 Widescreen
|
||||
$60Hz
|
||||
$60 FPS
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
$16:9 Widescreen
|
||||
04336CE8 3FABA000
|
||||
0433B920 406C4F00
|
||||
$50Hz
|
||||
$50 FPS
|
||||
040B6408 60000000
|
||||
0402F214 38600001
|
||||
0402F300 60000000
|
||||
|
@ -16,4 +16,4 @@ $50Hz
|
|||
|
||||
[AR_RetroAchievements_Verified]
|
||||
$16:9 Widescreen
|
||||
$50Hz
|
||||
$50 FPS
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# GXXE01, GXXP01, GXXJ01 - POKeMON XD
|
||||
# GXXE01, GXXP01, GXXJ01 - Pokemon XD: Gale of Darkness
|
||||
|
||||
[Core]
|
||||
# Prevents crash when Greevil's henchman and Zook show up
|
||||
|
@ -18,5 +18,5 @@ MMU = True
|
|||
SafeTextureCacheColorSamples = 0
|
||||
# Many areas of the game have unused vertexes, especially with cutscenes
|
||||
# involving Shadow Pokémon, such as the purification cutscene.
|
||||
# CPU Cull ends up greatly boosting performance for these cases.
|
||||
# CPU Cull ends up greatly boosting performance for these cases.
|
||||
CPUCull = True
|
||||
|
|
|
@ -12,17 +12,13 @@ $Allow Memory Card saving with Savestates
|
|||
[Patches_RetroAchievements_Verified]
|
||||
$Allow Memory Card saving with Savestates
|
||||
|
||||
[Gecko]
|
||||
[ActionReplay]
|
||||
$16:9 Widescreen
|
||||
042EB168 00001783
|
||||
042EB16C 000017EA
|
||||
0400F614 60000000
|
||||
0405C984 60000000
|
||||
040875BC 60000000
|
||||
0405C8AC 60000000
|
||||
04086A34 60000000
|
||||
04086930 60000000
|
||||
044ED860 3FC962F9
|
||||
$60 FPS
|
||||
042AF894 38000000
|
||||
042AEBEC 38000000
|
||||
|
||||
[Gecko_RetroAchievements_Verified]
|
||||
[AR_RetroAchievements_Verified]
|
||||
$16:9 Widescreen
|
||||
$60 FPS
|
||||
|
|
|
@ -11,3 +11,14 @@ $Allow Memory Card saving with Savestates
|
|||
|
||||
[Patches_RetroAchievements_Verified]
|
||||
$Allow Memory Card saving with Savestates
|
||||
|
||||
[ActionReplay]
|
||||
$16:9 Widescreen
|
||||
044CACE8 3FC962F9
|
||||
$60 FPS
|
||||
042AA2BC 38000000
|
||||
042A9614 38000000
|
||||
|
||||
[AR_RetroAchievements_Verified]
|
||||
$16:9 Widescreen
|
||||
$60 FPS
|
||||
|
|
|
@ -12,13 +12,17 @@ $Allow Memory Card saving with Savestates
|
|||
[Patches_RetroAchievements_Verified]
|
||||
$Allow Memory Card saving with Savestates
|
||||
|
||||
[Gecko]
|
||||
[ActionReplay]
|
||||
$16:9 Widescreen
|
||||
04005300 C3A2BFBC
|
||||
04005304 EFBD00B2
|
||||
04005308 480AF7A8
|
||||
040B4AAC 4BF50854
|
||||
0452A35C 3FAAAAAB
|
||||
$60 FPS
|
||||
042B17F8 38000000
|
||||
042B0B50 38000000
|
||||
|
||||
[Gecko_RetroAchievements_Verified]
|
||||
[AR_RetroAchievements_Verified]
|
||||
$16:9 Widescreen
|
||||
$60 FPS
|
||||
|
|
5
Data/Sys/GameSettings/SP4.ini
Normal file
5
Data/Sys/GameSettings/SP4.ini
Normal file
|
@ -0,0 +1,5 @@
|
|||
# SP4PJW - Pétanque Master
|
||||
|
||||
[Video_Hacks]
|
||||
# Immediate XFB causes seizure-inducing flickering
|
||||
ImmediateXFBEnable = False
|
|
@ -7,7 +7,7 @@ plugins {
|
|||
|
||||
@Suppress("UnstableApiUsage")
|
||||
android {
|
||||
compileSdkVersion = "android-34"
|
||||
compileSdkVersion = "android-36"
|
||||
ndkVersion = "27.0.12077973"
|
||||
|
||||
buildFeatures {
|
||||
|
@ -40,7 +40,7 @@ android {
|
|||
defaultConfig {
|
||||
applicationId = "org.dolphinemu.dolphinemu"
|
||||
minSdk = 21
|
||||
targetSdk = 34
|
||||
targetSdk = 36
|
||||
|
||||
versionCode = getBuildVersionCode()
|
||||
|
||||
|
|
|
@ -362,7 +362,7 @@ public final class NativeLibrary
|
|||
/**
|
||||
* Pauses emulation.
|
||||
*/
|
||||
public static native void PauseEmulation();
|
||||
public static native void PauseEmulation(boolean overrideAchievementRestrictions);
|
||||
|
||||
/**
|
||||
* Stops emulation.
|
||||
|
|
|
@ -455,7 +455,7 @@ class EmulationActivity : AppCompatActivity(), ThemeProvider {
|
|||
MENU_ACTION_REFRESH_WIIMOTES -> NativeLibrary.RefreshWiimotes()
|
||||
MENU_ACTION_PAUSE_EMULATION -> {
|
||||
hasUserPausedEmulation = true
|
||||
NativeLibrary.PauseEmulation()
|
||||
NativeLibrary.PauseEmulation(false)
|
||||
}
|
||||
|
||||
MENU_ACTION_UNPAUSE_EMULATION -> {
|
||||
|
|
|
@ -109,7 +109,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
|||
override fun onPause() {
|
||||
if (NativeLibrary.IsRunningAndUnpaused() && !NativeLibrary.IsShowingAlertMessage()) {
|
||||
Log.debug("[EmulationFragment] Pausing emulation.")
|
||||
NativeLibrary.PauseEmulation()
|
||||
NativeLibrary.PauseEmulation(true)
|
||||
}
|
||||
super.onPause()
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
plugins {
|
||||
id("com.android.application") version "8.9.0" apply false
|
||||
id("com.android.library") version "8.9.0" apply false
|
||||
id("com.android.application") version "8.11.0" apply false
|
||||
id("com.android.library") version "8.11.0" apply false
|
||||
id("org.jetbrains.kotlin.android") version "1.8.21" apply false
|
||||
id("com.android.test") version "8.9.0" apply false
|
||||
id("com.android.test") version "8.11.0" apply false
|
||||
id("androidx.baselineprofile") version "1.3.3" apply false
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
|
|
@ -271,10 +271,12 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_UnPauseEmula
|
|||
Core::SetState(Core::System::GetInstance(), Core::State::Running);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_PauseEmulation(JNIEnv*, jclass)
|
||||
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_PauseEmulation(
|
||||
JNIEnv*, jclass, bool override_achievement_restrictions)
|
||||
{
|
||||
HostThreadLock guard;
|
||||
Core::SetState(Core::System::GetInstance(), Core::State::Paused);
|
||||
Core::SetState(Core::System::GetInstance(), Core::State::Paused, true,
|
||||
override_achievement_restrictions);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_StopEmulation(JNIEnv*, jclass)
|
||||
|
|
|
@ -356,14 +356,13 @@ void GekkoIRPlugin::OnCloseParen(ParenType type)
|
|||
void GekkoIRPlugin::OnLabelDecl(std::string_view name)
|
||||
{
|
||||
const std::string name_str(name);
|
||||
if (m_symset.contains(name_str))
|
||||
if (const bool inserted = m_symset.insert(name_str).second; !inserted)
|
||||
{
|
||||
m_owner->EmitErrorHere(fmt::format("Label/Constant {} is already defined", name));
|
||||
return;
|
||||
}
|
||||
|
||||
m_labels[name_str] = m_active_block->BlockEndAddress();
|
||||
m_symset.insert(name_str);
|
||||
}
|
||||
|
||||
void GekkoIRPlugin::OnNumericLabelDecl(std::string_view, u32 num)
|
||||
|
@ -374,14 +373,13 @@ void GekkoIRPlugin::OnNumericLabelDecl(std::string_view, u32 num)
|
|||
void GekkoIRPlugin::OnVarDecl(std::string_view name)
|
||||
{
|
||||
const std::string name_str(name);
|
||||
if (m_symset.contains(name_str))
|
||||
if (const bool inserted = m_symset.insert(name_str).second; !inserted)
|
||||
{
|
||||
m_owner->EmitErrorHere(fmt::format("Label/Constant {} is already defined", name));
|
||||
return;
|
||||
}
|
||||
|
||||
m_active_var = &m_constants[name_str];
|
||||
m_symset.insert(name_str);
|
||||
}
|
||||
|
||||
void GekkoIRPlugin::PostParseAction()
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
|
@ -34,6 +35,7 @@ void Symbol::Rename(const std::string& symbol_name)
|
|||
|
||||
void SymbolDB::List()
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
for (const auto& func : m_functions)
|
||||
{
|
||||
DEBUG_LOG_FMT(OSHLE, "{} @ {:08x}: {} bytes (hash {:08x}) : {} calls", func.second.name,
|
||||
|
@ -44,11 +46,14 @@ void SymbolDB::List()
|
|||
|
||||
bool SymbolDB::IsEmpty() const
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
return m_functions.empty() && m_notes.empty();
|
||||
}
|
||||
|
||||
bool SymbolDB::Clear(const char* prefix)
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
|
||||
// TODO: honor prefix
|
||||
m_map_name.clear();
|
||||
if (IsEmpty())
|
||||
|
@ -61,16 +66,24 @@ bool SymbolDB::Clear(const char* prefix)
|
|||
}
|
||||
|
||||
void SymbolDB::Index()
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
Index(&m_functions);
|
||||
}
|
||||
|
||||
void SymbolDB::Index(XFuncMap* functions)
|
||||
{
|
||||
int i = 0;
|
||||
for (auto& func : m_functions)
|
||||
for (auto& func : *functions)
|
||||
{
|
||||
func.second.index = i++;
|
||||
}
|
||||
}
|
||||
|
||||
Symbol* SymbolDB::GetSymbolFromName(std::string_view name)
|
||||
const Symbol* SymbolDB::GetSymbolFromName(std::string_view name) const
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
|
||||
for (auto& func : m_functions)
|
||||
{
|
||||
if (func.second.function_name == name)
|
||||
|
@ -80,9 +93,10 @@ Symbol* SymbolDB::GetSymbolFromName(std::string_view name)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<Symbol*> SymbolDB::GetSymbolsFromName(std::string_view name)
|
||||
std::vector<const Symbol*> SymbolDB::GetSymbolsFromName(std::string_view name) const
|
||||
{
|
||||
std::vector<Symbol*> symbols;
|
||||
std::lock_guard lock(m_mutex);
|
||||
std::vector<const Symbol*> symbols;
|
||||
|
||||
for (auto& func : m_functions)
|
||||
{
|
||||
|
@ -93,8 +107,10 @@ std::vector<Symbol*> SymbolDB::GetSymbolsFromName(std::string_view name)
|
|||
return symbols;
|
||||
}
|
||||
|
||||
Symbol* SymbolDB::GetSymbolFromHash(u32 hash)
|
||||
const Symbol* SymbolDB::GetSymbolFromHash(u32 hash) const
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
|
||||
auto iter = m_checksum_to_function.find(hash);
|
||||
if (iter == m_checksum_to_function.end())
|
||||
return nullptr;
|
||||
|
@ -102,8 +118,10 @@ Symbol* SymbolDB::GetSymbolFromHash(u32 hash)
|
|||
return *iter->second.begin();
|
||||
}
|
||||
|
||||
std::vector<Symbol*> SymbolDB::GetSymbolsFromHash(u32 hash)
|
||||
std::vector<const Symbol*> SymbolDB::GetSymbolsFromHash(u32 hash) const
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
|
||||
const auto iter = m_checksum_to_function.find(hash);
|
||||
|
||||
if (iter == m_checksum_to_function.cend())
|
||||
|
@ -114,6 +132,34 @@ std::vector<Symbol*> SymbolDB::GetSymbolsFromHash(u32 hash)
|
|||
|
||||
void SymbolDB::AddCompleteSymbol(const Symbol& symbol)
|
||||
{
|
||||
m_functions.emplace(symbol.address, symbol);
|
||||
std::lock_guard lock(m_mutex);
|
||||
m_functions[symbol.address] = symbol;
|
||||
}
|
||||
|
||||
bool SymbolDB::RenameSymbol(const Symbol& symbol, const std::string& symbol_name)
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
|
||||
auto it = m_functions.find(symbol.address);
|
||||
if (it == m_functions.end())
|
||||
return false;
|
||||
|
||||
it->second.Rename(symbol_name);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SymbolDB::RenameSymbol(const Symbol& symbol, const std::string& symbol_name,
|
||||
const std::string& object_name)
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
|
||||
auto it = m_functions.find(symbol.address);
|
||||
if (it == m_functions.end())
|
||||
return false;
|
||||
|
||||
it->second.Rename(symbol_name);
|
||||
it->second.object_name = object_name;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
|
|
|
@ -7,12 +7,14 @@
|
|||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "Common/Assert.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
namespace Core
|
||||
|
@ -87,27 +89,71 @@ public:
|
|||
SymbolDB();
|
||||
virtual ~SymbolDB();
|
||||
|
||||
virtual Symbol* GetSymbolFromAddr(u32 addr) { return nullptr; }
|
||||
virtual Symbol* AddFunction(const Core::CPUThreadGuard& guard, u32 start_addr) { return nullptr; }
|
||||
virtual const Symbol* GetSymbolFromAddr(u32 addr) const { return nullptr; }
|
||||
virtual const Symbol* AddFunction(const Core::CPUThreadGuard& guard, u32 start_addr)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
void AddCompleteSymbol(const Symbol& symbol);
|
||||
bool RenameSymbol(const Symbol& symbol, const std::string& symbol_name);
|
||||
bool RenameSymbol(const Symbol& symbol, const std::string& symbol_name,
|
||||
const std::string& object_name);
|
||||
|
||||
Symbol* GetSymbolFromName(std::string_view name);
|
||||
std::vector<Symbol*> GetSymbolsFromName(std::string_view name);
|
||||
Symbol* GetSymbolFromHash(u32 hash);
|
||||
std::vector<Symbol*> GetSymbolsFromHash(u32 hash);
|
||||
const Symbol* GetSymbolFromName(std::string_view name) const;
|
||||
std::vector<const Symbol*> GetSymbolsFromName(std::string_view name) const;
|
||||
const Symbol* GetSymbolFromHash(u32 hash) const;
|
||||
std::vector<const Symbol*> GetSymbolsFromHash(u32 hash) const;
|
||||
|
||||
template <typename F>
|
||||
void ForEachSymbol(F f) const
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
for (const auto& [addr, symbol] : m_functions)
|
||||
f(symbol);
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
void ForEachSymbolWithMutation(F f)
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
for (auto& [addr, symbol] : m_functions)
|
||||
{
|
||||
f(symbol);
|
||||
ASSERT_MSG(COMMON, addr == symbol.address, "Symbol address was unexpectedly changed");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
void ForEachNote(F f) const
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
for (const auto& [addr, note] : m_notes)
|
||||
f(note);
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
void ForEachNoteWithMutation(F f)
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
for (auto& [addr, note] : m_notes)
|
||||
{
|
||||
f(note);
|
||||
ASSERT_MSG(COMMON, addr == note.address, "Note address was unexpectedly changed");
|
||||
}
|
||||
}
|
||||
|
||||
const XFuncMap& Symbols() const { return m_functions; }
|
||||
const XNoteMap& Notes() const { return m_notes; }
|
||||
XFuncMap& AccessSymbols() { return m_functions; }
|
||||
bool IsEmpty() const;
|
||||
bool Clear(const char* prefix = "");
|
||||
void List();
|
||||
void Index();
|
||||
|
||||
protected:
|
||||
static void Index(XFuncMap* functions);
|
||||
|
||||
XFuncMap m_functions;
|
||||
XNoteMap m_notes;
|
||||
XFuncPtrMap m_checksum_to_function;
|
||||
std::string m_map_name;
|
||||
mutable std::recursive_mutex m_mutex;
|
||||
};
|
||||
} // namespace Common
|
||||
|
|
|
@ -192,8 +192,8 @@ void AchievementManager::LoadGame(const DiscIO::Volume* volume)
|
|||
std::lock_guard lg{m_lock};
|
||||
#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
|
||||
const auto& names = volume->GetLongNames();
|
||||
if (names.contains(DiscIO::Language::English))
|
||||
m_title_estimate = names.at(DiscIO::Language::English);
|
||||
if (const auto it = names.find(DiscIO::Language::English); it != names.end())
|
||||
m_title_estimate = it->second;
|
||||
else if (!names.empty())
|
||||
m_title_estimate = names.begin()->second;
|
||||
else
|
||||
|
|
|
@ -89,8 +89,8 @@ public:
|
|||
static constexpr std::string_view BLUE = "#0B71C1";
|
||||
static constexpr std::string_view APPROVED_LIST_FILENAME = "ApprovedInis.json";
|
||||
static const inline Common::SHA1::Digest APPROVED_LIST_HASH = {
|
||||
0x6D, 0x91, 0xF5, 0xC1, 0xE2, 0x4C, 0xC3, 0x39, 0xF5, 0x7F,
|
||||
0xEC, 0xA9, 0x8C, 0xA9, 0xBD, 0x61, 0x28, 0x54, 0x11, 0x62};
|
||||
0x29, 0x4C, 0xBD, 0x08, 0xF0, 0x5F, 0x47, 0x94, 0xC9, 0xB8,
|
||||
0x05, 0x2E, 0x5C, 0xD6, 0x14, 0x48, 0xFA, 0x07, 0xE8, 0x53};
|
||||
|
||||
struct LeaderboardEntry
|
||||
{
|
||||
|
|
|
@ -643,8 +643,10 @@ bool CBoot::BootUp(Core::System& system, const Core::CPUThreadGuard& guard,
|
|||
SetDisc(system.GetDVDInterface(), DiscIO::CreateDisc(ipl.disc->path),
|
||||
ipl.disc->auto_disc_change_paths);
|
||||
}
|
||||
|
||||
AchievementManager::GetInstance().LoadGame(nullptr);
|
||||
else
|
||||
{
|
||||
AchievementManager::GetInstance().LoadGame(nullptr);
|
||||
}
|
||||
|
||||
SConfig::OnTitleDirectlyBooted(guard);
|
||||
return true;
|
||||
|
|
|
@ -680,7 +680,7 @@ static void EmuThread(Core::System& system, std::unique_ptr<BootParameters> boot
|
|||
// Set or get the running state
|
||||
|
||||
void SetState(Core::System& system, State state, bool report_state_change,
|
||||
bool initial_execution_state)
|
||||
bool override_achievement_restrictions)
|
||||
{
|
||||
// State cannot be controlled until the CPU Thread is operational
|
||||
if (s_state.load() != State::Running)
|
||||
|
@ -690,7 +690,7 @@ void SetState(Core::System& system, State state, bool report_state_change,
|
|||
{
|
||||
case State::Paused:
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
if (!initial_execution_state && !AchievementManager::GetInstance().CanPause())
|
||||
if (!override_achievement_restrictions && !AchievementManager::GetInstance().CanPause())
|
||||
return;
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
// NOTE: GetState() will return State::Paused immediately, even before anything has
|
||||
|
|
|
@ -146,7 +146,7 @@ bool WantsDeterminism();
|
|||
|
||||
// [NOT THREADSAFE] For use by Host only
|
||||
void SetState(Core::System& system, State state, bool report_state_change = true,
|
||||
bool initial_execution_state = false);
|
||||
bool override_achievement_restrictions = false);
|
||||
State GetState(Core::System& system);
|
||||
|
||||
void SaveScreenShot();
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
#include <cstddef>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
|
@ -104,7 +103,7 @@ public:
|
|||
{
|
||||
return 0xFFFFFFFF;
|
||||
}
|
||||
virtual std::string_view GetDescription(u32 /*address*/) const = 0;
|
||||
virtual std::string GetDescription(u32 /*address*/) const = 0;
|
||||
virtual void Clear(const CPUThreadGuard& guard) = 0;
|
||||
};
|
||||
} // namespace Core
|
||||
|
|
|
@ -80,7 +80,7 @@ bool GetCallstack(const Core::CPUThreadGuard& guard, std::vector<CallstackEntry>
|
|||
});
|
||||
|
||||
WalkTheStack(guard, [&output, &ppc_symbol_db](u32 func_addr) {
|
||||
std::string_view func_desc = ppc_symbol_db.GetDescription(func_addr);
|
||||
std::string func_desc = ppc_symbol_db.GetDescription(func_addr);
|
||||
if (func_desc.empty() || func_desc == "Invalid")
|
||||
func_desc = "(unknown)";
|
||||
|
||||
|
@ -107,14 +107,14 @@ void PrintCallstack(const Core::CPUThreadGuard& guard, Common::Log::LogType type
|
|||
GENERIC_LOG_FMT(type, level, " LR = 0 - this is bad");
|
||||
}
|
||||
|
||||
if (const std::string_view lr_desc = ppc_symbol_db.GetDescription(LR(ppc_state));
|
||||
if (const std::string lr_desc = ppc_symbol_db.GetDescription(LR(ppc_state));
|
||||
lr_desc != ppc_symbol_db.GetDescription(ppc_state.pc))
|
||||
{
|
||||
GENERIC_LOG_FMT(type, level, " * {} [ LR = {:08x} ]", lr_desc, LR(ppc_state));
|
||||
}
|
||||
|
||||
WalkTheStack(guard, [type, level, &ppc_symbol_db](u32 func_addr) {
|
||||
std::string_view func_desc = ppc_symbol_db.GetDescription(func_addr);
|
||||
std::string func_desc = ppc_symbol_db.GetDescription(func_addr);
|
||||
if (func_desc.empty() || func_desc == "Invalid")
|
||||
func_desc = "(unknown)";
|
||||
GENERIC_LOG_FMT(type, level, " * {} [ addr = {:08x} ]", func_desc, func_addr);
|
||||
|
|
|
@ -439,7 +439,7 @@ u32 PPCDebugInterface::GetColor(const Core::CPUThreadGuard* guard, u32 address)
|
|||
}
|
||||
// =============
|
||||
|
||||
std::string_view PPCDebugInterface::GetDescription(u32 address) const
|
||||
std::string PPCDebugInterface::GetDescription(u32 address) const
|
||||
{
|
||||
return m_ppc_symbol_db.GetDescription(address);
|
||||
}
|
||||
|
|
|
@ -102,7 +102,7 @@ public:
|
|||
void Step() override {}
|
||||
void RunTo(u32 address) override;
|
||||
u32 GetColor(const Core::CPUThreadGuard* guard, u32 address) const override;
|
||||
std::string_view GetDescription(u32 address) const override;
|
||||
std::string GetDescription(u32 address) const override;
|
||||
|
||||
std::shared_ptr<Core::NetworkCaptureLogger> NetworkLogger();
|
||||
|
||||
|
|
|
@ -381,7 +381,7 @@ void RSOView::Apply(const Core::CPUThreadGuard& guard, PPCSymbolDB* symbol_db) c
|
|||
u32 address = GetExportAddress(rso_export);
|
||||
if (address != 0)
|
||||
{
|
||||
Common::Symbol* symbol = symbol_db->AddFunction(guard, address);
|
||||
const Common::Symbol* symbol = symbol_db->AddFunction(guard, address);
|
||||
if (!symbol)
|
||||
symbol = symbol_db->GetSymbolFromAddr(address);
|
||||
|
||||
|
@ -389,8 +389,7 @@ void RSOView::Apply(const Core::CPUThreadGuard& guard, PPCSymbolDB* symbol_db) c
|
|||
if (symbol)
|
||||
{
|
||||
// Function symbol
|
||||
symbol->Rename(export_name);
|
||||
symbol->object_name = rso_name;
|
||||
symbol_db->RenameSymbol(*symbol, export_name, rso_name);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -269,7 +269,7 @@ private:
|
|||
|
||||
u8 revision_id = 0; // 0xf0
|
||||
u8 interrupt_mask = 0;
|
||||
u8 interrupt = 0;
|
||||
std::atomic<u8> interrupt = 0;
|
||||
u16 device_id = 0xD107;
|
||||
u8 acstart = 0x4E;
|
||||
u32 hash_challenge = 0;
|
||||
|
|
|
@ -65,31 +65,11 @@ void Nunchuk::BuildDesiredExtensionState(DesiredExtensionState* target_state)
|
|||
DataFormat nc_data = {};
|
||||
|
||||
// stick
|
||||
bool override_occurred = false;
|
||||
const ControllerEmu::AnalogStick::StateData stick_state =
|
||||
m_stick->GetState(m_input_override_function, &override_occurred);
|
||||
m_stick->GetState(m_input_override_function);
|
||||
nc_data.jx = MapFloat<u8>(stick_state.x, STICK_CENTER, 0, STICK_RANGE);
|
||||
nc_data.jy = MapFloat<u8>(stick_state.y, STICK_CENTER, 0, STICK_RANGE);
|
||||
|
||||
if (!override_occurred)
|
||||
{
|
||||
// Some terribly coded games check whether to move with a check like
|
||||
//
|
||||
// if (x != 0 && y != 0)
|
||||
// do_movement(x, y);
|
||||
//
|
||||
// With keyboard controls, these games break if you simply hit one
|
||||
// of the axes. Adjust this if you're hitting one of the axes so that
|
||||
// we slightly tweak the other axis.
|
||||
if (nc_data.jx != STICK_CENTER || nc_data.jy != STICK_CENTER)
|
||||
{
|
||||
if (nc_data.jx == STICK_CENTER)
|
||||
++nc_data.jx;
|
||||
if (nc_data.jy == STICK_CENTER)
|
||||
++nc_data.jy;
|
||||
}
|
||||
}
|
||||
|
||||
// buttons
|
||||
u8 buttons = 0;
|
||||
m_buttons->GetState(&buttons, nunchuk_button_bitmasks.data(), m_input_override_function);
|
||||
|
|
|
@ -964,8 +964,9 @@ s32 WiiSockMan::NewSocket(s32 af, s32 type, s32 protocol)
|
|||
|
||||
s32 WiiSockMan::GetHostSocket(s32 wii_fd) const
|
||||
{
|
||||
if (WiiSockets.contains(wii_fd))
|
||||
return WiiSockets.at(wii_fd).fd;
|
||||
auto socket_entry = WiiSockets.find(wii_fd);
|
||||
if (socket_entry != WiiSockets.end())
|
||||
return socket_entry->second.fd;
|
||||
return -EBADF;
|
||||
}
|
||||
|
||||
|
|
|
@ -186,9 +186,10 @@ FigureData SkylanderFigure::GetData() const
|
|||
|
||||
auto filter = std::make_pair(figure_data.figure_id, figure_data.variant_id);
|
||||
Type type = Type::Item;
|
||||
if (IOS::HLE::USB::list_skylanders.contains(filter))
|
||||
if (const auto it = IOS::HLE::USB::list_skylanders.find(filter);
|
||||
it != IOS::HLE::USB::list_skylanders.end())
|
||||
{
|
||||
auto found = IOS::HLE::USB::list_skylanders.at(filter);
|
||||
auto found = it->second;
|
||||
type = found.type;
|
||||
}
|
||||
|
||||
|
|
|
@ -176,9 +176,9 @@ std::optional<IPCReply> OH0::RegisterRemovalHook(const u64 device_id, const IOCt
|
|||
{
|
||||
std::lock_guard lock{m_hooks_mutex};
|
||||
// IOS only allows a single device removal hook.
|
||||
if (m_removal_hooks.contains(device_id))
|
||||
const bool inserted = m_removal_hooks.try_emplace(device_id, request.address).second;
|
||||
if (!inserted)
|
||||
return IPCReply(IPC_EEXIST);
|
||||
m_removal_hooks.insert({device_id, request.address});
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
|
|
|
@ -171,16 +171,20 @@ void USB_HIDv4::OnDeviceChange(ChangeEvent event, std::shared_ptr<USB::Device> d
|
|||
std::lock_guard id_map_lock{m_id_map_mutex};
|
||||
if (event == ChangeEvent::Inserted)
|
||||
{
|
||||
const auto id = device->GetId();
|
||||
s32 new_id = 0;
|
||||
while (m_ios_ids.contains(new_id))
|
||||
while (!m_ios_ids.emplace(new_id, id).second)
|
||||
++new_id;
|
||||
m_ios_ids[new_id] = device->GetId();
|
||||
m_device_ids[device->GetId()] = new_id;
|
||||
|
||||
m_device_ids[id] = new_id;
|
||||
}
|
||||
else if (event == ChangeEvent::Removed && m_device_ids.contains(device->GetId()))
|
||||
else if (event == ChangeEvent::Removed)
|
||||
{
|
||||
m_ios_ids.erase(m_device_ids.at(device->GetId()));
|
||||
m_device_ids.erase(device->GetId());
|
||||
if (const auto it = m_device_ids.find(device->GetId()); it != m_device_ids.end())
|
||||
{
|
||||
m_ios_ids.erase(it->second);
|
||||
m_device_ids.erase(it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,8 +10,8 @@
|
|||
#include <libusb.h>
|
||||
#endif
|
||||
|
||||
#include "Common/Assert.h"
|
||||
#include "Common/Flag.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/StringUtil.h"
|
||||
#include "Common/Thread.h"
|
||||
|
||||
|
@ -24,9 +24,11 @@ public:
|
|||
Impl()
|
||||
{
|
||||
const int ret = libusb_init(&m_context);
|
||||
ASSERT_MSG(IOS_USB, ret == LIBUSB_SUCCESS, "Failed to init libusb: {}", ErrorWrap(ret));
|
||||
if (ret != LIBUSB_SUCCESS)
|
||||
{
|
||||
ERROR_LOG_FMT(IOS_USB, "Failed to init libusb: {}", ErrorWrap(ret));
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
const int usbdk_ret = libusb_set_option(m_context, LIBUSB_OPTION_USE_USBDK);
|
||||
|
|
|
@ -2392,8 +2392,8 @@ void NetPlayClient::RequestGolfControl()
|
|||
std::string NetPlayClient::GetCurrentGolfer()
|
||||
{
|
||||
std::lock_guard lkp(m_crit.players);
|
||||
if (m_players.contains(m_current_golfer))
|
||||
return m_players[m_current_golfer].name;
|
||||
if (const auto it = m_players.find(m_current_golfer); it != m_players.end())
|
||||
return it->second.name;
|
||||
return "";
|
||||
}
|
||||
|
||||
|
|
|
@ -290,8 +290,8 @@ void NetPlayServer::ThreadFunc()
|
|||
auto& e = m_async_queue.Front();
|
||||
if (e.target_mode == TargetMode::Only)
|
||||
{
|
||||
if (m_players.contains(e.target_pid))
|
||||
Send(m_players.at(e.target_pid).socket, e.packet, e.channel_id);
|
||||
if (const auto it = m_players.find(e.target_pid); it != m_players.end())
|
||||
Send(it->second.socket, e.packet, e.channel_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -794,9 +794,10 @@ unsigned int NetPlayServer::OnData(sf::Packet& packet, Client& player)
|
|||
u32 cid;
|
||||
packet >> cid;
|
||||
|
||||
if (m_chunked_data_complete_count.contains(cid))
|
||||
if (const auto it = m_chunked_data_complete_count.find(cid);
|
||||
it != m_chunked_data_complete_count.end())
|
||||
{
|
||||
m_chunked_data_complete_count[cid]++;
|
||||
it->second++;
|
||||
m_chunked_data_complete_event.Set();
|
||||
}
|
||||
}
|
||||
|
@ -839,8 +840,11 @@ unsigned int NetPlayServer::OnData(sf::Packet& packet, Client& player)
|
|||
if (m_host_input_authority)
|
||||
{
|
||||
// Prevent crash before game stop if the golfer disconnects
|
||||
if (m_current_golfer != 0 && m_players.contains(m_current_golfer))
|
||||
Send(m_players.at(m_current_golfer).socket, spac);
|
||||
if (m_current_golfer != 0)
|
||||
{
|
||||
if (const auto it = m_players.find(m_current_golfer); it != m_players.end())
|
||||
Send(it->second.socket, spac);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -252,6 +252,7 @@ MemChecks::TMemChecksStr MemChecks::GetStrings() const
|
|||
void MemChecks::AddFromStrings(const TMemChecksStr& mc_strings)
|
||||
{
|
||||
const Core::CPUThreadGuard guard(m_system);
|
||||
DelayedMemCheckUpdate delayed_update(this);
|
||||
|
||||
for (const std::string& mc_string : mc_strings)
|
||||
{
|
||||
|
@ -279,13 +280,11 @@ void MemChecks::AddFromStrings(const TMemChecksStr& mc_strings)
|
|||
mc.condition = Expression::TryParse(condition);
|
||||
}
|
||||
|
||||
Add(std::move(mc), false);
|
||||
delayed_update |= Add(std::move(mc));
|
||||
}
|
||||
|
||||
Update();
|
||||
}
|
||||
|
||||
void MemChecks::Add(TMemCheck memory_check, bool update)
|
||||
DelayedMemCheckUpdate MemChecks::Add(TMemCheck memory_check)
|
||||
{
|
||||
const Core::CPUThreadGuard guard(m_system);
|
||||
|
||||
|
@ -304,8 +303,7 @@ void MemChecks::Add(TMemCheck memory_check, bool update)
|
|||
m_mem_checks.emplace_back(std::move(memory_check));
|
||||
}
|
||||
|
||||
if (update)
|
||||
Update();
|
||||
return DelayedMemCheckUpdate(this, true);
|
||||
}
|
||||
|
||||
bool MemChecks::ToggleEnable(u32 address)
|
||||
|
@ -319,20 +317,17 @@ bool MemChecks::ToggleEnable(u32 address)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool MemChecks::Remove(u32 address, bool update)
|
||||
DelayedMemCheckUpdate MemChecks::Remove(u32 address)
|
||||
{
|
||||
const auto iter = std::ranges::find(m_mem_checks, address, &TMemCheck::start_address);
|
||||
|
||||
if (iter == m_mem_checks.cend())
|
||||
return false;
|
||||
return DelayedMemCheckUpdate(this, false);
|
||||
|
||||
const Core::CPUThreadGuard guard(m_system);
|
||||
m_mem_checks.erase(iter);
|
||||
|
||||
if (update)
|
||||
Update();
|
||||
|
||||
return true;
|
||||
return DelayedMemCheckUpdate(this, true);
|
||||
}
|
||||
|
||||
void MemChecks::Clear()
|
||||
|
|
|
@ -97,6 +97,8 @@ private:
|
|||
Core::System& m_system;
|
||||
};
|
||||
|
||||
class DelayedMemCheckUpdate;
|
||||
|
||||
// Memory breakpoints
|
||||
class MemChecks
|
||||
{
|
||||
|
@ -115,13 +117,13 @@ public:
|
|||
TMemChecksStr GetStrings() const;
|
||||
void AddFromStrings(const TMemChecksStr& mc_strings);
|
||||
|
||||
void Add(TMemCheck memory_check, bool update = true);
|
||||
DelayedMemCheckUpdate Add(TMemCheck memory_check);
|
||||
|
||||
bool ToggleEnable(u32 address);
|
||||
|
||||
TMemCheck* GetMemCheck(u32 address, size_t size = 1);
|
||||
bool OverlapsMemcheck(u32 address, u32 length) const;
|
||||
bool Remove(u32 address, bool update = true);
|
||||
DelayedMemCheckUpdate Remove(u32 address);
|
||||
|
||||
void Update();
|
||||
void Clear();
|
||||
|
@ -132,3 +134,39 @@ private:
|
|||
Core::System& m_system;
|
||||
bool m_mem_breakpoints_set = false;
|
||||
};
|
||||
|
||||
class DelayedMemCheckUpdate final
|
||||
{
|
||||
public:
|
||||
DelayedMemCheckUpdate(MemChecks* memchecks, bool update_needed = false)
|
||||
: m_memchecks(memchecks), m_update_needed(update_needed)
|
||||
{
|
||||
}
|
||||
|
||||
DelayedMemCheckUpdate(const DelayedMemCheckUpdate&) = delete;
|
||||
DelayedMemCheckUpdate(DelayedMemCheckUpdate&& other) = delete;
|
||||
DelayedMemCheckUpdate& operator=(const DelayedMemCheckUpdate&) = delete;
|
||||
DelayedMemCheckUpdate& operator=(DelayedMemCheckUpdate&& other) = delete;
|
||||
|
||||
~DelayedMemCheckUpdate()
|
||||
{
|
||||
if (m_update_needed)
|
||||
m_memchecks->Update();
|
||||
}
|
||||
|
||||
DelayedMemCheckUpdate& operator|=(DelayedMemCheckUpdate&& other)
|
||||
{
|
||||
if (m_memchecks == other.m_memchecks)
|
||||
{
|
||||
m_update_needed |= other.m_update_needed;
|
||||
other.m_update_needed = false;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator bool() const { return m_update_needed; }
|
||||
|
||||
private:
|
||||
MemChecks* m_memchecks;
|
||||
bool m_update_needed;
|
||||
};
|
||||
|
|
|
@ -172,12 +172,12 @@ static void RemoveBreakpoint(BreakpointType type, u32 addr, u32 len)
|
|||
else
|
||||
{
|
||||
auto& memchecks = Core::System::GetInstance().GetPowerPC().GetMemChecks();
|
||||
DelayedMemCheckUpdate delayed_update(&memchecks);
|
||||
while (memchecks.GetMemCheck(addr, len) != nullptr)
|
||||
{
|
||||
memchecks.Remove(addr, false);
|
||||
delayed_update |= memchecks.Remove(addr);
|
||||
INFO_LOG_FMT(GDB_STUB, "gdb: removed a memcheck: {:08x} bytes at {:08x}", len, addr);
|
||||
}
|
||||
memchecks.Update();
|
||||
}
|
||||
Host_PPCBreakpointsChanged();
|
||||
}
|
||||
|
|
|
@ -40,9 +40,10 @@ const void* ConstantPool::GetConstant(const void* value, size_t element_size, si
|
|||
size_t index)
|
||||
{
|
||||
const size_t value_size = element_size * num_elements;
|
||||
auto iter = m_const_info.find(value);
|
||||
const auto [iter, inserted] = m_const_info.emplace(value, ConstantInfo{});
|
||||
ConstantInfo& info = iter->second;
|
||||
|
||||
if (iter == m_const_info.end())
|
||||
if (inserted)
|
||||
{
|
||||
void* ptr = std::align(ALIGNMENT, value_size, m_current_ptr, m_remaining_size);
|
||||
ASSERT_MSG(DYNA_REC, ptr, "Constant pool has run out of space.");
|
||||
|
@ -51,10 +52,9 @@ const void* ConstantPool::GetConstant(const void* value, size_t element_size, si
|
|||
m_remaining_size -= value_size;
|
||||
|
||||
std::memcpy(ptr, value, value_size);
|
||||
iter = m_const_info.emplace(std::make_pair(value, ConstantInfo{ptr, value_size})).first;
|
||||
info = ConstantInfo{ptr, value_size};
|
||||
}
|
||||
|
||||
const ConstantInfo& info = iter->second;
|
||||
ASSERT_MSG(DYNA_REC, info.m_size == value_size, "Constant has incorrect size in constant pool.");
|
||||
u8* location = static_cast<u8*>(info.m_location);
|
||||
return location + element_size * index;
|
||||
|
|
|
@ -187,7 +187,7 @@ void JitBaseBlockCache::FinalizeBlock(JitBlock& block, bool block_link,
|
|||
LinkBlock(block);
|
||||
}
|
||||
|
||||
Common::Symbol* symbol = nullptr;
|
||||
const Common::Symbol* symbol = nullptr;
|
||||
if (Common::JitRegister::IsEnabled() &&
|
||||
(symbol = m_jit.m_ppc_symbol_db.GetSymbolFromAddr(block.effectiveAddress)) != nullptr)
|
||||
{
|
||||
|
|
|
@ -346,10 +346,10 @@ static void FindFunctionsFromHandlers(const Core::CPUThreadGuard& guard, PPCSymb
|
|||
if (read_result.valid && PPCTables::IsValidInstruction(read_result.hex, entry.first))
|
||||
{
|
||||
// Check if this function is already mapped
|
||||
Common::Symbol* f = func_db->AddFunction(guard, entry.first);
|
||||
const Common::Symbol* f = func_db->AddFunction(guard, entry.first);
|
||||
if (!f)
|
||||
continue;
|
||||
f->Rename(entry.second);
|
||||
func_db->RenameSymbol(*f, entry.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -359,8 +359,8 @@ static void FindFunctionsAfterReturnInstruction(const Core::CPUThreadGuard& guar
|
|||
{
|
||||
std::vector<u32> funcAddrs;
|
||||
|
||||
for (const auto& func : func_db->Symbols())
|
||||
funcAddrs.push_back(func.second.address + func.second.size);
|
||||
func_db->ForEachSymbol(
|
||||
[&](const Common::Symbol& symbol) { funcAddrs.push_back(symbol.address + symbol.size); });
|
||||
|
||||
auto& mmu = guard.GetSystem().GetMMU();
|
||||
for (u32& location : funcAddrs)
|
||||
|
@ -380,7 +380,7 @@ static void FindFunctionsAfterReturnInstruction(const Core::CPUThreadGuard& guar
|
|||
if (read_result.valid && PPCTables::IsValidInstruction(read_result.hex, location))
|
||||
{
|
||||
// check if this function is already mapped
|
||||
Common::Symbol* f = func_db->AddFunction(guard, location);
|
||||
const Common::Symbol* f = func_db->AddFunction(guard, location);
|
||||
if (!f)
|
||||
break;
|
||||
else
|
||||
|
@ -407,45 +407,45 @@ void FindFunctions(const Core::CPUThreadGuard& guard, u32 startAddr, u32 endAddr
|
|||
int numLeafs = 0, numNice = 0, numUnNice = 0;
|
||||
int numTimer = 0, numRFI = 0, numStraightLeaf = 0;
|
||||
int leafSize = 0, niceSize = 0, unniceSize = 0;
|
||||
for (auto& func : func_db->AccessSymbols())
|
||||
{
|
||||
if (func.second.address == 4)
|
||||
func_db->ForEachSymbolWithMutation([&](Common::Symbol& f) {
|
||||
if (f.address == 4)
|
||||
{
|
||||
WARN_LOG_FMT(SYMBOLS, "Weird function");
|
||||
continue;
|
||||
}
|
||||
AnalyzeFunction2(func_db, &(func.second));
|
||||
Common::Symbol& f = func.second;
|
||||
if (f.name.substr(0, 3) == "zzz")
|
||||
{
|
||||
if (f.flags & Common::FFLAG_LEAF)
|
||||
f.Rename(f.name + "_leaf");
|
||||
if (f.flags & Common::FFLAG_STRAIGHT)
|
||||
f.Rename(f.name + "_straight");
|
||||
}
|
||||
if (f.flags & Common::FFLAG_LEAF)
|
||||
{
|
||||
numLeafs++;
|
||||
leafSize += f.size;
|
||||
}
|
||||
else if (f.flags & Common::FFLAG_ONLYCALLSNICELEAFS)
|
||||
{
|
||||
numNice++;
|
||||
niceSize += f.size;
|
||||
}
|
||||
else
|
||||
{
|
||||
numUnNice++;
|
||||
unniceSize += f.size;
|
||||
}
|
||||
AnalyzeFunction2(func_db, &f);
|
||||
if (f.name.substr(0, 3) == "zzz")
|
||||
{
|
||||
if (f.flags & Common::FFLAG_LEAF)
|
||||
f.Rename(f.name + "_leaf");
|
||||
if (f.flags & Common::FFLAG_STRAIGHT)
|
||||
f.Rename(f.name + "_straight");
|
||||
}
|
||||
if (f.flags & Common::FFLAG_LEAF)
|
||||
{
|
||||
numLeafs++;
|
||||
leafSize += f.size;
|
||||
}
|
||||
else if (f.flags & Common::FFLAG_ONLYCALLSNICELEAFS)
|
||||
{
|
||||
numNice++;
|
||||
niceSize += f.size;
|
||||
}
|
||||
else
|
||||
{
|
||||
numUnNice++;
|
||||
unniceSize += f.size;
|
||||
}
|
||||
|
||||
if (f.flags & Common::FFLAG_TIMERINSTRUCTIONS)
|
||||
numTimer++;
|
||||
if (f.flags & Common::FFLAG_RFI)
|
||||
numRFI++;
|
||||
if ((f.flags & Common::FFLAG_STRAIGHT) && (f.flags & Common::FFLAG_LEAF))
|
||||
numStraightLeaf++;
|
||||
}
|
||||
if (f.flags & Common::FFLAG_TIMERINSTRUCTIONS)
|
||||
numTimer++;
|
||||
if (f.flags & Common::FFLAG_RFI)
|
||||
numRFI++;
|
||||
if ((f.flags & Common::FFLAG_STRAIGHT) && (f.flags & Common::FFLAG_LEAF))
|
||||
numStraightLeaf++;
|
||||
}
|
||||
});
|
||||
if (numLeafs == 0)
|
||||
leafSize = 0;
|
||||
else
|
||||
|
|
|
@ -35,8 +35,10 @@ PPCSymbolDB::PPCSymbolDB() = default;
|
|||
PPCSymbolDB::~PPCSymbolDB() = default;
|
||||
|
||||
// Adds the function to the list, unless it's already there
|
||||
Common::Symbol* PPCSymbolDB::AddFunction(const Core::CPUThreadGuard& guard, u32 start_addr)
|
||||
const Common::Symbol* PPCSymbolDB::AddFunction(const Core::CPUThreadGuard& guard, u32 start_addr)
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
|
||||
// It's already in the list
|
||||
if (m_functions.contains(start_addr))
|
||||
return nullptr;
|
||||
|
@ -56,8 +58,18 @@ void PPCSymbolDB::AddKnownSymbol(const Core::CPUThreadGuard& guard, u32 startAdd
|
|||
const std::string& name, const std::string& object_name,
|
||||
Common::Symbol::Type type)
|
||||
{
|
||||
auto iter = m_functions.find(startAddr);
|
||||
if (iter != m_functions.end())
|
||||
std::lock_guard lock(m_mutex);
|
||||
AddKnownSymbol(guard, startAddr, size, name, object_name, type, &m_functions,
|
||||
&m_checksum_to_function);
|
||||
}
|
||||
|
||||
void PPCSymbolDB::AddKnownSymbol(const Core::CPUThreadGuard& guard, u32 startAddr, u32 size,
|
||||
const std::string& name, const std::string& object_name,
|
||||
Common::Symbol::Type type, XFuncMap* functions,
|
||||
XFuncPtrMap* checksum_to_function)
|
||||
{
|
||||
auto iter = functions->find(startAddr);
|
||||
if (iter != functions->end())
|
||||
{
|
||||
// already got it, let's just update name, checksum & size to be sure.
|
||||
Common::Symbol* tempfunc = &iter->second;
|
||||
|
@ -70,7 +82,7 @@ void PPCSymbolDB::AddKnownSymbol(const Core::CPUThreadGuard& guard, u32 startAdd
|
|||
else
|
||||
{
|
||||
// new symbol. run analyze.
|
||||
auto& new_symbol = m_functions.emplace(startAddr, name).first->second;
|
||||
auto& new_symbol = functions->emplace(startAddr, name).first->second;
|
||||
new_symbol.object_name = object_name;
|
||||
new_symbol.type = type;
|
||||
new_symbol.address = startAddr;
|
||||
|
@ -85,7 +97,7 @@ void PPCSymbolDB::AddKnownSymbol(const Core::CPUThreadGuard& guard, u32 startAdd
|
|||
name, size, new_symbol.size);
|
||||
new_symbol.size = size;
|
||||
}
|
||||
m_checksum_to_function[new_symbol.hash].insert(&new_symbol);
|
||||
(*checksum_to_function)[new_symbol.hash].insert(&new_symbol);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -96,9 +108,15 @@ void PPCSymbolDB::AddKnownSymbol(const Core::CPUThreadGuard& guard, u32 startAdd
|
|||
|
||||
void PPCSymbolDB::AddKnownNote(u32 start_addr, u32 size, const std::string& name)
|
||||
{
|
||||
auto iter = m_notes.find(start_addr);
|
||||
std::lock_guard lock(m_mutex);
|
||||
AddKnownNote(start_addr, size, name, &m_notes);
|
||||
}
|
||||
|
||||
if (iter != m_notes.end())
|
||||
void PPCSymbolDB::AddKnownNote(u32 start_addr, u32 size, const std::string& name, XNoteMap* notes)
|
||||
{
|
||||
auto iter = notes->find(start_addr);
|
||||
|
||||
if (iter != notes->end())
|
||||
{
|
||||
// Already got it, just update the name and size.
|
||||
Common::Note* tempfunc = &iter->second;
|
||||
|
@ -112,35 +130,38 @@ void PPCSymbolDB::AddKnownNote(u32 start_addr, u32 size, const std::string& name
|
|||
tf.address = start_addr;
|
||||
tf.size = size;
|
||||
|
||||
m_notes[start_addr] = tf;
|
||||
(*notes)[start_addr] = tf;
|
||||
}
|
||||
}
|
||||
|
||||
void PPCSymbolDB::DetermineNoteLayers()
|
||||
{
|
||||
if (m_notes.empty())
|
||||
std::lock_guard lock(m_mutex);
|
||||
DetermineNoteLayers(&m_notes);
|
||||
}
|
||||
|
||||
void PPCSymbolDB::DetermineNoteLayers(XNoteMap* notes)
|
||||
{
|
||||
if (notes->empty())
|
||||
return;
|
||||
|
||||
for (auto& note : m_notes)
|
||||
for (auto& note : *notes)
|
||||
note.second.layer = 0;
|
||||
|
||||
for (auto iter = m_notes.begin(); iter != m_notes.end(); ++iter)
|
||||
for (auto iter = notes->begin(); iter != notes->end(); ++iter)
|
||||
{
|
||||
const u32 range = iter->second.address + iter->second.size;
|
||||
auto search = m_notes.lower_bound(range);
|
||||
auto search = notes->lower_bound(range);
|
||||
|
||||
while (--search != iter)
|
||||
search->second.layer += 1;
|
||||
}
|
||||
}
|
||||
|
||||
Common::Symbol* PPCSymbolDB::GetSymbolFromAddr(u32 addr)
|
||||
const Common::Symbol* PPCSymbolDB::GetSymbolFromAddr(u32 addr) const
|
||||
{
|
||||
// If m_functions is changing, there should be a PPCSymbolsChanged signal afterward. The signal
|
||||
// will re-update persistent symbol displays by calling this function. Only one-off calls to this
|
||||
// function, such as printing the symbol to console, should be affected by leaving early.
|
||||
std::unique_lock<std::mutex> lock(m_write_lock, std::try_to_lock);
|
||||
if (!lock.owns_lock() || m_functions.empty())
|
||||
std::lock_guard lock(m_mutex);
|
||||
if (m_functions.empty())
|
||||
return nullptr;
|
||||
|
||||
auto it = m_functions.lower_bound(addr);
|
||||
|
@ -162,10 +183,10 @@ Common::Symbol* PPCSymbolDB::GetSymbolFromAddr(u32 addr)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
Common::Note* PPCSymbolDB::GetNoteFromAddr(u32 addr)
|
||||
const Common::Note* PPCSymbolDB::GetNoteFromAddr(u32 addr) const
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_write_lock, std::try_to_lock);
|
||||
if (!lock.owns_lock() || m_notes.empty())
|
||||
std::lock_guard lock(m_mutex);
|
||||
if (m_notes.empty())
|
||||
return nullptr;
|
||||
|
||||
auto itn = m_notes.lower_bound(addr);
|
||||
|
@ -195,15 +216,17 @@ Common::Note* PPCSymbolDB::GetNoteFromAddr(u32 addr)
|
|||
|
||||
void PPCSymbolDB::DeleteFunction(u32 start_address)
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
m_functions.erase(start_address);
|
||||
}
|
||||
|
||||
void PPCSymbolDB::DeleteNote(u32 start_address)
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
m_notes.erase(start_address);
|
||||
}
|
||||
|
||||
std::string_view PPCSymbolDB::GetDescription(u32 addr)
|
||||
std::string PPCSymbolDB::GetDescription(u32 addr) const
|
||||
{
|
||||
if (const Common::Symbol* const symbol = GetSymbolFromAddr(addr))
|
||||
return symbol->name;
|
||||
|
@ -212,9 +235,7 @@ std::string_view PPCSymbolDB::GetDescription(u32 addr)
|
|||
|
||||
void PPCSymbolDB::FillInCallers()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_write_lock, std::try_to_lock);
|
||||
if (!lock.owns_lock())
|
||||
return;
|
||||
std::lock_guard lock(m_mutex);
|
||||
|
||||
for (auto& p : m_functions)
|
||||
{
|
||||
|
@ -247,6 +268,8 @@ void PPCSymbolDB::FillInCallers()
|
|||
|
||||
void PPCSymbolDB::PrintCalls(u32 funcAddr) const
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
|
||||
const auto iter = m_functions.find(funcAddr);
|
||||
if (iter == m_functions.end())
|
||||
{
|
||||
|
@ -268,6 +291,8 @@ void PPCSymbolDB::PrintCalls(u32 funcAddr) const
|
|||
|
||||
void PPCSymbolDB::PrintCallers(u32 funcAddr) const
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
|
||||
const auto iter = m_functions.find(funcAddr);
|
||||
if (iter == m_functions.end())
|
||||
return;
|
||||
|
@ -286,6 +311,8 @@ void PPCSymbolDB::PrintCallers(u32 funcAddr) const
|
|||
|
||||
void PPCSymbolDB::LogFunctionCall(u32 addr)
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
|
||||
auto iter = m_functions.find(addr);
|
||||
if (iter == m_functions.end())
|
||||
return;
|
||||
|
@ -314,25 +341,22 @@ bool PPCSymbolDB::FindMapFile(std::string* existing_map_file, std::string* writa
|
|||
return false;
|
||||
}
|
||||
|
||||
// Returns true if m_functions was changed.
|
||||
bool PPCSymbolDB::LoadMapOnBoot(const Core::CPUThreadGuard& guard)
|
||||
{
|
||||
// Loads from emuthread and can crash with main thread accessing the map. Any other loads will be
|
||||
// done on the main thread and should be safe. Returns true if m_functions was changed.
|
||||
std::lock_guard lock(m_write_lock);
|
||||
|
||||
std::string existing_map_file;
|
||||
if (!PPCSymbolDB::FindMapFile(&existing_map_file, nullptr))
|
||||
return Clear();
|
||||
|
||||
// If the map is already loaded (such as restarting the same game), skip reloading.
|
||||
if (!IsEmpty() && existing_map_file == m_map_name)
|
||||
return false;
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
// If the map is already loaded (such as restarting the same game), skip reloading.
|
||||
if (!IsEmpty() && existing_map_file == m_map_name)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Load map into cleared m_functions.
|
||||
bool changed = Clear();
|
||||
|
||||
if (!LoadMap(guard, existing_map_file))
|
||||
return changed;
|
||||
if (!LoadMap(guard, std::move(existing_map_file)))
|
||||
return Clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -344,14 +368,11 @@ bool PPCSymbolDB::LoadMapOnBoot(const Core::CPUThreadGuard& guard)
|
|||
// function names and addresses that have a BLR before the start and at the end, but ignore any that
|
||||
// don't, and then tell you how many were good and how many it ignored. That way you either find out
|
||||
// it is all good and use it, find out it is partly good and use the good part, or find out that
|
||||
// only
|
||||
// a handful of functions lined up by coincidence and then you can clear the symbols. In the future
|
||||
// I
|
||||
// want to make it smarter, so it checks that there are no BLRs in the middle of the function
|
||||
// (by checking the code length), and also make it cope with added functions in the middle or work
|
||||
// based on the order of the functions and their approximate length. Currently that process has to
|
||||
// be
|
||||
// done manually and is very tedious.
|
||||
// only a handful of functions lined up by coincidence and then you can clear the symbols. In the
|
||||
// future I want to make it smarter, so it checks that there are no BLRs in the middle of the
|
||||
// function (by checking the code length), and also make it cope with added functions in the middle
|
||||
// or work based on the order of the functions and their approximate length. Currently that process
|
||||
// has to be done manually and is very tedious.
|
||||
// The use case for separate handling of map files that aren't bad is that you usually want to also
|
||||
// load names that aren't functions(if included in the map file) without them being rejected as
|
||||
// invalid.
|
||||
|
@ -362,12 +383,16 @@ bool PPCSymbolDB::LoadMapOnBoot(const Core::CPUThreadGuard& guard)
|
|||
// This one can load both leftover map files on game discs (like Zelda), and mapfiles
|
||||
// produced by SaveSymbolMap below.
|
||||
// bad=true means carefully load map files that might not be from exactly the right version
|
||||
bool PPCSymbolDB::LoadMap(const Core::CPUThreadGuard& guard, const std::string& filename, bool bad)
|
||||
bool PPCSymbolDB::LoadMap(const Core::CPUThreadGuard& guard, std::string filename, bool bad)
|
||||
{
|
||||
File::IOFile f(filename, "r");
|
||||
if (!f)
|
||||
return false;
|
||||
|
||||
XFuncMap new_functions;
|
||||
XNoteMap new_notes;
|
||||
XFuncPtrMap checksum_to_function;
|
||||
|
||||
// Two columns are used by Super Smash Bros. Brawl Korean map file
|
||||
// Three columns are commonly used
|
||||
// Four columns are used in American Mensa Academy map files and perhaps other games
|
||||
|
@ -579,9 +604,14 @@ bool PPCSymbolDB::LoadMap(const Core::CPUThreadGuard& guard, const std::string&
|
|||
++good_count;
|
||||
|
||||
if (section_name == ".note")
|
||||
AddKnownNote(vaddress, size, name);
|
||||
{
|
||||
AddKnownNote(vaddress, size, name, &new_notes);
|
||||
}
|
||||
else
|
||||
AddKnownSymbol(guard, vaddress, size, name_string, object_filename_string, type);
|
||||
{
|
||||
AddKnownSymbol(guard, vaddress, size, name_string, object_filename_string, type,
|
||||
&new_functions, &checksum_to_function);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -590,10 +620,16 @@ bool PPCSymbolDB::LoadMap(const Core::CPUThreadGuard& guard, const std::string&
|
|||
}
|
||||
}
|
||||
|
||||
m_map_name = filename;
|
||||
Index(&new_functions);
|
||||
DetermineNoteLayers(&new_notes);
|
||||
FillInCallers();
|
||||
|
||||
std::lock_guard lock(m_mutex);
|
||||
std::swap(m_functions, new_functions);
|
||||
std::swap(m_notes, new_notes);
|
||||
std::swap(m_checksum_to_function, checksum_to_function);
|
||||
std::swap(m_map_name, filename);
|
||||
|
||||
Index();
|
||||
DetermineNoteLayers();
|
||||
NOTICE_LOG_FMT(SYMBOLS, "{} symbols loaded, {} symbols ignored.", good_count, bad_count);
|
||||
return true;
|
||||
}
|
||||
|
@ -605,6 +641,8 @@ bool PPCSymbolDB::SaveSymbolMap(const std::string& filename) const
|
|||
if (!file)
|
||||
return false;
|
||||
|
||||
std::lock_guard lock(m_mutex);
|
||||
|
||||
// Write .text section
|
||||
auto function_symbols =
|
||||
m_functions |
|
||||
|
@ -655,7 +693,7 @@ bool PPCSymbolDB::SaveSymbolMap(const std::string& filename) const
|
|||
return true;
|
||||
}
|
||||
|
||||
// Save code map (won't work if Core is running)
|
||||
// Save code map
|
||||
//
|
||||
// Notes:
|
||||
// - Dolphin doesn't load back code maps
|
||||
|
@ -670,6 +708,8 @@ bool PPCSymbolDB::SaveCodeMap(const Core::CPUThreadGuard& guard, const std::stri
|
|||
// Write ".text" at the top
|
||||
f.WriteString(".text\n");
|
||||
|
||||
std::lock_guard lock(m_mutex);
|
||||
|
||||
const auto& ppc_debug_interface = guard.GetSystem().GetPowerPC().GetDebugInterface();
|
||||
|
||||
u32 next_address = 0;
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
|
@ -22,25 +21,25 @@ public:
|
|||
PPCSymbolDB();
|
||||
~PPCSymbolDB() override;
|
||||
|
||||
Common::Symbol* AddFunction(const Core::CPUThreadGuard& guard, u32 start_addr) override;
|
||||
const Common::Symbol* AddFunction(const Core::CPUThreadGuard& guard, u32 start_addr) override;
|
||||
void AddKnownSymbol(const Core::CPUThreadGuard& guard, u32 startAddr, u32 size,
|
||||
const std::string& name, const std::string& object_name,
|
||||
Common::Symbol::Type type = Common::Symbol::Type::Function);
|
||||
void AddKnownNote(u32 start_addr, u32 size, const std::string& name);
|
||||
|
||||
Common::Symbol* GetSymbolFromAddr(u32 addr) override;
|
||||
const Common::Symbol* GetSymbolFromAddr(u32 addr) const override;
|
||||
bool NoteExists() const { return !m_notes.empty(); }
|
||||
Common::Note* GetNoteFromAddr(u32 addr);
|
||||
const Common::Note* GetNoteFromAddr(u32 addr) const;
|
||||
void DetermineNoteLayers();
|
||||
void DeleteFunction(u32 start_address);
|
||||
void DeleteNote(u32 start_address);
|
||||
|
||||
std::string_view GetDescription(u32 addr);
|
||||
std::string GetDescription(u32 addr) const;
|
||||
|
||||
void FillInCallers();
|
||||
|
||||
bool LoadMapOnBoot(const Core::CPUThreadGuard& guard);
|
||||
bool LoadMap(const Core::CPUThreadGuard& guard, const std::string& filename, bool bad = false);
|
||||
bool LoadMap(const Core::CPUThreadGuard& guard, std::string filename, bool bad = false);
|
||||
bool SaveSymbolMap(const std::string& filename) const;
|
||||
bool SaveCodeMap(const Core::CPUThreadGuard& guard, const std::string& filename) const;
|
||||
|
||||
|
@ -51,5 +50,11 @@ public:
|
|||
static bool FindMapFile(std::string* existing_map_file, std::string* writable_map_file);
|
||||
|
||||
private:
|
||||
std::mutex m_write_lock;
|
||||
static void AddKnownSymbol(const Core::CPUThreadGuard& guard, u32 startAddr, u32 size,
|
||||
const std::string& name, const std::string& object_name,
|
||||
Common::Symbol::Type type, XFuncMap* functions,
|
||||
XFuncPtrMap* checksum_to_function);
|
||||
static void AddKnownNote(u32 start_addr, u32 size, const std::string& name, XNoteMap* notes);
|
||||
|
||||
static void DetermineNoteLayers(XNoteMap* notes);
|
||||
};
|
||||
|
|
|
@ -160,20 +160,18 @@ bool MEGASignatureDB::Save(const std::string& file_path) const
|
|||
|
||||
void MEGASignatureDB::Apply(const Core::CPUThreadGuard& guard, PPCSymbolDB* symbol_db) const
|
||||
{
|
||||
for (auto& it : symbol_db->AccessSymbols())
|
||||
{
|
||||
auto& symbol = it.second;
|
||||
symbol_db->ForEachSymbol([&](const Common::Symbol& symbol) {
|
||||
for (const auto& sig : m_signatures)
|
||||
{
|
||||
if (Compare(guard, symbol.address, symbol.size, sig))
|
||||
{
|
||||
symbol.name = sig.name;
|
||||
symbol_db->RenameSymbol(symbol, sig.name);
|
||||
INFO_LOG_FMT(SYMBOLS, "Found {} at {:08x} (size: {:08x})!", sig.name, symbol.address,
|
||||
symbol.size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
symbol_db->Index();
|
||||
}
|
||||
|
||||
|
|
|
@ -128,7 +128,7 @@ void HashSignatureDB::Apply(const Core::CPUThreadGuard& guard, PPCSymbolDB* symb
|
|||
for (const auto& function : symbol_db->GetSymbolsFromHash(entry.first))
|
||||
{
|
||||
// Found the function. Let's rename it according to the symbol file.
|
||||
function->Rename(entry.second.name);
|
||||
symbol_db->RenameSymbol(*function, entry.second.name);
|
||||
if (entry.second.size == static_cast<unsigned int>(function->size))
|
||||
{
|
||||
INFO_LOG_FMT(SYMBOLS, "Found {} at {:08x} (size: {:08x})!", entry.second.name,
|
||||
|
@ -146,18 +146,17 @@ void HashSignatureDB::Apply(const Core::CPUThreadGuard& guard, PPCSymbolDB* symb
|
|||
|
||||
void HashSignatureDB::Populate(const PPCSymbolDB* symbol_db, const std::string& filter)
|
||||
{
|
||||
for (const auto& symbol : symbol_db->Symbols())
|
||||
{
|
||||
if ((filter.empty() && (!symbol.second.name.empty()) &&
|
||||
symbol.second.name.substr(0, 3) != "zz_" && symbol.second.name.substr(0, 1) != ".") ||
|
||||
((!filter.empty()) && symbol.second.name.substr(0, filter.size()) == filter))
|
||||
symbol_db->ForEachSymbol([&](const Common::Symbol& symbol) {
|
||||
if ((filter.empty() && (!symbol.name.empty()) && symbol.name.substr(0, 3) != "zz_" &&
|
||||
symbol.name.substr(0, 1) != ".") ||
|
||||
((!filter.empty()) && symbol.name.substr(0, filter.size()) == filter))
|
||||
{
|
||||
DBFunc temp_dbfunc;
|
||||
temp_dbfunc.name = symbol.second.name;
|
||||
temp_dbfunc.size = symbol.second.size;
|
||||
m_database[symbol.second.hash] = temp_dbfunc;
|
||||
temp_dbfunc.name = symbol.name;
|
||||
temp_dbfunc.size = symbol.size;
|
||||
m_database[symbol.hash] = temp_dbfunc;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
u32 HashSignatureDB::ComputeCodeChecksum(const Core::CPUThreadGuard& guard, u32 offsetStart,
|
||||
|
|
|
@ -727,12 +727,12 @@ static bool ValidateHeaders(const StateHeader& header)
|
|||
std::string loaded_str = header.version_string;
|
||||
const u32 loaded_version = header.version_header.version_cookie - COOKIE_BASE;
|
||||
|
||||
if (s_old_versions.contains(loaded_version))
|
||||
if (const auto it = s_old_versions.find(loaded_version); it != s_old_versions.end())
|
||||
{
|
||||
// This is a REALLY old version, before we started writing the version string to file
|
||||
success = false;
|
||||
|
||||
std::pair<std::string, std::string> version_range = s_old_versions.find(loaded_version)->second;
|
||||
std::pair<std::string, std::string> version_range = it->second;
|
||||
std::string oldest_version = version_range.first;
|
||||
std::string newest_version = version_range.second;
|
||||
|
||||
|
|
|
@ -112,14 +112,16 @@ void GameConfigWidget::CreateWidgets()
|
|||
m_deterministic_dual_core =
|
||||
new ConfigStringChoice(choice, Config::MAIN_GPU_DETERMINISM_MODE, layer);
|
||||
|
||||
m_enable_mmu->setToolTip(tr(
|
||||
m_enable_mmu->SetDescription(tr(
|
||||
"Enables the Memory Management Unit, needed for some games. (ON = Compatible, OFF = Fast)"));
|
||||
|
||||
m_enable_fprf->setToolTip(tr("Enables Floating Point Result Flag calculation, needed for a few "
|
||||
"games. (ON = Compatible, OFF = Fast)"));
|
||||
m_sync_gpu->setToolTip(tr("Synchronizes the GPU and CPU threads to help prevent random freezes "
|
||||
"in Dual core mode. (ON = Compatible, OFF = Fast)"));
|
||||
m_emulate_disc_speed->setToolTip(
|
||||
m_enable_fprf->SetDescription(
|
||||
tr("Enables Floating Point Result Flag calculation, needed for a few "
|
||||
"games. (ON = Compatible, OFF = Fast)"));
|
||||
m_sync_gpu->SetDescription(
|
||||
tr("Synchronizes the GPU and CPU threads to help prevent random freezes "
|
||||
"in Dual core mode. (ON = Compatible, OFF = Fast)"));
|
||||
m_emulate_disc_speed->SetDescription(
|
||||
tr("Enable emulated disc speed. Disabling this can cause crashes "
|
||||
"and other problems in some games. "
|
||||
"(ON = Compatible, OFF = Unlocked)"));
|
||||
|
@ -143,11 +145,11 @@ void GameConfigWidget::CreateWidgets()
|
|||
m_use_monoscopic_shadows =
|
||||
new ConfigBool(tr("Monoscopic Shadows"), Config::GFX_STEREO_EFB_MONO_DEPTH, layer);
|
||||
|
||||
m_depth_slider->setToolTip(
|
||||
m_depth_slider->SetDescription(
|
||||
tr("This value is multiplied with the depth set in the graphics configuration."));
|
||||
m_convergence_spin->setToolTip(
|
||||
m_convergence_spin->SetDescription(
|
||||
tr("This value is added to the convergence value set in the graphics configuration."));
|
||||
m_use_monoscopic_shadows->setToolTip(
|
||||
m_use_monoscopic_shadows->SetDescription(
|
||||
tr("Use a single depth buffer for both eyes. Needed for a few games."));
|
||||
|
||||
stereoscopy_layout->addWidget(new ConfigSliderLabel(tr("Depth Percentage:"), m_depth_slider), 0,
|
||||
|
|
|
@ -335,7 +335,7 @@ void CodeViewWidget::Update(const Core::CPUThreadGuard* guard)
|
|||
|
||||
std::string ins = (split == std::string::npos ? disas : disas.substr(0, split));
|
||||
std::string param = (split == std::string::npos ? "" : disas.substr(split + 1));
|
||||
const std::string_view desc = debug_interface.GetDescription(addr);
|
||||
const std::string desc = debug_interface.GetDescription(addr);
|
||||
|
||||
const Common::Note* note = m_ppc_symbol_db.GetNoteFromAddr(addr);
|
||||
std::string note_string;
|
||||
|
@ -433,8 +433,6 @@ void CodeViewWidget::Update(const Core::CPUThreadGuard* guard)
|
|||
|
||||
CalculateBranchIndentation();
|
||||
|
||||
m_ppc_symbol_db.FillInCallers();
|
||||
|
||||
repaint();
|
||||
m_updating = false;
|
||||
}
|
||||
|
@ -950,7 +948,7 @@ void CodeViewWidget::OnFollowBranch()
|
|||
void CodeViewWidget::OnEditSymbol()
|
||||
{
|
||||
const u32 addr = GetContextAddress();
|
||||
Common::Symbol* const symbol = m_ppc_symbol_db.GetSymbolFromAddr(addr);
|
||||
const Common::Symbol* const symbol = m_ppc_symbol_db.GetSymbolFromAddr(addr);
|
||||
|
||||
if (symbol == nullptr)
|
||||
{
|
||||
|
@ -974,12 +972,14 @@ void CodeViewWidget::OnEditSymbol()
|
|||
}
|
||||
|
||||
if (symbol->name != name)
|
||||
symbol->Rename(name);
|
||||
m_ppc_symbol_db.RenameSymbol(*symbol, name);
|
||||
|
||||
if (symbol->size != size)
|
||||
{
|
||||
Core::CPUThreadGuard guard(m_system);
|
||||
PPCAnalyst::ReanalyzeFunction(guard, symbol->address, *symbol, size);
|
||||
Common::Symbol new_symbol = *symbol;
|
||||
PPCAnalyst::ReanalyzeFunction(guard, symbol->address, new_symbol, size);
|
||||
m_ppc_symbol_db.AddCompleteSymbol(new_symbol);
|
||||
}
|
||||
|
||||
emit Host::GetInstance()->PPCSymbolsChanged();
|
||||
|
@ -988,7 +988,7 @@ void CodeViewWidget::OnEditSymbol()
|
|||
void CodeViewWidget::OnDeleteSymbol()
|
||||
{
|
||||
const u32 addr = GetContextAddress();
|
||||
Common::Symbol* const symbol = m_ppc_symbol_db.GetSymbolFromAddr(addr);
|
||||
const Common::Symbol* const symbol = m_ppc_symbol_db.GetSymbolFromAddr(addr);
|
||||
|
||||
if (symbol == nullptr)
|
||||
return;
|
||||
|
@ -1039,7 +1039,7 @@ void CodeViewWidget::OnSelectionChanged()
|
|||
void CodeViewWidget::OnEditNote()
|
||||
{
|
||||
const u32 context_address = GetContextAddress();
|
||||
Common::Note* const note = m_ppc_symbol_db.GetNoteFromAddr(context_address);
|
||||
const Common::Note* const note = m_ppc_symbol_db.GetNoteFromAddr(context_address);
|
||||
|
||||
if (note == nullptr)
|
||||
return;
|
||||
|
@ -1071,7 +1071,7 @@ void CodeViewWidget::OnEditNote()
|
|||
void CodeViewWidget::OnDeleteNote()
|
||||
{
|
||||
const u32 context_address = GetContextAddress();
|
||||
Common::Note* const note = m_ppc_symbol_db.GetNoteFromAddr(context_address);
|
||||
const Common::Note* const note = m_ppc_symbol_db.GetNoteFromAddr(context_address);
|
||||
|
||||
if (note == nullptr)
|
||||
return;
|
||||
|
|
|
@ -423,14 +423,13 @@ void CodeWidget::UpdateSymbols()
|
|||
m_symbols_list->selectedItems()[0]->text();
|
||||
m_symbols_list->clear();
|
||||
|
||||
for (const auto& symbol : m_ppc_symbol_db.Symbols())
|
||||
{
|
||||
QString name = QString::fromStdString(symbol.second.name);
|
||||
m_ppc_symbol_db.ForEachSymbol([&](const Common::Symbol& symbol) {
|
||||
QString name = QString::fromStdString(symbol.name);
|
||||
|
||||
// If the symbol has an object name, add it to the entry name.
|
||||
if (!symbol.second.object_name.empty())
|
||||
if (!symbol.object_name.empty())
|
||||
{
|
||||
name += QString::fromStdString(fmt::format(" ({})", symbol.second.object_name));
|
||||
name += QString::fromStdString(fmt::format(" ({})", symbol.object_name));
|
||||
}
|
||||
|
||||
auto* item = new QListWidgetItem(name);
|
||||
|
@ -438,14 +437,14 @@ void CodeWidget::UpdateSymbols()
|
|||
item->setSelected(true);
|
||||
|
||||
// Disable non-function symbols as you can't do anything with them.
|
||||
if (symbol.second.type != Common::Symbol::Type::Function)
|
||||
if (symbol.type != Common::Symbol::Type::Function)
|
||||
item->setFlags(Qt::NoItemFlags);
|
||||
|
||||
item->setData(Qt::UserRole, symbol.second.address);
|
||||
item->setData(Qt::UserRole, symbol.address);
|
||||
|
||||
if (name.contains(m_symbol_filter, Qt::CaseInsensitive))
|
||||
m_symbols_list->addItem(item);
|
||||
}
|
||||
});
|
||||
|
||||
m_symbols_list->sortItems();
|
||||
}
|
||||
|
@ -457,19 +456,18 @@ void CodeWidget::UpdateNotes()
|
|||
m_note_list->selectedItems()[0]->text();
|
||||
m_note_list->clear();
|
||||
|
||||
for (const auto& note : m_ppc_symbol_db.Notes())
|
||||
{
|
||||
const QString name = QString::fromStdString(note.second.name);
|
||||
m_ppc_symbol_db.ForEachNote([&](const Common::Note& note) {
|
||||
const QString name = QString::fromStdString(note.name);
|
||||
|
||||
auto* item = new QListWidgetItem(name);
|
||||
if (name == selection)
|
||||
item->setSelected(true);
|
||||
|
||||
item->setData(Qt::UserRole, note.second.address);
|
||||
item->setData(Qt::UserRole, note.address);
|
||||
|
||||
if (name.toUpper().indexOf(m_symbol_filter.toUpper()) != -1)
|
||||
m_note_list->addItem(item);
|
||||
}
|
||||
});
|
||||
|
||||
m_note_list->sortItems();
|
||||
}
|
||||
|
|
|
@ -29,8 +29,10 @@
|
|||
#include "Core/Core.h"
|
||||
#include "Core/HW/AddressSpace.h"
|
||||
#include "Core/PowerPC/BreakPoints.h"
|
||||
#include "Core/PowerPC/PPCSymbolDB.h"
|
||||
#include "Core/PowerPC/PowerPC.h"
|
||||
#include "Core/System.h"
|
||||
#include "DolphinQt/Debugger/EditSymbolDialog.h"
|
||||
#include "DolphinQt/Host.h"
|
||||
#include "DolphinQt/Resources.h"
|
||||
#include "DolphinQt/Settings.h"
|
||||
|
@ -196,7 +198,7 @@ private:
|
|||
};
|
||||
|
||||
MemoryViewWidget::MemoryViewWidget(Core::System& system, QWidget* parent)
|
||||
: QWidget(parent), m_system(system)
|
||||
: QWidget(parent), m_system(system), m_ppc_symbol_db(m_system.GetPPCSymbolDB())
|
||||
{
|
||||
auto* layout = new QHBoxLayout();
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
|
@ -220,6 +222,8 @@ MemoryViewWidget::MemoryViewWidget(Core::System& system, QWidget* parent)
|
|||
this->setLayout(layout);
|
||||
|
||||
connect(&Settings::Instance(), &Settings::DebugFontChanged, this, &MemoryViewWidget::UpdateFont);
|
||||
connect(Host::GetInstance(), &Host::PPCSymbolsChanged, this,
|
||||
[this] { UpdateDispatcher(UpdateType::Symbols); });
|
||||
connect(Host::GetInstance(), &Host::PPCBreakpointsChanged, this,
|
||||
&MemoryViewWidget::UpdateBreakpointTags);
|
||||
connect(&Settings::Instance(), &Settings::EmulationStateChanged, this, [this] {
|
||||
|
@ -347,6 +351,9 @@ void MemoryViewWidget::UpdateDispatcher(UpdateType type)
|
|||
if (Core::GetState(m_system) == Core::State::Paused)
|
||||
GetValues();
|
||||
UpdateColumns();
|
||||
[[fallthrough]];
|
||||
case UpdateType::Symbols:
|
||||
UpdateSymbols();
|
||||
break;
|
||||
case UpdateType::Auto:
|
||||
// Values were captured on CPU thread while doing a callback.
|
||||
|
@ -371,7 +378,7 @@ void MemoryViewWidget::CreateTable()
|
|||
// Span is the number of unique memory values covered in one row.
|
||||
const int data_span = m_bytes_per_row / GetTypeSize(m_type);
|
||||
m_data_columns = m_dual_view ? data_span * 2 : data_span;
|
||||
const int total_columns = MISC_COLUMNS + m_data_columns;
|
||||
const int total_columns = MISC_COLUMNS + m_data_columns + (m_show_symbols ? 1 : 0);
|
||||
|
||||
const int rows =
|
||||
std::round((m_table->height() / static_cast<float>(m_table->rowHeight(0))) - 0.25);
|
||||
|
@ -440,6 +447,15 @@ void MemoryViewWidget::CreateTable()
|
|||
|
||||
m_table->setItem(i, c + MISC_COLUMNS, item.clone());
|
||||
}
|
||||
|
||||
if (!m_show_symbols)
|
||||
continue;
|
||||
|
||||
// Symbols
|
||||
auto* description_item = new QTableWidgetItem(QStringLiteral("-"));
|
||||
description_item->setFlags(Qt::ItemIsEnabled);
|
||||
|
||||
m_table->setItem(i, m_table->columnCount() - 1, description_item);
|
||||
}
|
||||
|
||||
// Update column width
|
||||
|
@ -500,6 +516,9 @@ void MemoryViewWidget::Update()
|
|||
item->setBackground(Qt::transparent);
|
||||
item->setData(USER_ROLE_VALID_ADDRESS, false);
|
||||
}
|
||||
|
||||
if (m_show_symbols)
|
||||
m_table->item(i, m_table->columnCount() - 1)->setData(USER_ROLE_CELL_ADDRESS, row_address);
|
||||
}
|
||||
|
||||
UpdateBreakpointTags();
|
||||
|
@ -576,6 +595,34 @@ void MemoryViewWidget::UpdateColumns()
|
|||
}
|
||||
}
|
||||
|
||||
void MemoryViewWidget::UpdateSymbols()
|
||||
{
|
||||
if (!m_show_symbols)
|
||||
return;
|
||||
|
||||
// Update symbols
|
||||
for (int i = 0; i < m_table->rowCount(); i++)
|
||||
{
|
||||
auto* item = m_table->item(i, m_table->columnCount() - 1);
|
||||
if (!item)
|
||||
continue;
|
||||
|
||||
const u32 address = item->data(USER_ROLE_CELL_ADDRESS).toUInt();
|
||||
const Common::Note* note = m_ppc_symbol_db.GetNoteFromAddr(address);
|
||||
|
||||
std::string desc;
|
||||
if (note == nullptr)
|
||||
desc = m_ppc_symbol_db.GetDescription(address);
|
||||
else
|
||||
desc = note->name;
|
||||
|
||||
item->setText(QString::fromStdString(" " + desc));
|
||||
}
|
||||
|
||||
if (m_show_symbols)
|
||||
m_table->resizeColumnToContents(m_table->columnCount() - 1);
|
||||
}
|
||||
|
||||
// Always runs on CPU thread from a callback.
|
||||
void MemoryViewWidget::UpdateOnFrameEnd()
|
||||
{
|
||||
|
@ -1010,6 +1057,7 @@ void MemoryViewWidget::ToggleBreakpoint(u32 addr, bool row)
|
|||
|
||||
{
|
||||
const Core::CPUThreadGuard guard(m_system);
|
||||
DelayedMemCheckUpdate delayed_update(&memchecks);
|
||||
|
||||
for (int i = 0; i < breaks; i++)
|
||||
{
|
||||
|
@ -1028,16 +1076,14 @@ void MemoryViewWidget::ToggleBreakpoint(u32 addr, bool row)
|
|||
check.log_on_hit = m_do_log;
|
||||
check.break_on_hit = true;
|
||||
|
||||
memchecks.Add(std::move(check), false);
|
||||
delayed_update |= memchecks.Add(std::move(check));
|
||||
}
|
||||
else if (check_ptr != nullptr)
|
||||
{
|
||||
// Using the pointer fixes misaligned breakpoints (0x11 breakpoint in 0x10 aligned view).
|
||||
memchecks.Remove(check_ptr->start_address, false);
|
||||
delayed_update |= memchecks.Remove(check_ptr->start_address);
|
||||
}
|
||||
}
|
||||
|
||||
memchecks.Update();
|
||||
}
|
||||
|
||||
emit Host::GetInstance()->PPCBreakpointsChanged();
|
||||
|
@ -1059,6 +1105,78 @@ void MemoryViewWidget::OnCopyHex(u32 addr)
|
|||
QStringLiteral("%1").arg(value, sizeof(u64) * 2, 16, QLatin1Char('0')).left(length * 2));
|
||||
}
|
||||
|
||||
void MemoryViewWidget::ShowSymbols(bool enable)
|
||||
{
|
||||
m_show_symbols = enable;
|
||||
UpdateDispatcher(UpdateType::Full);
|
||||
}
|
||||
|
||||
void MemoryViewWidget::OnEditSymbol(EditSymbolType type, u32 addr)
|
||||
{
|
||||
// Add Note and Add Region use these values.
|
||||
std::string name = "";
|
||||
std::string object_name = "";
|
||||
u32 size = GetTypeSize(m_type);
|
||||
u32 address = addr;
|
||||
EditSymbolDialog::Type dialog_type = EditSymbolDialog::Type::Note;
|
||||
|
||||
// Add and edit region are tied to the same context menu action.
|
||||
if (type == EditSymbolType::EditRegion)
|
||||
{
|
||||
// If symbol doesn't exist, it's safe to add a new region.
|
||||
const Common::Symbol* const symbol = m_ppc_symbol_db.GetSymbolFromAddr(addr);
|
||||
dialog_type = EditSymbolDialog::Type::Symbol;
|
||||
|
||||
if (symbol != nullptr)
|
||||
{
|
||||
// Leave the more specialized function editing to code widget.
|
||||
if (symbol->type != Common::Symbol::Type::Data)
|
||||
return;
|
||||
|
||||
// Edit data region.
|
||||
name = symbol->name;
|
||||
object_name = symbol->object_name;
|
||||
size = symbol->size;
|
||||
address = symbol->address;
|
||||
}
|
||||
}
|
||||
else if (type == EditSymbolType::EditNote)
|
||||
{
|
||||
const Common::Note* note = m_ppc_symbol_db.GetNoteFromAddr(addr);
|
||||
if (note == nullptr)
|
||||
return;
|
||||
|
||||
name = note->name;
|
||||
size = note->size;
|
||||
address = note->address;
|
||||
}
|
||||
|
||||
EditSymbolDialog dialog(this, address, &size, &name, dialog_type);
|
||||
|
||||
if (dialog.exec() != QDialog::Accepted)
|
||||
return;
|
||||
|
||||
if (dialog.DeleteRequested())
|
||||
{
|
||||
if (type == EditSymbolType::EditRegion)
|
||||
m_ppc_symbol_db.DeleteFunction(address);
|
||||
else
|
||||
m_ppc_symbol_db.DeleteNote(address);
|
||||
}
|
||||
else if (type == EditSymbolType::EditRegion)
|
||||
{
|
||||
m_ppc_symbol_db.AddKnownSymbol(Core::CPUThreadGuard{m_system}, address, size, name, object_name,
|
||||
Common::Symbol::Type::Data);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ppc_symbol_db.AddKnownNote(address, size, name);
|
||||
m_ppc_symbol_db.DetermineNoteLayers();
|
||||
}
|
||||
|
||||
emit Host::GetInstance()->PPCSymbolsChanged();
|
||||
}
|
||||
|
||||
void MemoryViewWidget::OnContextMenu(const QPoint& pos)
|
||||
{
|
||||
auto* item_selected = m_table->itemAt(pos);
|
||||
|
@ -1089,6 +1207,21 @@ void MemoryViewWidget::OnContextMenu(const QPoint& pos)
|
|||
|
||||
menu->addSeparator();
|
||||
|
||||
auto* note_add_action = menu->addAction(
|
||||
tr("Add Note"), this, [this, addr] { OnEditSymbol(EditSymbolType::AddNote, addr); });
|
||||
auto* note_edit_action = menu->addAction(
|
||||
tr("Edit Note"), this, [this, addr] { OnEditSymbol(EditSymbolType::EditNote, addr); });
|
||||
menu->addAction(tr("Add or edit region label"), this,
|
||||
[this, addr] { OnEditSymbol(EditSymbolType::EditRegion, addr); });
|
||||
|
||||
auto* note = m_ppc_symbol_db.GetNoteFromAddr(addr);
|
||||
note_edit_action->setEnabled(note != nullptr);
|
||||
// A note cannot be added ontop of the starting address of another note.
|
||||
if (note != nullptr && note->address == addr)
|
||||
note_add_action->setEnabled(false);
|
||||
|
||||
menu->addSeparator();
|
||||
|
||||
menu->addAction(tr("Show in code"), this, [this, addr] { emit ShowCode(addr); });
|
||||
|
||||
menu->addSeparator();
|
||||
|
|
|
@ -24,6 +24,8 @@ class CPUThreadGuard;
|
|||
class System;
|
||||
} // namespace Core
|
||||
|
||||
class PPCSymbolDB;
|
||||
|
||||
// Captures direct editing of the table.
|
||||
class TableEditDelegate : public QStyledItemDelegate
|
||||
{
|
||||
|
@ -76,14 +78,23 @@ public:
|
|||
Full,
|
||||
Addresses,
|
||||
Values,
|
||||
Symbols,
|
||||
Auto,
|
||||
};
|
||||
|
||||
enum class EditSymbolType
|
||||
{
|
||||
AddNote,
|
||||
EditNote,
|
||||
EditRegion,
|
||||
};
|
||||
|
||||
explicit MemoryViewWidget(Core::System& system, QWidget* parent = nullptr);
|
||||
|
||||
void CreateTable();
|
||||
void UpdateDispatcher(UpdateType type = UpdateType::Addresses);
|
||||
void Update();
|
||||
void UpdateSymbols();
|
||||
void UpdateOnFrameEnd();
|
||||
void GetValues();
|
||||
void UpdateFont(const QFont& font);
|
||||
|
@ -98,6 +109,7 @@ public:
|
|||
void SetBPType(BPType type);
|
||||
void SetAddress(u32 address);
|
||||
void SetFocus() const;
|
||||
void ShowSymbols(bool enable);
|
||||
|
||||
void SetBPLoggingEnabled(bool enabled);
|
||||
|
||||
|
@ -108,6 +120,7 @@ signals:
|
|||
void ActivateSearch();
|
||||
|
||||
private:
|
||||
void OnEditSymbol(EditSymbolType type, u32 addr);
|
||||
void OnContextMenu(const QPoint& pos);
|
||||
void OnCopyAddress(u32 addr);
|
||||
void OnCopyHex(u32 addr);
|
||||
|
@ -116,9 +129,11 @@ private:
|
|||
void UpdateColumns();
|
||||
void ScrollbarActionTriggered(int action);
|
||||
void ScrollbarSliderReleased();
|
||||
|
||||
std::optional<QString> ValueToString(const Core::CPUThreadGuard& guard, u32 address, Type type);
|
||||
|
||||
Core::System& m_system;
|
||||
PPCSymbolDB& m_ppc_symbol_db;
|
||||
|
||||
MemoryViewTable* m_table;
|
||||
QScrollBar* m_scrollbar;
|
||||
|
@ -137,6 +152,7 @@ private:
|
|||
int m_alignment = 16;
|
||||
int m_data_columns;
|
||||
bool m_dual_view = false;
|
||||
bool m_show_symbols = true;
|
||||
std::mutex m_updating;
|
||||
QColor m_highlight_color = QColor(120, 255, 255, 100);
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QListWidget>
|
||||
#include <QMenuBar>
|
||||
#include <QPushButton>
|
||||
#include <QRegularExpression>
|
||||
|
@ -32,6 +33,7 @@
|
|||
#include "Core/ConfigManager.h"
|
||||
#include "Core/Core.h"
|
||||
#include "Core/HW/AddressSpace.h"
|
||||
#include "Core/PowerPC/PPCSymbolDB.h"
|
||||
#include "Core/System.h"
|
||||
#include "DolphinQt/Debugger/MemoryViewWidget.h"
|
||||
#include "DolphinQt/Host.h"
|
||||
|
@ -41,7 +43,7 @@
|
|||
using Type = MemoryViewWidget::Type;
|
||||
|
||||
MemoryWidget::MemoryWidget(Core::System& system, QWidget* parent)
|
||||
: QDockWidget(parent), m_system(system)
|
||||
: QDockWidget(parent), m_system(system), m_ppc_symbol_db(system.GetPPCSymbolDB())
|
||||
{
|
||||
setWindowTitle(tr("Memory"));
|
||||
setObjectName(QStringLiteral("memory"));
|
||||
|
@ -247,6 +249,25 @@ void MemoryWidget::CreateWidgets()
|
|||
bp_layout->addWidget(m_bp_log_check);
|
||||
bp_layout->setSpacing(1);
|
||||
|
||||
// Notes
|
||||
m_labels_group = new QGroupBox(tr("Labels"));
|
||||
auto* symbols_box = new QTabWidget;
|
||||
m_note_list = new QListWidget;
|
||||
m_data_list = new QListWidget;
|
||||
m_symbols_list = new QListWidget;
|
||||
symbols_box->addTab(m_note_list, tr("Notes"));
|
||||
symbols_box->addTab(m_data_list, tr("Data"));
|
||||
symbols_box->addTab(m_symbols_list, tr("Symbols"));
|
||||
m_symbols_list->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
|
||||
auto* labels_layout = new QVBoxLayout;
|
||||
m_search_labels = new QLineEdit;
|
||||
m_search_labels->setPlaceholderText(tr("Filter Label List"));
|
||||
|
||||
m_labels_group->setLayout(labels_layout);
|
||||
labels_layout->addWidget(symbols_box);
|
||||
labels_layout->addWidget(m_search_labels);
|
||||
|
||||
// Sidebar
|
||||
auto* sidebar = new QWidget;
|
||||
auto* sidebar_layout = new QVBoxLayout;
|
||||
|
@ -262,8 +283,9 @@ void MemoryWidget::CreateWidgets()
|
|||
&MemoryWidget::OnSetValueFromFile);
|
||||
menubar->addMenu(menu_import);
|
||||
|
||||
// View Menu
|
||||
auto* auto_update_action =
|
||||
menu_views->addAction(tr("Auto update memory values"), this, [this](bool checked) {
|
||||
menu_views->addAction(tr("&Auto update memory values"), this, [this](bool checked) {
|
||||
m_auto_update_enabled = checked;
|
||||
if (checked)
|
||||
RegisterAfterFrameEventCallback();
|
||||
|
@ -274,14 +296,23 @@ void MemoryWidget::CreateWidgets()
|
|||
auto_update_action->setChecked(true);
|
||||
|
||||
auto* highlight_update_action =
|
||||
menu_views->addAction(tr("Highlight recently changed values"), this,
|
||||
menu_views->addAction(tr("&Highlight recently changed values"), this,
|
||||
[this](bool checked) { m_memory_view->ToggleHighlights(checked); });
|
||||
highlight_update_action->setCheckable(true);
|
||||
highlight_update_action->setChecked(true);
|
||||
|
||||
menu_views->addAction(tr("Highlight color"), this,
|
||||
menu_views->addAction(tr("Highlight &color"), this,
|
||||
[this] { m_memory_view->SetHighlightColor(); });
|
||||
|
||||
auto* show_notes =
|
||||
menu_views->addAction(tr("&Show symbols and notes"), this, [this](bool checked) {
|
||||
m_labels_visible = checked;
|
||||
m_memory_view->ShowSymbols(checked);
|
||||
UpdateNotes();
|
||||
});
|
||||
show_notes->setCheckable(true);
|
||||
show_notes->setChecked(true);
|
||||
|
||||
QMenu* menu_export = new QMenu(tr("&Export"), menubar);
|
||||
menu_export->addAction(tr("Dump &MRAM"), this, &MemoryWidget::OnDumpMRAM);
|
||||
menu_export->addAction(tr("Dump &ExRAM"), this, &MemoryWidget::OnDumpExRAM);
|
||||
|
@ -306,6 +337,7 @@ void MemoryWidget::CreateWidgets()
|
|||
sidebar_layout->addWidget(address_space_group);
|
||||
sidebar_layout->addItem(new QSpacerItem(1, 10));
|
||||
sidebar_layout->addWidget(bp_group);
|
||||
sidebar_layout->addWidget(m_labels_group);
|
||||
sidebar_layout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Expanding));
|
||||
|
||||
// Splitter
|
||||
|
@ -327,6 +359,7 @@ void MemoryWidget::CreateWidgets()
|
|||
auto* widget = new QWidget;
|
||||
widget->setLayout(layout);
|
||||
setWidget(widget);
|
||||
UpdateNotes();
|
||||
}
|
||||
|
||||
void MemoryWidget::ConnectWidgets()
|
||||
|
@ -359,6 +392,12 @@ void MemoryWidget::ConnectWidgets()
|
|||
|
||||
connect(m_base_check, &QCheckBox::toggled, this, &MemoryWidget::ValidateAndPreviewInputValue);
|
||||
connect(m_bp_log_check, &QCheckBox::toggled, this, &MemoryWidget::OnBPLogChanged);
|
||||
|
||||
for (auto* list : {m_symbols_list, m_data_list, m_note_list})
|
||||
connect(list, &QListWidget::itemClicked, this, &MemoryWidget::OnSelectLabel);
|
||||
|
||||
connect(Host::GetInstance(), &Host::PPCSymbolsChanged, this, &MemoryWidget::RefreshLabelBox);
|
||||
connect(m_search_labels, &QLineEdit::textChanged, this, &MemoryWidget::RefreshLabelBox);
|
||||
connect(m_memory_view, &MemoryViewWidget::ShowCode, this, &MemoryWidget::ShowCode);
|
||||
connect(m_memory_view, &MemoryViewWidget::RequestWatch, this, &MemoryWidget::RequestWatch);
|
||||
connect(m_memory_view, &MemoryViewWidget::ActivateSearch, this,
|
||||
|
@ -795,6 +834,98 @@ void MemoryWidget::OnSetValueFromFile()
|
|||
Update();
|
||||
}
|
||||
|
||||
void MemoryWidget::RefreshLabelBox()
|
||||
{
|
||||
if (!m_labels_visible || m_ppc_symbol_db.IsEmpty())
|
||||
{
|
||||
m_labels_group->hide();
|
||||
return;
|
||||
}
|
||||
|
||||
m_labels_group->show();
|
||||
|
||||
UpdateSymbols();
|
||||
UpdateNotes();
|
||||
}
|
||||
|
||||
void MemoryWidget::OnSelectLabel()
|
||||
{
|
||||
QList<QListWidgetItem*> items;
|
||||
if (m_note_list->isVisible())
|
||||
items = m_note_list->selectedItems();
|
||||
else if (m_symbols_list->isVisible())
|
||||
items = m_symbols_list->selectedItems();
|
||||
else if (m_data_list->isVisible())
|
||||
items = m_data_list->selectedItems();
|
||||
|
||||
if (items.isEmpty())
|
||||
return;
|
||||
|
||||
const u32 address = items[0]->data(Qt::UserRole).toUInt();
|
||||
|
||||
SetAddress(address);
|
||||
}
|
||||
|
||||
void MemoryWidget::UpdateSymbols()
|
||||
{
|
||||
const QString selection = m_symbols_list->selectedItems().isEmpty() ?
|
||||
QString{} :
|
||||
m_symbols_list->selectedItems()[0]->text();
|
||||
m_symbols_list->clear();
|
||||
m_data_list->clear();
|
||||
|
||||
m_ppc_symbol_db.ForEachSymbol([&](const Common::Symbol& symbol) {
|
||||
QString name = QString::fromStdString(symbol.name);
|
||||
|
||||
// If the symbol has an object name, add it to the entry name.
|
||||
if (!symbol.object_name.empty())
|
||||
{
|
||||
name += QString::fromStdString(fmt::format(" ({})", symbol.object_name));
|
||||
}
|
||||
|
||||
auto* item = new QListWidgetItem(name);
|
||||
if (name == selection)
|
||||
item->setSelected(true);
|
||||
|
||||
item->setData(Qt::UserRole, symbol.address);
|
||||
|
||||
if (!name.contains(m_search_labels->text(), Qt::CaseInsensitive))
|
||||
return;
|
||||
|
||||
if (symbol.type != Common::Symbol::Type::Function)
|
||||
m_data_list->addItem(item);
|
||||
else
|
||||
m_symbols_list->addItem(item);
|
||||
});
|
||||
|
||||
m_symbols_list->sortItems();
|
||||
}
|
||||
|
||||
void MemoryWidget::UpdateNotes()
|
||||
{
|
||||
// Save selection to re-apply.
|
||||
const QString selection = m_note_list->selectedItems().isEmpty() ?
|
||||
QStringLiteral("") :
|
||||
m_note_list->selectedItems()[0]->text();
|
||||
m_note_list->clear();
|
||||
|
||||
m_ppc_symbol_db.ForEachNote([&](const Common::Note& note) {
|
||||
const QString name = QString::fromStdString(note.name);
|
||||
|
||||
auto* item = new QListWidgetItem(name);
|
||||
if (name == selection)
|
||||
item->setSelected(true);
|
||||
|
||||
item->setData(Qt::UserRole, note.address);
|
||||
|
||||
// Filter notes based on the search text.
|
||||
if (name.contains(m_search_labels->text(), Qt::CaseInsensitive))
|
||||
m_note_list->addItem(item);
|
||||
});
|
||||
|
||||
m_note_list->sortItems();
|
||||
}
|
||||
|
||||
static void DumpArray(const std::string& filename, const u8* data, size_t length)
|
||||
{
|
||||
if (!data)
|
||||
|
|
|
@ -14,8 +14,11 @@
|
|||
class MemoryViewWidget;
|
||||
class QCheckBox;
|
||||
class QComboBox;
|
||||
class QGroupBox;
|
||||
class QHideEvent;
|
||||
class QLabel;
|
||||
class QLineEdit;
|
||||
class QListWidget;
|
||||
class QPushButton;
|
||||
class QRadioButton;
|
||||
class QShowEvent;
|
||||
|
@ -27,6 +30,8 @@ class System;
|
|||
class CPUThreadGuard;
|
||||
} // namespace Core
|
||||
|
||||
class PPCSymbolDB;
|
||||
|
||||
class MemoryWidget : public QDockWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
@ -66,6 +71,11 @@ private:
|
|||
void OnSetValue();
|
||||
void OnSetValueFromFile();
|
||||
|
||||
void OnSelectLabel();
|
||||
void RefreshLabelBox();
|
||||
void UpdateSymbols();
|
||||
void UpdateNotes();
|
||||
|
||||
void OnDumpMRAM();
|
||||
void OnDumpExRAM();
|
||||
void OnDumpARAM();
|
||||
|
@ -85,6 +95,7 @@ private:
|
|||
void ActivateSearchAddress();
|
||||
|
||||
Core::System& m_system;
|
||||
PPCSymbolDB& m_ppc_symbol_db;
|
||||
|
||||
MemoryViewWidget* m_memory_view;
|
||||
QSplitter* m_splitter;
|
||||
|
@ -115,6 +126,15 @@ private:
|
|||
QRadioButton* m_bp_read_only;
|
||||
QRadioButton* m_bp_write_only;
|
||||
QCheckBox* m_bp_log_check;
|
||||
|
||||
QGroupBox* m_labels_group;
|
||||
QLineEdit* m_search_labels;
|
||||
QListWidget* m_symbols_list;
|
||||
QListWidget* m_data_list;
|
||||
QListWidget* m_note_list;
|
||||
QString m_note_filter;
|
||||
bool m_labels_visible = true;
|
||||
|
||||
Common::EventHook m_vi_end_field_event;
|
||||
|
||||
bool m_auto_update_enabled = true;
|
||||
|
|
|
@ -251,17 +251,18 @@ static std::unique_ptr<QDirIterator> GetIterator(const QString& dir)
|
|||
void GameTracker::RemoveDirectoryInternal(const QString& dir)
|
||||
{
|
||||
RemovePath(dir);
|
||||
auto it = GetIterator(dir);
|
||||
while (it->hasNext())
|
||||
const auto dir_it = GetIterator(dir);
|
||||
while (dir_it->hasNext())
|
||||
{
|
||||
QString path = QFileInfo(it->next()).canonicalFilePath();
|
||||
if (m_tracked_files.contains(path))
|
||||
QString path = QFileInfo(dir_it->next()).canonicalFilePath();
|
||||
if (const auto it = m_tracked_files.find(path); it != m_tracked_files.end())
|
||||
{
|
||||
m_tracked_files[path].remove(dir);
|
||||
if (m_tracked_files[path].empty())
|
||||
auto& set = *it;
|
||||
set.remove(dir);
|
||||
if (set.isEmpty())
|
||||
{
|
||||
RemovePath(path);
|
||||
m_tracked_files.remove(path);
|
||||
m_tracked_files.erase(it);
|
||||
if (m_started)
|
||||
emit GameRemoved(path.toStdString());
|
||||
}
|
||||
|
@ -271,16 +272,14 @@ void GameTracker::RemoveDirectoryInternal(const QString& dir)
|
|||
|
||||
void GameTracker::UpdateDirectoryInternal(const QString& dir)
|
||||
{
|
||||
auto it = GetIterator(dir);
|
||||
while (it->hasNext() && !m_processing_halted)
|
||||
const auto dir_it = GetIterator(dir);
|
||||
while (dir_it->hasNext() && !m_processing_halted)
|
||||
{
|
||||
QString path = QFileInfo(it->next()).canonicalFilePath();
|
||||
QString path = QFileInfo(dir_it->next()).canonicalFilePath();
|
||||
|
||||
if (m_tracked_files.contains(path))
|
||||
if (const auto it = m_tracked_files.find(path); it != m_tracked_files.end())
|
||||
{
|
||||
auto& tracked_file = m_tracked_files[path];
|
||||
if (!tracked_file.contains(dir))
|
||||
tracked_file.insert(dir);
|
||||
it->insert(dir);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -656,8 +656,9 @@ void NetPlayDialog::UpdateGUI()
|
|||
|
||||
auto* name_item = new QTableWidgetItem(QString::fromStdString(p->name));
|
||||
name_item->setToolTip(name_item->text());
|
||||
const auto& status_info = player_status.contains(p->game_status) ?
|
||||
player_status.at(p->game_status) :
|
||||
const auto it = player_status.find(p->game_status);
|
||||
const auto& status_info = it != player_status.end() ?
|
||||
it->second :
|
||||
std::make_pair(QStringLiteral("?"), QStringLiteral("?"));
|
||||
auto* status_item = new QTableWidgetItem(status_info.first);
|
||||
status_item->setToolTip(status_info.second);
|
||||
|
|
|
@ -175,40 +175,40 @@ void Settings::ApplyStyle()
|
|||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
if (stylesheet_contents.isEmpty())
|
||||
// Unlike other OSes we don't automatically get a default dark theme on Windows.
|
||||
// We manually load a dark palette for our included "(Dark)" style,
|
||||
// and for *any* external style when the system is in "Dark" mode.
|
||||
// Unfortunately it doesn't seem trivial to load a palette based on the stylesheet itself.
|
||||
if (style_type == StyleType::Dark || (style_type != StyleType::Light && IsSystemDark()))
|
||||
{
|
||||
// No theme selected or found. Usually we would just fallthrough and set an empty stylesheet
|
||||
// which would select Qt's default theme, but unlike other OSes we don't automatically get a
|
||||
// default dark theme on Windows when the user has selected dark mode in the Windows settings.
|
||||
// So manually check if the user wants dark mode and, if yes, load our embedded dark theme.
|
||||
if (style_type == StyleType::Dark || (style_type != StyleType::Light && IsSystemDark()))
|
||||
if (stylesheet_contents.isEmpty())
|
||||
{
|
||||
QFile file(QStringLiteral(":/dolphin_dark_win/dark.qss"));
|
||||
if (file.open(QFile::ReadOnly))
|
||||
stylesheet_contents = QString::fromUtf8(file.readAll().data());
|
||||
}
|
||||
|
||||
QPalette palette = qApp->style()->standardPalette();
|
||||
palette.setColor(QPalette::Window, QColor(32, 32, 32));
|
||||
palette.setColor(QPalette::WindowText, QColor(220, 220, 220));
|
||||
palette.setColor(QPalette::Base, QColor(32, 32, 32));
|
||||
palette.setColor(QPalette::AlternateBase, QColor(48, 48, 48));
|
||||
palette.setColor(QPalette::PlaceholderText, QColor(126, 126, 126));
|
||||
palette.setColor(QPalette::Text, QColor(220, 220, 220));
|
||||
palette.setColor(QPalette::Button, QColor(48, 48, 48));
|
||||
palette.setColor(QPalette::ButtonText, QColor(220, 220, 220));
|
||||
palette.setColor(QPalette::BrightText, QColor(255, 255, 255));
|
||||
palette.setColor(QPalette::Highlight, QColor(0, 120, 215));
|
||||
palette.setColor(QPalette::HighlightedText, QColor(255, 255, 255));
|
||||
palette.setColor(QPalette::Link, QColor(100, 160, 220));
|
||||
palette.setColor(QPalette::LinkVisited, QColor(100, 160, 220));
|
||||
qApp->setPalette(palette);
|
||||
}
|
||||
else
|
||||
{
|
||||
// reset any palette changes that may exist from a previously set dark mode
|
||||
if (s_default_palette)
|
||||
qApp->setPalette(*s_default_palette);
|
||||
}
|
||||
QPalette palette = qApp->style()->standardPalette();
|
||||
palette.setColor(QPalette::Window, QColor(32, 32, 32));
|
||||
palette.setColor(QPalette::WindowText, QColor(220, 220, 220));
|
||||
palette.setColor(QPalette::Base, QColor(32, 32, 32));
|
||||
palette.setColor(QPalette::AlternateBase, QColor(48, 48, 48));
|
||||
palette.setColor(QPalette::PlaceholderText, QColor(126, 126, 126));
|
||||
palette.setColor(QPalette::Text, QColor(220, 220, 220));
|
||||
palette.setColor(QPalette::Button, QColor(48, 48, 48));
|
||||
palette.setColor(QPalette::ButtonText, QColor(220, 220, 220));
|
||||
palette.setColor(QPalette::BrightText, QColor(255, 255, 255));
|
||||
palette.setColor(QPalette::Highlight, QColor(0, 120, 215));
|
||||
palette.setColor(QPalette::HighlightedText, QColor(255, 255, 255));
|
||||
palette.setColor(QPalette::Link, QColor(100, 160, 220));
|
||||
palette.setColor(QPalette::LinkVisited, QColor(100, 160, 220));
|
||||
qApp->setPalette(palette);
|
||||
}
|
||||
else
|
||||
{
|
||||
// reset any palette changes that may exist from a previously set dark mode
|
||||
if (s_default_palette)
|
||||
qApp->setPalette(*s_default_palette);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -731,7 +731,7 @@ void GameCubePane::LoadSettings()
|
|||
}
|
||||
}
|
||||
|
||||
m_skip_main_menu->setEnabled(have_menu);
|
||||
m_skip_main_menu->setEnabled(have_menu || !m_skip_main_menu->isChecked());
|
||||
m_skip_main_menu->setToolTip(have_menu ? QString{} : tr("Put IPL ROMs in User/GC/<region>."));
|
||||
|
||||
// Device Settings
|
||||
|
|
|
@ -50,13 +50,6 @@ AnalogStick::StateData AnalogStick::GetState() const
|
|||
}
|
||||
|
||||
AnalogStick::StateData AnalogStick::GetState(const InputOverrideFunction& override_func) const
|
||||
{
|
||||
bool override_occurred = false;
|
||||
return GetState(override_func, &override_occurred);
|
||||
}
|
||||
|
||||
AnalogStick::StateData AnalogStick::GetState(const InputOverrideFunction& override_func,
|
||||
bool* override_occurred) const
|
||||
{
|
||||
StateData state = GetState();
|
||||
if (!override_func)
|
||||
|
@ -65,13 +58,11 @@ AnalogStick::StateData AnalogStick::GetState(const InputOverrideFunction& overri
|
|||
if (const std::optional<ControlState> x_override = override_func(name, X_INPUT_OVERRIDE, state.x))
|
||||
{
|
||||
state.x = *x_override;
|
||||
*override_occurred = true;
|
||||
}
|
||||
|
||||
if (const std::optional<ControlState> y_override = override_func(name, Y_INPUT_OVERRIDE, state.y))
|
||||
{
|
||||
state.y = *y_override;
|
||||
*override_occurred = true;
|
||||
}
|
||||
|
||||
return state;
|
||||
|
|
|
@ -22,7 +22,6 @@ public:
|
|||
|
||||
StateData GetState() const;
|
||||
StateData GetState(const InputOverrideFunction& override_func) const;
|
||||
StateData GetState(const InputOverrideFunction& override_func, bool* override_occurred) const;
|
||||
|
||||
private:
|
||||
Control* GetModifierInput() const override;
|
||||
|
|
|
@ -77,6 +77,8 @@ void Attachments::LoadConfig(Common::IniFile::Section* sec, const std::string& b
|
|||
|
||||
void Attachments::SaveConfig(Common::IniFile::Section* sec, const std::string& base)
|
||||
{
|
||||
ControlGroup::SaveConfig(sec, base);
|
||||
|
||||
if (GetSelectionSetting().IsSimpleValue())
|
||||
{
|
||||
sec->Set(base + name, GetAttachmentList()[GetSelectedAttachment()]->GetName(), "None");
|
||||
|
|
|
@ -10,8 +10,6 @@
|
|||
|
||||
#include <hidclass.h>
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include "Common/Flag.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "InputCommon/ControllerInterface/DInput/DInput.h"
|
||||
|
@ -20,7 +18,6 @@
|
|||
|
||||
#pragma comment(lib, "OneCoreUAP.Lib")
|
||||
|
||||
static std::mutex s_populate_mutex;
|
||||
// TODO is this really needed?
|
||||
static Common::Flag s_first_populate_devices_asked;
|
||||
static HCMNOTIFICATION s_notify_handle;
|
||||
|
@ -52,7 +49,6 @@ _Pre_satisfies_(EventDataSize >= sizeof(CM_NOTIFY_EVENT_DATA)) static DWORD CALL
|
|||
// listen for it.
|
||||
if (s_first_populate_devices_asked.IsSet())
|
||||
{
|
||||
std::lock_guard lk_population(s_populate_mutex);
|
||||
// TODO: we could easily use the message passed alongside this event, which tells
|
||||
// whether a device was added or removed, to avoid removing old, still connected, devices
|
||||
g_controller_interface.PlatformPopulateDevices([&] {
|
||||
|
@ -96,17 +92,18 @@ InputBackend::InputBackend(ControllerInterface* controller_interface)
|
|||
|
||||
void InputBackend::PopulateDevices()
|
||||
{
|
||||
std::lock_guard lk_population(s_populate_mutex);
|
||||
s_first_populate_devices_asked.Set();
|
||||
ciface::DInput::PopulateDevices(GetHWND());
|
||||
ciface::XInput::PopulateDevices();
|
||||
ciface::WGInput::PopulateDevices();
|
||||
g_controller_interface.PlatformPopulateDevices([this] {
|
||||
s_first_populate_devices_asked.Set();
|
||||
ciface::DInput::PopulateDevices(GetHWND());
|
||||
ciface::XInput::PopulateDevices();
|
||||
ciface::WGInput::PopulateDevices();
|
||||
});
|
||||
}
|
||||
|
||||
void InputBackend::HandleWindowChange()
|
||||
{
|
||||
std::lock_guard lk_population(s_populate_mutex);
|
||||
ciface::DInput::ChangeWindow(GetHWND());
|
||||
g_controller_interface.PlatformPopulateDevices(
|
||||
[this] { ciface::DInput::ChangeWindow(GetHWND()); });
|
||||
}
|
||||
|
||||
InputBackend::~InputBackend()
|
||||
|
|
|
@ -380,7 +380,7 @@ std::unique_ptr<VKPipeline> VKPipeline::Create(const AbstractPipelineConfig& con
|
|||
static const VkRect2D scissor = {{0, 0}, {1, 1}};
|
||||
static const VkPipelineViewportStateCreateInfo viewport_state = {
|
||||
VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
|
||||
&depth_clamp_state,
|
||||
g_backend_info.bSupportsUnrestrictedDepthRange ? &depth_clamp_state : nullptr,
|
||||
0, // VkPipelineViewportStateCreateFlags flags;
|
||||
1, // uint32_t viewportCount
|
||||
&viewport, // const VkViewport* pViewports
|
||||
|
|
|
@ -664,7 +664,7 @@ bool VulkanContext::SelectDeviceExtensions(bool enable_surface)
|
|||
AddExtension(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, false);
|
||||
AddExtension(VK_EXT_MEMORY_BUDGET_EXTENSION_NAME, false);
|
||||
|
||||
if (!DriverDetails::HasBug(DriverDetails::BUG_BROKEN_D32F_CLEAR))
|
||||
if (!DriverDetails::HasBug(DriverDetails::BUG_BROKEN_DEPTH_CLAMP_CONTROL))
|
||||
{
|
||||
// Unrestricted depth range is one of the few extensions that changes the behavior
|
||||
// of Vulkan just by being enabled, so we rely on lazy evaluation to ensure it is
|
||||
|
|
|
@ -158,6 +158,8 @@ constexpr BugInfo m_known_bugs[] = {
|
|||
BUG_BROKEN_DYNAMIC_SAMPLER_INDEXING, -1.0, -1.0, true},
|
||||
{API_VULKAN, OS_ANDROID, VENDOR_QUALCOMM, DRIVER_QUALCOMM, Family::UNKNOWN,
|
||||
BUG_SLOW_OPTIMAL_IMAGE_TO_BUFFER_COPY, -1.0, -1.0, true},
|
||||
{API_VULKAN, OS_ALL, VENDOR_ATI, DRIVER_ATI, Family::UNKNOWN, BUG_BROKEN_DEPTH_CLAMP_CONTROL,
|
||||
-1.0, -1.0, true},
|
||||
};
|
||||
|
||||
static std::map<Bug, BugInfo> m_bugs;
|
||||
|
@ -297,6 +299,7 @@ static const char* to_string(Bug bug)
|
|||
case BUG_BROKEN_DISCARD_WITH_EARLY_Z: return "broken-discard-with-early-z";
|
||||
case BUG_BROKEN_DYNAMIC_SAMPLER_INDEXING: return "broken-dynamic-sampler-indexing";
|
||||
case BUG_SLOW_OPTIMAL_IMAGE_TO_BUFFER_COPY: return "slow-optimal-image-to-buffer-copy";
|
||||
case BUG_BROKEN_DEPTH_CLAMP_CONTROL: return "broken-depth-clamp-control";
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
|
|
@ -342,7 +342,14 @@ enum Bug
|
|||
// Affected devices: Adreno
|
||||
// Started Version: -1
|
||||
// Ended Version: -1
|
||||
BUG_SLOW_OPTIMAL_IMAGE_TO_BUFFER_COPY
|
||||
BUG_SLOW_OPTIMAL_IMAGE_TO_BUFFER_COPY,
|
||||
|
||||
// BUG: Incorrect implementation of VK_EXT_depth_clamp_control causes incorrect depth values to
|
||||
// be written to the depth buffer.
|
||||
// Affected devices: Official AMD (RADV is unaffected)
|
||||
// Started Version: -1
|
||||
// Ended Version: -1
|
||||
BUG_BROKEN_DEPTH_CLAMP_CONTROL
|
||||
};
|
||||
|
||||
// Initializes our internal vendor, device family, and driver version
|
||||
|
|
|
@ -197,14 +197,13 @@ void GraphicsModManager::Load(const GraphicsModGroupConfig& config)
|
|||
{
|
||||
for (const GraphicsTargetGroupConfig& group : mod.m_groups)
|
||||
{
|
||||
if (m_groups.contains(group.m_name))
|
||||
if (const bool inserted = m_groups.insert(group.m_name).second; !inserted)
|
||||
{
|
||||
WARN_LOG_FMT(
|
||||
VIDEO,
|
||||
"Specified graphics mod group '{}' for mod '{}' is already specified by another mod.",
|
||||
group.m_name, mod.m_title);
|
||||
}
|
||||
m_groups.insert(group.m_name);
|
||||
|
||||
const auto internal_group = fmt::format("{}.{}", mod.m_title, group.m_name);
|
||||
for (const GraphicsTargetConfig& target : group.m_targets)
|
||||
|
|
|
@ -1535,26 +1535,21 @@ const AbstractPipeline* ShaderCache::GetTextureReinterpretPipeline(TextureFormat
|
|||
TextureFormat to_format)
|
||||
{
|
||||
const auto key = std::make_pair(from_format, to_format);
|
||||
auto iter = m_texture_reinterpret_pipelines.find(key);
|
||||
if (iter != m_texture_reinterpret_pipelines.end())
|
||||
const auto [iter, inserted] = m_texture_reinterpret_pipelines.emplace(key, nullptr);
|
||||
|
||||
if (!inserted)
|
||||
return iter->second.get();
|
||||
|
||||
std::string shader_source =
|
||||
FramebufferShaderGen::GenerateTextureReinterpretShader(from_format, to_format);
|
||||
if (shader_source.empty())
|
||||
{
|
||||
m_texture_reinterpret_pipelines.emplace(key, nullptr);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<AbstractShader> shader = g_gfx->CreateShaderFromSource(
|
||||
ShaderStage::Pixel, shader_source,
|
||||
fmt::format("Texture reinterpret pixel shader: {} to {}", from_format, to_format));
|
||||
if (!shader)
|
||||
{
|
||||
m_texture_reinterpret_pipelines.emplace(key, nullptr);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AbstractPipelineConfig config;
|
||||
config.vertex_format = nullptr;
|
||||
|
@ -1566,8 +1561,8 @@ const AbstractPipeline* ShaderCache::GetTextureReinterpretPipeline(TextureFormat
|
|||
config.blending_state = RenderState::GetNoBlendingBlendState();
|
||||
config.framebuffer_state = RenderState::GetRGBA8FramebufferState();
|
||||
config.usage = AbstractPipelineUsage::Utility;
|
||||
auto iiter = m_texture_reinterpret_pipelines.emplace(key, g_gfx->CreatePipeline(config));
|
||||
return iiter.first->second.get();
|
||||
iter->second = g_gfx->CreatePipeline(config);
|
||||
return iter->second.get();
|
||||
}
|
||||
|
||||
const AbstractShader*
|
||||
|
@ -1576,17 +1571,14 @@ ShaderCache::GetTextureDecodingShader(TextureFormat format,
|
|||
{
|
||||
const auto key = std::make_pair(static_cast<u32>(format),
|
||||
static_cast<u32>(palette_format.value_or(TLUTFormat::IA8)));
|
||||
const auto iter = m_texture_decoding_shaders.find(key);
|
||||
if (iter != m_texture_decoding_shaders.end())
|
||||
const auto [iter, inserted] = m_texture_decoding_shaders.emplace(key, nullptr);
|
||||
if (!inserted)
|
||||
return iter->second.get();
|
||||
|
||||
const std::string shader_source =
|
||||
TextureConversionShaderTiled::GenerateDecodingShader(format, palette_format, APIType::OpenGL);
|
||||
if (shader_source.empty())
|
||||
{
|
||||
m_texture_decoding_shaders.emplace(key, nullptr);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const std::string name =
|
||||
palette_format.has_value() ?
|
||||
|
@ -1596,12 +1588,9 @@ ShaderCache::GetTextureDecodingShader(TextureFormat format,
|
|||
std::unique_ptr<AbstractShader> shader =
|
||||
g_gfx->CreateShaderFromSource(ShaderStage::Compute, shader_source, name);
|
||||
if (!shader)
|
||||
{
|
||||
m_texture_decoding_shaders.emplace(key, nullptr);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto iiter = m_texture_decoding_shaders.emplace(key, std::move(shader));
|
||||
return iiter.first->second.get();
|
||||
iter->second = std::move(shader);
|
||||
return iter->second.get();
|
||||
}
|
||||
} // namespace VideoCommon
|
||||
|
|
|
@ -652,8 +652,8 @@ void TextureCacheBase::DoSaveState(PointerWrap& p)
|
|||
|
||||
auto refpair1 = std::make_pair(*id1, *id2);
|
||||
auto refpair2 = std::make_pair(*id2, *id1);
|
||||
if (!reference_pairs.contains(refpair1) && !reference_pairs.contains(refpair2))
|
||||
reference_pairs.insert(refpair1);
|
||||
if (!reference_pairs.contains(refpair2))
|
||||
reference_pairs.insert(std::move(refpair1));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1029,6 +1029,11 @@ SamplerState TextureCacheBase::GetSamplerState(u32 index, float custom_tex_scale
|
|||
// Anisotropic filtering option.
|
||||
if (g_ActiveConfig.iMaxAnisotropy != AnisotropicFilteringMode::Default &&
|
||||
IsAnisostropicEnhancementSafe(tm0))
|
||||
{
|
||||
state.tm0.anisotropic_filtering = Common::ToUnderlying(g_ActiveConfig.iMaxAnisotropy);
|
||||
}
|
||||
|
||||
if (state.tm0.anisotropic_filtering != 0)
|
||||
{
|
||||
// https://www.opengl.org/registry/specs/EXT/texture_filter_anisotropic.txt
|
||||
// For predictable results on all hardware/drivers, only use one of:
|
||||
|
@ -1041,7 +1046,6 @@ SamplerState TextureCacheBase::GetSamplerState(u32 index, float custom_tex_scale
|
|||
state.tm0.mag_filter = FilterMode::Linear;
|
||||
if (tm0.mipmap_filter != MipMode::None)
|
||||
state.tm0.mipmap_filter = FilterMode::Linear;
|
||||
state.tm0.anisotropic_filtering = Common::ToUnderlying(g_ActiveConfig.iMaxAnisotropy);
|
||||
}
|
||||
|
||||
if (has_arbitrary_mips && tm0.mipmap_filter != MipMode::None)
|
||||
|
|
|
@ -74,6 +74,28 @@ VertexShaderUid GetVertexShaderUid()
|
|||
return out;
|
||||
}
|
||||
|
||||
static void WritePrimitiveExpand(APIType api_type, const ShaderHostConfig& host_config,
|
||||
const vertex_shader_uid_data* uid_data, ShaderCode& out)
|
||||
{
|
||||
if (uid_data->vs_expand == VSExpand::None)
|
||||
return;
|
||||
|
||||
out.Write("InputData dolphin_primitive_expand_data(int index_offset)\n");
|
||||
out.Write("{{\n");
|
||||
if (api_type == APIType::D3D)
|
||||
{
|
||||
// D3D doesn't include the base vertex in SV_VertexID
|
||||
// See comment in UberShaderVertex for details
|
||||
out.Write("\tuint vertex_id = (gl_VertexID >> 2) + base_vertex;\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
out.Write("\tuint vertex_id = uint(gl_VertexID) >> 2u;\n");
|
||||
}
|
||||
out.Write("\treturn input_buffer[vertex_id + index_offset];\n");
|
||||
out.Write("}}\n\n");
|
||||
}
|
||||
|
||||
static void WriteTransformMatrices(APIType api_type, const ShaderHostConfig& host_config,
|
||||
const vertex_shader_uid_data* uid_data, ShaderCode& out)
|
||||
{
|
||||
|
@ -82,6 +104,11 @@ static void WriteTransformMatrices(APIType api_type, const ShaderHostConfig& hos
|
|||
out.Write("\tmat3x4 result;\n");
|
||||
if ((uid_data->components & VB_HAS_POSMTXIDX) != 0)
|
||||
{
|
||||
if (uid_data->vs_expand != VSExpand::None)
|
||||
{
|
||||
out.Write("\tInputData i = dolphin_primitive_expand_data(0);\n");
|
||||
out.Write("\tuvec4 posmtx = unpack_ubyte4(i.posmtx);\n");
|
||||
}
|
||||
// Vertex format has a per-vertex matrix
|
||||
out.Write("\tint posidx = int(posmtx.r);\n"
|
||||
"\tresult[0] = " I_TRANSFORMMATRICES "[posidx];\n"
|
||||
|
@ -108,6 +135,11 @@ static void WriteTransformMatrices(APIType api_type, const ShaderHostConfig& hos
|
|||
out.Write("\tmat3 result;\n");
|
||||
if ((uid_data->components & VB_HAS_POSMTXIDX) != 0)
|
||||
{
|
||||
if (uid_data->vs_expand != VSExpand::None)
|
||||
{
|
||||
out.Write("\tInputData i = dolphin_primitive_expand_data(0);\n");
|
||||
out.Write("\tuvec4 posmtx = unpack_ubyte4(i.posmtx);\n");
|
||||
}
|
||||
// Vertex format has a per-vertex matrix
|
||||
out.Write("\tint posidx = int(posmtx.r);\n");
|
||||
out.Write("\tint normidx = posidx & 31;\n"
|
||||
|
@ -401,7 +433,8 @@ ShaderCode GenerateVertexShaderCode(APIType api_type, const ShaderHostConfig& ho
|
|||
if (uid_data->components & VB_HAS_POSMTXIDX)
|
||||
{
|
||||
out.Write(" uint posmtx;\n");
|
||||
input_extract.Write("uint4 posmtx = unpack_ubyte4(i.posmtx);\n");
|
||||
// Note: posmtx is handled in the matrix transform functions and
|
||||
// doesn't need to be added to 'input_extract'
|
||||
}
|
||||
if (uid_data->position_has_3_elems)
|
||||
{
|
||||
|
@ -507,6 +540,7 @@ ShaderCode GenerateVertexShaderCode(APIType api_type, const ShaderHostConfig& ho
|
|||
}
|
||||
|
||||
// Note: this is done after to ensure above global variables are accessible
|
||||
WritePrimitiveExpand(api_type, host_config, uid_data, out);
|
||||
WriteTransformMatrices(api_type, host_config, uid_data, out);
|
||||
WriteTexCoordTransforms(api_type, host_config, uid_data, out);
|
||||
WriteVertexDefines(api_type, host_config, uid_data, out);
|
||||
|
@ -532,15 +566,7 @@ ShaderCode GenerateVertexShaderCode(APIType api_type, const ShaderHostConfig& ho
|
|||
|
||||
if (uid_data->vs_expand != VSExpand::None)
|
||||
{
|
||||
out.Write("bool is_bottom = (gl_VertexID & 2) != 0;\n"
|
||||
"bool is_right = (gl_VertexID & 1) != 0;\n");
|
||||
// D3D doesn't include the base vertex in SV_VertexID
|
||||
// See comment in UberShaderVertex for details
|
||||
if (api_type == APIType::D3D)
|
||||
out.Write("uint vertex_id = (gl_VertexID >> 2) + base_vertex;\n");
|
||||
else
|
||||
out.Write("uint vertex_id = uint(gl_VertexID) >> 2u;\n");
|
||||
out.Write("InputData i = input_buffer[vertex_id];\n"
|
||||
out.Write("InputData i = dolphin_primitive_expand_data(0);\n"
|
||||
"{}",
|
||||
input_extract.GetBuffer());
|
||||
}
|
||||
|
@ -691,14 +717,15 @@ ShaderCode GenerateVertexShaderCode(APIType api_type, const ShaderHostConfig& ho
|
|||
|
||||
if (uid_data->vs_expand == VSExpand::Line)
|
||||
{
|
||||
out.Write("bool is_bottom = (gl_VertexID & 2) != 0;\n");
|
||||
out.Write("// Line expansion\n"
|
||||
"uint other_id = vertex_id;\n"
|
||||
"int id_offset = 0;\n"
|
||||
"if (is_bottom) {{\n"
|
||||
" other_id -= 1u;\n"
|
||||
" id_offset -= 1;\n"
|
||||
"}} else {{\n"
|
||||
" other_id += 1u;\n"
|
||||
" id_offset += 1;\n"
|
||||
"}}\n"
|
||||
"InputData other = input_buffer[other_id];\n");
|
||||
"InputData other = dolphin_primitive_expand_data(id_offset);\n");
|
||||
if (uid_data->position_has_3_elems)
|
||||
out.Write("float4 other_pos = float4(other.pos0, other.pos1, other.pos2, 1.0f);\n");
|
||||
else
|
||||
|
@ -716,10 +743,16 @@ ShaderCode GenerateVertexShaderCode(APIType api_type, const ShaderHostConfig& ho
|
|||
{
|
||||
out.Write("other_pos = vec4(other_pos * dolphin_position_matrix(), 1.0);\n");
|
||||
}
|
||||
|
||||
// Variable needed by GenerateVSLineExpansion
|
||||
out.Write("bool is_right = (gl_VertexID & 1) != 0;\n");
|
||||
GenerateVSLineExpansion(out, "", uid_data->numTexGens);
|
||||
}
|
||||
else if (uid_data->vs_expand == VSExpand::Point)
|
||||
{
|
||||
// Variables needed by GenerateVSPointExpansion
|
||||
out.Write("bool is_bottom = (gl_VertexID & 2) != 0;\n");
|
||||
out.Write("bool is_right = (gl_VertexID & 1) != 0;\n");
|
||||
out.Write("// Point expansion\n");
|
||||
GenerateVSPointExpansion(out, "", uid_data->numTexGens);
|
||||
}
|
||||
|
|
|
@ -223,8 +223,6 @@ enum
|
|||
XFMEM_SETMATRIXINDA = 0x1018,
|
||||
XFMEM_SETMATRIXINDB = 0x1019,
|
||||
XFMEM_SETVIEWPORT = 0x101a,
|
||||
XFMEM_SETZSCALE = 0x101c,
|
||||
XFMEM_SETZOFFSET = 0x101f,
|
||||
XFMEM_SETPROJECTION = 0x1020,
|
||||
// XFMEM_SETPROJECTIONB = 0x1021,
|
||||
// XFMEM_SETPROJECTIONC = 0x1022,
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
% Document front page material
|
||||
\title{\textbf{\Huge GameCube DSP User's Manual}}
|
||||
\author{Reverse-engineered and documented by Duddie \\ \href{mailto:duddie@walla.com}{duddie@walla.com}}
|
||||
\date{\today\\v0.1.7}
|
||||
\date{\today\\v0.1.8}
|
||||
|
||||
% Title formatting commands
|
||||
\newcommand{\OpcodeTitle}[1]{\subsection{#1}\label{instruction:#1}}
|
||||
|
@ -265,6 +265,7 @@ The purpose of this documentation is purely academic and it aims at understandin
|
|||
0.1.5 & 2022.09.29 & vpelletier & Fixed \texttt{BLOOP} and \texttt{BLOOPI} suboperation order \\ \hline
|
||||
0.1.6 & 2022.06.20 & xperia64 & Accelerator documentation updates, fix register typo in ANDC and ORC descriptions \\ \hline
|
||||
0.1.7 & 2025.04.21 & Tilka & Fixed typos and complained about GFDL \\ \hline
|
||||
0.1.8 & 2025.07.27 & Tilka & Fixed some bit pattern inconsistencies in the 'LDAX* opcodes \\ \hline
|
||||
\end{tabular}
|
||||
\end{table}
|
||||
|
||||
|
@ -4615,7 +4616,7 @@ When the main and extension opcodes write to the same register, the register is
|
|||
|
||||
\begin{DSPOpcode}{'LDAXM}
|
||||
\begin{DSPOpcodeBytefield}{16}
|
||||
\monobitbox{4}{xxxx} & \monobitbox{4}{xxxx} & \monobitbox{4}{11sr} & \monobitbox{4}{0011}
|
||||
\monobitbox{4}{xxxx} & \monobitbox{4}{xxxx} & \monobitbox{4}{11sr} & \monobitbox{4}{1011}
|
||||
\end{DSPOpcodeBytefield}
|
||||
|
||||
\begin{DSPOpcodeFormat}
|
||||
|
@ -4642,7 +4643,7 @@ When the main and extension opcodes write to the same register, the register is
|
|||
|
||||
\begin{DSPOpcode}{'LDAXNM}
|
||||
\begin{DSPOpcodeBytefield}{16}
|
||||
\monobitbox{4}{xxxx} & \monobitbox{4}{xxxx} & \monobitbox{4}{11sr} & \monobitbox{4}{0011}
|
||||
\monobitbox{4}{xxxx} & \monobitbox{4}{xxxx} & \monobitbox{4}{11sr} & \monobitbox{4}{1111}
|
||||
\end{DSPOpcodeBytefield}
|
||||
|
||||
\begin{DSPOpcodeFormat}
|
||||
|
@ -4670,7 +4671,7 @@ When the main and extension opcodes write to the same register, the register is
|
|||
|
||||
\begin{DSPOpcode}{'LDAXN}
|
||||
\begin{DSPOpcodeBytefield}{16}
|
||||
\monobitbox{4}{xxxx} & \monobitbox{4}{xxxx} & \monobitbox{4}{11sr} & \monobitbox{4}{0011}
|
||||
\monobitbox{4}{xxxx} & \monobitbox{4}{xxxx} & \monobitbox{4}{11sr} & \monobitbox{4}{0111}
|
||||
\end{DSPOpcodeBytefield}
|
||||
|
||||
\begin{DSPOpcodeFormat}
|
||||
|
@ -5214,7 +5215,7 @@ Instruction & Opcode & Page \\ \hline
|
|||
\OpcodeRow{xxxx xxxx 11dr 10ss}{'LDM}
|
||||
\OpcodeRow{xxxx xxxx 11sr 1011}{'LDAXM}
|
||||
\OpcodeRow{xxxx xxxx 11dr 11ss}{'LDNM}
|
||||
\OpcodeRow{xxxx xxxx 11dr 1111}{'LDAXNM}
|
||||
\OpcodeRow{xxxx xxxx 11sr 1111}{'LDAXNM}
|
||||
\end{longtable}
|
||||
\end{center}
|
||||
|
||||
|
|
|
@ -76,6 +76,7 @@
|
|||
// Font data is encoded in 2 bit greyscale and in 8x8 blocks.
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue