mirror of
https://github.com/PabloMK7/citra.git
synced 2024-11-26 17:34:04 +00:00
Fix merge conflicts
This commit is contained in:
commit
8ba9ac0f74
288 changed files with 17413 additions and 13969 deletions
16
.gitignore
vendored
16
.gitignore
vendored
|
@ -8,3 +8,19 @@ src/common/scm_rev.cpp
|
||||||
# Project/editor files
|
# Project/editor files
|
||||||
*.swp
|
*.swp
|
||||||
.idea/
|
.idea/
|
||||||
|
|
||||||
|
# *nix related
|
||||||
|
# Common convention for backup or temporary files
|
||||||
|
*~
|
||||||
|
|
||||||
|
# OSX global filetypes
|
||||||
|
# Created by Finder or Spotlight in directories for various OS functionality (indexing, etc)
|
||||||
|
.DS_Store
|
||||||
|
.AppleDouble
|
||||||
|
.LSOverride
|
||||||
|
.Spotlight-V100
|
||||||
|
.Trashes
|
||||||
|
|
||||||
|
# Windows global filetypes
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
|
6
.gitmodules
vendored
6
.gitmodules
vendored
|
@ -1,3 +1,9 @@
|
||||||
[submodule "externals/inih/inih"]
|
[submodule "externals/inih/inih"]
|
||||||
path = externals/inih/inih
|
path = externals/inih/inih
|
||||||
url = https://github.com/svn2github/inih
|
url = https://github.com/svn2github/inih
|
||||||
|
[submodule "externals/boost"]
|
||||||
|
path = externals/boost
|
||||||
|
url = https://github.com/citra-emu/ext-boost.git
|
||||||
|
[submodule "externals/nihstro"]
|
||||||
|
path = externals/nihstro
|
||||||
|
url = https://github.com/neobrain/nihstro.git
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
set -x
|
||||||
|
|
||||||
#if OS is linux or is not set
|
#if OS is linux or is not set
|
||||||
if [ "$TRAVIS_OS_NAME" = linux -o -z "$TRAVIS_OS_NAME" ]; then
|
if [ "$TRAVIS_OS_NAME" = linux -o -z "$TRAVIS_OS_NAME" ]; then
|
||||||
|
|
|
@ -1,17 +1,22 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
set -x
|
||||||
|
|
||||||
#if OS is linux or is not set
|
#if OS is linux or is not set
|
||||||
if [ "$TRAVIS_OS_NAME" = linux -o -z "$TRAVIS_OS_NAME" ]; then
|
if [ "$TRAVIS_OS_NAME" = linux -o -z "$TRAVIS_OS_NAME" ]; then
|
||||||
sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y
|
sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y
|
||||||
sudo apt-get -qq update
|
sudo apt-get -qq update
|
||||||
sudo apt-get -qq install g++-4.8 xorg-dev libglu1-mesa-dev libxcursor-dev
|
sudo apt-get -qq install g++-4.9 xorg-dev libglu1-mesa-dev libxcursor-dev
|
||||||
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.8 90
|
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.9 90
|
||||||
(
|
(
|
||||||
git clone https://github.com/glfw/glfw.git --branch 3.0.4 --depth 1
|
git clone https://github.com/glfw/glfw.git --branch 3.0.4 --depth 1
|
||||||
mkdir glfw/build && cd glfw/build
|
mkdir glfw/build && cd glfw/build
|
||||||
cmake .. && make -j2 && sudo make install
|
cmake -DBUILD_SHARED_LIBS=ON \
|
||||||
|
-DGLFW_BUILD_EXAMPLES=OFF \
|
||||||
|
-DGLFW_BUILD_TESTS=OFF \
|
||||||
|
..
|
||||||
|
make -j4 && sudo make install
|
||||||
)
|
)
|
||||||
|
|
||||||
sudo apt-get install lib32stdc++6
|
sudo apt-get install lib32stdc++6
|
||||||
|
@ -19,6 +24,8 @@ if [ "$TRAVIS_OS_NAME" = linux -o -z "$TRAVIS_OS_NAME" ]; then
|
||||||
curl http://www.cmake.org/files/v2.8/cmake-2.8.11-Linux-i386.tar.gz \
|
curl http://www.cmake.org/files/v2.8/cmake-2.8.11-Linux-i386.tar.gz \
|
||||||
| sudo tar -xz -C /usr/local --strip-components=1
|
| sudo tar -xz -C /usr/local --strip-components=1
|
||||||
elif [ "$TRAVIS_OS_NAME" = osx ]; then
|
elif [ "$TRAVIS_OS_NAME" = osx ]; then
|
||||||
|
export HOMEBREW_CACHE="$PWD/.homebrew-cache"
|
||||||
|
mkdir -p "$HOMEBREW_CACHE"
|
||||||
brew tap homebrew/versions
|
brew tap homebrew/versions
|
||||||
brew install qt5 glfw3 pkgconfig
|
brew install qt5 glfw3 pkgconfig
|
||||||
fi
|
fi
|
||||||
|
|
|
@ -4,6 +4,11 @@ os:
|
||||||
|
|
||||||
language: cpp
|
language: cpp
|
||||||
|
|
||||||
|
cache:
|
||||||
|
apt: true
|
||||||
|
directories:
|
||||||
|
- .homebrew-cache
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
- sh .travis-deps.sh
|
- sh .travis-deps.sh
|
||||||
|
|
||||||
|
|
|
@ -6,16 +6,48 @@ project(citra)
|
||||||
|
|
||||||
if (NOT MSVC)
|
if (NOT MSVC)
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wno-attributes")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wno-attributes")
|
||||||
|
add_definitions(-pthread)
|
||||||
else()
|
else()
|
||||||
# Silence deprecation warnings
|
# Silence deprecation warnings
|
||||||
add_definitions(/D_CRT_SECURE_NO_WARNINGS)
|
add_definitions(/D_CRT_SECURE_NO_WARNINGS)
|
||||||
|
# set up output paths for executable binaries (.exe-files, and .dll-files on DLL-capable platforms)
|
||||||
|
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||||
|
|
||||||
|
# Tweak optimization settings
|
||||||
|
# As far as I can tell, there's no way to override the CMake defaults while leaving user
|
||||||
|
# changes intact, so we'll just clobber everything and say sorry.
|
||||||
|
message(STATUS "Cache compiler flags ignored, please edit CMakeFiles.txt to change the flags.")
|
||||||
|
# /MD - Multi-threaded runtime
|
||||||
|
# /Ox - Full optimization
|
||||||
|
# /Oi - Use intrinsic functions
|
||||||
|
# /Oy- - Don't omit frame pointer
|
||||||
|
# /GR- - Disable RTTI
|
||||||
|
# /GS- - No stack buffer overflow checks
|
||||||
|
# /EHsc - C++-only exception handling semantics
|
||||||
|
set(optimization_flags "/MD /Ox /Oi /Oy- /DNDEBUG /GR- /GS- /EHsc")
|
||||||
|
# /Zi - Output debugging information
|
||||||
|
# /Zo - enahnced debug info for optimized builds
|
||||||
|
set(CMAKE_C_FLAGS_RELEASE "${optimization_flags} /Zi" CACHE STRING "" FORCE)
|
||||||
|
set(CMAKE_CXX_FLAGS_RELEASE "${optimization_flags} /Zi" CACHE STRING "" FORCE)
|
||||||
|
set(CMAKE_C_FLAGS_RELWITHDEBINFO "${optimization_flags} /Zi /Zo" CACHE STRING "" FORCE)
|
||||||
|
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${optimization_flags} /Zi /Zo" CACHE STRING "" FORCE)
|
||||||
endif()
|
endif()
|
||||||
add_definitions(-DSINGLETHREADED)
|
add_definitions(-DSINGLETHREADED)
|
||||||
|
|
||||||
find_package(PNG)
|
find_package(PNG QUIET)
|
||||||
if (PNG_FOUND)
|
if (PNG_FOUND)
|
||||||
add_definitions(-DHAVE_PNG)
|
add_definitions(-DHAVE_PNG)
|
||||||
endif ()
|
else()
|
||||||
|
message(STATUS "libpng not found. Some debugging features have been disabled.")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
find_package(Boost 1.57.0)
|
||||||
|
if (Boost_FOUND)
|
||||||
|
include_directories(${Boost_INCLUDE_DIRS})
|
||||||
|
else()
|
||||||
|
message(STATUS "Boost 1.57.0 or newer not found, falling back to externals")
|
||||||
|
include_directories(externals/boost)
|
||||||
|
endif()
|
||||||
|
|
||||||
# Include bundled CMake modules
|
# Include bundled CMake modules
|
||||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/externals/cmake-modules")
|
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/externals/cmake-modules")
|
||||||
|
@ -65,10 +97,6 @@ if (ENABLE_GLFW)
|
||||||
|
|
||||||
set(GLFW_LIBRARIES glfw3)
|
set(GLFW_LIBRARIES glfw3)
|
||||||
else()
|
else()
|
||||||
if (NOT APPLE)
|
|
||||||
find_package(X11 REQUIRED)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
find_package(PkgConfig REQUIRED)
|
find_package(PkgConfig REQUIRED)
|
||||||
pkg_search_module(GLFW REQUIRED glfw3)
|
pkg_search_module(GLFW REQUIRED glfw3)
|
||||||
endif()
|
endif()
|
||||||
|
@ -131,6 +159,8 @@ set(INI_PREFIX "${CMAKE_CURRENT_SOURCE_DIR}/externals/inih")
|
||||||
include_directories(${INI_PREFIX})
|
include_directories(${INI_PREFIX})
|
||||||
add_subdirectory(${INI_PREFIX})
|
add_subdirectory(${INI_PREFIX})
|
||||||
|
|
||||||
|
include_directories(externals/nihstro/include)
|
||||||
|
|
||||||
# process subdirectories
|
# process subdirectories
|
||||||
if(ENABLE_QT)
|
if(ENABLE_QT)
|
||||||
include_directories(externals/qhexedit)
|
include_directories(externals/qhexedit)
|
||||||
|
|
|
@ -27,6 +27,7 @@ Follow the indentation/whitespace style shown below. Do not use tabs, use 4-spac
|
||||||
### Comments
|
### Comments
|
||||||
* For regular comments, use C++ style (`//`) comments, even for multi-line ones.
|
* For regular comments, use C++ style (`//`) comments, even for multi-line ones.
|
||||||
* For doc-comments (Doxygen comments), use `/// ` if it's a single line, else use the `/**` `*/` style featured in the example. Start the text on the second line, not the first containing `/**`.
|
* For doc-comments (Doxygen comments), use `/// ` if it's a single line, else use the `/**` `*/` style featured in the example. Start the text on the second line, not the first containing `/**`.
|
||||||
|
* For items that are both defined and declared in two separate files, put the doc-comment only next to the associated declaration. (In a header file, usually.) Otherwise, put it next to the implementation. Never duplicate doc-comments in both places.
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
namespace Example {
|
namespace Example {
|
||||||
|
|
2
Doxyfile
2
Doxyfile
|
@ -419,7 +419,7 @@ LOOKUP_CACHE_SIZE = 0
|
||||||
# normally produced when WARNINGS is set to YES.
|
# normally produced when WARNINGS is set to YES.
|
||||||
# The default value is: NO.
|
# The default value is: NO.
|
||||||
|
|
||||||
EXTRACT_ALL = NO
|
EXTRACT_ALL = YES
|
||||||
|
|
||||||
# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will
|
# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will
|
||||||
# be included in the documentation.
|
# be included in the documentation.
|
||||||
|
|
22
README.md
22
README.md
|
@ -1,17 +1,31 @@
|
||||||
citra emulator
|
Citra Emulator
|
||||||
==============
|
==============
|
||||||
[![Travis CI Build Status](https://travis-ci.org/citra-emu/citra.svg)](https://travis-ci.org/citra-emu/citra)
|
[![Travis CI Build Status](https://travis-ci.org/citra-emu/citra.svg)](https://travis-ci.org/citra-emu/citra)
|
||||||
|
|
||||||
An experimental open-source Nintendo 3DS emulator/debugger written in C++. Citra is written with portability in mind, with builds actively maintained for Windows, Linux and OS X. At this time, it only emulates a subset of 3DS hardware, and therefore is generally only useful for booting/debugging very simple homebrew demos. Citra is licensed under the GPLv2. Refer to the license.txt file included. Please read the [FAQ](https://github.com/citra-emu/citra/wiki/FAQ) before getting started with the project.
|
Citra is an experimental open-source Nintendo 3DS emulator/debugger written in C++. It is written with portability in mind, with builds actively maintained for Windows, Linux and OS X. Citra only emulates a subset of 3DS hardware, and therefore is generally only useful for running/debugging homebrew applications. At this time, Citra is even able to boot several commercial games! None of these run to a playable state, but we are working every day to advance the project forward.
|
||||||
|
|
||||||
For development discussion, please join us @ #citra on [freenode](http://webchat.freenode.net/).
|
Citra is licensed under the GPLv2. Refer to the license.txt file included. Please read the [FAQ](https://github.com/citra-emu/citra/wiki/FAQ) before getting started with the project.
|
||||||
|
|
||||||
|
For development discussion, please join us @ #citra on [freenode](http://webchat.freenode.net/?channels=citra).
|
||||||
|
|
||||||
### Development
|
### Development
|
||||||
|
|
||||||
If you want to contribute please take a took at the [Contributor's Guide](CONTRIBUTING.md), [Roadmap](https://github.com/citra-emu/citra/wiki/Roadmap) and [Developer Information](https://github.com/citra-emu/citra/wiki/Developer-Information) pages. You should as well contact any of the developers in the forum in order to know about the current state of the emulator.
|
If you want to contribute please take a look at the [Contributor's Guide](CONTRIBUTING.md), [Roadmap](https://github.com/citra-emu/citra/wiki/Roadmap) and [Developer Information](https://github.com/citra-emu/citra/wiki/Developer-Information) pages. You should as well contact any of the developers in the forum in order to know about the current state of the emulator.
|
||||||
|
|
||||||
### Building
|
### Building
|
||||||
|
|
||||||
* __Windows__: [Windows Build](https://github.com/citra-emu/citra/wiki/Windows-Build)
|
* __Windows__: [Windows Build](https://github.com/citra-emu/citra/wiki/Windows-Build)
|
||||||
* __Linux__: [Linux Build](https://github.com/citra-emu/citra/wiki/Linux-Build)
|
* __Linux__: [Linux Build](https://github.com/citra-emu/citra/wiki/Linux-Build)
|
||||||
* __OSX__: [OS X Build](https://github.com/citra-emu/citra/wiki/OS-X-Build)
|
* __OSX__: [OS X Build](https://github.com/citra-emu/citra/wiki/OS-X-Build)
|
||||||
|
|
||||||
|
|
||||||
|
### Support
|
||||||
|
If you like, you can [donate by PayPal](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=K899FANUJ2ZXW) - any donation received will go towards things like:
|
||||||
|
* 3DS consoles for developers to explore the hardware
|
||||||
|
* 3DS games for testing
|
||||||
|
* Any equipment required for homebrew
|
||||||
|
* Infrastructure setup
|
||||||
|
* Eventually 3D displays to get proper 3D output working
|
||||||
|
* ... etc ...
|
||||||
|
|
||||||
|
We also more than gladly accept used 3DS consoles, preferrably ones with firmware 4.5 or lower! If you would like to give yours away, don't hesitate to join our IRC channel #citra on [Freenode](http://webchat.freenode.net/?channels=citra) and talk to neobrain or bunnei. Mind you, IRC is slow-paced, so it might be a while until people reply. If you're in a hurry you can just leave contact details in the channel or via private message and we'll get back to you.
|
||||||
|
|
1
externals/boost
vendored
Submodule
1
externals/boost
vendored
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 97052c28acb141dbf3c5e14114af99045344b695
|
1
externals/nihstro
vendored
Submodule
1
externals/nihstro
vendored
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit fc71f8684d26ccf277ad68809c8bd7273141fe89
|
|
@ -12,25 +12,23 @@ set(HEADERS
|
||||||
|
|
||||||
create_directory_groups(${SRCS} ${HEADERS})
|
create_directory_groups(${SRCS} ${HEADERS})
|
||||||
|
|
||||||
# NOTE: This is a workaround for CMake bug 0006976 (missing X11_xf86vmode_LIB variable)
|
|
||||||
if (NOT X11_xf86vmode_LIB)
|
|
||||||
set(X11_xv86vmode_LIB Xxf86vm)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
add_executable(citra ${SRCS} ${HEADERS})
|
add_executable(citra ${SRCS} ${HEADERS})
|
||||||
target_link_libraries(citra core common video_core)
|
target_link_libraries(citra core common video_core)
|
||||||
target_link_libraries(citra ${GLFW_LIBRARIES} ${OPENGL_gl_LIBRARY} inih)
|
target_link_libraries(citra ${GLFW_LIBRARIES} ${OPENGL_gl_LIBRARY} inih)
|
||||||
|
|
||||||
|
if (UNIX)
|
||||||
|
target_link_libraries(citra -pthread)
|
||||||
|
endif()
|
||||||
|
|
||||||
if (APPLE)
|
if (APPLE)
|
||||||
target_link_libraries(citra iconv pthread ${COREFOUNDATION_LIBRARY})
|
target_link_libraries(citra iconv ${COREFOUNDATION_LIBRARY})
|
||||||
elseif (WIN32)
|
elseif (WIN32)
|
||||||
target_link_libraries(citra winmm)
|
target_link_libraries(citra winmm)
|
||||||
if (MINGW)
|
if (MINGW)
|
||||||
target_link_libraries(citra iconv)
|
target_link_libraries(citra iconv)
|
||||||
endif()
|
endif()
|
||||||
else() # Unix
|
else() # Unix
|
||||||
target_link_libraries(citra pthread rt)
|
target_link_libraries(citra rt)
|
||||||
target_link_libraries(citra ${X11_X11_LIB} ${X11_Xi_LIB} ${X11_Xcursor_LIB} ${X11_Xrandr_LIB} ${X11_xv86vmode_LIB})
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
#install(TARGETS citra RUNTIME DESTINATION ${bindir})
|
#install(TARGETS citra RUNTIME DESTINATION ${bindir})
|
||||||
|
|
|
@ -1,9 +1,14 @@
|
||||||
// Copyright 2014 Citra Emulator Project
|
// Copyright 2014 Citra Emulator Project
|
||||||
// Licensed under GPLv2
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
#include "common/common.h"
|
#include "common/common.h"
|
||||||
#include "common/log_manager.h"
|
#include "common/logging/text_formatter.h"
|
||||||
|
#include "common/logging/backend.h"
|
||||||
|
#include "common/logging/filter.h"
|
||||||
|
#include "common/scope_exit.h"
|
||||||
|
|
||||||
#include "core/settings.h"
|
#include "core/settings.h"
|
||||||
#include "core/system.h"
|
#include "core/system.h"
|
||||||
|
@ -15,17 +20,21 @@
|
||||||
|
|
||||||
/// Application entry point
|
/// Application entry point
|
||||||
int __cdecl main(int argc, char **argv) {
|
int __cdecl main(int argc, char **argv) {
|
||||||
LogManager::Init();
|
std::shared_ptr<Log::Logger> logger = Log::InitGlobalLogger();
|
||||||
|
Log::Filter log_filter(Log::Level::Debug);
|
||||||
|
std::thread logging_thread(Log::TextLoggingLoop, logger, &log_filter);
|
||||||
|
SCOPE_EXIT({
|
||||||
|
logger->Close();
|
||||||
|
logging_thread.join();
|
||||||
|
});
|
||||||
|
|
||||||
if (argc < 2) {
|
if (argc < 2) {
|
||||||
ERROR_LOG(BOOT, "Failed to load ROM: No ROM specified");
|
LOG_CRITICAL(Frontend, "Failed to load ROM: No ROM specified");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Config config;
|
Config config;
|
||||||
|
log_filter.ParseFilterString(Settings::values.log_filter);
|
||||||
if (!Settings::values.enable_log)
|
|
||||||
LogManager::Shutdown();
|
|
||||||
|
|
||||||
std::string boot_filename = argv[1];
|
std::string boot_filename = argv[1];
|
||||||
EmuWindow_GLFW* emu_window = new EmuWindow_GLFW;
|
EmuWindow_GLFW* emu_window = new EmuWindow_GLFW;
|
||||||
|
@ -34,7 +43,7 @@ int __cdecl main(int argc, char **argv) {
|
||||||
|
|
||||||
Loader::ResultStatus load_result = Loader::LoadFile(boot_filename);
|
Loader::ResultStatus load_result = Loader::LoadFile(boot_filename);
|
||||||
if (Loader::ResultStatus::Success != load_result) {
|
if (Loader::ResultStatus::Success != load_result) {
|
||||||
ERROR_LOG(BOOT, "Failed to load ROM (Error %i)!", load_result);
|
LOG_CRITICAL(Frontend, "Failed to load ROM (Error %i)!", load_result);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Copyright 2014 Citra Emulator Project
|
// Copyright 2014 Citra Emulator Project
|
||||||
// Licensed under GPLv2
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#include <GLFW/glfw3.h>
|
#include <GLFW/glfw3.h>
|
||||||
|
@ -22,21 +22,22 @@ Config::Config() {
|
||||||
bool Config::LoadINI(INIReader* config, const char* location, const std::string& default_contents, bool retry) {
|
bool Config::LoadINI(INIReader* config, const char* location, const std::string& default_contents, bool retry) {
|
||||||
if (config->ParseError() < 0) {
|
if (config->ParseError() < 0) {
|
||||||
if (retry) {
|
if (retry) {
|
||||||
ERROR_LOG(CONFIG, "Failed to load %s. Creating file from defaults...", location);
|
LOG_WARNING(Config, "Failed to load %s. Creating file from defaults...", location);
|
||||||
FileUtil::CreateFullPath(location);
|
FileUtil::CreateFullPath(location);
|
||||||
FileUtil::WriteStringToFile(true, default_contents, location);
|
FileUtil::WriteStringToFile(true, default_contents, location);
|
||||||
*config = INIReader(location); // Reopen file
|
*config = INIReader(location); // Reopen file
|
||||||
|
|
||||||
return LoadINI(config, location, default_contents, false);
|
return LoadINI(config, location, default_contents, false);
|
||||||
}
|
}
|
||||||
ERROR_LOG(CONFIG, "Failed.");
|
LOG_ERROR(Config, "Failed.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
INFO_LOG(CONFIG, "Successfully loaded %s", location);
|
LOG_INFO(Config, "Successfully loaded %s", location);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Config::ReadControls() {
|
void Config::ReadValues() {
|
||||||
|
// Controls
|
||||||
Settings::values.pad_a_key = glfw_config->GetInteger("Controls", "pad_a", GLFW_KEY_A);
|
Settings::values.pad_a_key = glfw_config->GetInteger("Controls", "pad_a", GLFW_KEY_A);
|
||||||
Settings::values.pad_b_key = glfw_config->GetInteger("Controls", "pad_b", GLFW_KEY_S);
|
Settings::values.pad_b_key = glfw_config->GetInteger("Controls", "pad_b", GLFW_KEY_S);
|
||||||
Settings::values.pad_x_key = glfw_config->GetInteger("Controls", "pad_x", GLFW_KEY_Z);
|
Settings::values.pad_x_key = glfw_config->GetInteger("Controls", "pad_x", GLFW_KEY_Z);
|
||||||
|
@ -54,27 +55,22 @@ void Config::ReadControls() {
|
||||||
Settings::values.pad_sdown_key = glfw_config->GetInteger("Controls", "pad_sdown", GLFW_KEY_DOWN);
|
Settings::values.pad_sdown_key = glfw_config->GetInteger("Controls", "pad_sdown", GLFW_KEY_DOWN);
|
||||||
Settings::values.pad_sleft_key = glfw_config->GetInteger("Controls", "pad_sleft", GLFW_KEY_LEFT);
|
Settings::values.pad_sleft_key = glfw_config->GetInteger("Controls", "pad_sleft", GLFW_KEY_LEFT);
|
||||||
Settings::values.pad_sright_key = glfw_config->GetInteger("Controls", "pad_sright", GLFW_KEY_RIGHT);
|
Settings::values.pad_sright_key = glfw_config->GetInteger("Controls", "pad_sright", GLFW_KEY_RIGHT);
|
||||||
}
|
|
||||||
|
|
||||||
void Config::ReadCore() {
|
// Core
|
||||||
Settings::values.cpu_core = glfw_config->GetInteger("Core", "cpu_core", Core::CPU_Interpreter);
|
Settings::values.cpu_core = glfw_config->GetInteger("Core", "cpu_core", Core::CPU_Interpreter);
|
||||||
Settings::values.gpu_refresh_rate = glfw_config->GetInteger("Core", "gpu_refresh_rate", 60);
|
Settings::values.gpu_refresh_rate = glfw_config->GetInteger("Core", "gpu_refresh_rate", 30);
|
||||||
}
|
Settings::values.frame_skip = glfw_config->GetInteger("Core", "frame_skip", 0);
|
||||||
|
|
||||||
void Config::ReadData() {
|
// Data Storage
|
||||||
Settings::values.use_virtual_sd = glfw_config->GetBoolean("Data Storage", "use_virtual_sd", true);
|
Settings::values.use_virtual_sd = glfw_config->GetBoolean("Data Storage", "use_virtual_sd", true);
|
||||||
}
|
|
||||||
|
|
||||||
void Config::ReadMiscellaneous() {
|
// Miscellaneous
|
||||||
Settings::values.enable_log = glfw_config->GetBoolean("Miscellaneous", "enable_log", true);
|
Settings::values.log_filter = glfw_config->Get("Miscellaneous", "log_filter", "*:Info");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Config::Reload() {
|
void Config::Reload() {
|
||||||
LoadINI(glfw_config, glfw_config_loc.c_str(), DefaultINI::glfw_config_file);
|
LoadINI(glfw_config, glfw_config_loc.c_str(), DefaultINI::glfw_config_file);
|
||||||
ReadControls();
|
ReadValues();
|
||||||
ReadCore();
|
|
||||||
ReadData();
|
|
||||||
ReadMiscellaneous();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Config::~Config() {
|
Config::~Config() {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Copyright 2014 Citra Emulator Project
|
// Copyright 2014 Citra Emulator Project
|
||||||
// Licensed under GPLv2
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
@ -15,10 +15,7 @@ class Config {
|
||||||
std::string glfw_config_loc;
|
std::string glfw_config_loc;
|
||||||
|
|
||||||
bool LoadINI(INIReader* config, const char* location, const std::string& default_contents="", bool retry=true);
|
bool LoadINI(INIReader* config, const char* location, const std::string& default_contents="", bool retry=true);
|
||||||
void ReadControls();
|
void ReadValues();
|
||||||
void ReadCore();
|
|
||||||
void ReadData();
|
|
||||||
void ReadMiscellaneous();
|
|
||||||
public:
|
public:
|
||||||
Config();
|
Config();
|
||||||
~Config();
|
~Config();
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Copyright 2014 Citra Emulator Project
|
// Copyright 2014 Citra Emulator Project
|
||||||
// Licensed under GPLv2
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
@ -28,13 +28,14 @@ pad_sright =
|
||||||
|
|
||||||
[Core]
|
[Core]
|
||||||
cpu_core = ## 0: Interpreter (default), 1: FastInterpreter (experimental)
|
cpu_core = ## 0: Interpreter (default), 1: FastInterpreter (experimental)
|
||||||
gpu_refresh_rate = ## 60 (default)
|
gpu_refresh_rate = ## 30 (default)
|
||||||
|
frame_skip = ## 0: No frameskip (default), 1 : 2x frameskip, 2 : 4x frameskip, etc.
|
||||||
|
|
||||||
[Data Storage]
|
[Data Storage]
|
||||||
use_virtual_sd =
|
use_virtual_sd =
|
||||||
|
|
||||||
[Miscellaneous]
|
[Miscellaneous]
|
||||||
enable_log =
|
log_filter = *:Info ## Examples: *:Debug Kernel.SVC:Trace Service.*:Critical
|
||||||
)";
|
)";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Copyright 2014 Citra Emulator Project
|
// Copyright 2014 Citra Emulator Project
|
||||||
// Licensed under GPLv2
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#include <GLFW/glfw3.h>
|
#include <GLFW/glfw3.h>
|
||||||
|
@ -36,15 +36,15 @@ const bool EmuWindow_GLFW::IsOpen() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuWindow_GLFW::OnFramebufferResizeEvent(GLFWwindow* win, int width, int height) {
|
void EmuWindow_GLFW::OnFramebufferResizeEvent(GLFWwindow* win, int width, int height) {
|
||||||
_dbg_assert_(GUI, width > 0);
|
_dbg_assert_(Frontend, width > 0);
|
||||||
_dbg_assert_(GUI, height > 0);
|
_dbg_assert_(Frontend, height > 0);
|
||||||
|
|
||||||
GetEmuWindow(win)->NotifyFramebufferSizeChanged(std::pair<unsigned,unsigned>(width, height));
|
GetEmuWindow(win)->NotifyFramebufferSizeChanged(std::pair<unsigned,unsigned>(width, height));
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuWindow_GLFW::OnClientAreaResizeEvent(GLFWwindow* win, int width, int height) {
|
void EmuWindow_GLFW::OnClientAreaResizeEvent(GLFWwindow* win, int width, int height) {
|
||||||
_dbg_assert_(GUI, width > 0);
|
_dbg_assert_(Frontend, width > 0);
|
||||||
_dbg_assert_(GUI, height > 0);
|
_dbg_assert_(Frontend, height > 0);
|
||||||
|
|
||||||
// NOTE: GLFW provides no proper way to set a minimal window size.
|
// NOTE: GLFW provides no proper way to set a minimal window size.
|
||||||
// Hence, we just ignore the corresponding EmuWindow hint.
|
// Hence, we just ignore the corresponding EmuWindow hint.
|
||||||
|
@ -58,9 +58,13 @@ EmuWindow_GLFW::EmuWindow_GLFW() {
|
||||||
|
|
||||||
ReloadSetKeymaps();
|
ReloadSetKeymaps();
|
||||||
|
|
||||||
|
glfwSetErrorCallback([](int error, const char *desc){
|
||||||
|
LOG_ERROR(Frontend, "GLFW 0x%08x: %s", error, desc);
|
||||||
|
});
|
||||||
|
|
||||||
// Initialize the window
|
// Initialize the window
|
||||||
if(glfwInit() != GL_TRUE) {
|
if(glfwInit() != GL_TRUE) {
|
||||||
printf("Failed to initialize GLFW! Exiting...");
|
LOG_CRITICAL(Frontend, "Failed to initialize GLFW! Exiting...");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||||
|
@ -72,10 +76,10 @@ EmuWindow_GLFW::EmuWindow_GLFW() {
|
||||||
std::string window_title = Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc);
|
std::string window_title = Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc);
|
||||||
m_render_window = glfwCreateWindow(VideoCore::kScreenTopWidth,
|
m_render_window = glfwCreateWindow(VideoCore::kScreenTopWidth,
|
||||||
(VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight),
|
(VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight),
|
||||||
window_title.c_str(), NULL, NULL);
|
window_title.c_str(), nullptr, nullptr);
|
||||||
|
|
||||||
if (m_render_window == NULL) {
|
if (m_render_window == nullptr) {
|
||||||
printf("Failed to create GLFW window! Exiting...");
|
LOG_CRITICAL(Frontend, "Failed to create GLFW window! Exiting...");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,7 +123,7 @@ void EmuWindow_GLFW::MakeCurrent() {
|
||||||
|
|
||||||
/// Releases (dunno if this is the "right" word) the GLFW context from the caller thread
|
/// Releases (dunno if this is the "right" word) the GLFW context from the caller thread
|
||||||
void EmuWindow_GLFW::DoneCurrent() {
|
void EmuWindow_GLFW::DoneCurrent() {
|
||||||
glfwMakeContextCurrent(NULL);
|
glfwMakeContextCurrent(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuWindow_GLFW::ReloadSetKeymaps() {
|
void EmuWindow_GLFW::ReloadSetKeymaps() {
|
||||||
|
@ -145,7 +149,7 @@ void EmuWindow_GLFW::OnMinimalClientAreaChangeRequest(const std::pair<unsigned,u
|
||||||
std::pair<int,int> current_size;
|
std::pair<int,int> current_size;
|
||||||
glfwGetWindowSize(m_render_window, ¤t_size.first, ¤t_size.second);
|
glfwGetWindowSize(m_render_window, ¤t_size.first, ¤t_size.second);
|
||||||
|
|
||||||
_dbg_assert_(GUI, (int)minimal_size.first > 0 && (int)minimal_size.second > 0);
|
_dbg_assert_(Frontend, (int)minimal_size.first > 0 && (int)minimal_size.second > 0);
|
||||||
int new_width = std::max(current_size.first, (int)minimal_size.first);
|
int new_width = std::max(current_size.first, (int)minimal_size.first);
|
||||||
int new_height = std::max(current_size.second, (int)minimal_size.second);
|
int new_height = std::max(current_size.second, (int)minimal_size.second);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Copyright 2014 Citra Emulator Project
|
// Copyright 2014 Citra Emulator Project
|
||||||
// Licensed under GPLv2
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
|
@ -8,9 +8,12 @@ set(SRCS
|
||||||
debugger/callstack.cpp
|
debugger/callstack.cpp
|
||||||
debugger/disassembler.cpp
|
debugger/disassembler.cpp
|
||||||
debugger/graphics.cpp
|
debugger/graphics.cpp
|
||||||
|
debugger/graphics_breakpoints.cpp
|
||||||
debugger/graphics_cmdlists.cpp
|
debugger/graphics_cmdlists.cpp
|
||||||
|
debugger/graphics_framebuffer.cpp
|
||||||
debugger/ramview.cpp
|
debugger/ramview.cpp
|
||||||
debugger/registers.cpp
|
debugger/registers.cpp
|
||||||
|
util/spinbox.cpp
|
||||||
bootmanager.cpp
|
bootmanager.cpp
|
||||||
hotkeys.cpp
|
hotkeys.cpp
|
||||||
main.cpp
|
main.cpp
|
||||||
|
@ -23,9 +26,13 @@ set(HEADERS
|
||||||
debugger/callstack.hxx
|
debugger/callstack.hxx
|
||||||
debugger/disassembler.hxx
|
debugger/disassembler.hxx
|
||||||
debugger/graphics.hxx
|
debugger/graphics.hxx
|
||||||
|
debugger/graphics_breakpoints.hxx
|
||||||
|
debugger/graphics_breakpoints_p.hxx
|
||||||
debugger/graphics_cmdlists.hxx
|
debugger/graphics_cmdlists.hxx
|
||||||
|
debugger/graphics_framebuffer.hxx
|
||||||
debugger/ramview.hxx
|
debugger/ramview.hxx
|
||||||
debugger/registers.hxx
|
debugger/registers.hxx
|
||||||
|
util/spinbox.hxx
|
||||||
bootmanager.hxx
|
bootmanager.hxx
|
||||||
hotkeys.hxx
|
hotkeys.hxx
|
||||||
main.hxx
|
main.hxx
|
||||||
|
@ -53,6 +60,10 @@ add_executable(citra-qt ${SRCS} ${HEADERS} ${UI_HDRS})
|
||||||
target_link_libraries(citra-qt core common video_core qhexedit)
|
target_link_libraries(citra-qt core common video_core qhexedit)
|
||||||
target_link_libraries(citra-qt ${OPENGL_gl_LIBRARY} ${CITRA_QT_LIBS})
|
target_link_libraries(citra-qt ${OPENGL_gl_LIBRARY} ${CITRA_QT_LIBS})
|
||||||
|
|
||||||
|
if (UNIX)
|
||||||
|
target_link_libraries(citra-qt -pthread)
|
||||||
|
endif()
|
||||||
|
|
||||||
if (APPLE)
|
if (APPLE)
|
||||||
target_link_libraries(citra-qt iconv ${COREFOUNDATION_LIBRARY})
|
target_link_libraries(citra-qt iconv ${COREFOUNDATION_LIBRARY})
|
||||||
elseif (WIN32)
|
elseif (WIN32)
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/settings.h"
|
#include "core/settings.h"
|
||||||
|
|
||||||
|
#include "video_core/debug_utils/debug_utils.h"
|
||||||
|
|
||||||
#include "video_core/video_core.h"
|
#include "video_core/video_core.h"
|
||||||
|
|
||||||
#include "citra_qt/version.h"
|
#include "citra_qt/version.h"
|
||||||
|
@ -60,26 +62,33 @@ void EmuThread::Stop()
|
||||||
{
|
{
|
||||||
if (!isRunning())
|
if (!isRunning())
|
||||||
{
|
{
|
||||||
INFO_LOG(MASTER_LOG, "EmuThread::Stop called while emu thread wasn't running, returning...");
|
LOG_WARNING(Frontend, "EmuThread::Stop called while emu thread wasn't running, returning...");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
stop_run = true;
|
stop_run = true;
|
||||||
|
|
||||||
|
// Release emu threads from any breakpoints, so that this doesn't hang forever.
|
||||||
|
Pica::g_debug_context->ClearBreakpoints();
|
||||||
|
|
||||||
//core::g_state = core::SYS_DIE;
|
//core::g_state = core::SYS_DIE;
|
||||||
|
|
||||||
wait(500);
|
// TODO: Waiting here is just a bad workaround for retarded shutdown logic.
|
||||||
if (isRunning())
|
|
||||||
{
|
|
||||||
WARN_LOG(MASTER_LOG, "EmuThread still running, terminating...");
|
|
||||||
quit();
|
|
||||||
wait(1000);
|
wait(1000);
|
||||||
if (isRunning())
|
if (isRunning())
|
||||||
{
|
{
|
||||||
WARN_LOG(MASTER_LOG, "EmuThread STILL running, something is wrong here...");
|
LOG_WARNING(Frontend, "EmuThread still running, terminating...");
|
||||||
|
quit();
|
||||||
|
|
||||||
|
// TODO: Waiting 50 seconds can be necessary if the logging subsystem has a lot of spam
|
||||||
|
// queued... This should be fixed.
|
||||||
|
wait(50000);
|
||||||
|
if (isRunning())
|
||||||
|
{
|
||||||
|
LOG_CRITICAL(Frontend, "EmuThread STILL running, something is wrong here...");
|
||||||
terminate();
|
terminate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
INFO_LOG(MASTER_LOG, "EmuThread stopped");
|
LOG_INFO(Frontend, "EmuThread stopped");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -230,7 +239,7 @@ QByteArray GRenderWindow::saveGeometry()
|
||||||
{
|
{
|
||||||
// If we are a top-level widget, store the current geometry
|
// If we are a top-level widget, store the current geometry
|
||||||
// otherwise, store the last backup
|
// otherwise, store the last backup
|
||||||
if (parent() == NULL)
|
if (parent() == nullptr)
|
||||||
return ((QGLWidget*)this)->saveGeometry();
|
return ((QGLWidget*)this)->saveGeometry();
|
||||||
else
|
else
|
||||||
return geometry;
|
return geometry;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Copyright 2014 Citra Emulator Project
|
// Copyright 2014 Citra Emulator Project
|
||||||
// Licensed under GPLv2
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
@ -21,7 +21,7 @@ Config::Config() {
|
||||||
Reload();
|
Reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Config::ReadControls() {
|
void Config::ReadValues() {
|
||||||
qt_config->beginGroup("Controls");
|
qt_config->beginGroup("Controls");
|
||||||
Settings::values.pad_a_key = qt_config->value("pad_a", Qt::Key_A).toInt();
|
Settings::values.pad_a_key = qt_config->value("pad_a", Qt::Key_A).toInt();
|
||||||
Settings::values.pad_b_key = qt_config->value("pad_b", Qt::Key_S).toInt();
|
Settings::values.pad_b_key = qt_config->value("pad_b", Qt::Key_S).toInt();
|
||||||
|
@ -41,9 +41,23 @@ void Config::ReadControls() {
|
||||||
Settings::values.pad_sleft_key = qt_config->value("pad_sleft", Qt::Key_Left).toInt();
|
Settings::values.pad_sleft_key = qt_config->value("pad_sleft", Qt::Key_Left).toInt();
|
||||||
Settings::values.pad_sright_key = qt_config->value("pad_sright", Qt::Key_Right).toInt();
|
Settings::values.pad_sright_key = qt_config->value("pad_sright", Qt::Key_Right).toInt();
|
||||||
qt_config->endGroup();
|
qt_config->endGroup();
|
||||||
|
|
||||||
|
qt_config->beginGroup("Core");
|
||||||
|
Settings::values.cpu_core = qt_config->value("cpu_core", Core::CPU_Interpreter).toInt();
|
||||||
|
Settings::values.gpu_refresh_rate = qt_config->value("gpu_refresh_rate", 30).toInt();
|
||||||
|
Settings::values.frame_skip = qt_config->value("frame_skip", 0).toInt();
|
||||||
|
qt_config->endGroup();
|
||||||
|
|
||||||
|
qt_config->beginGroup("Data Storage");
|
||||||
|
Settings::values.use_virtual_sd = qt_config->value("use_virtual_sd", true).toBool();
|
||||||
|
qt_config->endGroup();
|
||||||
|
|
||||||
|
qt_config->beginGroup("Miscellaneous");
|
||||||
|
Settings::values.log_filter = qt_config->value("log_filter", "*:Info").toString().toStdString();
|
||||||
|
qt_config->endGroup();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Config::SaveControls() {
|
void Config::SaveValues() {
|
||||||
qt_config->beginGroup("Controls");
|
qt_config->beginGroup("Controls");
|
||||||
qt_config->setValue("pad_a", Settings::values.pad_a_key);
|
qt_config->setValue("pad_a", Settings::values.pad_a_key);
|
||||||
qt_config->setValue("pad_b", Settings::values.pad_b_key);
|
qt_config->setValue("pad_b", Settings::values.pad_b_key);
|
||||||
|
@ -63,58 +77,28 @@ void Config::SaveControls() {
|
||||||
qt_config->setValue("pad_sleft", Settings::values.pad_sleft_key);
|
qt_config->setValue("pad_sleft", Settings::values.pad_sleft_key);
|
||||||
qt_config->setValue("pad_sright", Settings::values.pad_sright_key);
|
qt_config->setValue("pad_sright", Settings::values.pad_sright_key);
|
||||||
qt_config->endGroup();
|
qt_config->endGroup();
|
||||||
}
|
|
||||||
|
|
||||||
void Config::ReadCore() {
|
|
||||||
qt_config->beginGroup("Core");
|
|
||||||
Settings::values.cpu_core = qt_config->value("cpu_core", Core::CPU_Interpreter).toInt();
|
|
||||||
Settings::values.gpu_refresh_rate = qt_config->value("gpu_refresh_rate", 60).toInt();
|
|
||||||
qt_config->endGroup();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Config::SaveCore() {
|
|
||||||
qt_config->beginGroup("Core");
|
qt_config->beginGroup("Core");
|
||||||
qt_config->setValue("cpu_core", Settings::values.cpu_core);
|
qt_config->setValue("cpu_core", Settings::values.cpu_core);
|
||||||
qt_config->setValue("gpu_refresh_rate", Settings::values.gpu_refresh_rate);
|
qt_config->setValue("gpu_refresh_rate", Settings::values.gpu_refresh_rate);
|
||||||
|
qt_config->setValue("frame_skip", Settings::values.frame_skip);
|
||||||
qt_config->endGroup();
|
qt_config->endGroup();
|
||||||
}
|
|
||||||
|
|
||||||
void Config::ReadData() {
|
|
||||||
qt_config->beginGroup("Data Storage");
|
|
||||||
Settings::values.use_virtual_sd = qt_config->value("use_virtual_sd", true).toBool();
|
|
||||||
qt_config->endGroup();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Config::SaveData() {
|
|
||||||
qt_config->beginGroup("Data Storage");
|
qt_config->beginGroup("Data Storage");
|
||||||
qt_config->setValue("use_virtual_sd", Settings::values.use_virtual_sd);
|
qt_config->setValue("use_virtual_sd", Settings::values.use_virtual_sd);
|
||||||
qt_config->endGroup();
|
qt_config->endGroup();
|
||||||
}
|
|
||||||
|
|
||||||
void Config::ReadMiscellaneous() {
|
|
||||||
qt_config->beginGroup("Miscellaneous");
|
qt_config->beginGroup("Miscellaneous");
|
||||||
Settings::values.enable_log = qt_config->value("enable_log", true).toBool();
|
qt_config->setValue("log_filter", QString::fromStdString(Settings::values.log_filter));
|
||||||
qt_config->endGroup();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Config::SaveMiscellaneous() {
|
|
||||||
qt_config->beginGroup("Miscellaneous");
|
|
||||||
qt_config->setValue("enable_log", Settings::values.enable_log);
|
|
||||||
qt_config->endGroup();
|
qt_config->endGroup();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Config::Reload() {
|
void Config::Reload() {
|
||||||
ReadControls();
|
ReadValues();
|
||||||
ReadCore();
|
|
||||||
ReadData();
|
|
||||||
ReadMiscellaneous();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Config::Save() {
|
void Config::Save() {
|
||||||
SaveControls();
|
SaveValues();
|
||||||
SaveCore();
|
|
||||||
SaveData();
|
|
||||||
SaveMiscellaneous();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Config::~Config() {
|
Config::~Config() {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Copyright 2014 Citra Emulator Project
|
// Copyright 2014 Citra Emulator Project
|
||||||
// Licensed under GPLv2
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
@ -12,15 +12,8 @@ class Config {
|
||||||
QSettings* qt_config;
|
QSettings* qt_config;
|
||||||
std::string qt_config_loc;
|
std::string qt_config_loc;
|
||||||
|
|
||||||
void ReadControls();
|
void ReadValues();
|
||||||
void SaveControls();
|
void SaveValues();
|
||||||
void ReadCore();
|
|
||||||
void SaveCore();
|
|
||||||
void ReadData();
|
|
||||||
void SaveData();
|
|
||||||
|
|
||||||
void ReadMiscellaneous();
|
|
||||||
void SaveMiscellaneous();
|
|
||||||
public:
|
public:
|
||||||
Config();
|
Config();
|
||||||
~Config();
|
~Config();
|
||||||
|
|
|
@ -27,10 +27,10 @@ void CallstackWidget::OnCPUStepped()
|
||||||
ARM_Interface* app_core = Core::g_app_core;
|
ARM_Interface* app_core = Core::g_app_core;
|
||||||
|
|
||||||
u32 sp = app_core->GetReg(13); //stack pointer
|
u32 sp = app_core->GetReg(13); //stack pointer
|
||||||
u32 addr, ret_addr, call_addr, func_addr;
|
u32 ret_addr, call_addr, func_addr;
|
||||||
|
|
||||||
int counter = 0;
|
int counter = 0;
|
||||||
for (int addr = 0x10000000; addr >= sp; addr -= 4)
|
for (u32 addr = 0x10000000; addr >= sp; addr -= 4)
|
||||||
{
|
{
|
||||||
ret_addr = Memory::Read32(addr);
|
ret_addr = Memory::Read32(addr);
|
||||||
call_addr = ret_addr - 4; //get call address???
|
call_addr = ret_addr - 4; //get call address???
|
||||||
|
|
|
@ -220,7 +220,9 @@ void DisassemblerWidget::OnPause()
|
||||||
emu_thread.SetCpuRunning(false);
|
emu_thread.SetCpuRunning(false);
|
||||||
|
|
||||||
// TODO: By now, the CPU might not have actually stopped...
|
// TODO: By now, the CPU might not have actually stopped...
|
||||||
|
if (Core::g_app_core) {
|
||||||
model->SetNextInstruction(Core::g_app_core->GetPC());
|
model->SetNextInstruction(Core::g_app_core->GetPC());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DisassemblerWidget::OnToggleStartStop()
|
void DisassemblerWidget::OnToggleStartStop()
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Copyright 2014 Citra Emulator Project
|
// Copyright 2014 Citra Emulator Project
|
||||||
// Licensed under GPLv2
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#include "graphics.hxx"
|
#include "graphics.hxx"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Copyright 2014 Citra Emulator Project
|
// Copyright 2014 Citra Emulator Project
|
||||||
// Licensed under GPLv2
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
263
src/citra_qt/debugger/graphics_breakpoints.cpp
Normal file
263
src/citra_qt/debugger/graphics_breakpoints.cpp
Normal file
|
@ -0,0 +1,263 @@
|
||||||
|
// Copyright 2014 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <QMetaType>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QTreeWidget>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <QLabel>
|
||||||
|
|
||||||
|
#include "graphics_breakpoints.hxx"
|
||||||
|
#include "graphics_breakpoints_p.hxx"
|
||||||
|
|
||||||
|
BreakPointModel::BreakPointModel(std::shared_ptr<Pica::DebugContext> debug_context, QObject* parent)
|
||||||
|
: QAbstractListModel(parent), context_weak(debug_context),
|
||||||
|
at_breakpoint(debug_context->at_breakpoint),
|
||||||
|
active_breakpoint(debug_context->active_breakpoint)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int BreakPointModel::columnCount(const QModelIndex& parent) const
|
||||||
|
{
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
int BreakPointModel::rowCount(const QModelIndex& parent) const
|
||||||
|
{
|
||||||
|
return static_cast<int>(Pica::DebugContext::Event::NumEvents);
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant BreakPointModel::data(const QModelIndex& index, int role) const
|
||||||
|
{
|
||||||
|
const auto event = static_cast<Pica::DebugContext::Event>(index.row());
|
||||||
|
|
||||||
|
switch (role) {
|
||||||
|
case Qt::DisplayRole:
|
||||||
|
{
|
||||||
|
switch (index.column()) {
|
||||||
|
case 0:
|
||||||
|
{
|
||||||
|
static const std::map<Pica::DebugContext::Event, QString> map = {
|
||||||
|
{ Pica::DebugContext::Event::CommandLoaded, tr("Pica command loaded") },
|
||||||
|
{ Pica::DebugContext::Event::CommandProcessed, tr("Pica command processed") },
|
||||||
|
{ Pica::DebugContext::Event::IncomingPrimitiveBatch, tr("Incoming primitive batch") },
|
||||||
|
{ Pica::DebugContext::Event::FinishedPrimitiveBatch, tr("Finished primitive batch") },
|
||||||
|
{ Pica::DebugContext::Event::VertexLoaded, tr("Vertex Loaded") }
|
||||||
|
};
|
||||||
|
|
||||||
|
_dbg_assert_(Debug_GPU, map.size() == static_cast<size_t>(Pica::DebugContext::Event::NumEvents));
|
||||||
|
|
||||||
|
return (map.find(event) != map.end()) ? map.at(event) : QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
return data(index, Role_IsEnabled).toBool() ? tr("Enabled") : tr("Disabled");
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Qt::BackgroundRole:
|
||||||
|
{
|
||||||
|
if (at_breakpoint && index.row() == static_cast<int>(active_breakpoint)) {
|
||||||
|
return QBrush(QColor(0xE0, 0xE0, 0x10));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Role_IsEnabled:
|
||||||
|
{
|
||||||
|
auto context = context_weak.lock();
|
||||||
|
return context && context->breakpoints[event].enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant BreakPointModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||||
|
{
|
||||||
|
switch(role) {
|
||||||
|
case Qt::DisplayRole:
|
||||||
|
{
|
||||||
|
if (section == 0) {
|
||||||
|
return tr("Event");
|
||||||
|
} else if (section == 1) {
|
||||||
|
return tr("Status");
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BreakPointModel::setData(const QModelIndex& index, const QVariant& value, int role)
|
||||||
|
{
|
||||||
|
const auto event = static_cast<Pica::DebugContext::Event>(index.row());
|
||||||
|
|
||||||
|
switch (role) {
|
||||||
|
case Role_IsEnabled:
|
||||||
|
{
|
||||||
|
auto context = context_weak.lock();
|
||||||
|
if (!context)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
context->breakpoints[event].enabled = value.toBool();
|
||||||
|
QModelIndex changed_index = createIndex(index.row(), 1);
|
||||||
|
emit dataChanged(changed_index, changed_index);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void BreakPointModel::OnBreakPointHit(Pica::DebugContext::Event event)
|
||||||
|
{
|
||||||
|
auto context = context_weak.lock();
|
||||||
|
if (!context)
|
||||||
|
return;
|
||||||
|
|
||||||
|
active_breakpoint = context->active_breakpoint;
|
||||||
|
at_breakpoint = context->at_breakpoint;
|
||||||
|
emit dataChanged(createIndex(static_cast<int>(event), 0),
|
||||||
|
createIndex(static_cast<int>(event), 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
void BreakPointModel::OnResumed()
|
||||||
|
{
|
||||||
|
auto context = context_weak.lock();
|
||||||
|
if (!context)
|
||||||
|
return;
|
||||||
|
|
||||||
|
at_breakpoint = context->at_breakpoint;
|
||||||
|
emit dataChanged(createIndex(static_cast<int>(active_breakpoint), 0),
|
||||||
|
createIndex(static_cast<int>(active_breakpoint), 1));
|
||||||
|
active_breakpoint = context->active_breakpoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
GraphicsBreakPointsWidget::GraphicsBreakPointsWidget(std::shared_ptr<Pica::DebugContext> debug_context,
|
||||||
|
QWidget* parent)
|
||||||
|
: QDockWidget(tr("Pica Breakpoints"), parent),
|
||||||
|
Pica::DebugContext::BreakPointObserver(debug_context)
|
||||||
|
{
|
||||||
|
setObjectName("PicaBreakPointsWidget");
|
||||||
|
|
||||||
|
status_text = new QLabel(tr("Emulation running"));
|
||||||
|
resume_button = new QPushButton(tr("Resume"));
|
||||||
|
resume_button->setEnabled(false);
|
||||||
|
|
||||||
|
breakpoint_model = new BreakPointModel(debug_context, this);
|
||||||
|
breakpoint_list = new QTreeView;
|
||||||
|
breakpoint_list->setModel(breakpoint_model);
|
||||||
|
|
||||||
|
toggle_breakpoint_button = new QPushButton(tr("Enable"));
|
||||||
|
toggle_breakpoint_button->setEnabled(false);
|
||||||
|
|
||||||
|
qRegisterMetaType<Pica::DebugContext::Event>("Pica::DebugContext::Event");
|
||||||
|
|
||||||
|
connect(resume_button, SIGNAL(clicked()), this, SLOT(OnResumeRequested()));
|
||||||
|
|
||||||
|
connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event,void*)),
|
||||||
|
this, SLOT(OnBreakPointHit(Pica::DebugContext::Event,void*)),
|
||||||
|
Qt::BlockingQueuedConnection);
|
||||||
|
connect(this, SIGNAL(Resumed()), this, SLOT(OnResumed()));
|
||||||
|
|
||||||
|
connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event,void*)),
|
||||||
|
breakpoint_model, SLOT(OnBreakPointHit(Pica::DebugContext::Event)),
|
||||||
|
Qt::BlockingQueuedConnection);
|
||||||
|
connect(this, SIGNAL(Resumed()), breakpoint_model, SLOT(OnResumed()));
|
||||||
|
|
||||||
|
connect(this, SIGNAL(BreakPointsChanged(const QModelIndex&,const QModelIndex&)),
|
||||||
|
breakpoint_model, SIGNAL(dataChanged(const QModelIndex&,const QModelIndex&)));
|
||||||
|
|
||||||
|
connect(breakpoint_list->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)),
|
||||||
|
this, SLOT(OnBreakpointSelectionChanged(QModelIndex)));
|
||||||
|
|
||||||
|
connect(toggle_breakpoint_button, SIGNAL(clicked()), this, SLOT(OnToggleBreakpointEnabled()));
|
||||||
|
|
||||||
|
QWidget* main_widget = new QWidget;
|
||||||
|
auto main_layout = new QVBoxLayout;
|
||||||
|
{
|
||||||
|
auto sub_layout = new QHBoxLayout;
|
||||||
|
sub_layout->addWidget(status_text);
|
||||||
|
sub_layout->addWidget(resume_button);
|
||||||
|
main_layout->addLayout(sub_layout);
|
||||||
|
}
|
||||||
|
main_layout->addWidget(breakpoint_list);
|
||||||
|
main_layout->addWidget(toggle_breakpoint_button);
|
||||||
|
main_widget->setLayout(main_layout);
|
||||||
|
|
||||||
|
setWidget(main_widget);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GraphicsBreakPointsWidget::OnPicaBreakPointHit(Event event, void* data)
|
||||||
|
{
|
||||||
|
// Process in GUI thread
|
||||||
|
emit BreakPointHit(event, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GraphicsBreakPointsWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data)
|
||||||
|
{
|
||||||
|
status_text->setText(tr("Emulation halted at breakpoint"));
|
||||||
|
resume_button->setEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GraphicsBreakPointsWidget::OnPicaResume()
|
||||||
|
{
|
||||||
|
// Process in GUI thread
|
||||||
|
emit Resumed();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GraphicsBreakPointsWidget::OnResumed()
|
||||||
|
{
|
||||||
|
status_text->setText(tr("Emulation running"));
|
||||||
|
resume_button->setEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GraphicsBreakPointsWidget::OnResumeRequested()
|
||||||
|
{
|
||||||
|
if (auto context = context_weak.lock())
|
||||||
|
context->Resume();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GraphicsBreakPointsWidget::OnBreakpointSelectionChanged(const QModelIndex& index)
|
||||||
|
{
|
||||||
|
if (!index.isValid()) {
|
||||||
|
toggle_breakpoint_button->setEnabled(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
toggle_breakpoint_button->setEnabled(true);
|
||||||
|
UpdateToggleBreakpointButton(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GraphicsBreakPointsWidget::OnToggleBreakpointEnabled()
|
||||||
|
{
|
||||||
|
QModelIndex index = breakpoint_list->selectionModel()->currentIndex();
|
||||||
|
bool new_state = !(breakpoint_model->data(index, BreakPointModel::Role_IsEnabled).toBool());
|
||||||
|
|
||||||
|
breakpoint_model->setData(index, new_state,
|
||||||
|
BreakPointModel::Role_IsEnabled);
|
||||||
|
UpdateToggleBreakpointButton(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GraphicsBreakPointsWidget::UpdateToggleBreakpointButton(const QModelIndex& index)
|
||||||
|
{
|
||||||
|
if (true == breakpoint_model->data(index, BreakPointModel::Role_IsEnabled).toBool()) {
|
||||||
|
toggle_breakpoint_button->setText(tr("Disable"));
|
||||||
|
} else {
|
||||||
|
toggle_breakpoint_button->setText(tr("Enable"));
|
||||||
|
}
|
||||||
|
}
|
53
src/citra_qt/debugger/graphics_breakpoints.hxx
Normal file
53
src/citra_qt/debugger/graphics_breakpoints.hxx
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
// Copyright 2014 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include <QAbstractListModel>
|
||||||
|
#include <QDockWidget>
|
||||||
|
|
||||||
|
#include "video_core/debug_utils/debug_utils.h"
|
||||||
|
|
||||||
|
class QLabel;
|
||||||
|
class QPushButton;
|
||||||
|
class QTreeView;
|
||||||
|
|
||||||
|
class BreakPointModel;
|
||||||
|
|
||||||
|
class GraphicsBreakPointsWidget : public QDockWidget, Pica::DebugContext::BreakPointObserver {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
using Event = Pica::DebugContext::Event;
|
||||||
|
|
||||||
|
public:
|
||||||
|
GraphicsBreakPointsWidget(std::shared_ptr<Pica::DebugContext> debug_context,
|
||||||
|
QWidget* parent = nullptr);
|
||||||
|
|
||||||
|
void OnPicaBreakPointHit(Pica::DebugContext::Event event, void* data) override;
|
||||||
|
void OnPicaResume() override;
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void OnBreakPointHit(Pica::DebugContext::Event event, void* data);
|
||||||
|
void OnResumeRequested();
|
||||||
|
void OnResumed();
|
||||||
|
void OnBreakpointSelectionChanged(const QModelIndex&);
|
||||||
|
void OnToggleBreakpointEnabled();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void Resumed();
|
||||||
|
void BreakPointHit(Pica::DebugContext::Event event, void* data);
|
||||||
|
void BreakPointsChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void UpdateToggleBreakpointButton(const QModelIndex& index);
|
||||||
|
|
||||||
|
QLabel* status_text;
|
||||||
|
QPushButton* resume_button;
|
||||||
|
QPushButton* toggle_breakpoint_button;
|
||||||
|
|
||||||
|
BreakPointModel* breakpoint_model;
|
||||||
|
QTreeView* breakpoint_list;
|
||||||
|
};
|
38
src/citra_qt/debugger/graphics_breakpoints_p.hxx
Normal file
38
src/citra_qt/debugger/graphics_breakpoints_p.hxx
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
// Copyright 2014 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include <QAbstractListModel>
|
||||||
|
|
||||||
|
#include "video_core/debug_utils/debug_utils.h"
|
||||||
|
|
||||||
|
class BreakPointModel : public QAbstractListModel {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum {
|
||||||
|
Role_IsEnabled = Qt::UserRole,
|
||||||
|
};
|
||||||
|
|
||||||
|
BreakPointModel(std::shared_ptr<Pica::DebugContext> context, QObject* parent);
|
||||||
|
|
||||||
|
int columnCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||||
|
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||||
|
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
|
||||||
|
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
|
||||||
|
|
||||||
|
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void OnBreakPointHit(Pica::DebugContext::Event event);
|
||||||
|
void OnResumed();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::weak_ptr<Pica::DebugContext> context_weak;
|
||||||
|
bool at_breakpoint;
|
||||||
|
Pica::DebugContext::Event active_breakpoint;
|
||||||
|
};
|
|
@ -1,31 +1,179 @@
|
||||||
// Copyright 2014 Citra Emulator Project
|
// Copyright 2014 Citra Emulator Project
|
||||||
// Licensed under GPLv2
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <QLabel>
|
||||||
#include <QListView>
|
#include <QListView>
|
||||||
|
#include <QMainWindow>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
#include <QTreeView>
|
#include <QTreeView>
|
||||||
|
#include <QSpinBox>
|
||||||
|
#include <QComboBox>
|
||||||
|
|
||||||
|
#include "video_core/pica.h"
|
||||||
|
#include "video_core/math.h"
|
||||||
|
|
||||||
|
#include "video_core/debug_utils/debug_utils.h"
|
||||||
|
|
||||||
#include "graphics_cmdlists.hxx"
|
#include "graphics_cmdlists.hxx"
|
||||||
|
|
||||||
GPUCommandListModel::GPUCommandListModel(QObject* parent) : QAbstractListModel(parent)
|
#include "util/spinbox.hxx"
|
||||||
{
|
|
||||||
|
QImage LoadTexture(u8* src, const Pica::DebugUtils::TextureInfo& info) {
|
||||||
|
QImage decoded_image(info.width, info.height, QImage::Format_ARGB32);
|
||||||
|
for (int y = 0; y < info.height; ++y) {
|
||||||
|
for (int x = 0; x < info.width; ++x) {
|
||||||
|
Math::Vec4<u8> color = Pica::DebugUtils::LookupTexture(src, x, y, info, true);
|
||||||
|
decoded_image.setPixel(x, y, qRgba(color.r(), color.g(), color.b(), color.a()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return decoded_image;
|
||||||
|
}
|
||||||
|
|
||||||
|
class TextureInfoWidget : public QWidget {
|
||||||
|
public:
|
||||||
|
TextureInfoWidget(u8* src, const Pica::DebugUtils::TextureInfo& info, QWidget* parent = nullptr) : QWidget(parent) {
|
||||||
|
QLabel* image_widget = new QLabel;
|
||||||
|
QPixmap image_pixmap = QPixmap::fromImage(LoadTexture(src, info));
|
||||||
|
image_pixmap = image_pixmap.scaled(200, 100, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||||
|
image_widget->setPixmap(image_pixmap);
|
||||||
|
|
||||||
|
QVBoxLayout* layout = new QVBoxLayout;
|
||||||
|
layout->addWidget(image_widget);
|
||||||
|
setLayout(layout);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TextureInfoDockWidget::TextureInfoDockWidget(const Pica::DebugUtils::TextureInfo& info, QWidget* parent)
|
||||||
|
: QDockWidget(tr("Texture 0x%1").arg(info.physical_address, 8, 16, QLatin1Char('0'))),
|
||||||
|
info(info) {
|
||||||
|
|
||||||
|
QWidget* main_widget = new QWidget;
|
||||||
|
|
||||||
|
QLabel* image_widget = new QLabel;
|
||||||
|
|
||||||
|
connect(this, SIGNAL(UpdatePixmap(const QPixmap&)), image_widget, SLOT(setPixmap(const QPixmap&)));
|
||||||
|
|
||||||
|
CSpinBox* phys_address_spinbox = new CSpinBox;
|
||||||
|
phys_address_spinbox->SetBase(16);
|
||||||
|
phys_address_spinbox->SetRange(0, 0xFFFFFFFF);
|
||||||
|
phys_address_spinbox->SetPrefix("0x");
|
||||||
|
phys_address_spinbox->SetValue(info.physical_address);
|
||||||
|
connect(phys_address_spinbox, SIGNAL(ValueChanged(qint64)), this, SLOT(OnAddressChanged(qint64)));
|
||||||
|
|
||||||
|
QComboBox* format_choice = new QComboBox;
|
||||||
|
format_choice->addItem(tr("RGBA8"));
|
||||||
|
format_choice->addItem(tr("RGB8"));
|
||||||
|
format_choice->addItem(tr("RGBA5551"));
|
||||||
|
format_choice->addItem(tr("RGB565"));
|
||||||
|
format_choice->addItem(tr("RGBA4"));
|
||||||
|
format_choice->addItem(tr("IA8"));
|
||||||
|
format_choice->addItem(tr("UNK6"));
|
||||||
|
format_choice->addItem(tr("I8"));
|
||||||
|
format_choice->addItem(tr("A8"));
|
||||||
|
format_choice->addItem(tr("IA4"));
|
||||||
|
format_choice->addItem(tr("UNK10"));
|
||||||
|
format_choice->addItem(tr("A4"));
|
||||||
|
format_choice->setCurrentIndex(static_cast<int>(info.format));
|
||||||
|
connect(format_choice, SIGNAL(currentIndexChanged(int)), this, SLOT(OnFormatChanged(int)));
|
||||||
|
|
||||||
|
QSpinBox* width_spinbox = new QSpinBox;
|
||||||
|
width_spinbox->setMaximum(65535);
|
||||||
|
width_spinbox->setValue(info.width);
|
||||||
|
connect(width_spinbox, SIGNAL(valueChanged(int)), this, SLOT(OnWidthChanged(int)));
|
||||||
|
|
||||||
|
QSpinBox* height_spinbox = new QSpinBox;
|
||||||
|
height_spinbox->setMaximum(65535);
|
||||||
|
height_spinbox->setValue(info.height);
|
||||||
|
connect(height_spinbox, SIGNAL(valueChanged(int)), this, SLOT(OnHeightChanged(int)));
|
||||||
|
|
||||||
|
QSpinBox* stride_spinbox = new QSpinBox;
|
||||||
|
stride_spinbox->setMaximum(65535 * 4);
|
||||||
|
stride_spinbox->setValue(info.stride);
|
||||||
|
connect(stride_spinbox, SIGNAL(valueChanged(int)), this, SLOT(OnStrideChanged(int)));
|
||||||
|
|
||||||
|
QVBoxLayout* main_layout = new QVBoxLayout;
|
||||||
|
main_layout->addWidget(image_widget);
|
||||||
|
|
||||||
|
{
|
||||||
|
QHBoxLayout* sub_layout = new QHBoxLayout;
|
||||||
|
sub_layout->addWidget(new QLabel(tr("Source Address:")));
|
||||||
|
sub_layout->addWidget(phys_address_spinbox);
|
||||||
|
main_layout->addLayout(sub_layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
QHBoxLayout* sub_layout = new QHBoxLayout;
|
||||||
|
sub_layout->addWidget(new QLabel(tr("Format")));
|
||||||
|
sub_layout->addWidget(format_choice);
|
||||||
|
main_layout->addLayout(sub_layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
QHBoxLayout* sub_layout = new QHBoxLayout;
|
||||||
|
sub_layout->addWidget(new QLabel(tr("Width:")));
|
||||||
|
sub_layout->addWidget(width_spinbox);
|
||||||
|
sub_layout->addStretch();
|
||||||
|
sub_layout->addWidget(new QLabel(tr("Height:")));
|
||||||
|
sub_layout->addWidget(height_spinbox);
|
||||||
|
sub_layout->addStretch();
|
||||||
|
sub_layout->addWidget(new QLabel(tr("Stride:")));
|
||||||
|
sub_layout->addWidget(stride_spinbox);
|
||||||
|
main_layout->addLayout(sub_layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
main_widget->setLayout(main_layout);
|
||||||
|
|
||||||
|
emit UpdatePixmap(ReloadPixmap());
|
||||||
|
|
||||||
|
setWidget(main_widget);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureInfoDockWidget::OnAddressChanged(qint64 value) {
|
||||||
|
info.physical_address = value;
|
||||||
|
emit UpdatePixmap(ReloadPixmap());
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureInfoDockWidget::OnFormatChanged(int value) {
|
||||||
|
info.format = static_cast<Pica::Regs::TextureFormat>(value);
|
||||||
|
emit UpdatePixmap(ReloadPixmap());
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureInfoDockWidget::OnWidthChanged(int value) {
|
||||||
|
info.width = value;
|
||||||
|
emit UpdatePixmap(ReloadPixmap());
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureInfoDockWidget::OnHeightChanged(int value) {
|
||||||
|
info.height = value;
|
||||||
|
emit UpdatePixmap(ReloadPixmap());
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureInfoDockWidget::OnStrideChanged(int value) {
|
||||||
|
info.stride = value;
|
||||||
|
emit UpdatePixmap(ReloadPixmap());
|
||||||
|
}
|
||||||
|
|
||||||
|
QPixmap TextureInfoDockWidget::ReloadPixmap() const {
|
||||||
|
u8* src = Memory::GetPointer(Pica::PAddrToVAddr(info.physical_address));
|
||||||
|
return QPixmap::fromImage(LoadTexture(src, info));
|
||||||
|
}
|
||||||
|
|
||||||
|
GPUCommandListModel::GPUCommandListModel(QObject* parent) : QAbstractListModel(parent) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int GPUCommandListModel::rowCount(const QModelIndex& parent) const
|
int GPUCommandListModel::rowCount(const QModelIndex& parent) const {
|
||||||
{
|
|
||||||
return pica_trace.writes.size();
|
return pica_trace.writes.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
int GPUCommandListModel::columnCount(const QModelIndex& parent) const
|
int GPUCommandListModel::columnCount(const QModelIndex& parent) const {
|
||||||
{
|
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant GPUCommandListModel::data(const QModelIndex& index, int role) const
|
QVariant GPUCommandListModel::data(const QModelIndex& index, int role) const {
|
||||||
{
|
|
||||||
if (!index.isValid())
|
if (!index.isValid())
|
||||||
return QVariant();
|
return QVariant();
|
||||||
|
|
||||||
|
@ -36,21 +184,39 @@ QVariant GPUCommandListModel::data(const QModelIndex& index, int role) const
|
||||||
if (role == Qt::DisplayRole) {
|
if (role == Qt::DisplayRole) {
|
||||||
QString content;
|
QString content;
|
||||||
if (index.column() == 0) {
|
if (index.column() == 0) {
|
||||||
content = QString::fromLatin1(Pica::Regs::GetCommandName(cmd.cmd_id).c_str());
|
QString content = QString::fromLatin1(Pica::Regs::GetCommandName(cmd.cmd_id).c_str());
|
||||||
content.append(" ");
|
content.append(" ");
|
||||||
|
return content;
|
||||||
} else if (index.column() == 1) {
|
} else if (index.column() == 1) {
|
||||||
content.append(QString("%1 ").arg(cmd.hex, 8, 16, QLatin1Char('0')));
|
QString content = QString("%1 ").arg(cmd.hex, 8, 16, QLatin1Char('0'));
|
||||||
content.append(QString("%1 ").arg(val, 8, 16, QLatin1Char('0')));
|
content.append(QString("%1 ").arg(val, 8, 16, QLatin1Char('0')));
|
||||||
|
return content;
|
||||||
}
|
}
|
||||||
|
} else if (role == CommandIdRole) {
|
||||||
return QVariant(content);
|
return QVariant::fromValue<int>(cmd.cmd_id.Value());
|
||||||
}
|
}
|
||||||
|
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GPUCommandListModel::OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace& trace)
|
QVariant GPUCommandListModel::headerData(int section, Qt::Orientation orientation, int role) const {
|
||||||
{
|
switch(role) {
|
||||||
|
case Qt::DisplayRole:
|
||||||
|
{
|
||||||
|
if (section == 0) {
|
||||||
|
return tr("Command Name");
|
||||||
|
} else if (section == 1) {
|
||||||
|
return tr("Data");
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GPUCommandListModel::OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace& trace) {
|
||||||
beginResetModel();
|
beginResetModel();
|
||||||
|
|
||||||
pica_trace = trace;
|
pica_trace = trace;
|
||||||
|
@ -58,38 +224,107 @@ void GPUCommandListModel::OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace&
|
||||||
endResetModel();
|
endResetModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define COMMAND_IN_RANGE(cmd_id, reg_name) \
|
||||||
|
(cmd_id >= PICA_REG_INDEX(reg_name) && \
|
||||||
|
cmd_id < PICA_REG_INDEX(reg_name) + sizeof(decltype(Pica::registers.reg_name)) / 4)
|
||||||
|
|
||||||
GPUCommandListWidget::GPUCommandListWidget(QWidget* parent) : QDockWidget(tr("Pica Command List"), parent)
|
void GPUCommandListWidget::OnCommandDoubleClicked(const QModelIndex& index) {
|
||||||
{
|
const int command_id = list_widget->model()->data(index, GPUCommandListModel::CommandIdRole).toInt();
|
||||||
|
if (COMMAND_IN_RANGE(command_id, texture0) ||
|
||||||
|
COMMAND_IN_RANGE(command_id, texture1) ||
|
||||||
|
COMMAND_IN_RANGE(command_id, texture2)) {
|
||||||
|
|
||||||
|
unsigned index;
|
||||||
|
if (COMMAND_IN_RANGE(command_id, texture0)) {
|
||||||
|
index = 0;
|
||||||
|
} else if (COMMAND_IN_RANGE(command_id, texture1)) {
|
||||||
|
index = 1;
|
||||||
|
} else {
|
||||||
|
index = 2;
|
||||||
|
}
|
||||||
|
auto config = Pica::registers.GetTextures()[index].config;
|
||||||
|
auto format = Pica::registers.GetTextures()[index].format;
|
||||||
|
auto info = Pica::DebugUtils::TextureInfo::FromPicaRegister(config, format);
|
||||||
|
|
||||||
|
// TODO: Instead, emit a signal here to be caught by the main window widget.
|
||||||
|
auto main_window = static_cast<QMainWindow*>(parent());
|
||||||
|
main_window->tabifyDockWidget(this, new TextureInfoDockWidget(info, main_window));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GPUCommandListWidget::SetCommandInfo(const QModelIndex& index) {
|
||||||
|
QWidget* new_info_widget;
|
||||||
|
|
||||||
|
const int command_id = list_widget->model()->data(index, GPUCommandListModel::CommandIdRole).toInt();
|
||||||
|
if (COMMAND_IN_RANGE(command_id, texture0) ||
|
||||||
|
COMMAND_IN_RANGE(command_id, texture1) ||
|
||||||
|
COMMAND_IN_RANGE(command_id, texture2)) {
|
||||||
|
|
||||||
|
unsigned index;
|
||||||
|
if (COMMAND_IN_RANGE(command_id, texture0)) {
|
||||||
|
index = 0;
|
||||||
|
} else if (COMMAND_IN_RANGE(command_id, texture1)) {
|
||||||
|
index = 1;
|
||||||
|
} else {
|
||||||
|
index = 2;
|
||||||
|
}
|
||||||
|
auto config = Pica::registers.GetTextures()[index].config;
|
||||||
|
auto format = Pica::registers.GetTextures()[index].format;
|
||||||
|
|
||||||
|
auto info = Pica::DebugUtils::TextureInfo::FromPicaRegister(config, format);
|
||||||
|
u8* src = Memory::GetPointer(Pica::PAddrToVAddr(config.GetPhysicalAddress()));
|
||||||
|
new_info_widget = new TextureInfoWidget(src, info);
|
||||||
|
} else {
|
||||||
|
new_info_widget = new QWidget;
|
||||||
|
}
|
||||||
|
|
||||||
|
widget()->layout()->removeWidget(command_info_widget);
|
||||||
|
delete command_info_widget;
|
||||||
|
widget()->layout()->addWidget(new_info_widget);
|
||||||
|
command_info_widget = new_info_widget;
|
||||||
|
}
|
||||||
|
#undef COMMAND_IN_RANGE
|
||||||
|
|
||||||
|
GPUCommandListWidget::GPUCommandListWidget(QWidget* parent) : QDockWidget(tr("Pica Command List"), parent) {
|
||||||
|
setObjectName("Pica Command List");
|
||||||
GPUCommandListModel* model = new GPUCommandListModel(this);
|
GPUCommandListModel* model = new GPUCommandListModel(this);
|
||||||
|
|
||||||
QWidget* main_widget = new QWidget;
|
QWidget* main_widget = new QWidget;
|
||||||
|
|
||||||
QTreeView* list_widget = new QTreeView;
|
list_widget = new QTreeView;
|
||||||
list_widget->setModel(model);
|
list_widget->setModel(model);
|
||||||
list_widget->setFont(QFont("monospace"));
|
list_widget->setFont(QFont("monospace"));
|
||||||
list_widget->setRootIsDecorated(false);
|
list_widget->setRootIsDecorated(false);
|
||||||
|
|
||||||
QPushButton* toggle_tracing = new QPushButton(tr("Start Tracing"));
|
connect(list_widget->selectionModel(), SIGNAL(currentChanged(const QModelIndex&,const QModelIndex&)),
|
||||||
|
this, SLOT(SetCommandInfo(const QModelIndex&)));
|
||||||
|
connect(list_widget, SIGNAL(doubleClicked(const QModelIndex&)),
|
||||||
|
this, SLOT(OnCommandDoubleClicked(const QModelIndex&)));
|
||||||
|
|
||||||
|
toggle_tracing = new QPushButton(tr("Start Tracing"));
|
||||||
|
|
||||||
connect(toggle_tracing, SIGNAL(clicked()), this, SLOT(OnToggleTracing()));
|
connect(toggle_tracing, SIGNAL(clicked()), this, SLOT(OnToggleTracing()));
|
||||||
connect(this, SIGNAL(TracingFinished(const Pica::DebugUtils::PicaTrace&)),
|
connect(this, SIGNAL(TracingFinished(const Pica::DebugUtils::PicaTrace&)),
|
||||||
model, SLOT(OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace&)));
|
model, SLOT(OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace&)));
|
||||||
|
|
||||||
|
command_info_widget = new QWidget;
|
||||||
|
|
||||||
QVBoxLayout* main_layout = new QVBoxLayout;
|
QVBoxLayout* main_layout = new QVBoxLayout;
|
||||||
main_layout->addWidget(list_widget);
|
main_layout->addWidget(list_widget);
|
||||||
main_layout->addWidget(toggle_tracing);
|
main_layout->addWidget(toggle_tracing);
|
||||||
|
main_layout->addWidget(command_info_widget);
|
||||||
main_widget->setLayout(main_layout);
|
main_widget->setLayout(main_layout);
|
||||||
|
|
||||||
setWidget(main_widget);
|
setWidget(main_widget);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GPUCommandListWidget::OnToggleTracing()
|
void GPUCommandListWidget::OnToggleTracing() {
|
||||||
{
|
|
||||||
if (!Pica::DebugUtils::IsPicaTracing()) {
|
if (!Pica::DebugUtils::IsPicaTracing()) {
|
||||||
Pica::DebugUtils::StartPicaTracing();
|
Pica::DebugUtils::StartPicaTracing();
|
||||||
|
toggle_tracing->setText(tr("Finish Tracing"));
|
||||||
} else {
|
} else {
|
||||||
pica_trace = Pica::DebugUtils::FinishPicaTracing();
|
pica_trace = Pica::DebugUtils::FinishPicaTracing();
|
||||||
emit TracingFinished(*pica_trace);
|
emit TracingFinished(*pica_trace);
|
||||||
|
toggle_tracing->setText(tr("Start Tracing"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Copyright 2014 Citra Emulator Project
|
// Copyright 2014 Citra Emulator Project
|
||||||
// Licensed under GPLv2
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
@ -10,16 +10,24 @@
|
||||||
#include "video_core/gpu_debugger.h"
|
#include "video_core/gpu_debugger.h"
|
||||||
#include "video_core/debug_utils/debug_utils.h"
|
#include "video_core/debug_utils/debug_utils.h"
|
||||||
|
|
||||||
|
class QPushButton;
|
||||||
|
class QTreeView;
|
||||||
|
|
||||||
class GPUCommandListModel : public QAbstractListModel
|
class GPUCommandListModel : public QAbstractListModel
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
enum {
|
||||||
|
CommandIdRole = Qt::UserRole,
|
||||||
|
};
|
||||||
|
|
||||||
GPUCommandListModel(QObject* parent);
|
GPUCommandListModel(QObject* parent);
|
||||||
|
|
||||||
int columnCount(const QModelIndex& parent = QModelIndex()) const override;
|
int columnCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||||
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
|
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||||
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
|
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
|
||||||
|
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace& trace);
|
void OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace& trace);
|
||||||
|
@ -37,10 +45,39 @@ public:
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void OnToggleTracing();
|
void OnToggleTracing();
|
||||||
|
void OnCommandDoubleClicked(const QModelIndex&);
|
||||||
|
|
||||||
|
void SetCommandInfo(const QModelIndex&);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void TracingFinished(const Pica::DebugUtils::PicaTrace&);
|
void TracingFinished(const Pica::DebugUtils::PicaTrace&);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<Pica::DebugUtils::PicaTrace> pica_trace;
|
std::unique_ptr<Pica::DebugUtils::PicaTrace> pica_trace;
|
||||||
|
|
||||||
|
QTreeView* list_widget;
|
||||||
|
QWidget* command_info_widget;
|
||||||
|
QPushButton* toggle_tracing;
|
||||||
|
};
|
||||||
|
|
||||||
|
class TextureInfoDockWidget : public QDockWidget {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
TextureInfoDockWidget(const Pica::DebugUtils::TextureInfo& info, QWidget* parent = nullptr);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void UpdatePixmap(const QPixmap& pixmap);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void OnAddressChanged(qint64 value);
|
||||||
|
void OnFormatChanged(int value);
|
||||||
|
void OnWidthChanged(int value);
|
||||||
|
void OnHeightChanged(int value);
|
||||||
|
void OnStrideChanged(int value);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QPixmap ReloadPixmap() const;
|
||||||
|
|
||||||
|
Pica::DebugUtils::TextureInfo info;
|
||||||
};
|
};
|
||||||
|
|
283
src/citra_qt/debugger/graphics_framebuffer.cpp
Normal file
283
src/citra_qt/debugger/graphics_framebuffer.cpp
Normal file
|
@ -0,0 +1,283 @@
|
||||||
|
// Copyright 2014 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <QBoxLayout>
|
||||||
|
#include <QComboBox>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QMetaType>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QSpinBox>
|
||||||
|
|
||||||
|
#include "video_core/pica.h"
|
||||||
|
|
||||||
|
#include "graphics_framebuffer.hxx"
|
||||||
|
|
||||||
|
#include "util/spinbox.hxx"
|
||||||
|
|
||||||
|
BreakPointObserverDock::BreakPointObserverDock(std::shared_ptr<Pica::DebugContext> debug_context,
|
||||||
|
const QString& title, QWidget* parent)
|
||||||
|
: QDockWidget(title, parent), BreakPointObserver(debug_context)
|
||||||
|
{
|
||||||
|
qRegisterMetaType<Pica::DebugContext::Event>("Pica::DebugContext::Event");
|
||||||
|
|
||||||
|
connect(this, SIGNAL(Resumed()), this, SLOT(OnResumed()));
|
||||||
|
|
||||||
|
// NOTE: This signal is emitted from a non-GUI thread, but connect() takes
|
||||||
|
// care of delaying its handling to the GUI thread.
|
||||||
|
connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event,void*)),
|
||||||
|
this, SLOT(OnBreakPointHit(Pica::DebugContext::Event,void*)),
|
||||||
|
Qt::BlockingQueuedConnection);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BreakPointObserverDock::OnPicaBreakPointHit(Pica::DebugContext::Event event, void* data)
|
||||||
|
{
|
||||||
|
emit BreakPointHit(event, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BreakPointObserverDock::OnPicaResume()
|
||||||
|
{
|
||||||
|
emit Resumed();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
GraphicsFramebufferWidget::GraphicsFramebufferWidget(std::shared_ptr<Pica::DebugContext> debug_context,
|
||||||
|
QWidget* parent)
|
||||||
|
: BreakPointObserverDock(debug_context, tr("Pica Framebuffer"), parent),
|
||||||
|
framebuffer_source(Source::PicaTarget)
|
||||||
|
{
|
||||||
|
setObjectName("PicaFramebuffer");
|
||||||
|
|
||||||
|
framebuffer_source_list = new QComboBox;
|
||||||
|
framebuffer_source_list->addItem(tr("Active Render Target"));
|
||||||
|
framebuffer_source_list->addItem(tr("Custom"));
|
||||||
|
framebuffer_source_list->setCurrentIndex(static_cast<int>(framebuffer_source));
|
||||||
|
|
||||||
|
framebuffer_address_control = new CSpinBox;
|
||||||
|
framebuffer_address_control->SetBase(16);
|
||||||
|
framebuffer_address_control->SetRange(0, 0xFFFFFFFF);
|
||||||
|
framebuffer_address_control->SetPrefix("0x");
|
||||||
|
|
||||||
|
framebuffer_width_control = new QSpinBox;
|
||||||
|
framebuffer_width_control->setMinimum(1);
|
||||||
|
framebuffer_width_control->setMaximum(std::numeric_limits<int>::max()); // TODO: Find actual maximum
|
||||||
|
|
||||||
|
framebuffer_height_control = new QSpinBox;
|
||||||
|
framebuffer_height_control->setMinimum(1);
|
||||||
|
framebuffer_height_control->setMaximum(std::numeric_limits<int>::max()); // TODO: Find actual maximum
|
||||||
|
|
||||||
|
framebuffer_format_control = new QComboBox;
|
||||||
|
framebuffer_format_control->addItem(tr("RGBA8"));
|
||||||
|
framebuffer_format_control->addItem(tr("RGB8"));
|
||||||
|
framebuffer_format_control->addItem(tr("RGBA5551"));
|
||||||
|
framebuffer_format_control->addItem(tr("RGB565"));
|
||||||
|
framebuffer_format_control->addItem(tr("RGBA4"));
|
||||||
|
|
||||||
|
// TODO: This QLabel should shrink the image to the available space rather than just expanding...
|
||||||
|
framebuffer_picture_label = new QLabel;
|
||||||
|
|
||||||
|
auto enlarge_button = new QPushButton(tr("Enlarge"));
|
||||||
|
|
||||||
|
// Connections
|
||||||
|
connect(this, SIGNAL(Update()), this, SLOT(OnUpdate()));
|
||||||
|
connect(framebuffer_source_list, SIGNAL(currentIndexChanged(int)), this, SLOT(OnFramebufferSourceChanged(int)));
|
||||||
|
connect(framebuffer_address_control, SIGNAL(ValueChanged(qint64)), this, SLOT(OnFramebufferAddressChanged(qint64)));
|
||||||
|
connect(framebuffer_width_control, SIGNAL(valueChanged(int)), this, SLOT(OnFramebufferWidthChanged(int)));
|
||||||
|
connect(framebuffer_height_control, SIGNAL(valueChanged(int)), this, SLOT(OnFramebufferHeightChanged(int)));
|
||||||
|
connect(framebuffer_format_control, SIGNAL(currentIndexChanged(int)), this, SLOT(OnFramebufferFormatChanged(int)));
|
||||||
|
|
||||||
|
auto main_widget = new QWidget;
|
||||||
|
auto main_layout = new QVBoxLayout;
|
||||||
|
{
|
||||||
|
auto sub_layout = new QHBoxLayout;
|
||||||
|
sub_layout->addWidget(new QLabel(tr("Source:")));
|
||||||
|
sub_layout->addWidget(framebuffer_source_list);
|
||||||
|
main_layout->addLayout(sub_layout);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto sub_layout = new QHBoxLayout;
|
||||||
|
sub_layout->addWidget(new QLabel(tr("Virtual Address:")));
|
||||||
|
sub_layout->addWidget(framebuffer_address_control);
|
||||||
|
main_layout->addLayout(sub_layout);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto sub_layout = new QHBoxLayout;
|
||||||
|
sub_layout->addWidget(new QLabel(tr("Width:")));
|
||||||
|
sub_layout->addWidget(framebuffer_width_control);
|
||||||
|
main_layout->addLayout(sub_layout);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto sub_layout = new QHBoxLayout;
|
||||||
|
sub_layout->addWidget(new QLabel(tr("Height:")));
|
||||||
|
sub_layout->addWidget(framebuffer_height_control);
|
||||||
|
main_layout->addLayout(sub_layout);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto sub_layout = new QHBoxLayout;
|
||||||
|
sub_layout->addWidget(new QLabel(tr("Format:")));
|
||||||
|
sub_layout->addWidget(framebuffer_format_control);
|
||||||
|
main_layout->addLayout(sub_layout);
|
||||||
|
}
|
||||||
|
main_layout->addWidget(framebuffer_picture_label);
|
||||||
|
main_layout->addWidget(enlarge_button);
|
||||||
|
main_widget->setLayout(main_layout);
|
||||||
|
setWidget(main_widget);
|
||||||
|
|
||||||
|
// Load current data - TODO: Make sure this works when emulation is not running
|
||||||
|
if (debug_context && debug_context->at_breakpoint)
|
||||||
|
emit Update();
|
||||||
|
widget()->setEnabled(false); // TODO: Only enable if currently at breakpoint
|
||||||
|
}
|
||||||
|
|
||||||
|
void GraphicsFramebufferWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data)
|
||||||
|
{
|
||||||
|
emit Update();
|
||||||
|
widget()->setEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GraphicsFramebufferWidget::OnResumed()
|
||||||
|
{
|
||||||
|
widget()->setEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GraphicsFramebufferWidget::OnFramebufferSourceChanged(int new_value)
|
||||||
|
{
|
||||||
|
framebuffer_source = static_cast<Source>(new_value);
|
||||||
|
emit Update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GraphicsFramebufferWidget::OnFramebufferAddressChanged(qint64 new_value)
|
||||||
|
{
|
||||||
|
if (framebuffer_address != new_value) {
|
||||||
|
framebuffer_address = static_cast<unsigned>(new_value);
|
||||||
|
|
||||||
|
framebuffer_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
|
||||||
|
emit Update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GraphicsFramebufferWidget::OnFramebufferWidthChanged(int new_value)
|
||||||
|
{
|
||||||
|
if (framebuffer_width != new_value) {
|
||||||
|
framebuffer_width = new_value;
|
||||||
|
|
||||||
|
framebuffer_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
|
||||||
|
emit Update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GraphicsFramebufferWidget::OnFramebufferHeightChanged(int new_value)
|
||||||
|
{
|
||||||
|
if (framebuffer_height != new_value) {
|
||||||
|
framebuffer_height = new_value;
|
||||||
|
|
||||||
|
framebuffer_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
|
||||||
|
emit Update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GraphicsFramebufferWidget::OnFramebufferFormatChanged(int new_value)
|
||||||
|
{
|
||||||
|
if (framebuffer_format != static_cast<Format>(new_value)) {
|
||||||
|
framebuffer_format = static_cast<Format>(new_value);
|
||||||
|
|
||||||
|
framebuffer_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
|
||||||
|
emit Update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GraphicsFramebufferWidget::OnUpdate()
|
||||||
|
{
|
||||||
|
QPixmap pixmap;
|
||||||
|
|
||||||
|
switch (framebuffer_source) {
|
||||||
|
case Source::PicaTarget:
|
||||||
|
{
|
||||||
|
// TODO: Store a reference to the registers in the debug context instead of accessing them directly...
|
||||||
|
|
||||||
|
auto framebuffer = Pica::registers.framebuffer;
|
||||||
|
using Framebuffer = decltype(framebuffer);
|
||||||
|
|
||||||
|
framebuffer_address = framebuffer.GetColorBufferPhysicalAddress();
|
||||||
|
framebuffer_width = framebuffer.GetWidth();
|
||||||
|
framebuffer_height = framebuffer.GetHeight();
|
||||||
|
framebuffer_format = static_cast<Format>(framebuffer.color_format);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Source::Custom:
|
||||||
|
{
|
||||||
|
// Keep user-specified values
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
qDebug() << "Unknown framebuffer source " << static_cast<int>(framebuffer_source);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Implement a good way to visualize alpha components!
|
||||||
|
// TODO: Unify this decoding code with the texture decoder
|
||||||
|
switch (framebuffer_format) {
|
||||||
|
case Format::RGBA8:
|
||||||
|
{
|
||||||
|
QImage decoded_image(framebuffer_width, framebuffer_height, QImage::Format_ARGB32);
|
||||||
|
u32* color_buffer = (u32*)Memory::GetPointer(Pica::PAddrToVAddr(framebuffer_address));
|
||||||
|
for (unsigned y = 0; y < framebuffer_height; ++y) {
|
||||||
|
for (unsigned x = 0; x < framebuffer_width; ++x) {
|
||||||
|
u32 value = *(color_buffer + x + y * framebuffer_width);
|
||||||
|
|
||||||
|
decoded_image.setPixel(x, y, qRgba((value >> 16) & 0xFF, (value >> 8) & 0xFF, value & 0xFF, 255/*value >> 24*/));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pixmap = QPixmap::fromImage(decoded_image);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Format::RGB8:
|
||||||
|
{
|
||||||
|
QImage decoded_image(framebuffer_width, framebuffer_height, QImage::Format_ARGB32);
|
||||||
|
u8* color_buffer = Memory::GetPointer(Pica::PAddrToVAddr(framebuffer_address));
|
||||||
|
for (unsigned y = 0; y < framebuffer_height; ++y) {
|
||||||
|
for (unsigned x = 0; x < framebuffer_width; ++x) {
|
||||||
|
u8* pixel_pointer = color_buffer + x * 3 + y * 3 * framebuffer_width;
|
||||||
|
|
||||||
|
decoded_image.setPixel(x, y, qRgba(pixel_pointer[0], pixel_pointer[1], pixel_pointer[2], 255/*value >> 24*/));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pixmap = QPixmap::fromImage(decoded_image);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Format::RGBA5551:
|
||||||
|
{
|
||||||
|
QImage decoded_image(framebuffer_width, framebuffer_height, QImage::Format_ARGB32);
|
||||||
|
u32* color_buffer = (u32*)Memory::GetPointer(Pica::PAddrToVAddr(framebuffer_address));
|
||||||
|
for (unsigned y = 0; y < framebuffer_height; ++y) {
|
||||||
|
for (unsigned x = 0; x < framebuffer_width; ++x) {
|
||||||
|
u16 value = *(u16*)(((u8*)color_buffer) + x * 2 + y * framebuffer_width * 2);
|
||||||
|
u8 r = (value >> 11) & 0x1F;
|
||||||
|
u8 g = (value >> 6) & 0x1F;
|
||||||
|
u8 b = (value >> 1) & 0x1F;
|
||||||
|
u8 a = value & 1;
|
||||||
|
|
||||||
|
decoded_image.setPixel(x, y, qRgba(r, g, b, 255/*a*/));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pixmap = QPixmap::fromImage(decoded_image);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
qDebug() << "Unknown fb color format " << static_cast<int>(framebuffer_format);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
framebuffer_address_control->SetValue(framebuffer_address);
|
||||||
|
framebuffer_width_control->setValue(framebuffer_width);
|
||||||
|
framebuffer_height_control->setValue(framebuffer_height);
|
||||||
|
framebuffer_format_control->setCurrentIndex(static_cast<int>(framebuffer_format));
|
||||||
|
framebuffer_picture_label->setPixmap(pixmap);
|
||||||
|
}
|
92
src/citra_qt/debugger/graphics_framebuffer.hxx
Normal file
92
src/citra_qt/debugger/graphics_framebuffer.hxx
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
// Copyright 2014 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QDockWidget>
|
||||||
|
|
||||||
|
#include "video_core/debug_utils/debug_utils.h"
|
||||||
|
|
||||||
|
class QComboBox;
|
||||||
|
class QLabel;
|
||||||
|
class QSpinBox;
|
||||||
|
|
||||||
|
class CSpinBox;
|
||||||
|
|
||||||
|
// Utility class which forwards calls to OnPicaBreakPointHit and OnPicaResume to public slots.
|
||||||
|
// This is because the Pica breakpoint callbacks are called from a non-GUI thread, while
|
||||||
|
// the widget usually wants to perform reactions in the GUI thread.
|
||||||
|
class BreakPointObserverDock : public QDockWidget, Pica::DebugContext::BreakPointObserver {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
BreakPointObserverDock(std::shared_ptr<Pica::DebugContext> debug_context, const QString& title,
|
||||||
|
QWidget* parent = nullptr);
|
||||||
|
|
||||||
|
void OnPicaBreakPointHit(Pica::DebugContext::Event event, void* data) override;
|
||||||
|
void OnPicaResume() override;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
virtual void OnBreakPointHit(Pica::DebugContext::Event event, void* data) = 0;
|
||||||
|
virtual void OnResumed() = 0;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void Resumed();
|
||||||
|
void BreakPointHit(Pica::DebugContext::Event event, void* data);
|
||||||
|
};
|
||||||
|
|
||||||
|
class GraphicsFramebufferWidget : public BreakPointObserverDock {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
using Event = Pica::DebugContext::Event;
|
||||||
|
|
||||||
|
enum class Source {
|
||||||
|
PicaTarget = 0,
|
||||||
|
Custom = 1,
|
||||||
|
|
||||||
|
// TODO: Add GPU framebuffer sources!
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Format {
|
||||||
|
RGBA8 = 0,
|
||||||
|
RGB8 = 1,
|
||||||
|
RGBA5551 = 2,
|
||||||
|
RGB565 = 3,
|
||||||
|
RGBA4 = 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
GraphicsFramebufferWidget(std::shared_ptr<Pica::DebugContext> debug_context, QWidget* parent = nullptr);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void OnFramebufferSourceChanged(int new_value);
|
||||||
|
void OnFramebufferAddressChanged(qint64 new_value);
|
||||||
|
void OnFramebufferWidthChanged(int new_value);
|
||||||
|
void OnFramebufferHeightChanged(int new_value);
|
||||||
|
void OnFramebufferFormatChanged(int new_value);
|
||||||
|
void OnUpdate();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void OnBreakPointHit(Pica::DebugContext::Event event, void* data) override;
|
||||||
|
void OnResumed() override;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void Update();
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
QComboBox* framebuffer_source_list;
|
||||||
|
CSpinBox* framebuffer_address_control;
|
||||||
|
QSpinBox* framebuffer_width_control;
|
||||||
|
QSpinBox* framebuffer_height_control;
|
||||||
|
QComboBox* framebuffer_format_control;
|
||||||
|
|
||||||
|
QLabel* framebuffer_picture_label;
|
||||||
|
|
||||||
|
Source framebuffer_source;
|
||||||
|
unsigned framebuffer_address;
|
||||||
|
unsigned framebuffer_width;
|
||||||
|
unsigned framebuffer_height;
|
||||||
|
Format framebuffer_format;
|
||||||
|
};
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
struct Hotkey
|
struct Hotkey
|
||||||
{
|
{
|
||||||
Hotkey() : shortcut(NULL), context(Qt::WindowShortcut) {}
|
Hotkey() : shortcut(nullptr), context(Qt::WindowShortcut) {}
|
||||||
|
|
||||||
QKeySequence keyseq;
|
QKeySequence keyseq;
|
||||||
QShortcut* shortcut;
|
QShortcut* shortcut;
|
||||||
|
@ -81,7 +81,7 @@ QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widge
|
||||||
Hotkey& hk = hotkey_groups[group][action];
|
Hotkey& hk = hotkey_groups[group][action];
|
||||||
|
|
||||||
if (!hk.shortcut)
|
if (!hk.shortcut)
|
||||||
hk.shortcut = new QShortcut(hk.keyseq, widget, NULL, NULL, hk.context);
|
hk.shortcut = new QShortcut(hk.keyseq, widget, nullptr, nullptr, hk.context);
|
||||||
|
|
||||||
return hk.shortcut;
|
return hk.shortcut;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
#include <QtGui>
|
#include <QtGui>
|
||||||
#include <QDesktopWidget>
|
#include <QDesktopWidget>
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
|
@ -5,8 +7,13 @@
|
||||||
#include "main.hxx"
|
#include "main.hxx"
|
||||||
|
|
||||||
#include "common/common.h"
|
#include "common/common.h"
|
||||||
|
#include "common/logging/text_formatter.h"
|
||||||
|
#include "common/logging/log.h"
|
||||||
|
#include "common/logging/backend.h"
|
||||||
|
#include "common/logging/filter.h"
|
||||||
#include "common/platform.h"
|
#include "common/platform.h"
|
||||||
#include "common/log_manager.h"
|
#include "common/scope_exit.h"
|
||||||
|
|
||||||
#if EMU_PLATFORM == PLATFORM_LINUX
|
#if EMU_PLATFORM == PLATFORM_LINUX
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#endif
|
#endif
|
||||||
|
@ -20,7 +27,9 @@
|
||||||
#include "debugger/callstack.hxx"
|
#include "debugger/callstack.hxx"
|
||||||
#include "debugger/ramview.hxx"
|
#include "debugger/ramview.hxx"
|
||||||
#include "debugger/graphics.hxx"
|
#include "debugger/graphics.hxx"
|
||||||
|
#include "debugger/graphics_breakpoints.hxx"
|
||||||
#include "debugger/graphics_cmdlists.hxx"
|
#include "debugger/graphics_cmdlists.hxx"
|
||||||
|
#include "debugger/graphics_framebuffer.hxx"
|
||||||
|
|
||||||
#include "core/settings.h"
|
#include "core/settings.h"
|
||||||
#include "core/system.h"
|
#include "core/system.h"
|
||||||
|
@ -31,16 +40,12 @@
|
||||||
|
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
|
||||||
|
|
||||||
GMainWindow::GMainWindow()
|
GMainWindow::GMainWindow()
|
||||||
{
|
{
|
||||||
LogManager::Init();
|
Pica::g_debug_context = Pica::DebugContext::Construct();
|
||||||
|
|
||||||
Config config;
|
Config config;
|
||||||
|
|
||||||
if (!Settings::values.enable_log)
|
|
||||||
LogManager::Shutdown();
|
|
||||||
|
|
||||||
ui.setupUi(this);
|
ui.setupUi(this);
|
||||||
statusBar()->hide();
|
statusBar()->hide();
|
||||||
|
|
||||||
|
@ -67,12 +72,22 @@ GMainWindow::GMainWindow()
|
||||||
addDockWidget(Qt::RightDockWidgetArea, graphicsCommandsWidget);
|
addDockWidget(Qt::RightDockWidgetArea, graphicsCommandsWidget);
|
||||||
graphicsCommandsWidget->hide();
|
graphicsCommandsWidget->hide();
|
||||||
|
|
||||||
|
auto graphicsBreakpointsWidget = new GraphicsBreakPointsWidget(Pica::g_debug_context, this);
|
||||||
|
addDockWidget(Qt::RightDockWidgetArea, graphicsBreakpointsWidget);
|
||||||
|
graphicsBreakpointsWidget->hide();
|
||||||
|
|
||||||
|
auto graphicsFramebufferWidget = new GraphicsFramebufferWidget(Pica::g_debug_context, this);
|
||||||
|
addDockWidget(Qt::RightDockWidgetArea, graphicsFramebufferWidget);
|
||||||
|
graphicsFramebufferWidget->hide();
|
||||||
|
|
||||||
QMenu* debug_menu = ui.menu_View->addMenu(tr("Debugging"));
|
QMenu* debug_menu = ui.menu_View->addMenu(tr("Debugging"));
|
||||||
debug_menu->addAction(disasmWidget->toggleViewAction());
|
debug_menu->addAction(disasmWidget->toggleViewAction());
|
||||||
debug_menu->addAction(registersWidget->toggleViewAction());
|
debug_menu->addAction(registersWidget->toggleViewAction());
|
||||||
debug_menu->addAction(callstackWidget->toggleViewAction());
|
debug_menu->addAction(callstackWidget->toggleViewAction());
|
||||||
debug_menu->addAction(graphicsWidget->toggleViewAction());
|
debug_menu->addAction(graphicsWidget->toggleViewAction());
|
||||||
debug_menu->addAction(graphicsCommandsWidget->toggleViewAction());
|
debug_menu->addAction(graphicsCommandsWidget->toggleViewAction());
|
||||||
|
debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction());
|
||||||
|
debug_menu->addAction(graphicsFramebufferWidget->toggleViewAction());
|
||||||
|
|
||||||
// Set default UI state
|
// Set default UI state
|
||||||
// geometry: 55% of the window contents are in the upper screen half, 45% in the lower half
|
// geometry: 55% of the window contents are in the upper screen half, 45% in the lower half
|
||||||
|
@ -131,24 +146,20 @@ GMainWindow::GMainWindow()
|
||||||
GMainWindow::~GMainWindow()
|
GMainWindow::~GMainWindow()
|
||||||
{
|
{
|
||||||
// will get automatically deleted otherwise
|
// will get automatically deleted otherwise
|
||||||
if (render_window->parent() == NULL)
|
if (render_window->parent() == nullptr)
|
||||||
delete render_window;
|
delete render_window;
|
||||||
|
|
||||||
|
Pica::g_debug_context.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GMainWindow::BootGame(std::string filename)
|
void GMainWindow::BootGame(std::string filename)
|
||||||
{
|
{
|
||||||
NOTICE_LOG(MASTER_LOG, "Citra starting...\n");
|
LOG_INFO(Frontend, "Citra starting...\n");
|
||||||
System::Init(render_window);
|
System::Init(render_window);
|
||||||
|
|
||||||
if (Core::Init()) {
|
|
||||||
ERROR_LOG(MASTER_LOG, "Core initialization failed, exiting...");
|
|
||||||
Core::Stop();
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load a game or die...
|
// Load a game or die...
|
||||||
if (Loader::ResultStatus::Success != Loader::LoadFile(filename)) {
|
if (Loader::ResultStatus::Success != Loader::LoadFile(filename)) {
|
||||||
ERROR_LOG(BOOT, "Failed to load ROM!");
|
LOG_CRITICAL(Frontend, "Failed to load ROM!");
|
||||||
}
|
}
|
||||||
|
|
||||||
disasmWidget->Init();
|
disasmWidget->Init();
|
||||||
|
@ -164,7 +175,7 @@ void GMainWindow::BootGame(std::string filename)
|
||||||
|
|
||||||
void GMainWindow::OnMenuLoadFile()
|
void GMainWindow::OnMenuLoadFile()
|
||||||
{
|
{
|
||||||
QString filename = QFileDialog::getOpenFileName(this, tr("Load file"), QString(), tr("3DS executable (*.elf *.axf *.bin *.cci *.cxi)"));
|
QString filename = QFileDialog::getOpenFileName(this, tr("Load file"), QString(), tr("3DS executable (*.3dsx *.elf *.axf *.bin *.cci *.cxi)"));
|
||||||
if (filename.size())
|
if (filename.size())
|
||||||
BootGame(filename.toLatin1().data());
|
BootGame(filename.toLatin1().data());
|
||||||
}
|
}
|
||||||
|
@ -213,18 +224,21 @@ void GMainWindow::OnOpenHotkeysDialog()
|
||||||
void GMainWindow::ToggleWindowMode()
|
void GMainWindow::ToggleWindowMode()
|
||||||
{
|
{
|
||||||
bool enable = ui.action_Popout_Window_Mode->isChecked();
|
bool enable = ui.action_Popout_Window_Mode->isChecked();
|
||||||
if (enable && render_window->parent() != NULL)
|
if (enable && render_window->parent() != nullptr)
|
||||||
{
|
{
|
||||||
ui.horizontalLayout->removeWidget(render_window);
|
ui.horizontalLayout->removeWidget(render_window);
|
||||||
render_window->setParent(NULL);
|
render_window->setParent(nullptr);
|
||||||
render_window->setVisible(true);
|
render_window->setVisible(true);
|
||||||
render_window->RestoreGeometry();
|
render_window->RestoreGeometry();
|
||||||
|
render_window->setFocusPolicy(Qt::NoFocus);
|
||||||
}
|
}
|
||||||
else if (!enable && render_window->parent() == NULL)
|
else if (!enable && render_window->parent() == nullptr)
|
||||||
{
|
{
|
||||||
render_window->BackupGeometry();
|
render_window->BackupGeometry();
|
||||||
ui.horizontalLayout->addWidget(render_window);
|
ui.horizontalLayout->addWidget(render_window);
|
||||||
render_window->setVisible(true);
|
render_window->setVisible(true);
|
||||||
|
render_window->setFocusPolicy(Qt::ClickFocus);
|
||||||
|
render_window->setFocus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -255,9 +269,21 @@ void GMainWindow::closeEvent(QCloseEvent* event)
|
||||||
|
|
||||||
int __cdecl main(int argc, char* argv[])
|
int __cdecl main(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
|
std::shared_ptr<Log::Logger> logger = Log::InitGlobalLogger();
|
||||||
|
Log::Filter log_filter(Log::Level::Info);
|
||||||
|
std::thread logging_thread(Log::TextLoggingLoop, logger, &log_filter);
|
||||||
|
SCOPE_EXIT({
|
||||||
|
logger->Close();
|
||||||
|
logging_thread.join();
|
||||||
|
});
|
||||||
|
|
||||||
QApplication::setAttribute(Qt::AA_X11InitThreads);
|
QApplication::setAttribute(Qt::AA_X11InitThreads);
|
||||||
QApplication app(argc, argv);
|
QApplication app(argc, argv);
|
||||||
|
|
||||||
GMainWindow main_window;
|
GMainWindow main_window;
|
||||||
|
// After settings have been loaded by GMainWindow, apply the filter
|
||||||
|
log_filter.ParseFilterString(Settings::values.log_filter);
|
||||||
|
|
||||||
main_window.show();
|
main_window.show();
|
||||||
return app.exec();
|
return app.exec();
|
||||||
}
|
}
|
||||||
|
|
303
src/citra_qt/util/spinbox.cpp
Normal file
303
src/citra_qt/util/spinbox.cpp
Normal file
|
@ -0,0 +1,303 @@
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
|
||||||
|
// Copyright 2014 Tony Wasserka
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer in the
|
||||||
|
// documentation and/or other materials provided with the distribution.
|
||||||
|
// * Neither the name of the owner nor the names of its contributors may
|
||||||
|
// be used to endorse or promote products derived from this software
|
||||||
|
// without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
#include <QLineEdit>
|
||||||
|
#include <QRegExpValidator>
|
||||||
|
|
||||||
|
#include "common/log.h"
|
||||||
|
|
||||||
|
#include "spinbox.hxx"
|
||||||
|
|
||||||
|
CSpinBox::CSpinBox(QWidget* parent) : QAbstractSpinBox(parent), base(10), min_value(-100), max_value(100), value(0), num_digits(0)
|
||||||
|
{
|
||||||
|
// TODO: Might be nice to not immediately call the slot.
|
||||||
|
// Think of an address that is being replaced by a different one, in which case a lot
|
||||||
|
// invalid intermediate addresses would be read from during editing.
|
||||||
|
connect(lineEdit(), SIGNAL(textEdited(QString)), this, SLOT(OnEditingFinished()));
|
||||||
|
|
||||||
|
UpdateText();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSpinBox::SetValue(qint64 val)
|
||||||
|
{
|
||||||
|
auto old_value = value;
|
||||||
|
value = std::max(std::min(val, max_value), min_value);
|
||||||
|
|
||||||
|
if (old_value != value) {
|
||||||
|
UpdateText();
|
||||||
|
emit ValueChanged(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSpinBox::SetRange(qint64 min, qint64 max)
|
||||||
|
{
|
||||||
|
min_value = min;
|
||||||
|
max_value = max;
|
||||||
|
|
||||||
|
SetValue(value);
|
||||||
|
UpdateText();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSpinBox::stepBy(int steps)
|
||||||
|
{
|
||||||
|
auto new_value = value;
|
||||||
|
// Scale number of steps by the currently selected digit
|
||||||
|
// TODO: Move this code elsewhere and enable it.
|
||||||
|
// TODO: Support for num_digits==0, too
|
||||||
|
// TODO: Support base!=16, too
|
||||||
|
// TODO: Make the cursor not jump back to the end of the line...
|
||||||
|
/*if (base == 16 && num_digits > 0) {
|
||||||
|
int digit = num_digits - (lineEdit()->cursorPosition() - prefix.length()) - 1;
|
||||||
|
digit = std::max(0, std::min(digit, num_digits - 1));
|
||||||
|
steps <<= digit * 4;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
// Increment "new_value" by "steps", and perform annoying overflow checks, too.
|
||||||
|
if (steps < 0 && new_value + steps > new_value) {
|
||||||
|
new_value = std::numeric_limits<qint64>::min();
|
||||||
|
} else if (steps > 0 && new_value + steps < new_value) {
|
||||||
|
new_value = std::numeric_limits<qint64>::max();
|
||||||
|
} else {
|
||||||
|
new_value += steps;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetValue(new_value);
|
||||||
|
UpdateText();
|
||||||
|
}
|
||||||
|
|
||||||
|
QAbstractSpinBox::StepEnabled CSpinBox::stepEnabled() const
|
||||||
|
{
|
||||||
|
StepEnabled ret = StepNone;
|
||||||
|
|
||||||
|
if (value > min_value)
|
||||||
|
ret |= StepDownEnabled;
|
||||||
|
|
||||||
|
if (value < max_value)
|
||||||
|
ret |= StepUpEnabled;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSpinBox::SetBase(int base)
|
||||||
|
{
|
||||||
|
this->base = base;
|
||||||
|
|
||||||
|
UpdateText();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSpinBox::SetNumDigits(int num_digits)
|
||||||
|
{
|
||||||
|
this->num_digits = num_digits;
|
||||||
|
|
||||||
|
UpdateText();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSpinBox::SetPrefix(const QString& prefix)
|
||||||
|
{
|
||||||
|
this->prefix = prefix;
|
||||||
|
|
||||||
|
UpdateText();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSpinBox::SetSuffix(const QString& suffix)
|
||||||
|
{
|
||||||
|
this->suffix = suffix;
|
||||||
|
|
||||||
|
UpdateText();
|
||||||
|
}
|
||||||
|
|
||||||
|
static QString StringToInputMask(const QString& input) {
|
||||||
|
QString mask = input;
|
||||||
|
|
||||||
|
// ... replace any special characters by their escaped counterparts ...
|
||||||
|
mask.replace("\\", "\\\\");
|
||||||
|
mask.replace("A", "\\A");
|
||||||
|
mask.replace("a", "\\a");
|
||||||
|
mask.replace("N", "\\N");
|
||||||
|
mask.replace("n", "\\n");
|
||||||
|
mask.replace("X", "\\X");
|
||||||
|
mask.replace("x", "\\x");
|
||||||
|
mask.replace("9", "\\9");
|
||||||
|
mask.replace("0", "\\0");
|
||||||
|
mask.replace("D", "\\D");
|
||||||
|
mask.replace("d", "\\d");
|
||||||
|
mask.replace("#", "\\#");
|
||||||
|
mask.replace("H", "\\H");
|
||||||
|
mask.replace("h", "\\h");
|
||||||
|
mask.replace("B", "\\B");
|
||||||
|
mask.replace("b", "\\b");
|
||||||
|
mask.replace(">", "\\>");
|
||||||
|
mask.replace("<", "\\<");
|
||||||
|
mask.replace("!", "\\!");
|
||||||
|
|
||||||
|
return mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSpinBox::UpdateText()
|
||||||
|
{
|
||||||
|
// If a fixed number of digits is used, we put the line edit in insertion mode by setting an
|
||||||
|
// input mask.
|
||||||
|
QString mask;
|
||||||
|
if (num_digits != 0) {
|
||||||
|
mask += StringToInputMask(prefix);
|
||||||
|
|
||||||
|
// For base 10 and negative range, demand a single sign character
|
||||||
|
if (HasSign())
|
||||||
|
mask += "X"; // identified as "-" or "+" in the validator
|
||||||
|
|
||||||
|
// Uppercase digits greater than 9.
|
||||||
|
mask += ">";
|
||||||
|
|
||||||
|
// The greatest signed 64-bit number has 19 decimal digits.
|
||||||
|
// TODO: Could probably make this more generic with some logarithms.
|
||||||
|
// For reference, unsigned 64-bit can have up to 20 decimal digits.
|
||||||
|
int digits = (num_digits != 0) ? num_digits
|
||||||
|
: (base == 16) ? 16
|
||||||
|
: (base == 10) ? 19
|
||||||
|
: 0xFF; // fallback case...
|
||||||
|
|
||||||
|
// Match num_digits digits
|
||||||
|
// Digits irrelevant to the chosen number base are filtered in the validator
|
||||||
|
mask += QString("H").repeated(std::max(num_digits, 1));
|
||||||
|
|
||||||
|
// Switch off case conversion
|
||||||
|
mask += "!";
|
||||||
|
|
||||||
|
mask += StringToInputMask(suffix);
|
||||||
|
}
|
||||||
|
lineEdit()->setInputMask(mask);
|
||||||
|
|
||||||
|
// Set new text without changing the cursor position. This will cause the cursor to briefly
|
||||||
|
// appear at the end of the line and then to jump back to its original position. That's
|
||||||
|
// a bit ugly, but better than having setText() move the cursor permanently all the time.
|
||||||
|
int cursor_position = lineEdit()->cursorPosition();
|
||||||
|
lineEdit()->setText(TextFromValue());
|
||||||
|
lineEdit()->setCursorPosition(cursor_position);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString CSpinBox::TextFromValue()
|
||||||
|
{
|
||||||
|
return prefix
|
||||||
|
+ QString(HasSign() ? ((value < 0) ? "-" : "+") : "")
|
||||||
|
+ QString("%1").arg(abs(value), num_digits, base, QLatin1Char('0')).toUpper()
|
||||||
|
+ suffix;
|
||||||
|
}
|
||||||
|
|
||||||
|
qint64 CSpinBox::ValueFromText()
|
||||||
|
{
|
||||||
|
unsigned strpos = prefix.length();
|
||||||
|
|
||||||
|
QString num_string = text().mid(strpos, text().length() - strpos - suffix.length());
|
||||||
|
return num_string.toLongLong(nullptr, base);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CSpinBox::HasSign() const
|
||||||
|
{
|
||||||
|
return base == 10 && min_value < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSpinBox::OnEditingFinished()
|
||||||
|
{
|
||||||
|
// Only update for valid input
|
||||||
|
QString input = lineEdit()->text();
|
||||||
|
int pos = 0;
|
||||||
|
if (QValidator::Acceptable == validate(input, pos))
|
||||||
|
SetValue(ValueFromText());
|
||||||
|
}
|
||||||
|
|
||||||
|
QValidator::State CSpinBox::validate(QString& input, int& pos) const
|
||||||
|
{
|
||||||
|
if (!prefix.isEmpty() && input.left(prefix.length()) != prefix)
|
||||||
|
return QValidator::Invalid;
|
||||||
|
|
||||||
|
int strpos = prefix.length();
|
||||||
|
|
||||||
|
// Empty "numbers" allowed as intermediate values
|
||||||
|
if (strpos >= input.length() - HasSign() - suffix.length())
|
||||||
|
return QValidator::Intermediate;
|
||||||
|
|
||||||
|
_dbg_assert_(Frontend, base <= 10 || base == 16);
|
||||||
|
QString regexp;
|
||||||
|
|
||||||
|
// Demand sign character for negative ranges
|
||||||
|
if (HasSign())
|
||||||
|
regexp += "[+\\-]";
|
||||||
|
|
||||||
|
// Match digits corresponding to the chosen number base.
|
||||||
|
regexp += QString("[0-%1").arg(std::min(base, 9));
|
||||||
|
if (base == 16) {
|
||||||
|
regexp += "a-fA-F";
|
||||||
|
}
|
||||||
|
regexp += "]";
|
||||||
|
|
||||||
|
// Specify number of digits
|
||||||
|
if (num_digits > 0) {
|
||||||
|
regexp += QString("{%1}").arg(num_digits);
|
||||||
|
} else {
|
||||||
|
regexp += "+";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match string
|
||||||
|
QRegExp num_regexp(regexp);
|
||||||
|
int num_pos = strpos;
|
||||||
|
QString sub_input = input.mid(strpos, input.length() - strpos - suffix.length());
|
||||||
|
|
||||||
|
if (!num_regexp.exactMatch(sub_input) && num_regexp.matchedLength() == 0)
|
||||||
|
return QValidator::Invalid;
|
||||||
|
|
||||||
|
sub_input = sub_input.left(num_regexp.matchedLength());
|
||||||
|
bool ok;
|
||||||
|
qint64 val = sub_input.toLongLong(&ok, base);
|
||||||
|
|
||||||
|
if (!ok)
|
||||||
|
return QValidator::Invalid;
|
||||||
|
|
||||||
|
// Outside boundaries => don't accept
|
||||||
|
if (val < min_value || val > max_value)
|
||||||
|
return QValidator::Invalid;
|
||||||
|
|
||||||
|
// Make sure we are actually at the end of this string...
|
||||||
|
strpos += num_regexp.matchedLength();
|
||||||
|
|
||||||
|
if (!suffix.isEmpty() && input.mid(strpos) != suffix) {
|
||||||
|
return QValidator::Invalid;
|
||||||
|
} else {
|
||||||
|
strpos += suffix.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strpos != input.length())
|
||||||
|
return QValidator::Invalid;
|
||||||
|
|
||||||
|
// At this point we can say for sure that the input is fine. Let's fix it up a bit though
|
||||||
|
input.replace(num_pos, sub_input.length(), sub_input.toUpper());
|
||||||
|
|
||||||
|
return QValidator::Acceptable;
|
||||||
|
}
|
88
src/citra_qt/util/spinbox.hxx
Normal file
88
src/citra_qt/util/spinbox.hxx
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
|
||||||
|
// Copyright 2014 Tony Wasserka
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer in the
|
||||||
|
// documentation and/or other materials provided with the distribution.
|
||||||
|
// * Neither the name of the owner nor the names of its contributors may
|
||||||
|
// be used to endorse or promote products derived from this software
|
||||||
|
// without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QAbstractSpinBox>
|
||||||
|
#include <QtGlobal>
|
||||||
|
|
||||||
|
class QVariant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A custom spin box widget with enhanced functionality over Qt's QSpinBox
|
||||||
|
*/
|
||||||
|
class CSpinBox : public QAbstractSpinBox {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
CSpinBox(QWidget* parent = nullptr);
|
||||||
|
|
||||||
|
void stepBy(int steps) override;
|
||||||
|
StepEnabled stepEnabled() const override;
|
||||||
|
|
||||||
|
void SetValue(qint64 val);
|
||||||
|
|
||||||
|
void SetRange(qint64 min, qint64 max);
|
||||||
|
|
||||||
|
void SetBase(int base);
|
||||||
|
|
||||||
|
void SetPrefix(const QString& prefix);
|
||||||
|
void SetSuffix(const QString& suffix);
|
||||||
|
|
||||||
|
void SetNumDigits(int num_digits);
|
||||||
|
|
||||||
|
QValidator::State validate(QString& input, int& pos) const override;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void ValueChanged(qint64 val);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void OnEditingFinished();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void UpdateText();
|
||||||
|
|
||||||
|
bool HasSign() const;
|
||||||
|
|
||||||
|
QString TextFromValue();
|
||||||
|
qint64 ValueFromText();
|
||||||
|
|
||||||
|
qint64 min_value, max_value;
|
||||||
|
|
||||||
|
qint64 value;
|
||||||
|
|
||||||
|
QString prefix, suffix;
|
||||||
|
|
||||||
|
int base;
|
||||||
|
|
||||||
|
int num_digits;
|
||||||
|
};
|
|
@ -3,14 +3,15 @@ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp.in" "${CMAKE_CURRENT_SOU
|
||||||
|
|
||||||
set(SRCS
|
set(SRCS
|
||||||
break_points.cpp
|
break_points.cpp
|
||||||
console_listener.cpp
|
|
||||||
emu_window.cpp
|
emu_window.cpp
|
||||||
extended_trace.cpp
|
extended_trace.cpp
|
||||||
file_search.cpp
|
file_search.cpp
|
||||||
file_util.cpp
|
file_util.cpp
|
||||||
hash.cpp
|
hash.cpp
|
||||||
key_map.cpp
|
key_map.cpp
|
||||||
log_manager.cpp
|
logging/filter.cpp
|
||||||
|
logging/text_formatter.cpp
|
||||||
|
logging/backend.cpp
|
||||||
math_util.cpp
|
math_util.cpp
|
||||||
mem_arena.cpp
|
mem_arena.cpp
|
||||||
memory_util.cpp
|
memory_util.cpp
|
||||||
|
@ -32,7 +33,7 @@ set(HEADERS
|
||||||
common_funcs.h
|
common_funcs.h
|
||||||
common_paths.h
|
common_paths.h
|
||||||
common_types.h
|
common_types.h
|
||||||
console_listener.h
|
concurrent_ring_buffer.h
|
||||||
cpu_detect.h
|
cpu_detect.h
|
||||||
debug_interface.h
|
debug_interface.h
|
||||||
emu_window.h
|
emu_window.h
|
||||||
|
@ -44,13 +45,18 @@ set(HEADERS
|
||||||
key_map.h
|
key_map.h
|
||||||
linear_disk_cache.h
|
linear_disk_cache.h
|
||||||
log.h
|
log.h
|
||||||
log_manager.h
|
logging/text_formatter.h
|
||||||
|
logging/filter.h
|
||||||
|
logging/log.h
|
||||||
|
logging/backend.h
|
||||||
|
make_unique.h
|
||||||
math_util.h
|
math_util.h
|
||||||
mem_arena.h
|
mem_arena.h
|
||||||
memory_util.h
|
memory_util.h
|
||||||
msg_handler.h
|
msg_handler.h
|
||||||
platform.h
|
platform.h
|
||||||
scm_rev.h
|
scm_rev.h
|
||||||
|
scope_exit.h
|
||||||
string_util.h
|
string_util.h
|
||||||
swap.h
|
swap.h
|
||||||
symbols.h
|
symbols.h
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Licensed under GPLv2
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
|
||||||
|
@ -142,7 +142,7 @@ public:
|
||||||
|
|
||||||
__forceinline BitField& operator=(T val)
|
__forceinline BitField& operator=(T val)
|
||||||
{
|
{
|
||||||
storage = (storage & ~GetMask()) | (((StorageType)val << position) & GetMask());
|
Assign(val);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,6 +151,10 @@ public:
|
||||||
return Value();
|
return Value();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__forceinline void Assign(const T& value) {
|
||||||
|
storage = (storage & ~GetMask()) | (((StorageType)value << position) & GetMask());
|
||||||
|
}
|
||||||
|
|
||||||
__forceinline T Value() const
|
__forceinline T Value() const
|
||||||
{
|
{
|
||||||
if (std::numeric_limits<T>::is_signed)
|
if (std::numeric_limits<T>::is_signed)
|
||||||
|
@ -164,6 +168,12 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: we may want to change this to explicit operator bool() if it's bug-free in VS2015
|
||||||
|
__forceinline bool ToBool() const
|
||||||
|
{
|
||||||
|
return Value() != 0;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// StorageType is T for non-enum types and the underlying type of T if
|
// StorageType is T for non-enum types and the underlying type of T if
|
||||||
// T is an enumeration. Note that T is wrapped within an enable_if in the
|
// T is an enumeration. Note that T is wrapped within an enable_if in the
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Copyright 2013 Dolphin Emulator Project
|
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
||||||
// Licensed under GPLv2
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#include "common/common.h"
|
#include "common/common.h"
|
||||||
|
@ -180,7 +180,7 @@ void TMemCheck::Action(DebugInterface *debug_interface, u32 iValue, u32 addr,
|
||||||
{
|
{
|
||||||
if (Log)
|
if (Log)
|
||||||
{
|
{
|
||||||
INFO_LOG(MEMMAP, "CHK %08x (%s) %s%i %0*x at %08x (%s)",
|
LOG_DEBUG(Debug_Breakpoint, "CHK %08x (%s) %s%i %0*x at %08x (%s)",
|
||||||
pc, debug_interface->getDescription(pc).c_str(),
|
pc, debug_interface->getDescription(pc).c_str(),
|
||||||
write ? "Write" : "Read", size*8, size*2, iValue, addr,
|
write ? "Write" : "Read", size*8, size*2, iValue, addr,
|
||||||
debug_interface->getDescription(addr).c_str()
|
debug_interface->getDescription(addr).c_str()
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Copyright 2013 Dolphin Emulator Project
|
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
||||||
// Licensed under GPLv2
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
|
@ -154,7 +154,7 @@ public:
|
||||||
Do(foundVersion);
|
Do(foundVersion);
|
||||||
|
|
||||||
if (error == ERROR_FAILURE || foundVersion < minVer || foundVersion > ver) {
|
if (error == ERROR_FAILURE || foundVersion < minVer || foundVersion > ver) {
|
||||||
WARN_LOG(COMMON, "Savestate failure: wrong version %d found for %s", foundVersion, title);
|
LOG_ERROR(Common, "Savestate failure: wrong version %d found for %s", foundVersion, title);
|
||||||
SetError(ERROR_FAILURE);
|
SetError(ERROR_FAILURE);
|
||||||
return PointerWrapSection(*this, -1, title);
|
return PointerWrapSection(*this, -1, title);
|
||||||
}
|
}
|
||||||
|
@ -178,7 +178,14 @@ public:
|
||||||
case MODE_READ: if (memcmp(data, *ptr, size) != 0) return false; break;
|
case MODE_READ: if (memcmp(data, *ptr, size) != 0) return false; break;
|
||||||
case MODE_WRITE: memcpy(*ptr, data, size); break;
|
case MODE_WRITE: memcpy(*ptr, data, size); break;
|
||||||
case MODE_MEASURE: break; // MODE_MEASURE - don't need to do anything
|
case MODE_MEASURE: break; // MODE_MEASURE - don't need to do anything
|
||||||
case MODE_VERIFY: for(int i = 0; i < size; i++) _dbg_assert_msg_(COMMON, ((u8*)data)[i] == (*ptr)[i], "Savestate verification failure: %d (0x%X) (at %p) != %d (0x%X) (at %p).\n", ((u8*)data)[i], ((u8*)data)[i], &((u8*)data)[i], (*ptr)[i], (*ptr)[i], &(*ptr)[i]); break;
|
case MODE_VERIFY:
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
_dbg_assert_msg_(Common, ((u8*)data)[i] == (*ptr)[i],
|
||||||
|
"Savestate verification failure: %d (0x%X) (at %p) != %d (0x%X) (at %p).\n",
|
||||||
|
((u8*)data)[i], ((u8*)data)[i], &((u8*)data)[i],
|
||||||
|
(*ptr)[i], (*ptr)[i], &(*ptr)[i]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
default: break; // throw an error?
|
default: break; // throw an error?
|
||||||
}
|
}
|
||||||
(*ptr) += size;
|
(*ptr) += size;
|
||||||
|
@ -191,7 +198,14 @@ public:
|
||||||
case MODE_READ: memcpy(data, *ptr, size); break;
|
case MODE_READ: memcpy(data, *ptr, size); break;
|
||||||
case MODE_WRITE: memcpy(*ptr, data, size); break;
|
case MODE_WRITE: memcpy(*ptr, data, size); break;
|
||||||
case MODE_MEASURE: break; // MODE_MEASURE - don't need to do anything
|
case MODE_MEASURE: break; // MODE_MEASURE - don't need to do anything
|
||||||
case MODE_VERIFY: for(int i = 0; i < size; i++) _dbg_assert_msg_(COMMON, ((u8*)data)[i] == (*ptr)[i], "Savestate verification failure: %d (0x%X) (at %p) != %d (0x%X) (at %p).\n", ((u8*)data)[i], ((u8*)data)[i], &((u8*)data)[i], (*ptr)[i], (*ptr)[i], &(*ptr)[i]); break;
|
case MODE_VERIFY:
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
_dbg_assert_msg_(Common, ((u8*)data)[i] == (*ptr)[i],
|
||||||
|
"Savestate verification failure: %d (0x%X) (at %p) != %d (0x%X) (at %p).\n",
|
||||||
|
((u8*)data)[i], ((u8*)data)[i], &((u8*)data)[i],
|
||||||
|
(*ptr)[i], (*ptr)[i], &(*ptr)[i]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
default: break; // throw an error?
|
default: break; // throw an error?
|
||||||
}
|
}
|
||||||
(*ptr) += size;
|
(*ptr) += size;
|
||||||
|
@ -204,11 +218,11 @@ public:
|
||||||
{
|
{
|
||||||
for (auto it = x.begin(), end = x.end(); it != end; ++it)
|
for (auto it = x.begin(), end = x.end(); it != end; ++it)
|
||||||
{
|
{
|
||||||
if (it->second != NULL)
|
if (it->second != nullptr)
|
||||||
delete it->second;
|
delete it->second;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
T *dv = NULL;
|
T *dv = nullptr;
|
||||||
DoMap(x, dv);
|
DoMap(x, dv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -264,11 +278,11 @@ public:
|
||||||
{
|
{
|
||||||
for (auto it = x.begin(), end = x.end(); it != end; ++it)
|
for (auto it = x.begin(), end = x.end(); it != end; ++it)
|
||||||
{
|
{
|
||||||
if (it->second != NULL)
|
if (it->second != nullptr)
|
||||||
delete it->second;
|
delete it->second;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
T *dv = NULL;
|
T *dv = nullptr;
|
||||||
DoMultimap(x, dv);
|
DoMultimap(x, dv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -320,7 +334,7 @@ public:
|
||||||
template<class T>
|
template<class T>
|
||||||
void Do(std::vector<T *> &x)
|
void Do(std::vector<T *> &x)
|
||||||
{
|
{
|
||||||
T *dv = NULL;
|
T *dv = nullptr;
|
||||||
DoVector(x, dv);
|
DoVector(x, dv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -369,7 +383,7 @@ public:
|
||||||
template<class T>
|
template<class T>
|
||||||
void Do(std::deque<T *> &x)
|
void Do(std::deque<T *> &x)
|
||||||
{
|
{
|
||||||
T *dv = NULL;
|
T *dv = nullptr;
|
||||||
DoDeque(x, dv);
|
DoDeque(x, dv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -395,7 +409,7 @@ public:
|
||||||
template<class T>
|
template<class T>
|
||||||
void Do(std::list<T *> &x)
|
void Do(std::list<T *> &x)
|
||||||
{
|
{
|
||||||
T *dv = NULL;
|
T *dv = nullptr;
|
||||||
Do(x, dv);
|
Do(x, dv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -433,7 +447,7 @@ public:
|
||||||
{
|
{
|
||||||
for (auto it = x.begin(), end = x.end(); it != end; ++it)
|
for (auto it = x.begin(), end = x.end(); it != end; ++it)
|
||||||
{
|
{
|
||||||
if (*it != NULL)
|
if (*it != nullptr)
|
||||||
delete *it;
|
delete *it;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -476,7 +490,7 @@ public:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
ERROR_LOG(COMMON, "Savestate error: invalid mode %d.", mode);
|
LOG_ERROR(Common, "Savestate error: invalid mode %d.", mode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -490,7 +504,12 @@ public:
|
||||||
case MODE_READ: x = (char*)*ptr; break;
|
case MODE_READ: x = (char*)*ptr; break;
|
||||||
case MODE_WRITE: memcpy(*ptr, x.c_str(), stringLen); break;
|
case MODE_WRITE: memcpy(*ptr, x.c_str(), stringLen); break;
|
||||||
case MODE_MEASURE: break;
|
case MODE_MEASURE: break;
|
||||||
case MODE_VERIFY: _dbg_assert_msg_(COMMON, !strcmp(x.c_str(), (char*)*ptr), "Savestate verification failure: \"%s\" != \"%s\" (at %p).\n", x.c_str(), (char*)*ptr, ptr); break;
|
case MODE_VERIFY:
|
||||||
|
_dbg_assert_msg_(Common,
|
||||||
|
!strcmp(x.c_str(), (char*)*ptr),
|
||||||
|
"Savestate verification failure: \"%s\" != \"%s\" (at %p).\n",
|
||||||
|
x.c_str(), (char*)*ptr, ptr);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
(*ptr) += stringLen;
|
(*ptr) += stringLen;
|
||||||
}
|
}
|
||||||
|
@ -504,7 +523,11 @@ public:
|
||||||
case MODE_READ: x = (wchar_t*)*ptr; break;
|
case MODE_READ: x = (wchar_t*)*ptr; break;
|
||||||
case MODE_WRITE: memcpy(*ptr, x.c_str(), stringLen); break;
|
case MODE_WRITE: memcpy(*ptr, x.c_str(), stringLen); break;
|
||||||
case MODE_MEASURE: break;
|
case MODE_MEASURE: break;
|
||||||
case MODE_VERIFY: _dbg_assert_msg_(COMMON, x == (wchar_t*)*ptr, "Savestate verification failure: \"%ls\" != \"%ls\" (at %p).\n", x.c_str(), (wchar_t*)*ptr, ptr); break;
|
case MODE_VERIFY:
|
||||||
|
_dbg_assert_msg_(Common, x == (wchar_t*)*ptr,
|
||||||
|
"Savestate verification failure: \"%ls\" != \"%ls\" (at %p).\n",
|
||||||
|
x.c_str(), (wchar_t*)*ptr, ptr);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
(*ptr) += stringLen;
|
(*ptr) += stringLen;
|
||||||
}
|
}
|
||||||
|
@ -518,7 +541,7 @@ public:
|
||||||
void DoClass(T *&x) {
|
void DoClass(T *&x) {
|
||||||
if (mode == MODE_READ)
|
if (mode == MODE_READ)
|
||||||
{
|
{
|
||||||
if (x != NULL)
|
if (x != nullptr)
|
||||||
delete x;
|
delete x;
|
||||||
x = new T();
|
x = new T();
|
||||||
}
|
}
|
||||||
|
@ -567,7 +590,7 @@ public:
|
||||||
{
|
{
|
||||||
if (mode == MODE_READ)
|
if (mode == MODE_READ)
|
||||||
{
|
{
|
||||||
cur->next = 0;
|
cur->next = nullptr;
|
||||||
list_cur = cur;
|
list_cur = cur;
|
||||||
if (prev)
|
if (prev)
|
||||||
prev->next = cur;
|
prev->next = cur;
|
||||||
|
@ -586,13 +609,13 @@ public:
|
||||||
if (mode == MODE_READ)
|
if (mode == MODE_READ)
|
||||||
{
|
{
|
||||||
if (prev)
|
if (prev)
|
||||||
prev->next = 0;
|
prev->next = nullptr;
|
||||||
if (list_end)
|
if (list_end)
|
||||||
*list_end = prev;
|
*list_end = prev;
|
||||||
if (list_cur)
|
if (list_cur)
|
||||||
{
|
{
|
||||||
if (list_start == list_cur)
|
if (list_start == list_cur)
|
||||||
list_start = 0;
|
list_start = nullptr;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
LinkedListItem<T>* next = list_cur->next;
|
LinkedListItem<T>* next = list_cur->next;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Copyright 2013 Dolphin Emulator Project
|
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
||||||
// Licensed under GPLv2
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
// Copyright 2013 Dolphin Emulator Project
|
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
||||||
// Licensed under GPLv2
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "common_types.h"
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#define SLEEP(x) Sleep(x)
|
#define SLEEP(x) Sleep(x)
|
||||||
#else
|
#else
|
||||||
|
@ -73,6 +76,8 @@ inline u64 _rotr64(u64 x, unsigned int shift){
|
||||||
}
|
}
|
||||||
|
|
||||||
#else // _MSC_VER
|
#else // _MSC_VER
|
||||||
|
#include <locale.h>
|
||||||
|
|
||||||
// Function Cross-Compatibility
|
// Function Cross-Compatibility
|
||||||
#define strcasecmp _stricmp
|
#define strcasecmp _stricmp
|
||||||
#define strncasecmp _strnicmp
|
#define strncasecmp _strnicmp
|
||||||
|
@ -106,7 +111,7 @@ inline u64 _rotr64(u64 x, unsigned int shift){
|
||||||
// Restore the global locale
|
// Restore the global locale
|
||||||
_configthreadlocale(_DISABLE_PER_THREAD_LOCALE);
|
_configthreadlocale(_DISABLE_PER_THREAD_LOCALE);
|
||||||
}
|
}
|
||||||
else if(new_locale != NULL)
|
else if(new_locale != nullptr)
|
||||||
{
|
{
|
||||||
// Configure the thread to set the locale only for this thread
|
// Configure the thread to set the locale only for this thread
|
||||||
_configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
|
_configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Copyright 2013 Dolphin Emulator Project
|
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
||||||
// Licensed under GPLv2
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
@ -29,19 +29,6 @@
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Shared data dirs (Sys and shared User for linux)
|
|
||||||
#ifdef _WIN32
|
|
||||||
#define SYSDATA_DIR "sys"
|
|
||||||
#else
|
|
||||||
#ifdef DATA_DIR
|
|
||||||
#define SYSDATA_DIR DATA_DIR "sys"
|
|
||||||
#define SHARED_USER_DIR DATA_DIR USERDATA_DIR DIR_SEP
|
|
||||||
#else
|
|
||||||
#define SYSDATA_DIR "sys"
|
|
||||||
#define SHARED_USER_DIR ROOT_DIR DIR_SEP USERDATA_DIR DIR_SEP
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Dirs in both User and Sys
|
// Dirs in both User and Sys
|
||||||
#define EUR_DIR "EUR"
|
#define EUR_DIR "EUR"
|
||||||
#define USA_DIR "USA"
|
#define USA_DIR "USA"
|
||||||
|
@ -53,6 +40,9 @@
|
||||||
#define MAPS_DIR "maps"
|
#define MAPS_DIR "maps"
|
||||||
#define CACHE_DIR "cache"
|
#define CACHE_DIR "cache"
|
||||||
#define SDMC_DIR "sdmc"
|
#define SDMC_DIR "sdmc"
|
||||||
|
#define SAVEDATA_DIR "savedata"
|
||||||
|
#define SYSDATA_DIR "sysdata"
|
||||||
|
#define SYSSAVEDATA_DIR "syssavedata"
|
||||||
#define SHADERCACHE_DIR "shader_cache"
|
#define SHADERCACHE_DIR "shader_cache"
|
||||||
#define STATESAVES_DIR "state_saves"
|
#define STATESAVES_DIR "state_saves"
|
||||||
#define SCREENSHOTS_DIR "screenShots"
|
#define SCREENSHOTS_DIR "screenShots"
|
||||||
|
@ -70,6 +60,9 @@
|
||||||
#define DEBUGGER_CONFIG "debugger.ini"
|
#define DEBUGGER_CONFIG "debugger.ini"
|
||||||
#define LOGGER_CONFIG "logger.ini"
|
#define LOGGER_CONFIG "logger.ini"
|
||||||
|
|
||||||
|
// Sys files
|
||||||
|
#define SHARED_FONT "shared_font.bin"
|
||||||
|
|
||||||
// Files in the directory returned by GetUserPath(D_LOGS_IDX)
|
// Files in the directory returned by GetUserPath(D_LOGS_IDX)
|
||||||
#define MAIN_LOG "emu.log"
|
#define MAIN_LOG "emu.log"
|
||||||
|
|
||||||
|
|
|
@ -41,8 +41,6 @@ typedef std::int64_t s64; ///< 64-bit signed int
|
||||||
typedef float f32; ///< 32-bit floating point
|
typedef float f32; ///< 32-bit floating point
|
||||||
typedef double f64; ///< 64-bit floating point
|
typedef double f64; ///< 64-bit floating point
|
||||||
|
|
||||||
#include "common/common.h"
|
|
||||||
|
|
||||||
/// Union for fast 16-bit type casting
|
/// Union for fast 16-bit type casting
|
||||||
union t16 {
|
union t16 {
|
||||||
u8 _u8[2]; ///< 8-bit unsigned char(s)
|
u8 _u8[2]; ///< 8-bit unsigned char(s)
|
||||||
|
|
164
src/common/concurrent_ring_buffer.h
Normal file
164
src/common/concurrent_ring_buffer.h
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
// Copyright 2014 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <mutex>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#include "common/common.h" // for NonCopyable
|
||||||
|
#include "common/log.h" // for _dbg_assert_
|
||||||
|
|
||||||
|
namespace Common {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A MPMC (Multiple-Producer Multiple-Consumer) concurrent ring buffer. This data structure permits
|
||||||
|
* multiple threads to push and pop from a queue of bounded size.
|
||||||
|
*/
|
||||||
|
template <typename T, size_t ArraySize>
|
||||||
|
class ConcurrentRingBuffer : private NonCopyable {
|
||||||
|
public:
|
||||||
|
/// Value returned by the popping functions when the queue has been closed.
|
||||||
|
static const size_t QUEUE_CLOSED = -1;
|
||||||
|
|
||||||
|
ConcurrentRingBuffer() {}
|
||||||
|
|
||||||
|
~ConcurrentRingBuffer() {
|
||||||
|
// If for whatever reason the queue wasn't completely drained, destroy the left over items.
|
||||||
|
for (size_t i = reader_index, end = writer_index; i != end; i = (i + 1) % ArraySize) {
|
||||||
|
Data()[i].~T();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pushes a value to the queue. If the queue is full, this method will block. Does nothing if
|
||||||
|
* the queue is closed.
|
||||||
|
*/
|
||||||
|
void Push(T val) {
|
||||||
|
std::unique_lock<std::mutex> lock(mutex);
|
||||||
|
if (closed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the buffer is full, wait
|
||||||
|
writer.wait(lock, [&]{
|
||||||
|
return (writer_index + 1) % ArraySize != reader_index;
|
||||||
|
});
|
||||||
|
|
||||||
|
T* item = &Data()[writer_index];
|
||||||
|
new (item) T(std::move(val));
|
||||||
|
|
||||||
|
writer_index = (writer_index + 1) % ArraySize;
|
||||||
|
|
||||||
|
// Wake up waiting readers
|
||||||
|
lock.unlock();
|
||||||
|
reader.notify_one();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pops up to `dest_len` items from the queue, storing them in `dest`. This function will not
|
||||||
|
* block, and might return 0 values if there are no elements in the queue when it is called.
|
||||||
|
*
|
||||||
|
* @return The number of elements stored in `dest`. If the queue has been closed, returns
|
||||||
|
* `QUEUE_CLOSED`.
|
||||||
|
*/
|
||||||
|
size_t Pop(T* dest, size_t dest_len) {
|
||||||
|
std::unique_lock<std::mutex> lock(mutex);
|
||||||
|
if (closed && !CanRead()) {
|
||||||
|
return QUEUE_CLOSED;
|
||||||
|
}
|
||||||
|
return PopInternal(dest, dest_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pops up to `dest_len` items from the queue, storing them in `dest`. This function will block
|
||||||
|
* if there are no elements in the queue when it is called.
|
||||||
|
*
|
||||||
|
* @return The number of elements stored in `dest`. If the queue has been closed, returns
|
||||||
|
* `QUEUE_CLOSED`.
|
||||||
|
*/
|
||||||
|
size_t BlockingPop(T* dest, size_t dest_len) {
|
||||||
|
std::unique_lock<std::mutex> lock(mutex);
|
||||||
|
if (closed && !CanRead()) {
|
||||||
|
return QUEUE_CLOSED;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!CanRead()) {
|
||||||
|
reader.wait(lock);
|
||||||
|
if (closed && !CanRead()) {
|
||||||
|
return QUEUE_CLOSED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_dbg_assert_(Common, CanRead());
|
||||||
|
return PopInternal(dest, dest_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the queue. After calling this method, `Push` operations won't have any effect, and
|
||||||
|
* `PopMany` and `PopManyBlock` will start returning `QUEUE_CLOSED`. This is intended to allow
|
||||||
|
* a graceful shutdown of all consumers.
|
||||||
|
*/
|
||||||
|
void Close() {
|
||||||
|
std::unique_lock<std::mutex> lock(mutex);
|
||||||
|
closed = true;
|
||||||
|
// We need to wake up any reader that are waiting for an item that will never come.
|
||||||
|
lock.unlock();
|
||||||
|
reader.notify_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if `Close()` has been called.
|
||||||
|
bool IsClosed() const {
|
||||||
|
return closed;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
size_t PopInternal(T* dest, size_t dest_len) {
|
||||||
|
size_t output_count = 0;
|
||||||
|
while (output_count < dest_len && CanRead()) {
|
||||||
|
_dbg_assert_(Common, CanRead());
|
||||||
|
|
||||||
|
T* item = &Data()[reader_index];
|
||||||
|
T out_val = std::move(*item);
|
||||||
|
item->~T();
|
||||||
|
|
||||||
|
size_t prev_index = (reader_index + ArraySize - 1) % ArraySize;
|
||||||
|
reader_index = (reader_index + 1) % ArraySize;
|
||||||
|
if (writer_index == prev_index) {
|
||||||
|
writer.notify_one();
|
||||||
|
}
|
||||||
|
dest[output_count++] = std::move(out_val);
|
||||||
|
}
|
||||||
|
return output_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CanRead() const {
|
||||||
|
return reader_index != writer_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
T* Data() {
|
||||||
|
return static_cast<T*>(static_cast<void*>(&storage));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Storage for entries
|
||||||
|
typename std::aligned_storage<ArraySize * sizeof(T),
|
||||||
|
std::alignment_of<T>::value>::type storage;
|
||||||
|
|
||||||
|
/// Data is valid in the half-open interval [reader, writer). If they are `QUEUE_CLOSED` then the
|
||||||
|
/// queue has been closed.
|
||||||
|
size_t writer_index = 0, reader_index = 0;
|
||||||
|
// True if the queue has been closed.
|
||||||
|
bool closed = false;
|
||||||
|
|
||||||
|
/// Mutex that protects the entire data structure.
|
||||||
|
std::mutex mutex;
|
||||||
|
/// Signaling wakes up reader which is waiting for storage to be non-empty.
|
||||||
|
std::condition_variable reader;
|
||||||
|
/// Signaling wakes up writer which is waiting for storage to be non-full.
|
||||||
|
std::condition_variable writer;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
|
@ -1,319 +0,0 @@
|
||||||
// Copyright 2013 Dolphin Emulator Project
|
|
||||||
// Licensed under GPLv2
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#include <windows.h>
|
|
||||||
#include <array>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "common/common.h"
|
|
||||||
#include "common/log_manager.h" // Common
|
|
||||||
#include "common/console_listener.h" // Common
|
|
||||||
|
|
||||||
ConsoleListener::ConsoleListener()
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
hConsole = NULL;
|
|
||||||
bUseColor = true;
|
|
||||||
#else
|
|
||||||
bUseColor = isatty(fileno(stdout));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
ConsoleListener::~ConsoleListener()
|
|
||||||
{
|
|
||||||
Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 100, 100, "Dolphin Log Console"
|
|
||||||
// Open console window - width and height is the size of console window
|
|
||||||
// Name is the window title
|
|
||||||
void ConsoleListener::Open(bool Hidden, int Width, int Height, const char *Title)
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
if (!GetConsoleWindow())
|
|
||||||
{
|
|
||||||
// Open the console window and create the window handle for GetStdHandle()
|
|
||||||
AllocConsole();
|
|
||||||
// Hide
|
|
||||||
if (Hidden) ShowWindow(GetConsoleWindow(), SW_HIDE);
|
|
||||||
// Save the window handle that AllocConsole() created
|
|
||||||
hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
||||||
// Set the console window title
|
|
||||||
SetConsoleTitle(Common::UTF8ToTStr(Title).c_str());
|
|
||||||
// Set letter space
|
|
||||||
LetterSpace(80, 4000);
|
|
||||||
//MoveWindow(GetConsoleWindow(), 200,200, 800,800, true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void ConsoleListener::UpdateHandle()
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close the console window and close the eventual file handle
|
|
||||||
void ConsoleListener::Close()
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
if (hConsole == NULL)
|
|
||||||
return;
|
|
||||||
FreeConsole();
|
|
||||||
hConsole = NULL;
|
|
||||||
#else
|
|
||||||
fflush(NULL);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ConsoleListener::IsOpen()
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
return (hConsole != NULL);
|
|
||||||
#else
|
|
||||||
return true;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
LetterSpace: SetConsoleScreenBufferSize and SetConsoleWindowInfo are
|
|
||||||
dependent on each other, that's the reason for the additional checks.
|
|
||||||
*/
|
|
||||||
void ConsoleListener::BufferWidthHeight(int BufferWidth, int BufferHeight, int ScreenWidth, int ScreenHeight, bool BufferFirst)
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
BOOL SB, SW;
|
|
||||||
if (BufferFirst)
|
|
||||||
{
|
|
||||||
// Change screen buffer size
|
|
||||||
COORD Co = {BufferWidth, BufferHeight};
|
|
||||||
SB = SetConsoleScreenBufferSize(hConsole, Co);
|
|
||||||
// Change the screen buffer window size
|
|
||||||
SMALL_RECT coo = {0,0,ScreenWidth, ScreenHeight}; // top, left, right, bottom
|
|
||||||
SW = SetConsoleWindowInfo(hConsole, TRUE, &coo);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Change the screen buffer window size
|
|
||||||
SMALL_RECT coo = {0,0, ScreenWidth, ScreenHeight}; // top, left, right, bottom
|
|
||||||
SW = SetConsoleWindowInfo(hConsole, TRUE, &coo);
|
|
||||||
// Change screen buffer size
|
|
||||||
COORD Co = {BufferWidth, BufferHeight};
|
|
||||||
SB = SetConsoleScreenBufferSize(hConsole, Co);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
void ConsoleListener::LetterSpace(int Width, int Height)
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
// Get console info
|
|
||||||
CONSOLE_SCREEN_BUFFER_INFO ConInfo;
|
|
||||||
GetConsoleScreenBufferInfo(hConsole, &ConInfo);
|
|
||||||
|
|
||||||
//
|
|
||||||
int OldBufferWidth = ConInfo.dwSize.X;
|
|
||||||
int OldBufferHeight = ConInfo.dwSize.Y;
|
|
||||||
int OldScreenWidth = (ConInfo.srWindow.Right - ConInfo.srWindow.Left);
|
|
||||||
int OldScreenHeight = (ConInfo.srWindow.Bottom - ConInfo.srWindow.Top);
|
|
||||||
//
|
|
||||||
int NewBufferWidth = Width;
|
|
||||||
int NewBufferHeight = Height;
|
|
||||||
int NewScreenWidth = NewBufferWidth - 1;
|
|
||||||
int NewScreenHeight = OldScreenHeight;
|
|
||||||
|
|
||||||
// Width
|
|
||||||
BufferWidthHeight(NewBufferWidth, OldBufferHeight, NewScreenWidth, OldScreenHeight, (NewBufferWidth > OldScreenWidth-1));
|
|
||||||
// Height
|
|
||||||
BufferWidthHeight(NewBufferWidth, NewBufferHeight, NewScreenWidth, NewScreenHeight, (NewBufferHeight > OldScreenHeight-1));
|
|
||||||
|
|
||||||
// Resize the window too
|
|
||||||
//MoveWindow(GetConsoleWindow(), 200,200, (Width*8 + 50),(NewScreenHeight*12 + 200), true);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#ifdef _WIN32
|
|
||||||
COORD ConsoleListener::GetCoordinates(int BytesRead, int BufferWidth)
|
|
||||||
{
|
|
||||||
COORD Ret = {0, 0};
|
|
||||||
// Full rows
|
|
||||||
int Step = (int)floor((float)BytesRead / (float)BufferWidth);
|
|
||||||
Ret.Y += Step;
|
|
||||||
// Partial row
|
|
||||||
Ret.X = BytesRead - (BufferWidth * Step);
|
|
||||||
return Ret;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
void ConsoleListener::PixelSpace(int Left, int Top, int Width, int Height, bool Resize)
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
// Check size
|
|
||||||
if (Width < 8 || Height < 12) return;
|
|
||||||
|
|
||||||
bool DBef = true;
|
|
||||||
bool DAft = true;
|
|
||||||
std::string SLog = "";
|
|
||||||
|
|
||||||
const HWND hWnd = GetConsoleWindow();
|
|
||||||
const HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
||||||
|
|
||||||
// Get console info
|
|
||||||
CONSOLE_SCREEN_BUFFER_INFO ConInfo;
|
|
||||||
GetConsoleScreenBufferInfo(hConsole, &ConInfo);
|
|
||||||
DWORD BufferSize = ConInfo.dwSize.X * ConInfo.dwSize.Y;
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------
|
|
||||||
// Save the current text
|
|
||||||
// ------------------------
|
|
||||||
DWORD cCharsRead = 0;
|
|
||||||
COORD coordScreen = { 0, 0 };
|
|
||||||
|
|
||||||
static const int MAX_BYTES = 1024 * 16;
|
|
||||||
|
|
||||||
std::vector<std::array<TCHAR, MAX_BYTES>> Str;
|
|
||||||
std::vector<std::array<WORD, MAX_BYTES>> Attr;
|
|
||||||
|
|
||||||
// ReadConsoleOutputAttribute seems to have a limit at this level
|
|
||||||
static const int ReadBufferSize = MAX_BYTES - 32;
|
|
||||||
|
|
||||||
DWORD cAttrRead = ReadBufferSize;
|
|
||||||
DWORD BytesRead = 0;
|
|
||||||
while (BytesRead < BufferSize)
|
|
||||||
{
|
|
||||||
Str.resize(Str.size() + 1);
|
|
||||||
if (!ReadConsoleOutputCharacter(hConsole, Str.back().data(), ReadBufferSize, coordScreen, &cCharsRead))
|
|
||||||
SLog += Common::StringFromFormat("WriteConsoleOutputCharacter error");
|
|
||||||
|
|
||||||
Attr.resize(Attr.size() + 1);
|
|
||||||
if (!ReadConsoleOutputAttribute(hConsole, Attr.back().data(), ReadBufferSize, coordScreen, &cAttrRead))
|
|
||||||
SLog += Common::StringFromFormat("WriteConsoleOutputAttribute error");
|
|
||||||
|
|
||||||
// Break on error
|
|
||||||
if (cAttrRead == 0) break;
|
|
||||||
BytesRead += cAttrRead;
|
|
||||||
coordScreen = GetCoordinates(BytesRead, ConInfo.dwSize.X);
|
|
||||||
}
|
|
||||||
// Letter space
|
|
||||||
int LWidth = (int)(floor((float)Width / 8.0f) - 1.0f);
|
|
||||||
int LHeight = (int)(floor((float)Height / 12.0f) - 1.0f);
|
|
||||||
int LBufWidth = LWidth + 1;
|
|
||||||
int LBufHeight = (int)floor((float)BufferSize / (float)LBufWidth);
|
|
||||||
// Change screen buffer size
|
|
||||||
LetterSpace(LBufWidth, LBufHeight);
|
|
||||||
|
|
||||||
|
|
||||||
ClearScreen(true);
|
|
||||||
coordScreen.Y = 0;
|
|
||||||
coordScreen.X = 0;
|
|
||||||
DWORD cCharsWritten = 0;
|
|
||||||
|
|
||||||
int BytesWritten = 0;
|
|
||||||
DWORD cAttrWritten = 0;
|
|
||||||
for (size_t i = 0; i < Attr.size(); i++)
|
|
||||||
{
|
|
||||||
if (!WriteConsoleOutputCharacter(hConsole, Str[i].data(), ReadBufferSize, coordScreen, &cCharsWritten))
|
|
||||||
SLog += Common::StringFromFormat("WriteConsoleOutputCharacter error");
|
|
||||||
if (!WriteConsoleOutputAttribute(hConsole, Attr[i].data(), ReadBufferSize, coordScreen, &cAttrWritten))
|
|
||||||
SLog += Common::StringFromFormat("WriteConsoleOutputAttribute error");
|
|
||||||
|
|
||||||
BytesWritten += cAttrWritten;
|
|
||||||
coordScreen = GetCoordinates(BytesWritten, LBufWidth);
|
|
||||||
}
|
|
||||||
|
|
||||||
const int OldCursor = ConInfo.dwCursorPosition.Y * ConInfo.dwSize.X + ConInfo.dwCursorPosition.X;
|
|
||||||
COORD Coo = GetCoordinates(OldCursor, LBufWidth);
|
|
||||||
SetConsoleCursorPosition(hConsole, Coo);
|
|
||||||
|
|
||||||
if (SLog.length() > 0) Log(LogTypes::LNOTICE, SLog.c_str());
|
|
||||||
|
|
||||||
// Resize the window too
|
|
||||||
if (Resize) MoveWindow(GetConsoleWindow(), Left,Top, (Width + 100),Height, true);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void ConsoleListener::Log(LogTypes::LOG_LEVELS Level, const char *Text)
|
|
||||||
{
|
|
||||||
#if defined(_WIN32)
|
|
||||||
WORD Color;
|
|
||||||
|
|
||||||
switch (Level)
|
|
||||||
{
|
|
||||||
case OS_LEVEL: // light yellow
|
|
||||||
Color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY;
|
|
||||||
break;
|
|
||||||
case NOTICE_LEVEL: // light green
|
|
||||||
Color = FOREGROUND_GREEN | FOREGROUND_INTENSITY;
|
|
||||||
break;
|
|
||||||
case ERROR_LEVEL: // light red
|
|
||||||
Color = FOREGROUND_RED | FOREGROUND_INTENSITY;
|
|
||||||
break;
|
|
||||||
case WARNING_LEVEL: // light purple
|
|
||||||
Color = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY;
|
|
||||||
break;
|
|
||||||
case INFO_LEVEL: // cyan
|
|
||||||
Color = FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY;
|
|
||||||
break;
|
|
||||||
case DEBUG_LEVEL: // gray
|
|
||||||
Color = FOREGROUND_INTENSITY;
|
|
||||||
break;
|
|
||||||
default: // off-white
|
|
||||||
Color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
SetConsoleTextAttribute(hConsole, Color);
|
|
||||||
printf(Text);
|
|
||||||
#else
|
|
||||||
char ColorAttr[16] = "";
|
|
||||||
char ResetAttr[16] = "";
|
|
||||||
|
|
||||||
if (bUseColor)
|
|
||||||
{
|
|
||||||
strcpy(ResetAttr, "\033[0m");
|
|
||||||
switch (Level)
|
|
||||||
{
|
|
||||||
case NOTICE_LEVEL: // light green
|
|
||||||
strcpy(ColorAttr, "\033[92m");
|
|
||||||
break;
|
|
||||||
case ERROR_LEVEL: // light red
|
|
||||||
strcpy(ColorAttr, "\033[91m");
|
|
||||||
break;
|
|
||||||
case WARNING_LEVEL: // light yellow
|
|
||||||
strcpy(ColorAttr, "\033[93m");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fprintf(stderr, "%s%s%s", ColorAttr, Text, ResetAttr);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
// Clear console screen
|
|
||||||
void ConsoleListener::ClearScreen(bool Cursor)
|
|
||||||
{
|
|
||||||
#if defined(_WIN32)
|
|
||||||
COORD coordScreen = { 0, 0 };
|
|
||||||
DWORD cCharsWritten;
|
|
||||||
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
|
||||||
DWORD dwConSize;
|
|
||||||
|
|
||||||
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
||||||
|
|
||||||
GetConsoleScreenBufferInfo(hConsole, &csbi);
|
|
||||||
dwConSize = csbi.dwSize.X * csbi.dwSize.Y;
|
|
||||||
// Write space to the entire console
|
|
||||||
FillConsoleOutputCharacter(hConsole, TEXT(' '), dwConSize, coordScreen, &cCharsWritten);
|
|
||||||
GetConsoleScreenBufferInfo(hConsole, &csbi);
|
|
||||||
FillConsoleOutputAttribute(hConsole, csbi.wAttributes, dwConSize, coordScreen, &cCharsWritten);
|
|
||||||
// Reset cursor
|
|
||||||
if (Cursor) SetConsoleCursorPosition(hConsole, coordScreen);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -1,38 +0,0 @@
|
||||||
// Copyright 2013 Dolphin Emulator Project
|
|
||||||
// Licensed under GPLv2
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "common/log_manager.h"
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#include <windows.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class ConsoleListener : public LogListener
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ConsoleListener();
|
|
||||||
~ConsoleListener();
|
|
||||||
|
|
||||||
void Open(bool Hidden = false, int Width = 100, int Height = 100, const char * Name = "Console");
|
|
||||||
void UpdateHandle();
|
|
||||||
void Close();
|
|
||||||
bool IsOpen();
|
|
||||||
void LetterSpace(int Width, int Height);
|
|
||||||
void BufferWidthHeight(int BufferWidth, int BufferHeight, int ScreenWidth, int ScreenHeight, bool BufferFirst);
|
|
||||||
void PixelSpace(int Left, int Top, int Width, int Height, bool);
|
|
||||||
#ifdef _WIN32
|
|
||||||
COORD GetCoordinates(int BytesRead, int BufferWidth);
|
|
||||||
#endif
|
|
||||||
void Log(LogTypes::LOG_LEVELS, const char *Text) override;
|
|
||||||
void ClearScreen(bool Cursor = true);
|
|
||||||
|
|
||||||
private:
|
|
||||||
#ifdef _WIN32
|
|
||||||
HWND GetHwnd(void);
|
|
||||||
HANDLE hConsole;
|
|
||||||
#endif
|
|
||||||
bool bUseColor;
|
|
||||||
};
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Copyright 2013 Dolphin Emulator Project
|
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
||||||
// Licensed under GPLv2
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Copyright 2014 Citra Emulator Project
|
// Copyright 2014 Citra Emulator Project
|
||||||
// Licensed under GPLv2
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#include "emu_window.h"
|
#include "emu_window.h"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Copyright 2014 Citra Emulator Project
|
// Copyright 2014 Citra Emulator Project
|
||||||
// Licensed under GPLv2
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
|
@ -82,7 +82,7 @@ static void InitSymbolPath( PSTR lpszSymbolPath, PCSTR lpszIniPath )
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add user defined path
|
// Add user defined path
|
||||||
if ( lpszIniPath != NULL )
|
if ( lpszIniPath != nullptr )
|
||||||
if ( lpszIniPath[0] != '\0' )
|
if ( lpszIniPath[0] != '\0' )
|
||||||
{
|
{
|
||||||
strcat( lpszSymbolPath, ";" );
|
strcat( lpszSymbolPath, ";" );
|
||||||
|
@ -138,7 +138,7 @@ static BOOL GetFunctionInfoFromAddresses( ULONG fnAddress, ULONG stackAddress, L
|
||||||
DWORD dwSymSize = 10000;
|
DWORD dwSymSize = 10000;
|
||||||
TCHAR lpszUnDSymbol[BUFFERSIZE]=_T("?");
|
TCHAR lpszUnDSymbol[BUFFERSIZE]=_T("?");
|
||||||
CHAR lpszNonUnicodeUnDSymbol[BUFFERSIZE]="?";
|
CHAR lpszNonUnicodeUnDSymbol[BUFFERSIZE]="?";
|
||||||
LPTSTR lpszParamSep = NULL;
|
LPTSTR lpszParamSep = nullptr;
|
||||||
LPTSTR lpszParsed = lpszUnDSymbol;
|
LPTSTR lpszParsed = lpszUnDSymbol;
|
||||||
PIMAGEHLP_SYMBOL pSym = (PIMAGEHLP_SYMBOL)GlobalAlloc( GMEM_FIXED, dwSymSize );
|
PIMAGEHLP_SYMBOL pSym = (PIMAGEHLP_SYMBOL)GlobalAlloc( GMEM_FIXED, dwSymSize );
|
||||||
|
|
||||||
|
@ -187,13 +187,13 @@ static BOOL GetFunctionInfoFromAddresses( ULONG fnAddress, ULONG stackAddress, L
|
||||||
|
|
||||||
// Let's go through the stack, and modify the function prototype, and insert the actual
|
// Let's go through the stack, and modify the function prototype, and insert the actual
|
||||||
// parameter values from the stack
|
// parameter values from the stack
|
||||||
if ( _tcsstr( lpszUnDSymbol, _T("(void)") ) == NULL && _tcsstr( lpszUnDSymbol, _T("()") ) == NULL)
|
if ( _tcsstr( lpszUnDSymbol, _T("(void)") ) == nullptr && _tcsstr( lpszUnDSymbol, _T("()") ) == nullptr)
|
||||||
{
|
{
|
||||||
ULONG index = 0;
|
ULONG index = 0;
|
||||||
for( ; ; index++ )
|
for( ; ; index++ )
|
||||||
{
|
{
|
||||||
lpszParamSep = _tcschr( lpszParsed, _T(',') );
|
lpszParamSep = _tcschr( lpszParsed, _T(',') );
|
||||||
if ( lpszParamSep == NULL )
|
if ( lpszParamSep == nullptr )
|
||||||
break;
|
break;
|
||||||
|
|
||||||
*lpszParamSep = _T('\0');
|
*lpszParamSep = _T('\0');
|
||||||
|
@ -205,7 +205,7 @@ static BOOL GetFunctionInfoFromAddresses( ULONG fnAddress, ULONG stackAddress, L
|
||||||
}
|
}
|
||||||
|
|
||||||
lpszParamSep = _tcschr( lpszParsed, _T(')') );
|
lpszParamSep = _tcschr( lpszParsed, _T(')') );
|
||||||
if ( lpszParamSep != NULL )
|
if ( lpszParamSep != nullptr )
|
||||||
{
|
{
|
||||||
*lpszParamSep = _T('\0');
|
*lpszParamSep = _T('\0');
|
||||||
|
|
||||||
|
@ -248,7 +248,7 @@ static BOOL GetSourceInfoFromAddress( UINT address, LPTSTR lpszSourceInfo )
|
||||||
PCSTR2LPTSTR( lineInfo.FileName, lpszFileName );
|
PCSTR2LPTSTR( lineInfo.FileName, lpszFileName );
|
||||||
TCHAR fname[_MAX_FNAME];
|
TCHAR fname[_MAX_FNAME];
|
||||||
TCHAR ext[_MAX_EXT];
|
TCHAR ext[_MAX_EXT];
|
||||||
_tsplitpath(lpszFileName, NULL, NULL, fname, ext);
|
_tsplitpath(lpszFileName, nullptr, nullptr, fname, ext);
|
||||||
_stprintf( lpszSourceInfo, _T("%s%s(%d)"), fname, ext, lineInfo.LineNumber );
|
_stprintf( lpszSourceInfo, _T("%s%s(%d)"), fname, ext, lineInfo.LineNumber );
|
||||||
ret = TRUE;
|
ret = TRUE;
|
||||||
}
|
}
|
||||||
|
@ -332,11 +332,11 @@ void StackTrace( HANDLE hThread, const char* lpszMessage, FILE *file )
|
||||||
hProcess,
|
hProcess,
|
||||||
hThread,
|
hThread,
|
||||||
&callStack,
|
&callStack,
|
||||||
NULL,
|
nullptr,
|
||||||
NULL,
|
nullptr,
|
||||||
SymFunctionTableAccess,
|
SymFunctionTableAccess,
|
||||||
SymGetModuleBase,
|
SymGetModuleBase,
|
||||||
NULL);
|
nullptr);
|
||||||
|
|
||||||
if ( index == 0 )
|
if ( index == 0 )
|
||||||
continue;
|
continue;
|
||||||
|
@ -389,11 +389,11 @@ void StackTrace(HANDLE hThread, const char* lpszMessage, FILE *file, DWORD eip,
|
||||||
hProcess,
|
hProcess,
|
||||||
hThread,
|
hThread,
|
||||||
&callStack,
|
&callStack,
|
||||||
NULL,
|
nullptr,
|
||||||
NULL,
|
nullptr,
|
||||||
SymFunctionTableAccess,
|
SymFunctionTableAccess,
|
||||||
SymGetModuleBase,
|
SymGetModuleBase,
|
||||||
NULL);
|
nullptr);
|
||||||
|
|
||||||
if ( index == 0 )
|
if ( index == 0 )
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -57,7 +57,7 @@ public:
|
||||||
// advance the read pointer
|
// advance the read pointer
|
||||||
m_read_ptr = m_read_ptr->next;
|
m_read_ptr = m_read_ptr->next;
|
||||||
// set the next element to NULL to stop the recursive deletion
|
// set the next element to NULL to stop the recursive deletion
|
||||||
tmpptr->next = NULL;
|
tmpptr->next = nullptr;
|
||||||
delete tmpptr; // this also deletes the element
|
delete tmpptr; // this also deletes the element
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,7 +86,7 @@ private:
|
||||||
class ElementPtr
|
class ElementPtr
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ElementPtr() : current(NULL), next(NULL) {}
|
ElementPtr() : current(nullptr), next(nullptr) {}
|
||||||
|
|
||||||
~ElementPtr()
|
~ElementPtr()
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Copyright 2013 Dolphin Emulator Project
|
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
||||||
// Licensed under GPLv2
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Copyright 2013 Dolphin Emulator Project
|
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
||||||
// Licensed under GPLv2
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Copyright 2013 Dolphin Emulator Project
|
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
||||||
// Licensed under GPLv2
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
|
||||||
|
@ -88,7 +88,7 @@ bool IsDirectory(const std::string &filename)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (result < 0) {
|
if (result < 0) {
|
||||||
WARN_LOG(COMMON, "IsDirectory: stat failed on %s: %s",
|
LOG_WARNING(Common_Filesystem, "stat failed on %s: %s",
|
||||||
filename.c_str(), GetLastErrorMsg());
|
filename.c_str(), GetLastErrorMsg());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -100,33 +100,33 @@ bool IsDirectory(const std::string &filename)
|
||||||
// Doesn't supports deleting a directory
|
// Doesn't supports deleting a directory
|
||||||
bool Delete(const std::string &filename)
|
bool Delete(const std::string &filename)
|
||||||
{
|
{
|
||||||
INFO_LOG(COMMON, "Delete: file %s", filename.c_str());
|
LOG_INFO(Common_Filesystem, "file %s", filename.c_str());
|
||||||
|
|
||||||
// Return true because we care about the file no
|
// Return true because we care about the file no
|
||||||
// being there, not the actual delete.
|
// being there, not the actual delete.
|
||||||
if (!Exists(filename))
|
if (!Exists(filename))
|
||||||
{
|
{
|
||||||
WARN_LOG(COMMON, "Delete: %s does not exist", filename.c_str());
|
LOG_WARNING(Common_Filesystem, "%s does not exist", filename.c_str());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We can't delete a directory
|
// We can't delete a directory
|
||||||
if (IsDirectory(filename))
|
if (IsDirectory(filename))
|
||||||
{
|
{
|
||||||
WARN_LOG(COMMON, "Delete failed: %s is a directory", filename.c_str());
|
LOG_ERROR(Common_Filesystem, "Failed: %s is a directory", filename.c_str());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
if (!DeleteFile(Common::UTF8ToTStr(filename).c_str()))
|
if (!DeleteFile(Common::UTF8ToTStr(filename).c_str()))
|
||||||
{
|
{
|
||||||
WARN_LOG(COMMON, "Delete: DeleteFile failed on %s: %s",
|
LOG_ERROR(Common_Filesystem, "DeleteFile failed on %s: %s",
|
||||||
filename.c_str(), GetLastErrorMsg());
|
filename.c_str(), GetLastErrorMsg());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
if (unlink(filename.c_str()) == -1) {
|
if (unlink(filename.c_str()) == -1) {
|
||||||
WARN_LOG(COMMON, "Delete: unlink failed on %s: %s",
|
LOG_ERROR(Common_Filesystem, "unlink failed on %s: %s",
|
||||||
filename.c_str(), GetLastErrorMsg());
|
filename.c_str(), GetLastErrorMsg());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -138,17 +138,17 @@ bool Delete(const std::string &filename)
|
||||||
// Returns true if successful, or path already exists.
|
// Returns true if successful, or path already exists.
|
||||||
bool CreateDir(const std::string &path)
|
bool CreateDir(const std::string &path)
|
||||||
{
|
{
|
||||||
INFO_LOG(COMMON, "CreateDir: directory %s", path.c_str());
|
LOG_TRACE(Common_Filesystem, "directory %s", path.c_str());
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
if (::CreateDirectory(Common::UTF8ToTStr(path).c_str(), NULL))
|
if (::CreateDirectory(Common::UTF8ToTStr(path).c_str(), nullptr))
|
||||||
return true;
|
return true;
|
||||||
DWORD error = GetLastError();
|
DWORD error = GetLastError();
|
||||||
if (error == ERROR_ALREADY_EXISTS)
|
if (error == ERROR_ALREADY_EXISTS)
|
||||||
{
|
{
|
||||||
WARN_LOG(COMMON, "CreateDir: CreateDirectory failed on %s: already exists", path.c_str());
|
LOG_WARNING(Common_Filesystem, "CreateDirectory failed on %s: already exists", path.c_str());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
ERROR_LOG(COMMON, "CreateDir: CreateDirectory failed on %s: %i", path.c_str(), error);
|
LOG_ERROR(Common_Filesystem, "CreateDirectory failed on %s: %i", path.c_str(), error);
|
||||||
return false;
|
return false;
|
||||||
#else
|
#else
|
||||||
if (mkdir(path.c_str(), 0755) == 0)
|
if (mkdir(path.c_str(), 0755) == 0)
|
||||||
|
@ -158,11 +158,11 @@ bool CreateDir(const std::string &path)
|
||||||
|
|
||||||
if (err == EEXIST)
|
if (err == EEXIST)
|
||||||
{
|
{
|
||||||
WARN_LOG(COMMON, "CreateDir: mkdir failed on %s: already exists", path.c_str());
|
LOG_WARNING(Common_Filesystem, "mkdir failed on %s: already exists", path.c_str());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ERROR_LOG(COMMON, "CreateDir: mkdir failed on %s: %s", path.c_str(), strerror(err));
|
LOG_ERROR(Common_Filesystem, "mkdir failed on %s: %s", path.c_str(), strerror(err));
|
||||||
return false;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -171,11 +171,11 @@ bool CreateDir(const std::string &path)
|
||||||
bool CreateFullPath(const std::string &fullPath)
|
bool CreateFullPath(const std::string &fullPath)
|
||||||
{
|
{
|
||||||
int panicCounter = 100;
|
int panicCounter = 100;
|
||||||
INFO_LOG(COMMON, "CreateFullPath: path %s", fullPath.c_str());
|
LOG_TRACE(Common_Filesystem, "path %s", fullPath.c_str());
|
||||||
|
|
||||||
if (FileUtil::Exists(fullPath))
|
if (FileUtil::Exists(fullPath))
|
||||||
{
|
{
|
||||||
INFO_LOG(COMMON, "CreateFullPath: path exists %s", fullPath.c_str());
|
LOG_WARNING(Common_Filesystem, "path exists %s", fullPath.c_str());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -192,7 +192,7 @@ bool CreateFullPath(const std::string &fullPath)
|
||||||
// Include the '/' so the first call is CreateDir("/") rather than CreateDir("")
|
// Include the '/' so the first call is CreateDir("/") rather than CreateDir("")
|
||||||
std::string const subPath(fullPath.substr(0, position + 1));
|
std::string const subPath(fullPath.substr(0, position + 1));
|
||||||
if (!FileUtil::IsDirectory(subPath) && !FileUtil::CreateDir(subPath)) {
|
if (!FileUtil::IsDirectory(subPath) && !FileUtil::CreateDir(subPath)) {
|
||||||
ERROR_LOG(COMMON, "CreateFullPath: directory creation failed");
|
LOG_ERROR(Common, "CreateFullPath: directory creation failed");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,7 +200,7 @@ bool CreateFullPath(const std::string &fullPath)
|
||||||
panicCounter--;
|
panicCounter--;
|
||||||
if (panicCounter <= 0)
|
if (panicCounter <= 0)
|
||||||
{
|
{
|
||||||
ERROR_LOG(COMMON, "CreateFullPath: directory structure is too deep");
|
LOG_ERROR(Common, "CreateFullPath: directory structure is too deep");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
position++;
|
position++;
|
||||||
|
@ -211,12 +211,12 @@ bool CreateFullPath(const std::string &fullPath)
|
||||||
// Deletes a directory filename, returns true on success
|
// Deletes a directory filename, returns true on success
|
||||||
bool DeleteDir(const std::string &filename)
|
bool DeleteDir(const std::string &filename)
|
||||||
{
|
{
|
||||||
INFO_LOG(COMMON, "DeleteDir: directory %s", filename.c_str());
|
LOG_INFO(Common_Filesystem, "directory %s", filename.c_str());
|
||||||
|
|
||||||
// check if a directory
|
// check if a directory
|
||||||
if (!FileUtil::IsDirectory(filename))
|
if (!FileUtil::IsDirectory(filename))
|
||||||
{
|
{
|
||||||
ERROR_LOG(COMMON, "DeleteDir: Not a directory %s", filename.c_str());
|
LOG_ERROR(Common_Filesystem, "Not a directory %s", filename.c_str());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,7 +227,7 @@ bool DeleteDir(const std::string &filename)
|
||||||
if (rmdir(filename.c_str()) == 0)
|
if (rmdir(filename.c_str()) == 0)
|
||||||
return true;
|
return true;
|
||||||
#endif
|
#endif
|
||||||
ERROR_LOG(COMMON, "DeleteDir: %s: %s", filename.c_str(), GetLastErrorMsg());
|
LOG_ERROR(Common_Filesystem, "failed %s: %s", filename.c_str(), GetLastErrorMsg());
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -235,11 +235,11 @@ bool DeleteDir(const std::string &filename)
|
||||||
// renames file srcFilename to destFilename, returns true on success
|
// renames file srcFilename to destFilename, returns true on success
|
||||||
bool Rename(const std::string &srcFilename, const std::string &destFilename)
|
bool Rename(const std::string &srcFilename, const std::string &destFilename)
|
||||||
{
|
{
|
||||||
INFO_LOG(COMMON, "Rename: %s --> %s",
|
LOG_TRACE(Common_Filesystem, "%s --> %s",
|
||||||
srcFilename.c_str(), destFilename.c_str());
|
srcFilename.c_str(), destFilename.c_str());
|
||||||
if (rename(srcFilename.c_str(), destFilename.c_str()) == 0)
|
if (rename(srcFilename.c_str(), destFilename.c_str()) == 0)
|
||||||
return true;
|
return true;
|
||||||
ERROR_LOG(COMMON, "Rename: failed %s --> %s: %s",
|
LOG_ERROR(Common_Filesystem, "failed %s --> %s: %s",
|
||||||
srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
|
srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -247,13 +247,13 @@ bool Rename(const std::string &srcFilename, const std::string &destFilename)
|
||||||
// copies file srcFilename to destFilename, returns true on success
|
// copies file srcFilename to destFilename, returns true on success
|
||||||
bool Copy(const std::string &srcFilename, const std::string &destFilename)
|
bool Copy(const std::string &srcFilename, const std::string &destFilename)
|
||||||
{
|
{
|
||||||
INFO_LOG(COMMON, "Copy: %s --> %s",
|
LOG_TRACE(Common_Filesystem, "%s --> %s",
|
||||||
srcFilename.c_str(), destFilename.c_str());
|
srcFilename.c_str(), destFilename.c_str());
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
if (CopyFile(Common::UTF8ToTStr(srcFilename).c_str(), Common::UTF8ToTStr(destFilename).c_str(), FALSE))
|
if (CopyFile(Common::UTF8ToTStr(srcFilename).c_str(), Common::UTF8ToTStr(destFilename).c_str(), FALSE))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
ERROR_LOG(COMMON, "Copy: failed %s --> %s: %s",
|
LOG_ERROR(Common_Filesystem, "failed %s --> %s: %s",
|
||||||
srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
|
srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
|
||||||
return false;
|
return false;
|
||||||
#else
|
#else
|
||||||
|
@ -267,7 +267,7 @@ bool Copy(const std::string &srcFilename, const std::string &destFilename)
|
||||||
FILE *input = fopen(srcFilename.c_str(), "rb");
|
FILE *input = fopen(srcFilename.c_str(), "rb");
|
||||||
if (!input)
|
if (!input)
|
||||||
{
|
{
|
||||||
ERROR_LOG(COMMON, "Copy: input failed %s --> %s: %s",
|
LOG_ERROR(Common_Filesystem, "opening input failed %s --> %s: %s",
|
||||||
srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
|
srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -277,7 +277,7 @@ bool Copy(const std::string &srcFilename, const std::string &destFilename)
|
||||||
if (!output)
|
if (!output)
|
||||||
{
|
{
|
||||||
fclose(input);
|
fclose(input);
|
||||||
ERROR_LOG(COMMON, "Copy: output failed %s --> %s: %s",
|
LOG_ERROR(Common_Filesystem, "opening output failed %s --> %s: %s",
|
||||||
srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
|
srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -291,8 +291,8 @@ bool Copy(const std::string &srcFilename, const std::string &destFilename)
|
||||||
{
|
{
|
||||||
if (ferror(input) != 0)
|
if (ferror(input) != 0)
|
||||||
{
|
{
|
||||||
ERROR_LOG(COMMON,
|
LOG_ERROR(Common_Filesystem,
|
||||||
"Copy: failed reading from source, %s --> %s: %s",
|
"failed reading from source, %s --> %s: %s",
|
||||||
srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
|
srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
|
||||||
goto bail;
|
goto bail;
|
||||||
}
|
}
|
||||||
|
@ -302,8 +302,8 @@ bool Copy(const std::string &srcFilename, const std::string &destFilename)
|
||||||
int wnum = fwrite(buffer, sizeof(char), rnum, output);
|
int wnum = fwrite(buffer, sizeof(char), rnum, output);
|
||||||
if (wnum != rnum)
|
if (wnum != rnum)
|
||||||
{
|
{
|
||||||
ERROR_LOG(COMMON,
|
LOG_ERROR(Common_Filesystem,
|
||||||
"Copy: failed writing to output, %s --> %s: %s",
|
"failed writing to output, %s --> %s: %s",
|
||||||
srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
|
srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
|
||||||
goto bail;
|
goto bail;
|
||||||
}
|
}
|
||||||
|
@ -326,13 +326,13 @@ u64 GetSize(const std::string &filename)
|
||||||
{
|
{
|
||||||
if (!Exists(filename))
|
if (!Exists(filename))
|
||||||
{
|
{
|
||||||
WARN_LOG(COMMON, "GetSize: failed %s: No such file", filename.c_str());
|
LOG_ERROR(Common_Filesystem, "failed %s: No such file", filename.c_str());
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsDirectory(filename))
|
if (IsDirectory(filename))
|
||||||
{
|
{
|
||||||
WARN_LOG(COMMON, "GetSize: failed %s: is a directory", filename.c_str());
|
LOG_ERROR(Common_Filesystem, "failed %s: is a directory", filename.c_str());
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -343,12 +343,12 @@ u64 GetSize(const std::string &filename)
|
||||||
if (stat64(filename.c_str(), &buf) == 0)
|
if (stat64(filename.c_str(), &buf) == 0)
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
DEBUG_LOG(COMMON, "GetSize: %s: %lld",
|
LOG_TRACE(Common_Filesystem, "%s: %lld",
|
||||||
filename.c_str(), (long long)buf.st_size);
|
filename.c_str(), (long long)buf.st_size);
|
||||||
return buf.st_size;
|
return buf.st_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
ERROR_LOG(COMMON, "GetSize: Stat failed %s: %s",
|
LOG_ERROR(Common_Filesystem, "Stat failed %s: %s",
|
||||||
filename.c_str(), GetLastErrorMsg());
|
filename.c_str(), GetLastErrorMsg());
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -358,7 +358,7 @@ u64 GetSize(const int fd)
|
||||||
{
|
{
|
||||||
struct stat64 buf;
|
struct stat64 buf;
|
||||||
if (fstat64(fd, &buf) != 0) {
|
if (fstat64(fd, &buf) != 0) {
|
||||||
ERROR_LOG(COMMON, "GetSize: stat failed %i: %s",
|
LOG_ERROR(Common_Filesystem, "GetSize: stat failed %i: %s",
|
||||||
fd, GetLastErrorMsg());
|
fd, GetLastErrorMsg());
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -371,13 +371,13 @@ u64 GetSize(FILE *f)
|
||||||
// can't use off_t here because it can be 32-bit
|
// can't use off_t here because it can be 32-bit
|
||||||
u64 pos = ftello(f);
|
u64 pos = ftello(f);
|
||||||
if (fseeko(f, 0, SEEK_END) != 0) {
|
if (fseeko(f, 0, SEEK_END) != 0) {
|
||||||
ERROR_LOG(COMMON, "GetSize: seek failed %p: %s",
|
LOG_ERROR(Common_Filesystem, "GetSize: seek failed %p: %s",
|
||||||
f, GetLastErrorMsg());
|
f, GetLastErrorMsg());
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
u64 size = ftello(f);
|
u64 size = ftello(f);
|
||||||
if ((size != pos) && (fseeko(f, pos, SEEK_SET) != 0)) {
|
if ((size != pos) && (fseeko(f, pos, SEEK_SET) != 0)) {
|
||||||
ERROR_LOG(COMMON, "GetSize: seek failed %p: %s",
|
LOG_ERROR(Common_Filesystem, "GetSize: seek failed %p: %s",
|
||||||
f, GetLastErrorMsg());
|
f, GetLastErrorMsg());
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -387,11 +387,11 @@ u64 GetSize(FILE *f)
|
||||||
// creates an empty file filename, returns true on success
|
// creates an empty file filename, returns true on success
|
||||||
bool CreateEmptyFile(const std::string &filename)
|
bool CreateEmptyFile(const std::string &filename)
|
||||||
{
|
{
|
||||||
INFO_LOG(COMMON, "CreateEmptyFile: %s", filename.c_str());
|
LOG_TRACE(Common_Filesystem, "%s", filename.c_str());
|
||||||
|
|
||||||
if (!FileUtil::IOFile(filename, "wb"))
|
if (!FileUtil::IOFile(filename, "wb"))
|
||||||
{
|
{
|
||||||
ERROR_LOG(COMMON, "CreateEmptyFile: failed %s: %s",
|
LOG_ERROR(Common_Filesystem, "failed %s: %s",
|
||||||
filename.c_str(), GetLastErrorMsg());
|
filename.c_str(), GetLastErrorMsg());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -404,7 +404,7 @@ bool CreateEmptyFile(const std::string &filename)
|
||||||
// results into parentEntry. Returns the number of files+directories found
|
// results into parentEntry. Returns the number of files+directories found
|
||||||
u32 ScanDirectoryTree(const std::string &directory, FSTEntry& parentEntry)
|
u32 ScanDirectoryTree(const std::string &directory, FSTEntry& parentEntry)
|
||||||
{
|
{
|
||||||
INFO_LOG(COMMON, "ScanDirectoryTree: directory %s", directory.c_str());
|
LOG_TRACE(Common_Filesystem, "directory %s", directory.c_str());
|
||||||
// How many files + directories we found
|
// How many files + directories we found
|
||||||
u32 foundEntries = 0;
|
u32 foundEntries = 0;
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
@ -423,7 +423,7 @@ u32 ScanDirectoryTree(const std::string &directory, FSTEntry& parentEntry)
|
||||||
FSTEntry entry;
|
FSTEntry entry;
|
||||||
const std::string virtualName(Common::TStrToUTF8(ffd.cFileName));
|
const std::string virtualName(Common::TStrToUTF8(ffd.cFileName));
|
||||||
#else
|
#else
|
||||||
struct dirent dirent, *result = NULL;
|
struct dirent dirent, *result = nullptr;
|
||||||
|
|
||||||
DIR *dirp = opendir(directory.c_str());
|
DIR *dirp = opendir(directory.c_str());
|
||||||
if (!dirp)
|
if (!dirp)
|
||||||
|
@ -474,7 +474,7 @@ u32 ScanDirectoryTree(const std::string &directory, FSTEntry& parentEntry)
|
||||||
// Deletes the given directory and anything under it. Returns true on success.
|
// Deletes the given directory and anything under it. Returns true on success.
|
||||||
bool DeleteDirRecursively(const std::string &directory)
|
bool DeleteDirRecursively(const std::string &directory)
|
||||||
{
|
{
|
||||||
INFO_LOG(COMMON, "DeleteDirRecursively: %s", directory.c_str());
|
LOG_TRACE(Common_Filesystem, "%s", directory.c_str());
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
// Find the first file in the directory.
|
// Find the first file in the directory.
|
||||||
WIN32_FIND_DATA ffd;
|
WIN32_FIND_DATA ffd;
|
||||||
|
@ -491,7 +491,7 @@ bool DeleteDirRecursively(const std::string &directory)
|
||||||
{
|
{
|
||||||
const std::string virtualName(Common::TStrToUTF8(ffd.cFileName));
|
const std::string virtualName(Common::TStrToUTF8(ffd.cFileName));
|
||||||
#else
|
#else
|
||||||
struct dirent dirent, *result = NULL;
|
struct dirent dirent, *result = nullptr;
|
||||||
DIR *dirp = opendir(directory.c_str());
|
DIR *dirp = opendir(directory.c_str());
|
||||||
if (!dirp)
|
if (!dirp)
|
||||||
return false;
|
return false;
|
||||||
|
@ -552,7 +552,7 @@ void CopyDir(const std::string &source_path, const std::string &dest_path)
|
||||||
if (!FileUtil::Exists(source_path)) return;
|
if (!FileUtil::Exists(source_path)) return;
|
||||||
if (!FileUtil::Exists(dest_path)) FileUtil::CreateFullPath(dest_path);
|
if (!FileUtil::Exists(dest_path)) FileUtil::CreateFullPath(dest_path);
|
||||||
|
|
||||||
struct dirent dirent, *result = NULL;
|
struct dirent dirent, *result = nullptr;
|
||||||
DIR *dirp = opendir(source_path.c_str());
|
DIR *dirp = opendir(source_path.c_str());
|
||||||
if (!dirp) return;
|
if (!dirp) return;
|
||||||
|
|
||||||
|
@ -586,11 +586,11 @@ std::string GetCurrentDir()
|
||||||
{
|
{
|
||||||
char *dir;
|
char *dir;
|
||||||
// Get the current working directory (getcwd uses malloc)
|
// Get the current working directory (getcwd uses malloc)
|
||||||
if (!(dir = __getcwd(NULL, 0))) {
|
if (!(dir = __getcwd(nullptr, 0))) {
|
||||||
|
|
||||||
ERROR_LOG(COMMON, "GetCurrentDirectory failed: %s",
|
LOG_ERROR(Common_Filesystem, "GetCurrentDirectory failed: %s",
|
||||||
GetLastErrorMsg());
|
GetLastErrorMsg());
|
||||||
return NULL;
|
return nullptr;
|
||||||
}
|
}
|
||||||
std::string strDir = dir;
|
std::string strDir = dir;
|
||||||
free(dir);
|
free(dir);
|
||||||
|
@ -626,7 +626,7 @@ std::string& GetExeDirectory()
|
||||||
if (DolphinPath.empty())
|
if (DolphinPath.empty())
|
||||||
{
|
{
|
||||||
TCHAR Dolphin_exe_Path[2048];
|
TCHAR Dolphin_exe_Path[2048];
|
||||||
GetModuleFileName(NULL, Dolphin_exe_Path, 2048);
|
GetModuleFileName(nullptr, Dolphin_exe_Path, 2048);
|
||||||
DolphinPath = Common::TStrToUTF8(Dolphin_exe_Path);
|
DolphinPath = Common::TStrToUTF8(Dolphin_exe_Path);
|
||||||
DolphinPath = DolphinPath.substr(0, DolphinPath.find_last_of('\\'));
|
DolphinPath = DolphinPath.substr(0, DolphinPath.find_last_of('\\'));
|
||||||
}
|
}
|
||||||
|
@ -647,7 +647,7 @@ std::string GetSysDirectory()
|
||||||
#endif
|
#endif
|
||||||
sysDir += DIR_SEP;
|
sysDir += DIR_SEP;
|
||||||
|
|
||||||
INFO_LOG(COMMON, "GetSysDirectory: Setting to %s:", sysDir.c_str());
|
LOG_DEBUG(Common_Filesystem, "Setting to %s:", sysDir.c_str());
|
||||||
return sysDir;
|
return sysDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -676,6 +676,9 @@ const std::string& GetUserPath(const unsigned int DirIDX, const std::string &new
|
||||||
paths[D_MAPS_IDX] = paths[D_USER_IDX] + MAPS_DIR DIR_SEP;
|
paths[D_MAPS_IDX] = paths[D_USER_IDX] + MAPS_DIR DIR_SEP;
|
||||||
paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP;
|
paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP;
|
||||||
paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP;
|
paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP;
|
||||||
|
paths[D_SAVEDATA_IDX] = paths[D_USER_IDX] + SAVEDATA_DIR DIR_SEP;
|
||||||
|
paths[D_SYSDATA_IDX] = paths[D_USER_IDX] + SYSDATA_DIR DIR_SEP;
|
||||||
|
paths[D_SYSSAVEDATA_IDX] = paths[D_USER_IDX] + SYSSAVEDATA_DIR DIR_SEP;
|
||||||
paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP;
|
paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP;
|
||||||
paths[D_SHADERS_IDX] = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP;
|
paths[D_SHADERS_IDX] = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP;
|
||||||
paths[D_STATESAVES_IDX] = paths[D_USER_IDX] + STATESAVES_DIR DIR_SEP;
|
paths[D_STATESAVES_IDX] = paths[D_USER_IDX] + STATESAVES_DIR DIR_SEP;
|
||||||
|
@ -694,7 +697,7 @@ const std::string& GetUserPath(const unsigned int DirIDX, const std::string &new
|
||||||
{
|
{
|
||||||
if (!FileUtil::IsDirectory(newPath))
|
if (!FileUtil::IsDirectory(newPath))
|
||||||
{
|
{
|
||||||
WARN_LOG(COMMON, "Invalid path specified %s", newPath.c_str());
|
LOG_ERROR(Common_Filesystem, "Invalid path specified %s", newPath.c_str());
|
||||||
return paths[DirIDX];
|
return paths[DirIDX];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -717,6 +720,8 @@ const std::string& GetUserPath(const unsigned int DirIDX, const std::string &new
|
||||||
paths[D_MAPS_IDX] = paths[D_USER_IDX] + MAPS_DIR DIR_SEP;
|
paths[D_MAPS_IDX] = paths[D_USER_IDX] + MAPS_DIR DIR_SEP;
|
||||||
paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP;
|
paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP;
|
||||||
paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP;
|
paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP;
|
||||||
|
paths[D_SAVEDATA_IDX] = paths[D_USER_IDX] + SAVEDATA_DIR DIR_SEP;
|
||||||
|
paths[D_SYSSAVEDATA_IDX] = paths[D_USER_IDX] + SYSSAVEDATA_DIR DIR_SEP;
|
||||||
paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP;
|
paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP;
|
||||||
paths[D_SHADERS_IDX] = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP;
|
paths[D_SHADERS_IDX] = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP;
|
||||||
paths[D_STATESAVES_IDX] = paths[D_USER_IDX] + STATESAVES_DIR DIR_SEP;
|
paths[D_STATESAVES_IDX] = paths[D_USER_IDX] + STATESAVES_DIR DIR_SEP;
|
||||||
|
@ -753,19 +758,6 @@ const std::string& GetUserPath(const unsigned int DirIDX, const std::string &new
|
||||||
return paths[DirIDX];
|
return paths[DirIDX];
|
||||||
}
|
}
|
||||||
|
|
||||||
//std::string GetThemeDir(const std::string& theme_name)
|
|
||||||
//{
|
|
||||||
// std::string dir = FileUtil::GetUserPath(D_THEMES_IDX) + theme_name + "/";
|
|
||||||
//
|
|
||||||
//#if !defined(_WIN32)
|
|
||||||
// // If theme does not exist in user's dir load from shared directory
|
|
||||||
// if (!FileUtil::Exists(dir))
|
|
||||||
// dir = SHARED_USER_DIR THEMES_DIR "/" + theme_name + "/";
|
|
||||||
//#endif
|
|
||||||
//
|
|
||||||
// return dir;
|
|
||||||
//}
|
|
||||||
|
|
||||||
size_t WriteStringToFile(bool text_file, const std::string &str, const char *filename)
|
size_t WriteStringToFile(bool text_file, const std::string &str, const char *filename)
|
||||||
{
|
{
|
||||||
return FileUtil::IOFile(filename, text_file ? "w" : "wb").WriteBytes(str.data(), str.size());
|
return FileUtil::IOFile(filename, text_file ? "w" : "wb").WriteBytes(str.data(), str.size());
|
||||||
|
@ -826,7 +818,7 @@ void SplitFilename83(const std::string& filename, std::array<char, 9>& short_nam
|
||||||
}
|
}
|
||||||
|
|
||||||
IOFile::IOFile()
|
IOFile::IOFile()
|
||||||
: m_file(NULL), m_good(true)
|
: m_file(nullptr), m_good(true)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
IOFile::IOFile(std::FILE* file)
|
IOFile::IOFile(std::FILE* file)
|
||||||
|
@ -834,7 +826,7 @@ IOFile::IOFile(std::FILE* file)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
IOFile::IOFile(const std::string& filename, const char openmode[])
|
IOFile::IOFile(const std::string& filename, const char openmode[])
|
||||||
: m_file(NULL), m_good(true)
|
: m_file(nullptr), m_good(true)
|
||||||
{
|
{
|
||||||
Open(filename, openmode);
|
Open(filename, openmode);
|
||||||
}
|
}
|
||||||
|
@ -845,7 +837,7 @@ IOFile::~IOFile()
|
||||||
}
|
}
|
||||||
|
|
||||||
IOFile::IOFile(IOFile&& other)
|
IOFile::IOFile(IOFile&& other)
|
||||||
: m_file(NULL), m_good(true)
|
: m_file(nullptr), m_good(true)
|
||||||
{
|
{
|
||||||
Swap(other);
|
Swap(other);
|
||||||
}
|
}
|
||||||
|
@ -880,14 +872,14 @@ bool IOFile::Close()
|
||||||
if (!IsOpen() || 0 != std::fclose(m_file))
|
if (!IsOpen() || 0 != std::fclose(m_file))
|
||||||
m_good = false;
|
m_good = false;
|
||||||
|
|
||||||
m_file = NULL;
|
m_file = nullptr;
|
||||||
return m_good;
|
return m_good;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::FILE* IOFile::ReleaseHandle()
|
std::FILE* IOFile::ReleaseHandle()
|
||||||
{
|
{
|
||||||
std::FILE* const ret = m_file;
|
std::FILE* const ret = m_file;
|
||||||
m_file = NULL;
|
m_file = nullptr;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Copyright 2013 Dolphin Emulator Project
|
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
||||||
// Licensed under GPLv2
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
@ -27,6 +27,9 @@ enum {
|
||||||
D_STATESAVES_IDX,
|
D_STATESAVES_IDX,
|
||||||
D_SCREENSHOTS_IDX,
|
D_SCREENSHOTS_IDX,
|
||||||
D_SDMC_IDX,
|
D_SDMC_IDX,
|
||||||
|
D_SAVEDATA_IDX,
|
||||||
|
D_SYSDATA_IDX,
|
||||||
|
D_SYSSAVEDATA_IDX,
|
||||||
D_HIRESTEXTURES_IDX,
|
D_HIRESTEXTURES_IDX,
|
||||||
D_DUMP_IDX,
|
D_DUMP_IDX,
|
||||||
D_DUMPFRAMES_IDX,
|
D_DUMPFRAMES_IDX,
|
||||||
|
@ -202,11 +205,11 @@ public:
|
||||||
return WriteArray(reinterpret_cast<const char*>(data), length);
|
return WriteArray(reinterpret_cast<const char*>(data), length);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsOpen() { return NULL != m_file; }
|
bool IsOpen() { return nullptr != m_file; }
|
||||||
|
|
||||||
// m_good is set to false when a read, write or other function fails
|
// m_good is set to false when a read, write or other function fails
|
||||||
bool IsGood() { return m_good; }
|
bool IsGood() { return m_good; }
|
||||||
operator void*() { return m_good ? m_file : NULL; }
|
operator void*() { return m_good ? m_file : nullptr; }
|
||||||
|
|
||||||
std::FILE* ReleaseHandle();
|
std::FILE* ReleaseHandle();
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Copyright 2013 Dolphin Emulator Project
|
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
||||||
// Licensed under GPLv2
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Copyright 2013 Dolphin Emulator Project
|
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
||||||
// Licensed under GPLv2
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Copyright 2014 Citra Emulator Project
|
// Copyright 2014 Citra Emulator Project
|
||||||
// Licensed under GPLv2
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#include "key_map.h"
|
#include "key_map.h"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Copyright 2014 Citra Emulator Project
|
// Copyright 2014 Citra Emulator Project
|
||||||
// Licensed under GPLv2
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Copyright 2013 Dolphin Emulator Project
|
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
||||||
// Licensed under GPLv2
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
@ -70,7 +70,7 @@ public:
|
||||||
// good header, read some key/value pairs
|
// good header, read some key/value pairs
|
||||||
K key;
|
K key;
|
||||||
|
|
||||||
V *value = NULL;
|
V *value = nullptr;
|
||||||
u32 value_size;
|
u32 value_size;
|
||||||
u32 entry_number;
|
u32 entry_number;
|
||||||
|
|
||||||
|
|
131
src/common/log.h
131
src/common/log.h
|
@ -1,108 +1,12 @@
|
||||||
// Copyright 2013 Dolphin Emulator Project
|
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
||||||
// Licensed under GPLv2
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#ifndef LOGGING
|
#include "common/common_funcs.h"
|
||||||
#define LOGGING
|
#include "common/msg_handler.h"
|
||||||
#endif
|
#include "common/logging/log.h"
|
||||||
|
|
||||||
enum {
|
|
||||||
OS_LEVEL, // Printed by the emulated operating system
|
|
||||||
NOTICE_LEVEL, // VERY important information that is NOT errors. Like startup and OSReports.
|
|
||||||
ERROR_LEVEL, // Critical errors
|
|
||||||
WARNING_LEVEL, // Something is suspicious.
|
|
||||||
INFO_LEVEL, // General information.
|
|
||||||
DEBUG_LEVEL, // Detailed debugging - might make things slow.
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace LogTypes
|
|
||||||
{
|
|
||||||
|
|
||||||
enum LOG_TYPE {
|
|
||||||
ACTIONREPLAY,
|
|
||||||
AUDIO,
|
|
||||||
AUDIO_INTERFACE,
|
|
||||||
BOOT,
|
|
||||||
COMMANDPROCESSOR,
|
|
||||||
COMMON,
|
|
||||||
CONSOLE,
|
|
||||||
CONFIG,
|
|
||||||
DISCIO,
|
|
||||||
FILEMON,
|
|
||||||
DSPHLE,
|
|
||||||
DSPLLE,
|
|
||||||
DSP_MAIL,
|
|
||||||
DSPINTERFACE,
|
|
||||||
DVDINTERFACE,
|
|
||||||
DYNA_REC,
|
|
||||||
EXPANSIONINTERFACE,
|
|
||||||
GDB_STUB,
|
|
||||||
ARM11,
|
|
||||||
GSP,
|
|
||||||
OSHLE,
|
|
||||||
MASTER_LOG,
|
|
||||||
MEMMAP,
|
|
||||||
MEMCARD_MANAGER,
|
|
||||||
OSREPORT,
|
|
||||||
PAD,
|
|
||||||
PROCESSORINTERFACE,
|
|
||||||
PIXELENGINE,
|
|
||||||
SERIALINTERFACE,
|
|
||||||
SP1,
|
|
||||||
STREAMINGINTERFACE,
|
|
||||||
VIDEO,
|
|
||||||
VIDEOINTERFACE,
|
|
||||||
LOADER,
|
|
||||||
FILESYS,
|
|
||||||
WII_IPC_DVD,
|
|
||||||
WII_IPC_ES,
|
|
||||||
WII_IPC_FILEIO,
|
|
||||||
WII_IPC_HID,
|
|
||||||
KERNEL,
|
|
||||||
SVC,
|
|
||||||
NDMA,
|
|
||||||
HLE,
|
|
||||||
RENDER,
|
|
||||||
GPU,
|
|
||||||
HW,
|
|
||||||
TIME,
|
|
||||||
NETPLAY,
|
|
||||||
GUI,
|
|
||||||
|
|
||||||
NUMBER_OF_LOGS // Must be last
|
|
||||||
};
|
|
||||||
|
|
||||||
// FIXME: should this be removed?
|
|
||||||
enum LOG_LEVELS {
|
|
||||||
LOS = OS_LEVEL,
|
|
||||||
LNOTICE = NOTICE_LEVEL,
|
|
||||||
LERROR = ERROR_LEVEL,
|
|
||||||
LWARNING = WARNING_LEVEL,
|
|
||||||
LINFO = INFO_LEVEL,
|
|
||||||
LDEBUG = DEBUG_LEVEL,
|
|
||||||
};
|
|
||||||
|
|
||||||
#define LOGTYPES_LEVELS LogTypes::LOG_LEVELS
|
|
||||||
#define LOGTYPES_TYPE LogTypes::LOG_TYPE
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
void GenericLog(LOGTYPES_LEVELS level, LOGTYPES_TYPE type, const char*file, int line,
|
|
||||||
const char* function, const char* fmt, ...)
|
|
||||||
#ifdef __GNUC__
|
|
||||||
__attribute__((format(printf, 6, 7)))
|
|
||||||
#endif
|
|
||||||
;
|
|
||||||
|
|
||||||
#if defined LOGGING || defined _DEBUG || defined DEBUGFAST
|
|
||||||
#define MAX_LOGLEVEL LDEBUG
|
|
||||||
#else
|
|
||||||
#ifndef MAX_LOGLEVEL
|
|
||||||
#define MAX_LOGLEVEL LWARNING
|
|
||||||
#endif // loglevel
|
|
||||||
#endif // logging
|
|
||||||
|
|
||||||
#ifdef MSVC_VER
|
#ifdef MSVC_VER
|
||||||
#ifndef __func__
|
#ifndef __func__
|
||||||
|
@ -110,29 +14,16 @@ void GenericLog(LOGTYPES_LEVELS level, LOGTYPES_TYPE type, const char*file, int
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Let the compiler optimize this out
|
#if _DEBUG
|
||||||
#define GENERIC_LOG(t, v, ...) { \
|
|
||||||
if (v <= LogTypes::MAX_LOGLEVEL) \
|
|
||||||
GenericLog(v, t, __FILE__, __LINE__, __func__, __VA_ARGS__); \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define OS_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LOS, __VA_ARGS__) } while (0)
|
|
||||||
#define ERROR_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LERROR, __VA_ARGS__) } while (0)
|
|
||||||
#define WARN_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LWARNING, __VA_ARGS__) } while (0)
|
|
||||||
#define NOTICE_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LNOTICE, __VA_ARGS__) } while (0)
|
|
||||||
#define INFO_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LINFO, __VA_ARGS__) } while (0)
|
|
||||||
#define DEBUG_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LDEBUG, __VA_ARGS__) } while (0)
|
|
||||||
|
|
||||||
#if MAX_LOGLEVEL >= DEBUG_LEVEL
|
|
||||||
#define _dbg_assert_(_t_, _a_) \
|
#define _dbg_assert_(_t_, _a_) \
|
||||||
if (!(_a_)) {\
|
if (!(_a_)) {\
|
||||||
ERROR_LOG(_t_, "Error...\n\n Line: %d\n File: %s\n Time: %s\n\nIgnore and continue?", \
|
LOG_CRITICAL(_t_, "Error...\n\n Line: %d\n File: %s\n Time: %s\n\nIgnore and continue?", \
|
||||||
__LINE__, __FILE__, __TIME__); \
|
__LINE__, __FILE__, __TIME__); \
|
||||||
if (!PanicYesNo("*** Assertion (see log)***\n")) {Crash();} \
|
if (!PanicYesNo("*** Assertion (see log)***\n")) {Crash();} \
|
||||||
}
|
}
|
||||||
#define _dbg_assert_msg_(_t_, _a_, ...)\
|
#define _dbg_assert_msg_(_t_, _a_, ...)\
|
||||||
if (!(_a_)) {\
|
if (!(_a_)) {\
|
||||||
ERROR_LOG(_t_, __VA_ARGS__); \
|
LOG_CRITICAL(_t_, __VA_ARGS__); \
|
||||||
if (!PanicYesNo(__VA_ARGS__)) {Crash();} \
|
if (!PanicYesNo(__VA_ARGS__)) {Crash();} \
|
||||||
}
|
}
|
||||||
#define _dbg_update_() Host_UpdateLogDisplay();
|
#define _dbg_update_() Host_UpdateLogDisplay();
|
||||||
|
@ -144,12 +35,12 @@ void GenericLog(LOGTYPES_LEVELS level, LOGTYPES_TYPE type, const char*file, int
|
||||||
#define _dbg_assert_(_t_, _a_) {}
|
#define _dbg_assert_(_t_, _a_) {}
|
||||||
#define _dbg_assert_msg_(_t_, _a_, _desc_, ...) {}
|
#define _dbg_assert_msg_(_t_, _a_, _desc_, ...) {}
|
||||||
#endif // dbg_assert
|
#endif // dbg_assert
|
||||||
#endif // MAX_LOGLEVEL DEBUG
|
#endif
|
||||||
|
|
||||||
#define _assert_(_a_) _dbg_assert_(MASTER_LOG, _a_)
|
#define _assert_(_a_) _dbg_assert_(MASTER_LOG, _a_)
|
||||||
|
|
||||||
#ifndef GEKKO
|
#ifndef GEKKO
|
||||||
#ifdef MSVC_VER
|
#ifdef _WIN32
|
||||||
#define _assert_msg_(_t_, _a_, _fmt_, ...) \
|
#define _assert_msg_(_t_, _a_, _fmt_, ...) \
|
||||||
if (!(_a_)) {\
|
if (!(_a_)) {\
|
||||||
if (!PanicYesNo(_fmt_, __VA_ARGS__)) {Crash();} \
|
if (!PanicYesNo(_fmt_, __VA_ARGS__)) {Crash();} \
|
||||||
|
@ -159,7 +50,7 @@ void GenericLog(LOGTYPES_LEVELS level, LOGTYPES_TYPE type, const char*file, int
|
||||||
if (!(_a_)) {\
|
if (!(_a_)) {\
|
||||||
if (!PanicYesNo(_fmt_, ##__VA_ARGS__)) {Crash();} \
|
if (!PanicYesNo(_fmt_, ##__VA_ARGS__)) {Crash();} \
|
||||||
}
|
}
|
||||||
#endif // MSVC_VER
|
#endif // _WIN32
|
||||||
#else // GEKKO
|
#else // GEKKO
|
||||||
#define _assert_msg_(_t_, _a_, _fmt_, ...)
|
#define _assert_msg_(_t_, _a_, _fmt_, ...)
|
||||||
#endif
|
#endif
|
|
@ -1,199 +0,0 @@
|
||||||
// Copyright 2013 Dolphin Emulator Project
|
|
||||||
// Licensed under GPLv2
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
#include "common/log_manager.h"
|
|
||||||
#include "common/console_listener.h"
|
|
||||||
#include "common/timer.h"
|
|
||||||
|
|
||||||
void GenericLog(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, const char* file, int line,
|
|
||||||
const char* function, const char* fmt, ...)
|
|
||||||
{
|
|
||||||
va_list args;
|
|
||||||
va_start(args, fmt);
|
|
||||||
|
|
||||||
if (LogManager::GetInstance()) {
|
|
||||||
LogManager::GetInstance()->Log(level, type,
|
|
||||||
file, line, function, fmt, args);
|
|
||||||
}
|
|
||||||
va_end(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
LogManager *LogManager::m_logManager = NULL;
|
|
||||||
|
|
||||||
LogManager::LogManager()
|
|
||||||
{
|
|
||||||
// create log files
|
|
||||||
m_Log[LogTypes::MASTER_LOG] = new LogContainer("*", "Master Log");
|
|
||||||
m_Log[LogTypes::BOOT] = new LogContainer("BOOT", "Boot");
|
|
||||||
m_Log[LogTypes::COMMON] = new LogContainer("COMMON", "Common");
|
|
||||||
m_Log[LogTypes::CONFIG] = new LogContainer("CONFIG", "Configuration");
|
|
||||||
m_Log[LogTypes::DISCIO] = new LogContainer("DIO", "Disc IO");
|
|
||||||
m_Log[LogTypes::FILEMON] = new LogContainer("FileMon", "File Monitor");
|
|
||||||
m_Log[LogTypes::PAD] = new LogContainer("PAD", "Pad");
|
|
||||||
m_Log[LogTypes::PIXELENGINE] = new LogContainer("PE", "PixelEngine");
|
|
||||||
m_Log[LogTypes::COMMANDPROCESSOR] = new LogContainer("CP", "CommandProc");
|
|
||||||
m_Log[LogTypes::VIDEOINTERFACE] = new LogContainer("VI", "VideoInt");
|
|
||||||
m_Log[LogTypes::SERIALINTERFACE] = new LogContainer("SI", "SerialInt");
|
|
||||||
m_Log[LogTypes::PROCESSORINTERFACE] = new LogContainer("PI", "ProcessorInt");
|
|
||||||
m_Log[LogTypes::MEMMAP] = new LogContainer("MI", "MI & memmap");
|
|
||||||
m_Log[LogTypes::SP1] = new LogContainer("SP1", "Serial Port 1");
|
|
||||||
m_Log[LogTypes::STREAMINGINTERFACE] = new LogContainer("Stream", "StreamingInt");
|
|
||||||
m_Log[LogTypes::DSPINTERFACE] = new LogContainer("DSP", "DSPInterface");
|
|
||||||
m_Log[LogTypes::DVDINTERFACE] = new LogContainer("DVD", "DVDInterface");
|
|
||||||
m_Log[LogTypes::GSP] = new LogContainer("GSP", "GSP");
|
|
||||||
m_Log[LogTypes::EXPANSIONINTERFACE] = new LogContainer("EXI", "ExpansionInt");
|
|
||||||
m_Log[LogTypes::GDB_STUB] = new LogContainer("GDB_STUB", "GDB Stub");
|
|
||||||
m_Log[LogTypes::AUDIO_INTERFACE] = new LogContainer("AI", "AudioInt");
|
|
||||||
m_Log[LogTypes::ARM11] = new LogContainer("ARM11", "ARM11");
|
|
||||||
m_Log[LogTypes::OSHLE] = new LogContainer("HLE", "HLE");
|
|
||||||
m_Log[LogTypes::DSPHLE] = new LogContainer("DSPHLE", "DSP HLE");
|
|
||||||
m_Log[LogTypes::DSPLLE] = new LogContainer("DSPLLE", "DSP LLE");
|
|
||||||
m_Log[LogTypes::DSP_MAIL] = new LogContainer("DSPMails", "DSP Mails");
|
|
||||||
m_Log[LogTypes::VIDEO] = new LogContainer("Video", "Video Backend");
|
|
||||||
m_Log[LogTypes::AUDIO] = new LogContainer("Audio", "Audio Emulator");
|
|
||||||
m_Log[LogTypes::DYNA_REC] = new LogContainer("JIT", "JIT");
|
|
||||||
m_Log[LogTypes::CONSOLE] = new LogContainer("CONSOLE", "Dolphin Console");
|
|
||||||
m_Log[LogTypes::OSREPORT] = new LogContainer("OSREPORT", "OSReport");
|
|
||||||
m_Log[LogTypes::TIME] = new LogContainer("Time", "Core Timing");
|
|
||||||
m_Log[LogTypes::LOADER] = new LogContainer("Loader", "Loader");
|
|
||||||
m_Log[LogTypes::FILESYS] = new LogContainer("FileSys", "File System");
|
|
||||||
m_Log[LogTypes::WII_IPC_HID] = new LogContainer("WII_IPC_HID", "WII IPC HID");
|
|
||||||
m_Log[LogTypes::KERNEL] = new LogContainer("KERNEL", "KERNEL HLE");
|
|
||||||
m_Log[LogTypes::WII_IPC_DVD] = new LogContainer("WII_IPC_DVD", "WII IPC DVD");
|
|
||||||
m_Log[LogTypes::WII_IPC_ES] = new LogContainer("WII_IPC_ES", "WII IPC ES");
|
|
||||||
m_Log[LogTypes::WII_IPC_FILEIO] = new LogContainer("WII_IPC_FILEIO", "WII IPC FILEIO");
|
|
||||||
m_Log[LogTypes::RENDER] = new LogContainer("RENDER", "RENDER");
|
|
||||||
m_Log[LogTypes::GPU] = new LogContainer("GPU", "GPU");
|
|
||||||
m_Log[LogTypes::SVC] = new LogContainer("SVC", "Supervisor Call HLE");
|
|
||||||
m_Log[LogTypes::NDMA] = new LogContainer("NDMA", "NDMA");
|
|
||||||
m_Log[LogTypes::HLE] = new LogContainer("HLE", "High Level Emulation");
|
|
||||||
m_Log[LogTypes::HW] = new LogContainer("HW", "Hardware");
|
|
||||||
m_Log[LogTypes::ACTIONREPLAY] = new LogContainer("ActionReplay", "ActionReplay");
|
|
||||||
m_Log[LogTypes::MEMCARD_MANAGER] = new LogContainer("MemCard Manager", "MemCard Manager");
|
|
||||||
m_Log[LogTypes::NETPLAY] = new LogContainer("NETPLAY", "Netplay");
|
|
||||||
m_Log[LogTypes::GUI] = new LogContainer("GUI", "GUI");
|
|
||||||
|
|
||||||
m_fileLog = new FileLogListener(FileUtil::GetUserPath(F_MAINLOG_IDX).c_str());
|
|
||||||
m_consoleLog = new ConsoleListener();
|
|
||||||
m_debuggerLog = new DebuggerLogListener();
|
|
||||||
|
|
||||||
for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; ++i)
|
|
||||||
{
|
|
||||||
m_Log[i]->SetEnable(true);
|
|
||||||
m_Log[i]->AddListener(m_fileLog);
|
|
||||||
m_Log[i]->AddListener(m_consoleLog);
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
if (IsDebuggerPresent())
|
|
||||||
m_Log[i]->AddListener(m_debuggerLog);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
m_consoleLog->Open();
|
|
||||||
}
|
|
||||||
|
|
||||||
LogManager::~LogManager()
|
|
||||||
{
|
|
||||||
for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; ++i)
|
|
||||||
{
|
|
||||||
m_logManager->RemoveListener((LogTypes::LOG_TYPE)i, m_fileLog);
|
|
||||||
m_logManager->RemoveListener((LogTypes::LOG_TYPE)i, m_consoleLog);
|
|
||||||
m_logManager->RemoveListener((LogTypes::LOG_TYPE)i, m_debuggerLog);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; ++i)
|
|
||||||
delete m_Log[i];
|
|
||||||
|
|
||||||
delete m_fileLog;
|
|
||||||
delete m_consoleLog;
|
|
||||||
delete m_debuggerLog;
|
|
||||||
}
|
|
||||||
|
|
||||||
void LogManager::Log(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, const char* file,
|
|
||||||
int line, const char* function, const char *fmt, va_list args)
|
|
||||||
{
|
|
||||||
char temp[MAX_MSGLEN];
|
|
||||||
char msg[MAX_MSGLEN * 2];
|
|
||||||
LogContainer *log = m_Log[type];
|
|
||||||
|
|
||||||
if (!log->IsEnabled() || level > log->GetLevel() || ! log->HasListeners())
|
|
||||||
return;
|
|
||||||
|
|
||||||
Common::CharArrayFromFormatV(temp, MAX_MSGLEN, fmt, args);
|
|
||||||
|
|
||||||
static const char level_to_char[7] = "ONEWID";
|
|
||||||
sprintf(msg, "%s %s:%u %c[%s] %s: %s\n", Common::Timer::GetTimeFormatted().c_str(), file, line,
|
|
||||||
level_to_char[(int)level], log->GetShortName(), function, temp);
|
|
||||||
|
|
||||||
#ifdef ANDROID
|
|
||||||
Host_SysMessage(msg);
|
|
||||||
#endif
|
|
||||||
log->Trigger(level, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
void LogManager::Init()
|
|
||||||
{
|
|
||||||
m_logManager = new LogManager();
|
|
||||||
}
|
|
||||||
|
|
||||||
void LogManager::Shutdown()
|
|
||||||
{
|
|
||||||
delete m_logManager;
|
|
||||||
m_logManager = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
LogContainer::LogContainer(const char* shortName, const char* fullName, bool enable)
|
|
||||||
: m_enable(enable)
|
|
||||||
{
|
|
||||||
strncpy(m_fullName, fullName, 128);
|
|
||||||
strncpy(m_shortName, shortName, 32);
|
|
||||||
m_level = LogTypes::MAX_LOGLEVEL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// LogContainer
|
|
||||||
void LogContainer::AddListener(LogListener *listener)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lk(m_listeners_lock);
|
|
||||||
m_listeners.insert(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
void LogContainer::RemoveListener(LogListener *listener)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lk(m_listeners_lock);
|
|
||||||
m_listeners.erase(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
void LogContainer::Trigger(LogTypes::LOG_LEVELS level, const char *msg)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lk(m_listeners_lock);
|
|
||||||
|
|
||||||
std::set<LogListener*>::const_iterator i;
|
|
||||||
for (i = m_listeners.begin(); i != m_listeners.end(); ++i)
|
|
||||||
{
|
|
||||||
(*i)->Log(level, msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FileLogListener::FileLogListener(const char *filename)
|
|
||||||
{
|
|
||||||
OpenFStream(m_logfile, filename, std::ios::app);
|
|
||||||
SetEnable(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileLogListener::Log(LogTypes::LOG_LEVELS, const char *msg)
|
|
||||||
{
|
|
||||||
if (!IsEnabled() || !IsValid())
|
|
||||||
return;
|
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lk(m_log_lock);
|
|
||||||
m_logfile << msg << std::flush;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DebuggerLogListener::Log(LogTypes::LOG_LEVELS, const char *msg)
|
|
||||||
{
|
|
||||||
#if _MSC_VER
|
|
||||||
::OutputDebugStringA(msg);
|
|
||||||
#endif
|
|
||||||
}
|
|
|
@ -1,166 +0,0 @@
|
||||||
// Copyright 2013 Dolphin Emulator Project
|
|
||||||
// Licensed under GPLv2
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "common/log.h"
|
|
||||||
#include "common/string_util.h"
|
|
||||||
#include "common/file_util.h"
|
|
||||||
|
|
||||||
#include <cstring>
|
|
||||||
#include <set>
|
|
||||||
#include <mutex>
|
|
||||||
|
|
||||||
#define MAX_MESSAGES 8000
|
|
||||||
#define MAX_MSGLEN 1024
|
|
||||||
|
|
||||||
|
|
||||||
// pure virtual interface
|
|
||||||
class LogListener
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual ~LogListener() {}
|
|
||||||
|
|
||||||
virtual void Log(LogTypes::LOG_LEVELS, const char *msg) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
class FileLogListener : public LogListener
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
FileLogListener(const char *filename);
|
|
||||||
|
|
||||||
void Log(LogTypes::LOG_LEVELS, const char *msg) override;
|
|
||||||
|
|
||||||
bool IsValid() { return !m_logfile.fail(); }
|
|
||||||
bool IsEnabled() const { return m_enable; }
|
|
||||||
void SetEnable(bool enable) { m_enable = enable; }
|
|
||||||
|
|
||||||
const char* GetName() const { return "file"; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::mutex m_log_lock;
|
|
||||||
std::ofstream m_logfile;
|
|
||||||
bool m_enable;
|
|
||||||
};
|
|
||||||
|
|
||||||
class DebuggerLogListener : public LogListener
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
void Log(LogTypes::LOG_LEVELS, const char *msg) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class LogContainer
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
LogContainer(const char* shortName, const char* fullName, bool enable = false);
|
|
||||||
|
|
||||||
const char* GetShortName() const { return m_shortName; }
|
|
||||||
const char* GetFullName() const { return m_fullName; }
|
|
||||||
|
|
||||||
void AddListener(LogListener* listener);
|
|
||||||
void RemoveListener(LogListener* listener);
|
|
||||||
|
|
||||||
void Trigger(LogTypes::LOG_LEVELS, const char *msg);
|
|
||||||
|
|
||||||
bool IsEnabled() const { return m_enable; }
|
|
||||||
void SetEnable(bool enable) { m_enable = enable; }
|
|
||||||
|
|
||||||
LogTypes::LOG_LEVELS GetLevel() const { return m_level; }
|
|
||||||
|
|
||||||
void SetLevel(LogTypes::LOG_LEVELS level) { m_level = level; }
|
|
||||||
|
|
||||||
bool HasListeners() const { return !m_listeners.empty(); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
char m_fullName[128];
|
|
||||||
char m_shortName[32];
|
|
||||||
bool m_enable;
|
|
||||||
LogTypes::LOG_LEVELS m_level;
|
|
||||||
std::mutex m_listeners_lock;
|
|
||||||
std::set<LogListener*> m_listeners;
|
|
||||||
};
|
|
||||||
|
|
||||||
class ConsoleListener;
|
|
||||||
|
|
||||||
class LogManager : NonCopyable
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
LogContainer* m_Log[LogTypes::NUMBER_OF_LOGS];
|
|
||||||
FileLogListener *m_fileLog;
|
|
||||||
ConsoleListener *m_consoleLog;
|
|
||||||
DebuggerLogListener *m_debuggerLog;
|
|
||||||
static LogManager *m_logManager; // Singleton. Ugh.
|
|
||||||
|
|
||||||
LogManager();
|
|
||||||
~LogManager();
|
|
||||||
public:
|
|
||||||
|
|
||||||
static u32 GetMaxLevel() { return LogTypes::MAX_LOGLEVEL; }
|
|
||||||
|
|
||||||
void Log(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, const char* file, int line,
|
|
||||||
const char* function, const char *fmt, va_list args);
|
|
||||||
|
|
||||||
void SetLogLevel(LogTypes::LOG_TYPE type, LogTypes::LOG_LEVELS level)
|
|
||||||
{
|
|
||||||
m_Log[type]->SetLevel(level);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetEnable(LogTypes::LOG_TYPE type, bool enable)
|
|
||||||
{
|
|
||||||
m_Log[type]->SetEnable(enable);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsEnabled(LogTypes::LOG_TYPE type) const
|
|
||||||
{
|
|
||||||
return m_Log[type]->IsEnabled();
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* GetShortName(LogTypes::LOG_TYPE type) const
|
|
||||||
{
|
|
||||||
return m_Log[type]->GetShortName();
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* GetFullName(LogTypes::LOG_TYPE type) const
|
|
||||||
{
|
|
||||||
return m_Log[type]->GetFullName();
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddListener(LogTypes::LOG_TYPE type, LogListener *listener)
|
|
||||||
{
|
|
||||||
m_Log[type]->AddListener(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RemoveListener(LogTypes::LOG_TYPE type, LogListener *listener)
|
|
||||||
{
|
|
||||||
m_Log[type]->RemoveListener(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
FileLogListener *GetFileListener() const
|
|
||||||
{
|
|
||||||
return m_fileLog;
|
|
||||||
}
|
|
||||||
|
|
||||||
ConsoleListener *GetConsoleListener() const
|
|
||||||
{
|
|
||||||
return m_consoleLog;
|
|
||||||
}
|
|
||||||
|
|
||||||
DebuggerLogListener *GetDebuggerListener() const
|
|
||||||
{
|
|
||||||
return m_debuggerLog;
|
|
||||||
}
|
|
||||||
|
|
||||||
static LogManager* GetInstance()
|
|
||||||
{
|
|
||||||
return m_logManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void SetInstance(LogManager *logManager)
|
|
||||||
{
|
|
||||||
m_logManager = logManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Init();
|
|
||||||
static void Shutdown();
|
|
||||||
};
|
|
151
src/common/logging/backend.cpp
Normal file
151
src/common/logging/backend.cpp
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
// Copyright 2014 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include "common/log.h" // For _dbg_assert_
|
||||||
|
|
||||||
|
#include "common/logging/backend.h"
|
||||||
|
#include "common/logging/log.h"
|
||||||
|
#include "common/logging/text_formatter.h"
|
||||||
|
|
||||||
|
namespace Log {
|
||||||
|
|
||||||
|
static std::shared_ptr<Logger> global_logger;
|
||||||
|
|
||||||
|
/// Macro listing all log classes. Code should define CLS and SUB as desired before invoking this.
|
||||||
|
#define ALL_LOG_CLASSES() \
|
||||||
|
CLS(Log) \
|
||||||
|
CLS(Common) \
|
||||||
|
SUB(Common, Filesystem) \
|
||||||
|
SUB(Common, Memory) \
|
||||||
|
CLS(Core) \
|
||||||
|
SUB(Core, ARM11) \
|
||||||
|
CLS(Config) \
|
||||||
|
CLS(Debug) \
|
||||||
|
SUB(Debug, Emulated) \
|
||||||
|
SUB(Debug, GPU) \
|
||||||
|
SUB(Debug, Breakpoint) \
|
||||||
|
CLS(Kernel) \
|
||||||
|
SUB(Kernel, SVC) \
|
||||||
|
CLS(Service) \
|
||||||
|
SUB(Service, SRV) \
|
||||||
|
SUB(Service, FS) \
|
||||||
|
SUB(Service, APT) \
|
||||||
|
SUB(Service, GSP) \
|
||||||
|
SUB(Service, AC) \
|
||||||
|
SUB(Service, PTM) \
|
||||||
|
SUB(Service, CFG) \
|
||||||
|
SUB(Service, DSP) \
|
||||||
|
SUB(Service, HID) \
|
||||||
|
CLS(HW) \
|
||||||
|
SUB(HW, Memory) \
|
||||||
|
SUB(HW, GPU) \
|
||||||
|
CLS(Frontend) \
|
||||||
|
CLS(Render) \
|
||||||
|
SUB(Render, Software) \
|
||||||
|
SUB(Render, OpenGL) \
|
||||||
|
CLS(Loader)
|
||||||
|
|
||||||
|
Logger::Logger() {
|
||||||
|
// Register logging classes so that they can be queried at runtime
|
||||||
|
size_t parent_class;
|
||||||
|
all_classes.reserve((size_t)Class::Count);
|
||||||
|
|
||||||
|
#define CLS(x) \
|
||||||
|
all_classes.push_back(Class::x); \
|
||||||
|
parent_class = all_classes.size() - 1;
|
||||||
|
#define SUB(x, y) \
|
||||||
|
all_classes.push_back(Class::x##_##y); \
|
||||||
|
all_classes[parent_class].num_children += 1;
|
||||||
|
|
||||||
|
ALL_LOG_CLASSES()
|
||||||
|
#undef CLS
|
||||||
|
#undef SUB
|
||||||
|
|
||||||
|
// Ensures that ALL_LOG_CLASSES isn't missing any entries.
|
||||||
|
_dbg_assert_(Log, all_classes.size() == (size_t)Class::Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetClassName is a macro defined by Windows.h, grrr...
|
||||||
|
const char* Logger::GetLogClassName(Class log_class) {
|
||||||
|
switch (log_class) {
|
||||||
|
#define CLS(x) case Class::x: return #x;
|
||||||
|
#define SUB(x, y) case Class::x##_##y: return #x "." #y;
|
||||||
|
ALL_LOG_CLASSES()
|
||||||
|
#undef CLS
|
||||||
|
#undef SUB
|
||||||
|
}
|
||||||
|
return "Unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* Logger::GetLevelName(Level log_level) {
|
||||||
|
#define LVL(x) case Level::x: return #x
|
||||||
|
switch (log_level) {
|
||||||
|
LVL(Trace);
|
||||||
|
LVL(Debug);
|
||||||
|
LVL(Info);
|
||||||
|
LVL(Warning);
|
||||||
|
LVL(Error);
|
||||||
|
LVL(Critical);
|
||||||
|
}
|
||||||
|
return "Unknown";
|
||||||
|
#undef LVL
|
||||||
|
}
|
||||||
|
|
||||||
|
void Logger::LogMessage(Entry entry) {
|
||||||
|
ring_buffer.Push(std::move(entry));
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Logger::GetEntries(Entry* out_buffer, size_t buffer_len) {
|
||||||
|
return ring_buffer.BlockingPop(out_buffer, buffer_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Logger> InitGlobalLogger() {
|
||||||
|
global_logger = std::make_shared<Logger>();
|
||||||
|
return global_logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entry CreateEntry(Class log_class, Level log_level,
|
||||||
|
const char* filename, unsigned int line_nr, const char* function,
|
||||||
|
const char* format, va_list args) {
|
||||||
|
using std::chrono::steady_clock;
|
||||||
|
using std::chrono::duration_cast;
|
||||||
|
|
||||||
|
static steady_clock::time_point time_origin = steady_clock::now();
|
||||||
|
|
||||||
|
std::array<char, 4 * 1024> formatting_buffer;
|
||||||
|
|
||||||
|
Entry entry;
|
||||||
|
entry.timestamp = duration_cast<std::chrono::microseconds>(steady_clock::now() - time_origin);
|
||||||
|
entry.log_class = log_class;
|
||||||
|
entry.log_level = log_level;
|
||||||
|
|
||||||
|
snprintf(formatting_buffer.data(), formatting_buffer.size(), "%s:%s:%u", filename, function, line_nr);
|
||||||
|
entry.location = std::string(formatting_buffer.data());
|
||||||
|
|
||||||
|
vsnprintf(formatting_buffer.data(), formatting_buffer.size(), format, args);
|
||||||
|
entry.message = std::string(formatting_buffer.data());
|
||||||
|
|
||||||
|
return std::move(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogMessage(Class log_class, Level log_level,
|
||||||
|
const char* filename, unsigned int line_nr, const char* function,
|
||||||
|
const char* format, ...) {
|
||||||
|
va_list args;
|
||||||
|
va_start(args, format);
|
||||||
|
Entry entry = CreateEntry(log_class, log_level,
|
||||||
|
filename, line_nr, function, format, args);
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
if (global_logger != nullptr && !global_logger->IsClosed()) {
|
||||||
|
global_logger->LogMessage(std::move(entry));
|
||||||
|
} else {
|
||||||
|
// Fall back to directly printing to stderr
|
||||||
|
PrintMessage(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
134
src/common/logging/backend.h
Normal file
134
src/common/logging/backend.h
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
// Copyright 2014 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdarg>
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "common/concurrent_ring_buffer.h"
|
||||||
|
|
||||||
|
#include "common/logging/log.h"
|
||||||
|
|
||||||
|
namespace Log {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A log entry. Log entries are store in a structured format to permit more varied output
|
||||||
|
* formatting on different frontends, as well as facilitating filtering and aggregation.
|
||||||
|
*/
|
||||||
|
struct Entry {
|
||||||
|
std::chrono::microseconds timestamp;
|
||||||
|
Class log_class;
|
||||||
|
Level log_level;
|
||||||
|
std::string location;
|
||||||
|
std::string message;
|
||||||
|
|
||||||
|
Entry() = default;
|
||||||
|
|
||||||
|
// TODO(yuriks) Use defaulted move constructors once MSVC supports them
|
||||||
|
#define MOVE(member) member(std::move(o.member))
|
||||||
|
Entry(Entry&& o)
|
||||||
|
: MOVE(timestamp), MOVE(log_class), MOVE(log_level),
|
||||||
|
MOVE(location), MOVE(message)
|
||||||
|
{}
|
||||||
|
#undef MOVE
|
||||||
|
|
||||||
|
Entry& operator=(const Entry&& o) {
|
||||||
|
#define MOVE(member) member = std::move(o.member)
|
||||||
|
MOVE(timestamp);
|
||||||
|
MOVE(log_class);
|
||||||
|
MOVE(log_level);
|
||||||
|
MOVE(location);
|
||||||
|
MOVE(message);
|
||||||
|
#undef MOVE
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ClassInfo {
|
||||||
|
Class log_class;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Total number of (direct or indirect) sub classes this class has. If any, they follow in
|
||||||
|
* sequence after this class in the class list.
|
||||||
|
*/
|
||||||
|
unsigned int num_children = 0;
|
||||||
|
|
||||||
|
ClassInfo(Class log_class) : log_class(log_class) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logging management class. This class has the dual purpose of acting as an exchange point between
|
||||||
|
* the logging clients and the log outputter, as well as containing reflection info about available
|
||||||
|
* log classes.
|
||||||
|
*/
|
||||||
|
class Logger {
|
||||||
|
private:
|
||||||
|
using Buffer = Common::ConcurrentRingBuffer<Entry, 16 * 1024 / sizeof(Entry)>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static const size_t QUEUE_CLOSED = Buffer::QUEUE_CLOSED;
|
||||||
|
|
||||||
|
Logger();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of all vector classes and subclasses. The sequence returned is a pre-order of
|
||||||
|
* classes and subclasses, which together with the `num_children` field in ClassInfo, allows
|
||||||
|
* you to recover the hierarchy.
|
||||||
|
*/
|
||||||
|
const std::vector<ClassInfo>& GetClasses() const { return all_classes; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the name of the passed log class as a C-string. Subclasses are separated by periods
|
||||||
|
* instead of underscores as in the enumeration.
|
||||||
|
*/
|
||||||
|
static const char* GetLogClassName(Class log_class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the name of the passed log level as a C-string.
|
||||||
|
*/
|
||||||
|
static const char* GetLevelName(Level log_level);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends a messages to the log buffer.
|
||||||
|
* @note This function is thread safe.
|
||||||
|
*/
|
||||||
|
void LogMessage(Entry entry);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a batch of messages from the log buffer, blocking until they are available.
|
||||||
|
* @note This function is thread safe.
|
||||||
|
*
|
||||||
|
* @param out_buffer Destination buffer that will receive the log entries.
|
||||||
|
* @param buffer_len The maximum size of `out_buffer`.
|
||||||
|
* @return The number of entries stored. In case the logger is shutting down, `QUEUE_CLOSED` is
|
||||||
|
* returned, no entries are stored and the logger should shutdown.
|
||||||
|
*/
|
||||||
|
size_t GetEntries(Entry* out_buffer, size_t buffer_len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initiates a shutdown of the logger. This will indicate to log output clients that they
|
||||||
|
* should shutdown.
|
||||||
|
*/
|
||||||
|
void Close() { ring_buffer.Close(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if Close() has already been called on the Logger.
|
||||||
|
*/
|
||||||
|
bool IsClosed() const { return ring_buffer.IsClosed(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Buffer ring_buffer;
|
||||||
|
std::vector<ClassInfo> all_classes;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Creates a log entry by formatting the given source location, and message.
|
||||||
|
Entry CreateEntry(Class log_class, Level log_level,
|
||||||
|
const char* filename, unsigned int line_nr, const char* function,
|
||||||
|
const char* format, va_list args);
|
||||||
|
/// Initializes the default Logger.
|
||||||
|
std::shared_ptr<Logger> InitGlobalLogger();
|
||||||
|
|
||||||
|
}
|
132
src/common/logging/filter.cpp
Normal file
132
src/common/logging/filter.cpp
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
// Copyright 2014 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include "common/logging/filter.h"
|
||||||
|
#include "common/logging/backend.h"
|
||||||
|
#include "common/string_util.h"
|
||||||
|
|
||||||
|
namespace Log {
|
||||||
|
|
||||||
|
Filter::Filter(Level default_level) {
|
||||||
|
ResetAll(default_level);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Filter::ResetAll(Level level) {
|
||||||
|
class_levels.fill(level);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Filter::SetClassLevel(Class log_class, Level level) {
|
||||||
|
class_levels[static_cast<size_t>(log_class)] = level;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Filter::SetSubclassesLevel(const ClassInfo& log_class, Level level) {
|
||||||
|
const size_t log_class_i = static_cast<size_t>(log_class.log_class);
|
||||||
|
|
||||||
|
const size_t begin = log_class_i + 1;
|
||||||
|
const size_t end = begin + log_class.num_children;
|
||||||
|
for (size_t i = begin; begin < end; ++i) {
|
||||||
|
class_levels[i] = level;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Filter::ParseFilterString(const std::string& filter_str) {
|
||||||
|
auto clause_begin = filter_str.cbegin();
|
||||||
|
while (clause_begin != filter_str.cend()) {
|
||||||
|
auto clause_end = std::find(clause_begin, filter_str.cend(), ' ');
|
||||||
|
|
||||||
|
// If clause isn't empty
|
||||||
|
if (clause_end != clause_begin) {
|
||||||
|
ParseFilterRule(clause_begin, clause_end);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clause_end != filter_str.cend()) {
|
||||||
|
// Skip over the whitespace
|
||||||
|
++clause_end;
|
||||||
|
}
|
||||||
|
clause_begin = clause_end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename It>
|
||||||
|
static Level GetLevelByName(const It begin, const It end) {
|
||||||
|
for (u8 i = 0; i < static_cast<u8>(Level::Count); ++i) {
|
||||||
|
const char* level_name = Logger::GetLevelName(static_cast<Level>(i));
|
||||||
|
if (Common::ComparePartialString(begin, end, level_name)) {
|
||||||
|
return static_cast<Level>(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Level::Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename It>
|
||||||
|
static Class GetClassByName(const It begin, const It end) {
|
||||||
|
for (ClassType i = 0; i < static_cast<ClassType>(Class::Count); ++i) {
|
||||||
|
const char* level_name = Logger::GetLogClassName(static_cast<Class>(i));
|
||||||
|
if (Common::ComparePartialString(begin, end, level_name)) {
|
||||||
|
return static_cast<Class>(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Class::Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename InputIt, typename T>
|
||||||
|
static InputIt find_last(InputIt begin, const InputIt end, const T& value) {
|
||||||
|
auto match = end;
|
||||||
|
while (begin != end) {
|
||||||
|
auto new_match = std::find(begin, end, value);
|
||||||
|
if (new_match != end) {
|
||||||
|
match = new_match;
|
||||||
|
++new_match;
|
||||||
|
}
|
||||||
|
begin = new_match;
|
||||||
|
}
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Filter::ParseFilterRule(const std::string::const_iterator begin,
|
||||||
|
const std::string::const_iterator end) {
|
||||||
|
auto level_separator = std::find(begin, end, ':');
|
||||||
|
if (level_separator == end) {
|
||||||
|
LOG_ERROR(Log, "Invalid log filter. Must specify a log level after `:`: %s",
|
||||||
|
std::string(begin, end).c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Level level = GetLevelByName(level_separator + 1, end);
|
||||||
|
if (level == Level::Count) {
|
||||||
|
LOG_ERROR(Log, "Unknown log level in filter: %s", std::string(begin, end).c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Common::ComparePartialString(begin, level_separator, "*")) {
|
||||||
|
ResetAll(level);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto class_name_end = find_last(begin, level_separator, '.');
|
||||||
|
if (class_name_end != level_separator &&
|
||||||
|
!Common::ComparePartialString(class_name_end + 1, level_separator, "*")) {
|
||||||
|
class_name_end = level_separator;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Class log_class = GetClassByName(begin, class_name_end);
|
||||||
|
if (log_class == Class::Count) {
|
||||||
|
LOG_ERROR(Log, "Unknown log class in filter: %s", std::string(begin, end).c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (class_name_end == level_separator) {
|
||||||
|
SetClassLevel(log_class, level);
|
||||||
|
}
|
||||||
|
SetSubclassesLevel(log_class, level);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Filter::CheckMessage(Class log_class, Level level) const {
|
||||||
|
return static_cast<u8>(level) >= static_cast<u8>(class_levels[static_cast<size_t>(log_class)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
63
src/common/logging/filter.h
Normal file
63
src/common/logging/filter.h
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
// Copyright 2014 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "common/logging/log.h"
|
||||||
|
|
||||||
|
namespace Log {
|
||||||
|
|
||||||
|
struct ClassInfo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements a log message filter which allows different log classes to have different minimum
|
||||||
|
* severity levels. The filter can be changed at runtime and can be parsed from a string to allow
|
||||||
|
* editing via the interface or loading from a configuration file.
|
||||||
|
*/
|
||||||
|
class Filter {
|
||||||
|
public:
|
||||||
|
/// Initializes the filter with all classes having `default_level` as the minimum level.
|
||||||
|
Filter(Level default_level);
|
||||||
|
|
||||||
|
/// Resets the filter so that all classes have `level` as the minimum displayed level.
|
||||||
|
void ResetAll(Level level);
|
||||||
|
/// Sets the minimum level of `log_class` (and not of its subclasses) to `level`.
|
||||||
|
void SetClassLevel(Class log_class, Level level);
|
||||||
|
/**
|
||||||
|
* Sets the minimum level of all of `log_class` subclasses to `level`. The level of `log_class`
|
||||||
|
* itself is not changed.
|
||||||
|
*/
|
||||||
|
void SetSubclassesLevel(const ClassInfo& log_class, Level level);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a filter string and applies it to this filter.
|
||||||
|
*
|
||||||
|
* A filter string consists of a space-separated list of filter rules, each of the format
|
||||||
|
* `<class>:<level>`. `<class>` is a log class name, with subclasses separated using periods.
|
||||||
|
* A rule for a given class also affects all of its subclasses. `*` wildcards are allowed and
|
||||||
|
* can be used to apply a rule to all classes or to all subclasses of a class without affecting
|
||||||
|
* the parent class. `<level>` a severity level name which will be set as the minimum logging
|
||||||
|
* level of the matched classes. Rules are applied left to right, with each rule overriding
|
||||||
|
* previous ones in the sequence.
|
||||||
|
*
|
||||||
|
* A few examples of filter rules:
|
||||||
|
* - `*:Info` -- Resets the level of all classes to Info.
|
||||||
|
* - `Service:Info` -- Sets the level of Service and all subclasses (Service.FS, Service.APT,
|
||||||
|
* etc.) to Info.
|
||||||
|
* - `Service.*:Debug` -- Sets the level of all Service subclasses to Debug, while leaving the
|
||||||
|
* level of Service unchanged.
|
||||||
|
* - `Service.FS:Trace` -- Sets the level of the Service.FS class to Trace.
|
||||||
|
*/
|
||||||
|
void ParseFilterString(const std::string& filter_str);
|
||||||
|
bool ParseFilterRule(const std::string::const_iterator start, const std::string::const_iterator end);
|
||||||
|
|
||||||
|
/// Matches class/level combination against the filter, returning true if it passed.
|
||||||
|
bool CheckMessage(Class log_class, Level level) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::array<Level, (size_t)Class::Count> class_levels;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
115
src/common/logging/log.h
Normal file
115
src/common/logging/log.h
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
// Copyright 2014 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <chrono>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "common/common_types.h"
|
||||||
|
|
||||||
|
namespace Log {
|
||||||
|
|
||||||
|
/// Specifies the severity or level of detail of the log message.
|
||||||
|
enum class Level : u8 {
|
||||||
|
Trace, ///< Extremely detailed and repetitive debugging information that is likely to
|
||||||
|
/// pollute logs.
|
||||||
|
Debug, ///< Less detailed debugging information.
|
||||||
|
Info, ///< Status information from important points during execution.
|
||||||
|
Warning, ///< Minor or potential problems found during execution of a task.
|
||||||
|
Error, ///< Major problems found during execution of a task that prevent it from being
|
||||||
|
/// completed.
|
||||||
|
Critical, ///< Major problems during execution that threathen the stability of the entire
|
||||||
|
/// application.
|
||||||
|
|
||||||
|
Count ///< Total number of logging levels
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef u8 ClassType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the sub-system that generated the log message.
|
||||||
|
*
|
||||||
|
* @note If you add a new entry here, also add a corresponding one to `ALL_LOG_CLASSES` in log.cpp.
|
||||||
|
*/
|
||||||
|
enum class Class : ClassType {
|
||||||
|
Log, ///< Messages about the log system itself
|
||||||
|
Common, ///< Library routines
|
||||||
|
Common_Filesystem, ///< Filesystem interface library
|
||||||
|
Common_Memory, ///< Memory mapping and management functions
|
||||||
|
Core, ///< LLE emulation core
|
||||||
|
Core_ARM11, ///< ARM11 CPU core
|
||||||
|
Config, ///< Emulator configuration (including commandline)
|
||||||
|
Debug, ///< Debugging tools
|
||||||
|
Debug_Emulated, ///< Debug messages from the emulated programs
|
||||||
|
Debug_GPU, ///< GPU debugging tools
|
||||||
|
Debug_Breakpoint, ///< Logging breakpoints and watchpoints
|
||||||
|
Kernel, ///< The HLE implementation of the CTR kernel
|
||||||
|
Kernel_SVC, ///< Kernel system calls
|
||||||
|
Service, ///< HLE implementation of system services. Each major service
|
||||||
|
/// should have its own subclass.
|
||||||
|
Service_SRV, ///< The SRV (Service Directory) implementation
|
||||||
|
Service_FS, ///< The FS (Filesystem) service implementation
|
||||||
|
Service_APT, ///< The APT (Applets) service
|
||||||
|
Service_GSP, ///< The GSP (GPU control) service
|
||||||
|
Service_AC, ///< The AC (WiFi status) service
|
||||||
|
Service_PTM, ///< The PTM (Power status & misc.) service
|
||||||
|
Service_CFG, ///< The CFG (Configuration) service
|
||||||
|
Service_DSP, ///< The DSP (DSP control) service
|
||||||
|
Service_HID, ///< The HID (User input) service
|
||||||
|
HW, ///< Low-level hardware emulation
|
||||||
|
HW_Memory, ///< Memory-map and address translation
|
||||||
|
HW_GPU, ///< GPU control emulation
|
||||||
|
Frontend, ///< Emulator UI
|
||||||
|
Render, ///< Emulator video output and hardware acceleration
|
||||||
|
Render_Software, ///< Software renderer backend
|
||||||
|
Render_OpenGL, ///< OpenGL backend
|
||||||
|
Loader, ///< ROM loader
|
||||||
|
|
||||||
|
Count ///< Total number of logging classes
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Level below which messages are simply discarded without buffering regardless of the display
|
||||||
|
* settings.
|
||||||
|
*/
|
||||||
|
const Level MINIMUM_LEVEL =
|
||||||
|
#ifdef _DEBUG
|
||||||
|
Level::Trace;
|
||||||
|
#else
|
||||||
|
Level::Debug;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logs a message to the global logger. This proxy exists to avoid exposing the details of the
|
||||||
|
* Logger class, including the ConcurrentRingBuffer template, to all files that desire to log
|
||||||
|
* messages, reducing unecessary recompilations.
|
||||||
|
*/
|
||||||
|
void LogMessage(Class log_class, Level log_level,
|
||||||
|
const char* filename, unsigned int line_nr, const char* function,
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
_Printf_format_string_
|
||||||
|
#endif
|
||||||
|
const char* format, ...)
|
||||||
|
#ifdef __GNUC__
|
||||||
|
__attribute__((format(printf, 6, 7)))
|
||||||
|
#endif
|
||||||
|
;
|
||||||
|
|
||||||
|
} // namespace Log
|
||||||
|
|
||||||
|
#define LOG_GENERIC(log_class, log_level, ...) \
|
||||||
|
do { \
|
||||||
|
if (::Log::Level::log_level >= ::Log::MINIMUM_LEVEL) \
|
||||||
|
::Log::LogMessage(::Log::Class::log_class, ::Log::Level::log_level, \
|
||||||
|
__FILE__, __LINE__, __func__, __VA_ARGS__); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define LOG_TRACE( log_class, ...) LOG_GENERIC(log_class, Trace, __VA_ARGS__)
|
||||||
|
#define LOG_DEBUG( log_class, ...) LOG_GENERIC(log_class, Debug, __VA_ARGS__)
|
||||||
|
#define LOG_INFO( log_class, ...) LOG_GENERIC(log_class, Info, __VA_ARGS__)
|
||||||
|
#define LOG_WARNING( log_class, ...) LOG_GENERIC(log_class, Warning, __VA_ARGS__)
|
||||||
|
#define LOG_ERROR( log_class, ...) LOG_GENERIC(log_class, Error, __VA_ARGS__)
|
||||||
|
#define LOG_CRITICAL(log_class, ...) LOG_GENERIC(log_class, Critical, __VA_ARGS__)
|
136
src/common/logging/text_formatter.cpp
Normal file
136
src/common/logging/text_formatter.cpp
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
// Copyright 2014 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
# define WIN32_LEAN_AND_MEAN
|
||||||
|
# include <Windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "common/logging/backend.h"
|
||||||
|
#include "common/logging/filter.h"
|
||||||
|
#include "common/logging/log.h"
|
||||||
|
#include "common/logging/text_formatter.h"
|
||||||
|
|
||||||
|
#include "common/string_util.h"
|
||||||
|
|
||||||
|
namespace Log {
|
||||||
|
|
||||||
|
// TODO(bunnei): This should be moved to a generic path manipulation library
|
||||||
|
const char* TrimSourcePath(const char* path, const char* root) {
|
||||||
|
const char* p = path;
|
||||||
|
|
||||||
|
while (*p != '\0') {
|
||||||
|
const char* next_slash = p;
|
||||||
|
while (*next_slash != '\0' && *next_slash != '/' && *next_slash != '\\') {
|
||||||
|
++next_slash;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_src = Common::ComparePartialString(p, next_slash, root);
|
||||||
|
p = next_slash;
|
||||||
|
|
||||||
|
if (*p != '\0') {
|
||||||
|
++p;
|
||||||
|
}
|
||||||
|
if (is_src) {
|
||||||
|
path = p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FormatLogMessage(const Entry& entry, char* out_text, size_t text_len) {
|
||||||
|
unsigned int time_seconds = static_cast<unsigned int>(entry.timestamp.count() / 1000000);
|
||||||
|
unsigned int time_fractional = static_cast<unsigned int>(entry.timestamp.count() % 1000000);
|
||||||
|
|
||||||
|
const char* class_name = Logger::GetLogClassName(entry.log_class);
|
||||||
|
const char* level_name = Logger::GetLevelName(entry.log_level);
|
||||||
|
|
||||||
|
snprintf(out_text, text_len, "[%4u.%06u] %s <%s> %s: %s",
|
||||||
|
time_seconds, time_fractional, class_name, level_name,
|
||||||
|
TrimSourcePath(entry.location.c_str()), entry.message.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintMessage(const Entry& entry) {
|
||||||
|
std::array<char, 4 * 1024> format_buffer;
|
||||||
|
FormatLogMessage(entry, format_buffer.data(), format_buffer.size());
|
||||||
|
fputs(format_buffer.data(), stderr);
|
||||||
|
fputc('\n', stderr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintColoredMessage(const Entry& entry) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
static HANDLE console_handle = GetStdHandle(STD_ERROR_HANDLE);
|
||||||
|
|
||||||
|
CONSOLE_SCREEN_BUFFER_INFO original_info = {0};
|
||||||
|
GetConsoleScreenBufferInfo(console_handle, &original_info);
|
||||||
|
|
||||||
|
WORD color = 0;
|
||||||
|
switch (entry.log_level) {
|
||||||
|
case Level::Trace: // Grey
|
||||||
|
color = FOREGROUND_INTENSITY; break;
|
||||||
|
case Level::Debug: // Cyan
|
||||||
|
color = FOREGROUND_GREEN | FOREGROUND_BLUE; break;
|
||||||
|
case Level::Info: // Bright gray
|
||||||
|
color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; break;
|
||||||
|
case Level::Warning: // Bright yellow
|
||||||
|
color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY; break;
|
||||||
|
case Level::Error: // Bright red
|
||||||
|
color = FOREGROUND_RED | FOREGROUND_INTENSITY; break;
|
||||||
|
case Level::Critical: // Bright magenta
|
||||||
|
color = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetConsoleTextAttribute(console_handle, color);
|
||||||
|
#else
|
||||||
|
# define ESC "\x1b"
|
||||||
|
const char* color = "";
|
||||||
|
switch (entry.log_level) {
|
||||||
|
case Level::Trace: // Grey
|
||||||
|
color = ESC "[1;30m"; break;
|
||||||
|
case Level::Debug: // Cyan
|
||||||
|
color = ESC "[0;36m"; break;
|
||||||
|
case Level::Info: // Bright gray
|
||||||
|
color = ESC "[0;37m"; break;
|
||||||
|
case Level::Warning: // Bright yellow
|
||||||
|
color = ESC "[1;33m"; break;
|
||||||
|
case Level::Error: // Bright red
|
||||||
|
color = ESC "[1;31m"; break;
|
||||||
|
case Level::Critical: // Bright magenta
|
||||||
|
color = ESC "[1;35m"; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
fputs(color, stderr);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
PrintMessage(entry);
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
SetConsoleTextAttribute(console_handle, original_info.wAttributes);
|
||||||
|
#else
|
||||||
|
fputs(ESC "[0m", stderr);
|
||||||
|
# undef ESC
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextLoggingLoop(std::shared_ptr<Logger> logger, const Filter* filter) {
|
||||||
|
std::array<Entry, 256> entry_buffer;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
size_t num_entries = logger->GetEntries(entry_buffer.data(), entry_buffer.size());
|
||||||
|
if (num_entries == Logger::QUEUE_CLOSED) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i < num_entries; ++i) {
|
||||||
|
const Entry& entry = entry_buffer[i];
|
||||||
|
if (filter->CheckMessage(entry.log_class, entry.log_level)) {
|
||||||
|
PrintColoredMessage(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
41
src/common/logging/text_formatter.h
Normal file
41
src/common/logging/text_formatter.h
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
// Copyright 2014 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace Log {
|
||||||
|
|
||||||
|
class Logger;
|
||||||
|
struct Entry;
|
||||||
|
class Filter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to trim an arbitrary prefix from `path`, leaving only the part starting at `root`. It's
|
||||||
|
* intended to be used to strip a system-specific build directory from the `__FILE__` macro,
|
||||||
|
* leaving only the path relative to the sources root.
|
||||||
|
*
|
||||||
|
* @param path The input file path as a null-terminated string
|
||||||
|
* @param root The name of the root source directory as a null-terminated string. Path up to and
|
||||||
|
* including the last occurence of this name will be stripped
|
||||||
|
* @return A pointer to the same string passed as `path`, but starting at the trimmed portion
|
||||||
|
*/
|
||||||
|
const char* TrimSourcePath(const char* path, const char* root = "src");
|
||||||
|
|
||||||
|
/// Formats a log entry into the provided text buffer.
|
||||||
|
void FormatLogMessage(const Entry& entry, char* out_text, size_t text_len);
|
||||||
|
/// Formats and prints a log entry to stderr.
|
||||||
|
void PrintMessage(const Entry& entry);
|
||||||
|
/// Prints the same message as `PrintMessage`, but colored acoording to the severity level.
|
||||||
|
void PrintColoredMessage(const Entry& entry);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logging loop that repeatedly reads messages from the provided logger and prints them to the
|
||||||
|
* console. It is the baseline barebones log outputter.
|
||||||
|
*/
|
||||||
|
void TextLoggingLoop(std::shared_ptr<Logger> logger, const Filter* filter);
|
||||||
|
|
||||||
|
}
|
16
src/common/make_unique.h
Normal file
16
src/common/make_unique.h
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
// Copyright 2014 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace Common {
|
||||||
|
|
||||||
|
template <typename T, typename... Args>
|
||||||
|
std::unique_ptr<T> make_unique(Args&&... args) {
|
||||||
|
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
|
@ -1,5 +1,5 @@
|
||||||
// Copyright 2013 Dolphin Emulator Project
|
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
||||||
// Licensed under GPLv2
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Copyright 2013 Dolphin Emulator Project
|
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
||||||
// Licensed under GPLv2
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef IOS
|
#ifdef IOS
|
||||||
void* globalbase = NULL;
|
void* globalbase = nullptr;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef ANDROID
|
#ifdef ANDROID
|
||||||
|
@ -71,7 +71,7 @@ int ashmem_create_region(const char *name, size_t size)
|
||||||
return fd;
|
return fd;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
ERROR_LOG(MEMMAP, "NASTY ASHMEM ERROR: ret = %08x", ret);
|
LOG_ERROR(Common_Memory, "NASTY ASHMEM ERROR: ret = %08x", ret);
|
||||||
close(fd);
|
close(fd);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -121,7 +121,7 @@ void MemArena::GrabLowMemSpace(size_t size)
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#ifndef _XBOX
|
#ifndef _XBOX
|
||||||
hMemoryMapping = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, (DWORD)(size), NULL);
|
hMemoryMapping = CreateFileMapping(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, (DWORD)(size), nullptr);
|
||||||
GetSystemInfo(&sysInfo);
|
GetSystemInfo(&sysInfo);
|
||||||
#endif
|
#endif
|
||||||
#elif defined(ANDROID)
|
#elif defined(ANDROID)
|
||||||
|
@ -130,7 +130,7 @@ void MemArena::GrabLowMemSpace(size_t size)
|
||||||
// Note that it appears that ashmem is pinned by default, so no need to pin.
|
// Note that it appears that ashmem is pinned by default, so no need to pin.
|
||||||
if (fd < 0)
|
if (fd < 0)
|
||||||
{
|
{
|
||||||
ERROR_LOG(MEMMAP, "Failed to grab ashmem space of size: %08x errno: %d", (int)size, (int)(errno));
|
LOG_ERROR(Common_Memory, "Failed to grab ashmem space of size: %08x errno: %d", (int)size, (int)(errno));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
@ -148,12 +148,12 @@ void MemArena::GrabLowMemSpace(size_t size)
|
||||||
}
|
}
|
||||||
else if (errno != EEXIST)
|
else if (errno != EEXIST)
|
||||||
{
|
{
|
||||||
ERROR_LOG(MEMMAP, "shm_open failed: %s", strerror(errno));
|
LOG_ERROR(Common_Memory, "shm_open failed: %s", strerror(errno));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ftruncate(fd, size) < 0)
|
if (ftruncate(fd, size) < 0)
|
||||||
ERROR_LOG(MEMMAP, "Failed to allocate low memory space");
|
LOG_ERROR(Common_Memory, "Failed to allocate low memory space");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,7 +178,7 @@ void *MemArena::CreateView(s64 offset, size_t size, void *base)
|
||||||
#ifdef _XBOX
|
#ifdef _XBOX
|
||||||
size = roundup(size);
|
size = roundup(size);
|
||||||
// use 64kb pages
|
// use 64kb pages
|
||||||
void * ptr = VirtualAlloc(NULL, size, MEM_COMMIT | MEM_LARGE_PAGES, PAGE_READWRITE);
|
void * ptr = VirtualAlloc(nullptr, size, MEM_COMMIT | MEM_LARGE_PAGES, PAGE_READWRITE);
|
||||||
return ptr;
|
return ptr;
|
||||||
#else
|
#else
|
||||||
size = roundup(size);
|
size = roundup(size);
|
||||||
|
@ -197,7 +197,7 @@ void *MemArena::CreateView(s64 offset, size_t size, void *base)
|
||||||
|
|
||||||
if (retval == MAP_FAILED)
|
if (retval == MAP_FAILED)
|
||||||
{
|
{
|
||||||
NOTICE_LOG(MEMMAP, "mmap failed");
|
LOG_ERROR(Common_Memory, "mmap failed");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return retval;
|
return retval;
|
||||||
|
@ -243,8 +243,8 @@ u8* MemArena::Find4GBBase()
|
||||||
return base;
|
return base;
|
||||||
#else
|
#else
|
||||||
#ifdef IOS
|
#ifdef IOS
|
||||||
void* base = NULL;
|
void* base = nullptr;
|
||||||
if (globalbase == NULL){
|
if (globalbase == nullptr){
|
||||||
base = mmap(0, 0x08000000, PROT_READ | PROT_WRITE,
|
base = mmap(0, 0x08000000, PROT_READ | PROT_WRITE,
|
||||||
MAP_ANON | MAP_SHARED, -1, 0);
|
MAP_ANON | MAP_SHARED, -1, 0);
|
||||||
if (base == MAP_FAILED) {
|
if (base == MAP_FAILED) {
|
||||||
|
@ -357,7 +357,7 @@ bail:
|
||||||
if (views[j].out_ptr_low && *views[j].out_ptr_low)
|
if (views[j].out_ptr_low && *views[j].out_ptr_low)
|
||||||
{
|
{
|
||||||
arena->ReleaseView(*views[j].out_ptr_low, views[j].size);
|
arena->ReleaseView(*views[j].out_ptr_low, views[j].size);
|
||||||
*views[j].out_ptr_low = NULL;
|
*views[j].out_ptr_low = nullptr;
|
||||||
}
|
}
|
||||||
if (*views[j].out_ptr)
|
if (*views[j].out_ptr)
|
||||||
{
|
{
|
||||||
|
@ -369,7 +369,7 @@ bail:
|
||||||
arena->ReleaseView(*views[j].out_ptr, views[j].size);
|
arena->ReleaseView(*views[j].out_ptr, views[j].size);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
*views[j].out_ptr = NULL;
|
*views[j].out_ptr = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -415,7 +415,7 @@ u8 *MemoryMap_Setup(const MemoryView *views, int num_views, u32 flags, MemArena
|
||||||
#elif defined(_WIN32)
|
#elif defined(_WIN32)
|
||||||
// Try a whole range of possible bases. Return once we got a valid one.
|
// Try a whole range of possible bases. Return once we got a valid one.
|
||||||
u32 max_base_addr = 0x7FFF0000 - 0x10000000;
|
u32 max_base_addr = 0x7FFF0000 - 0x10000000;
|
||||||
u8 *base = NULL;
|
u8 *base = nullptr;
|
||||||
|
|
||||||
for (u32 base_addr = 0x01000000; base_addr < max_base_addr; base_addr += 0x400000)
|
for (u32 base_addr = 0x01000000; base_addr < max_base_addr; base_addr += 0x400000)
|
||||||
{
|
{
|
||||||
|
@ -423,7 +423,7 @@ u8 *MemoryMap_Setup(const MemoryView *views, int num_views, u32 flags, MemArena
|
||||||
base = (u8 *)base_addr;
|
base = (u8 *)base_addr;
|
||||||
if (Memory_TryBase(base, views, num_views, flags, arena))
|
if (Memory_TryBase(base, views, num_views, flags, arena))
|
||||||
{
|
{
|
||||||
INFO_LOG(MEMMAP, "Found valid memory base at %p after %i tries.", base, base_attempts);
|
LOG_DEBUG(Common_Memory, "Found valid memory base at %p after %i tries.", base, base_attempts);
|
||||||
base_attempts = 0;
|
base_attempts = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -442,7 +442,7 @@ u8 *MemoryMap_Setup(const MemoryView *views, int num_views, u32 flags, MemArena
|
||||||
u8 *base = MemArena::Find4GBBase();
|
u8 *base = MemArena::Find4GBBase();
|
||||||
if (!Memory_TryBase(base, views, num_views, flags, arena))
|
if (!Memory_TryBase(base, views, num_views, flags, arena))
|
||||||
{
|
{
|
||||||
ERROR_LOG(MEMMAP, "MemoryMap_Setup: Failed finding a memory base.");
|
LOG_ERROR(Common_Memory, "MemoryMap_Setup: Failed finding a memory base.");
|
||||||
PanicAlert("MemoryMap_Setup: Failed finding a memory base.");
|
PanicAlert("MemoryMap_Setup: Failed finding a memory base.");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -463,8 +463,8 @@ void MemoryMap_Shutdown(const MemoryView *views, int num_views, u32 flags, MemAr
|
||||||
arena->ReleaseView(*views[i].out_ptr_low, views[i].size);
|
arena->ReleaseView(*views[i].out_ptr_low, views[i].size);
|
||||||
if (*views[i].out_ptr && (views[i].out_ptr_low && *views[i].out_ptr != *views[i].out_ptr_low))
|
if (*views[i].out_ptr && (views[i].out_ptr_low && *views[i].out_ptr != *views[i].out_ptr_low))
|
||||||
arena->ReleaseView(*views[i].out_ptr, views[i].size);
|
arena->ReleaseView(*views[i].out_ptr, views[i].size);
|
||||||
*views[i].out_ptr = NULL;
|
*views[i].out_ptr = nullptr;
|
||||||
if (views[i].out_ptr_low)
|
if (views[i].out_ptr_low)
|
||||||
*views[i].out_ptr_low = NULL;
|
*views[i].out_ptr_low = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Copyright 2013 Dolphin Emulator Project
|
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
||||||
// Licensed under GPLv2
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
|
||||||
|
@ -93,7 +93,7 @@ void* AllocateMemoryPages(size_t size)
|
||||||
// printf("Mapped memory at %p (size %ld)\n", ptr,
|
// printf("Mapped memory at %p (size %ld)\n", ptr,
|
||||||
// (unsigned long)size);
|
// (unsigned long)size);
|
||||||
|
|
||||||
if (ptr == NULL)
|
if (ptr == nullptr)
|
||||||
PanicAlert("Failed to allocate raw memory");
|
PanicAlert("Failed to allocate raw memory");
|
||||||
|
|
||||||
return ptr;
|
return ptr;
|
||||||
|
@ -104,19 +104,19 @@ void* AllocateAlignedMemory(size_t size,size_t alignment)
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
void* ptr = _aligned_malloc(size,alignment);
|
void* ptr = _aligned_malloc(size,alignment);
|
||||||
#else
|
#else
|
||||||
void* ptr = NULL;
|
void* ptr = nullptr;
|
||||||
#ifdef ANDROID
|
#ifdef ANDROID
|
||||||
ptr = memalign(alignment, size);
|
ptr = memalign(alignment, size);
|
||||||
#else
|
#else
|
||||||
if (posix_memalign(&ptr, alignment, size) != 0)
|
if (posix_memalign(&ptr, alignment, size) != 0)
|
||||||
ERROR_LOG(MEMMAP, "Failed to allocate aligned memory");
|
LOG_ERROR(Common_Memory, "Failed to allocate aligned memory");
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// printf("Mapped memory at %p (size %ld)\n", ptr,
|
// printf("Mapped memory at %p (size %ld)\n", ptr,
|
||||||
// (unsigned long)size);
|
// (unsigned long)size);
|
||||||
|
|
||||||
if (ptr == NULL)
|
if (ptr == nullptr)
|
||||||
PanicAlert("Failed to allocate aligned memory");
|
PanicAlert("Failed to allocate aligned memory");
|
||||||
|
|
||||||
return ptr;
|
return ptr;
|
||||||
|
@ -130,7 +130,7 @@ void FreeMemoryPages(void* ptr, size_t size)
|
||||||
|
|
||||||
if (!VirtualFree(ptr, 0, MEM_RELEASE))
|
if (!VirtualFree(ptr, 0, MEM_RELEASE))
|
||||||
PanicAlert("FreeMemoryPages failed!\n%s", GetLastErrorMsg());
|
PanicAlert("FreeMemoryPages failed!\n%s", GetLastErrorMsg());
|
||||||
ptr = NULL; // Is this our responsibility?
|
ptr = nullptr; // Is this our responsibility?
|
||||||
|
|
||||||
#else
|
#else
|
||||||
munmap(ptr, size);
|
munmap(ptr, size);
|
||||||
|
@ -184,7 +184,7 @@ std::string MemUsage()
|
||||||
// Print information about the memory usage of the process.
|
// Print information about the memory usage of the process.
|
||||||
|
|
||||||
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processID);
|
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processID);
|
||||||
if (NULL == hProcess) return "MemUsage Error";
|
if (nullptr == hProcess) return "MemUsage Error";
|
||||||
|
|
||||||
if (GetProcessMemoryInfo(hProcess, &pmc, sizeof(pmc)))
|
if (GetProcessMemoryInfo(hProcess, &pmc, sizeof(pmc)))
|
||||||
Ret = Common::StringFromFormat("%s K", Common::ThousandSeparate(pmc.WorkingSetSize / 1024, 7).c_str());
|
Ret = Common::StringFromFormat("%s K", Common::ThousandSeparate(pmc.WorkingSetSize / 1024, 7).c_str());
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Copyright 2013 Dolphin Emulator Project
|
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
||||||
// Licensed under GPLv2
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Copyright 2013 Dolphin Emulator Project
|
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
||||||
// Licensed under GPLv2
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#include "common/common.h"
|
#include "common/common.h"
|
||||||
|
@ -23,9 +23,9 @@ const char* GetLastErrorMsg()
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
static __declspec(thread) char err_str[buff_size] = {};
|
static __declspec(thread) char err_str[buff_size] = {};
|
||||||
|
|
||||||
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
|
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, GetLastError(),
|
||||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||||
err_str, buff_size, NULL);
|
err_str, buff_size, nullptr);
|
||||||
#else
|
#else
|
||||||
static __thread char err_str[buff_size] = {};
|
static __thread char err_str[buff_size] = {};
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Copyright 2013 Dolphin Emulator Project
|
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
||||||
// Licensed under GPLv2
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
@ -75,7 +75,7 @@ bool MsgAlert(bool yes_no, int Style, const char* format, ...)
|
||||||
Common::CharArrayFromFormatV(buffer, sizeof(buffer)-1, str_translator(format).c_str(), args);
|
Common::CharArrayFromFormatV(buffer, sizeof(buffer)-1, str_translator(format).c_str(), args);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
|
|
||||||
ERROR_LOG(MASTER_LOG, "%s: %s", caption.c_str(), buffer);
|
LOG_INFO(Common, "%s: %s", caption.c_str(), buffer);
|
||||||
|
|
||||||
// Don't ignore questions, especially AskYesNo, PanicYesNo could be ignored
|
// Don't ignore questions, especially AskYesNo, PanicYesNo could be ignored
|
||||||
if (msg_handler && (AlertEnabled || Style == QUESTION || Style == CRITICAL))
|
if (msg_handler && (AlertEnabled || Style == QUESTION || Style == CRITICAL))
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Copyright 2013 Dolphin Emulator Project
|
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
||||||
// Licensed under GPLv2
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
|
@ -80,7 +80,7 @@
|
||||||
inline struct tm* localtime_r(const time_t *clock, struct tm *result) {
|
inline struct tm* localtime_r(const time_t *clock, struct tm *result) {
|
||||||
if (localtime_s(result, clock) == 0)
|
if (localtime_s(result, clock) == 0)
|
||||||
return result;
|
return result;
|
||||||
return NULL;
|
return nullptr;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Copyright 2014 Citra Emulator Project
|
// Copyright 2014 Citra Emulator Project
|
||||||
// Licensed under GPLv2
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
37
src/common/scope_exit.h
Normal file
37
src/common/scope_exit.h
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
// Copyright 2014 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
template <typename Func>
|
||||||
|
struct ScopeExitHelper {
|
||||||
|
explicit ScopeExitHelper(Func&& func) : func(std::move(func)) {}
|
||||||
|
~ScopeExitHelper() { func(); }
|
||||||
|
|
||||||
|
Func func;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Func>
|
||||||
|
ScopeExitHelper<Func> ScopeExit(Func&& func) { return ScopeExitHelper<Func>(std::move(func)); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This macro allows you to conveniently specify a block of code that will run on scope exit. Handy
|
||||||
|
* for doing ad-hoc clean-up tasks in a function with multiple returns.
|
||||||
|
*
|
||||||
|
* Example usage:
|
||||||
|
* \code
|
||||||
|
* const int saved_val = g_foo;
|
||||||
|
* g_foo = 55;
|
||||||
|
* SCOPE_EXIT({ g_foo = saved_val; });
|
||||||
|
*
|
||||||
|
* if (Bar()) {
|
||||||
|
* return 0;
|
||||||
|
* } else {
|
||||||
|
* return 20;
|
||||||
|
* }
|
||||||
|
* \endcode
|
||||||
|
*/
|
||||||
|
#define SCOPE_EXIT(body) auto scope_exit_helper_##__LINE__ = detail::ScopeExit([&]() body)
|
|
@ -1,8 +1,8 @@
|
||||||
// Copyright 2013 Dolphin Emulator Project
|
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
||||||
// Licensed under GPLv2
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#include <algorithm>
|
#include <boost/range/algorithm.hpp>
|
||||||
|
|
||||||
#include "common/common.h"
|
#include "common/common.h"
|
||||||
#include "common/string_util.h"
|
#include "common/string_util.h"
|
||||||
|
@ -18,20 +18,20 @@ namespace Common {
|
||||||
|
|
||||||
/// Make a string lowercase
|
/// Make a string lowercase
|
||||||
std::string ToLower(std::string str) {
|
std::string ToLower(std::string str) {
|
||||||
std::transform(str.begin(), str.end(), str.begin(), ::tolower);
|
boost::transform(str, str.begin(), ::tolower);
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Make a string uppercase
|
/// Make a string uppercase
|
||||||
std::string ToUpper(std::string str) {
|
std::string ToUpper(std::string str) {
|
||||||
std::transform(str.begin(), str.end(), str.begin(), ::toupper);
|
boost::transform(str, str.begin(), ::toupper);
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
// faster than sscanf
|
// faster than sscanf
|
||||||
bool AsciiToHex(const char* _szValue, u32& result)
|
bool AsciiToHex(const char* _szValue, u32& result)
|
||||||
{
|
{
|
||||||
char *endptr = NULL;
|
char *endptr = nullptr;
|
||||||
const u32 value = strtoul(_szValue, &endptr, 16);
|
const u32 value = strtoul(_szValue, &endptr, 16);
|
||||||
|
|
||||||
if (!endptr || *endptr)
|
if (!endptr || *endptr)
|
||||||
|
@ -69,7 +69,7 @@ bool CharArrayFromFormatV(char* out, int outsize, const char* format, va_list ar
|
||||||
// will be present in the middle of a multibyte sequence.
|
// will be present in the middle of a multibyte sequence.
|
||||||
//
|
//
|
||||||
// This is why we lookup an ANSI (cp1252) locale here and use _vsnprintf_l.
|
// This is why we lookup an ANSI (cp1252) locale here and use _vsnprintf_l.
|
||||||
static locale_t c_locale = NULL;
|
static locale_t c_locale = nullptr;
|
||||||
if (!c_locale)
|
if (!c_locale)
|
||||||
c_locale = _create_locale(LC_ALL, ".1252");
|
c_locale = _create_locale(LC_ALL, ".1252");
|
||||||
writtenCount = _vsnprintf_l(out, outsize, format, c_locale, args);
|
writtenCount = _vsnprintf_l(out, outsize, format, c_locale, args);
|
||||||
|
@ -92,7 +92,7 @@ bool CharArrayFromFormatV(char* out, int outsize, const char* format, va_list ar
|
||||||
std::string StringFromFormat(const char* format, ...)
|
std::string StringFromFormat(const char* format, ...)
|
||||||
{
|
{
|
||||||
va_list args;
|
va_list args;
|
||||||
char *buf = NULL;
|
char *buf = nullptr;
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
int required = 0;
|
int required = 0;
|
||||||
|
|
||||||
|
@ -107,7 +107,7 @@ std::string StringFromFormat(const char* format, ...)
|
||||||
#else
|
#else
|
||||||
va_start(args, format);
|
va_start(args, format);
|
||||||
if (vasprintf(&buf, format, args) < 0)
|
if (vasprintf(&buf, format, args) < 0)
|
||||||
ERROR_LOG(COMMON, "Unable to allocate memory for string");
|
LOG_ERROR(Common, "Unable to allocate memory for string");
|
||||||
va_end(args);
|
va_end(args);
|
||||||
|
|
||||||
std::string temp = buf;
|
std::string temp = buf;
|
||||||
|
@ -162,7 +162,7 @@ std::string StripQuotes(const std::string& s)
|
||||||
|
|
||||||
bool TryParse(const std::string &str, u32 *const output)
|
bool TryParse(const std::string &str, u32 *const output)
|
||||||
{
|
{
|
||||||
char *endptr = NULL;
|
char *endptr = nullptr;
|
||||||
|
|
||||||
// Reset errno to a value other than ERANGE
|
// Reset errno to a value other than ERANGE
|
||||||
errno = 0;
|
errno = 0;
|
||||||
|
@ -475,7 +475,7 @@ static std::string CodeToUTF8(const char* fromcode, const std::basic_string<T>&
|
||||||
iconv_t const conv_desc = iconv_open("UTF-8", fromcode);
|
iconv_t const conv_desc = iconv_open("UTF-8", fromcode);
|
||||||
if ((iconv_t)(-1) == conv_desc)
|
if ((iconv_t)(-1) == conv_desc)
|
||||||
{
|
{
|
||||||
ERROR_LOG(COMMON, "Iconv initialization failure [%s]: %s", fromcode, strerror(errno));
|
LOG_ERROR(Common, "Iconv initialization failure [%s]: %s", fromcode, strerror(errno));
|
||||||
iconv_close(conv_desc);
|
iconv_close(conv_desc);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -510,7 +510,7 @@ static std::string CodeToUTF8(const char* fromcode, const std::basic_string<T>&
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ERROR_LOG(COMMON, "iconv failure [%s]: %s", fromcode, strerror(errno));
|
LOG_ERROR(Common, "iconv failure [%s]: %s", fromcode, strerror(errno));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -528,10 +528,10 @@ std::u16string UTF8ToUTF16(const std::string& input)
|
||||||
{
|
{
|
||||||
std::u16string result;
|
std::u16string result;
|
||||||
|
|
||||||
iconv_t const conv_desc = iconv_open("UTF-16", "UTF-8");
|
iconv_t const conv_desc = iconv_open("UTF-16LE", "UTF-8");
|
||||||
if ((iconv_t)(-1) == conv_desc)
|
if ((iconv_t)(-1) == conv_desc)
|
||||||
{
|
{
|
||||||
ERROR_LOG(COMMON, "Iconv initialization failure [UTF-8]: %s", strerror(errno));
|
LOG_ERROR(Common, "Iconv initialization failure [UTF-8]: %s", strerror(errno));
|
||||||
iconv_close(conv_desc);
|
iconv_close(conv_desc);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -566,7 +566,7 @@ std::u16string UTF8ToUTF16(const std::string& input)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ERROR_LOG(COMMON, "iconv failure [UTF-8]: %s", strerror(errno));
|
LOG_ERROR(Common, "iconv failure [UTF-8]: %s", strerror(errno));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -582,7 +582,7 @@ std::u16string UTF8ToUTF16(const std::string& input)
|
||||||
|
|
||||||
std::string UTF16ToUTF8(const std::u16string& input)
|
std::string UTF16ToUTF8(const std::u16string& input)
|
||||||
{
|
{
|
||||||
return CodeToUTF8("UTF-16", input);
|
return CodeToUTF8("UTF-16LE", input);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string CP1252ToUTF8(const std::string& input)
|
std::string CP1252ToUTF8(const std::string& input)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Copyright 2013 Dolphin Emulator Project
|
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
||||||
// Licensed under GPLv2
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
@ -115,4 +115,19 @@ inline std::string UTF8ToTStr(const std::string& str)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compares the string defined by the range [`begin`, `end`) to the null-terminated C-string
|
||||||
|
* `other` for equality.
|
||||||
|
*/
|
||||||
|
template <typename InIt>
|
||||||
|
bool ComparePartialString(InIt begin, InIt end, const char* other) {
|
||||||
|
for (; begin != end && *other != '\0'; ++begin, ++other) {
|
||||||
|
if (*begin != *other) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Only return true if both strings finished at the same point
|
||||||
|
return (begin == end) == (*other == '\0');
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Copyright 2014 Citra Emulator Project
|
// Copyright 2014 Citra Emulator Project
|
||||||
// Licensed under GPLv2
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#include "common/symbols.h"
|
#include "common/symbols.h"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Copyright 2014 Citra Emulator Project
|
// Copyright 2014 Citra Emulator Project
|
||||||
// Licensed under GPLv2
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Copyright 2013 Dolphin Emulator Project
|
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
||||||
// Licensed under GPLv2
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#include "common/thread.h"
|
#include "common/thread.h"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Copyright 2013 Dolphin Emulator Project
|
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
||||||
// Licensed under GPLv2
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
@ -21,6 +21,7 @@
|
||||||
//for gettimeofday and struct time(spec|val)
|
//for gettimeofday and struct time(spec|val)
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
|
#include <unistd.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace Common
|
namespace Common
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Copyright 2014 Citra Emulator Project / PPSSPP Project
|
// Copyright 2014 Citra Emulator Project / PPSSPP Project
|
||||||
// Licensed under GPLv2
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
@ -37,7 +37,7 @@ struct ThreadQueueList {
|
||||||
~ThreadQueueList() {
|
~ThreadQueueList() {
|
||||||
for (int i = 0; i < NUM_QUEUES; ++i)
|
for (int i = 0; i < NUM_QUEUES; ++i)
|
||||||
{
|
{
|
||||||
if (queues[i].data != NULL)
|
if (queues[i].data != nullptr)
|
||||||
free(queues[i].data);
|
free(queues[i].data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,7 +46,7 @@ struct ThreadQueueList {
|
||||||
int contains(const IdType uid) {
|
int contains(const IdType uid) {
|
||||||
for (int i = 0; i < NUM_QUEUES; ++i)
|
for (int i = 0; i < NUM_QUEUES; ++i)
|
||||||
{
|
{
|
||||||
if (queues[i].data == NULL)
|
if (queues[i].data == nullptr)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
Queue *cur = &queues[i];
|
Queue *cur = &queues[i];
|
||||||
|
@ -133,7 +133,7 @@ struct ThreadQueueList {
|
||||||
inline void clear() {
|
inline void clear() {
|
||||||
for (int i = 0; i < NUM_QUEUES; ++i)
|
for (int i = 0; i < NUM_QUEUES; ++i)
|
||||||
{
|
{
|
||||||
if (queues[i].data != NULL)
|
if (queues[i].data != nullptr)
|
||||||
free(queues[i].data);
|
free(queues[i].data);
|
||||||
}
|
}
|
||||||
memset(queues, 0, sizeof(queues));
|
memset(queues, 0, sizeof(queues));
|
||||||
|
@ -147,7 +147,7 @@ struct ThreadQueueList {
|
||||||
|
|
||||||
inline void prepare(u32 priority) {
|
inline void prepare(u32 priority) {
|
||||||
Queue *cur = &queues[priority];
|
Queue *cur = &queues[priority];
|
||||||
if (cur->next == NULL)
|
if (cur->next == nullptr)
|
||||||
link(priority, INITIAL_CAPACITY);
|
link(priority, INITIAL_CAPACITY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,7 +176,7 @@ private:
|
||||||
|
|
||||||
for (int i = (int) priority - 1; i >= 0; --i)
|
for (int i = (int) priority - 1; i >= 0; --i)
|
||||||
{
|
{
|
||||||
if (queues[i].next != NULL)
|
if (queues[i].next != nullptr)
|
||||||
{
|
{
|
||||||
cur->next = queues[i].next;
|
cur->next = queues[i].next;
|
||||||
queues[i].next = cur;
|
queues[i].next = cur;
|
||||||
|
@ -193,7 +193,7 @@ private:
|
||||||
int size = cur->end - cur->first;
|
int size = cur->end - cur->first;
|
||||||
if (size >= cur->capacity - 2) {
|
if (size >= cur->capacity - 2) {
|
||||||
IdType *new_data = (IdType *)realloc(cur->data, cur->capacity * 2 * sizeof(IdType));
|
IdType *new_data = (IdType *)realloc(cur->data, cur->capacity * 2 * sizeof(IdType));
|
||||||
if (new_data != NULL) {
|
if (new_data != nullptr) {
|
||||||
cur->capacity *= 2;
|
cur->capacity *= 2;
|
||||||
cur->data = new_data;
|
cur->data = new_data;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Copyright 2013 Dolphin Emulator Project
|
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
||||||
// Licensed under GPLv2
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Copyright 2013 Dolphin Emulator Project
|
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
||||||
// Licensed under GPLv2
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
@ -25,7 +25,7 @@ u32 Timer::GetTimeMs()
|
||||||
return timeGetTime();
|
return timeGetTime();
|
||||||
#else
|
#else
|
||||||
struct timeval t;
|
struct timeval t;
|
||||||
(void)gettimeofday(&t, NULL);
|
(void)gettimeofday(&t, nullptr);
|
||||||
return ((u32)(t.tv_sec * 1000 + t.tv_usec / 1000));
|
return ((u32)(t.tv_sec * 1000 + t.tv_usec / 1000));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -183,7 +183,7 @@ std::string Timer::GetTimeFormatted()
|
||||||
return StringFromFormat("%s:%03i", tmp, tp.millitm);
|
return StringFromFormat("%s:%03i", tmp, tp.millitm);
|
||||||
#else
|
#else
|
||||||
struct timeval t;
|
struct timeval t;
|
||||||
(void)gettimeofday(&t, NULL);
|
(void)gettimeofday(&t, nullptr);
|
||||||
return StringFromFormat("%s:%03d", tmp, (int)(t.tv_usec / 1000));
|
return StringFromFormat("%s:%03d", tmp, (int)(t.tv_usec / 1000));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -197,7 +197,7 @@ double Timer::GetDoubleTime()
|
||||||
(void)::ftime(&tp);
|
(void)::ftime(&tp);
|
||||||
#else
|
#else
|
||||||
struct timeval t;
|
struct timeval t;
|
||||||
(void)gettimeofday(&t, NULL);
|
(void)gettimeofday(&t, nullptr);
|
||||||
#endif
|
#endif
|
||||||
// Get continuous timestamp
|
// Get continuous timestamp
|
||||||
u64 TmpSeconds = Common::Timer::GetTimeSinceJan1970();
|
u64 TmpSeconds = Common::Timer::GetTimeSinceJan1970();
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Copyright 2013 Dolphin Emulator Project
|
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
||||||
// Licensed under GPLv2
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
|
@ -281,28 +281,28 @@ int u8_read_escape_sequence(const char *str, u32 *dest)
|
||||||
do {
|
do {
|
||||||
digs[dno++] = str[i++];
|
digs[dno++] = str[i++];
|
||||||
} while (octal_digit(str[i]) && dno < 3);
|
} while (octal_digit(str[i]) && dno < 3);
|
||||||
ch = strtol(digs, NULL, 8);
|
ch = strtol(digs, nullptr, 8);
|
||||||
}
|
}
|
||||||
else if (str[0] == 'x') {
|
else if (str[0] == 'x') {
|
||||||
while (hex_digit(str[i]) && dno < 2) {
|
while (hex_digit(str[i]) && dno < 2) {
|
||||||
digs[dno++] = str[i++];
|
digs[dno++] = str[i++];
|
||||||
}
|
}
|
||||||
if (dno > 0)
|
if (dno > 0)
|
||||||
ch = strtol(digs, NULL, 16);
|
ch = strtol(digs, nullptr, 16);
|
||||||
}
|
}
|
||||||
else if (str[0] == 'u') {
|
else if (str[0] == 'u') {
|
||||||
while (hex_digit(str[i]) && dno < 4) {
|
while (hex_digit(str[i]) && dno < 4) {
|
||||||
digs[dno++] = str[i++];
|
digs[dno++] = str[i++];
|
||||||
}
|
}
|
||||||
if (dno > 0)
|
if (dno > 0)
|
||||||
ch = strtol(digs, NULL, 16);
|
ch = strtol(digs, nullptr, 16);
|
||||||
}
|
}
|
||||||
else if (str[0] == 'U') {
|
else if (str[0] == 'U') {
|
||||||
while (hex_digit(str[i]) && dno < 8) {
|
while (hex_digit(str[i]) && dno < 8) {
|
||||||
digs[dno++] = str[i++];
|
digs[dno++] = str[i++];
|
||||||
}
|
}
|
||||||
if (dno > 0)
|
if (dno > 0)
|
||||||
ch = strtol(digs, NULL, 16);
|
ch = strtol(digs, nullptr, 16);
|
||||||
}
|
}
|
||||||
*dest = ch;
|
*dest = ch;
|
||||||
|
|
||||||
|
@ -353,7 +353,7 @@ const char *u8_strchr(const char *s, u32 ch, int *charn)
|
||||||
lasti = i;
|
lasti = i;
|
||||||
(*charn)++;
|
(*charn)++;
|
||||||
}
|
}
|
||||||
return NULL;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *u8_memchr(const char *s, u32 ch, size_t sz, int *charn)
|
const char *u8_memchr(const char *s, u32 ch, size_t sz, int *charn)
|
||||||
|
@ -378,7 +378,7 @@ const char *u8_memchr(const char *s, u32 ch, size_t sz, int *charn)
|
||||||
lasti = i;
|
lasti = i;
|
||||||
(*charn)++;
|
(*charn)++;
|
||||||
}
|
}
|
||||||
return NULL;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
int u8_is_locale_utf8(const char *locale)
|
int u8_is_locale_utf8(const char *locale)
|
||||||
|
@ -419,35 +419,35 @@ bool UTF8StringHasNonASCII(const char *utf8string) {
|
||||||
|
|
||||||
std::string ConvertWStringToUTF8(const wchar_t *wstr) {
|
std::string ConvertWStringToUTF8(const wchar_t *wstr) {
|
||||||
int len = (int)wcslen(wstr);
|
int len = (int)wcslen(wstr);
|
||||||
int size = (int)WideCharToMultiByte(CP_UTF8, 0, wstr, len, 0, 0, NULL, NULL);
|
int size = (int)WideCharToMultiByte(CP_UTF8, 0, wstr, len, 0, 0, nullptr, nullptr);
|
||||||
std::string s;
|
std::string s;
|
||||||
s.resize(size);
|
s.resize(size);
|
||||||
if (size > 0) {
|
if (size > 0) {
|
||||||
WideCharToMultiByte(CP_UTF8, 0, wstr, len, &s[0], size, NULL, NULL);
|
WideCharToMultiByte(CP_UTF8, 0, wstr, len, &s[0], size, nullptr, nullptr);
|
||||||
}
|
}
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ConvertWStringToUTF8(const std::wstring &wstr) {
|
std::string ConvertWStringToUTF8(const std::wstring &wstr) {
|
||||||
int len = (int)wstr.size();
|
int len = (int)wstr.size();
|
||||||
int size = (int)WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), len, 0, 0, NULL, NULL);
|
int size = (int)WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), len, 0, 0, nullptr, nullptr);
|
||||||
std::string s;
|
std::string s;
|
||||||
s.resize(size);
|
s.resize(size);
|
||||||
if (size > 0) {
|
if (size > 0) {
|
||||||
WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), len, &s[0], size, NULL, NULL);
|
WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), len, &s[0], size, nullptr, nullptr);
|
||||||
}
|
}
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConvertUTF8ToWString(wchar_t *dest, size_t destSize, const std::string &source) {
|
void ConvertUTF8ToWString(wchar_t *dest, size_t destSize, const std::string &source) {
|
||||||
int len = (int)source.size();
|
int len = (int)source.size();
|
||||||
int size = (int)MultiByteToWideChar(CP_UTF8, 0, source.c_str(), len, NULL, 0);
|
int size = (int)MultiByteToWideChar(CP_UTF8, 0, source.c_str(), len, nullptr, 0);
|
||||||
MultiByteToWideChar(CP_UTF8, 0, source.c_str(), len, dest, std::min((int)destSize, size));
|
MultiByteToWideChar(CP_UTF8, 0, source.c_str(), len, dest, std::min((int)destSize, size));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::wstring ConvertUTF8ToWString(const std::string &source) {
|
std::wstring ConvertUTF8ToWString(const std::string &source) {
|
||||||
int len = (int)source.size();
|
int len = (int)source.size();
|
||||||
int size = (int)MultiByteToWideChar(CP_UTF8, 0, source.c_str(), len, NULL, 0);
|
int size = (int)MultiByteToWideChar(CP_UTF8, 0, source.c_str(), len, nullptr, 0);
|
||||||
std::wstring str;
|
std::wstring str;
|
||||||
str.resize(size);
|
str.resize(size);
|
||||||
if (size > 0) {
|
if (size > 0) {
|
||||||
|
|
|
@ -18,35 +18,46 @@ set(SRCS
|
||||||
arm/skyeye_common/vfp/vfpinstr.cpp
|
arm/skyeye_common/vfp/vfpinstr.cpp
|
||||||
arm/skyeye_common/vfp/vfpsingle.cpp
|
arm/skyeye_common/vfp/vfpsingle.cpp
|
||||||
file_sys/archive_romfs.cpp
|
file_sys/archive_romfs.cpp
|
||||||
|
file_sys/archive_savedata.cpp
|
||||||
file_sys/archive_sdmc.cpp
|
file_sys/archive_sdmc.cpp
|
||||||
|
file_sys/archive_systemsavedata.cpp
|
||||||
|
file_sys/disk_archive.cpp
|
||||||
file_sys/file_romfs.cpp
|
file_sys/file_romfs.cpp
|
||||||
file_sys/file_sdmc.cpp
|
|
||||||
file_sys/directory_romfs.cpp
|
file_sys/directory_romfs.cpp
|
||||||
file_sys/directory_sdmc.cpp
|
|
||||||
hle/kernel/address_arbiter.cpp
|
hle/kernel/address_arbiter.cpp
|
||||||
hle/kernel/archive.cpp
|
|
||||||
hle/kernel/event.cpp
|
hle/kernel/event.cpp
|
||||||
hle/kernel/kernel.cpp
|
hle/kernel/kernel.cpp
|
||||||
hle/kernel/mutex.cpp
|
hle/kernel/mutex.cpp
|
||||||
|
hle/kernel/semaphore.cpp
|
||||||
hle/kernel/shared_memory.cpp
|
hle/kernel/shared_memory.cpp
|
||||||
hle/kernel/thread.cpp
|
hle/kernel/thread.cpp
|
||||||
hle/service/ac_u.cpp
|
hle/service/ac_u.cpp
|
||||||
|
hle/service/act_u.cpp
|
||||||
|
hle/service/am_app.cpp
|
||||||
hle/service/am_net.cpp
|
hle/service/am_net.cpp
|
||||||
|
hle/service/apt_a.cpp
|
||||||
hle/service/apt_u.cpp
|
hle/service/apt_u.cpp
|
||||||
hle/service/boss_u.cpp
|
hle/service/boss_u.cpp
|
||||||
hle/service/cfg_i.cpp
|
hle/service/cecd_u.cpp
|
||||||
hle/service/cfg_u.cpp
|
hle/service/cfg/cfg.cpp
|
||||||
|
hle/service/cfg/cfg_i.cpp
|
||||||
|
hle/service/cfg/cfg_u.cpp
|
||||||
hle/service/csnd_snd.cpp
|
hle/service/csnd_snd.cpp
|
||||||
hle/service/dsp_dsp.cpp
|
hle/service/dsp_dsp.cpp
|
||||||
hle/service/err_f.cpp
|
hle/service/err_f.cpp
|
||||||
hle/service/fs_user.cpp
|
|
||||||
hle/service/frd_u.cpp
|
hle/service/frd_u.cpp
|
||||||
|
hle/service/fs/archive.cpp
|
||||||
|
hle/service/fs/fs_user.cpp
|
||||||
hle/service/gsp_gpu.cpp
|
hle/service/gsp_gpu.cpp
|
||||||
hle/service/hid_user.cpp
|
hle/service/hid_user.cpp
|
||||||
|
hle/service/http_c.cpp
|
||||||
hle/service/ir_rst.cpp
|
hle/service/ir_rst.cpp
|
||||||
hle/service/ir_u.cpp
|
hle/service/ir_u.cpp
|
||||||
|
hle/service/ldr_ro.cpp
|
||||||
hle/service/mic_u.cpp
|
hle/service/mic_u.cpp
|
||||||
hle/service/ndm_u.cpp
|
hle/service/ndm_u.cpp
|
||||||
|
hle/service/news_u.cpp
|
||||||
|
hle/service/nim_aoc.cpp
|
||||||
hle/service/nwm_uds.cpp
|
hle/service/nwm_uds.cpp
|
||||||
hle/service/pm_app.cpp
|
hle/service/pm_app.cpp
|
||||||
hle/service/ptm_u.cpp
|
hle/service/ptm_u.cpp
|
||||||
|
@ -59,10 +70,10 @@ set(SRCS
|
||||||
hle/svc.cpp
|
hle/svc.cpp
|
||||||
hw/gpu.cpp
|
hw/gpu.cpp
|
||||||
hw/hw.cpp
|
hw/hw.cpp
|
||||||
hw/ndma.cpp
|
|
||||||
loader/elf.cpp
|
loader/elf.cpp
|
||||||
loader/loader.cpp
|
loader/loader.cpp
|
||||||
loader/ncch.cpp
|
loader/ncch.cpp
|
||||||
|
loader/3dsx.cpp
|
||||||
core.cpp
|
core.cpp
|
||||||
core_timing.cpp
|
core_timing.cpp
|
||||||
mem_map.cpp
|
mem_map.cpp
|
||||||
|
@ -92,39 +103,51 @@ set(HEADERS
|
||||||
arm/skyeye_common/vfp/vfp.h
|
arm/skyeye_common/vfp/vfp.h
|
||||||
arm/skyeye_common/vfp/vfp_helper.h
|
arm/skyeye_common/vfp/vfp_helper.h
|
||||||
arm/arm_interface.h
|
arm/arm_interface.h
|
||||||
file_sys/archive.h
|
file_sys/archive_backend.h
|
||||||
file_sys/archive_romfs.h
|
file_sys/archive_romfs.h
|
||||||
|
file_sys/archive_savedata.h
|
||||||
file_sys/archive_sdmc.h
|
file_sys/archive_sdmc.h
|
||||||
file_sys/file.h
|
file_sys/archive_systemsavedata.h
|
||||||
|
file_sys/disk_archive.h
|
||||||
|
file_sys/file_backend.h
|
||||||
file_sys/file_romfs.h
|
file_sys/file_romfs.h
|
||||||
file_sys/file_sdmc.h
|
file_sys/directory_backend.h
|
||||||
file_sys/directory.h
|
|
||||||
file_sys/directory_romfs.h
|
file_sys/directory_romfs.h
|
||||||
file_sys/directory_sdmc.h
|
|
||||||
hle/kernel/address_arbiter.h
|
hle/kernel/address_arbiter.h
|
||||||
hle/kernel/archive.h
|
|
||||||
hle/kernel/event.h
|
hle/kernel/event.h
|
||||||
hle/kernel/kernel.h
|
hle/kernel/kernel.h
|
||||||
hle/kernel/mutex.h
|
hle/kernel/mutex.h
|
||||||
|
hle/kernel/semaphore.h
|
||||||
|
hle/kernel/session.h
|
||||||
hle/kernel/shared_memory.h
|
hle/kernel/shared_memory.h
|
||||||
hle/kernel/thread.h
|
hle/kernel/thread.h
|
||||||
hle/service/ac_u.h
|
hle/service/ac_u.h
|
||||||
|
hle/service/act_u.h
|
||||||
|
hle/service/am_app.h
|
||||||
hle/service/am_net.h
|
hle/service/am_net.h
|
||||||
|
hle/service/apt_a.h
|
||||||
hle/service/apt_u.h
|
hle/service/apt_u.h
|
||||||
hle/service/boss_u.h
|
hle/service/boss_u.h
|
||||||
hle/service/cfg_i.h
|
hle/service/cecd_u.h
|
||||||
hle/service/cfg_u.h
|
hle/service/cfg/cfg.h
|
||||||
|
hle/service/cfg/cfg_i.h
|
||||||
|
hle/service/cfg/cfg_u.h
|
||||||
hle/service/csnd_snd.h
|
hle/service/csnd_snd.h
|
||||||
hle/service/dsp_dsp.h
|
hle/service/dsp_dsp.h
|
||||||
hle/service/err_f.h
|
hle/service/err_f.h
|
||||||
hle/service/fs_user.h
|
|
||||||
hle/service/frd_u.h
|
hle/service/frd_u.h
|
||||||
|
hle/service/fs/archive.h
|
||||||
|
hle/service/fs/fs_user.h
|
||||||
hle/service/gsp_gpu.h
|
hle/service/gsp_gpu.h
|
||||||
hle/service/hid_user.h
|
hle/service/hid_user.h
|
||||||
|
hle/service/http_c.h
|
||||||
hle/service/ir_rst.h
|
hle/service/ir_rst.h
|
||||||
hle/service/ir_u.h
|
hle/service/ir_u.h
|
||||||
|
hle/service/ldr_ro.h
|
||||||
hle/service/mic_u.h
|
hle/service/mic_u.h
|
||||||
hle/service/ndm_u.h
|
hle/service/ndm_u.h
|
||||||
|
hle/service/news_u.h
|
||||||
|
hle/service/nim_aoc.h
|
||||||
hle/service/nwm_uds.h
|
hle/service/nwm_uds.h
|
||||||
hle/service/pm_app.h
|
hle/service/pm_app.h
|
||||||
hle/service/ptm_u.h
|
hle/service/ptm_u.h
|
||||||
|
@ -139,10 +162,10 @@ set(HEADERS
|
||||||
hle/svc.h
|
hle/svc.h
|
||||||
hw/gpu.h
|
hw/gpu.h
|
||||||
hw/hw.h
|
hw/hw.h
|
||||||
hw/ndma.h
|
|
||||||
loader/elf.h
|
loader/elf.h
|
||||||
loader/loader.h
|
loader/loader.h
|
||||||
loader/ncch.h
|
loader/ncch.h
|
||||||
|
loader/3dsx.h
|
||||||
core.h
|
core.h
|
||||||
core_timing.h
|
core_timing.h
|
||||||
mem_map.h
|
mem_map.h
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Copyright 2014 Citra Emulator Project
|
// Copyright 2014 Citra Emulator Project
|
||||||
// Licensed under GPLv2
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
@ -77,6 +77,12 @@ public:
|
||||||
*/
|
*/
|
||||||
virtual u64 GetTicks() const = 0;
|
virtual u64 GetTicks() const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Advance the CPU core by the specified number of ticks (e.g. to simulate CPU execution time)
|
||||||
|
* @param ticks Number of ticks to advance the CPU core
|
||||||
|
*/
|
||||||
|
virtual void AddTicks(u64 ticks) = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves the current CPU context
|
* Saves the current CPU context
|
||||||
* @param ctx Thread context to save
|
* @param ctx Thread context to save
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Copyright 2014 Citra Emulator Project
|
// Copyright 2014 Citra Emulator Project
|
||||||
// Licensed under GPLv2
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Copyright 2014 Citra Emulator Project
|
// Copyright 2014 Citra Emulator Project
|
||||||
// Licensed under GPLv2
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue