Merge latest master changes

This commit is contained in:
Rodolfo Bogado 2017-07-23 21:27:13 -03:00
parent 279892b39e
commit 74a03d9345
168 changed files with 14290 additions and 13885 deletions

View file

@ -16,7 +16,3 @@ EmulationIssues = Needs efb to Ram for proper lighting.
[ActionReplay]
# Add action replay cheats here.
[Video_Hacks]
EFBToTextureEnable = False

View file

@ -5,8 +5,8 @@ SRCDIR=Source
find $SRCDIR -name '*.cpp' -o -name '*.h' -o -name '*.c' | \
xgettext -d dolphin-emu -s --keyword=_ --keyword=wxTRANSLATE --keyword=SuccessAlertT \
--keyword=PanicAlertT --keyword=PanicYesNoT --keyword=AskYesNoT --keyword=CriticalAlertT \
--keyword=GetStringT --keyword=_trans --add-comments=i18n -p ./Languages/po \
-o dolphin-emu.pot -f - --package-name="Dolphin Emulator"
--keyword=GetStringT --keyword=_trans --keyword=tr --keyword=QT_TR_NOOP \
--add-comments=i18n -p ./Languages/po -o dolphin-emu.pot -f - --package-name="Dolphin Emulator"
sed -i "s/SOME DESCRIPTIVE TITLE\./Translation of dolphin-emu.pot to LANGUAGE/" Languages/po/dolphin-emu.pot
sed -i "s/YEAR THE PACKAGE'S COPYRIGHT HOLDER/2003-2013/" Languages/po/dolphin-emu.pot

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -125,7 +125,7 @@ void Host_SetStartupDebuggingParameters()
{
}
bool Host_UIHasFocus()
bool Host_UINeedsControllerState()
{
return true;
}
@ -144,10 +144,6 @@ void Host_ConnectWiimote(int wm_idx, bool connect)
{
}
void Host_SetWiiMoteConnectionState(int _State)
{
}
void Host_ShowVideoConfig(void*, const std::string&)
{
}

View file

@ -13,7 +13,7 @@
#define _assert_msg_(_t_, _a_, _fmt_, ...) \
if (!(_a_)) \
{ \
if (!PanicYesNo(_fmt_, __VA_ARGS__)) \
if (!PanicYesNo(_fmt_ "\n\nIgnore and continue?", __VA_ARGS__)) \
Crash(); \
}

View file

@ -71,7 +71,7 @@ void Section::Set(const std::string& key, double newValue)
void Section::Set(const std::string& key, int newValue)
{
Section::Set(key, StringFromInt(newValue));
Section::Set(key, std::to_string(newValue));
}
void Section::Set(const std::string& key, bool newValue)

View file

@ -51,38 +51,67 @@
// REMEMBER: strdup considered harmful!
namespace File
{
// Returns true if file filename exists
bool Exists(const std::string& filename)
{
struct stat file_info;
#ifdef _WIN32
int result = _tstat64(UTF8ToTStr(filename).c_str(), &file_info);
#else
int result = stat(filename.c_str(), &file_info);
#endif
return (result == 0);
FileInfo::FileInfo(const std::string& path)
{
m_exists = _tstat64(UTF8ToTStr(path).c_str(), &m_stat) == 0;
}
// Returns true if filename is a directory
bool IsDirectory(const std::string& filename)
FileInfo::FileInfo(const char* path) : FileInfo(std::string(path))
{
struct stat file_info;
#ifdef _WIN32
int result = _tstat64(UTF8ToTStr(filename).c_str(), &file_info);
}
#else
int result = stat(filename.c_str(), &file_info);
FileInfo::FileInfo(const std::string& path) : FileInfo(path.c_str())
{
}
FileInfo::FileInfo(const char* path)
{
m_exists = stat(path, &m_stat) == 0;
}
#endif
if (result < 0)
{
WARN_LOG(COMMON, "IsDirectory: stat failed on %s: %s", filename.c_str(), strerror(errno));
return false;
}
FileInfo::FileInfo(int fd)
{
m_exists = fstat(fd, &m_stat);
}
return S_ISDIR(file_info.st_mode);
bool FileInfo::Exists() const
{
return m_exists;
}
bool FileInfo::IsDirectory() const
{
return m_exists ? S_ISDIR(m_stat.st_mode) : false;
}
bool FileInfo::IsFile() const
{
return m_exists ? !S_ISDIR(m_stat.st_mode) : false;
}
u64 FileInfo::GetSize() const
{
return IsFile() ? m_stat.st_size : 0;
}
// Returns true if the path exists
bool Exists(const std::string& path)
{
return FileInfo(path).Exists();
}
// Returns true if the path exists and is a directory
bool IsDirectory(const std::string& path)
{
return FileInfo(path).IsDirectory();
}
// Returns true if the path exists and is a file
bool IsFile(const std::string& path)
{
return FileInfo(path).IsFile();
}
// Deletes a given filename, return true on success
@ -91,16 +120,18 @@ bool Delete(const std::string& filename)
{
INFO_LOG(COMMON, "Delete: file %s", filename.c_str());
const FileInfo file_info(filename);
// Return true because we care about the file no
// being there, not the actual delete.
if (!Exists(filename))
if (!file_info.Exists())
{
WARN_LOG(COMMON, "Delete: %s does not exist", filename.c_str());
return true;
}
// We can't delete a directory
if (IsDirectory(filename))
if (file_info.IsDirectory())
{
WARN_LOG(COMMON, "Delete failed: %s is a directory", filename.c_str());
return false;
@ -163,7 +194,7 @@ bool CreateFullPath(const std::string& fullPath)
int panicCounter = 100;
INFO_LOG(COMMON, "CreateFullPath: path %s", fullPath.c_str());
if (File::Exists(fullPath))
if (Exists(fullPath))
{
INFO_LOG(COMMON, "CreateFullPath: path exists %s", fullPath.c_str());
return true;
@ -181,7 +212,7 @@ bool CreateFullPath(const std::string& fullPath)
// Include the '/' so the first call is CreateDir("/") rather than CreateDir("")
std::string const subPath(fullPath.substr(0, position + 1));
if (!File::IsDirectory(subPath))
if (!IsDirectory(subPath))
File::CreateDir(subPath);
// A safety check
@ -201,7 +232,7 @@ bool DeleteDir(const std::string& filename)
INFO_LOG(COMMON, "DeleteDir: directory %s", filename.c_str());
// check if a directory
if (!File::IsDirectory(filename))
if (!IsDirectory(filename))
{
ERROR_LOG(COMMON, "DeleteDir: Not a directory %s", filename.c_str());
return false;
@ -344,46 +375,16 @@ bool Copy(const std::string& srcFilename, const std::string& destFilename)
#endif
}
// Returns the size of filename (64bit)
u64 GetSize(const std::string& filename)
// Returns the size of a file (or returns 0 if the path isn't a file that exists)
u64 GetSize(const std::string& path)
{
if (!Exists(filename))
{
WARN_LOG(COMMON, "GetSize: failed %s: No such file", filename.c_str());
return 0;
}
if (IsDirectory(filename))
{
WARN_LOG(COMMON, "GetSize: failed %s: is a directory", filename.c_str());
return 0;
}
struct stat buf;
#ifdef _WIN32
if (_tstat64(UTF8ToTStr(filename).c_str(), &buf) == 0)
#else
if (stat(filename.c_str(), &buf) == 0)
#endif
{
DEBUG_LOG(COMMON, "GetSize: %s: %lld", filename.c_str(), (long long)buf.st_size);
return buf.st_size;
}
ERROR_LOG(COMMON, "GetSize: Stat failed %s: %s", filename.c_str(), GetLastErrorMsg().c_str());
return 0;
return FileInfo(path).GetSize();
}
// Overloaded GetSize, accepts file descriptor
u64 GetSize(const int fd)
{
struct stat buf;
if (fstat(fd, &buf) != 0)
{
ERROR_LOG(COMMON, "GetSize: stat failed %i: %s", fd, GetLastErrorMsg().c_str());
return 0;
}
return buf.st_size;
return FileInfo(fd).GetSize();
}
// Overloaded GetSize, accepts FILE*
@ -458,7 +459,8 @@ FSTEntry ScanDirectoryTree(const std::string& directory, bool recursive)
continue;
auto physical_name = directory + DIR_SEP + virtual_name;
FSTEntry entry;
entry.isDirectory = IsDirectory(physical_name);
const FileInfo file_info(physical_name);
entry.isDirectory = file_info.IsDirectory();
if (entry.isDirectory)
{
if (recursive)
@ -469,7 +471,7 @@ FSTEntry ScanDirectoryTree(const std::string& directory, bool recursive)
}
else
{
entry.size = GetSize(physical_name);
entry.size = file_info.GetSize();
}
entry.virtualName = virtual_name;
entry.physicalName = physical_name;
@ -561,9 +563,9 @@ void CopyDir(const std::string& source_path, const std::string& dest_path)
{
if (source_path == dest_path)
return;
if (!File::Exists(source_path))
if (!Exists(source_path))
return;
if (!File::Exists(dest_path))
if (!Exists(dest_path))
File::CreateFullPath(dest_path);
#ifdef _WIN32
@ -596,11 +598,11 @@ void CopyDir(const std::string& source_path, const std::string& dest_path)
std::string dest = dest_path + DIR_SEP + virtualName;
if (IsDirectory(source))
{
if (!File::Exists(dest))
if (!Exists(dest))
File::CreateFullPath(dest + DIR_SEP);
CopyDir(source, dest);
}
else if (!File::Exists(dest))
else if (!Exists(dest))
File::Copy(source, dest);
#ifdef _WIN32
} while (FindNextFile(hFind, &ffd) != 0);
@ -784,9 +786,9 @@ static void RebuildUserDirectories(unsigned int dir_index)
s_user_paths[D_MEMORYWATCHER_IDX] = s_user_paths[D_USER_IDX] + MEMORYWATCHER_DIR DIR_SEP;
s_user_paths[F_MEMORYWATCHERLOCATIONS_IDX] =
s_user_paths[D_MEMORYWATCHER_IDX] + MEMORYWATCHER_LOCATIONS;
s_user_paths[D_MEMORYWATCHER_IDX] + MEMORYWATCHER_LOCATIONS;
s_user_paths[F_MEMORYWATCHERSOCKET_IDX] =
s_user_paths[D_MEMORYWATCHER_IDX] + MEMORYWATCHER_SOCKET;
s_user_paths[D_MEMORYWATCHER_IDX] + MEMORYWATCHER_SOCKET;
// The shader cache has moved to the cache directory, so remove the old one.
// TODO: remove that someday.
@ -856,12 +858,12 @@ void SetUserPath(unsigned int dir_index, const std::string& path)
std::string GetThemeDir(const std::string& theme_name)
{
std::string dir = File::GetUserPath(D_THEMES_IDX) + theme_name + "/";
if (File::Exists(dir))
if (Exists(dir))
return dir;
// If the theme doesn't exist in the user dir, load from shared directory
dir = GetSysDirectory() + THEMES_DIR "/" + theme_name + "/";
if (File::Exists(dir))
if (Exists(dir))
return dir;
// If the theme doesn't exist at all, load the default theme

View file

@ -9,6 +9,8 @@
#include <string>
#include <vector>
#include <sys/stat.h>
#include "Common/CommonTypes.h"
#include "Common/NonCopyable.h"
@ -81,14 +83,42 @@ struct FSTEntry
std::vector<FSTEntry> children;
};
// Returns true if file filename exists
bool Exists(const std::string& filename);
// The functions in this class are functionally identical to the standalone functions
// below, but if you are going to be calling more than one of the functions using the
// same path, creating a single FileInfo object and calling its functions multiple
// times is faster than calling standalone functions multiple times.
class FileInfo final
{
public:
explicit FileInfo(const std::string& path);
explicit FileInfo(const char* path);
explicit FileInfo(int fd);
// Returns true if filename is a directory
bool IsDirectory(const std::string& filename);
// Returns true if the path exists
bool Exists() const;
// Returns true if the path exists and is a directory
bool IsDirectory() const;
// Returns true if the path exists and is a file
bool IsFile() const;
// Returns the size of a file (or returns 0 if the path doesn't refer to a file)
u64 GetSize() const;
// Returns the size of filename (64bit)
u64 GetSize(const std::string& filename);
private:
struct stat m_stat;
bool m_exists;
};
// Returns true if the path exists
bool Exists(const std::string& path);
// Returns true if the path exists and is a directory
bool IsDirectory(const std::string& path);
// Returns true if the path exists and is a file
bool IsFile(const std::string& path);
// Returns the size of a file (or returns 0 if the path isn't a file that exists)
u64 GetSize(const std::string& path);
// Overloaded GetSize, accepts file descriptor
u64 GetSize(const int fd);

View file

@ -17,7 +17,7 @@ class cInterfaceAGL : public cInterfaceBase
{
public:
void Swap() override;
bool Create(void* window_handle, bool core) override;
bool Create(void* window_handle, bool stereo, bool core) override;
bool MakeCurrent() override;
bool ClearCurrent() override;
void Shutdown() override;

View file

@ -51,12 +51,15 @@ void cInterfaceAGL::Swap()
// Create rendering window.
// Call browser: Core.cpp:EmuThread() > main.cpp:Video_Initialize()
bool cInterfaceAGL::Create(void* window_handle, bool core)
bool cInterfaceAGL::Create(void* window_handle, bool stereo, bool core)
{
NSOpenGLPixelFormatAttribute attr[] = {NSOpenGLPFADoubleBuffer, NSOpenGLPFAOpenGLProfile,
core ? NSOpenGLProfileVersion3_2Core :
NSOpenGLProfileVersionLegacy,
NSOpenGLPFAAccelerated, 0};
NSOpenGLPixelFormatAttribute attr[] = {
NSOpenGLPFADoubleBuffer,
NSOpenGLPFAOpenGLProfile,
core ? NSOpenGLProfileVersion3_2Core : NSOpenGLProfileVersionLegacy,
NSOpenGLPFAAccelerated,
stereo ? NSOpenGLPFAStereo : static_cast<NSOpenGLPixelFormatAttribute>(0),
0};
NSOpenGLPixelFormat* fmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:attr];
if (fmt == nil)
{

View file

@ -13,7 +13,7 @@ void cInterfaceBGL::Swap()
m_gl->SwapBuffers();
}
bool cInterfaceBGL::Create(void* window_handle, bool core)
bool cInterfaceBGL::Create(void* window_handle, bool stereo, bool core)
{
m_window = static_cast<BWindow*>(window_handle);

View file

@ -14,7 +14,7 @@ class cInterfaceBGL final : public cInterfaceBase
public:
void Swap() override;
void* GetFuncAddress(const std::string& name) override;
bool Create(void* window_handle, bool core) override;
bool Create(void* window_handle, bool stereo, bool core) override;
bool MakeCurrent() override;
bool ClearCurrent() override;
void Shutdown() override;

View file

@ -111,7 +111,7 @@ void cInterfaceEGL::DetectMode()
// Create rendering window.
// Call browser: Core.cpp:EmuThread() > main.cpp:Video_Initialize()
bool cInterfaceEGL::Create(void* window_handle, bool core)
bool cInterfaceEGL::Create(void* window_handle, bool stereo, bool core)
{
EGLint egl_major, egl_minor;
bool supports_core_profile = false;

View file

@ -38,7 +38,7 @@ public:
void SwapInterval(int interval) override;
void SetMode(GLInterfaceMode mode) override { s_opengl_mode = mode; }
void* GetFuncAddress(const std::string& name) override;
bool Create(void* window_handle, bool core) override;
bool Create(void* window_handle, bool stereo, bool core) override;
bool Create(cInterfaceBase* main_context) override;
bool MakeCurrent() override;
bool ClearCurrent() override;

View file

@ -43,7 +43,7 @@ void cInterfaceGLX::Swap()
// Create rendering window.
// Call browser: Core.cpp:EmuThread() > main.cpp:Video_Initialize()
bool cInterfaceGLX::Create(void* window_handle, bool core)
bool cInterfaceGLX::Create(void* window_handle, bool stereo, bool core)
{
dpy = XOpenDisplay(nullptr);
int screen = DefaultScreen(dpy);
@ -87,6 +87,8 @@ bool cInterfaceGLX::Create(void* window_handle, bool core)
0,
GLX_DOUBLEBUFFER,
True,
GLX_STEREO,
stereo ? True : False,
None};
int fbcount = 0;
GLXFBConfig* fbc = glXChooseFBConfig(dpy, screen, visual_attribs, &fbcount);

View file

@ -24,7 +24,7 @@ public:
void SwapInterval(int Interval) override;
void Swap() override;
void* GetFuncAddress(const std::string& name) override;
bool Create(void* window_handle, bool core) override;
bool Create(void* window_handle, bool stereo, bool core) override;
bool MakeCurrent() override;
bool ClearCurrent() override;
void Shutdown() override;

View file

@ -200,7 +200,7 @@ bool cInterfaceWGL::PeekMessages()
// Create rendering window.
// Call browser: Core.cpp:EmuThread() > main.cpp:Video_Initialize()
bool cInterfaceWGL::Create(void* window_handle, bool core)
bool cInterfaceWGL::Create(void* window_handle, bool stereo, bool core)
{
if (!window_handle)
return false;
@ -219,12 +219,14 @@ bool cInterfaceWGL::Create(void* window_handle, bool core)
s_backbuffer_width = twidth;
s_backbuffer_height = theight;
static constexpr PIXELFORMATDESCRIPTOR pfd = {
const DWORD stereo_flag = stereo ? PFD_STEREO : 0;
static const PIXELFORMATDESCRIPTOR pfd = {
sizeof(PIXELFORMATDESCRIPTOR), // Size Of This Pixel Format Descriptor
1, // Version Number
PFD_DRAW_TO_WINDOW | // Format Must Support Window
PFD_SUPPORT_OPENGL | // Format Must Support OpenGL
PFD_DOUBLEBUFFER, // Must Support Double Buffering
PFD_DOUBLEBUFFER | // Must Support Double Buffering
stereo_flag, // Could Support Quad Buffering
PFD_TYPE_RGBA, // Request An RGBA Format
32, // Select Our Color Depth
0,

View file

@ -14,7 +14,7 @@ public:
void SwapInterval(int interval) override;
void Swap() override;
void* GetFuncAddress(const std::string& name) override;
bool Create(void* window_handle, bool core) override;
bool Create(void* window_handle, bool stereo, bool core) override;
bool Create(cInterfaceBase* main_context) override;
bool MakeCurrent() override;
bool ClearCurrent() override;

View file

@ -34,7 +34,7 @@ public:
virtual void SetMode(GLInterfaceMode mode) { s_opengl_mode = GLInterfaceMode::MODE_OPENGL; }
virtual GLInterfaceMode GetMode() { return s_opengl_mode; }
virtual void* GetFuncAddress(const std::string& name) { return nullptr; }
virtual bool Create(void* window_handle, bool core = true) { return true; }
virtual bool Create(void* window_handle, bool stereo = false, bool core = true) { return true; }
virtual bool Create(cInterfaceBase* main_context) { return true; }
virtual bool MakeCurrent() { return true; }
virtual bool ClearCurrent() { return true; }

View file

@ -93,7 +93,7 @@ void IniFile::Section::Set(const std::string& key, double newValue)
void IniFile::Section::Set(const std::string& key, int newValue)
{
Set(key, StringFromInt(newValue));
Set(key, std::to_string(newValue));
}
void IniFile::Section::Set(const std::string& key, s64 newValue)

View file

@ -6,7 +6,6 @@
#include <cstring>
#include <mutex>
#include <ostream>
#include <set>
#include <string>
#include "Common/CommonPaths.h"
@ -18,6 +17,36 @@
#include "Common/StringUtil.h"
#include "Common/Timer.h"
constexpr size_t MAX_MSGLEN = 1024;
class FileLogListener : public LogListener
{
public:
FileLogListener(const std::string& filename)
{
File::OpenFStream(m_logfile, filename, std::ios::app);
SetEnable(true);
}
void Log(LogTypes::LOG_LEVELS, const char* msg) override
{
if (!IsEnabled() || !IsValid())
return;
std::lock_guard<std::mutex> lk(m_log_lock);
m_logfile << msg << std::flush;
}
bool IsValid() const { return m_logfile.good(); }
bool IsEnabled() const { return m_enable; }
void SetEnable(bool enable) { m_enable = enable; }
// const char* GetName() const { return "file"; }
private:
std::mutex m_log_lock;
std::ofstream m_logfile;
bool m_enable;
};
void GenericLog(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, const char* file, int line,
const char* fmt, ...)
{
@ -28,8 +57,6 @@ void GenericLog(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, const char*
va_end(args);
}
LogManager* LogManager::m_logManager = nullptr;
static size_t DeterminePathCutOffPoint()
{
constexpr const char* pattern = DIR_SEP "Source" DIR_SEP "Core" DIR_SEP;
@ -42,53 +69,53 @@ static size_t DeterminePathCutOffPoint()
LogManager::LogManager()
{
// create log containers
m_Log[LogTypes::ACTIONREPLAY] = new LogContainer("ActionReplay", "ActionReplay");
m_Log[LogTypes::AUDIO] = new LogContainer("Audio", "Audio Emulator");
m_Log[LogTypes::AUDIO_INTERFACE] = new LogContainer("AI", "Audio Interface (AI)");
m_Log[LogTypes::BOOT] = new LogContainer("BOOT", "Boot");
m_Log[LogTypes::COMMANDPROCESSOR] = new LogContainer("CP", "CommandProc");
m_Log[LogTypes::COMMON] = new LogContainer("COMMON", "Common");
m_Log[LogTypes::CONSOLE] = new LogContainer("CONSOLE", "Dolphin Console");
m_Log[LogTypes::CORE] = new LogContainer("CORE", "Core");
m_Log[LogTypes::DISCIO] = new LogContainer("DIO", "Disc IO");
m_Log[LogTypes::DSPHLE] = new LogContainer("DSPHLE", "DSP HLE");
m_Log[LogTypes::DSPLLE] = new LogContainer("DSPLLE", "DSP LLE");
m_Log[LogTypes::DSP_MAIL] = new LogContainer("DSPMails", "DSP Mails");
m_Log[LogTypes::DSPINTERFACE] = new LogContainer("DSP", "DSPInterface");
m_Log[LogTypes::DVDINTERFACE] = new LogContainer("DVD", "DVD Interface");
m_Log[LogTypes::DYNA_REC] = new LogContainer("JIT", "Dynamic Recompiler");
m_Log[LogTypes::EXPANSIONINTERFACE] = new LogContainer("EXI", "Expansion Interface");
m_Log[LogTypes::FILEMON] = new LogContainer("FileMon", "File Monitor");
m_Log[LogTypes::GDB_STUB] = new LogContainer("GDB_STUB", "GDB Stub");
m_Log[LogTypes::GPFIFO] = new LogContainer("GP", "GPFifo");
m_Log[LogTypes::HOST_GPU] = new LogContainer("Host GPU", "Host GPU");
m_Log[LogTypes::IOS] = new LogContainer("IOS", "IOS");
m_Log[LogTypes::IOS_DI] = new LogContainer("IOS_DI", "IOS - Drive Interface");
m_Log[LogTypes::IOS_ES] = new LogContainer("IOS_ES", "IOS - ETicket Services");
m_Log[LogTypes::IOS_FILEIO] = new LogContainer("IOS_FILEIO", "IOS - FileIO");
m_Log[LogTypes::IOS_SD] = new LogContainer("IOS_SD", "IOS - SDIO");
m_Log[LogTypes::IOS_SSL] = new LogContainer("IOS_SSL", "IOS - SSL");
m_Log[LogTypes::IOS_STM] = new LogContainer("IOS_STM", "IOS - State Transition Manager");
m_Log[LogTypes::IOS_NET] = new LogContainer("IOS_NET", "IOS - Network");
m_Log[LogTypes::IOS_USB] = new LogContainer("IOS_USB", "IOS - USB");
m_Log[LogTypes::IOS_WC24] = new LogContainer("IOS_WC24", "IOS - WiiConnect24");
m_Log[LogTypes::IOS_WIIMOTE] = new LogContainer("IOS_WIIMOTE", "IOS - Wii Remote");
m_Log[LogTypes::MASTER_LOG] = new LogContainer("*", "Master Log");
m_Log[LogTypes::MEMCARD_MANAGER] = new LogContainer("MemCard Manager", "MemCard Manager");
m_Log[LogTypes::MEMMAP] = new LogContainer("MI", "MI & memmap");
m_Log[LogTypes::NETPLAY] = new LogContainer("NETPLAY", "Netplay");
m_Log[LogTypes::OSHLE] = new LogContainer("HLE", "HLE");
m_Log[LogTypes::OSREPORT] = new LogContainer("OSREPORT", "OSReport");
m_Log[LogTypes::PAD] = new LogContainer("PAD", "Pad");
m_Log[LogTypes::PIXELENGINE] = new LogContainer("PE", "PixelEngine");
m_Log[LogTypes::PROCESSORINTERFACE] = new LogContainer("PI", "ProcessorInt");
m_Log[LogTypes::POWERPC] = new LogContainer("PowerPC", "IBM CPU");
m_Log[LogTypes::SERIALINTERFACE] = new LogContainer("SI", "Serial Interface (SI)");
m_Log[LogTypes::SP1] = new LogContainer("SP1", "Serial Port 1");
m_Log[LogTypes::VIDEO] = new LogContainer("Video", "Video Backend");
m_Log[LogTypes::VIDEOINTERFACE] = new LogContainer("VI", "Video Interface (VI)");
m_Log[LogTypes::WIIMOTE] = new LogContainer("Wiimote", "Wiimote");
m_Log[LogTypes::WII_IPC] = new LogContainer("WII_IPC", "WII IPC");
m_log[LogTypes::ACTIONREPLAY] = {"ActionReplay", "ActionReplay"};
m_log[LogTypes::AUDIO] = {"Audio", "Audio Emulator"};
m_log[LogTypes::AUDIO_INTERFACE] = {"AI", "Audio Interface (AI)"};
m_log[LogTypes::BOOT] = {"BOOT", "Boot"};
m_log[LogTypes::COMMANDPROCESSOR] = {"CP", "CommandProc"};
m_log[LogTypes::COMMON] = {"COMMON", "Common"};
m_log[LogTypes::CONSOLE] = {"CONSOLE", "Dolphin Console"};
m_log[LogTypes::CORE] = {"CORE", "Core"};
m_log[LogTypes::DISCIO] = {"DIO", "Disc IO"};
m_log[LogTypes::DSPHLE] = {"DSPHLE", "DSP HLE"};
m_log[LogTypes::DSPLLE] = {"DSPLLE", "DSP LLE"};
m_log[LogTypes::DSP_MAIL] = {"DSPMails", "DSP Mails"};
m_log[LogTypes::DSPINTERFACE] = {"DSP", "DSPInterface"};
m_log[LogTypes::DVDINTERFACE] = {"DVD", "DVD Interface"};
m_log[LogTypes::DYNA_REC] = {"JIT", "Dynamic Recompiler"};
m_log[LogTypes::EXPANSIONINTERFACE] = {"EXI", "Expansion Interface"};
m_log[LogTypes::FILEMON] = {"FileMon", "File Monitor"};
m_log[LogTypes::GDB_STUB] = {"GDB_STUB", "GDB Stub"};
m_log[LogTypes::GPFIFO] = {"GP", "GPFifo"};
m_log[LogTypes::HOST_GPU] = {"Host GPU", "Host GPU"};
m_log[LogTypes::IOS] = {"IOS", "IOS"};
m_log[LogTypes::IOS_DI] = {"IOS_DI", "IOS - Drive Interface"};
m_log[LogTypes::IOS_ES] = {"IOS_ES", "IOS - ETicket Services"};
m_log[LogTypes::IOS_FILEIO] = {"IOS_FILEIO", "IOS - FileIO"};
m_log[LogTypes::IOS_SD] = {"IOS_SD", "IOS - SDIO"};
m_log[LogTypes::IOS_SSL] = {"IOS_SSL", "IOS - SSL"};
m_log[LogTypes::IOS_STM] = {"IOS_STM", "IOS - State Transition Manager"};
m_log[LogTypes::IOS_NET] = {"IOS_NET", "IOS - Network"};
m_log[LogTypes::IOS_USB] = {"IOS_USB", "IOS - USB"};
m_log[LogTypes::IOS_WC24] = {"IOS_WC24", "IOS - WiiConnect24"};
m_log[LogTypes::IOS_WIIMOTE] = {"IOS_WIIMOTE", "IOS - Wii Remote"};
m_log[LogTypes::MASTER_LOG] = {"*", "Master Log"};
m_log[LogTypes::MEMCARD_MANAGER] = {"MemCard Manager", "MemCard Manager"};
m_log[LogTypes::MEMMAP] = {"MI", "MI & memmap"};
m_log[LogTypes::NETPLAY] = {"NETPLAY", "Netplay"};
m_log[LogTypes::OSHLE] = {"HLE", "HLE"};
m_log[LogTypes::OSREPORT] = {"OSREPORT", "OSReport"};
m_log[LogTypes::PAD] = {"PAD", "Pad"};
m_log[LogTypes::PIXELENGINE] = {"PE", "PixelEngine"};
m_log[LogTypes::PROCESSORINTERFACE] = {"PI", "ProcessorInt"};
m_log[LogTypes::POWERPC] = {"PowerPC", "IBM CPU"};
m_log[LogTypes::SERIALINTERFACE] = {"SI", "Serial Interface (SI)"};
m_log[LogTypes::SP1] = {"SP1", "Serial Port 1"};
m_log[LogTypes::VIDEO] = {"Video", "Video Backend"};
m_log[LogTypes::VIDEOINTERFACE] = {"VI", "Video Interface (VI)"};
m_log[LogTypes::WIIMOTE] = {"Wiimote", "Wiimote"};
m_log[LogTypes::WII_IPC] = {"WII_IPC", "WII IPC"};
RegisterListener(LogListener::FILE_LISTENER,
new FileLogListener(File::GetUserPath(F_MAINLOG_IDX)));
@ -115,33 +142,42 @@ LogManager::LogManager()
if (verbosity > MAX_LOGLEVEL)
verbosity = MAX_LOGLEVEL;
for (LogContainer* container : m_Log)
{
bool enable;
logs->Get(container->GetShortName(), &enable, false);
container->SetEnable(enable);
container->SetLevel(static_cast<LogTypes::LOG_LEVELS>(verbosity));
if (enable && write_file)
container->AddListener(LogListener::FILE_LISTENER);
if (enable && write_console)
container->AddListener(LogListener::CONSOLE_LISTENER);
if (enable && write_window)
container->AddListener(LogListener::LOG_WINDOW_LISTENER);
}
SetLogLevel(static_cast<LogTypes::LOG_LEVELS>(verbosity));
EnableListener(LogListener::FILE_LISTENER, write_file);
EnableListener(LogListener::CONSOLE_LISTENER, write_console);
EnableListener(LogListener::LOG_WINDOW_LISTENER, write_window);
for (LogContainer& container : m_log)
logs->Get(container.m_short_name, &container.m_enable, false);
m_path_cutoff_point = DeterminePathCutOffPoint();
}
LogManager::~LogManager()
{
for (LogContainer* container : m_Log)
delete container;
// The log window listener pointer is owned by the GUI code.
delete m_listeners[LogListener::CONSOLE_LISTENER];
delete m_listeners[LogListener::FILE_LISTENER];
}
void LogManager::SaveSettings()
{
IniFile ini;
ini.Load(File::GetUserPath(F_LOGGERCONFIG_IDX));
IniFile::Section* options = ini.GetOrCreateSection("Options");
options->Set("Verbosity", GetLogLevel());
options->Set("WriteToFile", m_listener_ids[LogListener::FILE_LISTENER]);
options->Set("WriteToConsole", m_listener_ids[LogListener::CONSOLE_LISTENER]);
options->Set("WriteToWindow", m_listener_ids[LogListener::LOG_WINDOW_LISTENER]);
// Save all enabled/disabled states of the log types to the config ini.
for (const auto& container : m_log)
ini.GetOrCreateSection("Logs")->Set(container.m_short_name, container.m_enable);
ini.Save(File::GetUserPath(F_LOGGERCONFIG_IDX));
}
void LogManager::Log(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, const char* file,
int line, const char* format, va_list args)
{
@ -151,50 +187,83 @@ void LogManager::Log(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, const
void LogManager::LogWithFullPath(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type,
const char* file, int line, const char* format, va_list args)
{
char temp[MAX_MSGLEN];
LogContainer* log = m_Log[type];
if (!log->IsEnabled() || level > log->GetLevel() || !log->HasListeners())
if (!IsEnabled(type, level) || !static_cast<bool>(m_listener_ids))
return;
char temp[MAX_MSGLEN];
CharArrayFromFormatV(temp, MAX_MSGLEN, format, args);
std::string msg = StringFromFormat(
"%s %s:%u %c[%s]: %s\n", Common::Timer::GetTimeFormatted().c_str(), file, line,
LogTypes::LOG_LEVEL_TO_CHAR[(int)level], log->GetShortName().c_str(), temp);
std::string msg =
StringFromFormat("%s %s:%u %c[%s]: %s\n", Common::Timer::GetTimeFormatted().c_str(), file,
line, LogTypes::LOG_LEVEL_TO_CHAR[(int)level], GetShortName(type), temp);
for (auto listener_id : *log)
for (auto listener_id : m_listener_ids)
if (m_listeners[listener_id])
m_listeners[listener_id]->Log(level, msg.c_str());
}
LogTypes::LOG_LEVELS LogManager::GetLogLevel() const
{
return m_level;
}
void LogManager::SetLogLevel(LogTypes::LOG_LEVELS level)
{
m_level = level;
}
void LogManager::SetEnable(LogTypes::LOG_TYPE type, bool enable)
{
m_log[type].m_enable = enable;
}
bool LogManager::IsEnabled(LogTypes::LOG_TYPE type, LogTypes::LOG_LEVELS level) const
{
return m_log[type].m_enable && GetLogLevel() >= level;
}
const char* LogManager::GetShortName(LogTypes::LOG_TYPE type) const
{
return m_log[type].m_short_name;
}
const char* LogManager::GetFullName(LogTypes::LOG_TYPE type) const
{
return m_log[type].m_full_name;
}
void LogManager::RegisterListener(LogListener::LISTENER id, LogListener* listener)
{
m_listeners[id] = listener;
}
void LogManager::EnableListener(LogListener::LISTENER id, bool enable)
{
m_listener_ids[id] = enable;
}
bool LogManager::IsListenerEnabled(LogListener::LISTENER id) const
{
return m_listener_ids[id];
}
// Singleton. Ugh.
static LogManager* s_log_manager;
LogManager* LogManager::GetInstance()
{
return s_log_manager;
}
void LogManager::Init()
{
m_logManager = new LogManager();
s_log_manager = new LogManager();
}
void LogManager::Shutdown()
{
delete m_logManager;
m_logManager = nullptr;
}
LogContainer::LogContainer(const std::string& shortName, const std::string& fullName, bool enable)
: m_fullName(fullName), m_shortName(shortName), m_enable(enable), m_level(LogTypes::LWARNING)
{
}
FileLogListener::FileLogListener(const std::string& filename)
{
File::OpenFStream(m_logfile, filename, std::ios::app);
SetEnable(true);
}
void FileLogListener::Log(LogTypes::LOG_LEVELS, const char* msg)
{
if (!IsEnabled() || !IsValid())
return;
std::lock_guard<std::mutex> lk(m_log_lock);
m_logfile << msg << std::flush;
if (s_log_manager)
s_log_manager->SaveSettings();
delete s_log_manager;
s_log_manager = nullptr;
}

View file

@ -6,18 +6,11 @@
#include <array>
#include <cstdarg>
#include <fstream>
#include <mutex>
#include <set>
#include <string>
#include "Common/BitSet.h"
#include "Common/CommonTypes.h"
#include "Common/Logging/Log.h"
#include "Common/NonCopyable.h"
#define MAX_MSGLEN 1024
// pure virtual interface
class LogListener
{
@ -35,100 +28,47 @@ public:
};
};
class FileLogListener : public LogListener
{
public:
FileLogListener(const std::string& filename);
void Log(LogTypes::LOG_LEVELS, const char* msg) override;
bool IsValid() const { return m_logfile.good(); }
bool IsEnabled() const { return m_enable; }
void SetEnable(bool enable) { m_enable = enable; }
const char* GetName() const { return "file"; }
private:
std::mutex m_log_lock;
std::ofstream m_logfile;
bool m_enable;
};
class LogContainer
{
public:
LogContainer(const std::string& shortName, const std::string& fullName, bool enable = false);
std::string GetShortName() const { return m_shortName; }
std::string GetFullName() const { return m_fullName; }
void AddListener(LogListener::LISTENER id) { m_listener_ids[id] = 1; }
void RemoveListener(LogListener::LISTENER id) { m_listener_ids[id] = 0; }
void Trigger(LogTypes::LOG_LEVELS, const char* msg);
bool IsEnabled() const { return m_enable; }
void SetEnable(bool enable) { m_enable = enable; }
LogTypes::LOG_LEVELS GetLevel() const { return m_level; }
void SetLevel(LogTypes::LOG_LEVELS level) { m_level = level; }
bool HasListeners() const { return bool(m_listener_ids); }
typedef class BitSet32::Iterator iterator;
iterator begin() const { return m_listener_ids.begin(); }
iterator end() const { return m_listener_ids.end(); }
private:
std::string m_fullName;
std::string m_shortName;
bool m_enable;
LogTypes::LOG_LEVELS m_level;
BitSet32 m_listener_ids;
};
class ConsoleListener;
class LogManager : NonCopyable
{
private:
LogContainer* m_Log[LogTypes::NUMBER_OF_LOGS];
static LogManager* m_logManager; // Singleton. Ugh.
std::array<LogListener*, LogListener::NUMBER_OF_LISTENERS> m_listeners{};
size_t m_path_cutoff_point = 0;
LogManager();
~LogManager();
public:
static u32 GetMaxLevel() { return MAX_LOGLEVEL; }
static LogManager* GetInstance();
static void Init();
static void Shutdown();
void Log(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, const char* file, int line,
const char* fmt, va_list args);
void LogWithFullPath(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, const char* file,
int line, const char* fmt, va_list args);
void SetLogLevel(LogTypes::LOG_TYPE type, LogTypes::LOG_LEVELS level)
{
m_Log[type]->SetLevel(level);
}
LogTypes::LOG_LEVELS GetLogLevel() const;
void SetLogLevel(LogTypes::LOG_LEVELS level);
void SetEnable(LogTypes::LOG_TYPE type, bool enable) { m_Log[type]->SetEnable(enable); }
bool IsEnabled(LogTypes::LOG_TYPE type, LogTypes::LOG_LEVELS level = LogTypes::LNOTICE) const
{
return m_Log[type]->IsEnabled() && m_Log[type]->GetLevel() >= level;
}
void SetEnable(LogTypes::LOG_TYPE type, bool enable);
bool IsEnabled(LogTypes::LOG_TYPE type, LogTypes::LOG_LEVELS level = LogTypes::LNOTICE) const;
std::string GetShortName(LogTypes::LOG_TYPE type) const { return m_Log[type]->GetShortName(); }
std::string GetFullName(LogTypes::LOG_TYPE type) const { return m_Log[type]->GetFullName(); }
void RegisterListener(LogListener::LISTENER id, LogListener* listener)
{
m_listeners[id] = listener;
}
const char* GetShortName(LogTypes::LOG_TYPE type) const;
const char* GetFullName(LogTypes::LOG_TYPE type) const;
void AddListener(LogTypes::LOG_TYPE type, LogListener::LISTENER id)
{
m_Log[type]->AddListener(id);
}
void RegisterListener(LogListener::LISTENER id, LogListener* listener);
void EnableListener(LogListener::LISTENER id, bool enable);
bool IsListenerEnabled(LogListener::LISTENER id) const;
void RemoveListener(LogTypes::LOG_TYPE type, LogListener::LISTENER id)
{
m_Log[type]->RemoveListener(id);
}
void SaveSettings();
static LogManager* GetInstance() { return m_logManager; }
static void SetInstance(LogManager* logManager) { m_logManager = logManager; }
static void Init();
static void Shutdown();
private:
struct LogContainer
{
const char* m_short_name;
const char* m_full_name;
bool m_enable = false;
};
LogManager();
~LogManager();
LogTypes::LOG_LEVELS m_level;
std::array<LogContainer, LogTypes::NUMBER_OF_LOGS> m_log{};
std::array<LogListener*, LogListener::NUMBER_OF_LISTENERS> m_listeners{};
BitSet32 m_listener_ids;
size_t m_path_cutoff_point = 0;
};

View file

@ -283,13 +283,6 @@ bool TryParse(const std::string& str, bool* const output)
return true;
}
std::string StringFromInt(int value)
{
char temp[16];
sprintf(temp, "%i", value);
return temp;
}
std::string StringFromBool(bool value)
{
return value ? "True" : "False";

View file

@ -56,7 +56,6 @@ std::string ThousandSeparate(I value, int spaces = 0)
return oss.str();
}
std::string StringFromInt(int value);
std::string StringFromBool(bool value);
bool TryParse(const std::string& str, bool* output);

View file

@ -56,8 +56,7 @@ void SysConf::Load()
{
Clear();
if (!File::Exists(m_file_name) || File::GetSize(m_file_name) != SYSCONF_SIZE ||
!LoadFromFile(m_file_name))
if (File::GetSize(m_file_name) != SYSCONF_SIZE || !LoadFromFile(m_file_name))
{
WARN_LOG(CORE, "No valid SYSCONF detected. Creating a new one.");
InsertDefaultEntries();

View file

@ -329,6 +329,6 @@ void ReleaseTraversalClient()
if (!g_TraversalClient)
return;
g_TraversalClient.release();
g_MainNetHost.release();
g_TraversalClient.reset();
g_MainNetHost.reset();
}

View file

@ -33,7 +33,7 @@
#include "Common/CommonTypes.h"
#include "Common/IniFile.h"
#include "Common/Logging/LogManager.h"
#include "Common/Logging/Log.h"
#include "Common/MsgHandler.h"
#include "Common/StringUtil.h"
@ -278,7 +278,7 @@ static void LogInfo(const char* format, ...)
if (s_disable_logging)
return;
bool use_internal_log = s_use_internal_log.load(std::memory_order_relaxed);
if (LogManager::GetMaxLevel() < LogTypes::LINFO && !use_internal_log)
if (MAX_LOGLEVEL < LogTypes::LINFO && !use_internal_log)
return;
va_list args;

View file

@ -466,8 +466,8 @@ bool CBoot::BootUp(std::unique_ptr<BootParameters> boot)
BootExecutableReader::BootExecutableReader(const std::string& file_name)
{
m_bytes.resize(File::GetSize(file_name));
File::IOFile file{file_name, "rb"};
m_bytes.resize(file.GetSize());
file.ReadBytes(m_bytes.data(), m_bytes.size());
}

View file

@ -8,6 +8,7 @@
#include <array>
#include <list>
#include <map>
#include <optional>
#include <sstream>
#include <string>
#include <tuple>
@ -28,13 +29,17 @@
namespace ConfigLoaders
{
using ConfigLocation = Config::ConfigLocation;
// Returns all possible filenames in ascending order of priority
static std::vector<std::string> GetGameIniFilenames(const std::string& id, u16 revision)
std::vector<std::string> GetGameIniFilenames(const std::string& id, std::optional<u16> revision)
{
std::vector<std::string> filenames;
if (id.empty())
return filenames;
// INIs that match the system code (unique for each Virtual Console system)
filenames.push_back(id.substr(0, 1) + ".ini");
// INIs that match all regions
if (id.size() >= 4)
filenames.push_back(id.substr(0, 3) + ".ini");
@ -43,11 +48,13 @@ static std::vector<std::string> GetGameIniFilenames(const std::string& id, u16 r
filenames.push_back(id + ".ini");
// INIs with specific revisions
filenames.push_back(id + StringFromFormat("r%d", revision) + ".ini");
if (revision)
filenames.push_back(id + StringFromFormat("r%d", *revision) + ".ini");
return filenames;
}
using ConfigLocation = Config::ConfigLocation;
using INIToLocationMap = std::map<std::pair<std::string, std::string>, ConfigLocation>;
// This is a mapping from the legacy section-key pairs to ConfigLocations.

View file

@ -6,7 +6,9 @@
#include <cstring>
#include <memory>
#include <optional>
#include <string>
#include <vector>
#include "Common/CommonTypes.h"
@ -17,6 +19,8 @@ class ConfigLayerLoader;
namespace ConfigLoaders
{
std::vector<std::string> GetGameIniFilenames(const std::string& id, std::optional<u16> revision);
std::unique_ptr<Config::ConfigLayerLoader> GenerateGlobalGameConfigLoader(const std::string& id,
u16 revision);
std::unique_ptr<Config::ConfigLayerLoader> GenerateLocalGameConfigLoader(const std::string& id,

View file

@ -13,6 +13,7 @@
#include "AudioCommon/AudioCommon.h"
#include "Common/Assert.h"
#include "Common/CDUtils.h"
#include "Common/CommonPaths.h"
#include "Common/CommonTypes.h"
@ -26,6 +27,7 @@
#include "Core/Analytics.h"
#include "Core/Boot/Boot.h"
#include "Core/ConfigLoaders/GameConfigLoader.h"
#include "Core/Core.h"
#include "Core/FifoPlayer/FifoDataFile.h"
#include "Core/HLE/HLE.h"
@ -168,6 +170,7 @@ void SConfig::SaveInterfaceSettings(IniFile& ini)
interface->Set("ShowLogConfigWindow", m_InterfaceLogConfigWindow);
interface->Set("ExtendedFPSInfo", m_InterfaceExtendedFPSInfo);
interface->Set("ShowActiveTitle", m_show_active_title);
interface->Set("UseBuiltinTitleDatabase", m_use_builtin_title_database);
interface->Set("ShowDevelopmentWarning", m_show_development_warning);
interface->Set("ThemeName", theme_name);
interface->Set("PauseOnFocusLost", m_PauseOnFocusLost);
@ -481,6 +484,7 @@ void SConfig::LoadInterfaceSettings(IniFile& ini)
interface->Get("ShowLogConfigWindow", &m_InterfaceLogConfigWindow, false);
interface->Get("ExtendedFPSInfo", &m_InterfaceExtendedFPSInfo, false);
interface->Get("ShowActiveTitle", &m_show_active_title, true);
interface->Get("UseBuiltinTitleDatabase", &m_use_builtin_title_database, true);
interface->Get("ShowDevelopmentWarning", &m_show_development_warning, true);
interface->Get("ThemeName", &theme_name, DEFAULT_THEME_DIR);
interface->Get("PauseOnFocusLost", &m_PauseOnFocusLost, false);
@ -868,6 +872,19 @@ bool SConfig::IsUSBDeviceWhitelisted(const std::pair<u16, u16> vid_pid) const
return m_usb_passthrough_devices.find(vid_pid) != m_usb_passthrough_devices.end();
}
// The reason we need this function is because some memory card code
// expects to get a non-NTSC-K region even if we're emulating an NTSC-K Wii.
DiscIO::Region SConfig::ToGameCubeRegion(DiscIO::Region region)
{
if (region != DiscIO::Region::NTSC_K)
return region;
// GameCube has no NTSC-K region. No choice of replacement value is completely
// non-arbitrary, but let's go with NTSC-J since Korean GameCubes are NTSC-J.
return DiscIO::Region::NTSC_J;
}
const char* SConfig::GetDirectoryForRegion(DiscIO::Region region)
{
switch (region)
@ -882,13 +899,8 @@ const char* SConfig::GetDirectoryForRegion(DiscIO::Region region)
return EUR_DIR;
case DiscIO::Region::NTSC_K:
// This function can't return a Korean directory name, because this
// function is only used for GameCube things (memory cards, IPL), and
// GameCube has no NTSC-K region. Since NTSC-K doesn't correspond to any
// GameCube region, let's return an arbitrary pick. Returning nullptr like
// with unknown regions would be inappropriate, because Dolphin expects
// to get valid memory card paths even when running an NTSC-K Wii game.
return JAP_DIR;
_assert_msg_(BOOT, false, "NTSC-K is not a valid GameCube region");
return nullptr;
default:
return nullptr;
@ -981,7 +993,7 @@ bool SConfig::SetPathsAndGameMetadata(const BootParameters& boot)
return false;
// Set up region
const char* retrieved_region_dir = GetDirectoryForRegion(region);
const char* retrieved_region_dir = GetDirectoryForRegion(ToGameCubeRegion(region));
m_region = retrieved_region_dir ? region : DiscIO::Region::PAL;
const std::string set_region_dir = retrieved_region_dir ? retrieved_region_dir : EUR_DIR;
if (!retrieved_region_dir &&
@ -1082,7 +1094,7 @@ IniFile SConfig::LoadGameIni() const
IniFile SConfig::LoadDefaultGameIni(const std::string& id, std::optional<u16> revision)
{
IniFile game_ini;
for (const std::string& filename : GetGameIniFilenames(id, revision))
for (const std::string& filename : ConfigLoaders::GetGameIniFilenames(id, revision))
game_ini.Load(File::GetSysDirectory() + GAMESETTINGS_DIR DIR_SEP + filename, true);
return game_ini;
}
@ -1090,7 +1102,7 @@ IniFile SConfig::LoadDefaultGameIni(const std::string& id, std::optional<u16> re
IniFile SConfig::LoadLocalGameIni(const std::string& id, std::optional<u16> revision)
{
IniFile game_ini;
for (const std::string& filename : GetGameIniFilenames(id, revision))
for (const std::string& filename : ConfigLoaders::GetGameIniFilenames(id, revision))
game_ini.Load(File::GetUserPath(D_GAMESETTINGS_IDX) + filename, true);
return game_ini;
}
@ -1098,35 +1110,9 @@ IniFile SConfig::LoadLocalGameIni(const std::string& id, std::optional<u16> revi
IniFile SConfig::LoadGameIni(const std::string& id, std::optional<u16> revision)
{
IniFile game_ini;
for (const std::string& filename : GetGameIniFilenames(id, revision))
for (const std::string& filename : ConfigLoaders::GetGameIniFilenames(id, revision))
game_ini.Load(File::GetSysDirectory() + GAMESETTINGS_DIR DIR_SEP + filename, true);
for (const std::string& filename : GetGameIniFilenames(id, revision))
for (const std::string& filename : ConfigLoaders::GetGameIniFilenames(id, revision))
game_ini.Load(File::GetUserPath(D_GAMESETTINGS_IDX) + filename, true);
return game_ini;
}
// Returns all possible filenames in ascending order of priority
std::vector<std::string> SConfig::GetGameIniFilenames(const std::string& id,
std::optional<u16> revision)
{
std::vector<std::string> filenames;
if (id.empty())
return filenames;
// INIs that match the system code (unique for each Virtual Console system)
filenames.push_back(id.substr(0, 1) + ".ini");
// INIs that match all regions
if (id.size() >= 4)
filenames.push_back(id.substr(0, 3) + ".ini");
// Regular INIs
filenames.push_back(id + ".ini");
// INIs with specific revisions
if (revision)
filenames.push_back(id + StringFromFormat("r%d", *revision) + ".ini");
return filenames;
}

View file

@ -219,6 +219,9 @@ struct SConfig : NonCopyable
void SetRunningGameMetadata(const IOS::ES::TMDReader& tmd);
void LoadDefaults();
// Replaces NTSC-K with some other region, and doesn't replace non-NTSC-K regions
static DiscIO::Region ToGameCubeRegion(DiscIO::Region region);
// The region argument must be valid for GameCube (i.e. must not be NTSC-K)
static const char* GetDirectoryForRegion(DiscIO::Region region);
std::string GetBootROMPath(const std::string& region_directory) const;
bool SetPathsAndGameMetadata(const BootParameters& boot);
@ -233,9 +236,6 @@ struct SConfig : NonCopyable
static IniFile LoadLocalGameIni(const std::string& id, std::optional<u16> revision);
static IniFile LoadGameIni(const std::string& id, std::optional<u16> revision);
static std::vector<std::string> GetGameIniFilenames(const std::string& id,
std::optional<u16> revision);
std::string m_NANDPath;
std::string m_DumpPath;
@ -259,6 +259,7 @@ struct SConfig : NonCopyable
bool m_InterfaceLogConfigWindow;
bool m_InterfaceExtendedFPSInfo;
bool m_show_active_title = false;
bool m_use_builtin_title_database = true;
bool m_show_development_warning;
bool m_ListDrives;

View file

@ -102,7 +102,6 @@ static StoppedCallbackFunc s_on_stopped_callback;
static std::thread s_cpu_thread;
static bool s_request_refresh_info = false;
static int s_pause_and_lock_depth = 0;
static bool s_is_throttler_temp_disabled = false;
struct HostJob
@ -242,8 +241,8 @@ bool Init(std::unique_ptr<BootParameters> boot)
Core::UpdateWantDeterminism(/*initial*/ true);
INFO_LOG(OSREPORT, "Starting core = %s mode", SConfig::GetInstance().bWii ? "Wii" : "GameCube");
INFO_LOG(OSREPORT, "CPU Thread separate = %s", SConfig::GetInstance().bCPUThread ? "Yes" : "No");
INFO_LOG(BOOT, "Starting core = %s mode", SConfig::GetInstance().bWii ? "Wii" : "GameCube");
INFO_LOG(BOOT, "CPU Thread separate = %s", SConfig::GetInstance().bCPUThread ? "Yes" : "No");
Host_UpdateMainFrame(); // Disable any menus or buttons at boot
@ -761,17 +760,12 @@ void RequestRefreshInfo()
s_request_refresh_info = true;
}
bool PauseAndLock(bool do_lock, bool unpause_on_unlock)
static bool PauseAndLock(bool do_lock, bool unpause_on_unlock)
{
// WARNING: PauseAndLock is not fully threadsafe so is only valid on the Host Thread
if (!IsRunning())
return true;
// let's support recursive locking to simplify things on the caller's side,
// and let's do it at this outer level in case the individual systems don't support it.
if (do_lock ? s_pause_and_lock_depth++ : --s_pause_and_lock_depth)
return true;
bool was_unpaused = true;
if (do_lock)
{
@ -808,6 +802,19 @@ bool PauseAndLock(bool do_lock, bool unpause_on_unlock)
return was_unpaused;
}
void RunAsCPUThread(std::function<void()> function)
{
const bool is_cpu_thread = IsCPUThread();
bool was_unpaused = false;
if (!is_cpu_thread)
was_unpaused = PauseAndLock(true, true);
function();
if (!is_cpu_thread)
PauseAndLock(false, was_unpaused);
}
// Display FPS info
// This should only be called from VI
void VideoThrottle()
@ -961,22 +968,20 @@ void UpdateWantDeterminism(bool initial)
{
NOTICE_LOG(COMMON, "Want determinism <- %s", new_want_determinism ? "true" : "false");
bool was_unpaused = Core::PauseAndLock(true);
RunAsCPUThread([&] {
s_wants_determinism = new_want_determinism;
const auto ios = IOS::HLE::GetIOS();
if (ios)
ios->UpdateWantDeterminism(new_want_determinism);
Fifo::UpdateWantDeterminism(new_want_determinism);
// We need to clear the cache because some parts of the JIT depend on want_determinism,
// e.g. use of FMA.
JitInterface::ClearCache();
s_wants_determinism = new_want_determinism;
const auto ios = IOS::HLE::GetIOS();
if (ios)
ios->UpdateWantDeterminism(new_want_determinism);
Fifo::UpdateWantDeterminism(new_want_determinism);
// We need to clear the cache because some parts of the JIT depend on want_determinism, e.g. use
// of FMA.
JitInterface::ClearCache();
// Don't call InitializeWiiRoot during boot, because IOS already does it.
if (!initial)
Core::InitializeWiiRoot(s_wants_determinism);
Core::PauseAndLock(false, was_unpaused);
// Don't call InitializeWiiRoot during boot, because IOS already does it.
if (!initial)
Core::InitializeWiiRoot(s_wants_determinism);
});
}
}

View file

@ -74,12 +74,14 @@ void RequestRefreshInfo();
void UpdateTitle();
// waits until all systems are paused and fully idle, and acquires a lock on that state.
// or, if doLock is false, releases a lock on that state and optionally unpauses.
// calls must be balanced (once with doLock true, then once with doLock false) but may be recursive.
// the return value of the first call should be passed in as the second argument of the second call.
// [NOT THREADSAFE] Host only
bool PauseAndLock(bool doLock, bool unpauseOnUnlock = true);
// Run a function as the CPU thread.
//
// If called from the Host thread, the CPU thread is paused and the current thread temporarily
// becomes the CPU thread while running the function.
// If called from the CPU thread, the function will be run directly.
//
// This should only be called from the CPU thread or the host thread.
void RunAsCPUThread(std::function<void()> function);
// for calling back into UI code without introducing a dependency on it in core
using StoppedCallbackFunc = std::function<void()>;

View file

@ -52,9 +52,6 @@ void AXUCode::LoadResamplingCoefficients()
for (fidx = 0; fidx < ArraySize(filenames); ++fidx)
{
filename = filenames[fidx];
if (!File::Exists(filename))
continue;
if (File::GetSize(filename) != 0x1000)
continue;

View file

@ -118,7 +118,7 @@ bool ReadAnnotatedAssembly(const std::string& filename)
{
// Remove hex notation
if ((int)i == first_hex + 3 && (first_hex == 0 || line[first_hex - 1] != 'x') &&
(i >= len - 1 || line[i + 1] == ' '))
(i >= len - 1 || line[i + 1] == ' '))
{
hex_found = true;
break;

View file

@ -476,12 +476,7 @@ static void InsertDiscCallback(u64 userdata, s64 cyclesLate)
// Can only be called by the host thread
void ChangeDiscAsHost(const std::string& new_path)
{
bool was_unpaused = Core::PauseAndLock(true);
// The host thread is now temporarily the CPU thread
ChangeDiscAsCPU(new_path);
Core::PauseAndLock(false, was_unpaused);
Core::RunAsCPUThread([&] { ChangeDiscAsCPU(new_path); });
}
// Can only be called by the CPU thread

View file

@ -204,30 +204,26 @@ void CEXIIPL::LoadFontFile(const std::string& filename, u32 offset)
if (ipl_rom_path.empty())
ipl_rom_path = FindIPLDump(File::GetSysDirectory() + GC_SYS_DIR);
if (File::Exists(ipl_rom_path))
{
// The user has an IPL dump, load the font from it
File::IOFile stream(ipl_rom_path, "rb");
if (!stream)
return;
// Official Windows-1252 and Shift JIS fonts present on the IPL dumps are 0x2575 and 0x4a24d
// bytes long respectively, so, determine the size of the font being loaded based on the offset
u64 fontsize = (offset == 0x1aff00) ? 0x4a24d : 0x2575;
INFO_LOG(BOOT, "Found IPL dump, loading %s font from %s",
((offset == 0x1aff00) ? "Shift JIS" : "Windows-1252"), (ipl_rom_path).c_str());
stream.Seek(offset, 0);
stream.ReadBytes(m_pIPL + offset, fontsize);
m_FontsLoaded = true;
}
else
// If the user has an IPL dump, load the font from it
File::IOFile stream(ipl_rom_path, "rb");
if (!stream)
{
// No IPL dump available, load bundled font instead
LoadFileToIPL(filename, offset);
return;
}
// Official Windows-1252 and Shift JIS fonts present on the IPL dumps are 0x2575 and 0x4a24d
// bytes long respectively, so, determine the size of the font being loaded based on the offset
u64 fontsize = (offset == 0x1aff00) ? 0x4a24d : 0x2575;
INFO_LOG(BOOT, "Found IPL dump, loading %s font from %s",
((offset == 0x1aff00) ? "Shift JIS" : "Windows-1252"), (ipl_rom_path).c_str());
stream.Seek(offset, 0);
stream.ReadBytes(m_pIPL + offset, fontsize);
m_FontsLoaded = true;
}
void CEXIIPL::SetCS(int _iCS)

View file

@ -157,7 +157,7 @@ CEXIMemoryCard::CEXIMemoryCard(const int index, bool gciFolder) : card_index(ind
void CEXIMemoryCard::SetupGciFolder(u16 sizeMb)
{
DiscIO::Region region = SConfig::GetInstance().m_region;
const DiscIO::Region region = SConfig::ToGameCubeRegion(SConfig::GetInstance().m_region);
const std::string& game_id = SConfig::GetInstance().GetGameID();
u32 CurrentGameId = 0;
@ -176,11 +176,12 @@ void CEXIMemoryCard::SetupGciFolder(u16 sizeMb)
strDirectoryName = strDirectoryName + SConfig::GetDirectoryForRegion(region) + DIR_SEP +
StringFromFormat("Card %c", 'A' + card_index);
if (!File::Exists(strDirectoryName)) // first use of memcard folder, migrate automatically
const File::FileInfo file_info(strDirectoryName);
if (!file_info.Exists()) // first use of memcard folder, migrate automatically
{
MigrateFromMemcardFile(strDirectoryName + DIR_SEP, card_index);
}
else if (!File::IsDirectory(strDirectoryName))
else if (!file_info.IsDirectory())
{
if (File::Rename(strDirectoryName, strDirectoryName + ".original"))
{
@ -198,7 +199,7 @@ void CEXIMemoryCard::SetupGciFolder(u16 sizeMb)
}
memorycard = std::make_unique<GCMemcardDirectory>(strDirectoryName + DIR_SEP, card_index, sizeMb,
shift_jis, region, CurrentGameId);
shift_jis, CurrentGameId);
}
void CEXIMemoryCard::SetupRawMemcard(u16 sizeMb)

View file

@ -21,16 +21,15 @@
#include "Common/FileUtil.h"
#include "Common/Logging/Log.h"
#include "Common/MsgHandler.h"
#include "Common/StringUtil.h"
#include "Common/Thread.h"
#include "Core/ConfigManager.h"
#include "Core/Core.h"
#include "DiscIO/Volume.h"
const int NO_INDEX = -1;
static const char* MC_HDR = "MC_SYSTEM_AREA";
int GCMemcardDirectory::LoadGCI(const std::string& file_name, DiscIO::Region card_region,
bool current_game_only)
int GCMemcardDirectory::LoadGCI(const std::string& file_name, bool current_game_only)
{
File::IOFile gci_file(file_name, "rb");
if (gci_file)
@ -44,27 +43,14 @@ int GCMemcardDirectory::LoadGCI(const std::string& file_name, DiscIO::Region car
return NO_INDEX;
}
DiscIO::Region gci_region = DiscIO::RegionSwitchGC(gci.m_gci_header.Gamecode[3]);
// Some special save files have game IDs that we parse as UNKNOWN_REGION. For instance:
// - Datel Action Replay uses C as the fourth character. (Can be on any region's card.)
// - Homeland's network config file only uses null bytes. (Homeland is exclusive to Japan,
// but maybe the network config file ID was intended to be used in other regions too.)
if (card_region != gci_region && gci_region != DiscIO::Region::UNKNOWN_REGION)
{
PanicAlertT(
"GCI save file was not loaded because it is the wrong region for this memory card:\n%s",
file_name.c_str());
return NO_INDEX;
}
std::string gci_filename = gci.m_gci_header.GCI_FileName();
for (u16 i = 0; i < m_loaded_saves.size(); ++i)
{
if (m_loaded_saves[i] == gci_filename)
{
PanicAlertT("%s\nwas not loaded because it has the same internal filename as previously "
"loaded save\n%s",
gci.m_filename.c_str(), m_saves[i].m_filename.c_str());
"loaded save\n%s",
gci.m_filename.c_str(), m_saves[i].m_filename.c_str());
return NO_INDEX;
}
}
@ -75,8 +61,8 @@ int GCMemcardDirectory::LoadGCI(const std::string& file_name, DiscIO::Region car
if (num_blocks > 2043)
{
PanicAlertT(
"%s\nwas not loaded because it is an invalid GCI.\n Number of blocks claimed to be %u",
gci.m_filename.c_str(), num_blocks);
"%s\nwas not loaded because it is an invalid GCI.\n Number of blocks claimed to be %u",
gci.m_filename.c_str(), num_blocks);
return NO_INDEX;
}
@ -85,8 +71,8 @@ int GCMemcardDirectory::LoadGCI(const std::string& file_name, DiscIO::Region car
if (file_size != size + DENTRY_SIZE)
{
PanicAlertT("%s\nwas not loaded because it is an invalid GCI.\n File size (0x%" PRIx64
") does not match the size recorded in the header (0x%x)",
gci.m_filename.c_str(), file_size, size + DENTRY_SIZE);
") does not match the size recorded in the header (0x%x)",
gci.m_filename.c_str(), file_size, size + DENTRY_SIZE);
return NO_INDEX;
}
@ -105,9 +91,9 @@ int GCMemcardDirectory::LoadGCI(const std::string& file_name, DiscIO::Region car
if (total_blocks > free_blocks * 10)
{
PanicAlertT("%s\nwas not loaded because there is less than 10%% free blocks available on "
"the memory card\n"
"Total Blocks: %d; Free Blocks: %d",
gci.m_filename.c_str(), total_blocks, free_blocks);
"the memory card\n"
"Total Blocks: %d; Free Blocks: %d",
gci.m_filename.c_str(), total_blocks, free_blocks);
return NO_INDEX;
}
}
@ -115,8 +101,8 @@ int GCMemcardDirectory::LoadGCI(const std::string& file_name, DiscIO::Region car
if (first_block == 0xFFFF)
{
PanicAlertT(
"%s\nwas not loaded because there are not enough free blocks on the virtual memory card",
file_name.c_str());
"%s\nwas not loaded because there are not enough free blocks on the virtual memory card",
file_name.c_str());
return NO_INDEX;
}
*(u16*)&gci.m_gci_header.FirstBlock = first_block;
@ -136,26 +122,24 @@ int GCMemcardDirectory::LoadGCI(const std::string& file_name, DiscIO::Region car
}
GCMemcardDirectory::GCMemcardDirectory(const std::string& directory, int slot, u16 size_mbits,
bool shift_jis, DiscIO::Region card_region, int game_id)
: MemoryCardBase(slot, size_mbits), m_game_id(game_id), m_last_block(-1),
m_hdr(slot, size_mbits, shift_jis), m_bat1(size_mbits), m_saves(0),
m_save_directory(directory), m_exiting(false)
bool shift_jis, int game_id)
: MemoryCardBase(slot, size_mbits), m_game_id(game_id), m_last_block(-1),
m_hdr(slot, size_mbits, shift_jis), m_bat1(size_mbits), m_saves(0),
m_save_directory(directory), m_exiting(false)
{
// Use existing header data if available
if (File::Exists(m_save_directory + MC_HDR))
{
File::IOFile hdr_file((m_save_directory + MC_HDR), "rb");
hdr_file.ReadBytes(&m_hdr, BLOCK_SIZE);
File::IOFile((m_save_directory + MC_HDR), "rb").ReadBytes(&m_hdr, BLOCK_SIZE);
}
std::vector<std::string> filenames = Common::DoFileSearch({m_save_directory}, {".gci"});
std::vector<std::string> filenames = Common::DoFileSearch({ m_save_directory }, { ".gci" });
if (filenames.size() > 112)
{
Core::DisplayMessage("Warning: There are more than 112 save files on this memory card.\n"
" Only loading the first 112 in the folder, unless the game ID is the "
"same as the current game's ID",
4000);
" Only loading the first 112 in the folder, unless the game ID is the "
"same as the current game's ID",
4000);
}
for (const std::string& gci_file : filenames)
@ -163,11 +147,11 @@ GCMemcardDirectory::GCMemcardDirectory(const std::string& directory, int slot, u
if (m_saves.size() == DIRLEN)
{
PanicAlertT(
"There are too many GCI files in the folder\n%s.\nOnly the first 127 will be available",
m_save_directory.c_str());
"There are too many GCI files in the folder\n%s.\nOnly the first 127 will be available",
m_save_directory.c_str());
break;
}
int index = LoadGCI(gci_file, card_region, m_saves.size() > 112);
int index = LoadGCI(gci_file, m_saves.size() > 112);
if (index != NO_INDEX)
{
m_loaded_saves.push_back(m_saves.at(index).m_gci_header.GCI_FileName());
@ -190,9 +174,9 @@ void GCMemcardDirectory::FlushThread()
}
Common::SetCurrentThreadName(
StringFromFormat("Memcard %d flushing thread", m_card_index).c_str());
StringFromFormat("Memcard %d flushing thread", m_card_index).c_str());
constexpr std::chrono::seconds flush_interval{1};
constexpr std::chrono::seconds flush_interval{ 1 };
while (true)
{
// no-op until signalled
@ -233,7 +217,7 @@ s32 GCMemcardDirectory::Read(u32 src_address, s32 length, u8* dest_address)
// verify that we haven't calculated a length beyond BLOCK_SIZE
_dbg_assert_msg_(EXPANSIONINTERFACE, (src_address + length) % BLOCK_SIZE == 0,
"Memcard directory Read Logic Error");
"Memcard directory Read Logic Error");
}
if (m_last_block != block)
@ -293,7 +277,7 @@ s32 GCMemcardDirectory::Write(u32 dest_address, s32 length, const u8* src_addres
// verify that we haven't calculated a length beyond BLOCK_SIZE
_dbg_assert_msg_(EXPANSIONINTERFACE, (dest_address + length) % BLOCK_SIZE == 0,
"Memcard directory Write Logic Error");
"Memcard directory Write Logic Error");
}
if (m_last_block != block)
{
@ -312,7 +296,7 @@ s32 GCMemcardDirectory::Write(u32 dest_address, s32 length, const u8* src_addres
{
s32 to_write = std::min<s32>(DENTRY_SIZE, length);
bytes_written +=
DirectoryWrite(dest_address + bytes_written, to_write, src_address + bytes_written);
DirectoryWrite(dest_address + bytes_written, to_write, src_address + bytes_written);
length -= to_write;
}
return bytes_written;
@ -418,7 +402,7 @@ inline void GCMemcardDirectory::SyncSaves()
if ((gamecode != 0xFFFFFFFF) && (gamecode != new_gamecode))
{
PanicAlertT("Game overwrote with another games save. Data corruption ahead 0x%x, 0x%x",
BE32(m_saves[i].m_gci_header.Gamecode), BE32(current->Dir[i].Gamecode));
BE32(m_saves[i].m_gci_header.Gamecode), BE32(current->Dir[i].Gamecode));
}
memcpy((u8*)&(m_saves[i].m_gci_header), (u8*)&(current->Dir[i]), DENTRY_SIZE);
if (old_start != new_start)
@ -436,7 +420,7 @@ inline void GCMemcardDirectory::SyncSaves()
else if ((i < m_saves.size()) && (*(u32*)&(m_saves[i].m_gci_header) != 0xFFFFFFFF))
{
INFO_LOG(EXPANSIONINTERFACE, "Clearing and/or deleting save 0x%x",
BE32(m_saves[i].m_gci_header.Gamecode));
BE32(m_saves[i].m_gci_header.Gamecode));
*(u32*)&(m_saves[i].m_gci_header.Gamecode) = 0xFFFFFFFF;
m_saves[i].m_save_data.clear();
m_saves[i].m_used_blocks.clear();
@ -495,11 +479,11 @@ s32 GCMemcardDirectory::DirectoryWrite(u32 dest_address, u32 length, const u8* s
// needed to update the update ctr, checksums
// could check for writes to the 6 important bytes but doubtful that it improves performance
// noticably
memcpy((u8*)(dest) + offset, src_address, length);
memcpy((u8*)(dest)+offset, src_address, length);
SyncSaves();
}
else
memcpy((u8*)(dest) + offset, src_address, length);
memcpy((u8*)(dest)+offset, src_address, length);
return length;
}
@ -528,8 +512,8 @@ bool GCMemcardDirectory::SetUsedBlocks(int save_index)
if (blocks_from_bat != num_blocks)
{
PanicAlertT("Warning: Number of blocks indicated by the BAT (%u) does not match that of the "
"loaded file header (%u)",
blocks_from_bat, num_blocks);
"loaded file header (%u)",
blocks_from_bat, num_blocks);
return false;
}
@ -554,7 +538,7 @@ void GCMemcardDirectory::FlushToFile()
// to
// skip flushing this file until actual save data is modified
ERROR_LOG(EXPANSIONINTERFACE,
"GCI header modified without corresponding save data changes");
"GCI header modified without corresponding save data changes");
continue;
}
if (m_saves[i].m_filename.empty())
@ -570,7 +554,7 @@ void GCMemcardDirectory::FlushToFile()
}
if (File::Exists(default_save_name))
PanicAlertT("Failed to find new filename.\n%s\n will be overwritten",
default_save_name.c_str());
default_save_name.c_str());
m_saves[i].m_filename = default_save_name;
}
File::IOFile gci(m_saves[i].m_filename, "wb");
@ -582,16 +566,16 @@ void GCMemcardDirectory::FlushToFile()
if (gci.IsGood())
{
Core::DisplayMessage(
StringFromFormat("Wrote save contents to %s", m_saves[i].m_filename.c_str()), 4000);
StringFromFormat("Wrote save contents to %s", m_saves[i].m_filename.c_str()), 4000);
}
else
{
++errors;
Core::DisplayMessage(StringFromFormat("Failed to write save contents to %s",
m_saves[i].m_filename.c_str()),
4000);
m_saves[i].m_filename.c_str()),
4000);
ERROR_LOG(EXPANSIONINTERFACE, "Failed to save data to %s",
m_saves[i].m_filename.c_str());
m_saves[i].m_filename.c_str());
}
}
}
@ -618,7 +602,7 @@ void GCMemcardDirectory::FlushToFile()
if (gamecode != m_game_id && gamecode != 0xFFFFFFFF && m_saves[i].m_save_data.size())
{
INFO_LOG(EXPANSIONINTERFACE, "Flushing savedata to disk for %s",
m_saves[i].m_filename.c_str());
m_saves[i].m_filename.c_str());
m_saves[i].m_save_data.clear();
}
}
@ -704,7 +688,7 @@ void MigrateFromMemcardFile(const std::string& directory_name, int card_index)
{
File::CreateFullPath(directory_name);
std::string ini_memcard = (card_index == 0) ? SConfig::GetInstance().m_strMemoryCardA :
SConfig::GetInstance().m_strMemoryCardB;
SConfig::GetInstance().m_strMemoryCardB;
if (File::Exists(ini_memcard))
{
GCMemcard memcard(ini_memcard.c_str());

View file

@ -12,7 +12,6 @@
#include "Common/Event.h"
#include "Common/NonCopyable.h"
#include "Core/HW/GCMemcard/GCMemcard.h"
#include "DiscIO/Enums.h"
// Uncomment this to write the system data of the memorycard from directory to disc
//#define _WRITE_MC_HEADER 1
@ -22,7 +21,7 @@ class GCMemcardDirectory : public MemoryCardBase, NonCopyable
{
public:
GCMemcardDirectory(const std::string& directory, int slot, u16 size_mbits, bool shift_jis,
DiscIO::Region card_region, int game_id);
int game_id);
~GCMemcardDirectory();
void FlushToFile();
void FlushThread();
@ -33,7 +32,7 @@ public:
void DoState(PointerWrap& p) override;
private:
int LoadGCI(const std::string& file_name, DiscIO::Region card_region, bool current_game_only);
int LoadGCI(const std::string& file_name, bool current_game_only);
inline s32 SaveAreaRW(u32 block, bool writing = false);
// s32 DirectoryRead(u32 offset, u32 length, u8* dest_address);
s32 DirectoryWrite(u32 dest_address, u32 length, const u8* src_address);

View file

@ -343,6 +343,7 @@ void CWiiSaveCrypted::ImportWiiSaveFiles()
std::string file_path_full = m_wii_title_path + file_path;
File::CreateFullPath(file_path_full);
const File::FileInfo file_info(file_path_full);
if (file_hdr_tmp.type == 1)
{
file_size = Common::swap32(file_hdr_tmp.size);
@ -361,7 +362,7 @@ void CWiiSaveCrypted::ImportWiiSaveFiles()
mbedtls_aes_crypt_cbc(&m_aes_ctx, MBEDTLS_AES_DECRYPT, file_size_rounded, m_iv,
static_cast<const u8*>(file_data_enc.data()), file_data.data());
if (!File::Exists(file_path_full) ||
if (!file_info.Exists() ||
AskYesNoT("%s already exists, overwrite?", file_path_full.c_str()))
{
INFO_LOG(CONSOLE, "Creating file %s", file_path_full.c_str());
@ -372,12 +373,12 @@ void CWiiSaveCrypted::ImportWiiSaveFiles()
}
else if (file_hdr_tmp.type == 2)
{
if (!File::Exists(file_path_full))
if (!file_info.Exists())
{
if (!File::CreateDir(file_path_full))
ERROR_LOG(CONSOLE, "Failed to create directory %s", file_path_full.c_str());
}
else if (!File::IsDirectory(file_path_full))
else if (!file_info.IsDirectory())
{
ERROR_LOG(CONSOLE,
"Failed to create directory %s because a file with the same name exists",
@ -399,13 +400,14 @@ void CWiiSaveCrypted::ExportWiiSaveFiles()
memset(&file_hdr_tmp, 0, FILE_HDR_SZ);
u32 file_size = 0;
if (File::IsDirectory(m_files_list[i]))
const File::FileInfo file_info(m_files_list[i]);
if (file_info.IsDirectory())
{
file_hdr_tmp.type = 2;
}
else
{
file_size = static_cast<u32>(File::GetSize(m_files_list[i]));
file_size = static_cast<u32>(file_info.GetSize());
file_hdr_tmp.type = 1;
}

View file

@ -6,6 +6,7 @@
#ifdef ANDROID
#include <jni.h>
#include <string>
#include "Common/StringUtil.h"
#include "Core/HW/WiimoteReal/WiimoteReal.h"
@ -17,7 +18,7 @@ class WiimoteAndroid final : public Wiimote
public:
WiimoteAndroid(int index);
~WiimoteAndroid() override;
std::string GetId() const override { return "Android " + StringFromInt(m_mayflash_index); }
std::string GetId() const override { return "Android " + std::to_string(m_mayflash_index); }
protected:
bool ConnectInternal() override;
void DisconnectInternal() override;

View file

@ -23,7 +23,7 @@
// The host can be just a command line app that opens a window, or a full blown debugger
// interface.
bool Host_UIHasFocus();
bool Host_UINeedsControllerState();
bool Host_RendererHasFocus();
bool Host_RendererIsFullscreen();
void Host_ConnectWiimote(int wm_idx, bool connect);
@ -31,7 +31,6 @@ void Host_Message(int Id);
void Host_NotifyMapLoaded();
void Host_RefreshDSPDebuggerWindow();
void Host_RequestRenderWindowSize(int width, int height);
void Host_SetWiiMoteConnectionState(int _State);
void Host_UpdateDisasmDialog();
void Host_UpdateMainFrame();
void Host_UpdateTitle(const std::string& title);

View file

@ -321,15 +321,7 @@ void ES::Context::DoState(PointerWrap& p)
{
p.Do(uid);
p.Do(gid);
title_import.tmd.DoState(p);
p.Do(title_import.content_id);
p.Do(title_import.content_buffer);
p.Do(title_export.valid);
title_export.tmd.DoState(p);
p.Do(title_export.title_key);
p.Do(title_export.contents);
title_import_export.DoState(p);
p.Do(active);
p.Do(ipc_fd);

View file

@ -13,6 +13,7 @@
#include "Core/IOS/Device.h"
#include "Core/IOS/ES/Formats.h"
#include "Core/IOS/IOS.h"
#include "Core/IOS/IOSC.h"
class PointerWrap;
@ -55,35 +56,21 @@ public:
ReturnCode Close(u32 fd) override;
IPCCommandResult IOCtlV(const IOCtlVRequest& request) override;
struct OpenedContent
struct TitleImportExportContext
{
bool m_opened = false;
u64 m_title_id = 0;
IOS::ES::Content m_content;
u32 m_position = 0;
u32 m_uid = 0;
};
struct TitleImportContext
{
IOS::ES::TMDReader tmd;
u32 content_id = 0xFFFFFFFF;
std::vector<u8> content_buffer;
};
// TODO: merge this with TitleImportContext. Also reuse the global content table.
struct TitleExportContext
{
struct ExportContent
{
OpenedContent content;
std::array<u8, 16> iv{};
};
void DoState(PointerWrap& p);
bool valid = false;
IOS::ES::TMDReader tmd;
std::array<u8, 16> title_key;
std::map<u32, ExportContent> contents;
IOSC::Handle key_handle = 0;
struct ContentContext
{
bool valid = false;
u32 id = 0;
std::array<u8, 16> iv{};
std::vector<u8> buffer;
};
ContentContext content;
};
struct Context
@ -92,8 +79,7 @@ public:
u16 gid = 0;
u32 uid = 0;
TitleImportContext title_import;
TitleExportContext title_export;
TitleImportExportContext title_import_export;
bool active = false;
// We use this to associate an IPC fd with an ES context.
s32 ipc_fd = -1;
@ -113,6 +99,12 @@ public:
u32 GetSharedContentsCount() const;
std::vector<std::array<u8, 20>> GetSharedContents() const;
// Title contents
s32 OpenContent(const IOS::ES::TMDReader& tmd, u16 content_index, u32 uid);
ReturnCode CloseContent(u32 cfd, u32 uid);
s32 ReadContent(u32 cfd, u8* buffer, u32 size, u32 uid);
s32 SeekContent(u32 cfd, u32 offset, SeekMode mode, u32 uid);
// Title management
ReturnCode ImportTicket(const std::vector<u8>& ticket_bytes, const std::vector<u8>& cert_chain);
ReturnCode ImportTmd(Context& context, const std::vector<u8>& tmd_bytes);
@ -342,7 +334,15 @@ private:
static const DiscIO::NANDContentLoader& AccessContentDevice(u64 title_id);
s32 OpenContent(const IOS::ES::TMDReader& tmd, u16 content_index, u32 uid);
// TODO: reuse the FS code.
struct OpenedContent
{
bool m_opened = false;
u64 m_title_id = 0;
IOS::ES::Content m_content;
u32 m_position = 0;
u32 m_uid = 0;
};
using ContentTable = std::array<OpenedContent, 16>;
ContentTable m_content_table;

View file

@ -232,14 +232,6 @@ u64 TMDReader::GetIOSId() const
return Common::swap64(m_bytes.data() + offsetof(TMDHeader, ios_id));
}
DiscIO::Region TMDReader::GetRegion() const
{
if (GetTitleId() == Titles::SYSTEM_MENU)
return DiscIO::GetSysMenuRegion(GetTitleVersion());
return DiscIO::RegionSwitchWii(static_cast<u8>(GetTitleId() & 0xff));
}
u64 TMDReader::GetTitleId() const
{
return Common::swap64(m_bytes.data() + offsetof(TMDHeader, title_id));
@ -260,6 +252,14 @@ u16 TMDReader::GetGroupId() const
return Common::swap16(m_bytes.data() + offsetof(TMDHeader, group_id));
}
DiscIO::Region TMDReader::GetRegion() const
{
if (GetTitleId() == Titles::SYSTEM_MENU)
return DiscIO::GetSysMenuRegion(GetTitleVersion());
return DiscIO::RegionSwitchWii(static_cast<u8>(GetTitleId() & 0xff));
}
std::string TMDReader::GetGameID() const
{
char game_id[6];

View file

@ -49,6 +49,8 @@ enum TitleFlags : u32
TITLE_TYPE_0x4 = 0x4,
// Used for DLC titles.
TITLE_TYPE_DATA = 0x8,
// Unknown.
TITLE_TYPE_0x10 = 0x10,
// Appears to be used for WFS titles.
TITLE_TYPE_WFS_MAYBE = 0x20,
// Unknown.
@ -185,12 +187,14 @@ public:
u16 GetBootIndex() const;
u64 GetIOSId() const;
DiscIO::Region GetRegion() const;
u64 GetTitleId() const;
u32 GetTitleFlags() const;
u16 GetTitleVersion() const;
u16 GetGroupId() const;
// Provides a best guess for the region. Might be inaccurate or UNKNOWN_REGION.
DiscIO::Region GetRegion() const;
// Constructs a 6-character game ID in the format typically used by Dolphin.
// If the 6-character game ID would contain unprintable characters,
// the title ID converted to hexadecimal is returned instead.

View file

@ -88,23 +88,16 @@ IPCCommandResult ES::OpenActiveTitleContent(u32 caller_uid, const IOCtlVRequest&
return GetDefaultReply(OpenContent(GetTitleContext().tmd, content_index, caller_uid));
}
IPCCommandResult ES::ReadContent(u32 uid, const IOCtlVRequest& request)
s32 ES::ReadContent(u32 cfd, u8* buffer, u32 size, u32 uid)
{
if (!request.HasNumberOfValidVectors(1, 1) || request.in_vectors[0].size != sizeof(u32))
return GetDefaultReply(ES_EINVAL);
const u32 cfd = Memory::Read_U32(request.in_vectors[0].address);
u32 size = request.io_vectors[0].size;
const u32 addr = request.io_vectors[0].address;
if (cfd >= m_content_table.size())
return GetDefaultReply(ES_EINVAL);
return ES_EINVAL;
OpenedContent& entry = m_content_table[cfd];
if (entry.m_uid != uid)
return GetDefaultReply(ES_EACCES);
return ES_EACCES;
if (!entry.m_opened)
return GetDefaultReply(IPC_EINVAL);
return IPC_EINVAL;
// XXX: make this reuse the FS code... ES just does a simple "IOS_Read" call here
// instead of all this duplicated filesystem logic.
@ -112,46 +105,45 @@ IPCCommandResult ES::ReadContent(u32 uid, const IOCtlVRequest& request)
if (entry.m_position + size > entry.m_content.size)
size = static_cast<u32>(entry.m_content.size) - entry.m_position;
if (size > 0)
const DiscIO::NANDContentLoader& ContentLoader = AccessContentDevice(entry.m_title_id);
// ContentLoader should never be invalid; rContent has been created by it.
if (ContentLoader.IsValid() && ContentLoader.GetTicket().IsValid())
{
if (addr)
const DiscIO::NANDContent* pContent = ContentLoader.GetContentByIndex(entry.m_content.index);
pContent->m_Data->Open();
if (!pContent->m_Data->GetRange(entry.m_position, size, buffer))
{
const DiscIO::NANDContentLoader& ContentLoader = AccessContentDevice(entry.m_title_id);
// ContentLoader should never be invalid; rContent has been created by it.
if (ContentLoader.IsValid() && ContentLoader.GetTicket().IsValid())
{
const DiscIO::NANDContent* pContent =
ContentLoader.GetContentByIndex(entry.m_content.index);
pContent->m_Data->Open();
if (!pContent->m_Data->GetRange(entry.m_position, size, Memory::GetPointer(addr)))
ERROR_LOG(IOS_ES, "ES: failed to read %u bytes from %u!", size, entry.m_position);
}
entry.m_position += size;
}
else
{
PanicAlert("IOCTL_ES_READCONTENT - bad destination");
ERROR_LOG(IOS_ES, "ES: failed to read %u bytes from %u!", size, entry.m_position);
return ES_SHORT_READ;
}
}
return GetDefaultReply(size);
entry.m_position += size;
return size;
}
IPCCommandResult ES::CloseContent(u32 uid, const IOCtlVRequest& request)
IPCCommandResult ES::ReadContent(u32 uid, const IOCtlVRequest& request)
{
if (!request.HasNumberOfValidVectors(1, 0) || request.in_vectors[0].size != sizeof(u32))
if (!request.HasNumberOfValidVectors(1, 1) || request.in_vectors[0].size != sizeof(u32))
return GetDefaultReply(ES_EINVAL);
const u32 cfd = Memory::Read_U32(request.in_vectors[0].address);
const u32 size = request.io_vectors[0].size;
const u32 addr = request.io_vectors[0].address;
return GetDefaultReply(ReadContent(cfd, Memory::GetPointer(addr), size, uid));
}
ReturnCode ES::CloseContent(u32 cfd, u32 uid)
{
if (cfd >= m_content_table.size())
return GetDefaultReply(ES_EINVAL);
return ES_EINVAL;
OpenedContent& entry = m_content_table[cfd];
if (entry.m_uid != uid)
return GetDefaultReply(ES_EACCES);
return ES_EACCES;
if (!entry.m_opened)
return GetDefaultReply(IPC_EINVAL);
return IPC_EINVAL;
// XXX: again, this should be a simple IOS_Close.
const DiscIO::NANDContentLoader& ContentLoader = AccessContentDevice(entry.m_title_id);
@ -164,7 +156,49 @@ IPCCommandResult ES::CloseContent(u32 uid, const IOCtlVRequest& request)
entry = {};
INFO_LOG(IOS_ES, "CloseContent: CFD %u", cfd);
return GetDefaultReply(IPC_SUCCESS);
return IPC_SUCCESS;
}
IPCCommandResult ES::CloseContent(u32 uid, const IOCtlVRequest& request)
{
if (!request.HasNumberOfValidVectors(1, 0) || request.in_vectors[0].size != sizeof(u32))
return GetDefaultReply(ES_EINVAL);
const u32 cfd = Memory::Read_U32(request.in_vectors[0].address);
return GetDefaultReply(CloseContent(cfd, uid));
}
s32 ES::SeekContent(u32 cfd, u32 offset, SeekMode mode, u32 uid)
{
if (cfd >= m_content_table.size())
return ES_EINVAL;
OpenedContent& entry = m_content_table[cfd];
if (entry.m_uid != uid)
return ES_EACCES;
if (!entry.m_opened)
return IPC_EINVAL;
// XXX: This should be a simple IOS_Seek.
switch (mode)
{
case SeekMode::IOS_SEEK_SET:
entry.m_position = offset;
break;
case SeekMode::IOS_SEEK_CUR:
entry.m_position += offset;
break;
case SeekMode::IOS_SEEK_END:
entry.m_position = static_cast<u32>(entry.m_content.size) + offset;
break;
default:
return FS_EINVAL;
}
return entry.m_position;
}
IPCCommandResult ES::SeekContent(u32 uid, const IOCtlVRequest& request)
@ -173,35 +207,10 @@ IPCCommandResult ES::SeekContent(u32 uid, const IOCtlVRequest& request)
return GetDefaultReply(ES_EINVAL);
const u32 cfd = Memory::Read_U32(request.in_vectors[0].address);
if (cfd >= m_content_table.size())
return GetDefaultReply(ES_EINVAL);
const u32 offset = Memory::Read_U32(request.in_vectors[1].address);
const SeekMode mode = static_cast<SeekMode>(Memory::Read_U32(request.in_vectors[2].address));
OpenedContent& entry = m_content_table[cfd];
if (entry.m_uid != uid)
return GetDefaultReply(ES_EACCES);
if (!entry.m_opened)
return GetDefaultReply(IPC_EINVAL);
u32 Addr = Memory::Read_U32(request.in_vectors[1].address);
u32 Mode = Memory::Read_U32(request.in_vectors[2].address);
// XXX: This should be a simple IOS_Seek.
switch (Mode)
{
case 0: // SET
entry.m_position = Addr;
break;
case 1: // CUR
entry.m_position += Addr;
break;
case 2: // END
entry.m_position = static_cast<u32>(entry.m_content.size) + Addr;
break;
}
return GetDefaultReply(entry.m_position);
return GetDefaultReply(SeekContent(cfd, offset, mode, uid));
}
} // namespace Device
} // namespace HLE

View file

@ -13,12 +13,12 @@
#include <mbedtls/sha1.h>
#include "Common/Align.h"
#include "Common/Crypto/AES.h"
#include "Common/File.h"
#include "Common/FileUtil.h"
#include "Common/Logging/Log.h"
#include "Common/NandPaths.h"
#include "Common/StringUtil.h"
#include "Core/CommonTitles.h"
#include "Core/HW/Memmap.h"
#include "Core/IOS/ES/Formats.h"
#include "Core/ec_wii.h"
@ -45,6 +45,17 @@ static ReturnCode WriteTicket(const IOS::ES::TicketReader& ticket)
return ticket_file.WriteBytes(raw_ticket.data(), raw_ticket.size()) ? IPC_SUCCESS : ES_EIO;
}
void ES::TitleImportExportContext::DoState(PointerWrap& p)
{
p.Do(valid);
p.Do(key_handle);
tmd.DoState(p);
p.Do(content.valid);
p.Do(content.id);
p.Do(content.iv);
p.Do(content.buffer);
}
ReturnCode ES::ImportTicket(const std::vector<u8>& ticket_bytes, const std::vector<u8>& cert_chain)
{
IOS::ES::TicketReader ticket{ticket_bytes};
@ -94,12 +105,45 @@ IPCCommandResult ES::ImportTicket(const IOCtlVRequest& request)
return GetDefaultReply(ImportTicket(bytes, cert_chain));
}
constexpr std::array<u8, 16> NULL_KEY{};
// Used for exporting titles and importing them back (ImportTmd and ExportTitleInit).
static ReturnCode InitBackupKey(const IOS::ES::TMDReader& tmd, IOSC& iosc, IOSC::Handle* key)
{
// Some versions of IOS have a bug that causes it to use a zeroed key instead of the PRNG key.
// When Nintendo decided to fix it, they added checks to keep using the zeroed key only in
// affected titles to avoid making existing exports useless.
// Ignore the region byte.
const u64 title_id = tmd.GetTitleId() | 0xff;
const u32 title_flags = tmd.GetTitleFlags();
const u32 affected_type = IOS::ES::TITLE_TYPE_0x10 | IOS::ES::TITLE_TYPE_DATA;
if (title_id == Titles::SYSTEM_MENU || (title_flags & affected_type) != affected_type ||
!(title_id == 0x00010005735841ff || title_id - 0x00010005735a41ff <= 0x700))
{
*key = IOSC::HANDLE_PRNG_KEY;
return IPC_SUCCESS;
}
// Otherwise, use a null key.
ReturnCode ret = iosc.CreateObject(key, IOSC::TYPE_SECRET_KEY, IOSC::SUBTYPE_AES128, PID_ES);
return ret == IPC_SUCCESS ? iosc.ImportSecretKey(*key, NULL_KEY.data(), PID_ES) : ret;
}
static void ResetTitleImportContext(ES::Context* context, IOSC& iosc)
{
if (context->title_import_export.key_handle)
iosc.DeleteObject(context->title_import_export.key_handle, PID_ES);
context->title_import_export = {};
}
ReturnCode ES::ImportTmd(Context& context, const std::vector<u8>& tmd_bytes)
{
// Ioctlv 0x2b writes the TMD to /tmp/title.tmd (for imports) and doesn't seem to write it
// to either /import or /title. So here we simply have to set the import TMD.
context.title_import.tmd.SetBytes(tmd_bytes);
if (!context.title_import.tmd.IsValid())
ResetTitleImportContext(&context, m_ios.GetIOSC());
context.title_import_export.tmd.SetBytes(tmd_bytes);
if (!context.title_import_export.tmd.IsValid())
return ES_EINVAL;
std::vector<u8> cert_store;
@ -108,17 +152,19 @@ ReturnCode ES::ImportTmd(Context& context, const std::vector<u8>& tmd_bytes)
return ret;
ret = VerifyContainer(VerifyContainerType::TMD, VerifyMode::UpdateCertStore,
context.title_import.tmd, cert_store);
context.title_import_export.tmd, cert_store);
if (ret != IPC_SUCCESS)
{
// Reset the import context so that further calls consider the state as invalid.
context.title_import.tmd.SetBytes({});
return ret;
}
if (!InitImport(context.title_import.tmd.GetTitleId()))
if (!InitImport(context.title_import_export.tmd.GetTitleId()))
return ES_EIO;
ret = InitBackupKey(GetTitleContext().tmd, m_ios.GetIOSC(),
&context.title_import_export.key_handle);
if (ret != IPC_SUCCESS)
return ret;
context.title_import_export.valid = true;
return IPC_SUCCESS;
}
@ -135,29 +181,45 @@ IPCCommandResult ES::ImportTmd(Context& context, const IOCtlVRequest& request)
return GetDefaultReply(ImportTmd(context, tmd));
}
static ReturnCode InitTitleImportKey(const std::vector<u8>& ticket_bytes, IOSC& iosc,
IOSC::Handle* handle)
{
ReturnCode ret = iosc.CreateObject(handle, IOSC::TYPE_SECRET_KEY, IOSC::SUBTYPE_AES128, PID_ES);
if (ret != IPC_SUCCESS)
return ret;
std::array<u8, 16> iv{};
std::copy_n(&ticket_bytes[offsetof(IOS::ES::Ticket, title_id)], sizeof(u64), iv.begin());
const u8 index = ticket_bytes[offsetof(IOS::ES::Ticket, common_key_index)];
if (index > 1)
return ES_INVALID_TICKET;
return iosc.ImportSecretKey(
*handle, index == 0 ? IOSC::HANDLE_COMMON_KEY : IOSC::HANDLE_NEW_COMMON_KEY, iv.data(),
&ticket_bytes[offsetof(IOS::ES::Ticket, title_key)], PID_ES);
}
ReturnCode ES::ImportTitleInit(Context& context, const std::vector<u8>& tmd_bytes,
const std::vector<u8>& cert_chain)
{
INFO_LOG(IOS_ES, "ImportTitleInit");
context.title_import.tmd.SetBytes(tmd_bytes);
if (!context.title_import.tmd.IsValid())
ResetTitleImportContext(&context, m_ios.GetIOSC());
context.title_import_export.tmd.SetBytes(tmd_bytes);
if (!context.title_import_export.tmd.IsValid())
{
ERROR_LOG(IOS_ES, "Invalid TMD while adding title (size = %zd)", tmd_bytes.size());
return ES_EINVAL;
}
// Finish a previous import (if it exists).
FinishStaleImport(context.title_import.tmd.GetTitleId());
FinishStaleImport(context.title_import_export.tmd.GetTitleId());
ReturnCode ret = VerifyContainer(VerifyContainerType::TMD, VerifyMode::UpdateCertStore,
context.title_import.tmd, cert_chain);
context.title_import_export.tmd, cert_chain);
if (ret != IPC_SUCCESS)
{
context.title_import.tmd.SetBytes({});
return ret;
}
const auto ticket = DiscIO::FindSignedTicket(context.title_import.tmd.GetTitleId());
const auto ticket = DiscIO::FindSignedTicket(context.title_import_export.tmd.GetTitleId());
if (!ticket.IsValid())
return ES_NO_TICKET;
@ -169,14 +231,17 @@ ReturnCode ES::ImportTitleInit(Context& context, const std::vector<u8>& tmd_byte
ret = VerifyContainer(VerifyContainerType::Ticket, VerifyMode::DoNotUpdateCertStore, ticket,
cert_store);
if (ret != IPC_SUCCESS)
{
context.title_import.tmd.SetBytes({});
return ret;
}
if (!InitImport(context.title_import.tmd.GetTitleId()))
ret = InitTitleImportKey(ticket.GetBytes(), m_ios.GetIOSC(),
&context.title_import_export.key_handle);
if (ret != IPC_SUCCESS)
return ret;
if (!InitImport(context.title_import_export.tmd.GetTitleId()))
return ES_EIO;
context.title_import_export.valid = true;
return IPC_SUCCESS;
}
@ -197,30 +262,40 @@ IPCCommandResult ES::ImportTitleInit(Context& context, const IOCtlVRequest& requ
ReturnCode ES::ImportContentBegin(Context& context, u64 title_id, u32 content_id)
{
if (context.title_import.content_id != 0xFFFFFFFF)
if (context.title_import_export.content.valid)
{
ERROR_LOG(IOS_ES, "Trying to add content when we haven't finished adding "
"another content. Unsupported.");
return ES_EINVAL;
}
context.title_import.content_id = content_id;
context.title_import.content_buffer.clear();
context.title_import_export.content = {};
context.title_import_export.content.id = content_id;
INFO_LOG(IOS_ES, "ImportContentBegin: title %016" PRIx64 ", content ID %08x", title_id,
context.title_import.content_id);
context.title_import_export.content.id);
if (!context.title_import.tmd.IsValid())
if (!context.title_import_export.valid)
return ES_EINVAL;
if (title_id != context.title_import.tmd.GetTitleId())
if (title_id != context.title_import_export.tmd.GetTitleId())
{
ERROR_LOG(IOS_ES, "ImportContentBegin: title id %016" PRIx64 " != "
"TMD title id %016" PRIx64 ", ignoring",
title_id, context.title_import.tmd.GetTitleId());
title_id, context.title_import_export.tmd.GetTitleId());
return ES_EINVAL;
}
// The IV for title content decryption is the lower two bytes of the
// content index, zero extended.
IOS::ES::Content content_info;
if (!context.title_import_export.tmd.FindContentById(context.title_import_export.content.id,
&content_info))
return ES_EINVAL;
context.title_import_export.content.iv[0] = (content_info.index >> 8) & 0xFF;
context.title_import_export.content.iv[1] = content_info.index & 0xFF;
context.title_import_export.content.valid = true;
// We're supposed to return a "content file descriptor" here, which is
// passed to further AddContentData / AddContentFinish. But so far there is
// no known content installer which performs content addition concurrently.
@ -242,8 +317,8 @@ IPCCommandResult ES::ImportContentBegin(Context& context, const IOCtlVRequest& r
ReturnCode ES::ImportContentData(Context& context, u32 content_fd, const u8* data, u32 data_size)
{
INFO_LOG(IOS_ES, "ImportContentData: content fd %08x, size %d", content_fd, data_size);
context.title_import.content_buffer.insert(context.title_import.content_buffer.end(), data,
data + data_size);
context.title_import_export.content.buffer.insert(
context.title_import_export.content.buffer.end(), data, data + data_size);
return IPC_SUCCESS;
}
@ -274,29 +349,20 @@ ReturnCode ES::ImportContentEnd(Context& context, u32 content_fd)
{
INFO_LOG(IOS_ES, "ImportContentEnd: content fd %08x", content_fd);
if (context.title_import.content_id == 0xFFFFFFFF)
if (!context.title_import_export.valid || !context.title_import_export.content.valid)
return ES_EINVAL;
if (!context.title_import.tmd.IsValid())
return ES_EINVAL;
std::vector<u8> decrypted_data(context.title_import_export.content.buffer.size());
const ReturnCode decrypt_ret = m_ios.GetIOSC().Decrypt(
context.title_import_export.key_handle, context.title_import_export.content.iv.data(),
context.title_import_export.content.buffer.data(),
context.title_import_export.content.buffer.size(), decrypted_data.data(), PID_ES);
if (decrypt_ret != IPC_SUCCESS)
return decrypt_ret;
// Try to find the title key from a pre-installed ticket.
IOS::ES::TicketReader ticket = DiscIO::FindSignedTicket(context.title_import.tmd.GetTitleId());
if (!ticket.IsValid())
return ES_NO_TICKET;
// The IV for title content decryption is the lower two bytes of the
// content index, zero extended.
IOS::ES::Content content_info;
if (!context.title_import.tmd.FindContentById(context.title_import.content_id, &content_info))
return ES_EINVAL;
u8 iv[16] = {0};
iv[0] = (content_info.index >> 8) & 0xFF;
iv[1] = content_info.index & 0xFF;
std::vector<u8> decrypted_data = Common::AES::Decrypt(
ticket.GetTitleKey(m_ios.GetIOSC()).data(), iv, context.title_import.content_buffer.data(),
context.title_import.content_buffer.size());
context.title_import_export.tmd.FindContentById(context.title_import_export.content.id,
&content_info);
if (!CheckIfContentHashMatches(decrypted_data, content_info))
{
ERROR_LOG(IOS_ES, "ImportContentEnd: Hash for content %08x doesn't match", content_info.id);
@ -311,13 +377,14 @@ ReturnCode ES::ImportContentEnd(Context& context, u32 content_fd)
}
else
{
content_path = GetImportContentPath(context.title_import.tmd.GetTitleId(),
context.title_import.content_id);
content_path = GetImportContentPath(context.title_import_export.tmd.GetTitleId(),
context.title_import_export.content.id);
}
File::CreateFullPath(content_path);
const std::string temp_path = Common::RootUserPath(Common::FROM_SESSION_ROOT) +
StringFromFormat("/tmp/%08x.app", context.title_import.content_id);
const std::string temp_path =
Common::RootUserPath(Common::FROM_SESSION_ROOT) +
StringFromFormat("/tmp/%08x.app", context.title_import_export.content.id);
File::CreateFullPath(temp_path);
{
@ -335,7 +402,7 @@ ReturnCode ES::ImportContentEnd(Context& context, u32 content_fd)
return ES_EIO;
}
context.title_import.content_id = 0xFFFFFFFF;
context.title_import_export.content = {};
return IPC_SUCCESS;
}
@ -350,12 +417,12 @@ IPCCommandResult ES::ImportContentEnd(Context& context, const IOCtlVRequest& req
ReturnCode ES::ImportTitleDone(Context& context)
{
if (!context.title_import.tmd.IsValid() || context.title_import.content_id != 0xFFFFFFFF)
if (!context.title_import_export.valid || context.title_import_export.content.valid)
return ES_EINVAL;
// Make sure all listed, non-optional contents have been imported.
const u64 title_id = context.title_import.tmd.GetTitleId();
const std::vector<IOS::ES::Content> contents = context.title_import.tmd.GetContents();
const u64 title_id = context.title_import_export.tmd.GetTitleId();
const std::vector<IOS::ES::Content> contents = context.title_import_export.tmd.GetContents();
const IOS::ES::SharedContentMap shared_content_map{Common::FROM_SESSION_ROOT};
const bool has_all_required_contents =
std::all_of(contents.cbegin(), contents.cend(), [&](const IOS::ES::Content& content) {
@ -373,14 +440,14 @@ ReturnCode ES::ImportTitleDone(Context& context)
if (!has_all_required_contents)
return ES_EINVAL;
if (!WriteImportTMD(context.title_import.tmd))
if (!WriteImportTMD(context.title_import_export.tmd))
return ES_EIO;
if (!FinishImport(context.title_import.tmd))
if (!FinishImport(context.title_import_export.tmd))
return ES_EIO;
INFO_LOG(IOS_ES, "ImportTitleDone: title %016" PRIx64, title_id);
context.title_import.tmd.SetBytes({});
ResetTitleImportContext(&context, m_ios.GetIOSC());
return IPC_SUCCESS;
}
@ -394,19 +461,25 @@ IPCCommandResult ES::ImportTitleDone(Context& context, const IOCtlVRequest& requ
ReturnCode ES::ImportTitleCancel(Context& context)
{
if (!context.title_import.tmd.IsValid())
// The TMD buffer can exist without a valid title import context.
if (context.title_import_export.tmd.GetBytes().empty() ||
context.title_import_export.content.valid)
return ES_EINVAL;
FinishStaleImport(context.title_import.tmd.GetTitleId());
if (context.title_import_export.valid)
{
const u64 title_id = context.title_import_export.tmd.GetTitleId();
FinishStaleImport(title_id);
INFO_LOG(IOS_ES, "ImportTitleCancel: title %016" PRIx64, title_id);
}
INFO_LOG(IOS_ES, "ImportTitleCancel: title %016" PRIx64, context.title_import.tmd.GetTitleId());
context.title_import.tmd.SetBytes({});
ResetTitleImportContext(&context, m_ios.GetIOSC());
return IPC_SUCCESS;
}
IPCCommandResult ES::ImportTitleCancel(Context& context, const IOCtlVRequest& request)
{
if (!request.HasNumberOfValidVectors(0, 0) || !context.title_import.tmd.IsValid())
if (!request.HasNumberOfValidVectors(0, 0))
return GetDefaultReply(ES_EINVAL);
return GetDefaultReply(ImportTitleCancel(context));
@ -555,30 +628,28 @@ IPCCommandResult ES::DeleteContent(const IOCtlVRequest& request)
ReturnCode ES::ExportTitleInit(Context& context, u64 title_id, u8* tmd_bytes, u32 tmd_size)
{
// No concurrent title import/export is allowed.
if (context.title_export.valid)
if (context.title_import_export.valid)
return ES_EINVAL;
const auto tmd = FindInstalledTMD(title_id);
if (!tmd.IsValid())
return FS_ENOENT;
context.title_export.tmd = tmd;
ResetTitleImportContext(&context, m_ios.GetIOSC());
context.title_import_export.tmd = tmd;
const auto ticket = DiscIO::FindSignedTicket(context.title_export.tmd.GetTitleId());
if (!ticket.IsValid())
return ES_NO_TICKET;
if (ticket.GetTitleId() != context.title_export.tmd.GetTitleId())
return ES_EINVAL;
const ReturnCode ret = InitBackupKey(GetTitleContext().tmd, m_ios.GetIOSC(),
&context.title_import_export.key_handle);
if (ret != IPC_SUCCESS)
return ret;
context.title_export.title_key = ticket.GetTitleKey(m_ios.GetIOSC());
const std::vector<u8>& raw_tmd = context.title_export.tmd.GetBytes();
const std::vector<u8>& raw_tmd = context.title_import_export.tmd.GetBytes();
if (tmd_size != raw_tmd.size())
return ES_EINVAL;
std::copy_n(raw_tmd.cbegin(), raw_tmd.size(), tmd_bytes);
context.title_export.valid = true;
context.title_import_export.valid = true;
return IPC_SUCCESS;
}
@ -596,38 +667,33 @@ IPCCommandResult ES::ExportTitleInit(Context& context, const IOCtlVRequest& requ
ReturnCode ES::ExportContentBegin(Context& context, u64 title_id, u32 content_id)
{
if (!context.title_export.valid || context.title_export.tmd.GetTitleId() != title_id)
context.title_import_export.content = {};
if (!context.title_import_export.valid ||
context.title_import_export.tmd.GetTitleId() != title_id)
{
ERROR_LOG(IOS_ES, "Tried to use ExportContentBegin with an invalid title export context.");
return ES_EINVAL;
}
const auto& content_loader = AccessContentDevice(title_id);
if (!content_loader.IsValid())
return FS_ENOENT;
const auto* content = content_loader.GetContentByID(content_id);
if (!content)
IOS::ES::Content content_info;
if (!context.title_import_export.tmd.FindContentById(content_id, &content_info))
return ES_EINVAL;
OpenedContent entry;
entry.m_position = 0;
entry.m_content = content->m_metadata;
entry.m_title_id = title_id;
content->m_Data->Open();
context.title_import_export.content.id = content_id;
context.title_import_export.content.valid = true;
u32 cfd = 0;
while (context.title_export.contents.find(cfd) != context.title_export.contents.end())
cfd++;
const s32 ret = OpenContent(context.title_import_export.tmd, content_info.index, 0);
if (ret < 0)
{
ResetTitleImportContext(&context, m_ios.GetIOSC());
return static_cast<ReturnCode>(ret);
}
TitleExportContext::ExportContent content_export;
content_export.content = std::move(entry);
content_export.iv[0] = (content->m_metadata.index >> 8) & 0xFF;
content_export.iv[1] = content->m_metadata.index & 0xFF;
context.title_import_export.content.iv[0] = (content_info.index >> 8) & 0xFF;
context.title_import_export.content.iv[1] = content_info.index & 0xFF;
context.title_export.contents.emplace(cfd, content_export);
// IOS returns a content ID which is passed to further content calls.
return static_cast<ReturnCode>(cfd);
return static_cast<ReturnCode>(ret);
}
IPCCommandResult ES::ExportContentBegin(Context& context, const IOCtlVRequest& request)
@ -644,26 +710,20 @@ IPCCommandResult ES::ExportContentBegin(Context& context, const IOCtlVRequest& r
ReturnCode ES::ExportContentData(Context& context, u32 content_fd, u8* data, u32 data_size)
{
const auto iterator = context.title_export.contents.find(content_fd);
if (!context.title_export.valid || iterator == context.title_export.contents.end() ||
iterator->second.content.m_position >= iterator->second.content.m_content.size)
if (!context.title_import_export.valid || !context.title_import_export.content.valid || !data ||
data_size == 0)
{
CloseContent(content_fd, 0);
context.title_import_export = {};
return ES_EINVAL;
}
auto& metadata = iterator->second.content;
const auto& content_loader = AccessContentDevice(metadata.m_title_id);
const auto* content = content_loader.GetContentByID(metadata.m_content.id);
content->m_Data->Open();
const u32 length =
std::min(static_cast<u32>(metadata.m_content.size - metadata.m_position), data_size);
std::vector<u8> buffer(length);
if (!content->m_Data->GetRange(metadata.m_position, length, buffer.data()))
std::vector<u8> buffer(data_size);
const s32 read_size = ReadContent(content_fd, buffer.data(), static_cast<u32>(buffer.size()), 0);
if (read_size < 0)
{
ERROR_LOG(IOS_ES, "ExportContentData: ES_SHORT_READ");
CloseContent(content_fd, 0);
ResetTitleImportContext(&context, m_ios.GetIOSC());
return ES_SHORT_READ;
}
@ -671,11 +731,14 @@ ReturnCode ES::ExportContentData(Context& context, u32 content_fd, u8* data, u32
// let's just follow IOS here.
buffer.resize(Common::AlignUp(buffer.size(), 32));
const std::vector<u8> output =
Common::AES::Encrypt(context.title_export.title_key.data(), iterator->second.iv.data(),
buffer.data(), buffer.size());
std::copy_n(output.cbegin(), output.size(), data);
metadata.m_position += length;
std::vector<u8> output(buffer.size());
const ReturnCode decrypt_ret = m_ios.GetIOSC().Encrypt(
context.title_import_export.key_handle, context.title_import_export.content.iv.data(),
buffer.data(), buffer.size(), output.data(), PID_ES);
if (decrypt_ret != IPC_SUCCESS)
return decrypt_ret;
std::copy(output.cbegin(), output.cend(), data);
return IPC_SUCCESS;
}
@ -696,20 +759,9 @@ IPCCommandResult ES::ExportContentData(Context& context, const IOCtlVRequest& re
ReturnCode ES::ExportContentEnd(Context& context, u32 content_fd)
{
const auto iterator = context.title_export.contents.find(content_fd);
if (!context.title_export.valid || iterator == context.title_export.contents.end() ||
iterator->second.content.m_position != iterator->second.content.m_content.size)
{
if (!context.title_import_export.valid || !context.title_import_export.content.valid)
return ES_EINVAL;
}
// XXX: Check the content hash, as IOS does?
const auto& content_loader = AccessContentDevice(iterator->second.content.m_title_id);
content_loader.GetContentByID(iterator->second.content.m_content.id)->m_Data->Close();
context.title_export.contents.erase(iterator);
return IPC_SUCCESS;
return CloseContent(content_fd, 0);
}
IPCCommandResult ES::ExportContentEnd(Context& context, const IOCtlVRequest& request)
@ -723,10 +775,7 @@ IPCCommandResult ES::ExportContentEnd(Context& context, const IOCtlVRequest& req
ReturnCode ES::ExportTitleDone(Context& context)
{
if (!context.title_export.valid)
return ES_EINVAL;
context.title_export.valid = false;
ResetTitleImportContext(&context, m_ios.GetIOSC());
return IPC_SUCCESS;
}

View file

@ -524,13 +524,15 @@ IPCCommandResult FS::ReadDirectory(const IOCtlVRequest& request)
INFO_LOG(IOS_FILEIO, "FS: IOCTL_READ_DIR %s", DirName.c_str());
if (!File::Exists(DirName))
const File::FileInfo file_info(DirName);
if (!file_info.Exists())
{
WARN_LOG(IOS_FILEIO, "FS: Search not found: %s", DirName.c_str());
return GetFSReply(FS_ENOENT);
}
if (!File::IsDirectory(DirName))
if (!file_info.IsDirectory())
{
// It's not a directory, so error.
// Games don't usually seem to care WHICH error they get, as long as it's <
@ -613,27 +615,13 @@ IPCCommandResult FS::GetUsage(const IOCtlVRequest& request)
INFO_LOG(IOS_FILEIO, "IOCTL_GETUSAGE %s", path.c_str());
if (File::IsDirectory(path))
{
// LPFaint99: After I found that setting the number of inodes to the number of children + 1
// for the directory itself
// I decided to compare with sneek which has the following 2 special cases which are
// Copyright (C) 2009-2011 crediar http://code.google.com/p/sneek/
if ((relativepath.compare(0, 16, "/title/00010001") == 0) ||
(relativepath.compare(0, 16, "/title/00010005") == 0))
{
fsBlocks = 23; // size is size/0x4000
iNodes = 42; // empty folders return a FileCount of 1
}
else
{
File::FSTEntry parentDir = File::ScanDirectoryTree(path, true);
// add one for the folder itself
iNodes = 1 + (u32)parentDir.size;
File::FSTEntry parentDir = File::ScanDirectoryTree(path, true);
// add one for the folder itself
iNodes = 1 + (u32)parentDir.size;
u64 totalSize =
ComputeTotalFileSize(parentDir); // "Real" size, to be converted to nand blocks
u64 totalSize = ComputeTotalFileSize(parentDir); // "Real" size, to be converted to nand blocks
fsBlocks = (u32)(totalSize / (16 * 1024)); // one bock is 16kb
}
fsBlocks = (u32)(totalSize / (16 * 1024)); // one bock is 16kb
INFO_LOG(IOS_FILEIO, "FS: fsBlock: %i, iNodes: %i", fsBlocks, iNodes);
}

View file

@ -102,7 +102,7 @@ ReturnCode FileIO::Open(const OpenRequest& request)
// The file must exist before we can open it
// It should be created by ISFS_CreateFile, not here
if (!File::Exists(m_filepath) || File::IsDirectory(m_filepath))
if (!File::IsFile(m_filepath))
{
WARN_LOG(IOS_FILEIO, "FileIO: Open (%s) failed - File doesn't exist %s", Modes[m_Mode],
m_filepath.c_str());

View file

@ -85,11 +85,19 @@ constexpr size_t AES128_KEY_SIZE = 0x10;
ReturnCode IOSC::ImportSecretKey(Handle dest_handle, Handle decrypt_handle, u8* iv,
const u8* encrypted_key, u32 pid)
{
if (!HasOwnership(dest_handle, pid) || !HasOwnership(decrypt_handle, pid) ||
IsDefaultHandle(dest_handle))
{
std::array<u8, AES128_KEY_SIZE> decrypted_key;
const ReturnCode ret =
Decrypt(decrypt_handle, iv, encrypted_key, AES128_KEY_SIZE, decrypted_key.data(), pid);
if (ret != IPC_SUCCESS)
return ret;
return ImportSecretKey(dest_handle, decrypted_key.data(), pid);
}
ReturnCode IOSC::ImportSecretKey(Handle dest_handle, const u8* decrypted_key, u32 pid)
{
if (!HasOwnership(dest_handle, pid) || IsDefaultHandle(dest_handle))
return IOSC_EACCES;
}
KeyEntry* dest_entry = FindEntry(dest_handle);
if (!dest_entry)
@ -99,8 +107,8 @@ ReturnCode IOSC::ImportSecretKey(Handle dest_handle, Handle decrypt_handle, u8*
if (dest_entry->type != TYPE_SECRET_KEY || dest_entry->subtype != SUBTYPE_AES128)
return IOSC_INVALID_OBJTYPE;
dest_entry->data.resize(AES128_KEY_SIZE);
return Decrypt(decrypt_handle, iv, encrypted_key, AES128_KEY_SIZE, dest_entry->data.data(), pid);
dest_entry->data = std::vector<u8>(decrypted_key, decrypted_key + AES128_KEY_SIZE);
return IPC_SUCCESS;
}
ReturnCode IOSC::ImportPublicKey(Handle dest_handle, const u8* public_key,
@ -422,8 +430,9 @@ void IOSC::LoadDefaultEntries(ConsoleType console_type)
break;
}
// Unimplemented.
m_key_entries[HANDLE_PRNG_KEY] = {TYPE_SECRET_KEY, SUBTYPE_AES128, std::vector<u8>(16), 3};
m_key_entries[HANDLE_PRNG_KEY] = {
TYPE_SECRET_KEY, SUBTYPE_AES128,
std::vector<u8>(ec.GetBackupKey(), ec.GetBackupKey() + AES128_KEY_SIZE), 3};
m_key_entries[HANDLE_SD_KEY] = {TYPE_SECRET_KEY,
SUBTYPE_AES128,

View file

@ -172,6 +172,8 @@ public:
// Import a secret, encrypted key into dest_handle, which will be decrypted using decrypt_handle.
ReturnCode ImportSecretKey(Handle dest_handle, Handle decrypt_handle, u8* iv,
const u8* encrypted_key, u32 pid);
// Import a secret key that is already decrypted.
ReturnCode ImportSecretKey(Handle dest_handle, const u8* decrypted_key, u32 pid);
// Import a public key. public_key_exponent must be passed for RSA keys.
ReturnCode ImportPublicKey(Handle dest_handle, const u8* public_key,
const u8* public_key_exponent, u32 pid);

View file

@ -27,8 +27,15 @@ constexpr u32 RAM_VENDOR_MIOS = 0xCAFEBABE;
// The writes are usually contained in a single function that
// mostly writes raw literals to the relevant locations.
// e.g. IOS9, version 1034, content id 0x00000006, function at 0xffff6884
constexpr std::array<MemoryValues, 40> ios_memory_values = {
constexpr std::array<MemoryValues, 41> ios_memory_values = {
{{
4, 0x40003, 0x81006, MEM1_SIZE,
MEM1_SIZE, MEM1_END, MEM1_ARENA_BEGIN, MEM1_ARENA_END,
MEM2_SIZE, MEM2_SIZE, 0x93400000, MEM2_ARENA_BEGIN,
0x933E0000, 0x933E0000, 0x93400000, HOLLYWOOD_REVISION,
RAM_VENDOR, PLACEHOLDER, PLACEHOLDER, 0,
},
{
9, 0x9040a, 0x030110, MEM1_SIZE,
MEM1_SIZE, MEM1_END, MEM1_ARENA_BEGIN, MEM1_ARENA_END,
MEM2_SIZE, MEM2_SIZE, 0x93400000, MEM2_ARENA_BEGIN,
@ -324,7 +331,7 @@ constexpr std::array<MemoryValues, 40> ios_memory_values = {
PLACEHOLDER,
}}};
const std::array<MemoryValues, 40>& GetMemoryValues()
const std::array<MemoryValues, 41>& GetMemoryValues()
{
return ios_memory_values;
}

View file

@ -36,6 +36,6 @@ struct MemoryValues
u32 sysmenu_sync;
};
const std::array<MemoryValues, 40>& GetMemoryValues();
const std::array<MemoryValues, 41>& GetMemoryValues();
}
}

View file

@ -27,22 +27,15 @@ NWC24Config::NWC24Config()
void NWC24Config::ReadConfig()
{
if (File::Exists(m_path))
if (!File::IOFile(m_path, "rb").ReadBytes(&m_data, sizeof(m_data)))
{
if (!File::IOFile(m_path, "rb").ReadBytes(&m_data, sizeof(m_data)))
{
ResetConfig();
}
else
{
const s32 config_error = CheckNwc24Config();
if (config_error)
ERROR_LOG(IOS_WC24, "There is an error in the config for for WC24: %d", config_error);
}
ResetConfig();
}
else
{
ResetConfig();
const s32 config_error = CheckNwc24Config();
if (config_error)
ERROR_LOG(IOS_WC24, "There is an error in the config for for WC24: %d", config_error);
}
}

View file

@ -4,6 +4,7 @@
#include "Core/IOS/Network/KD/NetKDTime.h"
#include <cinttypes>
#include <string>
#include "Common/CommonTypes.h"
@ -27,34 +28,50 @@ IPCCommandResult NetKDTime::IOCtl(const IOCtlRequest& request)
s32 result = 0;
u32 common_result = 0;
// TODO Writes stuff to /shared2/nwc24/misc.bin
// u32 update_misc = 0;
u32 update_misc = 0;
switch (request.request)
{
case IOCTL_NW24_GET_UNIVERSAL_TIME:
Memory::Write_U64(GetAdjustedUTC(), request.buffer_out + 4);
break;
{
const u64 adjusted_utc = GetAdjustedUTC();
Memory::Write_U64(adjusted_utc, request.buffer_out + 4);
INFO_LOG(IOS_WC24, "IOCTL_NW24_GET_UNIVERSAL_TIME = %d, time = %" PRIu64, result, adjusted_utc);
}
break;
case IOCTL_NW24_SET_UNIVERSAL_TIME:
SetAdjustedUTC(Memory::Read_U64(request.buffer_in));
// update_misc = Memory::Read_U32(request.buffer_in + 8);
break;
{
const u64 adjusted_utc = Memory::Read_U64(request.buffer_in);
SetAdjustedUTC(adjusted_utc);
update_misc = Memory::Read_U32(request.buffer_in + 8);
INFO_LOG(IOS_WC24, "IOCTL_NW24_SET_UNIVERSAL_TIME (%" PRIu64 ", %u) = %d", adjusted_utc,
update_misc, result);
}
break;
case IOCTL_NW24_SET_RTC_COUNTER:
rtc = Memory::Read_U32(request.buffer_in);
// update_misc = Memory::Read_U32(request.buffer_in + 4);
update_misc = Memory::Read_U32(request.buffer_in + 4);
INFO_LOG(IOS_WC24, "IOCTL_NW24_SET_RTC_COUNTER (%" PRIu64 ", %u) = %d", rtc, update_misc,
result);
break;
case IOCTL_NW24_GET_TIME_DIFF:
Memory::Write_U64(GetAdjustedUTC() - rtc, request.buffer_out + 4);
break;
{
const u64 time_diff = GetAdjustedUTC() - rtc;
Memory::Write_U64(time_diff, request.buffer_out + 4);
INFO_LOG(IOS_WC24, "IOCTL_NW24_GET_TIME_DIFF = %d, time_diff = %" PRIu64, result, time_diff);
}
break;
case IOCTL_NW24_UNIMPLEMENTED:
result = -9;
INFO_LOG(IOS_WC24, "IOCTL_NW24_UNIMPLEMENTED = %d", result);
break;
default:
ERROR_LOG(IOS_NET, "%s - unknown IOCtl: %x", GetDeviceName().c_str(), request.request);
request.DumpUnknown(GetDeviceName(), LogTypes::IOS_WC24);
break;
}

View file

@ -27,15 +27,8 @@ WiiNetConfig::WiiNetConfig()
void WiiNetConfig::ReadConfig()
{
if (File::Exists(m_path))
{
if (!File::IOFile(m_path, "rb").ReadBytes(&m_data, sizeof(m_data)))
ResetConfig();
}
else
{
if (!File::IOFile(m_path, "rb").ReadBytes(&m_data, sizeof(m_data)))
ResetConfig();
}
}
void WiiNetConfig::WriteConfig() const

View file

@ -40,9 +40,9 @@ void BackUpBTInfoSection(const SysConf* sysconf)
void RestoreBTInfoSection(SysConf* sysconf)
{
const std::string filename = File::GetUserPath(D_SESSION_WIIROOT_IDX) + DIR_SEP WII_BTDINF_BACKUP;
if (!File::Exists(filename))
return;
File::IOFile backup(filename, "rb");
if (!backup)
return;
std::vector<u8> section(BT_INFO_SECTION_LENGTH);
if (!backup.ReadBytes(section.data(), section.size()))
{

View file

@ -48,17 +48,17 @@ BluetoothEmu::BluetoothEmu(Kernel& ios, const std::string& device_name)
BackUpBTInfoSection(&sysconf);
_conf_pads BT_DINF;
bdaddr_t tmpBD = BDADDR_ANY;
bdaddr_t tmpBD;
u8 i = 0;
while (i < MAX_BBMOTES)
{
// Previous records can be safely overwritten, since they are backed up
tmpBD.b[5] = BT_DINF.active[i].bdaddr[0] = BT_DINF.registered[i].bdaddr[0] = i;
tmpBD.b[4] = BT_DINF.active[i].bdaddr[1] = BT_DINF.registered[i].bdaddr[1] = 0;
tmpBD.b[3] = BT_DINF.active[i].bdaddr[2] = BT_DINF.registered[i].bdaddr[2] = 0x79;
tmpBD.b[2] = BT_DINF.active[i].bdaddr[3] = BT_DINF.registered[i].bdaddr[3] = 0x19;
tmpBD.b[1] = BT_DINF.active[i].bdaddr[4] = BT_DINF.registered[i].bdaddr[4] = 2;
tmpBD.b[0] = BT_DINF.active[i].bdaddr[5] = BT_DINF.registered[i].bdaddr[5] = 0x11;
tmpBD[5] = BT_DINF.active[i].bdaddr[0] = BT_DINF.registered[i].bdaddr[0] = i;
tmpBD[4] = BT_DINF.active[i].bdaddr[1] = BT_DINF.registered[i].bdaddr[1] = 0;
tmpBD[3] = BT_DINF.active[i].bdaddr[2] = BT_DINF.registered[i].bdaddr[2] = 0x79;
tmpBD[2] = BT_DINF.active[i].bdaddr[3] = BT_DINF.registered[i].bdaddr[3] = 0x19;
tmpBD[1] = BT_DINF.active[i].bdaddr[4] = BT_DINF.registered[i].bdaddr[4] = 2;
tmpBD[0] = BT_DINF.active[i].bdaddr[5] = BT_DINF.registered[i].bdaddr[5] = 0x11;
const char* wmName;
if (i == WIIMOTE_BALANCE_BOARD)
@ -68,8 +68,8 @@ BluetoothEmu::BluetoothEmu(Kernel& ios, const std::string& device_name)
memcpy(BT_DINF.registered[i].name, wmName, 20);
memcpy(BT_DINF.active[i].name, wmName, 20);
DEBUG_LOG(IOS_WIIMOTE, "Wii Remote %d BT ID %x,%x,%x,%x,%x,%x", i, tmpBD.b[0], tmpBD.b[1],
tmpBD.b[2], tmpBD.b[3], tmpBD.b[4], tmpBD.b[5]);
DEBUG_LOG(IOS_WIIMOTE, "Wii Remote %d BT ID %x,%x,%x,%x,%x,%x", i, tmpBD[0], tmpBD[1], tmpBD[2],
tmpBD[3], tmpBD[4], tmpBD[5]);
m_WiiMotes.emplace_back(this, i, tmpBD, g_wiimote_sources[i] != WIIMOTE_SRC_NONE);
i++;
}
@ -83,21 +83,10 @@ BluetoothEmu::BluetoothEmu(Kernel& ios, const std::string& device_name)
sysconf.GetOrAddEntry("BT.DINF", SysConf::Entry::Type::BigArray)->bytes = std::move(data);
if (!sysconf.Save())
PanicAlertT("Failed to write BT.DINF to SYSCONF");
// The BCM2045's btaddr:
m_ControllerBD.b[0] = 0x11;
m_ControllerBD.b[1] = 0x02;
m_ControllerBD.b[2] = 0x19;
m_ControllerBD.b[3] = 0x79;
m_ControllerBD.b[4] = 0x00;
m_ControllerBD.b[5] = 0xFF;
Host_SetWiiMoteConnectionState(0);
}
BluetoothEmu::~BluetoothEmu()
{
Host_SetWiiMoteConnectionState(0);
m_WiiMotes.clear();
}
@ -355,10 +344,7 @@ void BluetoothEmu::Update()
for (auto& wiimote : m_WiiMotes)
{
if (wiimote.EventPagingChanged(m_ScanEnable))
{
Host_SetWiiMoteConnectionState(1);
SendEventRequestConnection(wiimote);
}
}
}
@ -486,9 +472,9 @@ bool BluetoothEmu::SendEventInquiryResponse()
pResponse->clock_offset = 0x3818;
DEBUG_LOG(IOS_WIIMOTE, "Event: Send Fake Inquiry of one controller");
DEBUG_LOG(IOS_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", pResponse->bdaddr.b[0],
pResponse->bdaddr.b[1], pResponse->bdaddr.b[2], pResponse->bdaddr.b[3],
pResponse->bdaddr.b[4], pResponse->bdaddr.b[5]);
DEBUG_LOG(IOS_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", pResponse->bdaddr[0],
pResponse->bdaddr[1], pResponse->bdaddr[2], pResponse->bdaddr[3],
pResponse->bdaddr[4], pResponse->bdaddr[5]);
}
AddEventToQueue(Event);
@ -528,10 +514,10 @@ bool BluetoothEmu::SendEventConnectionComplete(const bdaddr_t& _bd)
DEBUG_LOG(IOS_WIIMOTE, "Event: SendEventConnectionComplete");
DEBUG_LOG(IOS_WIIMOTE, " Connection_Handle: 0x%04x", pConnectionComplete->Connection_Handle);
DEBUG_LOG(IOS_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", pConnectionComplete->bdaddr.b[0],
pConnectionComplete->bdaddr.b[1], pConnectionComplete->bdaddr.b[2],
pConnectionComplete->bdaddr.b[3], pConnectionComplete->bdaddr.b[4],
pConnectionComplete->bdaddr.b[5]);
DEBUG_LOG(IOS_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", pConnectionComplete->bdaddr[0],
pConnectionComplete->bdaddr[1], pConnectionComplete->bdaddr[2],
pConnectionComplete->bdaddr[3], pConnectionComplete->bdaddr[4],
pConnectionComplete->bdaddr[5]);
DEBUG_LOG(IOS_WIIMOTE, " LinkType: %s", s_szLinkType[pConnectionComplete->LinkType]);
DEBUG_LOG(IOS_WIIMOTE, " EncryptionEnabled: %i", pConnectionComplete->EncryptionEnabled);
@ -562,10 +548,10 @@ bool BluetoothEmu::SendEventRequestConnection(WiimoteDevice& _rWiiMote)
};
DEBUG_LOG(IOS_WIIMOTE, "Event: SendEventRequestConnection");
DEBUG_LOG(IOS_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x",
pEventRequestConnection->bdaddr.b[0], pEventRequestConnection->bdaddr.b[1],
pEventRequestConnection->bdaddr.b[2], pEventRequestConnection->bdaddr.b[3],
pEventRequestConnection->bdaddr.b[4], pEventRequestConnection->bdaddr.b[5]);
DEBUG_LOG(IOS_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", pEventRequestConnection->bdaddr[0],
pEventRequestConnection->bdaddr[1], pEventRequestConnection->bdaddr[2],
pEventRequestConnection->bdaddr[3], pEventRequestConnection->bdaddr[4],
pEventRequestConnection->bdaddr[5]);
DEBUG_LOG(IOS_WIIMOTE, " COD[0]: 0x%02x", pEventRequestConnection->uclass[0]);
DEBUG_LOG(IOS_WIIMOTE, " COD[1]: 0x%02x", pEventRequestConnection->uclass[1]);
DEBUG_LOG(IOS_WIIMOTE, " COD[2]: 0x%02x", pEventRequestConnection->uclass[2]);
@ -639,9 +625,9 @@ bool BluetoothEmu::SendEventRemoteNameReq(const bdaddr_t& _bd)
strcpy((char*)pRemoteNameReq->RemoteName, pWiiMote->GetName());
DEBUG_LOG(IOS_WIIMOTE, "Event: SendEventRemoteNameReq");
DEBUG_LOG(IOS_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", pRemoteNameReq->bdaddr.b[0],
pRemoteNameReq->bdaddr.b[1], pRemoteNameReq->bdaddr.b[2], pRemoteNameReq->bdaddr.b[3],
pRemoteNameReq->bdaddr.b[4], pRemoteNameReq->bdaddr.b[5]);
DEBUG_LOG(IOS_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", pRemoteNameReq->bdaddr[0],
pRemoteNameReq->bdaddr[1], pRemoteNameReq->bdaddr[2], pRemoteNameReq->bdaddr[3],
pRemoteNameReq->bdaddr[4], pRemoteNameReq->bdaddr[5]);
DEBUG_LOG(IOS_WIIMOTE, " RemoteName: %s", pRemoteNameReq->RemoteName);
AddEventToQueue(Event);
@ -774,9 +760,9 @@ bool BluetoothEmu::SendEventRoleChange(bdaddr_t _bd, bool _master)
AddEventToQueue(Event);
DEBUG_LOG(IOS_WIIMOTE, "Event: SendEventRoleChange");
DEBUG_LOG(IOS_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", pRoleChange->bdaddr.b[0],
pRoleChange->bdaddr.b[1], pRoleChange->bdaddr.b[2], pRoleChange->bdaddr.b[3],
pRoleChange->bdaddr.b[4], pRoleChange->bdaddr.b[5]);
DEBUG_LOG(IOS_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", pRoleChange->bdaddr[0],
pRoleChange->bdaddr[1], pRoleChange->bdaddr[2], pRoleChange->bdaddr[3],
pRoleChange->bdaddr[4], pRoleChange->bdaddr[5]);
DEBUG_LOG(IOS_WIIMOTE, " NewRole: %i", pRoleChange->NewRole);
return true;
@ -876,9 +862,9 @@ bool BluetoothEmu::SendEventLinkKeyNotification(const u8 num_to_send)
link_key_info->bdaddr = m_WiiMotes[i].GetBD();
memcpy(link_key_info->key, m_WiiMotes[i].GetLinkKey(), HCI_KEY_SIZE);
DEBUG_LOG(IOS_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", link_key_info->bdaddr.b[0],
link_key_info->bdaddr.b[1], link_key_info->bdaddr.b[2], link_key_info->bdaddr.b[3],
link_key_info->bdaddr.b[4], link_key_info->bdaddr.b[5]);
DEBUG_LOG(IOS_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", link_key_info->bdaddr[0],
link_key_info->bdaddr[1], link_key_info->bdaddr[2], link_key_info->bdaddr[3],
link_key_info->bdaddr[4], link_key_info->bdaddr[5]);
}
AddEventToQueue(Event);
@ -897,10 +883,10 @@ bool BluetoothEmu::SendEventRequestLinkKey(const bdaddr_t& _bd)
pEventRequestLinkKey->bdaddr = _bd;
DEBUG_LOG(IOS_WIIMOTE, "Event: SendEventRequestLinkKey");
DEBUG_LOG(IOS_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", pEventRequestLinkKey->bdaddr.b[0],
pEventRequestLinkKey->bdaddr.b[1], pEventRequestLinkKey->bdaddr.b[2],
pEventRequestLinkKey->bdaddr.b[3], pEventRequestLinkKey->bdaddr.b[4],
pEventRequestLinkKey->bdaddr.b[5]);
DEBUG_LOG(IOS_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", pEventRequestLinkKey->bdaddr[0],
pEventRequestLinkKey->bdaddr[1], pEventRequestLinkKey->bdaddr[2],
pEventRequestLinkKey->bdaddr[3], pEventRequestLinkKey->bdaddr[4],
pEventRequestLinkKey->bdaddr[5]);
AddEventToQueue(Event);
@ -1180,10 +1166,10 @@ void BluetoothEmu::CommandCreateCon(const u8* input)
INFO_LOG(IOS_WIIMOTE, "Command: HCI_CMD_CREATE_CON");
DEBUG_LOG(IOS_WIIMOTE, "Input:");
DEBUG_LOG(IOS_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", create_connection->bdaddr.b[0],
create_connection->bdaddr.b[1], create_connection->bdaddr.b[2],
create_connection->bdaddr.b[3], create_connection->bdaddr.b[4],
create_connection->bdaddr.b[5]);
DEBUG_LOG(IOS_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", create_connection->bdaddr[0],
create_connection->bdaddr[1], create_connection->bdaddr[2],
create_connection->bdaddr[3], create_connection->bdaddr[4],
create_connection->bdaddr[5]);
DEBUG_LOG(IOS_WIIMOTE, " pkt_type: %i", create_connection->pkt_type);
DEBUG_LOG(IOS_WIIMOTE, " page_scan_rep_mode: %i", create_connection->page_scan_rep_mode);
@ -1203,7 +1189,6 @@ void BluetoothEmu::CommandDisconnect(const u8* input)
DEBUG_LOG(IOS_WIIMOTE, " ConnectionHandle: 0x%04x", disconnect->con_handle);
DEBUG_LOG(IOS_WIIMOTE, " Reason: 0x%02x", disconnect->reason);
Host_SetWiiMoteConnectionState(0);
DisplayDisconnectMessage((disconnect->con_handle & 0xFF) + 1, disconnect->reason);
SendEventCommandStatus(HCI_CMD_DISCONNECT);
@ -1223,10 +1208,10 @@ void BluetoothEmu::CommandAcceptCon(const u8* input)
};
INFO_LOG(IOS_WIIMOTE, "Command: HCI_CMD_ACCEPT_CON");
DEBUG_LOG(IOS_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", accept_connection->bdaddr.b[0],
accept_connection->bdaddr.b[1], accept_connection->bdaddr.b[2],
accept_connection->bdaddr.b[3], accept_connection->bdaddr.b[4],
accept_connection->bdaddr.b[5]);
DEBUG_LOG(IOS_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", accept_connection->bdaddr[0],
accept_connection->bdaddr[1], accept_connection->bdaddr[2],
accept_connection->bdaddr[3], accept_connection->bdaddr[4],
accept_connection->bdaddr[5]);
DEBUG_LOG(IOS_WIIMOTE, " role: %s", roles[accept_connection->role]);
SendEventCommandStatus(HCI_CMD_ACCEPT_CON);
@ -1245,9 +1230,9 @@ void BluetoothEmu::CommandLinkKeyRep(const u8* input)
const hci_link_key_rep_cp* key_rep = reinterpret_cast<const hci_link_key_rep_cp*>(input);
INFO_LOG(IOS_WIIMOTE, "Command: HCI_CMD_LINK_KEY_REP");
DEBUG_LOG(IOS_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", key_rep->bdaddr.b[0],
key_rep->bdaddr.b[1], key_rep->bdaddr.b[2], key_rep->bdaddr.b[3], key_rep->bdaddr.b[4],
key_rep->bdaddr.b[5]);
DEBUG_LOG(IOS_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", key_rep->bdaddr[0],
key_rep->bdaddr[1], key_rep->bdaddr[2], key_rep->bdaddr[3], key_rep->bdaddr[4],
key_rep->bdaddr[5]);
hci_link_key_rep_rp reply;
reply.status = 0x00;
@ -1261,9 +1246,9 @@ void BluetoothEmu::CommandLinkKeyNegRep(const u8* input)
const hci_link_key_neg_rep_cp* key_neg = reinterpret_cast<const hci_link_key_neg_rep_cp*>(input);
INFO_LOG(IOS_WIIMOTE, "Command: HCI_CMD_LINK_KEY_NEG_REP");
DEBUG_LOG(IOS_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", key_neg->bdaddr.b[0],
key_neg->bdaddr.b[1], key_neg->bdaddr.b[2], key_neg->bdaddr.b[3], key_neg->bdaddr.b[4],
key_neg->bdaddr.b[5]);
DEBUG_LOG(IOS_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", key_neg->bdaddr[0],
key_neg->bdaddr[1], key_neg->bdaddr[2], key_neg->bdaddr[3], key_neg->bdaddr[4],
key_neg->bdaddr[5]);
hci_link_key_neg_rep_rp reply;
reply.status = 0x00;
@ -1305,10 +1290,9 @@ void BluetoothEmu::CommandRemoteNameReq(const u8* input)
reinterpret_cast<const hci_remote_name_req_cp*>(input);
INFO_LOG(IOS_WIIMOTE, "Command: HCI_CMD_REMOTE_NAME_REQ");
DEBUG_LOG(IOS_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", remote_name_req->bdaddr.b[0],
remote_name_req->bdaddr.b[1], remote_name_req->bdaddr.b[2],
remote_name_req->bdaddr.b[3], remote_name_req->bdaddr.b[4],
remote_name_req->bdaddr.b[5]);
DEBUG_LOG(IOS_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", remote_name_req->bdaddr[0],
remote_name_req->bdaddr[1], remote_name_req->bdaddr[2], remote_name_req->bdaddr[3],
remote_name_req->bdaddr[4], remote_name_req->bdaddr[5]);
DEBUG_LOG(IOS_WIIMOTE, " page_scan_rep_mode: %i", remote_name_req->page_scan_rep_mode);
DEBUG_LOG(IOS_WIIMOTE, " page_scan_mode: %i", remote_name_req->page_scan_mode);
DEBUG_LOG(IOS_WIIMOTE, " clock_offset: %i", remote_name_req->clock_offset);
@ -1440,10 +1424,10 @@ void BluetoothEmu::CommandReadStoredLinkKey(const u8* input)
INFO_LOG(IOS_WIIMOTE, "Command: HCI_CMD_READ_STORED_LINK_KEY:");
DEBUG_LOG(IOS_WIIMOTE, "input:");
DEBUG_LOG(IOS_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", read_stored_link_key->bdaddr.b[0],
read_stored_link_key->bdaddr.b[1], read_stored_link_key->bdaddr.b[2],
read_stored_link_key->bdaddr.b[3], read_stored_link_key->bdaddr.b[4],
read_stored_link_key->bdaddr.b[5]);
DEBUG_LOG(IOS_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", read_stored_link_key->bdaddr[0],
read_stored_link_key->bdaddr[1], read_stored_link_key->bdaddr[2],
read_stored_link_key->bdaddr[3], read_stored_link_key->bdaddr[4],
read_stored_link_key->bdaddr[5]);
DEBUG_LOG(IOS_WIIMOTE, " read_all: %i", read_stored_link_key->read_all);
DEBUG_LOG(IOS_WIIMOTE, "return:");
DEBUG_LOG(IOS_WIIMOTE, " max_num_keys: %i", reply.max_num_keys);
@ -1460,10 +1444,10 @@ void BluetoothEmu::CommandDeleteStoredLinkKey(const u8* input)
reinterpret_cast<const hci_delete_stored_link_key_cp*>(input);
INFO_LOG(IOS_WIIMOTE, "Command: HCI_OCF_DELETE_STORED_LINK_KEY");
DEBUG_LOG(IOS_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", delete_stored_link_key->bdaddr.b[0],
delete_stored_link_key->bdaddr.b[1], delete_stored_link_key->bdaddr.b[2],
delete_stored_link_key->bdaddr.b[3], delete_stored_link_key->bdaddr.b[4],
delete_stored_link_key->bdaddr.b[5]);
DEBUG_LOG(IOS_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", delete_stored_link_key->bdaddr[0],
delete_stored_link_key->bdaddr[1], delete_stored_link_key->bdaddr[2],
delete_stored_link_key->bdaddr[3], delete_stored_link_key->bdaddr[4],
delete_stored_link_key->bdaddr[5]);
DEBUG_LOG(IOS_WIIMOTE, " delete_all: 0x%01x", delete_stored_link_key->delete_all);
WiimoteDevice* wiiMote = AccessWiiMote(delete_stored_link_key->bdaddr);
@ -1710,9 +1694,8 @@ void BluetoothEmu::CommandReadBDAdrr(const u8* input)
INFO_LOG(IOS_WIIMOTE, "Command: HCI_CMD_READ_BDADDR:");
DEBUG_LOG(IOS_WIIMOTE, "return:");
DEBUG_LOG(IOS_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", reply.bdaddr.b[0],
reply.bdaddr.b[1], reply.bdaddr.b[2], reply.bdaddr.b[3], reply.bdaddr.b[4],
reply.bdaddr.b[5]);
DEBUG_LOG(IOS_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", reply.bdaddr[0], reply.bdaddr[1],
reply.bdaddr[2], reply.bdaddr[3], reply.bdaddr[4], reply.bdaddr[5]);
SendEventCommandComplete(HCI_CMD_READ_BDADDR, &reply, sizeof(hci_read_bdaddr_rp));
}
@ -1754,19 +1737,12 @@ void BluetoothEmu::CommandVendorSpecific_FC4C(const u8* input, u32 size)
// --- helper
//
//
WiimoteDevice* BluetoothEmu::AccessWiiMote(const bdaddr_t& _rAddr)
WiimoteDevice* BluetoothEmu::AccessWiiMote(const bdaddr_t& address)
{
for (auto& wiimote : m_WiiMotes)
{
const bdaddr_t& BD = wiimote.GetBD();
if ((_rAddr.b[0] == BD.b[0]) && (_rAddr.b[1] == BD.b[1]) && (_rAddr.b[2] == BD.b[2]) &&
(_rAddr.b[3] == BD.b[3]) && (_rAddr.b[4] == BD.b[4]) && (_rAddr.b[5] == BD.b[5]))
return &wiimote;
}
ERROR_LOG(IOS_WIIMOTE, "Can't find WiiMote by bd: %02x:%02x:%02x:%02x:%02x:%02x", _rAddr.b[0],
_rAddr.b[1], _rAddr.b[2], _rAddr.b[3], _rAddr.b[4], _rAddr.b[5]);
return nullptr;
const auto iterator =
std::find_if(m_WiiMotes.begin(), m_WiiMotes.end(),
[&address](const WiimoteDevice& remote) { return remote.GetBD() == address; });
return iterator != m_WiiMotes.cend() ? &*iterator : nullptr;
}
WiimoteDevice* BluetoothEmu::AccessWiiMote(u16 _ConnectionHandle)

View file

@ -66,7 +66,7 @@ public:
void DoState(PointerWrap& p) override;
private:
bdaddr_t m_ControllerBD;
bdaddr_t m_ControllerBD{{0x11, 0x02, 0x19, 0x79, 0x00, 0xff}};
// this is used to trigger connecting via ACL
u8 m_ScanEnable = 0;

View file

@ -29,7 +29,6 @@
#include "Core/Core.h"
#include "Core/HW/Memmap.h"
#include "Core/IOS/Device.h"
#include "Core/IOS/USB/Bluetooth/hci.h"
#include "VideoCommon/OnScreenDisplay.h"
namespace IOS
@ -201,15 +200,9 @@ IPCCommandResult BluetoothReal::IOCtlV(const IOCtlVRequest& request)
hci_delete_stored_link_key_cp delete_cmd;
Memory::CopyFromEmu(&delete_cmd, cmd->data_address, sizeof(delete_cmd));
if (delete_cmd.delete_all)
{
m_link_keys.clear();
}
else
{
btaddr_t addr;
std::copy(std::begin(delete_cmd.bdaddr.b), std::end(delete_cmd.bdaddr.b), addr.begin());
m_link_keys.erase(addr);
}
m_link_keys.erase(delete_cmd.bdaddr);
}
auto buffer = std::make_unique<u8[]>(cmd->length + LIBUSB_CONTROL_SETUP_SIZE);
libusb_fill_control_setup(buffer.get(), cmd->request_type, cmd->request, cmd->value, cmd->index,
@ -416,7 +409,7 @@ bool BluetoothReal::SendHCIStoreLinkKeyCommand()
// The HCI command field is limited to uint8_t, and libusb to uint16_t.
const u8 payload_size =
static_cast<u8>(sizeof(hci_write_stored_link_key_cp)) +
(sizeof(btaddr_t) + sizeof(linkkey_t)) * static_cast<u8>(m_link_keys.size());
(sizeof(bdaddr_t) + sizeof(linkkey_t)) * static_cast<u8>(m_link_keys.size());
std::vector<u8> packet(sizeof(hci_cmd_hdr_t) + payload_size);
auto* header = reinterpret_cast<hci_cmd_hdr_t*>(packet.data());
@ -526,7 +519,7 @@ void BluetoothReal::LoadLinkKeys()
if (index == std::string::npos)
continue;
btaddr_t address;
bdaddr_t address;
Common::StringToMacAddress(pair.substr(0, index), address.data());
std::reverse(address.begin(), address.end());
@ -549,7 +542,7 @@ void BluetoothReal::SaveLinkKeys()
std::ostringstream oss;
for (const auto& entry : m_link_keys)
{
btaddr_t address;
bdaddr_t address;
// Reverse the address so that it is stored in the correct order in the config file
std::reverse_copy(entry.first.begin(), entry.first.end(), address.begin());
oss << Common::MacAddressToString(address.data());
@ -674,11 +667,9 @@ void BluetoothReal::HandleBulkOrIntrTransfer(libusb_transfer* tr)
const auto* notification =
reinterpret_cast<hci_link_key_notification_ep*>(tr->buffer + sizeof(hci_event_hdr_t));
btaddr_t addr;
std::copy(std::begin(notification->bdaddr.b), std::end(notification->bdaddr.b), addr.begin());
linkkey_t key;
std::copy(std::begin(notification->key), std::end(notification->key), std::begin(key));
m_link_keys[addr] = key;
m_link_keys[notification->bdaddr] = key;
}
else if (event->event == HCI_EVENT_COMMAND_COMPL &&
reinterpret_cast<hci_command_compl_ep*>(tr->buffer + sizeof(*event))->opcode ==

View file

@ -18,6 +18,7 @@
#include "Common/Timer.h"
#include "Core/IOS/IOS.h"
#include "Core/IOS/USB/Bluetooth/BTBase.h"
#include "Core/IOS/USB/Bluetooth/hci.h"
#include "Core/IOS/USB/USBV0.h"
class PointerWrap;
@ -40,7 +41,6 @@ enum class SyncButtonState
Ignored,
};
using btaddr_t = std::array<u8, 6>;
using linkkey_t = std::array<u8, 16>;
namespace Device
@ -101,7 +101,7 @@ private:
// This stores the address of paired devices and associated link keys.
// It is needed because some adapters forget all stored link keys when they are reset,
// which breaks pairings because the Wii relies on the Bluetooth module to remember them.
std::map<btaddr_t, linkkey_t> m_link_keys;
std::map<bdaddr_t, linkkey_t> m_link_keys;
Common::Flag m_need_reset_keys;
// This flag is set when a libusb transfer failed (for reasons other than timing out)

View file

@ -37,16 +37,9 @@ WiimoteDevice::WiimoteDevice(Device::BluetoothEmu* host, int number, bdaddr_t bd
m_ConnectionHandle = 0x100 + number;
memset(m_LinkKey, 0xA0 + number, HCI_KEY_SIZE);
bdaddr_t _nullBD = BDADDR_ANY;
if (memcmp(&m_BD, &_nullBD, sizeof(bdaddr_t)) == 0)
{
m_BD.b[0] = 0x11;
m_BD.b[1] = 0x02;
m_BD.b[2] = 0x19;
m_BD.b[3] = 0x79;
m_BD.b[4] = 0x00;
m_BD.b[5] = number;
}
if (m_BD == BDADDR_ANY)
m_BD = {{0x11, 0x02, 0x19, 0x79, static_cast<u8>(number)}};
uclass[0] = 0x00;
uclass[1] = 0x04;
uclass[2] = 0x48;
@ -910,9 +903,6 @@ void WiimoteDevice::ReceiveL2capData(u16 scid, const void* _pData, u32 _Size)
// Update Offset to the final size of the report
Offset += _Size;
// Update the status bar
Host_SetWiiMoteConnectionState(2);
// Send the report
m_pHost->SendACLPacket(GetConnectionHandle(), DataFrame, Offset);
}

View file

@ -83,7 +83,10 @@
#pragma once
#include <stdint.h>
#include <array>
#include <cstdint>
#include "Common/CommonTypes.h"
// All structs in this file are packed
#pragma pack(push, 1)
@ -94,18 +97,8 @@
/*
* Bluetooth device address
*/
#ifndef __BLUETOOTH_H
typedef struct
{
uint8_t b[BLUETOOTH_BDADDR_SIZE];
} bdaddr_t;
#define BDADDR_ANY \
{ \
{ \
0, 0, 0, 0, 0, 0 \
} \
}
#endif
using bdaddr_t = std::array<u8, BLUETOOTH_BDADDR_SIZE>;
constexpr bdaddr_t BDADDR_ANY{};
/**************************************************************************
**************************************************************************

View file

@ -66,9 +66,8 @@ static PlayMode s_playMode = MODE_NONE;
static u8 s_controllers = 0;
static ControllerState s_padState;
static DTMHeader tmpHeader;
static u8* tmpInput = nullptr;
static size_t tmpInputAllocated = 0;
static u64 s_currentByte = 0, s_totalBytes = 0;
static std::vector<u8> s_temp_input;
static u64 s_currentByte = 0;
static u64 s_currentFrame = 0, s_totalFrames = 0; // VI
static u64 s_currentLagCount = 0;
static u64 s_totalLagCount = 0; // just stats
@ -105,28 +104,6 @@ static WiiManipFunction s_wii_manip_func;
static std::string s_current_file_name;
// NOTE: Host / CPU Thread
static void EnsureTmpInputSize(size_t bound)
{
if (tmpInputAllocated >= bound)
return;
// The buffer expands in powers of two of DTM_BASE_LENGTH
// (standard exponential buffer growth).
size_t newAlloc = DTM_BASE_LENGTH;
while (newAlloc < bound)
newAlloc *= 2;
u8* newTmpInput = new u8[newAlloc];
tmpInputAllocated = newAlloc;
if (tmpInput != nullptr)
{
if (s_totalBytes > 0)
memcpy(newTmpInput, tmpInput, (size_t)s_totalBytes);
delete[] tmpInput;
}
tmpInput = newTmpInput;
}
static bool IsMovieHeader(u8 magic[4])
{
return magic[0] == 'D' && magic[1] == 'T' && magic[2] == 'M' && magic[3] == 0x1A;
@ -567,68 +544,66 @@ bool BeginRecordingInput(int controllers)
if (s_playMode != MODE_NONE || controllers == 0)
return false;
bool was_unpaused = Core::PauseAndLock(true);
Core::RunAsCPUThread([controllers] {
s_controllers = controllers;
s_currentFrame = s_totalFrames = 0;
s_currentLagCount = s_totalLagCount = 0;
s_currentInputCount = s_totalInputCount = 0;
s_totalTickCount = s_tickCountAtLastInput = 0;
s_bongos = 0;
s_memcards = 0;
if (NetPlay::IsNetPlayRunning())
{
s_bNetPlay = true;
s_recordingStartTime = ExpansionInterface::CEXIIPL::NetPlay_GetEmulatedTime();
}
else if (SConfig::GetInstance().bEnableCustomRTC)
{
s_recordingStartTime = SConfig::GetInstance().m_customRTCValue;
}
else
{
s_recordingStartTime = Common::Timer::GetLocalTimeSinceJan1970();
}
s_controllers = controllers;
s_currentFrame = s_totalFrames = 0;
s_currentLagCount = s_totalLagCount = 0;
s_currentInputCount = s_totalInputCount = 0;
s_totalTickCount = s_tickCountAtLastInput = 0;
s_bongos = 0;
s_memcards = 0;
if (NetPlay::IsNetPlayRunning())
{
s_bNetPlay = true;
s_recordingStartTime = ExpansionInterface::CEXIIPL::NetPlay_GetEmulatedTime();
}
else if (SConfig::GetInstance().bEnableCustomRTC)
{
s_recordingStartTime = SConfig::GetInstance().m_customRTCValue;
}
else
{
s_recordingStartTime = Common::Timer::GetLocalTimeSinceJan1970();
}
s_rerecords = 0;
s_rerecords = 0;
for (int i = 0; i < SerialInterface::MAX_SI_CHANNELS; ++i)
{
if (SConfig::GetInstance().m_SIDevice[i] == SerialInterface::SIDEVICE_GC_TARUKONGA)
s_bongos |= (1 << i);
}
for (int i = 0; i < SerialInterface::MAX_SI_CHANNELS; ++i)
{
if (SConfig::GetInstance().m_SIDevice[i] == SerialInterface::SIDEVICE_GC_TARUKONGA)
s_bongos |= (1 << i);
}
if (Core::IsRunningAndStarted())
{
const std::string save_path = File::GetUserPath(D_STATESAVES_IDX) + "dtm.sav";
if (File::Exists(save_path))
File::Delete(save_path);
if (Core::IsRunningAndStarted())
{
const std::string save_path = File::GetUserPath(D_STATESAVES_IDX) + "dtm.sav";
if (File::Exists(save_path))
File::Delete(save_path);
State::SaveAs(save_path);
s_bRecordingFromSaveState = true;
State::SaveAs(save_path);
s_bRecordingFromSaveState = true;
std::thread md5thread(GetMD5);
md5thread.detach();
GetSettings();
}
std::thread md5thread(GetMD5);
md5thread.detach();
GetSettings();
}
// Wiimotes cause desync issues if they're not reset before launching the game
if (!Core::IsRunningAndStarted())
{
// This will also reset the wiimotes for gamecube games, but that shouldn't do anything
Wiimote::ResetAllWiimotes();
}
// Wiimotes cause desync issues if they're not reset before launching the game
if (!Core::IsRunningAndStarted())
{
// This will also reset the wiimotes for gamecube games, but that shouldn't do anything
Wiimote::ResetAllWiimotes();
}
s_playMode = MODE_RECORDING;
s_author = SConfig::GetInstance().m_strMovieAuthor;
s_temp_input.clear();
s_playMode = MODE_RECORDING;
s_author = SConfig::GetInstance().m_strMovieAuthor;
EnsureTmpInputSize(1);
s_currentByte = 0;
s_currentByte = s_totalBytes = 0;
if (Core::IsRunning())
Core::UpdateWantDeterminism();
Core::PauseAndLock(false, was_unpaused);
if (Core::IsRunning())
Core::UpdateWantDeterminism();
});
Core::DisplayMessage("Starting movie recording", 2000);
return true;
@ -887,10 +862,9 @@ void RecordInput(GCPadStatus* PadStatus, int controllerID)
CheckPadStatus(PadStatus, controllerID);
EnsureTmpInputSize((size_t)(s_currentByte + sizeof(ControllerState)));
memcpy(&tmpInput[s_currentByte], &s_padState, sizeof(ControllerState));
s_temp_input.resize(s_currentByte + sizeof(ControllerState));
memcpy(&s_temp_input[s_currentByte], &s_padState, sizeof(ControllerState));
s_currentByte += sizeof(ControllerState);
s_totalBytes = s_currentByte;
}
// NOTE: CPU Thread
@ -909,11 +883,10 @@ void RecordWiimote(int wiimote, u8* data, u8 size)
return;
InputUpdate();
EnsureTmpInputSize((size_t)(s_currentByte + size + 1));
tmpInput[s_currentByte++] = size;
memcpy(&(tmpInput[s_currentByte]), data, size);
s_temp_input.resize(s_currentByte + size + 1);
s_temp_input[s_currentByte++] = size;
memcpy(&s_temp_input[s_currentByte], data, size);
s_currentByte += size;
s_totalBytes = s_currentByte;
}
// NOTE: EmuThread / Host Thread
@ -960,20 +933,13 @@ bool PlayInput(const std::string& filename)
if (s_playMode != MODE_NONE)
return false;
if (!File::Exists(filename))
File::IOFile recording_file(filename, "rb");
if (!recording_file.ReadArray(&tmpHeader, 1))
return false;
File::IOFile g_recordfd;
if (!g_recordfd.Open(filename, "rb"))
return false;
g_recordfd.ReadArray(&tmpHeader, 1);
if (!IsMovieHeader(tmpHeader.filetype))
{
PanicAlertT("Invalid recording file");
g_recordfd.Close();
return false;
}
@ -993,11 +959,10 @@ bool PlayInput(const std::string& filename)
Core::UpdateWantDeterminism();
s_totalBytes = g_recordfd.GetSize() - 256;
EnsureTmpInputSize((size_t)s_totalBytes);
g_recordfd.ReadArray(tmpInput, (size_t)s_totalBytes);
s_temp_input.resize(recording_file.GetSize() - 256);
recording_file.ReadBytes(s_temp_input.data(), s_temp_input.size());
s_currentByte = 0;
g_recordfd.Close();
recording_file.Close();
// Load savestate (and skip to frame data)
if (tmpHeader.bFromSaveState)
@ -1070,38 +1035,37 @@ void LoadInput(const std::string& filename)
afterEnd = true;
}
if (!s_bReadOnly || tmpInput == nullptr)
if (!s_bReadOnly || s_temp_input.empty())
{
s_totalFrames = tmpHeader.frameCount;
s_totalLagCount = tmpHeader.lagCount;
s_totalInputCount = tmpHeader.inputCount;
s_totalTickCount = s_tickCountAtLastInput = tmpHeader.tickCount;
EnsureTmpInputSize((size_t)totalSavedBytes);
s_totalBytes = totalSavedBytes;
t_record.ReadArray(tmpInput, (size_t)s_totalBytes);
s_temp_input.resize(static_cast<size_t>(totalSavedBytes));
t_record.ReadBytes(s_temp_input.data(), s_temp_input.size());
}
else if (s_currentByte > 0)
{
if (s_currentByte > totalSavedBytes)
{
}
else if (s_currentByte > s_totalBytes)
else if (s_currentByte > s_temp_input.size())
{
afterEnd = true;
PanicAlertT("Warning: You loaded a save that's after the end of the current movie. (byte %u "
"> %u) (input %u > %u). You should load another save before continuing, or load "
"> %zu) (input %u > %u). You should load another save before continuing, or load "
"this state with read-only mode off.",
(u32)s_currentByte + 256, (u32)s_totalBytes + 256, (u32)s_currentInputCount,
(u32)s_currentByte + 256, s_temp_input.size() + 256, (u32)s_currentInputCount,
(u32)s_totalInputCount);
}
else if (s_currentByte > 0 && s_totalBytes > 0)
else if (s_currentByte > 0 && !s_temp_input.empty())
{
// verify identical from movie start to the save's current frame
std::vector<u8> movInput(s_currentByte);
t_record.ReadArray(movInput.data(), movInput.size());
const auto result = std::mismatch(movInput.begin(), movInput.end(), tmpInput);
const auto result = std::mismatch(movInput.begin(), movInput.end(), s_temp_input.begin());
if (result.first != movInput.end())
{
@ -1120,15 +1084,17 @@ void LoadInput(const std::string& filename)
"read-only mode off. Otherwise you'll probably get a desync.",
byte_offset, byte_offset);
std::copy(movInput.begin(), movInput.end(), tmpInput);
std::copy(movInput.begin(), movInput.end(), s_temp_input.begin());
}
else
{
const ptrdiff_t frame = mismatch_index / sizeof(ControllerState);
ControllerState curPadState;
memcpy(&curPadState, &tmpInput[frame * sizeof(ControllerState)], sizeof(ControllerState));
memcpy(&curPadState, &s_temp_input[frame * sizeof(ControllerState)],
sizeof(ControllerState));
ControllerState movPadState;
memcpy(&movPadState, &movInput[frame * sizeof(ControllerState)], sizeof(ControllerState));
memcpy(&movPadState, &s_temp_input[frame * sizeof(ControllerState)],
sizeof(ControllerState));
PanicAlertT(
"Warning: You loaded a save whose movie mismatches on frame %td. You should load "
"another save before continuing, or load this state with read-only mode off. "
@ -1193,7 +1159,7 @@ void LoadInput(const std::string& filename)
// NOTE: CPU Thread
static void CheckInputEnd()
{
if (s_currentByte >= s_totalBytes ||
if (s_currentByte >= s_temp_input.size() ||
(CoreTiming::GetTicks() > s_totalTickCount && !IsRecordingInputFromSaveState()))
{
EndPlayInput(!s_bReadOnly);
@ -1205,13 +1171,13 @@ void PlayController(GCPadStatus* PadStatus, int controllerID)
{
// Correct playback is entirely dependent on the emulator polling the controllers
// in the same order done during recording
if (!IsPlayingInput() || !IsUsingPad(controllerID) || tmpInput == nullptr)
if (!IsPlayingInput() || !IsUsingPad(controllerID) || s_temp_input.empty())
return;
if (s_currentByte + sizeof(ControllerState) > s_totalBytes)
if (s_currentByte + sizeof(ControllerState) > s_temp_input.size())
{
PanicAlertT("Premature movie end in PlayController. %u + %zu > %u", (u32)s_currentByte,
sizeof(ControllerState), (u32)s_totalBytes);
PanicAlertT("Premature movie end in PlayController. %u + %zu > %zu", (u32)s_currentByte,
sizeof(ControllerState), s_temp_input.size());
EndPlayInput(!s_bReadOnly);
return;
}
@ -1222,7 +1188,7 @@ void PlayController(GCPadStatus* PadStatus, int controllerID)
memset(PadStatus, 0, sizeof(GCPadStatus));
PadStatus->err = e;
memcpy(&s_padState, &tmpInput[s_currentByte], sizeof(ControllerState));
memcpy(&s_padState, &s_temp_input[s_currentByte], sizeof(ControllerState));
s_currentByte += sizeof(ControllerState);
PadStatus->triggerLeft = s_padState.TriggerL;
@ -1306,20 +1272,20 @@ void PlayController(GCPadStatus* PadStatus, int controllerID)
bool PlayWiimote(int wiimote, u8* data, const WiimoteEmu::ReportFeatures& rptf, int ext,
const wiimote_key key)
{
if (!IsPlayingInput() || !IsUsingWiimote(wiimote) || tmpInput == nullptr)
if (!IsPlayingInput() || !IsUsingWiimote(wiimote) || s_temp_input.empty())
return false;
if (s_currentByte > s_totalBytes)
if (s_currentByte > s_temp_input.size())
{
PanicAlertT("Premature movie end in PlayWiimote. %u > %u", (u32)s_currentByte,
(u32)s_totalBytes);
PanicAlertT("Premature movie end in PlayWiimote. %u > %zu", (u32)s_currentByte,
s_temp_input.size());
EndPlayInput(!s_bReadOnly);
return false;
}
u8 size = rptf.size;
u8 sizeInMovie = tmpInput[s_currentByte];
u8 sizeInMovie = s_temp_input[s_currentByte];
if (size != sizeInMovie)
{
@ -1335,15 +1301,15 @@ bool PlayWiimote(int wiimote, u8* data, const WiimoteEmu::ReportFeatures& rptf,
s_currentByte++;
if (s_currentByte + size > s_totalBytes)
if (s_currentByte + size > s_temp_input.size())
{
PanicAlertT("Premature movie end in PlayWiimote. %u + %d > %u", (u32)s_currentByte, size,
(u32)s_totalBytes);
PanicAlertT("Premature movie end in PlayWiimote. %u + %d > %zu", (u32)s_currentByte, size,
s_temp_input.size());
EndPlayInput(!s_bReadOnly);
return false;
}
memcpy(data, &(tmpInput[s_currentByte]), size);
memcpy(data, &s_temp_input[s_currentByte], size);
s_currentByte += size;
s_currentInputCount++;
@ -1447,7 +1413,7 @@ void SaveRecording(const std::string& filename)
save_record.WriteArray(&header, 1);
bool success = save_record.WriteArray(tmpInput, (size_t)s_totalBytes);
bool success = save_record.WriteBytes(s_temp_input.data(), s_temp_input.size());
if (success && s_bRecordingFromSaveState)
{
@ -1607,10 +1573,7 @@ static void GetMD5()
// NOTE: EmuThread
void Shutdown()
{
s_currentInputCount = s_totalInputCount = s_totalFrames = s_totalBytes = s_tickCountAtLastInput =
0;
delete[] tmpInput;
tmpInput = nullptr;
tmpInputAllocated = 0;
s_currentInputCount = s_totalInputCount = s_totalFrames = s_tickCountAtLastInput = 0;
s_temp_input.clear();
}
};

View file

@ -20,7 +20,7 @@
bool BreakPoints::IsAddressBreakPoint(u32 address) const
{
return std::any_of(m_breakpoints.begin(), m_breakpoints.end(),
[address](const auto& bp) { return bp.address == address; });
[address](const auto& bp) { return bp.address == address; });
}
bool BreakPoints::IsTempBreakPoint(u32 address) const
@ -139,9 +139,9 @@ MemChecks::TMemChecksStr MemChecks::GetStrings() const
std::stringstream ss;
ss << std::hex << mc.start_address;
ss << " " << (mc.is_ranged ? mc.end_address : mc.start_address) << " "
<< (mc.is_ranged ? "n" : "") << (mc.is_break_on_read ? "r" : "")
<< (mc.is_break_on_write ? "w" : "") << (mc.log_on_hit ? "l" : "")
<< (mc.break_on_hit ? "p" : "");
<< (mc.is_ranged ? "n" : "") << (mc.is_break_on_read ? "r" : "")
<< (mc.is_break_on_write ? "w" : "") << (mc.log_on_hit ? "l" : "")
<< (mc.break_on_hit ? "p" : "");
mc_strings.push_back(ss.str());
}
@ -174,14 +174,14 @@ void MemChecks::Add(const TMemCheck& memory_check)
if (GetMemCheck(memory_check.start_address) == nullptr)
{
bool had_any = HasAny();
bool lock = Core::PauseAndLock(true);
m_mem_checks.push_back(memory_check);
// If this is the first one, clear the JIT cache so it can switch to
// watchpoint-compatible code.
if (!had_any && g_jit)
g_jit->ClearCache();
PowerPC::DBATUpdated();
Core::PauseAndLock(false, lock);
Core::RunAsCPUThread([&] {
m_mem_checks.push_back(memory_check);
// If this is the first one, clear the JIT cache so it can switch to
// watchpoint-compatible code.
if (!had_any && g_jit)
g_jit->ClearCache();
PowerPC::DBATUpdated();
});
}
}
@ -191,12 +191,12 @@ void MemChecks::Remove(u32 address)
{
if (i->start_address == address)
{
bool lock = Core::PauseAndLock(true);
m_mem_checks.erase(i);
if (!HasAny() && g_jit)
g_jit->ClearCache();
PowerPC::DBATUpdated();
Core::PauseAndLock(false, lock);
Core::RunAsCPUThread([&] {
m_mem_checks.erase(i);
if (!HasAny() && g_jit)
g_jit->ClearCache();
PowerPC::DBATUpdated();
});
return;
}
}
@ -225,9 +225,9 @@ bool MemChecks::OverlapsMemcheck(u32 address, u32 length)
for (TMemCheck memcheck : m_mem_checks)
{
if (((memcheck.start_address | page_end_suffix) == page_end_address ||
(memcheck.end_address | page_end_suffix) == page_end_address) ||
((memcheck.start_address | page_end_suffix) < page_end_address &&
(memcheck.end_address | page_end_suffix) > page_end_address))
(memcheck.end_address | page_end_suffix) == page_end_address) ||
((memcheck.start_address | page_end_suffix) < page_end_address &&
(memcheck.end_address | page_end_suffix) > page_end_address))
{
return true;
}
@ -236,16 +236,16 @@ bool MemChecks::OverlapsMemcheck(u32 address, u32 length)
}
bool TMemCheck::Action(DebugInterface* debug_interface, u32 value, u32 addr, bool write,
size_t size, u32 pc)
size_t size, u32 pc)
{
if ((write && is_break_on_write) || (!write && is_break_on_read))
{
if (log_on_hit)
{
NOTICE_LOG(MEMMAP, "MBP %08x (%s) %s%zu %0*x at %08x (%s)", pc,
debug_interface->GetDescription(pc).c_str(), write ? "Write" : "Read", size * 8,
static_cast<int>(size * 2), value, addr,
debug_interface->GetDescription(addr).c_str());
debug_interface->GetDescription(pc).c_str(), write ? "Write" : "Read", size * 8,
static_cast<int>(size * 2), value, addr,
debug_interface->GetDescription(addr).c_str());
}
if (break_on_hit)
return true;
@ -256,7 +256,7 @@ bool TMemCheck::Action(DebugInterface* debug_interface, u32 value, u32 addr, boo
bool Watches::IsAddressWatch(u32 address) const
{
return std::any_of(m_watches.begin(), m_watches.end(),
[address](const auto& watch) { return watch.address == address; });
[address](const auto& watch) { return watch.address == address; });
}
Watches::TWatchesStr Watches::GetStrings() const

View file

@ -73,23 +73,23 @@ static Common::Event g_compressAndDumpStateSyncEvent;
static std::thread g_save_thread;
// Don't forget to increase this after doing changes on the savestate system
static const u32 STATE_VERSION = 87; // Last changed in PR 5707
static const u32 STATE_VERSION = 88; // Last changed in PR 5733
// Maps savestate versions to Dolphin versions.
// Versions after 42 don't need to be added to this list,
// because they save the exact Dolphin version to savestates.
// Maps savestate versions to Dolphin versions.
// Versions after 42 don't need to be added to this list,
// because they save the exact Dolphin version to savestates.
static const std::map<u32, std::pair<std::string, std::string>> s_old_versions = {
// The 16 -> 17 change modified the size of StateHeader,
// so versions older than that can't even be decompressed anymore
{17, {"3.5-1311", "3.5-1364"}}, {18, {"3.5-1366", "3.5-1371"}}, {19, {"3.5-1372", "3.5-1408"}},
{20, {"3.5-1409", "4.0-704"}}, {21, {"4.0-705", "4.0-889"}}, {22, {"4.0-905", "4.0-1871"}},
{23, {"4.0-1873", "4.0-1900"}}, {24, {"4.0-1902", "4.0-1919"}}, {25, {"4.0-1921", "4.0-1936"}},
{26, {"4.0-1939", "4.0-1959"}}, {27, {"4.0-1961", "4.0-2018"}}, {28, {"4.0-2020", "4.0-2291"}},
{29, {"4.0-2293", "4.0-2360"}}, {30, {"4.0-2362", "4.0-2628"}}, {31, {"4.0-2632", "4.0-3331"}},
{32, {"4.0-3334", "4.0-3340"}}, {33, {"4.0-3342", "4.0-3373"}}, {34, {"4.0-3376", "4.0-3402"}},
{35, {"4.0-3409", "4.0-3603"}}, {36, {"4.0-3610", "4.0-4480"}}, {37, {"4.0-4484", "4.0-4943"}},
{38, {"4.0-4963", "4.0-5267"}}, {39, {"4.0-5279", "4.0-5525"}}, {40, {"4.0-5531", "4.0-5809"}},
{41, {"4.0-5811", "4.0-5923"}}, {42, {"4.0-5925", "4.0-5946"}}};
// The 16 -> 17 change modified the size of StateHeader,
// so versions older than that can't even be decompressed anymore
{ 17,{ "3.5-1311", "3.5-1364" } },{ 18,{ "3.5-1366", "3.5-1371" } },{ 19,{ "3.5-1372", "3.5-1408" } },
{ 20,{ "3.5-1409", "4.0-704" } },{ 21,{ "4.0-705", "4.0-889" } },{ 22,{ "4.0-905", "4.0-1871" } },
{ 23,{ "4.0-1873", "4.0-1900" } },{ 24,{ "4.0-1902", "4.0-1919" } },{ 25,{ "4.0-1921", "4.0-1936" } },
{ 26,{ "4.0-1939", "4.0-1959" } },{ 27,{ "4.0-1961", "4.0-2018" } },{ 28,{ "4.0-2020", "4.0-2291" } },
{ 29,{ "4.0-2293", "4.0-2360" } },{ 30,{ "4.0-2362", "4.0-2628" } },{ 31,{ "4.0-2632", "4.0-3331" } },
{ 32,{ "4.0-3334", "4.0-3340" } },{ 33,{ "4.0-3342", "4.0-3373" } },{ 34,{ "4.0-3376", "4.0-3402" } },
{ 35,{ "4.0-3409", "4.0-3603" } },{ 36,{ "4.0-3610", "4.0-4480" } },{ 37,{ "4.0-4484", "4.0-4943" } },
{ 38,{ "4.0-4963", "4.0-5267" } },{ 39,{ "4.0-5279", "4.0-5525" } },{ 40,{ "4.0-5531", "4.0-5809" } },
{ 41,{ "4.0-5811", "4.0-5923" } },{ 42,{ "4.0-5925", "4.0-5946" } } };
enum
{
@ -164,8 +164,8 @@ static std::string DoState(PointerWrap& p)
if (is_wii != is_wii_currently)
{
OSD::AddMessage(StringFromFormat("Cannot load a savestate created under %s mode in %s mode",
is_wii ? "Wii" : "GC", is_wii_currently ? "Wii" : "GC"),
OSD::Duration::NORMAL, OSD::Color::RED);
is_wii ? "Wii" : "GC", is_wii_currently ? "Wii" : "GC"),
OSD::Duration::NORMAL, OSD::Color::RED);
p.SetMode(PointerWrap::MODE_MEASURE);
return version_created_by;
}
@ -207,42 +207,36 @@ void LoadFromBuffer(std::vector<u8>& buffer)
return;
}
bool wasUnpaused = Core::PauseAndLock(true);
u8* ptr = &buffer[0];
PointerWrap p(&ptr, PointerWrap::MODE_READ);
DoState(p);
Core::PauseAndLock(false, wasUnpaused);
Core::RunAsCPUThread([&] {
u8* ptr = &buffer[0];
PointerWrap p(&ptr, PointerWrap::MODE_READ);
DoState(p);
});
}
void SaveToBuffer(std::vector<u8>& buffer)
{
bool wasUnpaused = Core::PauseAndLock(true);
Core::RunAsCPUThread([&] {
u8* ptr = nullptr;
PointerWrap p(&ptr, PointerWrap::MODE_MEASURE);
u8* ptr = nullptr;
PointerWrap p(&ptr, PointerWrap::MODE_MEASURE);
DoState(p);
const size_t buffer_size = reinterpret_cast<size_t>(ptr);
buffer.resize(buffer_size);
DoState(p);
const size_t buffer_size = reinterpret_cast<size_t>(ptr);
buffer.resize(buffer_size);
ptr = &buffer[0];
p.SetMode(PointerWrap::MODE_WRITE);
DoState(p);
Core::PauseAndLock(false, wasUnpaused);
ptr = &buffer[0];
p.SetMode(PointerWrap::MODE_WRITE);
DoState(p);
});
}
void VerifyBuffer(std::vector<u8>& buffer)
{
bool wasUnpaused = Core::PauseAndLock(true);
u8* ptr = &buffer[0];
PointerWrap p(&ptr, PointerWrap::MODE_VERIFY);
DoState(p);
Core::PauseAndLock(false, wasUnpaused);
Core::RunAsCPUThread([&] {
u8* ptr = &buffer[0];
PointerWrap p(&ptr, PointerWrap::MODE_VERIFY);
DoState(p);
});
}
// return state number not in map
@ -399,48 +393,44 @@ static void CompressAndDumpState(CompressAndDumpState_args save_args)
void SaveAs(const std::string& filename, bool wait)
{
// Pause the core while we save the state
bool wasUnpaused = Core::PauseAndLock(true);
// Measure the size of the buffer.
u8* ptr = nullptr;
PointerWrap p(&ptr, PointerWrap::MODE_MEASURE);
DoState(p);
const size_t buffer_size = reinterpret_cast<size_t>(ptr);
// Then actually do the write.
{
std::lock_guard<std::mutex> lk(g_cs_current_buffer);
g_current_buffer.resize(buffer_size);
ptr = &g_current_buffer[0];
p.SetMode(PointerWrap::MODE_WRITE);
Core::RunAsCPUThread([&] {
// Measure the size of the buffer.
u8* ptr = nullptr;
PointerWrap p(&ptr, PointerWrap::MODE_MEASURE);
DoState(p);
}
const size_t buffer_size = reinterpret_cast<size_t>(ptr);
if (p.GetMode() == PointerWrap::MODE_WRITE)
{
Core::DisplayMessage("Saving State...", 1000);
// Then actually do the write.
{
std::lock_guard<std::mutex> lk(g_cs_current_buffer);
g_current_buffer.resize(buffer_size);
ptr = &g_current_buffer[0];
p.SetMode(PointerWrap::MODE_WRITE);
DoState(p);
}
CompressAndDumpState_args save_args;
save_args.buffer_vector = &g_current_buffer;
save_args.buffer_mutex = &g_cs_current_buffer;
save_args.filename = filename;
save_args.wait = wait;
if (p.GetMode() == PointerWrap::MODE_WRITE)
{
Core::DisplayMessage("Saving State...", 1000);
Flush();
g_save_thread = std::thread(CompressAndDumpState, save_args);
g_compressAndDumpStateSyncEvent.Wait();
CompressAndDumpState_args save_args;
save_args.buffer_vector = &g_current_buffer;
save_args.buffer_mutex = &g_cs_current_buffer;
save_args.filename = filename;
save_args.wait = wait;
g_last_filename = filename;
}
else
{
// someone aborted the save by changing the mode?
Core::DisplayMessage("Unable to save: Internal DoState Error", 4000);
}
Flush();
g_save_thread = std::thread(CompressAndDumpState, save_args);
g_compressAndDumpStateSyncEvent.Wait();
// Resume the core and disable stepping
Core::PauseAndLock(false, wasUnpaused);
g_last_filename = filename;
}
else
{
// someone aborted the save by changing the mode?
Core::DisplayMessage("Unable to save: Internal DoState Error", 4000);
}
});
}
bool ReadHeader(const std::string& filename, StateHeader& header)
@ -486,7 +476,7 @@ static void LoadFileStateData(const std::string& filename, std::vector<u8>& ret_
if (strncmp(SConfig::GetInstance().GetGameID().c_str(), header.gameID, 6))
{
Core::DisplayMessage(
StringFromFormat("State belongs to a different game (ID %.*s)", 6, header.gameID), 2000);
StringFromFormat("State belongs to a different game (ID %.*s)", 6, header.gameID), 2000);
return;
}
@ -513,8 +503,8 @@ static void LoadFileStateData(const std::string& filename, std::vector<u8>& ret_
{
// This doesn't seem to happen anymore.
PanicAlertT("Internal LZO Error - decompression failed (%d) (%li, %li) \n"
"Try loading the state again",
res, i, new_len);
"Try loading the state again",
res, i, new_len);
return;
}
@ -549,72 +539,68 @@ void LoadAs(const std::string& filename)
return;
}
// Stop the core while we load the state
bool wasUnpaused = Core::PauseAndLock(true);
Core::RunAsCPUThread([&] {
g_loadDepth++;
g_loadDepth++;
// Save temp buffer for undo load state
if (!Movie::IsJustStartingRecordingInputFromSaveState())
{
std::lock_guard<std::mutex> lk(g_cs_undo_load_buffer);
SaveToBuffer(g_undo_load_buffer);
if (Movie::IsMovieActive())
Movie::SaveRecording(File::GetUserPath(D_STATESAVES_IDX) + "undo.dtm");
else if (File::Exists(File::GetUserPath(D_STATESAVES_IDX) + "undo.dtm"))
File::Delete(File::GetUserPath(D_STATESAVES_IDX) + "undo.dtm");
}
bool loaded = false;
bool loadedSuccessfully = false;
std::string version_created_by;
// brackets here are so buffer gets freed ASAP
{
std::vector<u8> buffer;
LoadFileStateData(filename, buffer);
if (!buffer.empty())
// Save temp buffer for undo load state
if (!Movie::IsJustStartingRecordingInputFromSaveState())
{
u8* ptr = &buffer[0];
PointerWrap p(&ptr, PointerWrap::MODE_READ);
version_created_by = DoState(p);
loaded = true;
loadedSuccessfully = (p.GetMode() == PointerWrap::MODE_READ);
std::lock_guard<std::mutex> lk(g_cs_undo_load_buffer);
SaveToBuffer(g_undo_load_buffer);
if (Movie::IsMovieActive())
Movie::SaveRecording(File::GetUserPath(D_STATESAVES_IDX) + "undo.dtm");
else if (File::Exists(File::GetUserPath(D_STATESAVES_IDX) + "undo.dtm"))
File::Delete(File::GetUserPath(D_STATESAVES_IDX) + "undo.dtm");
}
}
if (loaded)
{
if (loadedSuccessfully)
bool loaded = false;
bool loadedSuccessfully = false;
std::string version_created_by;
// brackets here are so buffer gets freed ASAP
{
Core::DisplayMessage(StringFromFormat("Loaded state from %s", filename.c_str()), 2000);
if (File::Exists(filename + ".dtm"))
Movie::LoadInput(filename + ".dtm");
else if (!Movie::IsJustStartingRecordingInputFromSaveState() &&
!Movie::IsJustStartingPlayingInputFromSaveState())
Movie::EndPlayInput(false);
std::vector<u8> buffer;
LoadFileStateData(filename, buffer);
if (!buffer.empty())
{
u8* ptr = &buffer[0];
PointerWrap p(&ptr, PointerWrap::MODE_READ);
version_created_by = DoState(p);
loaded = true;
loadedSuccessfully = (p.GetMode() == PointerWrap::MODE_READ);
}
}
else
if (loaded)
{
// failed to load
Core::DisplayMessage("Unable to load: Can't load state from other versions!", 4000);
if (!version_created_by.empty())
Core::DisplayMessage("The savestate was created using " + version_created_by, 4000);
if (loadedSuccessfully)
{
Core::DisplayMessage(StringFromFormat("Loaded state from %s", filename.c_str()), 2000);
if (File::Exists(filename + ".dtm"))
Movie::LoadInput(filename + ".dtm");
else if (!Movie::IsJustStartingRecordingInputFromSaveState() &&
!Movie::IsJustStartingPlayingInputFromSaveState())
Movie::EndPlayInput(false);
}
else
{
// failed to load
Core::DisplayMessage("Unable to load: Can't load state from other versions!", 4000);
if (!version_created_by.empty())
Core::DisplayMessage("The savestate was created using " + version_created_by, 4000);
// since we could be in an inconsistent state now (and might crash or whatever), undo.
if (g_loadDepth < 2)
UndoLoadState();
// since we could be in an inconsistent state now (and might crash or whatever), undo.
if (g_loadDepth < 2)
UndoLoadState();
}
}
}
if (s_on_after_load_callback)
s_on_after_load_callback();
if (s_on_after_load_callback)
s_on_after_load_callback();
g_loadDepth--;
// resume dat core
Core::PauseAndLock(false, wasUnpaused);
g_loadDepth--;
});
}
void SetOnAfterLoadCallback(AfterLoadCallbackFunc callback)
@ -624,24 +610,22 @@ void SetOnAfterLoadCallback(AfterLoadCallbackFunc callback)
void VerifyAt(const std::string& filename)
{
bool wasUnpaused = Core::PauseAndLock(true);
Core::RunAsCPUThread([&] {
std::vector<u8> buffer;
LoadFileStateData(filename, buffer);
std::vector<u8> buffer;
LoadFileStateData(filename, buffer);
if (!buffer.empty())
{
u8* ptr = &buffer[0];
PointerWrap p(&ptr, PointerWrap::MODE_VERIFY);
DoState(p);
if (!buffer.empty())
{
u8* ptr = &buffer[0];
PointerWrap p(&ptr, PointerWrap::MODE_VERIFY);
DoState(p);
if (p.GetMode() == PointerWrap::MODE_VERIFY)
Core::DisplayMessage(StringFromFormat("Verified state at %s", filename.c_str()), 2000);
else
Core::DisplayMessage("Unable to Verify : Can't verify state from other revisions !", 4000);
}
Core::PauseAndLock(false, wasUnpaused);
if (p.GetMode() == PointerWrap::MODE_VERIFY)
Core::DisplayMessage(StringFromFormat("Verified state at %s", filename.c_str()), 2000);
else
Core::DisplayMessage("Unable to Verify : Can't verify state from other revisions !", 4000);
}
});
}
void Init()
@ -671,7 +655,7 @@ void Shutdown()
static std::string MakeStateFilename(int number)
{
return StringFromFormat("%s%s.s%02i", File::GetUserPath(D_STATESAVES_IDX).c_str(),
SConfig::GetInstance().GetGameID().c_str(), number);
SConfig::GetInstance().GetGameID().c_str(), number);
}
void Save(int slot, bool wait)

View file

@ -51,7 +51,7 @@ using Map = std::unordered_map<std::string, std::string>;
// Note that this function will not overwrite entries that already are in the maps
static bool LoadMap(const std::string& file_path, Map& map,
std::function<bool(const std::string& game_id)> predicate)
std::function<bool(const std::string& game_id)> predicate)
{
std::ifstream txt;
File::OpenFStream(txt, file_path, std::ios::in);
@ -83,7 +83,7 @@ static bool IsGCTitle(const std::string& game_id)
{
const char system_id = game_id[0];
return game_id.length() == 6 &&
(system_id == 'G' || system_id == 'D' || system_id == 'U' || system_id == 'P');
(system_id == 'G' || system_id == 'D' || system_id == 'U' || system_id == 'P');
}
static bool IsWiiTitle(const std::string& game_id)
@ -94,12 +94,12 @@ static bool IsWiiTitle(const std::string& game_id)
static bool IsJapaneseGCTitle(const std::string& game_id)
{
return IsGCTitle(game_id) && DiscIO::RegionSwitchGC(game_id[3]) == DiscIO::Region::NTSC_J;
return IsGCTitle(game_id) && DiscIO::CountrySwitch(game_id[3]) == DiscIO::Country::COUNTRY_JAPAN;
}
static bool IsNonJapaneseGCTitle(const std::string& game_id)
{
return IsGCTitle(game_id) && DiscIO::RegionSwitchGC(game_id[3]) != DiscIO::Region::NTSC_J;
return IsGCTitle(game_id) && DiscIO::CountrySwitch(game_id[3]) != DiscIO::Country::COUNTRY_JAPAN;
}
// Note that this function will not overwrite entries that already are in the maps
@ -124,6 +124,9 @@ TitleDatabase::TitleDatabase()
if (!LoadMap(load_directory + "wiitdb.txt", m_gc_title_map, m_wii_title_map))
LoadMap(load_directory + "titles.txt", m_gc_title_map, m_wii_title_map);
if (!SConfig::GetInstance().m_use_builtin_title_database)
return;
// Load the database in the console language.
// Note: The GameCube language setting can't be set to Japanese,
// so instead, we use Japanese names iff the games are NTSC-J.
@ -133,7 +136,7 @@ TitleDatabase::TitleDatabase()
if (gc_code != "en")
{
LoadMap(File::GetSysDirectory() + "wiitdb-" + gc_code + ".txt", m_gc_title_map,
IsNonJapaneseGCTitle);
IsNonJapaneseGCTitle);
}
if (wii_code != "en")
LoadMap(File::GetSysDirectory() + "wiitdb-" + wii_code + ".txt", m_wii_title_map, IsWiiTitle);
@ -147,7 +150,7 @@ TitleDatabase::TitleDatabase()
// i18n: "Wii Menu" (or System Menu) refers to the Wii's main menu,
// which is (usually) the first thing users see when a Wii console starts.
m_wii_title_map.emplace("0000000100000002", GetStringT("Wii Menu"));
for (const auto& id : {"HAXX", "JODI", "00010001af1bf516", "LULZ", "OHBC"})
for (const auto& id : { "HAXX", "JODI", "00010001af1bf516", "LULZ", "OHBC" })
m_wii_title_map.emplace(id, "The Homebrew Channel");
}
@ -157,7 +160,7 @@ std::string TitleDatabase::GetTitleName(const std::string& game_id, TitleType ty
{
const auto& map = IsWiiTitle(game_id) ? m_wii_title_map : m_gc_title_map;
const std::string key =
type == TitleType::Channel && game_id.length() == 6 ? game_id.substr(0, 4) : game_id;
type == TitleType::Channel && game_id.length() == 6 ? game_id.substr(0, 4) : game_id;
const auto iterator = map.find(key);
return iterator != map.end() ? iterator->second : "";
}

View file

@ -26,21 +26,21 @@
constexpr u32 default_NG_key_id = 0x6AAB8C59;
constexpr u8 default_NG_priv[] = {
0x00, 0xAB, 0xEE, 0xC1, 0xDD, 0xB4, 0xA6, 0x16, 0x6B, 0x70, 0xFD, 0x7E, 0x56, 0x67, 0x70,
0x57, 0x55, 0x27, 0x38, 0xA3, 0x26, 0xC5, 0x46, 0x16, 0xF7, 0x62, 0xC9, 0xED, 0x73, 0xF2,
0x00, 0xAB, 0xEE, 0xC1, 0xDD, 0xB4, 0xA6, 0x16, 0x6B, 0x70, 0xFD, 0x7E, 0x56, 0x67, 0x70,
0x57, 0x55, 0x27, 0x38, 0xA3, 0x26, 0xC5, 0x46, 0x16, 0xF7, 0x62, 0xC9, 0xED, 0x73, 0xF2,
};
constexpr u8 default_NG_sig[] = {
// R
0x00, 0xD8, 0x81, 0x63, 0xB2, 0x00, 0x6B, 0x0B, 0x54, 0x82, 0x88, 0x63, 0x81, 0x1C, 0x00, 0x71,
0x12, 0xED, 0xB7, 0xFD, 0x21, 0xAB, 0x0E, 0x50, 0x0E, 0x1F, 0xBF, 0x78, 0xAD, 0x37,
// S
0x00, 0x71, 0x8D, 0x82, 0x41, 0xEE, 0x45, 0x11, 0xC7, 0x3B, 0xAC, 0x08, 0xB6, 0x83, 0xDC, 0x05,
0xB8, 0xA8, 0x90, 0x1F, 0xA8, 0x2A, 0x0E, 0x4E, 0x76, 0xEF, 0x44, 0x72, 0x99, 0xF8,
// R
0x00, 0xD8, 0x81, 0x63, 0xB2, 0x00, 0x6B, 0x0B, 0x54, 0x82, 0x88, 0x63, 0x81, 0x1C, 0x00, 0x71,
0x12, 0xED, 0xB7, 0xFD, 0x21, 0xAB, 0x0E, 0x50, 0x0E, 0x1F, 0xBF, 0x78, 0xAD, 0x37,
// S
0x00, 0x71, 0x8D, 0x82, 0x41, 0xEE, 0x45, 0x11, 0xC7, 0x3B, 0xAC, 0x08, 0xB6, 0x83, 0xDC, 0x05,
0xB8, 0xA8, 0x90, 0x1F, 0xA8, 0x2A, 0x0E, 0x4E, 0x76, 0xEF, 0x44, 0x72, 0x99, 0xF8,
};
static void MakeBlankSigECCert(u8* cert_out, const char* signer, const char* name,
const u8* private_key, u32 key_id)
const u8* private_key, u32 key_id)
{
memset(cert_out, 0, 0x180);
*(u32*)cert_out = Common::swap32(0x10002);
@ -87,7 +87,7 @@ void MakeNGCert(u8* ng_cert_out, u32 NG_id, u32 NG_key_id, const u8* NG_priv, co
// NG_id is the device-unique id to use
// if NG_priv is nullptr or NG_id is 0, it will use builtin defaults
void MakeAPSigAndCert(u8* sig_out, u8* ap_cert_out, u64 title_id, u8* data, u32 data_size,
const u8* NG_priv, u32 NG_id)
const u8* NG_priv, u32 NG_id)
{
u8 hash[20];
u8 ap_priv[30];
@ -137,7 +137,7 @@ EcWii::EcWii()
else
{
ERROR_LOG(IOS_ES, "Failed to read keys.bin, check it is the correct size of %08zX bytes.",
sizeof(BootMiiKeysBin));
sizeof(BootMiiKeysBin));
}
}
else
@ -148,9 +148,9 @@ EcWii::EcWii()
else
{
ERROR_LOG(
IOS_ES,
"%s could not be found. Using default values. We recommend you grab keys.bin from BootMii.",
keys_path.c_str());
IOS_ES,
"%s could not be found. Using default values. We recommend you grab keys.bin from BootMii.",
keys_path.c_str());
}
if (init)
@ -181,6 +181,11 @@ const u8* EcWii::GetNGSig() const
return BootMiiKeysBin.ng_sig;
}
const u8* EcWii::GetBackupKey() const
{
return BootMiiKeysBin.backup_key;
}
void EcWii::InitDefaults()
{
memset(&BootMiiKeysBin, 0, sizeof(BootMiiKeysBin));

View file

@ -8,19 +8,19 @@
// http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
/*
*
* Structs for keys.bin taken from:
*
* mini - a Free Software replacement for the Nintendo/BroadOn IOS.
* crypto hardware support
*
* Copyright (C) 2008, 2009 Haxx Enterprises <bushing@gmail.com>
* Copyright (C) 2008, 2009 Sven Peter <svenpeter@gmail.com>
* Copyright (C) 2008, 2009 Hector Martin "marcan" <marcan@marcansoft.com>
*
* # This code is licensed to you under the terms of the GNU GPL, version 2;
* # see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
*/
*
* Structs for keys.bin taken from:
*
* mini - a Free Software replacement for the Nintendo/BroadOn IOS.
* crypto hardware support
*
* Copyright (C) 2008, 2009 Haxx Enterprises <bushing@gmail.com>
* Copyright (C) 2008, 2009 Sven Peter <svenpeter@gmail.com>
* Copyright (C) 2008, 2009 Hector Martin "marcan" <marcan@marcansoft.com>
*
* # This code is licensed to you under the terms of the GNU GPL, version 2;
* # see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
*/
#pragma once
@ -30,7 +30,7 @@ constexpr u32 DEFAULT_WII_DEVICE_ID = 0x0403AC68;
void MakeNGCert(u8* ng_cert_out, u32 NG_id, u32 NG_key_id, const u8* NG_priv, const u8* NG_sig);
void MakeAPSigAndCert(u8* sig_out, u8* ap_cert_out, u64 title_id, u8* data, u32 data_size,
const u8* NG_priv, u32 NG_id);
const u8* NG_priv, u32 NG_id);
class EcWii
{
@ -42,6 +42,7 @@ public:
u32 GetNGKeyID() const;
const u8* GetNGPriv() const;
const u8* GetNGSig() const;
const u8* GetBackupKey() const;
private:
void InitDefaults();
@ -59,7 +60,7 @@ private:
#ifndef _WIN32
__attribute__((packed))
#endif
eep_ctr_t;
eep_ctr_t;
struct
{
@ -82,7 +83,7 @@ private:
};
};
u8 nand_key[0x10]; // 0x158
u8 rng_key[0x10]; // 0x168
u8 backup_key[0x10]; // 0x168
u32 unk1; // 0x178
u32 unk2; // 0x17C
u8 eeprom_pad[0x80]; // 0x180
@ -106,7 +107,7 @@ private:
__attribute__((packed))
#endif
BootMiiKeysBin;
BootMiiKeysBin;
#pragma pack(pop)
};

Some files were not shown because too many files have changed in this diff Show more