mirror of
https://github.com/Tinob/Ishiiruka.git
synced 2024-06-16 03:17:27 -04:00
Merge latest master changes
This commit is contained in:
parent
53753d0474
commit
ba330d64fc
|
@ -17,3 +17,11 @@ find_package_handle_standard_args(MINIUPNPC DEFAULT_MSG MINIUPNPC_INCLUDE_DIR MI
|
|||
set(MINIUPNPC_LIBRARIES ${MINIUPNPC_LIBRARY})
|
||||
set(MINIUPNPC_INCLUDE_DIRS ${MINIUPNPC_INCLUDE_DIR})
|
||||
mark_as_advanced(MINIUPNPC_INCLUDE_DIR MINIUPNPC_LIBRARY MINIUPNPC_API_VERSION_STR)
|
||||
|
||||
if (MINIUPNPC_FOUND AND NOT TARGET miniupnpc)
|
||||
add_library(Miniupnpc::miniupnpc UNKNOWN IMPORTED)
|
||||
set_target_properties(Miniupnpc::miniupnpc PROPERTIES
|
||||
IMPORTED_LOCATION ${MINIUPNPC_LIBRARIES}
|
||||
INTERFACE_INCLUDE_DIRECTORIES ${MINIUPNPC_INCLUDE_DIRS}
|
||||
)
|
||||
endif()
|
||||
|
|
|
@ -673,12 +673,8 @@ if(USE_UPNP)
|
|||
else()
|
||||
message(STATUS "Using static miniupnpc from Externals")
|
||||
add_subdirectory(Externals/miniupnpc)
|
||||
set(MINIUPNPC_INCLUDE_DIRS Externals/miniupnpc/src)
|
||||
set(MINIUPNPC_LIBRARIES miniupnpc)
|
||||
endif()
|
||||
add_definitions(-DUSE_UPNP)
|
||||
include_directories(${MINIUPNPC_INCLUDE_DIRS})
|
||||
list(APPEND LIBS ${MINIUPNPC_LIBRARIES})
|
||||
endif()
|
||||
|
||||
if(NOT APPLE)
|
||||
|
|
14
Data/Sys/GameSettings/HA8.ini
Normal file
14
Data/Sys/GameSettings/HA8.ini
Normal file
|
@ -0,0 +1,14 @@
|
|||
# HA8xxx - Super Mario Bros. 2 (Brawl VC)
|
||||
|
||||
[Core]
|
||||
# Values set here will override the main Dolphin settings.
|
||||
|
||||
[Video_Settings]
|
||||
SafeTextureCacheColorSamples = 0
|
||||
|
||||
[Video_Hacks]
|
||||
EFBToTextureEnable = False
|
||||
|
||||
[Video_Enhancements]
|
||||
MaxAnisotropy = 0
|
||||
ForceFiltering = False
|
14
Data/Sys/GameSettings/HA9.ini
Normal file
14
Data/Sys/GameSettings/HA9.ini
Normal file
|
@ -0,0 +1,14 @@
|
|||
# HA8xxx - Super Mario Bros. (Brawl VC)
|
||||
|
||||
[Core]
|
||||
# Values set here will override the main Dolphin settings.
|
||||
|
||||
[Video_Settings]
|
||||
SafeTextureCacheColorSamples = 0
|
||||
|
||||
[Video_Hacks]
|
||||
EFBToTextureEnable = False
|
||||
|
||||
[Video_Enhancements]
|
||||
MaxAnisotropy = 0
|
||||
ForceFiltering = False
|
14
Data/Sys/GameSettings/HBA.ini
Normal file
14
Data/Sys/GameSettings/HBA.ini
Normal file
|
@ -0,0 +1,14 @@
|
|||
# HBAxxx - The Legend of Zelda (Brawl VC)
|
||||
|
||||
[Core]
|
||||
# Values set here will override the main Dolphin settings.
|
||||
|
||||
[Video_Settings]
|
||||
SafeTextureCacheColorSamples = 0
|
||||
|
||||
[Video_Hacks]
|
||||
EFBToTextureEnable = False
|
||||
|
||||
[Video_Enhancements]
|
||||
MaxAnisotropy = 0
|
||||
ForceFiltering = False
|
14
Data/Sys/GameSettings/HBB.ini
Normal file
14
Data/Sys/GameSettings/HBB.ini
Normal file
|
@ -0,0 +1,14 @@
|
|||
# HBBxxx - Kirby's Adventure (Brawl VC)
|
||||
|
||||
[Core]
|
||||
# Values set here will override the main Dolphin settings.
|
||||
|
||||
[Video_Settings]
|
||||
SafeTextureCacheColorSamples = 0
|
||||
|
||||
[Video_Hacks]
|
||||
EFBToTextureEnable = False
|
||||
|
||||
[Video_Enhancements]
|
||||
MaxAnisotropy = 0
|
||||
ForceFiltering = False
|
14
Data/Sys/GameSettings/HBC.ini
Normal file
14
Data/Sys/GameSettings/HBC.ini
Normal file
|
@ -0,0 +1,14 @@
|
|||
# HBCxxx - Kid Icarus (Brawl VC)
|
||||
|
||||
[Core]
|
||||
# Values set here will override the main Dolphin settings.
|
||||
|
||||
[Video_Settings]
|
||||
SafeTextureCacheColorSamples = 0
|
||||
|
||||
[Video_Hacks]
|
||||
EFBToTextureEnable = False
|
||||
|
||||
[Video_Enhancements]
|
||||
MaxAnisotropy = 0
|
||||
ForceFiltering = False
|
14
Data/Sys/GameSettings/HBD.ini
Normal file
14
Data/Sys/GameSettings/HBD.ini
Normal file
|
@ -0,0 +1,14 @@
|
|||
# HBDxxx - Ice Climber (Brawl VC)
|
||||
|
||||
[Core]
|
||||
# Values set here will override the main Dolphin settings.
|
||||
|
||||
[Video_Settings]
|
||||
SafeTextureCacheColorSamples = 0
|
||||
|
||||
[Video_Hacks]
|
||||
EFBToTextureEnable = False
|
||||
|
||||
[Video_Enhancements]
|
||||
MaxAnisotropy = 0
|
||||
ForceFiltering = False
|
7
Data/Sys/GameSettings/HBF.ini
Normal file
7
Data/Sys/GameSettings/HBF.ini
Normal file
|
@ -0,0 +1,7 @@
|
|||
# HBFxxx - Super Mario World (Brawl VC)
|
||||
|
||||
[Core]
|
||||
# Values set here will override the main Dolphin settings.
|
||||
|
||||
[Video_Settings]
|
||||
SafeTextureCacheColorSamples = 0
|
7
Data/Sys/GameSettings/HBG.ini
Normal file
7
Data/Sys/GameSettings/HBG.ini
Normal file
|
@ -0,0 +1,7 @@
|
|||
# HBGxxx - F-Zero (Brawl VC)
|
||||
|
||||
[Core]
|
||||
# Values set here will override the main Dolphin settings.
|
||||
|
||||
[Video_Settings]
|
||||
SafeTextureCacheColorSamples = 0
|
9
Data/Sys/GameSettings/HBI.ini
Normal file
9
Data/Sys/GameSettings/HBI.ini
Normal file
|
@ -0,0 +1,9 @@
|
|||
# HBIxxx - Super Metroid (Brawl VC)
|
||||
|
||||
[Core]
|
||||
# Values set here will override the main Dolphin settings.
|
||||
|
||||
[Video_Settings]
|
||||
SafeTextureCacheColorSamples = 0
|
||||
UseXFB = True
|
||||
UseRealXFB = False
|
8
Data/Sys/GameSettings/HBK.ini
Normal file
8
Data/Sys/GameSettings/HBK.ini
Normal file
|
@ -0,0 +1,8 @@
|
|||
# HBKxxx - The Legend of Zelda: Ocarina of Time (Brawl VC)
|
||||
|
||||
[Core]
|
||||
# Values set here will override the main Dolphin settings.
|
||||
|
||||
[Video_Hacks]
|
||||
# Fixes Link preview not appearing in Equipment Menu screen
|
||||
EFBToTextureEnable = False
|
4
Externals/miniupnpc/CMakeLists.txt
vendored
4
Externals/miniupnpc/CMakeLists.txt
vendored
|
@ -18,8 +18,6 @@ if(APPLE)
|
|||
add_definitions(-DMACOSX -D_DARWIN_C_SOURCE)
|
||||
endif()
|
||||
|
||||
include_directories(include)
|
||||
|
||||
set(SRCS src/igd_desc_parse.c
|
||||
src/miniupnpc.c
|
||||
src/minixml.c
|
||||
|
@ -35,4 +33,6 @@ set(SRCS src/igd_desc_parse.c
|
|||
src/receivedata.c)
|
||||
|
||||
add_library(miniupnpc STATIC ${SRCS})
|
||||
target_include_directories(miniupnpc PUBLIC src)
|
||||
|
||||
add_library(Miniupnpc::miniupnpc ALIAS miniupnpc)
|
||||
|
|
2
Externals/wxWidgets3/wx/wxcocoa.h
vendored
2
Externals/wxWidgets3/wx/wxcocoa.h
vendored
|
@ -249,7 +249,7 @@
|
|||
|
||||
#define wxUSE_INTL 1
|
||||
|
||||
#define wxUSE_XLOCALE 1
|
||||
#define wxUSE_XLOCALE 0
|
||||
|
||||
#define wxUSE_DATETIME 1
|
||||
|
||||
|
|
2
Externals/wxWidgets3/wx/wxgtk.h
vendored
2
Externals/wxWidgets3/wx/wxgtk.h
vendored
|
@ -239,7 +239,7 @@
|
|||
|
||||
#define wxUSE_INTL 1
|
||||
|
||||
#define wxUSE_XLOCALE 1
|
||||
#define wxUSE_XLOCALE 0
|
||||
|
||||
#define wxUSE_DATETIME 1
|
||||
|
||||
|
|
|
@ -246,13 +246,6 @@ public final class NativeLibrary
|
|||
*/
|
||||
public static native void SetConfig(String configFile, String Section, String Key, String Value);
|
||||
|
||||
/**
|
||||
* Sets the filename to be run during emulation.
|
||||
*
|
||||
* @param filename The filename to be run during emulation.
|
||||
*/
|
||||
public static native void SetFilename(String filename);
|
||||
|
||||
/**
|
||||
* Gets the embedded banner within the given ISO/ROM.
|
||||
*
|
||||
|
@ -328,7 +321,7 @@ public final class NativeLibrary
|
|||
/**
|
||||
* Begins emulation.
|
||||
*/
|
||||
public static native void Run();
|
||||
public static native void Run(String path);
|
||||
|
||||
// Surface Handling
|
||||
public static native void SurfaceChanged(Surface surf);
|
||||
|
|
|
@ -66,9 +66,6 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
|
|||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
|
||||
{
|
||||
String path = getArguments().getString(ARGUMENT_GAME_PATH);
|
||||
NativeLibrary.SetFilename(path);
|
||||
|
||||
View contents = inflater.inflate(R.layout.fragment_emulation, container, false);
|
||||
|
||||
SurfaceView surfaceView = (SurfaceView) contents.findViewById(R.id.surface_emulation);
|
||||
|
@ -241,7 +238,8 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
|
|||
Log.info("[EmulationFragment] Starting emulation: " + mSurface);
|
||||
|
||||
// Start emulation using the provided Surface.
|
||||
NativeLibrary.Run();
|
||||
String path = getArguments().getString(ARGUMENT_GAME_PATH);
|
||||
NativeLibrary.Run(path);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -274,10 +274,13 @@ public final class SettingsFile
|
|||
current = sectionFromLine(line);
|
||||
sections.put(current.getName(), current);
|
||||
}
|
||||
else if ((current != null) && line.contains("="))
|
||||
else if ((current != null))
|
||||
{
|
||||
Setting setting = settingFromLine(current, line, fileName);
|
||||
current.putSetting(setting);
|
||||
if (setting != null)
|
||||
{
|
||||
current.putSetting(setting);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -381,6 +384,12 @@ public final class SettingsFile
|
|||
{
|
||||
String[] splitLine = line.split("=");
|
||||
|
||||
if (splitLine.length != 2)
|
||||
{
|
||||
Log.warning("Skipping invalid config line \"" + line + "\"");
|
||||
return null;
|
||||
}
|
||||
|
||||
String key = splitLine[0].trim();
|
||||
String value = splitLine[1].trim();
|
||||
|
||||
|
|
|
@ -1,19 +1,11 @@
|
|||
<resources>
|
||||
<!-- Default screen margins, per the Android Design guidelines. -->
|
||||
<dimen name="activity_horizontal_margin">16dp</dimen>
|
||||
<dimen name="activity_vertical_margin">16dp</dimen>
|
||||
|
||||
<dimen name="diameter">48dp</dimen>
|
||||
<dimen name="elevation_low">1dp</dimen>
|
||||
<dimen name="elevation_high">4dp</dimen>
|
||||
<dimen name="add_button_margin">16dp</dimen>
|
||||
|
||||
<dimen name="main_appbar_height">128dp</dimen>
|
||||
|
||||
<dimen name="spacing_xsmall">2dp</dimen>
|
||||
<dimen name="spacing_small">4dp</dimen>
|
||||
<dimen name="spacing_medium">8dp</dimen>
|
||||
<dimen name="spacing_medlarge">12dp</dimen>
|
||||
<dimen name="spacing_large">16dp</dimen>
|
||||
<dimen name="spacing_xlarge">32dp</dimen>
|
||||
</resources>
|
||||
|
|
|
@ -52,23 +52,8 @@
|
|||
</style>
|
||||
|
||||
<!-- Themes for Dialogs -->
|
||||
<style name="DolphinDialogBase" parent="Theme.AppCompat.Light.Dialog">
|
||||
<item name="colorPrimary">@color/dolphin_blue</item>
|
||||
<item name="colorPrimaryDark">@color/dolphin_blue_dark</item>
|
||||
</style>
|
||||
|
||||
<!-- Inherit from the Base Dolphin Dialog Theme -->
|
||||
<style name="DolphinDialogWii" parent="DolphinDialogBase">
|
||||
<item name="colorAccent">@color/dolphin_accent_wii</item>
|
||||
</style>
|
||||
|
||||
<style name="DolphinDialogGamecube" parent="DolphinDialogBase">
|
||||
<item name="colorAccent">@color/dolphin_accent_gamecube</item>
|
||||
</style>
|
||||
|
||||
<style name="DolphinDialogWiiware" parent="DolphinDialogBase">
|
||||
<item name="colorAccent">@color/dolphin_accent_wiiware</item>
|
||||
</style>
|
||||
|
||||
<style name="DolphinEmulationBase" parent="Theme.AppCompat.Light.DarkActionBar">
|
||||
<item name="colorPrimary">@color/dolphin_blue</item>
|
||||
|
|
|
@ -53,7 +53,6 @@ JavaVM* g_java_vm;
|
|||
namespace
|
||||
{
|
||||
ANativeWindow* s_surf;
|
||||
std::string s_filename;
|
||||
std::string s_set_userpath;
|
||||
|
||||
jclass s_jni_class;
|
||||
|
@ -466,7 +465,8 @@ JNIEXPORT void JNICALL
|
|||
Java_org_dolphinemu_dolphinemu_NativeLibrary_WriteProfileResults(JNIEnv* env, jobject obj);
|
||||
JNIEXPORT void JNICALL
|
||||
Java_org_dolphinemu_dolphinemu_NativeLibrary_CacheClassesAndMethods(JNIEnv* env, jobject obj);
|
||||
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_Run(JNIEnv* env, jobject obj);
|
||||
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_Run(JNIEnv* env, jobject obj,
|
||||
jstring jFile);
|
||||
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SurfaceChanged(JNIEnv* env,
|
||||
jobject obj,
|
||||
jobject surf);
|
||||
|
@ -633,13 +633,6 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SetConfig(
|
|||
ini.Save(File::GetUserPath(D_CONFIG_IDX) + std::string(file));
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SetFilename(JNIEnv* env,
|
||||
jobject obj,
|
||||
jstring jFile)
|
||||
{
|
||||
s_filename = GetJString(env, jFile);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SaveState(JNIEnv* env,
|
||||
jobject obj,
|
||||
jint slot)
|
||||
|
@ -773,9 +766,11 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_RefreshWiimo
|
|||
WiimoteReal::Refresh();
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_Run(JNIEnv* env, jobject obj)
|
||||
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_Run(JNIEnv* env, jobject obj,
|
||||
jstring jFile)
|
||||
{
|
||||
__android_log_print(ANDROID_LOG_INFO, DOLPHIN_TAG, "Running : %s", s_filename.c_str());
|
||||
const std::string path = GetJString(env, jFile);
|
||||
__android_log_print(ANDROID_LOG_INFO, DOLPHIN_TAG, "Running : %s", path.c_str());
|
||||
|
||||
// Install our callbacks
|
||||
OSD::AddCallback(OSD::CallbackType::Initialization, ButtonManager::Init);
|
||||
|
@ -791,7 +786,7 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_Run(JNIEnv*
|
|||
|
||||
// No use running the loop when booting fails
|
||||
s_have_wm_user_stop = false;
|
||||
if (BootManager::BootCore(BootParameters::GenerateFromFile(s_filename)))
|
||||
if (BootManager::BootCore(BootParameters::GenerateFromFile(path)))
|
||||
{
|
||||
static constexpr int TIMEOUT = 10000;
|
||||
static constexpr int WAIT_STEP = 25;
|
||||
|
|
|
@ -19,14 +19,19 @@
|
|||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/File.h"
|
||||
#include "Common/NonCopyable.h"
|
||||
|
||||
class WaveFileWriter : NonCopyable
|
||||
class WaveFileWriter
|
||||
{
|
||||
public:
|
||||
WaveFileWriter();
|
||||
~WaveFileWriter();
|
||||
|
||||
WaveFileWriter(const WaveFileWriter&) = delete;
|
||||
WaveFileWriter& operator=(const WaveFileWriter&) = delete;
|
||||
WaveFileWriter(WaveFileWriter&&) = delete;
|
||||
WaveFileWriter& operator=(WaveFileWriter&&) = delete;
|
||||
|
||||
|
||||
bool Start(const std::string& filename, unsigned int HLESampleRate);
|
||||
void Stop();
|
||||
|
||||
|
|
|
@ -4057,7 +4057,7 @@ void ARM64XEmitter::ANDI2R(ARM64Reg Rd, ARM64Reg Rn, u64 imm, ARM64Reg scratch)
|
|||
else
|
||||
{
|
||||
_assert_msg_(DYNA_REC, scratch != INVALID_REG,
|
||||
"ANDSI2R - failed to construct logical immediate value from %08x, need scratch",
|
||||
"ANDI2R - failed to construct logical immediate value from %08x, need scratch",
|
||||
(u32)imm);
|
||||
MOVI2R(scratch, imm);
|
||||
AND(Rd, Rn, scratch);
|
||||
|
|
|
@ -120,6 +120,10 @@ endif()
|
|||
|
||||
add_dolphin_library(common "${SRCS}" "${LIBS}")
|
||||
|
||||
if(USE_UPNP)
|
||||
target_link_libraries(common PRIVATE Miniupnpc::miniupnpc)
|
||||
endif()
|
||||
|
||||
if(OPROFILE_FOUND)
|
||||
target_link_libraries(common PRIVATE ${OPROFILE_LIBRARIES})
|
||||
endif()
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
#include "Common/Assert.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/MemoryUtil.h"
|
||||
#include "Common/NonCopyable.h"
|
||||
|
||||
// Everything that needs to generate code should inherit from this.
|
||||
// You get memory management for free, plus, you can use all emitter functions without
|
||||
|
@ -18,7 +17,7 @@
|
|||
// Example implementation:
|
||||
// class JIT : public CodeBlock<ARMXEmitter> {}
|
||||
template <class T>
|
||||
class CodeBlock : public T, NonCopyable
|
||||
class CodeBlock : public T
|
||||
{
|
||||
private:
|
||||
// A privately used function to set the executable RAM space to something invalid.
|
||||
|
@ -37,11 +36,16 @@ protected:
|
|||
std::vector<CodeBlock*> m_children;
|
||||
|
||||
public:
|
||||
CodeBlock() = default;
|
||||
virtual ~CodeBlock()
|
||||
{
|
||||
if (region)
|
||||
FreeCodeSpace();
|
||||
}
|
||||
CodeBlock(const CodeBlock&) = delete;
|
||||
CodeBlock& operator=(const CodeBlock&) = delete;
|
||||
CodeBlock(CodeBlock&&) = delete;
|
||||
CodeBlock& operator=(CodeBlock&&) = delete;
|
||||
|
||||
// Call this before you generate any code.
|
||||
void AllocCodeSpace(size_t size)
|
||||
|
|
|
@ -138,7 +138,6 @@
|
|||
<ClInclude Include="MsgHandler.h" />
|
||||
<ClInclude Include="NandPaths.h" />
|
||||
<ClInclude Include="Network.h" />
|
||||
<ClInclude Include="NonCopyable.h" />
|
||||
<ClInclude Include="PcapFile.h" />
|
||||
<ClInclude Include="Profiler.h" />
|
||||
<ClInclude Include="ScopeGuard.h" />
|
||||
|
|
|
@ -237,7 +237,6 @@
|
|||
<Filter>GL\GLInterface</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Assert.h" />
|
||||
<ClInclude Include="NonCopyable.h" />
|
||||
<ClInclude Include="Analytics.h" />
|
||||
<ClInclude Include="Semaphore.h" />
|
||||
<ClInclude Include="ConstantBuffer.h" />
|
||||
|
|
|
@ -22,27 +22,35 @@
|
|||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#define strerror_r(err, buf, len) strerror_s(buf, len, err)
|
||||
#endif
|
||||
|
||||
// Generic function to get last error message.
|
||||
// Call directly after the command or use the error num.
|
||||
// This function might change the error code.
|
||||
std::string GetLastErrorMsg()
|
||||
{
|
||||
const size_t buff_size = 256;
|
||||
char err_str[buff_size];
|
||||
constexpr size_t BUFFER_SIZE = 256;
|
||||
|
||||
// Wrapper function to get last strerror(errno) string.
|
||||
// This function might change the error code.
|
||||
std::string LastStrerrorString()
|
||||
{
|
||||
char error_message[BUFFER_SIZE];
|
||||
|
||||
#ifdef _WIN32
|
||||
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, GetLastError(),
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), err_str, buff_size, nullptr);
|
||||
#else
|
||||
// We assume that the XSI-compliant version of strerror_r (returns int) is used
|
||||
// rather than the GNU version (returns char*). The returned value is stored to
|
||||
// an int variable to get a compile-time check that the return type is not char*.
|
||||
const int result = strerror_r(errno, err_str, buff_size);
|
||||
const int result = strerror_r(errno, error_message, BUFFER_SIZE);
|
||||
if (result != 0)
|
||||
return "";
|
||||
#endif
|
||||
|
||||
return std::string(err_str);
|
||||
return std::string(error_message);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
// Wrapper function to get GetLastError() string.
|
||||
// This function might change the error code.
|
||||
std::string GetLastErrorString()
|
||||
{
|
||||
char error_message[BUFFER_SIZE];
|
||||
|
||||
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, GetLastError(),
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), error_message, BUFFER_SIZE, nullptr);
|
||||
return std::string(error_message);
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -86,7 +86,12 @@ __declspec(dllimport) void __stdcall DebugBreak(void);
|
|||
}
|
||||
#endif // WIN32 ndef
|
||||
|
||||
// Generic function to get last error message.
|
||||
// Call directly after the command or use the error num.
|
||||
// Wrapper function to get last strerror(errno) string.
|
||||
// This function might change the error code.
|
||||
std::string GetLastErrorMsg();
|
||||
std::string LastStrerrorString();
|
||||
|
||||
#ifdef _WIN32
|
||||
// Wrapper function to get GetLastError() string.
|
||||
// This function might change the error code.
|
||||
std::string GetLastErrorString();
|
||||
#endif
|
||||
|
|
|
@ -18,12 +18,12 @@
|
|||
#define LONG int
|
||||
#endif
|
||||
|
||||
typedef uint8_t u8;
|
||||
typedef uint16_t u16;
|
||||
typedef uint32_t u32;
|
||||
typedef uint64_t u64;
|
||||
using u8 = std::uint8_t;
|
||||
using u16 = std::uint16_t;
|
||||
using u32 = std::uint32_t;
|
||||
using u64 = std::uint64_t;
|
||||
|
||||
typedef int8_t s8;
|
||||
typedef int16_t s16;
|
||||
typedef int32_t s32;
|
||||
typedef int64_t s64;
|
||||
using s8 = std::int8_t;
|
||||
using s16 = std::int16_t;
|
||||
using s32 = std::int32_t;
|
||||
using s64 = std::int64_t;
|
||||
|
|
|
@ -9,14 +9,13 @@
|
|||
#include <string>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/NonCopyable.h"
|
||||
|
||||
namespace File
|
||||
{
|
||||
// simple wrapper for cstdlib file functions to
|
||||
// hopefully will make error checking easier
|
||||
// and make forgetting an fclose() harder
|
||||
class IOFile : public NonCopyable
|
||||
class IOFile
|
||||
{
|
||||
public:
|
||||
IOFile();
|
||||
|
@ -25,6 +24,9 @@ public:
|
|||
|
||||
~IOFile();
|
||||
|
||||
IOFile(const IOFile&) = delete;
|
||||
IOFile& operator=(const IOFile&) = delete;
|
||||
|
||||
IOFile(IOFile&& other) noexcept;
|
||||
IOFile& operator=(IOFile&& other) noexcept;
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <fcntl.h>
|
||||
#include <fstream>
|
||||
#include <limits.h>
|
||||
#include <string>
|
||||
#include <sys/stat.h>
|
||||
|
@ -141,14 +142,14 @@ bool Delete(const std::string& filename)
|
|||
if (!DeleteFile(UTF8ToTStr(filename).c_str()))
|
||||
{
|
||||
WARN_LOG(COMMON, "Delete: DeleteFile failed on %s: %s", filename.c_str(),
|
||||
GetLastErrorMsg().c_str());
|
||||
GetLastErrorString().c_str());
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
if (unlink(filename.c_str()) == -1)
|
||||
{
|
||||
WARN_LOG(COMMON, "Delete: unlink failed on %s: %s", filename.c_str(),
|
||||
GetLastErrorMsg().c_str());
|
||||
LastStrerrorString().c_str());
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
@ -241,11 +242,14 @@ bool DeleteDir(const std::string& filename)
|
|||
#ifdef _WIN32
|
||||
if (::RemoveDirectory(UTF8ToTStr(filename).c_str()))
|
||||
return true;
|
||||
ERROR_LOG(COMMON, "DeleteDir: RemoveDirectory failed on %s: %s", filename.c_str(),
|
||||
GetLastErrorString().c_str());
|
||||
#else
|
||||
if (rmdir(filename.c_str()) == 0)
|
||||
return true;
|
||||
ERROR_LOG(COMMON, "DeleteDir: rmdir failed on %s: %s", filename.c_str(),
|
||||
LastStrerrorString().c_str());
|
||||
#endif
|
||||
ERROR_LOG(COMMON, "DeleteDir: %s: %s", filename.c_str(), GetLastErrorMsg().c_str());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -268,12 +272,14 @@ bool Rename(const std::string& srcFilename, const std::string& destFilename)
|
|||
if (MoveFile(sf.c_str(), df.c_str()))
|
||||
return true;
|
||||
}
|
||||
ERROR_LOG(COMMON, "Rename: MoveFile failed on %s --> %s: %s", srcFilename.c_str(),
|
||||
destFilename.c_str(), GetLastErrorString().c_str());
|
||||
#else
|
||||
if (rename(srcFilename.c_str(), destFilename.c_str()) == 0)
|
||||
return true;
|
||||
ERROR_LOG(COMMON, "Rename: rename failed on %s --> %s: %s", srcFilename.c_str(),
|
||||
destFilename.c_str(), LastStrerrorString().c_str());
|
||||
#endif
|
||||
ERROR_LOG(COMMON, "Rename: failed %s --> %s: %s", srcFilename.c_str(), destFilename.c_str(),
|
||||
GetLastErrorMsg().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -312,66 +318,22 @@ bool RenameSync(const std::string& srcFilename, const std::string& destFilename)
|
|||
return true;
|
||||
}
|
||||
|
||||
// copies file srcFilename to destFilename, returns true on success
|
||||
bool Copy(const std::string& srcFilename, const std::string& destFilename)
|
||||
// copies file source_path to destination_path, returns true on success
|
||||
bool Copy(const std::string& source_path, const std::string& destination_path)
|
||||
{
|
||||
INFO_LOG(COMMON, "Copy: %s --> %s", srcFilename.c_str(), destFilename.c_str());
|
||||
INFO_LOG(COMMON, "Copy: %s --> %s", source_path.c_str(), destination_path.c_str());
|
||||
#ifdef _WIN32
|
||||
if (CopyFile(UTF8ToTStr(srcFilename).c_str(), UTF8ToTStr(destFilename).c_str(), FALSE))
|
||||
if (CopyFile(UTF8ToTStr(source_path).c_str(), UTF8ToTStr(destination_path).c_str(), FALSE))
|
||||
return true;
|
||||
|
||||
ERROR_LOG(COMMON, "Copy: failed %s --> %s: %s", srcFilename.c_str(), destFilename.c_str(),
|
||||
GetLastErrorMsg().c_str());
|
||||
ERROR_LOG(COMMON, "Copy: failed %s --> %s: %s", source_path.c_str(), destination_path.c_str(),
|
||||
GetLastErrorString().c_str());
|
||||
return false;
|
||||
#else
|
||||
|
||||
// buffer size
|
||||
#define BSIZE 1024
|
||||
|
||||
char buffer[BSIZE];
|
||||
|
||||
// Open input file
|
||||
std::ifstream input;
|
||||
OpenFStream(input, srcFilename, std::ifstream::in | std::ifstream::binary);
|
||||
if (!input.is_open())
|
||||
{
|
||||
ERROR_LOG(COMMON, "Copy: input failed %s --> %s: %s", srcFilename.c_str(), destFilename.c_str(),
|
||||
GetLastErrorMsg().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// open output file
|
||||
File::IOFile output(destFilename, "wb");
|
||||
|
||||
if (!output.IsOpen())
|
||||
{
|
||||
ERROR_LOG(COMMON, "Copy: output failed %s --> %s: %s", srcFilename.c_str(),
|
||||
destFilename.c_str(), GetLastErrorMsg().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// copy loop
|
||||
while (!input.eof())
|
||||
{
|
||||
// read input
|
||||
input.read(buffer, BSIZE);
|
||||
if (!input)
|
||||
{
|
||||
ERROR_LOG(COMMON, "Copy: failed reading from source, %s --> %s: %s", srcFilename.c_str(),
|
||||
destFilename.c_str(), GetLastErrorMsg().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// write output
|
||||
if (!output.WriteBytes(buffer, BSIZE))
|
||||
{
|
||||
ERROR_LOG(COMMON, "Copy: failed writing to output, %s --> %s: %s", srcFilename.c_str(),
|
||||
destFilename.c_str(), GetLastErrorMsg().c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
std::ifstream source{ source_path, std::ios::binary };
|
||||
std::ofstream destination{ destination_path, std::ios::binary };
|
||||
destination << source.rdbuf();
|
||||
return source.good() && destination.good();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -394,14 +356,14 @@ u64 GetSize(FILE* f)
|
|||
u64 pos = ftello(f);
|
||||
if (fseeko(f, 0, SEEK_END) != 0)
|
||||
{
|
||||
ERROR_LOG(COMMON, "GetSize: seek failed %p: %s", f, GetLastErrorMsg().c_str());
|
||||
ERROR_LOG(COMMON, "GetSize: seek failed %p: %s", f, LastStrerrorString().c_str());
|
||||
return 0;
|
||||
}
|
||||
|
||||
u64 size = ftello(f);
|
||||
if ((size != pos) && (fseeko(f, pos, SEEK_SET) != 0))
|
||||
{
|
||||
ERROR_LOG(COMMON, "GetSize: seek failed %p: %s", f, GetLastErrorMsg().c_str());
|
||||
ERROR_LOG(COMMON, "GetSize: seek failed %p: %s", f, LastStrerrorString().c_str());
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -416,7 +378,7 @@ bool CreateEmptyFile(const std::string& filename)
|
|||
if (!File::IOFile(filename, "wb"))
|
||||
{
|
||||
ERROR_LOG(COMMON, "CreateEmptyFile: failed %s: %s", filename.c_str(),
|
||||
GetLastErrorMsg().c_str());
|
||||
LastStrerrorString().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -559,7 +521,7 @@ bool DeleteDirRecursively(const std::string& directory)
|
|||
}
|
||||
|
||||
// Create directory and copy contents (does not overwrite existing files)
|
||||
void CopyDir(const std::string& source_path, const std::string& dest_path)
|
||||
void CopyDir(const std::string& source_path, const std::string& dest_path, bool destructive)
|
||||
{
|
||||
if (source_path == dest_path)
|
||||
return;
|
||||
|
@ -600,15 +562,21 @@ void CopyDir(const std::string& source_path, const std::string& dest_path)
|
|||
{
|
||||
if (!Exists(dest))
|
||||
File::CreateFullPath(dest + DIR_SEP);
|
||||
CopyDir(source, dest);
|
||||
CopyDir(source, dest, destructive);
|
||||
}
|
||||
else if (!destructive && !Exists(dest))
|
||||
{
|
||||
Copy(source, dest);
|
||||
}
|
||||
else if (destructive)
|
||||
{
|
||||
Rename(source, dest);
|
||||
}
|
||||
else if (!Exists(dest))
|
||||
File::Copy(source, dest);
|
||||
#ifdef _WIN32
|
||||
} while (FindNextFile(hFind, &ffd) != 0);
|
||||
FindClose(hFind);
|
||||
#else
|
||||
}
|
||||
}
|
||||
closedir(dirp);
|
||||
#endif
|
||||
}
|
||||
|
@ -620,7 +588,7 @@ std::string GetCurrentDir()
|
|||
char* dir = __getcwd(nullptr, 0);
|
||||
if (!dir)
|
||||
{
|
||||
ERROR_LOG(COMMON, "GetCurrentDirectory failed: %s", GetLastErrorMsg().c_str());
|
||||
ERROR_LOG(COMMON, "GetCurrentDirectory failed: %s", LastStrerrorString().c_str());
|
||||
return nullptr;
|
||||
}
|
||||
std::string strDir = dir;
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
#include <sys/stat.h>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/NonCopyable.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "Common/StringUtil.h"
|
||||
|
@ -160,8 +159,9 @@ bool DeleteDirRecursively(const std::string& directory);
|
|||
// Returns the current directory
|
||||
std::string GetCurrentDir();
|
||||
|
||||
// Create directory and copy contents (does not overwrite existing files)
|
||||
void CopyDir(const std::string& source_path, const std::string& dest_path);
|
||||
// Create directory and copy contents (optionally overwrites existing files)
|
||||
void CopyDir(const std::string& source_path, const std::string& dest_path,
|
||||
bool destructive = false);
|
||||
|
||||
// Set the current directory to given directory
|
||||
bool SetCurrentDir(const std::string& directory);
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
|
||||
#include "Common/BitSet.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/NonCopyable.h"
|
||||
|
||||
// pure virtual interface
|
||||
class LogListener
|
||||
|
@ -28,7 +27,7 @@ public:
|
|||
};
|
||||
};
|
||||
|
||||
class LogManager : NonCopyable
|
||||
class LogManager
|
||||
{
|
||||
public:
|
||||
static LogManager* GetInstance();
|
||||
|
@ -36,9 +35,9 @@ public:
|
|||
static void Shutdown();
|
||||
|
||||
void Log(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, const char* file, int line,
|
||||
const char* fmt, va_list args);
|
||||
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);
|
||||
int line, const char* fmt, va_list args);
|
||||
|
||||
LogTypes::LOG_LEVELS GetLogLevel() const;
|
||||
void SetLogLevel(LogTypes::LOG_LEVELS level);
|
||||
|
@ -66,6 +65,11 @@ private:
|
|||
LogManager();
|
||||
~LogManager();
|
||||
|
||||
LogManager(const LogManager&) = delete;
|
||||
LogManager& operator=(const LogManager&) = delete;
|
||||
LogManager(LogManager&&) = delete;
|
||||
LogManager& operator=(LogManager&&) = delete;
|
||||
|
||||
LogTypes::LOG_LEVELS m_level;
|
||||
std::array<LogContainer, LogTypes::NUMBER_OF_LOGS> m_log{};
|
||||
std::array<LogListener*, LogListener::NUMBER_OF_LISTENERS> m_listeners{};
|
||||
|
|
|
@ -136,7 +136,7 @@ u8* MemArena::FindMemoryBase()
|
|||
u8* base = static_cast<u8*>(VirtualAlloc(nullptr, memory_size, MEM_RESERVE, PAGE_READWRITE));
|
||||
if (!base)
|
||||
{
|
||||
PanicAlert("Failed to map enough memory space: %s", GetLastErrorMsg().c_str());
|
||||
PanicAlert("Failed to map enough memory space: %s", GetLastErrorString().c_str());
|
||||
return nullptr;
|
||||
}
|
||||
VirtualFree(base, 0, MEM_RELEASE);
|
||||
|
@ -153,7 +153,7 @@ u8* MemArena::FindMemoryBase()
|
|||
void* base = mmap(nullptr, memory_size, PROT_NONE, flags, -1, 0);
|
||||
if (base == MAP_FAILED)
|
||||
{
|
||||
PanicAlert("Failed to map enough memory space: %s", GetLastErrorMsg().c_str());
|
||||
PanicAlert("Failed to map enough memory space: %s", LastStrerrorString().c_str());
|
||||
return nullptr;
|
||||
}
|
||||
munmap(base, memory_size);
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <psapi.h>
|
||||
#include "Common/StringUtil.h"
|
||||
#else
|
||||
#include <stdio.h>
|
||||
|
@ -89,20 +88,13 @@ void FreeMemoryPages(void* ptr, size_t size)
|
|||
{
|
||||
if (ptr)
|
||||
{
|
||||
bool error_occurred = false;
|
||||
|
||||
#ifdef _WIN32
|
||||
if (!VirtualFree(ptr, 0, MEM_RELEASE))
|
||||
error_occurred = true;
|
||||
PanicAlert("FreeMemoryPages failed!\nVirtualFree: %s", GetLastErrorString().c_str());
|
||||
#else
|
||||
int retval = munmap(ptr, size);
|
||||
|
||||
if (retval != 0)
|
||||
error_occurred = true;
|
||||
if (munmap(ptr, size) != 0)
|
||||
PanicAlert("FreeMemoryPages failed!\nmunmap: %s", LastStrerrorString().c_str());
|
||||
#endif
|
||||
|
||||
if (error_occurred)
|
||||
PanicAlert("FreeMemoryPages failed!\n%s", GetLastErrorMsg().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -120,84 +112,40 @@ void FreeAlignedMemory(void* ptr)
|
|||
|
||||
void ReadProtectMemory(void* ptr, size_t size)
|
||||
{
|
||||
bool error_occurred = false;
|
||||
|
||||
#ifdef _WIN32
|
||||
DWORD oldValue;
|
||||
if (!VirtualProtect(ptr, size, PAGE_NOACCESS, &oldValue))
|
||||
error_occurred = true;
|
||||
PanicAlert("ReadProtectMemory failed!\nVirtualProtect: %s", GetLastErrorString().c_str());
|
||||
#else
|
||||
int retval = mprotect(ptr, size, PROT_NONE);
|
||||
|
||||
if (retval != 0)
|
||||
error_occurred = true;
|
||||
if (mprotect(ptr, size, PROT_NONE) != 0)
|
||||
PanicAlert("ReadProtectMemory failed!\nmprotect: %s", LastStrerrorString().c_str());
|
||||
#endif
|
||||
|
||||
if (error_occurred)
|
||||
PanicAlert("ReadProtectMemory failed!\n%s", GetLastErrorMsg().c_str());
|
||||
}
|
||||
|
||||
void WriteProtectMemory(void* ptr, size_t size, bool allowExecute)
|
||||
{
|
||||
bool error_occurred = false;
|
||||
|
||||
#ifdef _WIN32
|
||||
DWORD oldValue;
|
||||
if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READ : PAGE_READONLY, &oldValue))
|
||||
error_occurred = true;
|
||||
PanicAlert("WriteProtectMemory failed!\nVirtualProtect: %s", GetLastErrorString().c_str());
|
||||
#else
|
||||
int retval = mprotect(ptr, size, allowExecute ? (PROT_READ | PROT_EXEC) : PROT_READ);
|
||||
|
||||
if (retval != 0)
|
||||
error_occurred = true;
|
||||
if (mprotect(ptr, size, allowExecute ? (PROT_READ | PROT_EXEC) : PROT_READ) != 0)
|
||||
PanicAlert("WriteProtectMemory failed!\nmprotect: %s", LastStrerrorString().c_str());
|
||||
#endif
|
||||
|
||||
if (error_occurred)
|
||||
PanicAlert("WriteProtectMemory failed!\n%s", GetLastErrorMsg().c_str());
|
||||
}
|
||||
|
||||
void UnWriteProtectMemory(void* ptr, size_t size, bool allowExecute)
|
||||
{
|
||||
bool error_occurred = false;
|
||||
|
||||
#ifdef _WIN32
|
||||
DWORD oldValue;
|
||||
if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE, &oldValue))
|
||||
error_occurred = true;
|
||||
PanicAlert("UnWriteProtectMemory failed!\nVirtualProtect: %s", GetLastErrorString().c_str());
|
||||
#else
|
||||
int retval = mprotect(ptr, size, allowExecute ? (PROT_READ | PROT_WRITE | PROT_EXEC) :
|
||||
PROT_WRITE | PROT_READ);
|
||||
|
||||
if (retval != 0)
|
||||
error_occurred = true;
|
||||
#endif
|
||||
|
||||
if (error_occurred)
|
||||
PanicAlert("UnWriteProtectMemory failed!\n%s", GetLastErrorMsg().c_str());
|
||||
}
|
||||
|
||||
std::string MemUsage()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
#pragma comment(lib, "psapi")
|
||||
DWORD processID = GetCurrentProcessId();
|
||||
HANDLE hProcess;
|
||||
PROCESS_MEMORY_COUNTERS pmc;
|
||||
std::string Ret;
|
||||
|
||||
// Print information about the memory usage of the process.
|
||||
|
||||
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processID);
|
||||
if (nullptr == hProcess)
|
||||
return "MemUsage Error";
|
||||
|
||||
if (GetProcessMemoryInfo(hProcess, &pmc, sizeof(pmc)))
|
||||
Ret = StringFromFormat("%s K", ThousandSeparate(pmc.WorkingSetSize / 1024, 7).c_str());
|
||||
|
||||
CloseHandle(hProcess);
|
||||
return Ret;
|
||||
#else
|
||||
return "";
|
||||
if (mprotect(ptr, size,
|
||||
allowExecute ? (PROT_READ | PROT_WRITE | PROT_EXEC) : PROT_WRITE | PROT_READ) != 0)
|
||||
{
|
||||
PanicAlert("UnWriteProtectMemory failed!\nmprotect: %s", LastStrerrorString().c_str());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
// Copyright 2015 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
// An inheritable class to disallow the copy constructor and operator= functions
|
||||
class NonCopyable
|
||||
{
|
||||
protected:
|
||||
constexpr NonCopyable() = default;
|
||||
~NonCopyable() = default;
|
||||
|
||||
NonCopyable(const NonCopyable&) = delete;
|
||||
NonCopyable& operator=(const NonCopyable&) = delete;
|
||||
|
||||
NonCopyable(NonCopyable&&) = default;
|
||||
NonCopyable& operator=(NonCopyable&&) = default;
|
||||
};
|
|
@ -18,9 +18,8 @@
|
|||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/File.h"
|
||||
#include "Common/NonCopyable.h"
|
||||
|
||||
class PCAP final : public NonCopyable
|
||||
class PCAP final
|
||||
{
|
||||
public:
|
||||
// Takes ownership of the file object. Assumes the file object is already
|
||||
|
|
|
@ -291,7 +291,7 @@ bool SDCardCreate(u64 disk_size /*in MB*/, const std::string& filename)
|
|||
FailWrite:
|
||||
ERROR_LOG(COMMON, "Could not write to '%s', aborting...", filename.c_str());
|
||||
if (unlink(filename.c_str()) < 0)
|
||||
ERROR_LOG(COMMON, "unlink(%s) failed: %s", filename.c_str(), GetLastErrorMsg().c_str());
|
||||
ERROR_LOG(COMMON, "unlink(%s) failed: %s", filename.c_str(), LastStrerrorString().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
#include <vector>
|
||||
|
||||
#include "Common/IniFile.h"
|
||||
#include "Common/NonCopyable.h"
|
||||
#include "Core/HW/EXI/EXI_Device.h"
|
||||
#include "Core/HW/SI/SI_Device.h"
|
||||
#include "Core/TitleDatabase.h"
|
||||
|
@ -52,7 +51,7 @@ enum GPUDeterminismMode
|
|||
|
||||
struct BootParameters;
|
||||
|
||||
struct SConfig : NonCopyable
|
||||
struct SConfig
|
||||
{
|
||||
// Wii Devices
|
||||
bool m_WiiSDCard;
|
||||
|
@ -320,6 +319,11 @@ struct SConfig : NonCopyable
|
|||
bool m_SSLDumpRootCA;
|
||||
bool m_SSLDumpPeerCert;
|
||||
|
||||
SConfig(const SConfig&) = delete;
|
||||
SConfig& operator=(const SConfig&) = delete;
|
||||
SConfig(SConfig&&) = delete;
|
||||
SConfig& operator=(SConfig&&) = delete;
|
||||
|
||||
// Save settings
|
||||
void SaveSettings();
|
||||
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
#include <string>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/NonCopyable.h"
|
||||
|
||||
class PCAP;
|
||||
|
||||
|
@ -51,7 +50,7 @@ public:
|
|||
|
||||
// A capture logger implementation that logs to PCAP files in a custom
|
||||
// packet-based format.
|
||||
class PCAPDSPCaptureLogger final : public DSPCaptureLogger, NonCopyable
|
||||
class PCAPDSPCaptureLogger final : public DSPCaptureLogger
|
||||
{
|
||||
public:
|
||||
// Automatically creates a writeable file (truncate existing file).
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/NandPaths.h"
|
||||
#include "Common/NonCopyable.h"
|
||||
#include "Common/Swap.h"
|
||||
#include "Common/Timer.h"
|
||||
|
||||
|
@ -295,7 +294,7 @@ public:
|
|||
std::string m_filename;
|
||||
};
|
||||
|
||||
class GCMemcard : NonCopyable
|
||||
class GCMemcard
|
||||
{
|
||||
private:
|
||||
bool m_valid;
|
||||
|
@ -317,6 +316,12 @@ private:
|
|||
public:
|
||||
explicit GCMemcard(const std::string& fileName, bool forceCreation = false,
|
||||
bool shift_jis = false);
|
||||
|
||||
GCMemcard(const GCMemcard&) = delete;
|
||||
GCMemcard& operator=(const GCMemcard&) = delete;
|
||||
GCMemcard(GCMemcard&&) = default;
|
||||
GCMemcard& operator=(GCMemcard&&) = default;
|
||||
|
||||
bool IsValid() const { return m_valid; }
|
||||
bool IsShiftJIS() const;
|
||||
bool Save();
|
||||
|
|
|
@ -10,19 +10,24 @@
|
|||
#include <vector>
|
||||
|
||||
#include "Common/Event.h"
|
||||
#include "Common/NonCopyable.h"
|
||||
#include "Core/HW/GCMemcard/GCMemcard.h"
|
||||
|
||||
// Uncomment this to write the system data of the memorycard from directory to disc
|
||||
//#define _WRITE_MC_HEADER 1
|
||||
void MigrateFromMemcardFile(const std::string& directory_name, int card_index);
|
||||
|
||||
class GCMemcardDirectory : public MemoryCardBase, NonCopyable
|
||||
class GCMemcardDirectory : public MemoryCardBase
|
||||
{
|
||||
public:
|
||||
GCMemcardDirectory(const std::string& directory, int slot, u16 size_mbits, bool shift_jis,
|
||||
int game_id);
|
||||
~GCMemcardDirectory();
|
||||
|
||||
GCMemcardDirectory(const GCMemcardDirectory&) = delete;
|
||||
GCMemcardDirectory& operator=(const GCMemcardDirectory&) = delete;
|
||||
GCMemcardDirectory(GCMemcardDirectory&&) = default;
|
||||
GCMemcardDirectory& operator=(GCMemcardDirectory&&) = default;
|
||||
|
||||
void FlushToFile();
|
||||
void FlushThread();
|
||||
s32 Read(u32 src_address, s32 length, u8* dest_address) override;
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
#include <memory>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/NonCopyable.h"
|
||||
|
||||
// All the templated and very repetitive MMIO-related code is isolated in this
|
||||
// file for easier reading. It mostly contains code related to handling methods
|
||||
|
@ -119,7 +118,7 @@ public:
|
|||
// inlinable, we need to provide some of the implementation of these two
|
||||
// classes here and can't just use a forward declaration.
|
||||
template <typename T>
|
||||
class ReadHandler : public NonCopyable
|
||||
class ReadHandler
|
||||
{
|
||||
public:
|
||||
ReadHandler();
|
||||
|
@ -155,7 +154,7 @@ private:
|
|||
std::function<T(u32)> m_ReadFunc;
|
||||
};
|
||||
template <typename T>
|
||||
class WriteHandler : public NonCopyable
|
||||
class WriteHandler
|
||||
{
|
||||
public:
|
||||
WriteHandler();
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "Common/CommonFuncs.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/ScopeGuard.h"
|
||||
#include "Common/Thread.h"
|
||||
#include "Core/HW/WiimoteCommon/WiimoteConstants.h"
|
||||
#include "Core/HW/WiimoteReal/IOWin.h"
|
||||
|
@ -184,7 +185,7 @@ inline void init_lib()
|
|||
namespace WiimoteReal
|
||||
{
|
||||
int IOWrite(HANDLE& dev_handle, OVERLAPPED& hid_overlap_write, enum WinWriteMethod& stack,
|
||||
const u8* buf, size_t len, DWORD* written);
|
||||
const u8* buf, size_t len, DWORD* written);
|
||||
int IORead(HANDLE& dev_handle, OVERLAPPED& hid_overlap_read, u8* buf, int index);
|
||||
|
||||
template <typename T>
|
||||
|
@ -201,12 +202,12 @@ WiimoteScannerWindows::WiimoteScannerWindows()
|
|||
|
||||
WiimoteScannerWindows::~WiimoteScannerWindows()
|
||||
{
|
||||
// TODO: what do we want here?
|
||||
// TODO: what do we want here?
|
||||
#if 0
|
||||
ProcessWiimotes(false, [](HANDLE, BLUETOOTH_RADIO_INFO&, BLUETOOTH_DEVICE_INFO_STRUCT& btdi)
|
||||
{
|
||||
RemoveWiimote(btdi);
|
||||
});
|
||||
ProcessWiimotes(false, [](HANDLE, BLUETOOTH_RADIO_INFO&, BLUETOOTH_DEVICE_INFO_STRUCT& btdi)
|
||||
{
|
||||
RemoveWiimote(btdi);
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -231,7 +232,7 @@ void WiimoteScannerWindows::Update()
|
|||
// including that device for further processing
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/hardware/ff549417(v=vs.85).aspx
|
||||
static bool GetParentDevice(const DEVINST& child_device_instance, HDEVINFO* parent_device_info,
|
||||
PSP_DEVINFO_DATA parent_device_data)
|
||||
PSP_DEVINFO_DATA parent_device_data)
|
||||
{
|
||||
ULONG status;
|
||||
ULONG problem_number;
|
||||
|
@ -258,7 +259,7 @@ static bool GetParentDevice(const DEVINST& child_device_instance, HDEVINFO* pare
|
|||
|
||||
// Get the device id of the parent, required to open the device info
|
||||
result =
|
||||
CM_Get_Device_ID(parent_device, parent_device_id.data(), (ULONG)parent_device_id.size(), 0);
|
||||
CM_Get_Device_ID(parent_device, parent_device_id.data(), (ULONG)parent_device_id.size(), 0);
|
||||
if (result != CR_SUCCESS)
|
||||
{
|
||||
return false;
|
||||
|
@ -269,7 +270,7 @@ static bool GetParentDevice(const DEVINST& child_device_instance, HDEVINFO* pare
|
|||
|
||||
// Open the device info data of the parent and put it in the emtpy info set
|
||||
if (!SetupDiOpenDeviceInfo((*parent_device_info), parent_device_id.data(), nullptr, 0,
|
||||
parent_device_data))
|
||||
parent_device_data))
|
||||
{
|
||||
SetupDiDestroyDeviceInfoList(parent_device_info);
|
||||
return false;
|
||||
|
@ -279,19 +280,19 @@ static bool GetParentDevice(const DEVINST& child_device_instance, HDEVINFO* pare
|
|||
}
|
||||
|
||||
std::wstring GetDeviceProperty(const HDEVINFO& device_info, const PSP_DEVINFO_DATA device_data,
|
||||
const DEVPROPKEY* requested_property)
|
||||
const DEVPROPKEY* requested_property)
|
||||
{
|
||||
DWORD required_size = 0;
|
||||
DEVPROPTYPE device_property_type;
|
||||
|
||||
SetupDiGetDeviceProperty(device_info, device_data, requested_property, &device_property_type,
|
||||
nullptr, 0, &required_size, 0);
|
||||
nullptr, 0, &required_size, 0);
|
||||
|
||||
std::vector<BYTE> unicode_buffer(required_size, 0);
|
||||
|
||||
BOOL result =
|
||||
SetupDiGetDeviceProperty(device_info, device_data, requested_property, &device_property_type,
|
||||
unicode_buffer.data(), required_size, nullptr, 0);
|
||||
SetupDiGetDeviceProperty(device_info, device_data, requested_property, &device_property_type,
|
||||
unicode_buffer.data(), required_size, nullptr, 0);
|
||||
if (!result)
|
||||
{
|
||||
return std::wstring();
|
||||
|
@ -316,7 +317,7 @@ static bool CheckForToshibaStack(const DEVINST& hid_interface_device_instance)
|
|||
if (GetParentDevice(hid_interface_device_instance, &parent_device_info, &parent_device_data))
|
||||
{
|
||||
std::wstring class_driver_provider =
|
||||
GetDeviceProperty(parent_device_info, &parent_device_data, &DEVPKEY_Device_DriverProvider);
|
||||
GetDeviceProperty(parent_device_info, &parent_device_data, &DEVPKEY_Device_DriverProvider);
|
||||
|
||||
SetupDiDestroyDeviceInfoList(parent_device_info);
|
||||
|
||||
|
@ -333,7 +334,7 @@ static WinWriteMethod GetInitialWriteMethod(bool IsUsingToshibaStack)
|
|||
// Currently Toshiba Bluetooth Stack needs the Output buffer to be the size of the largest output
|
||||
// report
|
||||
return (IsUsingToshibaStack ? WWM_WRITE_FILE_LARGEST_REPORT_SIZE :
|
||||
WWM_WRITE_FILE_ACTUAL_REPORT_SIZE);
|
||||
WWM_WRITE_FILE_ACTUAL_REPORT_SIZE);
|
||||
}
|
||||
|
||||
static int WriteToHandle(HANDLE& dev_handle, WinWriteMethod& method, const u8* buf, size_t size)
|
||||
|
@ -361,13 +362,15 @@ static int ReadFromHandle(HANDLE& dev_handle, u8* buf)
|
|||
static bool IsWiimote(const std::basic_string<TCHAR>& device_path, WinWriteMethod& method)
|
||||
{
|
||||
HANDLE dev_handle = CreateFile(device_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING,
|
||||
FILE_FLAG_OVERLAPPED, nullptr);
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING,
|
||||
FILE_FLAG_OVERLAPPED, nullptr);
|
||||
if (dev_handle == INVALID_HANDLE_VALUE)
|
||||
return false;
|
||||
|
||||
Common::ScopeGuard handle_guard{ [&dev_handle] { CloseHandle(dev_handle); } };
|
||||
|
||||
u8 buf[MAX_PAYLOAD];
|
||||
u8 const req_status_report[] = {WR_SET_REPORT | BT_OUTPUT, RT_REQUEST_STATUS, 0};
|
||||
u8 const req_status_report[] = { WR_SET_REPORT | BT_OUTPUT, RT_REQUEST_STATUS, 0 };
|
||||
int invalid_report_count = 0;
|
||||
int rc = WriteToHandle(dev_handle, method, req_status_report, sizeof(req_status_report));
|
||||
while (rc > 0)
|
||||
|
@ -396,13 +399,13 @@ static bool IsWiimote(const std::basic_string<TCHAR>& device_path, WinWriteMetho
|
|||
// wm is an array of max_wiimotes Wiimotes
|
||||
// Returns the total number of found and connected Wiimotes.
|
||||
void WiimoteScannerWindows::FindWiimotes(std::vector<Wiimote*>& found_wiimotes,
|
||||
Wiimote*& found_board)
|
||||
Wiimote*& found_board)
|
||||
{
|
||||
if (!s_loaded_ok)
|
||||
return;
|
||||
|
||||
ProcessWiimotes(true, [](HANDLE hRadio, const BLUETOOTH_RADIO_INFO& rinfo,
|
||||
BLUETOOTH_DEVICE_INFO_STRUCT& btdi) {
|
||||
BLUETOOTH_DEVICE_INFO_STRUCT& btdi) {
|
||||
ForgetWiimote(btdi);
|
||||
AttachWiimote(hRadio, rinfo, btdi);
|
||||
});
|
||||
|
@ -413,14 +416,14 @@ void WiimoteScannerWindows::FindWiimotes(std::vector<Wiimote*>& found_wiimotes,
|
|||
|
||||
// Get all hid devices connected
|
||||
HDEVINFO const device_info =
|
||||
SetupDiGetClassDevs(&device_id, nullptr, nullptr, (DIGCF_DEVICEINTERFACE | DIGCF_PRESENT));
|
||||
SetupDiGetClassDevs(&device_id, nullptr, nullptr, (DIGCF_DEVICEINTERFACE | DIGCF_PRESENT));
|
||||
|
||||
SP_DEVICE_INTERFACE_DATA device_data = {};
|
||||
device_data.cbSize = sizeof(device_data);
|
||||
PSP_DEVICE_INTERFACE_DETAIL_DATA detail_data = nullptr;
|
||||
|
||||
for (int index = 0;
|
||||
SetupDiEnumDeviceInterfaces(device_info, nullptr, &device_id, index, &device_data); ++index)
|
||||
SetupDiEnumDeviceInterfaces(device_info, nullptr, &device_id, index, &device_data); ++index)
|
||||
{
|
||||
// Get the size of the data block required
|
||||
DWORD len;
|
||||
|
@ -433,7 +436,7 @@ void WiimoteScannerWindows::FindWiimotes(std::vector<Wiimote*>& found_wiimotes,
|
|||
|
||||
// Query the data for this device
|
||||
if (SetupDiGetDeviceInterfaceDetail(device_info, &device_data, detail_data, len, nullptr,
|
||||
&device_info_data))
|
||||
&device_info_data))
|
||||
{
|
||||
std::basic_string<TCHAR> device_path(detail_data->DevicePath);
|
||||
bool IsUsingToshibaStack = CheckForToshibaStack(device_info_data.DevInst);
|
||||
|
@ -498,7 +501,7 @@ bool WiimoteWindows::ConnectInternal()
|
|||
auto const open_flags = FILE_SHARE_READ | FILE_SHARE_WRITE;
|
||||
|
||||
m_dev_handle = CreateFile(m_devicepath.c_str(), GENERIC_READ | GENERIC_WRITE, open_flags, nullptr,
|
||||
OPEN_EXISTING, FILE_FLAG_OVERLAPPED, nullptr);
|
||||
OPEN_EXISTING, FILE_FLAG_OVERLAPPED, nullptr);
|
||||
|
||||
if (m_dev_handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
|
@ -507,38 +510,38 @@ bool WiimoteWindows::ConnectInternal()
|
|||
}
|
||||
|
||||
#if 0
|
||||
TCHAR name[128] = {};
|
||||
pHidD_GetProductString(dev_handle, name, 128);
|
||||
TCHAR name[128] = {};
|
||||
pHidD_GetProductString(dev_handle, name, 128);
|
||||
|
||||
//ERROR_LOG(WIIMOTE, "Product string: %s", TStrToUTF8(name).c_str());
|
||||
//ERROR_LOG(WIIMOTE, "Product string: %s", TStrToUTF8(name).c_str());
|
||||
|
||||
if (!IsValidBluetoothName(TStrToUTF8(name)))
|
||||
{
|
||||
CloseHandle(dev_handle);
|
||||
dev_handle = 0;
|
||||
return false;
|
||||
}
|
||||
if (!IsValidBluetoothName(TStrToUTF8(name)))
|
||||
{
|
||||
CloseHandle(dev_handle);
|
||||
dev_handle = 0;
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
HIDD_ATTRIBUTES attr;
|
||||
attr.Size = sizeof(attr);
|
||||
if (!pHidD_GetAttributes(dev_handle, &attr))
|
||||
{
|
||||
CloseHandle(dev_handle);
|
||||
dev_handle = 0;
|
||||
return false;
|
||||
}
|
||||
HIDD_ATTRIBUTES attr;
|
||||
attr.Size = sizeof(attr);
|
||||
if (!pHidD_GetAttributes(dev_handle, &attr))
|
||||
{
|
||||
CloseHandle(dev_handle);
|
||||
dev_handle = 0;
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
// TODO: thread isn't started here now, do this elsewhere
|
||||
// This isn't as drastic as it sounds, since the process in which the threads
|
||||
// reside is normal priority. Needed for keeping audio reports at a decent rate
|
||||
/*
|
||||
if (!SetThreadPriority(m_wiimote_thread.native_handle(), THREAD_PRIORITY_TIME_CRITICAL))
|
||||
{
|
||||
ERROR_LOG(WIIMOTE, "Failed to set Wiimote thread priority");
|
||||
}
|
||||
if (!SetThreadPriority(m_wiimote_thread.native_handle(), THREAD_PRIORITY_TIME_CRITICAL))
|
||||
{
|
||||
ERROR_LOG(WIIMOTE, "Failed to set Wiimote thread priority");
|
||||
}
|
||||
*/
|
||||
|
||||
return true;
|
||||
|
@ -554,8 +557,8 @@ void WiimoteWindows::DisconnectInternal()
|
|||
}
|
||||
|
||||
WiimoteWindows::WiimoteWindows(const std::basic_string<TCHAR>& path,
|
||||
WinWriteMethod initial_write_method)
|
||||
: m_devicepath(path), m_write_method(initial_write_method)
|
||||
WinWriteMethod initial_write_method)
|
||||
: m_devicepath(path), m_write_method(initial_write_method)
|
||||
{
|
||||
m_dev_handle = nullptr;
|
||||
|
||||
|
@ -711,8 +714,8 @@ static int IOWritePerSetOutputReport(HANDLE& dev_handle, const u8* buf, size_t l
|
|||
}
|
||||
|
||||
static int IOWritePerWriteFile(HANDLE& dev_handle, OVERLAPPED& hid_overlap_write,
|
||||
WinWriteMethod& write_method, const u8* buf, size_t len,
|
||||
DWORD* written)
|
||||
WinWriteMethod& write_method, const u8* buf, size_t len,
|
||||
DWORD* written)
|
||||
{
|
||||
DWORD bytes_written;
|
||||
LPCVOID write_buffer = buf + 1;
|
||||
|
@ -734,7 +737,7 @@ static int IOWritePerWriteFile(HANDLE& dev_handle, OVERLAPPED& hid_overlap_write
|
|||
|
||||
ResetEvent(hid_overlap_write.hEvent);
|
||||
BOOLEAN result =
|
||||
WriteFile(dev_handle, write_buffer, bytes_to_write, &bytes_written, &hid_overlap_write);
|
||||
WriteFile(dev_handle, write_buffer, bytes_to_write, &bytes_written, &hid_overlap_write);
|
||||
if (!result)
|
||||
{
|
||||
const DWORD error = GetLastError();
|
||||
|
@ -818,7 +821,7 @@ static int IOWritePerWriteFile(HANDLE& dev_handle, OVERLAPPED& hid_overlap_write
|
|||
// Wiimotes work with WriteFile
|
||||
// as they don't accept output reports via the Control Channel.
|
||||
int IOWrite(HANDLE& dev_handle, OVERLAPPED& hid_overlap_write, WinWriteMethod& write_method,
|
||||
const u8* buf, size_t len, DWORD* written)
|
||||
const u8* buf, size_t len, DWORD* written)
|
||||
{
|
||||
switch (write_method)
|
||||
{
|
||||
|
@ -881,7 +884,7 @@ void ProcessWiimotes(bool new_scan, const T& callback)
|
|||
{
|
||||
// btdi.szName is sometimes missing it's content - it's a bt feature..
|
||||
DEBUG_LOG(WIIMOTE, "Authenticated %i connected %i remembered %i ", btdi.fAuthenticated,
|
||||
btdi.fConnected, btdi.fRemembered);
|
||||
btdi.fConnected, btdi.fRemembered);
|
||||
|
||||
if (IsValidDeviceName(UTF16ToUTF8(btdi.szName)))
|
||||
{
|
||||
|
@ -917,7 +920,7 @@ void RemoveWiimote(BLUETOOTH_DEVICE_INFO_STRUCT& btdi)
|
|||
}
|
||||
|
||||
bool AttachWiimote(HANDLE hRadio, const BLUETOOTH_RADIO_INFO& radio_info,
|
||||
BLUETOOTH_DEVICE_INFO_STRUCT& btdi)
|
||||
BLUETOOTH_DEVICE_INFO_STRUCT& btdi)
|
||||
{
|
||||
// We don't want "remembered" devices.
|
||||
// SetServiceState will just fail with them..
|
||||
|
@ -926,16 +929,16 @@ bool AttachWiimote(HANDLE hRadio, const BLUETOOTH_RADIO_INFO& radio_info,
|
|||
auto const& wm_addr = btdi.Address.rgBytes;
|
||||
|
||||
NOTICE_LOG(WIIMOTE, "Found Wiimote (%02x:%02x:%02x:%02x:%02x:%02x). Enabling HID service.",
|
||||
wm_addr[0], wm_addr[1], wm_addr[2], wm_addr[3], wm_addr[4], wm_addr[5]);
|
||||
wm_addr[0], wm_addr[1], wm_addr[2], wm_addr[3], wm_addr[4], wm_addr[5]);
|
||||
|
||||
#if defined(AUTHENTICATE_WIIMOTES)
|
||||
// Authenticate
|
||||
auto const& radio_addr = radio_info.address.rgBytes;
|
||||
// FIXME Not sure this usage of OOB_DATA_INFO is correct...
|
||||
BLUETOOTH_OOB_DATA_INFO oob_data_info = {0};
|
||||
BLUETOOTH_OOB_DATA_INFO oob_data_info = { 0 };
|
||||
memcpy(&oob_data_info.C[0], &radio_addr[0], sizeof(WCHAR) * 6);
|
||||
const DWORD auth_result = pBluetoothAuthenticateDeviceEx(nullptr, hRadio, &btdi, &oob_data_info,
|
||||
MITMProtectionNotDefined);
|
||||
MITMProtectionNotDefined);
|
||||
|
||||
if (ERROR_SUCCESS != auth_result)
|
||||
{
|
||||
|
@ -946,17 +949,17 @@ bool AttachWiimote(HANDLE hRadio, const BLUETOOTH_RADIO_INFO& radio_info,
|
|||
GUID guids[16];
|
||||
// If this is not done, the Wii device will not remember the pairing
|
||||
const DWORD srv_result =
|
||||
pBluetoothEnumerateInstalledServices(hRadio, &btdi, &pcServices, guids);
|
||||
pBluetoothEnumerateInstalledServices(hRadio, &btdi, &pcServices, guids);
|
||||
|
||||
if (ERROR_SUCCESS != srv_result)
|
||||
{
|
||||
ERROR_LOG(WIIMOTE, "AttachWiimote: BluetoothEnumerateInstalledServices returned %08x",
|
||||
srv_result);
|
||||
srv_result);
|
||||
}
|
||||
#endif
|
||||
// Activate service
|
||||
const DWORD hr = pBluetoothSetServiceState(
|
||||
hRadio, &btdi, &HumanInterfaceDeviceServiceClass_UUID, BLUETOOTH_SERVICE_ENABLE);
|
||||
hRadio, &btdi, &HumanInterfaceDeviceServiceClass_UUID, BLUETOOTH_SERVICE_ENABLE);
|
||||
|
||||
g_connect_times[btdi.Address.ullLong] = std::time(nullptr);
|
||||
|
||||
|
@ -984,7 +987,7 @@ bool ForgetWiimote(BLUETOOTH_DEVICE_INFO_STRUCT& btdi)
|
|||
|
||||
auto pair_time = g_connect_times.find(btdi.Address.ullLong);
|
||||
if (pair_time == g_connect_times.end() ||
|
||||
std::difftime(time(nullptr), pair_time->second) >= avoid_forget_seconds)
|
||||
std::difftime(time(nullptr), pair_time->second) >= avoid_forget_seconds)
|
||||
{
|
||||
// Make Windows forget about device so it will re-find it if visible.
|
||||
// This is also required to detect a disconnect for some reason..
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
#include "Common/Event.h"
|
||||
#include "Common/FifoQueue.h"
|
||||
#include "Common/Flag.h"
|
||||
#include "Common/NonCopyable.h"
|
||||
#include "Core/HW/Wiimote.h"
|
||||
#include "Core/HW/WiimoteCommon/WiimoteConstants.h"
|
||||
#include "Core/HW/WiimoteCommon/WiimoteHid.h"
|
||||
|
@ -24,9 +23,14 @@ class PointerWrap;
|
|||
|
||||
namespace WiimoteReal
|
||||
{
|
||||
class Wiimote : NonCopyable
|
||||
class Wiimote
|
||||
{
|
||||
public:
|
||||
Wiimote(const Wiimote&) = delete;
|
||||
Wiimote& operator=(const Wiimote&) = delete;
|
||||
Wiimote(Wiimote&&) = default;
|
||||
Wiimote& operator=(Wiimote&&) = default;
|
||||
|
||||
virtual ~Wiimote() {}
|
||||
// This needs to be called in derived destructors!
|
||||
void Shutdown();
|
||||
|
@ -148,7 +152,7 @@ private:
|
|||
std::thread m_scan_thread;
|
||||
Common::Flag m_scan_thread_running;
|
||||
Common::Event m_scan_mode_changed_event;
|
||||
std::atomic<WiimoteScanMode> m_scan_mode{WiimoteScanMode::DO_NOT_SCAN};
|
||||
std::atomic<WiimoteScanMode> m_scan_mode{ WiimoteScanMode::DO_NOT_SCAN };
|
||||
};
|
||||
|
||||
extern std::mutex g_wiimotes_mutex;
|
||||
|
|
|
@ -327,6 +327,15 @@ void HotkeyManager::LoadDefaults(const ControllerInterface& ciface)
|
|||
const std::string ALT = "((LMENU | RMENU) & !(LSHIFT | RSHIFT) & !(LCONTROL | RCONTROL))";
|
||||
const std::string SHIFT = "(!(LMENU | RMENU) & (LSHIFT | RSHIFT) & !(LCONTROL | RCONTROL))";
|
||||
const std::string CTRL = "(!(LMENU | RMENU) & !(LSHIFT | RSHIFT) & (LCONTROL | RCONTROL))";
|
||||
#elif __APPLE__
|
||||
const std::string NON =
|
||||
"(!`Left Alt` & !(`Left Shift`| `Right Shift`) & !(`Left Control` | `Right Control`))";
|
||||
const std::string ALT =
|
||||
"(`Left Alt` & !(`Left Shift`| `Right Shift`) & !(`Left Control` | `Right Control`))";
|
||||
const std::string SHIFT =
|
||||
"(!`Left Alt` & (`Left Shift`| `Right Shift`) & !(`Left Control` | `Right Control`))";
|
||||
const std::string CTRL =
|
||||
"(!`Left Alt` & !(`Left Shift`| `Right Shift`) & (`Left Control` | `Right Control`))";
|
||||
#else
|
||||
const std::string NON = "(!`Alt_L` & !(`Shift_L` | `Shift_R`) & !(`Control_L` | `Control_R` ))";
|
||||
const std::string ALT = "(`Alt_L` & !(`Shift_L` | `Shift_R`) & !(`Control_L` | `Control_R` ))";
|
||||
|
|
|
@ -50,7 +50,6 @@ typedef struct pollfd pollfd_t;
|
|||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/NonCopyable.h"
|
||||
#include "Core/HW/Memmap.h"
|
||||
#include "Core/IOS/IOS.h"
|
||||
#include "Core/IOS/Network/IP/Top.h"
|
||||
|
@ -207,7 +206,7 @@ public:
|
|||
void operator=(WiiSocket const&) = delete;
|
||||
};
|
||||
|
||||
class WiiSockMan : public ::NonCopyable
|
||||
class WiiSockMan
|
||||
{
|
||||
public:
|
||||
static s32 GetNetErrorCode(s32 ret, const char* caller, bool isRW);
|
||||
|
@ -249,7 +248,10 @@ public:
|
|||
|
||||
private:
|
||||
WiiSockMan() = default;
|
||||
|
||||
WiiSockMan(const WiiSockMan&) = delete;
|
||||
WiiSockMan& operator=(const WiiSockMan&) = delete;
|
||||
WiiSockMan(WiiSockMan&&) = delete;
|
||||
WiiSockMan& operator=(WiiSockMan&&) = delete;
|
||||
std::unordered_map<s32, WiiSocket> WiiSockets;
|
||||
s32 errno_last;
|
||||
};
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include "Core/IOS/WFS/WFSI.h"
|
||||
|
||||
#include <cinttypes>
|
||||
#include <mbedtls/aes.h>
|
||||
#include <stack>
|
||||
#include <string>
|
||||
|
@ -20,6 +21,20 @@
|
|||
#include "Core/IOS/WFS/WFSSRV.h"
|
||||
#include "DiscIO/NANDContentLoader.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
std::string TitleIdStr(u64 tid)
|
||||
{
|
||||
return StringFromFormat("%c%c%c%c", static_cast<char>(tid >> 24), static_cast<char>(tid >> 16),
|
||||
static_cast<char>(tid >> 8), static_cast<char>(tid));
|
||||
}
|
||||
|
||||
std::string GroupIdStr(u16 gid)
|
||||
{
|
||||
return StringFromFormat("%c%c", gid >> 8, gid & 0xFF);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace IOS
|
||||
{
|
||||
namespace HLE
|
||||
|
@ -64,7 +79,7 @@ void ARCUnpacker::Extract(const WriteCallback& callback)
|
|||
u32 size = Common::swap32(node + 8);
|
||||
std::string basename = string_table + name_offset;
|
||||
std::string fullname =
|
||||
current_directory.empty() ? basename : current_directory + "/" + basename;
|
||||
current_directory.empty() ? basename : current_directory + "/" + basename;
|
||||
|
||||
u8 flags = *node;
|
||||
if (flags == 1)
|
||||
|
@ -74,7 +89,7 @@ void ARCUnpacker::Extract(const WriteCallback& callback)
|
|||
else
|
||||
{
|
||||
std::vector<u8> contents(m_whole_file.data() + data_offset,
|
||||
m_whole_file.data() + data_offset + size);
|
||||
m_whole_file.data() + data_offset + size);
|
||||
callback(fullname, contents);
|
||||
}
|
||||
}
|
||||
|
@ -86,23 +101,54 @@ WFSI::WFSI(Kernel& ios, const std::string& device_name) : Device(ios, device_nam
|
|||
{
|
||||
}
|
||||
|
||||
void WFSI::SetCurrentTitleIdAndGroupId(u64 tid, u16 gid)
|
||||
{
|
||||
m_current_title_id = tid;
|
||||
m_current_group_id = gid;
|
||||
|
||||
m_current_title_id_str = TitleIdStr(tid);
|
||||
m_current_group_id_str = GroupIdStr(gid);
|
||||
}
|
||||
|
||||
void WFSI::SetImportTitleIdAndGroupId(u64 tid, u16 gid)
|
||||
{
|
||||
m_import_title_id = tid;
|
||||
m_import_group_id = gid;
|
||||
|
||||
m_import_title_id_str = TitleIdStr(tid);
|
||||
m_import_group_id_str = GroupIdStr(gid);
|
||||
}
|
||||
|
||||
IPCCommandResult WFSI::IOCtl(const IOCtlRequest& request)
|
||||
{
|
||||
s32 return_error_code = IPC_SUCCESS;
|
||||
|
||||
switch (request.request)
|
||||
{
|
||||
case IOCTL_WFSI_PREPARE_DEVICE:
|
||||
case IOCTL_WFSI_IMPORT_TITLE_INIT:
|
||||
{
|
||||
u32 tmd_addr = Memory::Read_U32(request.buffer_in);
|
||||
u32 tmd_size = Memory::Read_U32(request.buffer_in + 4);
|
||||
|
||||
INFO_LOG(IOS_WFS, "IOCTL_WFSI_PREPARE_DEVICE");
|
||||
m_patch_type = static_cast<PatchType>(Memory::Read_U32(request.buffer_in + 32));
|
||||
m_continue_install = Memory::Read_U32(request.buffer_in + 36);
|
||||
|
||||
constexpr u32 MAX_TMD_SIZE = 0x4000;
|
||||
if (tmd_size > MAX_TMD_SIZE)
|
||||
INFO_LOG(IOS_WFS, "IOCTL_WFSI_IMPORT_TITLE_INIT: patch type %d, continue install: %s",
|
||||
m_patch_type, m_continue_install ? "true" : "false");
|
||||
|
||||
if (m_patch_type == PatchType::PATCH_TYPE_2)
|
||||
{
|
||||
ERROR_LOG(IOS_WFS, "IOCTL_WFSI_PREPARE_DEVICE: TMD size too large (%d)", tmd_size);
|
||||
const std::string content_dir =
|
||||
StringFromFormat("/vol/%s/title/%s/%s/content", m_device_name.c_str(),
|
||||
m_current_group_id_str.c_str(), m_current_title_id_str.c_str());
|
||||
|
||||
File::Rename(WFS::NativePath(content_dir + "/default.dol"),
|
||||
WFS::NativePath(content_dir + "/_default.dol"));
|
||||
}
|
||||
|
||||
if (!IOS::ES::IsValidTMDSize(tmd_size))
|
||||
{
|
||||
ERROR_LOG(IOS_WFS, "IOCTL_WFSI_IMPORT_TITLE_INIT: TMD size too large (%d)", tmd_size);
|
||||
return_error_code = IPC_EINVAL;
|
||||
break;
|
||||
}
|
||||
|
@ -121,18 +167,25 @@ IPCCommandResult WFSI::IOCtl(const IOCtlRequest& request)
|
|||
memcpy(m_aes_key, ticket.GetTitleKey(m_ios.GetIOSC()).data(), sizeof(m_aes_key));
|
||||
mbedtls_aes_setkey_dec(&m_aes_ctx, m_aes_key, 128);
|
||||
|
||||
SetImportTitleIdAndGroupId(m_tmd.GetTitleId(), m_tmd.GetGroupId());
|
||||
|
||||
if (m_patch_type == PatchType::PATCH_TYPE_1)
|
||||
CancelPatchImport(m_continue_install);
|
||||
else if (m_patch_type == PatchType::NOT_A_PATCH)
|
||||
CancelTitleImport(m_continue_install);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case IOCTL_WFSI_PREPARE_PROFILE:
|
||||
m_base_extract_path = StringFromFormat("/vol/%s/tmp/", m_device_name.c_str());
|
||||
// Fall through intended.
|
||||
// Fall through intended.
|
||||
|
||||
case IOCTL_WFSI_PREPARE_CONTENT:
|
||||
{
|
||||
const char* ioctl_name = request.request == IOCTL_WFSI_PREPARE_PROFILE ?
|
||||
"IOCTL_WFSI_PREPARE_PROFILE" :
|
||||
"IOCTL_WFSI_PREPARE_CONTENT";
|
||||
"IOCTL_WFSI_PREPARE_PROFILE" :
|
||||
"IOCTL_WFSI_PREPARE_CONTENT";
|
||||
|
||||
// Initializes the IV from the index of the content in the TMD contents.
|
||||
u32 content_id = Memory::Read_U32(request.buffer_in + 8);
|
||||
|
@ -148,7 +201,7 @@ IPCCommandResult WFSI::IOCtl(const IOCtlRequest& request)
|
|||
m_aes_iv[0] = content_info.index >> 8;
|
||||
m_aes_iv[1] = content_info.index & 0xFF;
|
||||
INFO_LOG(IOS_WFS, "%s: Content id %08x found at index %d", ioctl_name, content_id,
|
||||
content_info.index);
|
||||
content_info.index);
|
||||
|
||||
m_arc_unpacker.Reset();
|
||||
break;
|
||||
|
@ -158,29 +211,29 @@ IPCCommandResult WFSI::IOCtl(const IOCtlRequest& request)
|
|||
case IOCTL_WFSI_IMPORT_CONTENT:
|
||||
{
|
||||
const char* ioctl_name = request.request == IOCTL_WFSI_IMPORT_PROFILE ?
|
||||
"IOCTL_WFSI_IMPORT_PROFILE" :
|
||||
"IOCTL_WFSI_IMPORT_CONTENT";
|
||||
"IOCTL_WFSI_IMPORT_PROFILE" :
|
||||
"IOCTL_WFSI_IMPORT_CONTENT";
|
||||
|
||||
u32 content_id = Memory::Read_U32(request.buffer_in + 0xC);
|
||||
u32 input_ptr = Memory::Read_U32(request.buffer_in + 0x10);
|
||||
u32 input_size = Memory::Read_U32(request.buffer_in + 0x14);
|
||||
INFO_LOG(IOS_WFS, "%s: %08x bytes of data at %08x from content id %d", ioctl_name, input_size,
|
||||
input_ptr, content_id);
|
||||
input_ptr, content_id);
|
||||
|
||||
std::vector<u8> decrypted(input_size);
|
||||
mbedtls_aes_crypt_cbc(&m_aes_ctx, MBEDTLS_AES_DECRYPT, input_size, m_aes_iv,
|
||||
Memory::GetPointer(input_ptr), decrypted.data());
|
||||
Memory::GetPointer(input_ptr), decrypted.data());
|
||||
|
||||
m_arc_unpacker.AddBytes(decrypted);
|
||||
break;
|
||||
}
|
||||
|
||||
case IOCTL_WFSI_FINALIZE_PROFILE:
|
||||
case IOCTL_WFSI_FINALIZE_CONTENT:
|
||||
case IOCTL_WFSI_IMPORT_CONTENT_END:
|
||||
case IOCTL_WFSI_IMPORT_PROFILE_END:
|
||||
{
|
||||
const char* ioctl_name = request.request == IOCTL_WFSI_FINALIZE_PROFILE ?
|
||||
"IOCTL_WFSI_FINALIZE_PROFILE" :
|
||||
"IOCTL_WFSI_FINALIZE_CONTENT";
|
||||
const char* ioctl_name = request.request == IOCTL_WFSI_IMPORT_PROFILE_END ?
|
||||
"IOCTL_WFSI_IMPORT_PROFILE_END" :
|
||||
"IOCTL_WFSI_IMPORT_CONTENT_END";
|
||||
INFO_LOG(IOS_WFS, "%s", ioctl_name);
|
||||
|
||||
auto callback = [this](const std::string& filename, const std::vector<u8>& bytes) {
|
||||
|
@ -204,6 +257,53 @@ IPCCommandResult WFSI::IOCtl(const IOCtlRequest& request)
|
|||
break;
|
||||
}
|
||||
|
||||
case IOCTL_WFSI_FINALIZE_TITLE_INSTALL:
|
||||
{
|
||||
std::string tmd_path;
|
||||
if (m_patch_type == NOT_A_PATCH)
|
||||
{
|
||||
std::string title_install_dir = StringFromFormat("/vol/%s/_install/%s", m_device_name.c_str(),
|
||||
m_import_title_id_str.c_str());
|
||||
std::string title_final_dir =
|
||||
StringFromFormat("/vol/%s/title/%s/%s", m_device_name.c_str(),
|
||||
m_import_group_id_str.c_str(), m_import_title_id_str.c_str());
|
||||
File::Rename(WFS::NativePath(title_install_dir), WFS::NativePath(title_final_dir));
|
||||
|
||||
tmd_path = StringFromFormat("/vol/%s/title/%s/%s/meta/%016" PRIx64 ".tmd",
|
||||
m_device_name.c_str(), m_import_group_id_str.c_str(),
|
||||
m_import_title_id_str.c_str(), m_import_title_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string patch_dir =
|
||||
StringFromFormat("/vol/%s/title/%s/%s/_patch", m_device_name.c_str(),
|
||||
m_current_group_id_str.c_str(), m_current_title_id_str.c_str());
|
||||
File::DeleteDirRecursively(WFS::NativePath(patch_dir));
|
||||
|
||||
tmd_path = StringFromFormat("/vol/%s/title/%s/%s/meta/%016" PRIx64 ".tmd",
|
||||
m_device_name.c_str(), m_current_group_id_str.c_str(),
|
||||
m_current_title_id_str.c_str(), m_import_title_id);
|
||||
}
|
||||
|
||||
File::IOFile tmd_file(WFS::NativePath(tmd_path), "wb");
|
||||
tmd_file.WriteBytes(m_tmd.GetBytes().data(), m_tmd.GetBytes().size());
|
||||
break;
|
||||
}
|
||||
|
||||
case IOCTL_WFSI_FINALIZE_PATCH_INSTALL:
|
||||
{
|
||||
INFO_LOG(IOS_WFS, "IOCTL_WFSI_FINALIZE_PATCH_INSTALL");
|
||||
if (m_patch_type != NOT_A_PATCH)
|
||||
{
|
||||
std::string current_title_dir =
|
||||
StringFromFormat("/vol/%s/title/%s/%s", m_device_name.c_str(),
|
||||
m_current_group_id_str.c_str(), m_current_title_id_str.c_str());
|
||||
std::string patch_dir = current_title_dir + "/_patch";
|
||||
File::CopyDir(WFS::NativePath(patch_dir), WFS::NativePath(current_title_dir), true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case IOCTL_WFSI_DELETE_TITLE:
|
||||
// Bytes 0-4: ??
|
||||
// Bytes 4-8: game id
|
||||
|
@ -211,26 +311,40 @@ IPCCommandResult WFSI::IOCtl(const IOCtlRequest& request)
|
|||
WARN_LOG(IOS_WFS, "IOCTL_WFSI_DELETE_TITLE: unimplemented");
|
||||
break;
|
||||
|
||||
case IOCTL_WFSI_IMPORT_TITLE:
|
||||
WARN_LOG(IOS_WFS, "IOCTL_WFSI_IMPORT_TITLE: unimplemented");
|
||||
case IOCTL_WFSI_GET_VERSION:
|
||||
INFO_LOG(IOS_WFS, "IOCTL_WFSI_GET_VERSION");
|
||||
Memory::Write_U32(0x20, request.buffer_out);
|
||||
break;
|
||||
|
||||
case IOCTL_WFSI_IMPORT_TITLE_CANCEL:
|
||||
{
|
||||
INFO_LOG(IOS_WFS, "IOCTL_WFSI_IMPORT_TITLE_CANCEL");
|
||||
|
||||
bool continue_install = Memory::Read_U32(request.buffer_in) != 0;
|
||||
if (m_patch_type == PatchType::NOT_A_PATCH)
|
||||
return_error_code = CancelTitleImport(continue_install);
|
||||
else if (m_patch_type == PatchType::PATCH_TYPE_1 || m_patch_type == PatchType::PATCH_TYPE_2)
|
||||
return_error_code = CancelPatchImport(continue_install);
|
||||
else
|
||||
return_error_code = WFS_EINVAL;
|
||||
|
||||
m_tmd = {};
|
||||
break;
|
||||
}
|
||||
|
||||
case IOCTL_WFSI_INIT:
|
||||
{
|
||||
INFO_LOG(IOS_WFS, "IOCTL_WFSI_INIT");
|
||||
if (GetIOS()->GetES()->GetTitleId(&m_title_id) < 0)
|
||||
u64 tid;
|
||||
if (GetIOS()->GetES()->GetTitleId(&tid) < 0)
|
||||
{
|
||||
ERROR_LOG(IOS_WFS, "IOCTL_WFSI_INIT: Could not get title id.");
|
||||
return_error_code = IPC_EINVAL;
|
||||
break;
|
||||
}
|
||||
m_title_id_str = StringFromFormat(
|
||||
"%c%c%c%c", static_cast<char>(m_title_id >> 24), static_cast<char>(m_title_id >> 16),
|
||||
static_cast<char>(m_title_id >> 8), static_cast<char>(m_title_id));
|
||||
|
||||
IOS::ES::TMDReader tmd = GetIOS()->GetES()->FindInstalledTMD(m_title_id);
|
||||
m_group_id = tmd.GetGroupId();
|
||||
m_group_id_str = StringFromFormat("%c%c", m_group_id >> 8, m_group_id & 0xFF);
|
||||
IOS::ES::TMDReader tmd = GetIOS()->GetES()->FindInstalledTMD(tid);
|
||||
SetCurrentTitleIdAndGroupId(tmd.GetTitleId(), tmd.GetGroupId());
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -240,18 +354,89 @@ IPCCommandResult WFSI::IOCtl(const IOCtlRequest& request)
|
|||
break;
|
||||
|
||||
case IOCTL_WFSI_APPLY_TITLE_PROFILE:
|
||||
{
|
||||
INFO_LOG(IOS_WFS, "IOCTL_WFSI_APPLY_TITLE_PROFILE");
|
||||
|
||||
m_base_extract_path = StringFromFormat("/vol/%s/_install/%s/content", m_device_name.c_str(),
|
||||
m_title_id_str.c_str());
|
||||
File::CreateFullPath(WFS::NativePath(m_base_extract_path));
|
||||
if (m_patch_type == NOT_A_PATCH)
|
||||
{
|
||||
std::string install_directory = StringFromFormat("/vol/%s/_install", m_device_name.c_str());
|
||||
if (!m_continue_install && File::IsDirectory(WFS::NativePath(install_directory)))
|
||||
{
|
||||
File::DeleteDirRecursively(WFS::NativePath(install_directory));
|
||||
}
|
||||
|
||||
m_base_extract_path = StringFromFormat("%s/%s/content", install_directory.c_str(),
|
||||
m_import_title_id_str.c_str());
|
||||
File::CreateFullPath(WFS::NativePath(m_base_extract_path));
|
||||
File::CreateDir(WFS::NativePath(m_base_extract_path));
|
||||
|
||||
for (auto dir : { "work", "meta", "save" })
|
||||
{
|
||||
std::string path = StringFromFormat("%s/%s/%s", install_directory.c_str(),
|
||||
m_import_title_id_str.c_str(), dir);
|
||||
File::CreateDir(WFS::NativePath(path));
|
||||
}
|
||||
|
||||
std::string group_path = StringFromFormat("/vol/%s/title/%s", m_device_name.c_str(),
|
||||
m_import_group_id_str.c_str());
|
||||
File::CreateFullPath(WFS::NativePath(group_path));
|
||||
File::CreateDir(WFS::NativePath(group_path));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_base_extract_path =
|
||||
StringFromFormat("/vol/%s/title/%s/%s/_patch/content", m_device_name.c_str(),
|
||||
m_current_group_id_str.c_str(), m_current_title_id_str.c_str());
|
||||
File::CreateFullPath(WFS::NativePath(m_base_extract_path));
|
||||
File::CreateDir(WFS::NativePath(m_base_extract_path));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case IOCTL_WFSI_GET_TMD:
|
||||
{
|
||||
u64 subtitle_id = Memory::Read_U64(request.buffer_in);
|
||||
u32 address = Memory::Read_U32(request.buffer_in + 24);
|
||||
INFO_LOG(IOS_WFS, "IOCTL_WFSI_GET_TMD: subtitle ID %016" PRIx64, subtitle_id);
|
||||
|
||||
u32 tmd_size;
|
||||
return_error_code =
|
||||
GetTmd(m_current_group_id, m_current_title_id, subtitle_id, address, &tmd_size);
|
||||
Memory::Write_U32(tmd_size, request.buffer_out);
|
||||
break;
|
||||
}
|
||||
|
||||
case IOCTL_WFSI_GET_TMD_ABSOLUTE:
|
||||
{
|
||||
u64 subtitle_id = Memory::Read_U64(request.buffer_in);
|
||||
u32 address = Memory::Read_U32(request.buffer_in + 24);
|
||||
u16 group_id = Memory::Read_U16(request.buffer_in + 36);
|
||||
u32 title_id = Memory::Read_U32(request.buffer_in + 32);
|
||||
INFO_LOG(IOS_WFS, "IOCTL_WFSI_GET_TMD_ABSOLUTE: tid %08x, gid %04x, subtitle ID %016" PRIx64,
|
||||
title_id, group_id, subtitle_id);
|
||||
|
||||
u32 tmd_size;
|
||||
return_error_code = GetTmd(group_id, title_id, subtitle_id, address, &tmd_size);
|
||||
Memory::Write_U32(tmd_size, request.buffer_out);
|
||||
break;
|
||||
}
|
||||
|
||||
case IOCTL_WFSI_SET_FST_BUFFER:
|
||||
{
|
||||
INFO_LOG(IOS_WFS, "IOCTL_WFSI_SET_FST_BUFFER: address %08x, size %08x", request.buffer_in,
|
||||
request.buffer_in_size);
|
||||
break;
|
||||
}
|
||||
|
||||
case IOCTL_WFSI_NOOP:
|
||||
break;
|
||||
|
||||
case IOCTL_WFSI_LOAD_DOL:
|
||||
{
|
||||
std::string path = StringFromFormat("/vol/%s/title/%s/%s/content", m_device_name.c_str(),
|
||||
m_group_id_str.c_str(), m_title_id_str.c_str());
|
||||
std::string path =
|
||||
StringFromFormat("/vol/%s/title/%s/%s/content", m_device_name.c_str(),
|
||||
m_current_group_id_str.c_str(), m_current_title_id_str.c_str());
|
||||
|
||||
u32 dol_addr = Memory::Read_U32(request.buffer_in + 0x18);
|
||||
u32 max_dol_size = Memory::Read_U32(request.buffer_in + 0x14);
|
||||
|
@ -267,13 +452,13 @@ IPCCommandResult WFSI::IOCtl(const IOCtlRequest& request)
|
|||
}
|
||||
|
||||
INFO_LOG(IOS_WFS, "IOCTL_WFSI_LOAD_DOL: loading %s at address %08x (size %d)", path.c_str(),
|
||||
dol_addr, max_dol_size);
|
||||
dol_addr, max_dol_size);
|
||||
|
||||
File::IOFile fp(WFS::NativePath(path), "rb");
|
||||
if (!fp)
|
||||
{
|
||||
WARN_LOG(IOS_WFS, "IOCTL_WFSI_LOAD_DOL: no such file or directory: %s", path.c_str());
|
||||
return_error_code = WFSI_ENOENT;
|
||||
return_error_code = WFS_ENOENT;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -291,6 +476,23 @@ IPCCommandResult WFSI::IOCtl(const IOCtlRequest& request)
|
|||
break;
|
||||
}
|
||||
|
||||
case IOCTL_WFSI_CHECK_HAS_SPACE:
|
||||
WARN_LOG(IOS_WFS, "IOCTL_WFSI_CHECK_HAS_SPACE: returning true");
|
||||
|
||||
// TODO(wfs): implement this properly.
|
||||
// 1 is returned if there is free space, 0 otherwise.
|
||||
//
|
||||
// WFSI builds a path depending on the import state
|
||||
// /vol/VOLUME_ID/title/GROUP_ID/GAME_ID
|
||||
// /vol/VOLUME_ID/_install/GAME_ID
|
||||
// then removes everything after the last path separator ('/')
|
||||
// it then calls WFSISrvGetFreeBlkNum (ioctl 0x5a, aliased to 0x5b) with that path.
|
||||
// If the ioctl fails, WFSI returns 0.
|
||||
// If the ioctl succeeds, WFSI returns 0 or 1 depending on the three u32s in the input buffer
|
||||
// and the three u32s returned by WFSSRV (TODO: figure out what it does)
|
||||
return_error_code = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
// TODO(wfs): Should be returning an error. However until we have
|
||||
// everything properly stubbed it's easier to simulate the methods
|
||||
|
@ -302,6 +504,75 @@ IPCCommandResult WFSI::IOCtl(const IOCtlRequest& request)
|
|||
|
||||
return GetDefaultReply(return_error_code);
|
||||
}
|
||||
|
||||
u32 WFSI::GetTmd(u16 group_id, u32 title_id, u64 subtitle_id, u32 address, u32* size) const
|
||||
{
|
||||
std::string path =
|
||||
StringFromFormat("/vol/%s/title/%s/%s/meta/%016" PRIx64 ".tmd", m_device_name.c_str(),
|
||||
GroupIdStr(group_id).c_str(), TitleIdStr(title_id).c_str(), subtitle_id);
|
||||
File::IOFile fp(WFS::NativePath(path), "rb");
|
||||
if (!fp)
|
||||
{
|
||||
WARN_LOG(IOS_WFS, "GetTmd: no such file or directory: %s", path.c_str());
|
||||
return WFS_ENOENT;
|
||||
}
|
||||
if (address)
|
||||
{
|
||||
fp.ReadBytes(Memory::GetPointer(address), fp.GetSize());
|
||||
}
|
||||
*size = fp.GetSize();
|
||||
return IPC_SUCCESS;
|
||||
}
|
||||
|
||||
static s32 DeleteTemporaryFiles(const std::string& device_name, u64 title_id)
|
||||
{
|
||||
File::Delete(WFS::NativePath(
|
||||
StringFromFormat("/vol/%s/tmp/%016" PRIx64 ".ini", device_name.c_str(), title_id)));
|
||||
File::Delete(WFS::NativePath(
|
||||
StringFromFormat("/vol/%s/tmp/%016" PRIx64 ".ppcini", device_name.c_str(), title_id)));
|
||||
return IPC_SUCCESS;
|
||||
}
|
||||
|
||||
s32 WFSI::CancelTitleImport(bool continue_install)
|
||||
{
|
||||
m_arc_unpacker.Reset();
|
||||
|
||||
if (!continue_install)
|
||||
{
|
||||
File::DeleteDirRecursively(
|
||||
WFS::NativePath(StringFromFormat("/vol/%s/_install", m_device_name.c_str())));
|
||||
}
|
||||
|
||||
DeleteTemporaryFiles(m_device_name, m_import_title_id);
|
||||
|
||||
return IPC_SUCCESS;
|
||||
}
|
||||
|
||||
s32 WFSI::CancelPatchImport(bool continue_install)
|
||||
{
|
||||
m_arc_unpacker.Reset();
|
||||
|
||||
if (!continue_install)
|
||||
{
|
||||
File::DeleteDirRecursively(WFS::NativePath(
|
||||
StringFromFormat("/vol/%s/title/%s/%s/_patch", m_device_name.c_str(),
|
||||
m_current_group_id_str.c_str(), m_current_title_id_str.c_str())));
|
||||
|
||||
if (m_patch_type == PatchType::PATCH_TYPE_2)
|
||||
{
|
||||
// Move back _default.dol to default.dol.
|
||||
const std::string content_dir =
|
||||
StringFromFormat("/vol/%s/title/%s/%s/content", m_device_name.c_str(),
|
||||
m_current_group_id_str.c_str(), m_current_title_id_str.c_str());
|
||||
File::Rename(WFS::NativePath(content_dir + "/_default.dol"),
|
||||
WFS::NativePath(content_dir + "/default.dol"));
|
||||
}
|
||||
}
|
||||
|
||||
DeleteTemporaryFiles(m_device_name, m_current_title_id);
|
||||
|
||||
return IPC_SUCCESS;
|
||||
}
|
||||
} // namespace Device
|
||||
} // namespace HLE
|
||||
} // namespace IOS
|
||||
|
|
|
@ -44,6 +44,14 @@ public:
|
|||
IPCCommandResult IOCtl(const IOCtlRequest& request) override;
|
||||
|
||||
private:
|
||||
u32 GetTmd(u16 group_id, u32 title_id, u64 subtitle_id, u32 address, u32* size) const;
|
||||
|
||||
void SetCurrentTitleIdAndGroupId(u64 tid, u16 gid);
|
||||
void SetImportTitleIdAndGroupId(u64 tid, u16 gid);
|
||||
|
||||
s32 CancelTitleImport(bool continue_install);
|
||||
s32 CancelPatchImport(bool continue_install);
|
||||
|
||||
std::string m_device_name;
|
||||
|
||||
mbedtls_aes_context m_aes_ctx;
|
||||
|
@ -52,39 +60,69 @@ private:
|
|||
|
||||
IOS::ES::TMDReader m_tmd;
|
||||
std::string m_base_extract_path;
|
||||
u64 m_title_id;
|
||||
std::string m_title_id_str;
|
||||
u16 m_group_id;
|
||||
std::string m_group_id_str;
|
||||
|
||||
u64 m_current_title_id;
|
||||
std::string m_current_title_id_str;
|
||||
u16 m_current_group_id;
|
||||
std::string m_current_group_id_str;
|
||||
u64 m_import_title_id;
|
||||
std::string m_import_title_id_str;
|
||||
u16 m_import_group_id;
|
||||
std::string m_import_group_id_str;
|
||||
|
||||
// Set on IMPORT_TITLE_INIT when the next profile application should not delete
|
||||
// temporary install files.
|
||||
bool m_continue_install = false;
|
||||
|
||||
// Set on IMPORT_TITLE_INIT to indicate that the install is a patch and not a
|
||||
// standalone title.
|
||||
enum PatchType
|
||||
{
|
||||
NOT_A_PATCH,
|
||||
PATCH_TYPE_1,
|
||||
PATCH_TYPE_2,
|
||||
};
|
||||
PatchType m_patch_type = NOT_A_PATCH;
|
||||
|
||||
ARCUnpacker m_arc_unpacker;
|
||||
|
||||
enum
|
||||
{
|
||||
WFSI_ENOENT = -12000,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
IOCTL_WFSI_PREPARE_DEVICE = 0x02,
|
||||
IOCTL_WFSI_IMPORT_TITLE_INIT = 0x02,
|
||||
|
||||
IOCTL_WFSI_PREPARE_CONTENT = 0x03,
|
||||
IOCTL_WFSI_IMPORT_CONTENT = 0x04,
|
||||
IOCTL_WFSI_FINALIZE_CONTENT = 0x05,
|
||||
IOCTL_WFSI_IMPORT_CONTENT_END = 0x05,
|
||||
|
||||
IOCTL_WFSI_FINALIZE_TITLE_INSTALL = 0x06,
|
||||
|
||||
IOCTL_WFSI_DELETE_TITLE = 0x17,
|
||||
IOCTL_WFSI_IMPORT_TITLE = 0x2f,
|
||||
|
||||
IOCTL_WFSI_GET_VERSION = 0x1b,
|
||||
|
||||
IOCTL_WFSI_IMPORT_TITLE_CANCEL = 0x2f,
|
||||
|
||||
IOCTL_WFSI_INIT = 0x81,
|
||||
IOCTL_WFSI_SET_DEVICE_NAME = 0x82,
|
||||
|
||||
IOCTL_WFSI_PREPARE_PROFILE = 0x86,
|
||||
IOCTL_WFSI_IMPORT_PROFILE = 0x87,
|
||||
IOCTL_WFSI_FINALIZE_PROFILE = 0x88,
|
||||
IOCTL_WFSI_IMPORT_PROFILE_END = 0x88,
|
||||
|
||||
IOCTL_WFSI_APPLY_TITLE_PROFILE = 0x89,
|
||||
|
||||
IOCTL_WFSI_GET_TMD = 0x8a,
|
||||
IOCTL_WFSI_GET_TMD_ABSOLUTE = 0x8b,
|
||||
|
||||
IOCTL_WFSI_SET_FST_BUFFER = 0x8e,
|
||||
|
||||
IOCTL_WFSI_NOOP = 0x8f,
|
||||
|
||||
IOCTL_WFSI_LOAD_DOL = 0x90,
|
||||
|
||||
IOCTL_WFSI_FINALIZE_PATCH_INSTALL = 0x91,
|
||||
|
||||
IOCTL_WFSI_CHECK_HAS_SPACE = 0x95,
|
||||
};
|
||||
};
|
||||
} // namespace Device
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include "Core/IOS/WFS/WFSSRV.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cinttypes>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
@ -58,7 +59,7 @@ IPCCommandResult WFSSRV::IOCtl(const IOCtlRequest& request)
|
|||
// Close all hanging attach/detach ioctls with an appropriate error code.
|
||||
for (auto address : m_hanging)
|
||||
{
|
||||
IOCtlRequest hanging_request{address};
|
||||
IOCtlRequest hanging_request{ address };
|
||||
Memory::Write_U32(0x80000000, hanging_request.buffer_out);
|
||||
Memory::Write_U32(0, hanging_request.buffer_out + 4);
|
||||
Memory::Write_U32(0, hanging_request.buffer_out + 8);
|
||||
|
@ -100,6 +101,29 @@ IPCCommandResult WFSSRV::IOCtl(const IOCtlRequest& request)
|
|||
INFO_LOG(IOS_WFS, "IOCTL_WFS_FLUSH: doing nothing");
|
||||
break;
|
||||
|
||||
case IOCTL_WFS_MKDIR:
|
||||
{
|
||||
std::string path = NormalizePath(
|
||||
Memory::GetString(request.buffer_in + 34, Memory::Read_U16(request.buffer_in + 32)));
|
||||
std::string native_path = WFS::NativePath(path);
|
||||
|
||||
if (File::Exists(native_path))
|
||||
{
|
||||
INFO_LOG(IOS_WFS, "IOCTL_WFS_MKDIR(%s): already exists", path.c_str());
|
||||
return_error_code = WFS_EEXIST;
|
||||
}
|
||||
else if (!File::CreateDir(native_path))
|
||||
{
|
||||
INFO_LOG(IOS_WFS, "IOCTL_WFS_MKDIR(%s): no such file or directory", path.c_str());
|
||||
return_error_code = WFS_ENOENT;
|
||||
}
|
||||
else
|
||||
{
|
||||
INFO_LOG(IOS_WFS, "IOCTL_WFS_MKDIR(%s): directory created", path.c_str());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO(wfs): Globbing is not really implemented, we just fake the one case
|
||||
// (listing /vol/*) which is required to get the installer to work.
|
||||
case IOCTL_WFS_GLOB_START:
|
||||
|
@ -120,13 +144,13 @@ IPCCommandResult WFSSRV::IOCtl(const IOCtlRequest& request)
|
|||
|
||||
case IOCTL_WFS_SET_HOMEDIR:
|
||||
m_home_directory =
|
||||
Memory::GetString(request.buffer_in + 2, Memory::Read_U16(request.buffer_in));
|
||||
Memory::GetString(request.buffer_in + 2, Memory::Read_U16(request.buffer_in));
|
||||
INFO_LOG(IOS_WFS, "IOCTL_WFS_SET_HOMEDIR: %s", m_home_directory.c_str());
|
||||
break;
|
||||
|
||||
case IOCTL_WFS_CHDIR:
|
||||
m_current_directory =
|
||||
Memory::GetString(request.buffer_in + 2, Memory::Read_U16(request.buffer_in));
|
||||
Memory::GetString(request.buffer_in + 2, Memory::Read_U16(request.buffer_in));
|
||||
INFO_LOG(IOS_WFS, "IOCTL_WFS_CHDIR: %s", m_current_directory.c_str());
|
||||
break;
|
||||
|
||||
|
@ -136,9 +160,48 @@ IPCCommandResult WFSSRV::IOCtl(const IOCtlRequest& request)
|
|||
Memory::CopyToEmu(request.buffer_out + 2, m_home_directory.data(), m_home_directory.size());
|
||||
break;
|
||||
|
||||
case IOCTL_WFS_GET_ATTRIBUTES:
|
||||
{
|
||||
std::string path = NormalizePath(
|
||||
Memory::GetString(request.buffer_in + 2, Memory::Read_U16(request.buffer_in)));
|
||||
std::string native_path = WFS::NativePath(path);
|
||||
Memory::Memset(0, request.buffer_out, request.buffer_out_size);
|
||||
if (!File::Exists(native_path))
|
||||
{
|
||||
INFO_LOG(IOS_WFS, "IOCTL_WFS_GET_ATTRIBUTES(%s): no such file or directory", path.c_str());
|
||||
return_error_code = WFS_ENOENT;
|
||||
}
|
||||
else if (File::IsDirectory(native_path))
|
||||
{
|
||||
INFO_LOG(IOS_WFS, "IOCTL_WFS_GET_ATTRIBUTES(%s): directory", path.c_str());
|
||||
Memory::Write_U32(0x80000000, request.buffer_out + 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
u32 size = static_cast<u32>(File::GetSize(native_path));
|
||||
INFO_LOG(IOS_WFS, "IOCTL_WFS_GET_ATTRIBUTES(%s): file with size %d", path.c_str(), size);
|
||||
Memory::Write_U32(size, request.buffer_out);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case IOCTL_WFS_RENAME:
|
||||
case IOCTL_WFS_RENAME_2:
|
||||
{
|
||||
const std::string source_path =
|
||||
Memory::GetString(request.buffer_in + 2, Memory::Read_U16(request.buffer_in));
|
||||
const std::string dest_path =
|
||||
Memory::GetString(request.buffer_in + 512 + 2, Memory::Read_U16(request.buffer_in + 512));
|
||||
return_error_code = Rename(source_path, dest_path);
|
||||
break;
|
||||
}
|
||||
|
||||
case IOCTL_WFS_CREATE_OPEN:
|
||||
case IOCTL_WFS_OPEN:
|
||||
{
|
||||
u32 mode = Memory::Read_U32(request.buffer_in);
|
||||
const char* ioctl_name =
|
||||
request.request == IOCTL_WFS_OPEN ? "IOCTL_WFS_OPEN" : "IOCTL_WFS_CREATE_OPEN";
|
||||
u32 mode = request.request == IOCTL_WFS_OPEN ? Memory::Read_U32(request.buffer_in) : 2;
|
||||
u16 path_len = Memory::Read_U16(request.buffer_in + 0x20);
|
||||
std::string path = Memory::GetString(request.buffer_in + 0x22, path_len);
|
||||
|
||||
|
@ -153,14 +216,21 @@ IPCCommandResult WFSSRV::IOCtl(const IOCtlRequest& request)
|
|||
|
||||
if (!fd_obj->Open())
|
||||
{
|
||||
ERROR_LOG(IOS_WFS, "IOCTL_WFS_OPEN(%s, %d): error opening file", path.c_str(), mode);
|
||||
ERROR_LOG(IOS_WFS, "%s(%s, %d): error opening file", ioctl_name, path.c_str(), mode);
|
||||
ReleaseFileDescriptor(fd);
|
||||
return_error_code = WFS_ENOENT;
|
||||
break;
|
||||
}
|
||||
|
||||
INFO_LOG(IOS_WFS, "IOCTL_WFS_OPEN(%s, %d) -> %d", path.c_str(), mode, fd);
|
||||
Memory::Write_U16(fd, request.buffer_out + 0x14);
|
||||
INFO_LOG(IOS_WFS, "%s(%s, %d) -> %d", ioctl_name, path.c_str(), mode, fd);
|
||||
if (request.request == IOCTL_WFS_OPEN)
|
||||
{
|
||||
Memory::Write_U16(fd, request.buffer_out + 0x14);
|
||||
}
|
||||
else
|
||||
{
|
||||
Memory::Write_U16(fd, request.buffer_out);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -194,6 +264,16 @@ IPCCommandResult WFSSRV::IOCtl(const IOCtlRequest& request)
|
|||
break;
|
||||
}
|
||||
|
||||
case IOCTL_WFS_CLOSE_2:
|
||||
{
|
||||
// TODO(wfs): Figure out the exact semantics difference from the other
|
||||
// close.
|
||||
u16 fd = Memory::Read_U16(request.buffer_in + 0x4);
|
||||
INFO_LOG(IOS_WFS, "IOCTL_WFS_CLOSE_2(%d)", fd);
|
||||
ReleaseFileDescriptor(fd);
|
||||
break;
|
||||
}
|
||||
|
||||
case IOCTL_WFS_READ:
|
||||
case IOCTL_WFS_READ_ABSOLUTE:
|
||||
{
|
||||
|
@ -230,11 +310,50 @@ IPCCommandResult WFSSRV::IOCtl(const IOCtlRequest& request)
|
|||
}
|
||||
|
||||
INFO_LOG(IOS_WFS, "IOCTL_WFS_READ: read %zd bytes from FD %d (%s)", read_bytes, fd,
|
||||
fd_obj->path.c_str());
|
||||
fd_obj->path.c_str());
|
||||
return_error_code = static_cast<int>(read_bytes);
|
||||
break;
|
||||
}
|
||||
|
||||
case IOCTL_WFS_WRITE:
|
||||
case IOCTL_WFS_WRITE_ABSOLUTE:
|
||||
{
|
||||
u32 addr = Memory::Read_U32(request.buffer_in);
|
||||
u32 position = Memory::Read_U32(request.buffer_in + 4); // Only for absolute.
|
||||
u16 fd = Memory::Read_U16(request.buffer_in + 0xC);
|
||||
u32 size = Memory::Read_U32(request.buffer_in + 8);
|
||||
|
||||
bool absolute = request.request == IOCTL_WFS_WRITE_ABSOLUTE;
|
||||
|
||||
FileDescriptor* fd_obj = FindFileDescriptor(fd);
|
||||
if (fd_obj == nullptr)
|
||||
{
|
||||
ERROR_LOG(IOS_WFS, "IOCTL_WFS_WRITE: invalid file descriptor %d", fd);
|
||||
return_error_code = WFS_EBADFD;
|
||||
break;
|
||||
}
|
||||
|
||||
u64 previous_position = fd_obj->file.Tell();
|
||||
if (absolute)
|
||||
{
|
||||
fd_obj->file.Seek(position, SEEK_SET);
|
||||
}
|
||||
fd_obj->file.WriteArray(Memory::GetPointer(addr), size);
|
||||
// TODO(wfs): Handle write errors.
|
||||
if (absolute)
|
||||
{
|
||||
fd_obj->file.Seek(previous_position, SEEK_SET);
|
||||
}
|
||||
else
|
||||
{
|
||||
fd_obj->position += size;
|
||||
}
|
||||
|
||||
INFO_LOG(IOS_WFS, "IOCTL_WFS_WRITE: written %d bytes from FD %d (%s)", size, fd,
|
||||
fd_obj->path.c_str());
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
// TODO(wfs): Should be returning -3. However until we have everything
|
||||
// properly stubbed it's easier to simulate the methods succeeding.
|
||||
|
@ -246,6 +365,26 @@ IPCCommandResult WFSSRV::IOCtl(const IOCtlRequest& request)
|
|||
return GetDefaultReply(return_error_code);
|
||||
}
|
||||
|
||||
s32 WFSSRV::Rename(std::string source, std::string dest) const
|
||||
{
|
||||
source = NormalizePath(source);
|
||||
dest = NormalizePath(dest);
|
||||
|
||||
INFO_LOG(IOS_WFS, "IOCTL_WFS_RENAME: %s to %s", source.c_str(), dest.c_str());
|
||||
|
||||
const bool opened = std::any_of(m_fds.begin(), m_fds.end(),
|
||||
[&](const auto& fd) { return fd.in_use && fd.path == source; });
|
||||
|
||||
if (opened)
|
||||
return WFS_FILE_IS_OPENED;
|
||||
|
||||
// TODO(wfs): Handle other rename failures
|
||||
if (!File::Rename(WFS::NativePath(source), WFS::NativePath(dest)))
|
||||
return WFS_ENOENT;
|
||||
|
||||
return IPC_SUCCESS;
|
||||
}
|
||||
|
||||
std::string WFSSRV::NormalizePath(const std::string& path) const
|
||||
{
|
||||
std::string expanded;
|
||||
|
|
|
@ -22,6 +22,15 @@ namespace WFS
|
|||
std::string NativePath(const std::string& wfs_path);
|
||||
}
|
||||
|
||||
enum
|
||||
{
|
||||
WFS_EINVAL = -10003, // Invalid argument.
|
||||
WFS_EBADFD = -10026, // Invalid file descriptor.
|
||||
WFS_EEXIST = -10027, // File already exists.
|
||||
WFS_ENOENT = -10028, // No such file or directory.
|
||||
WFS_FILE_IS_OPENED = -10032, // Cannot perform operation on an opened file.
|
||||
};
|
||||
|
||||
namespace Device
|
||||
{
|
||||
class WFSSRV : public Device
|
||||
|
@ -31,6 +40,8 @@ public:
|
|||
|
||||
IPCCommandResult IOCtl(const IOCtlRequest& request) override;
|
||||
|
||||
s32 Rename(std::string source, std::string dest) const;
|
||||
|
||||
private:
|
||||
// WFS device name, e.g. msc01/msc02.
|
||||
std::string m_device_name;
|
||||
|
@ -59,7 +70,9 @@ private:
|
|||
IOCTL_WFS_GET_HOMEDIR = 0x12,
|
||||
IOCTL_WFS_GETCWD = 0x13,
|
||||
IOCTL_WFS_DELETE = 0x15,
|
||||
IOCTL_WFS_RENAME = 0x16,
|
||||
IOCTL_WFS_GET_ATTRIBUTES = 0x17,
|
||||
IOCTL_WFS_CREATE_OPEN = 0x19,
|
||||
IOCTL_WFS_OPEN = 0x1A,
|
||||
IOCTL_WFS_GET_SIZE = 0x1B,
|
||||
IOCTL_WFS_CLOSE = 0x1E,
|
||||
|
@ -67,13 +80,10 @@ private:
|
|||
IOCTL_WFS_WRITE = 0x22,
|
||||
IOCTL_WFS_ATTACH_DETACH = 0x2d,
|
||||
IOCTL_WFS_ATTACH_DETACH_2 = 0x2e,
|
||||
IOCTL_WFS_RENAME_2 = 0x41,
|
||||
IOCTL_WFS_CLOSE_2 = 0x47,
|
||||
IOCTL_WFS_READ_ABSOLUTE = 0x48,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
WFS_EBADFD = -10026, // Invalid file descriptor.
|
||||
WFS_ENOENT = -10028, // No such file or directory.
|
||||
IOCTL_WFS_WRITE_ABSOLUTE = 0x49,
|
||||
};
|
||||
|
||||
struct FileDescriptor
|
||||
|
|
|
@ -4,8 +4,7 @@
|
|||
|
||||
// Originally written by Sven Peter <sven@fail0verflow.com> for anergistic.
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdarg.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
@ -20,8 +19,14 @@
|
|||
#include <sys/un.h>
|
||||
#endif
|
||||
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Core/HW/CPU.h"
|
||||
#include "Core/HW/Memmap.h"
|
||||
#include "Core/Host.h"
|
||||
#include "Core/PowerPC/GDBStub.h"
|
||||
#include "Core/PowerPC/Gekko.h"
|
||||
#include "Core/PowerPC/PPCCache.h"
|
||||
#include "Core/PowerPC/PowerPC.h"
|
||||
|
||||
#define GDB_BFR_MAX 10000
|
||||
#define GDB_MAX_BP 10
|
||||
|
@ -276,8 +281,8 @@ static void gdb_read_command()
|
|||
if (chk_calc != chk_read)
|
||||
{
|
||||
ERROR_LOG(GDB_STUB,
|
||||
"gdb: invalid checksum: calculated %02x and read %02x for $%s# (length: %d)",
|
||||
chk_calc, chk_read, cmd_bfr, cmd_len);
|
||||
"gdb: invalid checksum: calculated %02x and read %02x for $%s# (length: %d)",
|
||||
chk_calc, chk_read, cmd_bfr, cmd_len);
|
||||
cmd_len = 0;
|
||||
|
||||
gdb_nak();
|
||||
|
@ -368,7 +373,7 @@ static void gdb_handle_query()
|
|||
static void gdb_handle_set_thread()
|
||||
{
|
||||
if (memcmp(cmd_bfr, "Hg0", 3) == 0 || memcmp(cmd_bfr, "Hc-1", 4) == 0 ||
|
||||
memcmp(cmd_bfr, "Hc0", 4) == 0 || memcmp(cmd_bfr, "Hc1", 4) == 0)
|
||||
memcmp(cmd_bfr, "Hc0", 4) == 0 || memcmp(cmd_bfr, "Hc1", 4) == 0)
|
||||
return gdb_reply("OK");
|
||||
gdb_reply("E01");
|
||||
}
|
||||
|
@ -487,7 +492,7 @@ static void gdb_read_registers()
|
|||
/*
|
||||
for (i = 0; i < 32; i++)
|
||||
{
|
||||
wbe32hex(bufptr + i*8, riPS0(i));
|
||||
wbe32hex(bufptr + i*8, riPS0(i));
|
||||
}
|
||||
bufptr += 32 * 8;
|
||||
wbe32hex(bufptr, PC); bufptr += 4;
|
||||
|
@ -804,7 +809,7 @@ WSADATA InitData;
|
|||
// exported functions
|
||||
|
||||
static void gdb_init_generic(int domain, const sockaddr* server_addr, socklen_t server_addrlen,
|
||||
sockaddr* client_addr, socklen_t* client_addrlen);
|
||||
sockaddr* client_addr, socklen_t* client_addrlen);
|
||||
|
||||
#ifndef _WIN32
|
||||
void gdb_init_local(const char* socket)
|
||||
|
@ -831,7 +836,7 @@ void gdb_init(u32 port)
|
|||
socklen_t client_addrlen = sizeof(saddr_client);
|
||||
|
||||
gdb_init_generic(PF_INET, (const sockaddr*)&saddr_server, sizeof(saddr_server),
|
||||
(sockaddr*)&saddr_client, &client_addrlen);
|
||||
(sockaddr*)&saddr_client, &client_addrlen);
|
||||
|
||||
saddr_client.sin_addr.s_addr = ntohl(saddr_client.sin_addr.s_addr);
|
||||
/*if (((saddr_client.sin_addr.s_addr >> 24) & 0xff) != 127 ||
|
||||
|
@ -843,7 +848,7 @@ void gdb_init(u32 port)
|
|||
}
|
||||
|
||||
static void gdb_init_generic(int domain, const sockaddr* server_addr, socklen_t server_addrlen,
|
||||
sockaddr* client_addr, socklen_t* client_addrlen)
|
||||
sockaddr* client_addr, socklen_t* client_addrlen)
|
||||
{
|
||||
int on;
|
||||
#ifdef _WIN32
|
||||
|
|
|
@ -6,14 +6,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/Thread.h"
|
||||
|
||||
#include "Core/HW/CPU.h"
|
||||
#include "Core/HW/Memmap.h"
|
||||
#include "Core/PowerPC/PowerPC.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#define SIGTRAP 5
|
||||
|
|
|
@ -403,7 +403,7 @@ void Jit64::mtmsr(UGeckoInstruction inst)
|
|||
// external exceptions when going out of mtmsr in order to execute delayed
|
||||
// interrupts as soon as possible.
|
||||
TEST(32, PPCSTATE(msr), Imm32(0x8000));
|
||||
FixupBranch eeDisabled = J_CC(CC_Z);
|
||||
FixupBranch eeDisabled = J_CC(CC_Z, true);
|
||||
|
||||
TEST(32, PPCSTATE(Exceptions),
|
||||
Imm32(EXCEPTION_EXTERNAL_INT | EXCEPTION_PERFORMANCE_MONITOR | EXCEPTION_DECREMENTER));
|
||||
|
|
|
@ -77,8 +77,8 @@ void JitArm64::FlushCarry()
|
|||
js.carryFlagSet = false;
|
||||
}
|
||||
|
||||
void JitArm64::reg_imm(u32 d, u32 a, u32 value, u32 (*do_op)(u32, u32),
|
||||
void (ARM64XEmitter::*op)(ARM64Reg, ARM64Reg, u64, ARM64Reg), bool Rc)
|
||||
void JitArm64::reg_imm(u32 d, u32 a, u32 value, u32(*do_op)(u32, u32),
|
||||
void (ARM64XEmitter::*op)(ARM64Reg, ARM64Reg, u64, ARM64Reg), bool Rc)
|
||||
{
|
||||
if (gpr.IsImm(a))
|
||||
{
|
||||
|
@ -116,7 +116,7 @@ static constexpr u32 BitXOR(u32 a, u32 b)
|
|||
void JitArm64::arith_imm(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
u32 a = inst.RA, s = inst.RS;
|
||||
|
||||
switch (inst.OPCD)
|
||||
|
@ -150,7 +150,7 @@ void JitArm64::arith_imm(UGeckoInstruction inst)
|
|||
void JitArm64::addix(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
u32 d = inst.RD, a = inst.RA;
|
||||
|
||||
u32 imm = (u32)(s32)inst.SIMM_16;
|
||||
|
@ -184,7 +184,7 @@ void JitArm64::addix(UGeckoInstruction inst)
|
|||
void JitArm64::boolX(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
int a = inst.RA, s = inst.RS, b = inst.RB;
|
||||
|
||||
if (gpr.IsImm(s) && gpr.IsImm(b))
|
||||
|
@ -294,7 +294,7 @@ void JitArm64::boolX(UGeckoInstruction inst)
|
|||
void JitArm64::addx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
FALLBACK_IF(inst.OE);
|
||||
|
||||
int a = inst.RA, b = inst.RB, d = inst.RD;
|
||||
|
@ -329,7 +329,7 @@ void JitArm64::addx(UGeckoInstruction inst)
|
|||
void JitArm64::extsXx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
int a = inst.RA, s = inst.RS;
|
||||
int size = inst.SUBOP10 == 922 ? 16 : 8;
|
||||
|
||||
|
@ -351,7 +351,7 @@ void JitArm64::extsXx(UGeckoInstruction inst)
|
|||
void JitArm64::cntlzwx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
int a = inst.RA;
|
||||
int s = inst.RS;
|
||||
|
||||
|
@ -373,7 +373,7 @@ void JitArm64::cntlzwx(UGeckoInstruction inst)
|
|||
void JitArm64::negx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
int a = inst.RA;
|
||||
int d = inst.RD;
|
||||
|
||||
|
@ -397,7 +397,7 @@ void JitArm64::negx(UGeckoInstruction inst)
|
|||
void JitArm64::cmp(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
|
||||
int crf = inst.CRFD;
|
||||
u32 a = inst.RA, b = inst.RB;
|
||||
|
@ -434,7 +434,7 @@ void JitArm64::cmp(UGeckoInstruction inst)
|
|||
void JitArm64::cmpl(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
|
||||
int crf = inst.CRFD;
|
||||
u32 a = inst.RA, b = inst.RB;
|
||||
|
@ -462,7 +462,7 @@ void JitArm64::cmpl(UGeckoInstruction inst)
|
|||
void JitArm64::cmpi(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
|
||||
u32 a = inst.RA;
|
||||
s64 B = inst.SIMM_16;
|
||||
|
@ -491,7 +491,7 @@ void JitArm64::cmpi(UGeckoInstruction inst)
|
|||
void JitArm64::cmpli(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
u32 a = inst.RA;
|
||||
u64 B = inst.UIMM;
|
||||
int crf = inst.CRFD;
|
||||
|
@ -518,7 +518,7 @@ void JitArm64::cmpli(UGeckoInstruction inst)
|
|||
void JitArm64::rlwinmx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
u32 a = inst.RA, s = inst.RS;
|
||||
|
||||
u32 mask = Helper_Mask(inst.MB, inst.ME);
|
||||
|
@ -532,7 +532,12 @@ void JitArm64::rlwinmx(UGeckoInstruction inst)
|
|||
|
||||
gpr.BindToRegister(a, a == s);
|
||||
|
||||
if (!inst.SH)
|
||||
if (!inst.SH && mask == 0xFFFFFFFF)
|
||||
{
|
||||
if (a != s)
|
||||
MOV(gpr.R(a), gpr.R(s));
|
||||
}
|
||||
else if (!inst.SH)
|
||||
{
|
||||
// Immediate mask
|
||||
ANDI2R(gpr.R(a), gpr.R(s), mask);
|
||||
|
@ -562,7 +567,7 @@ void JitArm64::rlwinmx(UGeckoInstruction inst)
|
|||
void JitArm64::rlwnmx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
u32 a = inst.RA, b = inst.RB, s = inst.RS;
|
||||
u32 mask = Helper_Mask(inst.MB, inst.ME);
|
||||
|
||||
|
@ -599,7 +604,7 @@ void JitArm64::rlwnmx(UGeckoInstruction inst)
|
|||
void JitArm64::srawix(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
|
||||
int a = inst.RA;
|
||||
int s = inst.RS;
|
||||
|
@ -670,7 +675,7 @@ void JitArm64::srawix(UGeckoInstruction inst)
|
|||
void JitArm64::addic(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
|
||||
int a = inst.RA, d = inst.RD;
|
||||
bool rc = inst.OPCD == 13;
|
||||
|
@ -703,7 +708,7 @@ void JitArm64::addic(UGeckoInstruction inst)
|
|||
void JitArm64::mulli(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
|
||||
int a = inst.RA, d = inst.RD;
|
||||
|
||||
|
@ -725,7 +730,7 @@ void JitArm64::mulli(UGeckoInstruction inst)
|
|||
void JitArm64::mullwx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
FALLBACK_IF(inst.OE);
|
||||
|
||||
int a = inst.RA, b = inst.RB, d = inst.RD;
|
||||
|
@ -749,7 +754,7 @@ void JitArm64::mullwx(UGeckoInstruction inst)
|
|||
void JitArm64::mulhwx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
|
||||
int a = inst.RA, b = inst.RB, d = inst.RD;
|
||||
|
||||
|
@ -774,7 +779,7 @@ void JitArm64::mulhwx(UGeckoInstruction inst)
|
|||
void JitArm64::mulhwux(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
|
||||
int a = inst.RA, b = inst.RB, d = inst.RD;
|
||||
|
||||
|
@ -799,7 +804,7 @@ void JitArm64::mulhwux(UGeckoInstruction inst)
|
|||
void JitArm64::addzex(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
FALLBACK_IF(inst.OE);
|
||||
|
||||
int a = inst.RA, d = inst.RD;
|
||||
|
@ -832,7 +837,7 @@ void JitArm64::addzex(UGeckoInstruction inst)
|
|||
void JitArm64::subfx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
FALLBACK_IF(inst.OE);
|
||||
|
||||
int a = inst.RA, b = inst.RB, d = inst.RD;
|
||||
|
@ -856,7 +861,7 @@ void JitArm64::subfx(UGeckoInstruction inst)
|
|||
void JitArm64::subfex(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
FALLBACK_IF(inst.OE);
|
||||
|
||||
int a = inst.RA, b = inst.RB, d = inst.RD;
|
||||
|
@ -926,7 +931,7 @@ void JitArm64::subfex(UGeckoInstruction inst)
|
|||
void JitArm64::subfcx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
FALLBACK_IF(inst.OE);
|
||||
|
||||
int a = inst.RA, b = inst.RB, d = inst.RD;
|
||||
|
@ -958,7 +963,7 @@ void JitArm64::subfcx(UGeckoInstruction inst)
|
|||
void JitArm64::subfzex(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
FALLBACK_IF(inst.OE);
|
||||
|
||||
int a = inst.RA, d = inst.RD;
|
||||
|
@ -988,7 +993,7 @@ void JitArm64::subfzex(UGeckoInstruction inst)
|
|||
void JitArm64::subfic(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
|
||||
int a = inst.RA, d = inst.RD;
|
||||
s32 imm = inst.SIMM_16;
|
||||
|
@ -1017,7 +1022,7 @@ void JitArm64::subfic(UGeckoInstruction inst)
|
|||
void JitArm64::addex(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
FALLBACK_IF(inst.OE);
|
||||
|
||||
int a = inst.RA, b = inst.RB, d = inst.RD;
|
||||
|
@ -1082,7 +1087,7 @@ void JitArm64::addex(UGeckoInstruction inst)
|
|||
void JitArm64::addcx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
FALLBACK_IF(inst.OE);
|
||||
|
||||
int a = inst.RA, b = inst.RB, d = inst.RD;
|
||||
|
@ -1111,7 +1116,7 @@ void JitArm64::addcx(UGeckoInstruction inst)
|
|||
void JitArm64::divwux(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
FALLBACK_IF(inst.OE);
|
||||
|
||||
int a = inst.RA, b = inst.RB, d = inst.RD;
|
||||
|
@ -1139,7 +1144,7 @@ void JitArm64::divwux(UGeckoInstruction inst)
|
|||
void JitArm64::divwx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
FALLBACK_IF(inst.OE);
|
||||
|
||||
int a = inst.RA, b = inst.RB, d = inst.RD;
|
||||
|
@ -1217,7 +1222,7 @@ void JitArm64::divwx(UGeckoInstruction inst)
|
|||
void JitArm64::slwx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
|
||||
int a = inst.RA, b = inst.RB, s = inst.RS;
|
||||
|
||||
|
@ -1265,7 +1270,7 @@ void JitArm64::slwx(UGeckoInstruction inst)
|
|||
void JitArm64::srwx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
|
||||
int a = inst.RA, b = inst.RB, s = inst.RS;
|
||||
|
||||
|
@ -1312,7 +1317,7 @@ void JitArm64::srwx(UGeckoInstruction inst)
|
|||
void JitArm64::srawx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
|
||||
int a = inst.RA, b = inst.RB, s = inst.RS;
|
||||
bool inplace_carry = CanMergeNextInstructions(1) && js.op[1].wantsCAInFlags;
|
||||
|
@ -1415,7 +1420,7 @@ void JitArm64::srawx(UGeckoInstruction inst)
|
|||
void JitArm64::rlwimix(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
|
||||
int a = inst.RA, s = inst.RS;
|
||||
u32 mask = Helper_Mask(inst.MB, inst.ME);
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cinttypes>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <locale>
|
||||
#include <map>
|
||||
|
@ -33,10 +32,6 @@
|
|||
|
||||
namespace DiscIO
|
||||
{
|
||||
static const DiscContent& AddFileToContents(std::set<DiscContent>* contents,
|
||||
const std::string& path, u64 offset,
|
||||
u64 max_size = UINT64_MAX);
|
||||
|
||||
// Reads as many bytes as the vector fits (or less, if the file is smaller).
|
||||
// Returns the number of bytes read.
|
||||
static size_t ReadFileToVector(const std::string& path, std::vector<u8>* vector);
|
||||
|
@ -64,12 +59,12 @@ constexpr u8 FILE_ENTRY = 0;
|
|||
constexpr u8 DIRECTORY_ENTRY = 1;
|
||||
|
||||
DiscContent::DiscContent(u64 offset, u64 size, const std::string& path)
|
||||
: m_offset(offset), m_size(size), m_content_source(path)
|
||||
: m_offset(offset), m_size(size), m_content_source(path)
|
||||
{
|
||||
}
|
||||
|
||||
DiscContent::DiscContent(u64 offset, u64 size, const u8* data)
|
||||
: m_offset(offset), m_size(size), m_content_source(data)
|
||||
: m_offset(offset), m_size(size), m_content_source(data)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -82,6 +77,11 @@ u64 DiscContent::GetOffset() const
|
|||
return m_offset;
|
||||
}
|
||||
|
||||
u64 DiscContent::GetEndOffset() const
|
||||
{
|
||||
return m_offset + m_size;
|
||||
}
|
||||
|
||||
u64 DiscContent::GetSize() const
|
||||
{
|
||||
return m_size;
|
||||
|
@ -120,6 +120,55 @@ bool DiscContent::Read(u64* offset, u64* length, u8** buffer) const
|
|||
return true;
|
||||
}
|
||||
|
||||
void DiscContentContainer::Add(u64 offset, u64 size, const std::string& path)
|
||||
{
|
||||
if (size != 0)
|
||||
m_contents.emplace(offset, size, path);
|
||||
}
|
||||
|
||||
void DiscContentContainer::Add(u64 offset, u64 size, const u8* data)
|
||||
{
|
||||
if (size != 0)
|
||||
m_contents.emplace(offset, size, data);
|
||||
}
|
||||
|
||||
u64 DiscContentContainer::CheckSizeAndAdd(u64 offset, const std::string& path)
|
||||
{
|
||||
const u64 size = File::GetSize(path);
|
||||
Add(offset, size, path);
|
||||
return size;
|
||||
}
|
||||
|
||||
u64 DiscContentContainer::CheckSizeAndAdd(u64 offset, u64 max_size, const std::string& path)
|
||||
{
|
||||
const u64 size = std::min(File::GetSize(path), max_size);
|
||||
Add(offset, size, path);
|
||||
return size;
|
||||
}
|
||||
|
||||
bool DiscContentContainer::Read(u64 offset, u64 length, u8* buffer) const
|
||||
{
|
||||
// Determine which DiscContent the offset refers to
|
||||
std::set<DiscContent>::const_iterator it = m_contents.upper_bound(DiscContent(offset));
|
||||
|
||||
while (it != m_contents.end() && length > 0)
|
||||
{
|
||||
// Zero fill to start of DiscContent data
|
||||
PadToAddress(it->GetOffset(), &offset, &length, &buffer);
|
||||
|
||||
if (!it->Read(&offset, &length, &buffer))
|
||||
return false;
|
||||
|
||||
++it;
|
||||
_dbg_assert_(DISCIO, it == m_contents.end() || it->GetOffset() >= offset);
|
||||
}
|
||||
|
||||
// Zero fill if we went beyond the last DiscContent
|
||||
std::fill_n(buffer, static_cast<size_t>(length), 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static std::optional<PartitionType> ParsePartitionDirectoryName(const std::string& name)
|
||||
{
|
||||
if (name.size() < 2)
|
||||
|
@ -157,9 +206,9 @@ static bool IsDirectorySeparator(char c)
|
|||
{
|
||||
return c == '/'
|
||||
#ifdef _WIN32
|
||||
|| c == '\\'
|
||||
|| c == '\\'
|
||||
#endif
|
||||
;
|
||||
;
|
||||
}
|
||||
|
||||
static bool PathCharactersEqual(char a, char b)
|
||||
|
@ -186,7 +235,7 @@ static bool PathEndsWith(const std::string& path, const std::string& suffix)
|
|||
}
|
||||
|
||||
static bool IsValidDirectoryBlob(const std::string& dol_path, std::string* partition_root,
|
||||
std::string* true_root = nullptr)
|
||||
std::string* true_root = nullptr)
|
||||
{
|
||||
if (!PathEndsWith(dol_path, "/sys/main.dol"))
|
||||
return false;
|
||||
|
@ -205,7 +254,7 @@ static bool IsValidDirectoryBlob(const std::string& dol_path, std::string* parti
|
|||
if (true_root)
|
||||
{
|
||||
*true_root =
|
||||
dol_path.substr(0, dol_path.find_last_of(dir_separator, partition_root->size() - 2) + 1);
|
||||
dol_path.substr(0, dol_path.find_last_of(dir_separator, partition_root->size() - 2) + 1);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -229,8 +278,8 @@ static bool IsInFilesDirectory(const std::string& path)
|
|||
const size_t slash_before_pos = files_pos - 1;
|
||||
const size_t slash_after_pos = files_pos + 5;
|
||||
if ((files_pos == 0 || IsDirectorySeparator(path[slash_before_pos])) &&
|
||||
(slash_after_pos == path.size() || (IsDirectorySeparator(path[slash_after_pos]))) &&
|
||||
ExistsAndIsValidDirectoryBlob(path.substr(0, files_pos) + "sys/main.dol"))
|
||||
(slash_after_pos == path.size() || (IsDirectorySeparator(path[slash_after_pos]))) &&
|
||||
ExistsAndIsValidDirectoryBlob(path.substr(0, files_pos) + "sys/main.dol"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@ -248,7 +297,7 @@ static bool IsMainDolForNonGamePartition(const std::string& path)
|
|||
std::string partition_directory_name = partition_root.substr(true_root.size());
|
||||
partition_directory_name.pop_back(); // Remove trailing slash
|
||||
const std::optional<PartitionType> partition_type =
|
||||
ParsePartitionDirectoryName(partition_directory_name);
|
||||
ParsePartitionDirectoryName(partition_directory_name);
|
||||
if (!partition_type || *partition_type == PartitionType::Game)
|
||||
return false; // volume_path is the game partition's /sys/main.dol
|
||||
|
||||
|
@ -256,8 +305,8 @@ static bool IsMainDolForNonGamePartition(const std::string& path)
|
|||
for (const File::FSTEntry& entry : true_root_entry.children)
|
||||
{
|
||||
if (entry.isDirectory &&
|
||||
ParsePartitionDirectoryName(entry.virtualName) == PartitionType::Game &&
|
||||
ExistsAndIsValidDirectoryBlob(entry.physicalName + "/sys/main.dol"))
|
||||
ParsePartitionDirectoryName(entry.virtualName) == PartitionType::Game &&
|
||||
ExistsAndIsValidDirectoryBlob(entry.physicalName + "/sys/main.dol"))
|
||||
{
|
||||
return true; // volume_path is the /sys/main.dol for a non-game partition
|
||||
}
|
||||
|
@ -281,7 +330,7 @@ std::unique_ptr<DirectoryBlobReader> DirectoryBlobReader::Create(const std::stri
|
|||
}
|
||||
|
||||
DirectoryBlobReader::DirectoryBlobReader(const std::string& game_partition_root,
|
||||
const std::string& true_root)
|
||||
const std::string& true_root)
|
||||
{
|
||||
DirectoryBlobPartition game_partition(game_partition_root, {});
|
||||
m_is_wii = game_partition.IsWii();
|
||||
|
@ -312,7 +361,7 @@ DirectoryBlobReader::DirectoryBlobReader(const std::string& game_partition_root,
|
|||
if (type && *type != PartitionType::Game)
|
||||
{
|
||||
partitions.emplace_back(DirectoryBlobPartition(entry.physicalName + "/", m_is_wii),
|
||||
*type);
|
||||
*type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -322,45 +371,11 @@ DirectoryBlobReader::DirectoryBlobReader(const std::string& game_partition_root,
|
|||
}
|
||||
}
|
||||
|
||||
bool DirectoryBlobReader::ReadInternal(u64 offset, u64 length, u8* buffer,
|
||||
const std::set<DiscContent>& contents)
|
||||
{
|
||||
if (contents.empty())
|
||||
return true;
|
||||
|
||||
// Determine which DiscContent the offset refers to
|
||||
std::set<DiscContent>::const_iterator it = contents.lower_bound(DiscContent(offset));
|
||||
if (it->GetOffset() > offset && it != contents.begin())
|
||||
--it;
|
||||
|
||||
// zero fill to start of file data
|
||||
PadToAddress(it->GetOffset(), &offset, &length, &buffer);
|
||||
|
||||
while (it != contents.end() && length > 0)
|
||||
{
|
||||
_dbg_assert_(DISCIO, it->GetOffset() <= offset);
|
||||
if (!it->Read(&offset, &length, &buffer))
|
||||
return false;
|
||||
|
||||
++it;
|
||||
|
||||
if (it != contents.end())
|
||||
{
|
||||
_dbg_assert_(DISCIO, it->GetOffset() >= offset);
|
||||
PadToAddress(it->GetOffset(), &offset, &length, &buffer);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DirectoryBlobReader::Read(u64 offset, u64 length, u8* buffer)
|
||||
{
|
||||
// TODO: We don't handle raw access to the encrypted area of Wii discs correctly.
|
||||
|
||||
const std::set<DiscContent>& contents =
|
||||
m_is_wii ? m_nonpartition_contents : m_gamecube_pseudopartition.GetContents();
|
||||
return ReadInternal(offset, length, buffer, contents);
|
||||
return (m_is_wii ? m_nonpartition_contents : m_gamecube_pseudopartition.GetContents())
|
||||
.Read(offset, length, buffer);
|
||||
}
|
||||
|
||||
bool DirectoryBlobReader::SupportsReadWiiDecrypted() const
|
||||
|
@ -377,7 +392,7 @@ bool DirectoryBlobReader::ReadWiiDecrypted(u64 offset, u64 size, u8* buffer, u64
|
|||
if (it == m_partitions.end())
|
||||
return false;
|
||||
|
||||
return ReadInternal(offset, size, buffer, it->second.GetContents());
|
||||
return it->second.GetContents().Read(offset, size, buffer);
|
||||
}
|
||||
|
||||
BlobType DirectoryBlobReader::GetBlobType() const
|
||||
|
@ -397,19 +412,19 @@ u64 DirectoryBlobReader::GetDataSize() const
|
|||
}
|
||||
|
||||
void DirectoryBlobReader::SetNonpartitionDiscHeader(const std::vector<u8>& partition_header,
|
||||
const std::string& game_partition_root)
|
||||
const std::string& game_partition_root)
|
||||
{
|
||||
constexpr u64 NONPARTITION_DISCHEADER_ADDRESS = 0;
|
||||
constexpr u64 NONPARTITION_DISCHEADER_SIZE = 0x100;
|
||||
|
||||
m_disc_header_nonpartition.resize(NONPARTITION_DISCHEADER_SIZE);
|
||||
const size_t header_bin_bytes_read =
|
||||
ReadFileToVector(game_partition_root + "disc/header.bin", &m_disc_header_nonpartition);
|
||||
ReadFileToVector(game_partition_root + "disc/header.bin", &m_disc_header_nonpartition);
|
||||
|
||||
// If header.bin is missing or smaller than expected, use the content of sys/boot.bin instead
|
||||
std::copy(partition_header.data() + header_bin_bytes_read,
|
||||
partition_header.data() + m_disc_header_nonpartition.size(),
|
||||
m_disc_header_nonpartition.data() + header_bin_bytes_read);
|
||||
partition_header.data() + m_disc_header_nonpartition.size(),
|
||||
m_disc_header_nonpartition.data() + header_bin_bytes_read);
|
||||
|
||||
// 0x60 and 0x61 are the only differences between the partition and non-partition headers
|
||||
if (header_bin_bytes_read < 0x60)
|
||||
|
@ -417,8 +432,7 @@ void DirectoryBlobReader::SetNonpartitionDiscHeader(const std::vector<u8>& parti
|
|||
if (header_bin_bytes_read < 0x61)
|
||||
m_disc_header_nonpartition[0x61] = 0;
|
||||
|
||||
m_nonpartition_contents.emplace(NONPARTITION_DISCHEADER_ADDRESS, NONPARTITION_DISCHEADER_SIZE,
|
||||
m_disc_header_nonpartition.data());
|
||||
m_nonpartition_contents.Add(NONPARTITION_DISCHEADER_ADDRESS, m_disc_header_nonpartition);
|
||||
}
|
||||
|
||||
void DirectoryBlobReader::SetWiiRegionData(const std::string& game_partition_root)
|
||||
|
@ -436,26 +450,25 @@ void DirectoryBlobReader::SetWiiRegionData(const std::string& game_partition_roo
|
|||
|
||||
constexpr u64 WII_REGION_DATA_ADDRESS = 0x4E000;
|
||||
constexpr u64 WII_REGION_DATA_SIZE = 0x20;
|
||||
m_nonpartition_contents.emplace(WII_REGION_DATA_ADDRESS, WII_REGION_DATA_SIZE,
|
||||
m_wii_region_data.data());
|
||||
m_nonpartition_contents.Add(WII_REGION_DATA_ADDRESS, m_wii_region_data);
|
||||
}
|
||||
|
||||
void DirectoryBlobReader::SetPartitions(std::vector<PartitionWithType>&& partitions)
|
||||
{
|
||||
std::sort(partitions.begin(), partitions.end(),
|
||||
[](const PartitionWithType& lhs, const PartitionWithType& rhs) {
|
||||
if (lhs.type == rhs.type)
|
||||
return lhs.partition.GetRootDirectory() < rhs.partition.GetRootDirectory();
|
||||
[](const PartitionWithType& lhs, const PartitionWithType& rhs) {
|
||||
if (lhs.type == rhs.type)
|
||||
return lhs.partition.GetRootDirectory() < rhs.partition.GetRootDirectory();
|
||||
|
||||
// Ascending sort by partition type, except Update (1) comes before before Game (0)
|
||||
return (lhs.type > PartitionType::Update || rhs.type > PartitionType::Update) ?
|
||||
lhs.type < rhs.type :
|
||||
lhs.type > rhs.type;
|
||||
});
|
||||
// Ascending sort by partition type, except Update (1) comes before before Game (0)
|
||||
return (lhs.type > PartitionType::Update || rhs.type > PartitionType::Update) ?
|
||||
lhs.type < rhs.type :
|
||||
lhs.type > rhs.type;
|
||||
});
|
||||
|
||||
u32 subtable_1_size = 0;
|
||||
while (subtable_1_size < partitions.size() && subtable_1_size < 3 &&
|
||||
partitions[subtable_1_size].type <= PartitionType::Channel)
|
||||
partitions[subtable_1_size].type <= PartitionType::Channel)
|
||||
{
|
||||
++subtable_1_size;
|
||||
}
|
||||
|
@ -496,19 +509,18 @@ void DirectoryBlobReader::SetPartitions(std::vector<PartitionWithType>&& partiti
|
|||
const u64 partition_data_size = partitions[i].partition.GetDataSize();
|
||||
m_partitions.emplace(partition_address, std::move(partitions[i].partition));
|
||||
const u64 unaligned_next_partition_address =
|
||||
VolumeWii::PartitionOffsetToRawOffset(partition_data_size, Partition(partition_address));
|
||||
VolumeWii::PartitionOffsetToRawOffset(partition_data_size, Partition(partition_address));
|
||||
partition_address = Common::AlignUp(unaligned_next_partition_address, 0x10000ull);
|
||||
}
|
||||
m_data_size = partition_address;
|
||||
|
||||
m_nonpartition_contents.emplace(PARTITION_TABLE_ADDRESS, m_partition_table.size(),
|
||||
m_partition_table.data());
|
||||
m_nonpartition_contents.Add(PARTITION_TABLE_ADDRESS, m_partition_table);
|
||||
}
|
||||
|
||||
// This function sets the header that's shortly before the start of the encrypted
|
||||
// area, not the header that's right at the beginning of the encrypted area
|
||||
void DirectoryBlobReader::SetPartitionHeader(const DirectoryBlobPartition& partition,
|
||||
u64 partition_address)
|
||||
u64 partition_address)
|
||||
{
|
||||
constexpr u32 TICKET_OFFSET = 0x0;
|
||||
constexpr u32 TICKET_SIZE = 0x2a4;
|
||||
|
@ -519,40 +531,39 @@ void DirectoryBlobReader::SetPartitionHeader(const DirectoryBlobPartition& parti
|
|||
|
||||
const std::string& partition_root = partition.GetRootDirectory();
|
||||
|
||||
AddFileToContents(&m_nonpartition_contents, partition_root + "ticket.bin",
|
||||
partition_address + TICKET_OFFSET, TICKET_SIZE);
|
||||
m_nonpartition_contents.CheckSizeAndAdd(partition_address + TICKET_OFFSET, TICKET_SIZE,
|
||||
partition_root + "ticket.bin");
|
||||
|
||||
const DiscContent& tmd = AddFileToContents(&m_nonpartition_contents, partition_root + "tmd.bin",
|
||||
partition_address + TMD_OFFSET, MAX_TMD_SIZE);
|
||||
const u64 tmd_size = m_nonpartition_contents.CheckSizeAndAdd(
|
||||
partition_address + TMD_OFFSET, MAX_TMD_SIZE, partition_root + "tmd.bin");
|
||||
|
||||
const u64 cert_offset = Common::AlignUp(TMD_OFFSET + tmd.GetSize(), 0x20ull);
|
||||
const u64 cert_offset = Common::AlignUp(TMD_OFFSET + tmd_size, 0x20ull);
|
||||
const u64 max_cert_size = H3_OFFSET - cert_offset;
|
||||
const DiscContent& cert = AddFileToContents(&m_nonpartition_contents, partition_root + "cert.bin",
|
||||
partition_address + cert_offset, max_cert_size);
|
||||
const u64 cert_size = m_nonpartition_contents.CheckSizeAndAdd(
|
||||
partition_address + cert_offset, max_cert_size, partition_root + "cert.bin");
|
||||
|
||||
AddFileToContents(&m_nonpartition_contents, partition_root + "h3.bin",
|
||||
partition_address + H3_OFFSET, H3_SIZE);
|
||||
m_nonpartition_contents.CheckSizeAndAdd(partition_address + H3_OFFSET, H3_SIZE,
|
||||
partition_root + "h3.bin");
|
||||
|
||||
constexpr u32 PARTITION_HEADER_SIZE = 0x1c;
|
||||
constexpr u32 DATA_OFFSET = 0x20000;
|
||||
const u64 data_size = Common::AlignUp(partition.GetDataSize(), 0x7c00) / 0x7c00 * 0x8000;
|
||||
m_partition_headers.emplace_back(PARTITION_HEADER_SIZE);
|
||||
std::vector<u8>& partition_header = m_partition_headers.back();
|
||||
Write32(static_cast<u32>(tmd.GetSize()), 0x0, &partition_header);
|
||||
Write32(static_cast<u32>(tmd_size), 0x0, &partition_header);
|
||||
Write32(TMD_OFFSET >> 2, 0x4, &partition_header);
|
||||
Write32(static_cast<u32>(cert.GetSize()), 0x8, &partition_header);
|
||||
Write32(static_cast<u32>(cert_size), 0x8, &partition_header);
|
||||
Write32(static_cast<u32>(cert_offset >> 2), 0x0C, &partition_header);
|
||||
Write32(H3_OFFSET >> 2, 0x10, &partition_header);
|
||||
Write32(DATA_OFFSET >> 2, 0x14, &partition_header);
|
||||
Write32(static_cast<u32>(data_size >> 2), 0x18, &partition_header);
|
||||
|
||||
m_nonpartition_contents.emplace(partition_address + TICKET_SIZE, PARTITION_HEADER_SIZE,
|
||||
partition_header.data());
|
||||
m_nonpartition_contents.Add(partition_address + TICKET_SIZE, partition_header);
|
||||
}
|
||||
|
||||
DirectoryBlobPartition::DirectoryBlobPartition(const std::string& root_directory,
|
||||
std::optional<bool> is_wii)
|
||||
: m_root_directory(root_directory)
|
||||
std::optional<bool> is_wii)
|
||||
: m_root_directory(root_directory)
|
||||
{
|
||||
SetDiscHeaderAndDiscType(is_wii);
|
||||
SetBI2();
|
||||
|
@ -569,7 +580,7 @@ void DirectoryBlobPartition::SetDiscHeaderAndDiscType(std::optional<bool> is_wii
|
|||
if (ReadFileToVector(boot_bin_path, &m_disc_header) < 0x20)
|
||||
ERROR_LOG(DISCIO, "%s doesn't exist or is too small", boot_bin_path.c_str());
|
||||
|
||||
m_contents.emplace(DISCHEADER_ADDRESS, DISCHEADER_SIZE, m_disc_header.data());
|
||||
m_contents.Add(DISCHEADER_ADDRESS, m_disc_header);
|
||||
|
||||
if (is_wii.has_value())
|
||||
{
|
||||
|
@ -600,7 +611,7 @@ void DirectoryBlobPartition::SetBI2()
|
|||
if (!m_is_wii && bytes_read < 0x1C)
|
||||
ERROR_LOG(DISCIO, "Couldn't read region from %s", bi2_path.c_str());
|
||||
|
||||
m_contents.emplace(BI2_ADDRESS, BI2_SIZE, m_bi2.data());
|
||||
m_contents.Add(BI2_ADDRESS, m_bi2);
|
||||
}
|
||||
|
||||
u64 DirectoryBlobPartition::SetApploader()
|
||||
|
@ -617,7 +628,7 @@ u64 DirectoryBlobPartition::SetApploader()
|
|||
else
|
||||
{
|
||||
const size_t apploader_size = 0x20 + Common::swap32(*(u32*)&m_apploader[0x14]) +
|
||||
Common::swap32(*(u32*)&m_apploader[0x18]);
|
||||
Common::swap32(*(u32*)&m_apploader[0x18]);
|
||||
if (apploader_size != m_apploader.size())
|
||||
ERROR_LOG(DISCIO, "%s is the wrong size... Is it really an apploader?", path.c_str());
|
||||
else
|
||||
|
@ -633,7 +644,7 @@ u64 DirectoryBlobPartition::SetApploader()
|
|||
|
||||
constexpr u64 APPLOADER_ADDRESS = 0x2440;
|
||||
|
||||
m_contents.emplace(APPLOADER_ADDRESS, m_apploader.size(), m_apploader.data());
|
||||
m_contents.Add(APPLOADER_ADDRESS, m_apploader);
|
||||
|
||||
// Return DOL address, 32 byte aligned (plus 32 byte padding)
|
||||
return Common::AlignUp(APPLOADER_ADDRESS + m_apploader.size() + 0x20, 0x20ull);
|
||||
|
@ -641,13 +652,12 @@ u64 DirectoryBlobPartition::SetApploader()
|
|||
|
||||
u64 DirectoryBlobPartition::SetDOL(u64 dol_address)
|
||||
{
|
||||
const DiscContent& dol =
|
||||
AddFileToContents(&m_contents, m_root_directory + "sys/main.dol", dol_address);
|
||||
const u64 dol_size = m_contents.CheckSizeAndAdd(dol_address, m_root_directory + "sys/main.dol");
|
||||
|
||||
Write32(static_cast<u32>(dol_address >> m_address_shift), 0x0420, &m_disc_header);
|
||||
|
||||
// Return FST address, 32 byte aligned (plus 32 byte padding)
|
||||
return Common::AlignUp(dol_address + dol.GetSize() + 0x20, 0x20ull);
|
||||
return Common::AlignUp(dol_address + dol_size + 0x20, 0x20ull);
|
||||
}
|
||||
|
||||
void DirectoryBlobPartition::BuildFST(u64 fst_address)
|
||||
|
@ -671,11 +681,11 @@ void DirectoryBlobPartition::BuildFST(u64 fst_address)
|
|||
u32 name_offset = 0; // Offset within name table
|
||||
u32 root_offset = 0; // Offset of root of FST
|
||||
|
||||
// write root entry
|
||||
// write root entry
|
||||
WriteEntryData(&fst_offset, DIRECTORY_ENTRY, 0, 0, total_entries, m_address_shift);
|
||||
|
||||
WriteDirectory(rootEntry, &fst_offset, &name_offset, ¤t_data_address, root_offset,
|
||||
name_table_offset);
|
||||
name_table_offset);
|
||||
|
||||
// overflow check, compare the aligned name offset with the aligned name table size
|
||||
_assert_(Common::AlignUp(name_offset, 1ull << m_address_shift) == name_table_size);
|
||||
|
@ -685,19 +695,19 @@ void DirectoryBlobPartition::BuildFST(u64 fst_address)
|
|||
Write32((u32)(m_fst_data.size() >> m_address_shift), 0x0428, &m_disc_header);
|
||||
Write32((u32)(m_fst_data.size() >> m_address_shift), 0x042c, &m_disc_header);
|
||||
|
||||
m_contents.emplace(fst_address, m_fst_data.size(), m_fst_data.data());
|
||||
m_contents.Add(fst_address, m_fst_data);
|
||||
|
||||
m_data_size = current_data_address;
|
||||
}
|
||||
|
||||
void DirectoryBlobPartition::WriteEntryData(u32* entry_offset, u8 type, u32 name_offset,
|
||||
u64 data_offset, u64 length, u32 address_shift)
|
||||
u64 data_offset, u64 length, u32 address_shift)
|
||||
{
|
||||
m_fst_data[(*entry_offset)++] = type;
|
||||
|
||||
m_fst_data[(*entry_offset)++] = (name_offset >> 16) & 0xff;
|
||||
m_fst_data[(*entry_offset)++] = (name_offset >> 8) & 0xff;
|
||||
m_fst_data[(*entry_offset)++] = (name_offset)&0xff;
|
||||
m_fst_data[(*entry_offset)++] = (name_offset) & 0xff;
|
||||
|
||||
Write32((u32)(data_offset >> address_shift), *entry_offset, &m_fst_data);
|
||||
*entry_offset += 4;
|
||||
|
@ -707,7 +717,7 @@ void DirectoryBlobPartition::WriteEntryData(u32* entry_offset, u8 type, u32 name
|
|||
}
|
||||
|
||||
void DirectoryBlobPartition::WriteEntryName(u32* name_offset, const std::string& name,
|
||||
u64 name_table_offset)
|
||||
u64 name_table_offset)
|
||||
{
|
||||
strncpy((char*)&m_fst_data[*name_offset + name_table_offset], name.c_str(), name.length() + 1);
|
||||
|
||||
|
@ -715,14 +725,14 @@ void DirectoryBlobPartition::WriteEntryName(u32* name_offset, const std::string&
|
|||
}
|
||||
|
||||
void DirectoryBlobPartition::WriteDirectory(const File::FSTEntry& parent_entry, u32* fst_offset,
|
||||
u32* name_offset, u64* data_offset,
|
||||
u32 parent_entry_index, u64 name_table_offset)
|
||||
u32* name_offset, u64* data_offset,
|
||||
u32 parent_entry_index, u64 name_table_offset)
|
||||
{
|
||||
std::vector<File::FSTEntry> sorted_entries = parent_entry.children;
|
||||
|
||||
// Sort for determinism
|
||||
std::sort(sorted_entries.begin(), sorted_entries.end(), [](const File::FSTEntry& one,
|
||||
const File::FSTEntry& two) {
|
||||
const File::FSTEntry& two) {
|
||||
const std::string one_upper = ASCIIToUppercase(one.virtualName);
|
||||
const std::string two_upper = ASCIIToUppercase(two.virtualName);
|
||||
return one_upper == two_upper ? one.virtualName < two.virtualName : one_upper < two_upper;
|
||||
|
@ -734,7 +744,7 @@ void DirectoryBlobPartition::WriteDirectory(const File::FSTEntry& parent_entry,
|
|||
{
|
||||
u32 entry_index = *fst_offset / ENTRY_SIZE;
|
||||
WriteEntryData(fst_offset, DIRECTORY_ENTRY, *name_offset, parent_entry_index,
|
||||
entry_index + entry.size + 1, 0);
|
||||
entry_index + entry.size + 1, 0);
|
||||
WriteEntryName(name_offset, entry.virtualName, name_table_offset);
|
||||
|
||||
WriteDirectory(entry, fst_offset, name_offset, data_offset, entry_index, name_table_offset);
|
||||
|
@ -743,25 +753,18 @@ void DirectoryBlobPartition::WriteDirectory(const File::FSTEntry& parent_entry,
|
|||
{
|
||||
// put entry in FST
|
||||
WriteEntryData(fst_offset, FILE_ENTRY, *name_offset, *data_offset, entry.size,
|
||||
m_address_shift);
|
||||
m_address_shift);
|
||||
WriteEntryName(name_offset, entry.virtualName, name_table_offset);
|
||||
|
||||
// write entry to virtual disc
|
||||
auto result = m_contents.emplace(*data_offset, entry.size, entry.physicalName);
|
||||
_dbg_assert_(DISCIO, result.second); // Check that this offset wasn't already occupied
|
||||
m_contents.Add(*data_offset, entry.size, entry.physicalName);
|
||||
|
||||
// 32 KiB aligned - many games are fine with less alignment, but not all
|
||||
*data_offset = Common::AlignUp(*data_offset + std::max<u64>(entry.size, 1ull), 0x8000ull);
|
||||
*data_offset = Common::AlignUp(*data_offset + entry.size, 0x8000ull);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const DiscContent& AddFileToContents(std::set<DiscContent>* contents,
|
||||
const std::string& path, u64 offset, u64 max_size)
|
||||
{
|
||||
return *(contents->emplace(offset, std::min(File::GetSize(path), max_size), path).first);
|
||||
}
|
||||
|
||||
static size_t ReadFileToVector(const std::string& path, std::vector<u8>* vector)
|
||||
{
|
||||
File::IOFile file(path, "rb");
|
||||
|
@ -817,7 +820,7 @@ static void ConvertUTF8NamesToSHIFTJIS(File::FSTEntry* parent_entry)
|
|||
static std::string ASCIIToUppercase(std::string str)
|
||||
{
|
||||
std::transform(str.begin(), str.end(), str.begin(),
|
||||
[](char c) { return std::toupper(c, std::locale::classic()); });
|
||||
[](char c) { return std::toupper(c, std::locale::classic()); });
|
||||
return str;
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/FileUtil.h"
|
||||
#include "Common/NonCopyable.h"
|
||||
#include "DiscIO/Blob.h"
|
||||
|
||||
namespace File
|
||||
|
@ -43,34 +42,58 @@ public:
|
|||
explicit DiscContent(u64 offset);
|
||||
|
||||
u64 GetOffset() const;
|
||||
u64 GetEndOffset() const;
|
||||
u64 GetSize() const;
|
||||
bool Read(u64* offset, u64* length, u8** buffer) const;
|
||||
|
||||
bool operator==(const DiscContent& other) const { return m_offset == other.m_offset; }
|
||||
bool operator==(const DiscContent& other) const { return GetEndOffset() == other.GetEndOffset(); }
|
||||
bool operator!=(const DiscContent& other) const { return !(*this == other); }
|
||||
bool operator<(const DiscContent& other) const { return m_offset < other.m_offset; }
|
||||
bool operator<(const DiscContent& other) const { return GetEndOffset() < other.GetEndOffset(); }
|
||||
bool operator>(const DiscContent& other) const { return other < *this; }
|
||||
bool operator<=(const DiscContent& other) const { return !(*this < other); }
|
||||
bool operator>=(const DiscContent& other) const { return !(*this > other); }
|
||||
private:
|
||||
u64 m_offset;
|
||||
u64 m_size = 0;
|
||||
std::string m_path;
|
||||
ContentSource m_content_source;
|
||||
};
|
||||
|
||||
// We do not allow copying, because it might mess up the pointers inside DiscContents
|
||||
class DirectoryBlobPartition : private NonCopyable
|
||||
class DiscContentContainer
|
||||
{
|
||||
public:
|
||||
template <typename T>
|
||||
void Add(u64 offset, const std::vector<T>& vector)
|
||||
{
|
||||
return Add(offset, vector.size() * sizeof(T), reinterpret_cast<const u8*>(vector.data()));
|
||||
}
|
||||
void Add(u64 offset, u64 size, const std::string& path);
|
||||
void Add(u64 offset, u64 size, const u8* data);
|
||||
u64 CheckSizeAndAdd(u64 offset, const std::string& path);
|
||||
u64 CheckSizeAndAdd(u64 offset, u64 max_size, const std::string& path);
|
||||
|
||||
bool Read(u64 offset, u64 length, u8* buffer) const;
|
||||
|
||||
private:
|
||||
std::set<DiscContent> m_contents;
|
||||
};
|
||||
|
||||
class DirectoryBlobPartition
|
||||
{
|
||||
public:
|
||||
DirectoryBlobPartition() = default;
|
||||
DirectoryBlobPartition(const std::string& root_directory, std::optional<bool> is_wii);
|
||||
|
||||
// We do not allow copying, because it might mess up the pointers inside DiscContents
|
||||
DirectoryBlobPartition(const DirectoryBlobPartition&) = delete;
|
||||
DirectoryBlobPartition& operator=(const DirectoryBlobPartition&) = delete;
|
||||
DirectoryBlobPartition(DirectoryBlobPartition&&) = default;
|
||||
DirectoryBlobPartition& operator=(DirectoryBlobPartition&&) = default;
|
||||
|
||||
bool IsWii() const { return m_is_wii; }
|
||||
u64 GetDataSize() const { return m_data_size; }
|
||||
const std::string& GetRootDirectory() const { return m_root_directory; }
|
||||
const std::vector<u8>& GetHeader() const { return m_disc_header; }
|
||||
const std::set<DiscContent>& GetContents() const { return m_contents; }
|
||||
const DiscContentContainer& GetContents() const { return m_contents; }
|
||||
private:
|
||||
void SetDiscHeaderAndDiscType(std::optional<bool> is_wii);
|
||||
void SetBI2();
|
||||
|
@ -84,12 +107,12 @@ private:
|
|||
|
||||
// FST creation
|
||||
void WriteEntryData(u32* entry_offset, u8 type, u32 name_offset, u64 data_offset, u64 length,
|
||||
u32 address_shift);
|
||||
u32 address_shift);
|
||||
void WriteEntryName(u32* name_offset, const std::string& name, u64 name_table_offset);
|
||||
void WriteDirectory(const File::FSTEntry& parent_entry, u32* fst_offset, u32* name_offset,
|
||||
u64* data_offset, u32 parent_entry_index, u64 name_table_offset);
|
||||
u64* data_offset, u32 parent_entry_index, u64 name_table_offset);
|
||||
|
||||
std::set<DiscContent> m_contents;
|
||||
DiscContentContainer m_contents;
|
||||
std::vector<u8> m_disc_header;
|
||||
std::vector<u8> m_bi2;
|
||||
std::vector<u8> m_apploader;
|
||||
|
@ -103,12 +126,17 @@ private:
|
|||
u64 m_data_size;
|
||||
};
|
||||
|
||||
// We do not allow copying, because it might mess up the pointers inside DiscContents
|
||||
class DirectoryBlobReader : public BlobReader, private NonCopyable
|
||||
class DirectoryBlobReader : public BlobReader
|
||||
{
|
||||
public:
|
||||
static std::unique_ptr<DirectoryBlobReader> Create(const std::string& dol_path);
|
||||
|
||||
// We do not allow copying, because it might mess up the pointers inside DiscContents
|
||||
DirectoryBlobReader(const DirectoryBlobReader&) = delete;
|
||||
DirectoryBlobReader& operator=(const DirectoryBlobReader&) = delete;
|
||||
DirectoryBlobReader(DirectoryBlobReader&&) = default;
|
||||
DirectoryBlobReader& operator=(DirectoryBlobReader&&) = default;
|
||||
|
||||
bool Read(u64 offset, u64 length, u8* buffer) override;
|
||||
bool SupportsReadWiiDecrypted() const override;
|
||||
bool ReadWiiDecrypted(u64 offset, u64 size, u8* buffer, u64 partition_offset) override;
|
||||
|
@ -121,7 +149,7 @@ private:
|
|||
struct PartitionWithType
|
||||
{
|
||||
PartitionWithType(DirectoryBlobPartition&& partition_, PartitionType type_)
|
||||
: partition(std::move(partition_)), type(type_)
|
||||
: partition(std::move(partition_)), type(type_)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -130,12 +158,10 @@ private:
|
|||
};
|
||||
|
||||
explicit DirectoryBlobReader(const std::string& game_partition_root,
|
||||
const std::string& true_root);
|
||||
|
||||
bool ReadInternal(u64 offset, u64 length, u8* buffer, const std::set<DiscContent>& contents);
|
||||
const std::string& true_root);
|
||||
|
||||
void SetNonpartitionDiscHeader(const std::vector<u8>& partition_header,
|
||||
const std::string& game_partition_root);
|
||||
const std::string& game_partition_root);
|
||||
void SetWiiRegionData(const std::string& game_partition_root);
|
||||
void SetPartitions(std::vector<PartitionWithType>&& partitions);
|
||||
void SetPartitionHeader(const DirectoryBlobPartition& partition, u64 partition_address);
|
||||
|
@ -144,7 +170,7 @@ private:
|
|||
DirectoryBlobPartition m_gamecube_pseudopartition;
|
||||
|
||||
// For Wii:
|
||||
std::set<DiscContent> m_nonpartition_contents;
|
||||
DiscContentContainer m_nonpartition_contents;
|
||||
std::map<u64, DirectoryBlobPartition> m_partitions;
|
||||
|
||||
bool m_is_wii;
|
||||
|
|
|
@ -28,12 +28,12 @@ std::string DirectoryNameForPartitionType(u32 partition_type)
|
|||
case 2:
|
||||
return "CHANNEL";
|
||||
default:
|
||||
const std::string type_as_game_id{static_cast<char>((partition_type >> 24) & 0xFF),
|
||||
static_cast<char>((partition_type >> 16) & 0xFF),
|
||||
static_cast<char>((partition_type >> 8) & 0xFF),
|
||||
static_cast<char>(partition_type & 0xFF)};
|
||||
const std::string type_as_game_id{ static_cast<char>((partition_type >> 24) & 0xFF),
|
||||
static_cast<char>((partition_type >> 16) & 0xFF),
|
||||
static_cast<char>((partition_type >> 8) & 0xFF),
|
||||
static_cast<char>(partition_type & 0xFF) };
|
||||
if (std::all_of(type_as_game_id.cbegin(), type_as_game_id.cend(),
|
||||
[](char c) { return std::isprint(c, std::locale::classic()); }))
|
||||
[](char c) { return std::isprint(c, std::locale::classic()); }))
|
||||
{
|
||||
return "P-" + type_as_game_id;
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ std::string DirectoryNameForPartitionType(u32 partition_type)
|
|||
}
|
||||
|
||||
u64 ReadFile(const Volume& volume, const Partition& partition, const FileInfo* file_info,
|
||||
u8* buffer, u64 max_buffer_size, u64 offset_in_file)
|
||||
u8* buffer, u64 max_buffer_size, u64 offset_in_file)
|
||||
{
|
||||
if (!file_info || file_info->IsDirectory() || offset_in_file >= file_info->GetSize())
|
||||
return 0;
|
||||
|
@ -51,9 +51,9 @@ u64 ReadFile(const Volume& volume, const Partition& partition, const FileInfo* f
|
|||
const u64 read_length = std::min(max_buffer_size, file_info->GetSize() - offset_in_file);
|
||||
|
||||
DEBUG_LOG(DISCIO, "Reading %" PRIx64 " bytes at %" PRIx64 " from file %s. Offset: %" PRIx64
|
||||
" Size: %" PRIx32,
|
||||
read_length, offset_in_file, file_info->GetPath().c_str(), file_info->GetOffset(),
|
||||
file_info->GetSize());
|
||||
" Size: %" PRIx32,
|
||||
read_length, offset_in_file, file_info->GetPath().c_str(), file_info->GetOffset(),
|
||||
file_info->GetSize());
|
||||
|
||||
if (!volume.Read(file_info->GetOffset() + offset_in_file, read_length, buffer, partition))
|
||||
return 0;
|
||||
|
@ -62,7 +62,7 @@ u64 ReadFile(const Volume& volume, const Partition& partition, const FileInfo* f
|
|||
}
|
||||
|
||||
bool ExportData(const Volume& volume, const Partition& partition, u64 offset, u64 size,
|
||||
const std::string& export_filename)
|
||||
const std::string& export_filename)
|
||||
{
|
||||
File::IOFile f(export_filename, "wb");
|
||||
if (!f)
|
||||
|
@ -89,19 +89,19 @@ bool ExportData(const Volume& volume, const Partition& partition, u64 offset, u6
|
|||
}
|
||||
|
||||
bool ExportFile(const Volume& volume, const Partition& partition, const FileInfo* file_info,
|
||||
const std::string& export_filename)
|
||||
const std::string& export_filename)
|
||||
{
|
||||
if (!file_info || file_info->IsDirectory())
|
||||
return false;
|
||||
|
||||
return ExportData(volume, partition, file_info->GetOffset(), file_info->GetSize(),
|
||||
export_filename);
|
||||
export_filename);
|
||||
}
|
||||
|
||||
void ExportDirectory(const Volume& volume, const Partition partition, const FileInfo& directory,
|
||||
bool recursive, const std::string& filesystem_path,
|
||||
const std::string& export_folder,
|
||||
const std::function<bool(const std::string& path)>& update_progress)
|
||||
bool recursive, const std::string& filesystem_path,
|
||||
const std::string& export_folder,
|
||||
const std::function<bool(const std::string& path)>& update_progress)
|
||||
{
|
||||
File::CreateFullPath(export_folder + '/');
|
||||
|
||||
|
@ -147,7 +147,7 @@ bool ExportWiiRegionData(const Volume& volume, const std::string& export_filenam
|
|||
}
|
||||
|
||||
bool ExportTicket(const Volume& volume, const Partition& partition,
|
||||
const std::string& export_filename)
|
||||
const std::string& export_filename)
|
||||
{
|
||||
if (volume.GetVolumeType() != Platform::WII_DISC)
|
||||
return false;
|
||||
|
@ -162,22 +162,22 @@ bool ExportTMD(const Volume& volume, const Partition& partition, const std::stri
|
|||
|
||||
const std::optional<u32> size = volume.ReadSwapped<u32>(partition.offset + 0x2a4, PARTITION_NONE);
|
||||
const std::optional<u64> offset =
|
||||
volume.ReadSwappedAndShifted(partition.offset + 0x2a8, PARTITION_NONE);
|
||||
volume.ReadSwappedAndShifted(partition.offset + 0x2a8, PARTITION_NONE);
|
||||
if (!size || !offset)
|
||||
return false;
|
||||
|
||||
return ExportData(volume, PARTITION_NONE, *offset, *size, export_filename);
|
||||
return ExportData(volume, PARTITION_NONE, partition.offset + *offset, *size, export_filename);
|
||||
}
|
||||
|
||||
bool ExportCertificateChain(const Volume& volume, const Partition& partition,
|
||||
const std::string& export_filename)
|
||||
const std::string& export_filename)
|
||||
{
|
||||
if (volume.GetVolumeType() != Platform::WII_DISC)
|
||||
return false;
|
||||
|
||||
const std::optional<u32> size = volume.ReadSwapped<u32>(partition.offset + 0x2ac, PARTITION_NONE);
|
||||
const std::optional<u64> offset =
|
||||
volume.ReadSwappedAndShifted(partition.offset + 0x2b0, PARTITION_NONE);
|
||||
volume.ReadSwappedAndShifted(partition.offset + 0x2b0, PARTITION_NONE);
|
||||
if (!size || !offset)
|
||||
return false;
|
||||
|
||||
|
@ -185,13 +185,13 @@ bool ExportCertificateChain(const Volume& volume, const Partition& partition,
|
|||
}
|
||||
|
||||
bool ExportH3Hashes(const Volume& volume, const Partition& partition,
|
||||
const std::string& export_filename)
|
||||
const std::string& export_filename)
|
||||
{
|
||||
if (volume.GetVolumeType() != Platform::WII_DISC)
|
||||
return false;
|
||||
|
||||
const std::optional<u64> offset =
|
||||
volume.ReadSwappedAndShifted(partition.offset + 0x2b4, PARTITION_NONE);
|
||||
volume.ReadSwappedAndShifted(partition.offset + 0x2b4, PARTITION_NONE);
|
||||
if (!offset)
|
||||
return false;
|
||||
|
||||
|
@ -199,7 +199,7 @@ bool ExportH3Hashes(const Volume& volume, const Partition& partition,
|
|||
}
|
||||
|
||||
bool ExportHeader(const Volume& volume, const Partition& partition,
|
||||
const std::string& export_filename)
|
||||
const std::string& export_filename)
|
||||
{
|
||||
if (!IsDisc(volume.GetVolumeType()))
|
||||
return false;
|
||||
|
@ -208,7 +208,7 @@ bool ExportHeader(const Volume& volume, const Partition& partition,
|
|||
}
|
||||
|
||||
bool ExportBI2Data(const Volume& volume, const Partition& partition,
|
||||
const std::string& export_filename)
|
||||
const std::string& export_filename)
|
||||
{
|
||||
if (!IsDisc(volume.GetVolumeType()))
|
||||
return false;
|
||||
|
@ -217,7 +217,7 @@ bool ExportBI2Data(const Volume& volume, const Partition& partition,
|
|||
}
|
||||
|
||||
bool ExportApploader(const Volume& volume, const Partition& partition,
|
||||
const std::string& export_filename)
|
||||
const std::string& export_filename)
|
||||
{
|
||||
if (!IsDisc(volume.GetVolumeType()))
|
||||
return false;
|
||||
|
@ -319,7 +319,7 @@ bool ExportFST(const Volume& volume, const Partition& partition, const std::stri
|
|||
}
|
||||
|
||||
bool ExportSystemData(const Volume& volume, const Partition& partition,
|
||||
const std::string& export_folder)
|
||||
const std::string& export_folder)
|
||||
{
|
||||
bool success = true;
|
||||
|
||||
|
|
|
@ -27,22 +27,22 @@ namespace DiscIO
|
|||
{
|
||||
constexpr u32 FST_ENTRY_SIZE = 4 * 3; // An FST entry consists of three 32-bit integers
|
||||
|
||||
// Set everything manually.
|
||||
// Set everything manually.
|
||||
FileInfoGCWii::FileInfoGCWii(const u8* fst, u8 offset_shift, u32 index, u32 total_file_infos)
|
||||
: m_fst(fst), m_offset_shift(offset_shift), m_index(index), m_total_file_infos(total_file_infos)
|
||||
: m_fst(fst), m_offset_shift(offset_shift), m_index(index), m_total_file_infos(total_file_infos)
|
||||
{
|
||||
}
|
||||
|
||||
// For the root object only.
|
||||
// m_fst and m_index must be correctly set before GetSize() is called!
|
||||
FileInfoGCWii::FileInfoGCWii(const u8* fst, u8 offset_shift)
|
||||
: m_fst(fst), m_offset_shift(offset_shift), m_index(0), m_total_file_infos(GetSize())
|
||||
: m_fst(fst), m_offset_shift(offset_shift), m_index(0), m_total_file_infos(GetSize())
|
||||
{
|
||||
}
|
||||
|
||||
// Copy data that is common to the whole file system.
|
||||
FileInfoGCWii::FileInfoGCWii(const FileInfoGCWii& file_info, u32 index)
|
||||
: FileInfoGCWii(file_info.m_fst, file_info.m_offset_shift, index, file_info.m_total_file_infos)
|
||||
: FileInfoGCWii(file_info.m_fst, file_info.m_offset_shift, index, file_info.m_total_file_infos)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -82,7 +82,7 @@ FileInfo::const_iterator FileInfoGCWii::end() const
|
|||
u32 FileInfoGCWii::Get(EntryProperty entry_property) const
|
||||
{
|
||||
return Common::swap32(m_fst + FST_ENTRY_SIZE * m_index +
|
||||
sizeof(u32) * static_cast<int>(entry_property));
|
||||
sizeof(u32) * static_cast<int>(entry_property));
|
||||
}
|
||||
|
||||
u32 FileInfoGCWii::GetSize() const
|
||||
|
@ -108,7 +108,7 @@ u32 FileInfoGCWii::GetTotalChildren() const
|
|||
u64 FileInfoGCWii::GetNameOffset() const
|
||||
{
|
||||
return static_cast<u64>(FST_ENTRY_SIZE) * m_total_file_infos +
|
||||
(Get(EntryProperty::NAME_OFFSET) & 0xFFFFFF);
|
||||
(Get(EntryProperty::NAME_OFFSET) & 0xFFFFFF);
|
||||
}
|
||||
|
||||
std::string FileInfoGCWii::GetName() const
|
||||
|
@ -136,7 +136,7 @@ std::string FileInfoGCWii::GetPath() const
|
|||
// because the root directory at index 0 contains all files.
|
||||
FileInfoGCWii potential_parent(*this, m_index - 1);
|
||||
while (!(potential_parent.IsDirectory() &&
|
||||
potential_parent.Get(EntryProperty::FILE_SIZE) > m_index))
|
||||
potential_parent.Get(EntryProperty::FILE_SIZE) > m_index))
|
||||
{
|
||||
potential_parent = FileInfoGCWii(*this, potential_parent.m_index - 1);
|
||||
}
|
||||
|
@ -185,7 +185,7 @@ bool FileInfoGCWii::IsValid(u64 fst_size, const FileInfoGCWii& parent_directory)
|
|||
}
|
||||
|
||||
FileSystemGCWii::FileSystemGCWii(const Volume* volume, const Partition& partition)
|
||||
: FileSystem(volume, partition), m_valid(false), m_root(nullptr, 0, 0, 0)
|
||||
: FileSystem(volume, partition), m_valid(false), m_root(nullptr, 0, 0, 0)
|
||||
{
|
||||
u8 offset_shift;
|
||||
// Check if this is a GameCube or Wii disc
|
||||
|
@ -266,7 +266,7 @@ std::unique_ptr<FileInfo> FileSystemGCWii::FindFileInfo(const std::string& path)
|
|||
}
|
||||
|
||||
std::unique_ptr<FileInfo> FileSystemGCWii::FindFileInfo(const std::string& path,
|
||||
const FileInfo& file_info) const
|
||||
const FileInfo& file_info) const
|
||||
{
|
||||
// Given a path like "directory1/directory2/fileA.bin", this function will
|
||||
// find directory1 and then call itself to search for "directory2/fileA.bin".
|
||||
|
@ -309,7 +309,11 @@ std::unique_ptr<FileInfo> FileSystemGCWii::FindFileInfo(u64 disc_offset) const
|
|||
{
|
||||
FileInfoGCWii file_info(m_root, i);
|
||||
if (!file_info.IsDirectory())
|
||||
m_offset_file_info_cache.emplace(file_info.GetOffset() + file_info.GetSize(), i);
|
||||
{
|
||||
const u32 size = file_info.GetSize();
|
||||
if (size != 0)
|
||||
m_offset_file_info_cache.emplace(file_info.GetOffset() + size, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -73,7 +73,7 @@ void AdvancedWidget::CreateWidgets()
|
|||
utility_layout->addWidget(m_dump_efb_target, 2, 0);
|
||||
utility_layout->addWidget(m_enable_freelook, 2, 1);
|
||||
#if defined(HAVE_FFMPEG)
|
||||
utility_layout->addWidget(m_dump_use_ffv1, 3, -1);
|
||||
utility_layout->addWidget(m_dump_use_ffv1, 3, 0);
|
||||
#endif
|
||||
|
||||
// Misc.
|
||||
|
|
|
@ -69,7 +69,6 @@ QGroupBox* InfoWidget::CreateBannerDetails()
|
|||
m_long_maker = CreateValueDisplay();
|
||||
m_description = new QTextEdit();
|
||||
m_description->setReadOnly(true);
|
||||
QWidget* banner = CreateBannerGraphic();
|
||||
CreateLanguageSelector();
|
||||
|
||||
layout->addRow(tr("Show Language:"), m_language_selector);
|
||||
|
@ -85,7 +84,11 @@ QGroupBox* InfoWidget::CreateBannerDetails()
|
|||
{
|
||||
layout->addRow(tr("Name:"), m_long_name);
|
||||
}
|
||||
layout->addRow(tr("Banner:"), banner);
|
||||
|
||||
if (!m_game.GetBanner().isNull())
|
||||
{
|
||||
layout->addRow(tr("Banner:"), CreateBannerGraphic());
|
||||
}
|
||||
|
||||
group->setLayout(layout);
|
||||
return group;
|
||||
|
|
|
@ -12,7 +12,8 @@
|
|||
|
||||
PropertiesDialog::PropertiesDialog(QWidget* parent, const GameFile& game) : QDialog(parent)
|
||||
{
|
||||
setWindowTitle(QStringLiteral("%1: %2").arg(game.GetGameID()).arg(game.GetLongName()));
|
||||
setWindowTitle(
|
||||
QStringLiteral("%1: %2 - %3").arg(game.GetFileName(), game.GetGameID(), game.GetLongName()));
|
||||
QVBoxLayout* layout = new QVBoxLayout();
|
||||
|
||||
QTabWidget* tab_widget = new QTabWidget(this);
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include <QCryptographicHash>
|
||||
#include <QDataStream>
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QImage>
|
||||
#include <QSharedPointer>
|
||||
|
||||
|
@ -82,7 +83,7 @@ QString GameFile::GetCacheFileName() const
|
|||
// files with the same names in different folders.
|
||||
QString hash =
|
||||
QString::fromUtf8(QCryptographicHash::hash(m_path.toUtf8(), QCryptographicHash::Md5).toHex());
|
||||
return folder + m_file_name + hash;
|
||||
return folder + GetFileName() + hash;
|
||||
}
|
||||
|
||||
void GameFile::ReadBanner(const DiscIO::Volume& volume)
|
||||
|
@ -99,8 +100,6 @@ void GameFile::ReadBanner(const DiscIO::Volume& volume)
|
|||
|
||||
if (!banner.isNull())
|
||||
m_banner = QPixmap::fromImage(banner);
|
||||
else
|
||||
m_banner = Resources::GetMisc(Resources::BANNER_MISSING);
|
||||
}
|
||||
|
||||
bool GameFile::LoadFileInfo(const QString& path)
|
||||
|
@ -109,9 +108,6 @@ bool GameFile::LoadFileInfo(const QString& path)
|
|||
if (!info.exists() || !info.isReadable())
|
||||
return false;
|
||||
|
||||
m_file_name = info.fileName();
|
||||
m_extension = info.suffix();
|
||||
m_folder = info.dir().dirName();
|
||||
m_last_modified = info.lastModified();
|
||||
m_size = info.size();
|
||||
|
||||
|
@ -129,7 +125,8 @@ void GameFile::LoadState()
|
|||
|
||||
bool GameFile::IsElfOrDol()
|
||||
{
|
||||
return m_extension == QStringLiteral("elf") || m_extension == QStringLiteral("dol");
|
||||
QString extension = GetFileExtension();
|
||||
return extension == QStringLiteral("elf") || extension == QStringLiteral("dol");
|
||||
}
|
||||
|
||||
bool GameFile::TryLoadCache()
|
||||
|
@ -192,13 +189,11 @@ bool GameFile::TryLoadElfDol()
|
|||
return false;
|
||||
|
||||
m_revision = 0;
|
||||
m_long_names[DiscIO::Language::LANGUAGE_ENGLISH] = m_file_name;
|
||||
m_platform = DiscIO::Platform::ELF_DOL;
|
||||
m_region = DiscIO::Region::UNKNOWN_REGION;
|
||||
m_country = DiscIO::Country::COUNTRY_UNKNOWN;
|
||||
m_blob_type = DiscIO::BlobType::DIRECTORY;
|
||||
m_raw_size = m_size;
|
||||
m_banner = Resources::GetMisc(Resources::BANNER_MISSING);
|
||||
m_rating = 0;
|
||||
|
||||
return true;
|
||||
|
@ -209,6 +204,21 @@ void GameFile::SaveCache()
|
|||
// TODO
|
||||
}
|
||||
|
||||
QString GameFile::GetFileName() const
|
||||
{
|
||||
return QFileInfo(m_path).fileName();
|
||||
}
|
||||
|
||||
QString GameFile::GetFileExtension() const
|
||||
{
|
||||
return QFileInfo(m_path).suffix();
|
||||
}
|
||||
|
||||
QString GameFile::GetFileFolder() const
|
||||
{
|
||||
return QFileInfo(m_path).dir().dirName();
|
||||
}
|
||||
|
||||
QString GameFile::GetBannerString(const QMap<DiscIO::Language, QString>& m) const
|
||||
{
|
||||
// Try the settings language, then English, then just pick one.
|
||||
|
|
|
@ -30,9 +30,9 @@ public:
|
|||
bool IsValid() const;
|
||||
// These will be properly initialized before we try to load the file.
|
||||
QString GetFilePath() const { return m_path; }
|
||||
QString GetFileName() const { return m_file_name; }
|
||||
QString GetFileExtension() const { return m_extension; }
|
||||
QString GetFileFolder() const { return m_folder; }
|
||||
QString GetFileName() const;
|
||||
QString GetFileExtension() const;
|
||||
QString GetFileFolder() const;
|
||||
qint64 GetFileSize() const { return m_size; }
|
||||
// The rest will not.
|
||||
QString GetGameID() const { return m_game_id; }
|
||||
|
@ -88,9 +88,6 @@ private:
|
|||
|
||||
bool m_valid;
|
||||
QString m_path;
|
||||
QString m_file_name;
|
||||
QString m_extension;
|
||||
QString m_folder;
|
||||
QDateTime m_last_modified;
|
||||
qint64 m_size = 0;
|
||||
|
||||
|
@ -105,7 +102,6 @@ private:
|
|||
QMap<DiscIO::Language, QString> m_short_makers;
|
||||
QMap<DiscIO::Language, QString> m_long_makers;
|
||||
QMap<DiscIO::Language, QString> m_descriptions;
|
||||
QString m_company;
|
||||
u8 m_disc_number = 0;
|
||||
DiscIO::Region m_region;
|
||||
DiscIO::Platform m_platform;
|
||||
|
|
|
@ -49,8 +49,6 @@ GameList::GameList(QWidget* parent) : QStackedWidget(parent)
|
|||
|
||||
connect(m_list, &QTableView::doubleClicked, this, &GameList::GameSelected);
|
||||
connect(m_grid, &QListView::doubleClicked, this, &GameList::GameSelected);
|
||||
connect(&Settings::Instance(), &Settings::PathAdded, m_model, &GameListModel::DirectoryAdded);
|
||||
connect(&Settings::Instance(), &Settings::PathRemoved, m_model, &GameListModel::DirectoryRemoved);
|
||||
connect(m_model, &QAbstractItemModel::rowsInserted, this, &GameList::ConsiderViewChange);
|
||||
connect(m_model, &QAbstractItemModel::rowsRemoved, this, &GameList::ConsiderViewChange);
|
||||
|
||||
|
|
|
@ -14,8 +14,11 @@ GameListModel::GameListModel(QObject* parent) : QAbstractTableModel(parent)
|
|||
{
|
||||
connect(&m_tracker, &GameTracker::GameLoaded, this, &GameListModel::UpdateGame);
|
||||
connect(&m_tracker, &GameTracker::GameRemoved, this, &GameListModel::RemoveGame);
|
||||
connect(this, &GameListModel::DirectoryAdded, &m_tracker, &GameTracker::AddDirectory);
|
||||
connect(this, &GameListModel::DirectoryRemoved, &m_tracker, &GameTracker::RemoveDirectory);
|
||||
connect(&Settings::Instance(), &Settings::PathAdded, &m_tracker, &GameTracker::AddDirectory);
|
||||
connect(&Settings::Instance(), &Settings::PathRemoved, &m_tracker, &GameTracker::RemoveDirectory);
|
||||
|
||||
for (const QString& dir : Settings::Instance().GetPaths())
|
||||
m_tracker.AddDirectory(dir);
|
||||
|
||||
connect(&Settings::Instance(), &Settings::ThemeChanged, [this] {
|
||||
// Tell the view to repaint. The signal 'dataChanged' also seems like it would work here, but
|
||||
|
@ -60,6 +63,8 @@ QVariant GameListModel::data(const QModelIndex& index, int role) const
|
|||
// GameCube banners are 96x32, but Wii banners are 192x64.
|
||||
// TODO: use custom banners from rom directory like DolphinWX?
|
||||
QPixmap banner = game->GetBanner();
|
||||
if (banner.isNull())
|
||||
banner = Resources::GetMisc(Resources::BANNER_MISSING);
|
||||
banner.setDevicePixelRatio(std::max(banner.width() / GAMECUBE_BANNER_SIZE.width(),
|
||||
banner.height() / GAMECUBE_BANNER_SIZE.height()));
|
||||
return banner;
|
||||
|
@ -73,7 +78,10 @@ QVariant GameListModel::data(const QModelIndex& index, int role) const
|
|||
Core::TitleDatabase::TitleType::Channel :
|
||||
Core::TitleDatabase::TitleType::Other));
|
||||
if (display_name.isEmpty())
|
||||
return game->GetLongName();
|
||||
display_name = game->GetLongName();
|
||||
|
||||
if (display_name.isEmpty())
|
||||
display_name = game->GetFileName();
|
||||
|
||||
return display_name;
|
||||
}
|
||||
|
@ -200,19 +208,22 @@ QSharedPointer<GameFile> GameListModel::GetGameFile(int index) const
|
|||
return m_games[index];
|
||||
}
|
||||
|
||||
void GameListModel::UpdateGame(QSharedPointer<GameFile> game)
|
||||
void GameListModel::UpdateGame(const QSharedPointer<GameFile>& game)
|
||||
{
|
||||
QString path = game->GetFilePath();
|
||||
|
||||
int entry = FindGame(path);
|
||||
if (entry < 0)
|
||||
entry = m_games.size();
|
||||
int index = FindGame(path);
|
||||
if (index < 0)
|
||||
{
|
||||
beginInsertRows(QModelIndex(), m_games.size(), m_games.size());
|
||||
m_games.push_back(game);
|
||||
endInsertRows();
|
||||
}
|
||||
else
|
||||
return;
|
||||
|
||||
beginInsertRows(QModelIndex(), entry, entry);
|
||||
m_games.insert(entry, game);
|
||||
endInsertRows();
|
||||
{
|
||||
m_games[index] = game;
|
||||
emit dataChanged(createIndex(index, 0), createIndex(index + 1, columnCount(QModelIndex())));
|
||||
}
|
||||
}
|
||||
|
||||
void GameListModel::RemoveGame(const QString& path)
|
||||
|
|
|
@ -45,13 +45,9 @@ public:
|
|||
NUM_COLS
|
||||
};
|
||||
|
||||
void UpdateGame(QSharedPointer<GameFile> game);
|
||||
void UpdateGame(const QSharedPointer<GameFile>& game);
|
||||
void RemoveGame(const QString& path);
|
||||
|
||||
signals:
|
||||
void DirectoryAdded(const QString& dir);
|
||||
void DirectoryRemoved(const QString& dir);
|
||||
|
||||
private:
|
||||
// Index in m_games, or -1 if it isn't found
|
||||
int FindGame(const QString& path) const;
|
||||
|
|
|
@ -17,26 +17,11 @@ static const QStringList game_filters{
|
|||
|
||||
GameTracker::GameTracker(QObject* parent) : QFileSystemWatcher(parent)
|
||||
{
|
||||
m_loader = new GameLoader;
|
||||
m_loader->moveToThread(&m_loader_thread);
|
||||
|
||||
qRegisterMetaType<QSharedPointer<GameFile>>();
|
||||
connect(&m_loader_thread, &QThread::finished, m_loader, &QObject::deleteLater);
|
||||
connect(this, &QFileSystemWatcher::directoryChanged, this, &GameTracker::UpdateDirectory);
|
||||
connect(this, &QFileSystemWatcher::fileChanged, this, &GameTracker::UpdateFile);
|
||||
connect(this, &GameTracker::PathChanged, m_loader, &GameLoader::LoadGame);
|
||||
connect(m_loader, &GameLoader::GameLoaded, this, &GameTracker::GameLoaded);
|
||||
|
||||
m_loader_thread.start();
|
||||
|
||||
for (QString dir : Settings::Instance().GetPaths())
|
||||
AddDirectory(dir);
|
||||
}
|
||||
|
||||
GameTracker::~GameTracker()
|
||||
{
|
||||
m_loader_thread.quit();
|
||||
m_loader_thread.wait();
|
||||
m_load_thread.Reset([this](const QString& path) { LoadGame(path); });
|
||||
}
|
||||
|
||||
void GameTracker::AddDirectory(const QString& dir)
|
||||
|
@ -84,7 +69,7 @@ void GameTracker::UpdateDirectory(const QString& dir)
|
|||
{
|
||||
addPath(path);
|
||||
m_tracked_files[path] = QSet<QString>{dir};
|
||||
emit PathChanged(path);
|
||||
m_load_thread.EmplaceItem(path);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -130,7 +115,7 @@ void GameTracker::UpdateFile(const QString& file)
|
|||
GameRemoved(file);
|
||||
addPath(file);
|
||||
|
||||
emit PathChanged(file);
|
||||
m_load_thread.EmplaceItem(file);
|
||||
}
|
||||
else if (removePath(file))
|
||||
{
|
||||
|
@ -139,7 +124,7 @@ void GameTracker::UpdateFile(const QString& file)
|
|||
}
|
||||
}
|
||||
|
||||
void GameLoader::LoadGame(const QString& path)
|
||||
void GameTracker::LoadGame(const QString& path)
|
||||
{
|
||||
if (!DiscIO::ShouldHideFromGameList(path.toStdString()))
|
||||
{
|
||||
|
|
|
@ -9,25 +9,19 @@
|
|||
#include <QSet>
|
||||
#include <QSharedPointer>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QThread>
|
||||
|
||||
#include "Common/WorkQueueThread.h"
|
||||
#include "DolphinQt2/GameList/GameFile.h"
|
||||
|
||||
class GameLoader;
|
||||
|
||||
// Watches directories and loads GameFiles in a separate thread.
|
||||
// To use this, just add directories using AddDirectory, and listen for the
|
||||
// GameLoaded and GameRemoved signals. Ignore the PathChanged signal, it's
|
||||
// only there because the Qt people made fileChanged and directoryChanged
|
||||
// private.
|
||||
// GameLoaded and GameRemoved signals.
|
||||
class GameTracker final : public QFileSystemWatcher
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit GameTracker(QObject* parent = nullptr);
|
||||
~GameTracker();
|
||||
|
||||
void AddDirectory(const QString& dir);
|
||||
void RemoveDirectory(const QString& dir);
|
||||
|
@ -36,28 +30,15 @@ signals:
|
|||
void GameLoaded(QSharedPointer<GameFile> game);
|
||||
void GameRemoved(const QString& path);
|
||||
|
||||
void PathChanged(const QString& path);
|
||||
|
||||
private:
|
||||
void LoadGame(const QString& path);
|
||||
void UpdateDirectory(const QString& dir);
|
||||
void UpdateFile(const QString& path);
|
||||
QSet<QString> FindMissingFiles(const QString& dir);
|
||||
|
||||
// game path -> directories that track it
|
||||
QMap<QString, QSet<QString>> m_tracked_files;
|
||||
QThread m_loader_thread;
|
||||
GameLoader* m_loader;
|
||||
};
|
||||
|
||||
class GameLoader final : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
void LoadGame(const QString& path);
|
||||
|
||||
signals:
|
||||
void GameLoaded(QSharedPointer<GameFile> game);
|
||||
Common::WorkQueueThread<QString> m_load_thread;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(QSharedPointer<GameFile>)
|
||||
|
|
|
@ -9,8 +9,6 @@
|
|||
#include <QObject>
|
||||
#include <QVector>
|
||||
|
||||
#include "Common/NonCopyable.h"
|
||||
|
||||
#include "Core/NetPlayClient.h"
|
||||
#include "Core/NetPlayServer.h"
|
||||
|
||||
|
@ -23,11 +21,16 @@ class GameListModel;
|
|||
class InputConfig;
|
||||
|
||||
// UI settings to be stored in the config directory.
|
||||
class Settings final : public QObject, NonCopyable
|
||||
class Settings final : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Settings(const Settings&) = delete;
|
||||
Settings& operator=(const Settings&) = delete;
|
||||
Settings(Settings&&) = delete;
|
||||
Settings& operator=(Settings&&) = delete;
|
||||
|
||||
static Settings& Instance();
|
||||
|
||||
// UI
|
||||
|
|
|
@ -203,8 +203,9 @@ void AdvancedConfigPane::UpdateCPUClock()
|
|||
int percent = static_cast<int>(std::round(SConfig::GetInstance().m_OCFactor * 100.f));
|
||||
int clock = static_cast<int>(std::round(SConfig::GetInstance().m_OCFactor * core_clock));
|
||||
|
||||
m_clock_override_text->SetLabel(
|
||||
SConfig::GetInstance().m_OCEnable ? wxString::Format("%d %% (%d MHz)", percent, clock) : wxString(""));
|
||||
m_clock_override_text->SetLabel(SConfig::GetInstance().m_OCEnable ?
|
||||
wxString::Format("%d %% (%d MHz)", percent, clock) :
|
||||
wxString());
|
||||
}
|
||||
|
||||
void AdvancedConfigPane::LoadCustomRTC()
|
||||
|
|
|
@ -352,15 +352,15 @@ FramebufferManager::FramebufferManager(int targetWidth, int targetHeight, int ms
|
|||
ProgramShaderCache::CompileShader(m_EfbPokes,
|
||||
StringFromFormat(
|
||||
"in vec2 rawpos;\n"
|
||||
"in vec4 color0;\n" // color
|
||||
"in int color1;\n" // depth
|
||||
"in vec4 rawcolor0;\n" // color
|
||||
"in int rawcolor1;\n" // depth
|
||||
"out vec4 v_c;\n"
|
||||
"out float v_z;\n"
|
||||
"void main(void) {\n"
|
||||
" gl_Position = vec4(((rawpos + 0.5) / vec2(640.0, 528.0) * 2.0 - 1.0) * vec2(1.0, -1.0), 0.0, 1.0);\n"
|
||||
" gl_PointSize = %d.0 / 640.0;\n"
|
||||
" v_c = color0.bgra;\n"
|
||||
" v_z = float(color1 & 0xFFFFFF) / 16777216.0;\n"
|
||||
" v_c = rawcolor0.bgra;\n"
|
||||
" v_z = float(rawcolor1 & 0xFFFFFF) / 16777216.0;\n"
|
||||
"}\n", m_targetWidth).c_str(),
|
||||
|
||||
StringFromFormat(
|
||||
|
|
|
@ -13,12 +13,17 @@
|
|||
namespace OGL
|
||||
{
|
||||
|
||||
class SamplerCache : NonCopyable
|
||||
class SamplerCache
|
||||
{
|
||||
public:
|
||||
SamplerCache();
|
||||
~SamplerCache();
|
||||
|
||||
SamplerCache(const SamplerCache&) = delete;
|
||||
SamplerCache& operator=(const SamplerCache&) = delete;
|
||||
SamplerCache(SamplerCache&&) = delete;
|
||||
SamplerCache& operator=(SamplerCache&&) = delete;
|
||||
|
||||
void SetSamplerState(u32 stage, const TexMode0& tm0, const TexMode1& tm1, bool custom_tex);
|
||||
void Clear();
|
||||
void BindNearestSampler(u32 stage);
|
||||
|
|
|
@ -765,12 +765,6 @@ namespace TexDecoder
|
|||
|
||||
// Decodes all known Gamecube/Wii texture formats.
|
||||
// by ector
|
||||
|
||||
bool IsCompressed(HostTextureFormat format)
|
||||
{
|
||||
return format >= PC_TEX_FMT_DXT1 && format <= PC_TEX_FMT_BPTC;
|
||||
}
|
||||
|
||||
s32 GetTexelSizeInNibbles(s32 format)
|
||||
{
|
||||
switch (format & 0x3f) {
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
#pragma once
|
||||
#include <limits>
|
||||
|
||||
#include "Common/NonCopyable.h"
|
||||
#include "Common/Swap.h"
|
||||
|
||||
#include "VideoCommon/CPMemory.h"
|
||||
|
@ -145,12 +144,15 @@ struct PortableVertexDeclaration
|
|||
|
||||
// Note that this class can't just invent arbitrary vertex formats out of its input -
|
||||
// all the data loading code must always be made compatible.
|
||||
class NativeVertexFormat : NonCopyable
|
||||
class NativeVertexFormat
|
||||
{
|
||||
public:
|
||||
virtual ~NativeVertexFormat()
|
||||
{}
|
||||
|
||||
NativeVertexFormat(const NativeVertexFormat&) = delete;
|
||||
NativeVertexFormat& operator=(const NativeVertexFormat&) = delete;
|
||||
NativeVertexFormat(NativeVertexFormat&&) = default;
|
||||
NativeVertexFormat& operator=(NativeVertexFormat&&) = default;
|
||||
virtual void SetupVertexPointers() = 0;
|
||||
|
||||
u32 GetVertexStride() const
|
||||
|
|
|
@ -778,10 +778,8 @@ bool Renderer::IsFrameDumping()
|
|||
if (m_screenshot_request.IsSet())
|
||||
return true;
|
||||
|
||||
#if defined(HAVE_LIBAV) || defined(_WIN32)
|
||||
if (SConfig::GetInstance().m_DumpFrames)
|
||||
return true;
|
||||
#endif
|
||||
|
||||
ShutdownFrameDumping();
|
||||
return false;
|
||||
|
|
|
@ -114,7 +114,10 @@ enum HostTextureFormat
|
|||
|
||||
namespace TexDecoder
|
||||
{
|
||||
bool IsCompressed(HostTextureFormat format);
|
||||
static inline bool IsCompressed(HostTextureFormat format)
|
||||
{
|
||||
return format >= PC_TEX_FMT_DXT1 && format <= PC_TEX_FMT_BPTC;
|
||||
}
|
||||
u32 GetTexelSizeInNibbles(u32 format);
|
||||
u32 GetTextureSizeInBytes(u32 width, u32 height, u32 format);
|
||||
u32 GetBlockWidthInTexels(u32 format);
|
||||
|
@ -129,4 +132,4 @@ void DecodeTexel(u8 *dst, const u8 *src, u32 s, u32 t, u32 imageWidth, u32 texfo
|
|||
void DecodeTexelRGBA8FromTmem(u8 *dst, const u8 *src_ar, const u8* src_gb, u32 s, u32 t, u32 imageWidth);
|
||||
void DecodeTexelBGRA8FromTmem(u8 *dst, const u8 *src_ar, const u8* src_gb, u32 s, u32 t, u32 imageWidth);
|
||||
void SetTexFmtOverlayOptions(bool enable, bool center);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -775,12 +775,6 @@ namespace TexDecoder
|
|||
|
||||
// Decodes all known Gamecube/Wii texture formats.
|
||||
// by ector
|
||||
|
||||
bool IsCompressed(HostTextureFormat format)
|
||||
{
|
||||
return format >= PC_TEX_FMT_DXT1 && format <= PC_TEX_FMT_BPTC;
|
||||
}
|
||||
|
||||
u32 GetTexelSizeInNibbles(u32 format)
|
||||
{
|
||||
switch (format & 0x3f)
|
||||
|
@ -3322,4 +3316,4 @@ HostTextureFormat DecodeBGRA8FromTmem(u32* dst, const u8 *src_ar, const u8 *src_
|
|||
}
|
||||
return PC_TEX_FMT_BGRA32;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue