mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 05:40: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 | ||||
| *.swp | ||||
| .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"] | ||||
| 	path = externals/inih/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 | ||||
| 
 | ||||
| set -e | ||||
| set -x | ||||
| 
 | ||||
| #if OS is linux or is not set | ||||
| if [ "$TRAVIS_OS_NAME" = linux -o -z "$TRAVIS_OS_NAME" ]; then  | ||||
|  |  | |||
|  | @ -1,17 +1,22 @@ | |||
| #!/bin/sh | ||||
| 
 | ||||
| set -e | ||||
| set -x | ||||
| 
 | ||||
| #if OS is linux or is not set | ||||
| if [ "$TRAVIS_OS_NAME" = linux -o -z "$TRAVIS_OS_NAME" ]; then | ||||
|     sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y | ||||
|     sudo apt-get -qq update | ||||
|     sudo apt-get -qq install g++-4.8 xorg-dev libglu1-mesa-dev libxcursor-dev | ||||
|     sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.8 90 | ||||
|     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.9 90 | ||||
|     ( | ||||
|         git clone https://github.com/glfw/glfw.git --branch 3.0.4 --depth 1 | ||||
|         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 | ||||
|  | @ -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 \ | ||||
|         | sudo tar -xz -C /usr/local --strip-components=1 | ||||
| elif [ "$TRAVIS_OS_NAME" = osx ]; then | ||||
|     export HOMEBREW_CACHE="$PWD/.homebrew-cache" | ||||
|     mkdir -p "$HOMEBREW_CACHE" | ||||
|     brew tap homebrew/versions | ||||
|     brew install qt5 glfw3 pkgconfig | ||||
| fi | ||||
|  |  | |||
|  | @ -4,6 +4,11 @@ os: | |||
| 
 | ||||
| language: cpp | ||||
| 
 | ||||
| cache: | ||||
|   apt: true | ||||
|   directories: | ||||
|     - .homebrew-cache | ||||
| 
 | ||||
| before_install: | ||||
|  - sh .travis-deps.sh | ||||
| 
 | ||||
|  |  | |||
|  | @ -6,16 +6,48 @@ project(citra) | |||
| 
 | ||||
| if (NOT MSVC) | ||||
|     set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wno-attributes") | ||||
|     add_definitions(-pthread) | ||||
| else() | ||||
|     # Silence deprecation 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() | ||||
| add_definitions(-DSINGLETHREADED) | ||||
| 
 | ||||
| find_package(PNG) | ||||
| find_package(PNG QUIET) | ||||
| if (PNG_FOUND) | ||||
|     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 | ||||
| list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/externals/cmake-modules") | ||||
|  | @ -65,10 +97,6 @@ if (ENABLE_GLFW) | |||
| 
 | ||||
|         set(GLFW_LIBRARIES glfw3) | ||||
|     else() | ||||
|         if (NOT APPLE) | ||||
|             find_package(X11 REQUIRED) | ||||
|         endif() | ||||
| 
 | ||||
|         find_package(PkgConfig REQUIRED) | ||||
|         pkg_search_module(GLFW REQUIRED glfw3) | ||||
|     endif() | ||||
|  | @ -131,6 +159,8 @@ set(INI_PREFIX "${CMAKE_CURRENT_SOURCE_DIR}/externals/inih") | |||
| include_directories(${INI_PREFIX}) | ||||
| add_subdirectory(${INI_PREFIX}) | ||||
| 
 | ||||
| include_directories(externals/nihstro/include) | ||||
| 
 | ||||
| # process subdirectories | ||||
| if(ENABLE_QT) | ||||
|     include_directories(externals/qhexedit) | ||||
|  |  | |||
|  | @ -27,6 +27,7 @@ Follow the indentation/whitespace style shown below. Do not use tabs, use 4-spac | |||
| ### Comments | ||||
| * 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 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 | ||||
| namespace Example { | ||||
|  |  | |||
							
								
								
									
										2
									
								
								Doxyfile
									
										
									
									
									
								
							
							
						
						
									
										2
									
								
								Doxyfile
									
										
									
									
									
								
							|  | @ -419,7 +419,7 @@ LOOKUP_CACHE_SIZE      = 0 | |||
| # normally produced when WARNINGS is set to YES. | ||||
| # 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 | ||||
| # be included in the documentation. | ||||
|  |  | |||
							
								
								
									
										22
									
								
								README.md
									
										
									
									
									
								
							
							
						
						
									
										22
									
								
								README.md
									
										
									
									
									
								
							|  | @ -1,17 +1,31 @@ | |||
| citra emulator | ||||
| Citra Emulator | ||||
| ============== | ||||
| [](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 | ||||
| 
 | ||||
| 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 | ||||
| 
 | ||||
| * __Windows__: [Windows Build](https://github.com/citra-emu/citra/wiki/Windows-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) | ||||
| 
 | ||||
| 
 | ||||
| ### 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}) | ||||
| 
 | ||||
| # 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}) | ||||
| target_link_libraries(citra core common video_core) | ||||
| target_link_libraries(citra ${GLFW_LIBRARIES} ${OPENGL_gl_LIBRARY} inih) | ||||
| 
 | ||||
| if (UNIX) | ||||
|     target_link_libraries(citra -pthread) | ||||
| endif() | ||||
| 
 | ||||
| if (APPLE) | ||||
|     target_link_libraries(citra iconv pthread ${COREFOUNDATION_LIBRARY}) | ||||
|     target_link_libraries(citra iconv ${COREFOUNDATION_LIBRARY}) | ||||
| elseif (WIN32) | ||||
|     target_link_libraries(citra winmm) | ||||
|     if (MINGW) | ||||
|         target_link_libraries(citra iconv) | ||||
|     endif()     | ||||
| else() # Unix | ||||
|     target_link_libraries(citra pthread rt) | ||||
|     target_link_libraries(citra ${X11_X11_LIB} ${X11_Xi_LIB} ${X11_Xcursor_LIB} ${X11_Xrandr_LIB} ${X11_xv86vmode_LIB}) | ||||
|     target_link_libraries(citra rt) | ||||
| endif() | ||||
| 
 | ||||
| #install(TARGETS citra RUNTIME DESTINATION ${bindir}) | ||||
|  |  | |||
|  | @ -1,9 +1,14 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <thread> | ||||
| 
 | ||||
| #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/system.h" | ||||
|  | @ -15,17 +20,21 @@ | |||
| 
 | ||||
| /// Application entry point
 | ||||
| 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) { | ||||
|         ERROR_LOG(BOOT, "Failed to load ROM: No ROM specified"); | ||||
|         LOG_CRITICAL(Frontend, "Failed to load ROM: No ROM specified"); | ||||
|         return -1; | ||||
|     } | ||||
| 
 | ||||
|     Config config; | ||||
| 
 | ||||
|     if (!Settings::values.enable_log) | ||||
|         LogManager::Shutdown(); | ||||
|     log_filter.ParseFilterString(Settings::values.log_filter); | ||||
| 
 | ||||
|     std::string boot_filename = argv[1]; | ||||
|     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); | ||||
|     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; | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #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) { | ||||
|     if (config->ParseError() < 0) { | ||||
|         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::WriteStringToFile(true, default_contents, location); | ||||
|             *config = INIReader(location); // Reopen file
 | ||||
| 
 | ||||
|             return LoadINI(config, location, default_contents, false); | ||||
|         } | ||||
|         ERROR_LOG(CONFIG, "Failed."); | ||||
|         LOG_ERROR(Config, "Failed."); | ||||
|         return false; | ||||
|     } | ||||
|     INFO_LOG(CONFIG, "Successfully loaded %s", location); | ||||
|     LOG_INFO(Config, "Successfully loaded %s", location); | ||||
|     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_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); | ||||
|  | @ -54,27 +55,22 @@ void Config::ReadControls() { | |||
|     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_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.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); | ||||
| } | ||||
| 
 | ||||
| void Config::ReadMiscellaneous() { | ||||
|     Settings::values.enable_log = glfw_config->GetBoolean("Miscellaneous", "enable_log", true); | ||||
|     // Miscellaneous
 | ||||
|     Settings::values.log_filter = glfw_config->Get("Miscellaneous", "log_filter", "*:Info"); | ||||
| } | ||||
| 
 | ||||
| void Config::Reload() { | ||||
|     LoadINI(glfw_config, glfw_config_loc.c_str(), DefaultINI::glfw_config_file); | ||||
|     ReadControls(); | ||||
|     ReadCore(); | ||||
|     ReadData(); | ||||
|     ReadMiscellaneous(); | ||||
|     ReadValues(); | ||||
| } | ||||
| 
 | ||||
| Config::~Config() { | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
|  | @ -15,10 +15,7 @@ class Config { | |||
|     std::string glfw_config_loc; | ||||
| 
 | ||||
|     bool LoadINI(INIReader* config, const char* location, const std::string& default_contents="", bool retry=true); | ||||
|     void ReadControls(); | ||||
|     void ReadCore(); | ||||
|     void ReadData(); | ||||
|     void ReadMiscellaneous(); | ||||
|     void ReadValues(); | ||||
| public: | ||||
|     Config(); | ||||
|     ~Config(); | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
|  | @ -28,13 +28,14 @@ pad_sright = | |||
| 
 | ||||
| [Core] | ||||
| 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] | ||||
| use_virtual_sd = | ||||
| 
 | ||||
| [Miscellaneous] | ||||
| enable_log = | ||||
| log_filter = *:Info  ## Examples: *:Debug Kernel.SVC:Trace Service.*:Critical | ||||
| )"; | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <GLFW/glfw3.h> | ||||
|  | @ -36,15 +36,15 @@ const bool EmuWindow_GLFW::IsOpen() { | |||
| } | ||||
| 
 | ||||
| void EmuWindow_GLFW::OnFramebufferResizeEvent(GLFWwindow* win, int width, int height) { | ||||
|     _dbg_assert_(GUI, width > 0); | ||||
|     _dbg_assert_(GUI, height > 0); | ||||
|     _dbg_assert_(Frontend, width > 0); | ||||
|     _dbg_assert_(Frontend, height > 0); | ||||
| 
 | ||||
|     GetEmuWindow(win)->NotifyFramebufferSizeChanged(std::pair<unsigned,unsigned>(width, height)); | ||||
| } | ||||
| 
 | ||||
| void EmuWindow_GLFW::OnClientAreaResizeEvent(GLFWwindow* win, int width, int height) { | ||||
|     _dbg_assert_(GUI, width > 0); | ||||
|     _dbg_assert_(GUI, height > 0); | ||||
|     _dbg_assert_(Frontend, width > 0); | ||||
|     _dbg_assert_(Frontend, height > 0); | ||||
| 
 | ||||
|     // NOTE: GLFW provides no proper way to set a minimal window size.
 | ||||
|     //       Hence, we just ignore the corresponding EmuWindow hint.
 | ||||
|  | @ -58,9 +58,13 @@ EmuWindow_GLFW::EmuWindow_GLFW() { | |||
| 
 | ||||
|     ReloadSetKeymaps(); | ||||
| 
 | ||||
|     glfwSetErrorCallback([](int error, const char *desc){ | ||||
|         LOG_ERROR(Frontend, "GLFW 0x%08x: %s", error, desc); | ||||
|     }); | ||||
| 
 | ||||
|     // Initialize the window
 | ||||
|     if(glfwInit() != GL_TRUE) { | ||||
|         printf("Failed to initialize GLFW! Exiting..."); | ||||
|         LOG_CRITICAL(Frontend, "Failed to initialize GLFW! Exiting..."); | ||||
|         exit(1); | ||||
|     } | ||||
|     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); | ||||
|     m_render_window = glfwCreateWindow(VideoCore::kScreenTopWidth, | ||||
|         (VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight), | ||||
|         window_title.c_str(), NULL, NULL); | ||||
|         window_title.c_str(), nullptr, nullptr); | ||||
| 
 | ||||
|     if (m_render_window == NULL) { | ||||
|         printf("Failed to create GLFW window! Exiting..."); | ||||
|     if (m_render_window == nullptr) { | ||||
|         LOG_CRITICAL(Frontend, "Failed to create GLFW window! Exiting..."); | ||||
|         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
 | ||||
| void EmuWindow_GLFW::DoneCurrent() { | ||||
|     glfwMakeContextCurrent(NULL); | ||||
|     glfwMakeContextCurrent(nullptr); | ||||
| } | ||||
| 
 | ||||
| void EmuWindow_GLFW::ReloadSetKeymaps() { | ||||
|  | @ -145,7 +149,7 @@ void EmuWindow_GLFW::OnMinimalClientAreaChangeRequest(const std::pair<unsigned,u | |||
|     std::pair<int,int> current_size; | ||||
|     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_height = std::max(current_size.second, (int)minimal_size.second); | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
|  |  | |||
|  | @ -8,9 +8,12 @@ set(SRCS | |||
|             debugger/callstack.cpp | ||||
|             debugger/disassembler.cpp | ||||
|             debugger/graphics.cpp | ||||
|             debugger/graphics_breakpoints.cpp | ||||
|             debugger/graphics_cmdlists.cpp | ||||
|             debugger/graphics_framebuffer.cpp | ||||
|             debugger/ramview.cpp | ||||
|             debugger/registers.cpp | ||||
|             util/spinbox.cpp | ||||
|             bootmanager.cpp | ||||
|             hotkeys.cpp | ||||
|             main.cpp | ||||
|  | @ -23,9 +26,13 @@ set(HEADERS | |||
|             debugger/callstack.hxx | ||||
|             debugger/disassembler.hxx | ||||
|             debugger/graphics.hxx | ||||
|             debugger/graphics_breakpoints.hxx | ||||
|             debugger/graphics_breakpoints_p.hxx | ||||
|             debugger/graphics_cmdlists.hxx | ||||
|             debugger/graphics_framebuffer.hxx | ||||
|             debugger/ramview.hxx | ||||
|             debugger/registers.hxx | ||||
|             util/spinbox.hxx | ||||
|             bootmanager.hxx | ||||
|             hotkeys.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 ${OPENGL_gl_LIBRARY} ${CITRA_QT_LIBS}) | ||||
| 
 | ||||
| if (UNIX) | ||||
|     target_link_libraries(citra-qt -pthread) | ||||
| endif() | ||||
| 
 | ||||
| if (APPLE) | ||||
|     target_link_libraries(citra-qt iconv ${COREFOUNDATION_LIBRARY}) | ||||
| elseif (WIN32) | ||||
|  |  | |||
|  | @ -14,6 +14,8 @@ | |||
| #include "core/core.h" | ||||
| #include "core/settings.h" | ||||
| 
 | ||||
| #include "video_core/debug_utils/debug_utils.h" | ||||
| 
 | ||||
| #include "video_core/video_core.h" | ||||
| 
 | ||||
| #include "citra_qt/version.h" | ||||
|  | @ -60,26 +62,33 @@ void EmuThread::Stop() | |||
| { | ||||
|     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; | ||||
|     } | ||||
|     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;
 | ||||
| 
 | ||||
|     wait(500); | ||||
|     // TODO: Waiting here is just a bad workaround for retarded shutdown logic.
 | ||||
|     wait(1000); | ||||
|     if (isRunning()) | ||||
|     { | ||||
|         WARN_LOG(MASTER_LOG, "EmuThread still running, terminating..."); | ||||
|         LOG_WARNING(Frontend, "EmuThread still running, terminating..."); | ||||
|         quit(); | ||||
|         wait(1000); | ||||
| 
 | ||||
|         // 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()) | ||||
|         { | ||||
|             WARN_LOG(MASTER_LOG, "EmuThread STILL running, something is wrong here..."); | ||||
|             LOG_CRITICAL(Frontend, "EmuThread STILL running, something is wrong here..."); | ||||
|             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
 | ||||
|     // otherwise, store the last backup
 | ||||
|     if (parent() == NULL) | ||||
|     if (parent() == nullptr) | ||||
|         return ((QGLWidget*)this)->saveGeometry(); | ||||
|     else | ||||
|         return geometry; | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <QString> | ||||
|  | @ -21,7 +21,7 @@ Config::Config() { | |||
|     Reload(); | ||||
| } | ||||
| 
 | ||||
| void Config::ReadControls() { | ||||
| void Config::ReadValues() { | ||||
|     qt_config->beginGroup("Controls"); | ||||
|     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(); | ||||
|  | @ -41,9 +41,23 @@ void Config::ReadControls() { | |||
|     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(); | ||||
|     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->setValue("pad_a", Settings::values.pad_a_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_sright", Settings::values.pad_sright_key); | ||||
|     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->setValue("cpu_core", Settings::values.cpu_core); | ||||
|     qt_config->setValue("gpu_refresh_rate", Settings::values.gpu_refresh_rate); | ||||
|     qt_config->setValue("frame_skip", Settings::values.frame_skip); | ||||
|     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->setValue("use_virtual_sd", Settings::values.use_virtual_sd); | ||||
|     qt_config->endGroup(); | ||||
| } | ||||
| 
 | ||||
| void Config::ReadMiscellaneous() { | ||||
|     qt_config->beginGroup("Miscellaneous"); | ||||
|     Settings::values.enable_log = qt_config->value("enable_log", true).toBool(); | ||||
|     qt_config->endGroup(); | ||||
| } | ||||
| 
 | ||||
| void Config::SaveMiscellaneous() { | ||||
|     qt_config->beginGroup("Miscellaneous"); | ||||
|     qt_config->setValue("enable_log", Settings::values.enable_log); | ||||
|     qt_config->setValue("log_filter", QString::fromStdString(Settings::values.log_filter)); | ||||
|     qt_config->endGroup(); | ||||
| } | ||||
| 
 | ||||
| void Config::Reload() { | ||||
|     ReadControls(); | ||||
|     ReadCore(); | ||||
|     ReadData(); | ||||
|     ReadMiscellaneous(); | ||||
|     ReadValues(); | ||||
| } | ||||
| 
 | ||||
| void Config::Save() { | ||||
|     SaveControls(); | ||||
|     SaveCore(); | ||||
|     SaveData(); | ||||
|     SaveMiscellaneous(); | ||||
|     SaveValues(); | ||||
| } | ||||
| 
 | ||||
| Config::~Config() { | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
|  | @ -12,15 +12,8 @@ class Config { | |||
|     QSettings* qt_config; | ||||
|     std::string qt_config_loc; | ||||
| 
 | ||||
|     void ReadControls(); | ||||
|     void SaveControls(); | ||||
|     void ReadCore(); | ||||
|     void SaveCore(); | ||||
|     void ReadData(); | ||||
|     void SaveData(); | ||||
| 
 | ||||
|     void ReadMiscellaneous(); | ||||
|     void SaveMiscellaneous(); | ||||
|     void ReadValues(); | ||||
|     void SaveValues(); | ||||
| public: | ||||
|     Config(); | ||||
|     ~Config(); | ||||
|  |  | |||
|  | @ -27,10 +27,10 @@ void CallstackWidget::OnCPUStepped() | |||
|     ARM_Interface* app_core = Core::g_app_core; | ||||
| 
 | ||||
|     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; | ||||
|     for (int addr = 0x10000000; addr >= sp; addr -= 4) | ||||
|     for (u32 addr = 0x10000000; addr >= sp; addr -= 4) | ||||
|     { | ||||
|         ret_addr = Memory::Read32(addr); | ||||
|         call_addr = ret_addr - 4; //get call address???
 | ||||
|  |  | |||
|  | @ -220,7 +220,9 @@ void DisassemblerWidget::OnPause() | |||
|     emu_thread.SetCpuRunning(false); | ||||
| 
 | ||||
|     // TODO: By now, the CPU might not have actually stopped...
 | ||||
|     model->SetNextInstruction(Core::g_app_core->GetPC()); | ||||
|     if (Core::g_app_core) { | ||||
|         model->SetNextInstruction(Core::g_app_core->GetPC()); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void DisassemblerWidget::OnToggleStartStop() | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include "graphics.hxx" | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #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
 | ||||
| // Licensed under GPLv2
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <QLabel> | ||||
| #include <QListView> | ||||
| #include <QMainWindow> | ||||
| #include <QPushButton> | ||||
| #include <QVBoxLayout> | ||||
| #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" | ||||
| 
 | ||||
| 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(); | ||||
| } | ||||
| 
 | ||||
| int GPUCommandListModel::columnCount(const QModelIndex& parent) const | ||||
| { | ||||
| int GPUCommandListModel::columnCount(const QModelIndex& parent) const { | ||||
|     return 2; | ||||
| } | ||||
| 
 | ||||
| QVariant GPUCommandListModel::data(const QModelIndex& index, int role) const | ||||
| { | ||||
| QVariant GPUCommandListModel::data(const QModelIndex& index, int role) const { | ||||
|     if (!index.isValid()) | ||||
|         return QVariant(); | ||||
| 
 | ||||
|  | @ -36,21 +184,39 @@ QVariant GPUCommandListModel::data(const QModelIndex& index, int role) const | |||
|     if (role == Qt::DisplayRole) { | ||||
|         QString content; | ||||
|         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(" "); | ||||
|             return content; | ||||
|         } 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'))); | ||||
|             return content; | ||||
|         } | ||||
| 
 | ||||
|         return QVariant(content); | ||||
|     } else if (role == CommandIdRole) { | ||||
|         return QVariant::fromValue<int>(cmd.cmd_id.Value()); | ||||
|     } | ||||
| 
 | ||||
|     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(); | ||||
| 
 | ||||
|     pica_trace = trace; | ||||
|  | @ -58,38 +224,107 @@ void GPUCommandListModel::OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace& | |||
|     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); | ||||
| 
 | ||||
|     QWidget* main_widget = new QWidget; | ||||
| 
 | ||||
|     QTreeView* list_widget = new QTreeView; | ||||
|     list_widget = new QTreeView; | ||||
|     list_widget->setModel(model); | ||||
|     list_widget->setFont(QFont("monospace")); | ||||
|     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(this, SIGNAL(TracingFinished(const Pica::DebugUtils::PicaTrace&)), | ||||
|             model, SLOT(OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace&))); | ||||
| 
 | ||||
|     command_info_widget = new QWidget; | ||||
| 
 | ||||
|     QVBoxLayout* main_layout = new QVBoxLayout; | ||||
|     main_layout->addWidget(list_widget); | ||||
|     main_layout->addWidget(toggle_tracing); | ||||
|     main_layout->addWidget(command_info_widget); | ||||
|     main_widget->setLayout(main_layout); | ||||
| 
 | ||||
|     setWidget(main_widget); | ||||
| } | ||||
| 
 | ||||
| void GPUCommandListWidget::OnToggleTracing() | ||||
| { | ||||
| void GPUCommandListWidget::OnToggleTracing() { | ||||
|     if (!Pica::DebugUtils::IsPicaTracing()) { | ||||
|         Pica::DebugUtils::StartPicaTracing(); | ||||
|         toggle_tracing->setText(tr("Finish Tracing")); | ||||
|     } else { | ||||
|         pica_trace = Pica::DebugUtils::FinishPicaTracing(); | ||||
|         emit TracingFinished(*pica_trace); | ||||
|         toggle_tracing->setText(tr("Start Tracing")); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
|  | @ -10,16 +10,24 @@ | |||
| #include "video_core/gpu_debugger.h" | ||||
| #include "video_core/debug_utils/debug_utils.h" | ||||
| 
 | ||||
| class QPushButton; | ||||
| class QTreeView; | ||||
| 
 | ||||
| class GPUCommandListModel : public QAbstractListModel | ||||
| { | ||||
|     Q_OBJECT | ||||
| 
 | ||||
| public: | ||||
|     enum { | ||||
|         CommandIdRole = Qt::UserRole, | ||||
|     }; | ||||
| 
 | ||||
|     GPUCommandListModel(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; | ||||
| 
 | ||||
| public slots: | ||||
|     void OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace& trace); | ||||
|  | @ -37,10 +45,39 @@ public: | |||
| 
 | ||||
| public slots: | ||||
|     void OnToggleTracing(); | ||||
|     void OnCommandDoubleClicked(const QModelIndex&); | ||||
| 
 | ||||
|     void SetCommandInfo(const QModelIndex&); | ||||
| 
 | ||||
| signals: | ||||
|     void TracingFinished(const Pica::DebugUtils::PicaTrace&); | ||||
| 
 | ||||
| private: | ||||
|     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 | ||||
| { | ||||
|     Hotkey() : shortcut(NULL), context(Qt::WindowShortcut) {} | ||||
|     Hotkey() : shortcut(nullptr), context(Qt::WindowShortcut) {} | ||||
| 
 | ||||
|     QKeySequence keyseq; | ||||
|     QShortcut* shortcut; | ||||
|  | @ -81,7 +81,7 @@ QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widge | |||
|     Hotkey& hk = hotkey_groups[group][action]; | ||||
| 
 | ||||
|     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; | ||||
| } | ||||
|  |  | |||
|  | @ -1,3 +1,5 @@ | |||
| #include <thread> | ||||
| 
 | ||||
| #include <QtGui> | ||||
| #include <QDesktopWidget> | ||||
| #include <QFileDialog> | ||||
|  | @ -5,8 +7,13 @@ | |||
| #include "main.hxx" | ||||
| 
 | ||||
| #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/log_manager.h" | ||||
| #include "common/scope_exit.h" | ||||
| 
 | ||||
| #if EMU_PLATFORM == PLATFORM_LINUX | ||||
| #include <unistd.h> | ||||
| #endif | ||||
|  | @ -20,7 +27,9 @@ | |||
| #include "debugger/callstack.hxx" | ||||
| #include "debugger/ramview.hxx" | ||||
| #include "debugger/graphics.hxx" | ||||
| #include "debugger/graphics_breakpoints.hxx" | ||||
| #include "debugger/graphics_cmdlists.hxx" | ||||
| #include "debugger/graphics_framebuffer.hxx" | ||||
| 
 | ||||
| #include "core/settings.h" | ||||
| #include "core/system.h" | ||||
|  | @ -31,16 +40,12 @@ | |||
| 
 | ||||
| #include "version.h" | ||||
| 
 | ||||
| 
 | ||||
| GMainWindow::GMainWindow() | ||||
| { | ||||
|     LogManager::Init(); | ||||
|     Pica::g_debug_context = Pica::DebugContext::Construct(); | ||||
| 
 | ||||
|     Config config; | ||||
| 
 | ||||
|     if (!Settings::values.enable_log) | ||||
|         LogManager::Shutdown(); | ||||
| 
 | ||||
|     ui.setupUi(this); | ||||
|     statusBar()->hide(); | ||||
| 
 | ||||
|  | @ -67,12 +72,22 @@ GMainWindow::GMainWindow() | |||
|     addDockWidget(Qt::RightDockWidgetArea, graphicsCommandsWidget); | ||||
|     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")); | ||||
|     debug_menu->addAction(disasmWidget->toggleViewAction()); | ||||
|     debug_menu->addAction(registersWidget->toggleViewAction()); | ||||
|     debug_menu->addAction(callstackWidget->toggleViewAction()); | ||||
|     debug_menu->addAction(graphicsWidget->toggleViewAction()); | ||||
|     debug_menu->addAction(graphicsCommandsWidget->toggleViewAction()); | ||||
|     debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction()); | ||||
|     debug_menu->addAction(graphicsFramebufferWidget->toggleViewAction()); | ||||
| 
 | ||||
|     // Set default UI state
 | ||||
|     // 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() | ||||
| { | ||||
|     // will get automatically deleted otherwise
 | ||||
|     if (render_window->parent() == NULL) | ||||
|     if (render_window->parent() == nullptr) | ||||
|         delete render_window; | ||||
| 
 | ||||
|     Pica::g_debug_context.reset(); | ||||
| } | ||||
| 
 | ||||
| void GMainWindow::BootGame(std::string filename) | ||||
| { | ||||
|     NOTICE_LOG(MASTER_LOG, "Citra starting...\n"); | ||||
|     LOG_INFO(Frontend, "Citra starting...\n"); | ||||
|     System::Init(render_window); | ||||
| 
 | ||||
|     if (Core::Init()) { | ||||
|         ERROR_LOG(MASTER_LOG, "Core initialization failed, exiting..."); | ||||
|         Core::Stop(); | ||||
|         exit(1); | ||||
|     } | ||||
| 
 | ||||
|     // Load a game or die...
 | ||||
|     if (Loader::ResultStatus::Success != Loader::LoadFile(filename)) { | ||||
|         ERROR_LOG(BOOT, "Failed to load ROM!"); | ||||
|         LOG_CRITICAL(Frontend, "Failed to load ROM!"); | ||||
|     } | ||||
| 
 | ||||
|     disasmWidget->Init(); | ||||
|  | @ -164,7 +175,7 @@ void GMainWindow::BootGame(std::string filename) | |||
| 
 | ||||
| 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()) | ||||
|        BootGame(filename.toLatin1().data()); | ||||
| } | ||||
|  | @ -213,18 +224,21 @@ void GMainWindow::OnOpenHotkeysDialog() | |||
| void GMainWindow::ToggleWindowMode() | ||||
| { | ||||
|     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); | ||||
|         render_window->setParent(NULL); | ||||
|         render_window->setParent(nullptr); | ||||
|         render_window->setVisible(true); | ||||
|         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(); | ||||
|         ui.horizontalLayout->addWidget(render_window); | ||||
|         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[]) | ||||
| { | ||||
|     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 app(argc, argv); | ||||
| 
 | ||||
|     GMainWindow main_window; | ||||
|     // After settings have been loaded by GMainWindow, apply the filter
 | ||||
|     log_filter.ParseFilterString(Settings::values.log_filter); | ||||
| 
 | ||||
|     main_window.show(); | ||||
|     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 | ||||
|             break_points.cpp | ||||
|             console_listener.cpp | ||||
|             emu_window.cpp | ||||
|             extended_trace.cpp | ||||
|             file_search.cpp | ||||
|             file_util.cpp | ||||
|             hash.cpp | ||||
|             key_map.cpp | ||||
|             log_manager.cpp | ||||
|             logging/filter.cpp | ||||
|             logging/text_formatter.cpp | ||||
|             logging/backend.cpp | ||||
|             math_util.cpp | ||||
|             mem_arena.cpp | ||||
|             memory_util.cpp | ||||
|  | @ -32,7 +33,7 @@ set(HEADERS | |||
|             common_funcs.h | ||||
|             common_paths.h | ||||
|             common_types.h | ||||
|             console_listener.h | ||||
|             concurrent_ring_buffer.h | ||||
|             cpu_detect.h | ||||
|             debug_interface.h | ||||
|             emu_window.h | ||||
|  | @ -44,13 +45,18 @@ set(HEADERS | |||
|             key_map.h | ||||
|             linear_disk_cache.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 | ||||
|             mem_arena.h | ||||
|             memory_util.h | ||||
|             msg_handler.h | ||||
|             platform.h | ||||
|             scm_rev.h | ||||
|             scope_exit.h | ||||
|             string_util.h | ||||
|             swap.h | ||||
|             symbols.h | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| // Licensed under GPLv2
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| 
 | ||||
|  | @ -142,7 +142,7 @@ public: | |||
| 
 | ||||
|     __forceinline BitField& operator=(T val) | ||||
|     { | ||||
|         storage = (storage & ~GetMask()) | (((StorageType)val << position) & GetMask()); | ||||
|         Assign(val); | ||||
|         return *this; | ||||
|     } | ||||
| 
 | ||||
|  | @ -151,6 +151,10 @@ public: | |||
|         return Value(); | ||||
|     } | ||||
| 
 | ||||
|     __forceinline void Assign(const T& value) { | ||||
|         storage = (storage & ~GetMask()) | (((StorageType)value << position) & GetMask()); | ||||
|     } | ||||
| 
 | ||||
|     __forceinline T Value() const | ||||
|     { | ||||
|         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: | ||||
|     // 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
 | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2013 Dolphin Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include "common/common.h" | ||||
|  | @ -180,7 +180,7 @@ void TMemCheck::Action(DebugInterface *debug_interface, u32 iValue, u32 addr, | |||
|     { | ||||
|         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(), | ||||
|                 write ? "Write" : "Read", size*8, size*2, iValue, addr, | ||||
|                 debug_interface->getDescription(addr).c_str() | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2013 Dolphin Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
|  |  | |||
|  | @ -154,7 +154,7 @@ public: | |||
|             Do(foundVersion); | ||||
| 
 | ||||
|         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); | ||||
|             return PointerWrapSection(*this, -1, title); | ||||
|         } | ||||
|  | @ -178,7 +178,14 @@ public: | |||
|         case MODE_READ:    if (memcmp(data, *ptr, size) != 0) return false; break; | ||||
|         case MODE_WRITE: memcpy(*ptr, data, size); break; | ||||
|         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?
 | ||||
|         } | ||||
|         (*ptr) += size; | ||||
|  | @ -191,7 +198,14 @@ public: | |||
|         case MODE_READ:    memcpy(data, *ptr, size); break; | ||||
|         case MODE_WRITE: memcpy(*ptr, data, size); break; | ||||
|         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?
 | ||||
|         } | ||||
|         (*ptr) += size; | ||||
|  | @ -204,11 +218,11 @@ public: | |||
|         { | ||||
|             for (auto it = x.begin(), end = x.end(); it != end; ++it) | ||||
|             { | ||||
|                 if (it->second != NULL) | ||||
|                 if (it->second != nullptr) | ||||
|                     delete it->second; | ||||
|             } | ||||
|         } | ||||
|         T *dv = NULL; | ||||
|         T *dv = nullptr; | ||||
|         DoMap(x, dv); | ||||
|     } | ||||
| 
 | ||||
|  | @ -264,11 +278,11 @@ public: | |||
|         { | ||||
|             for (auto it = x.begin(), end = x.end(); it != end; ++it) | ||||
|             { | ||||
|                 if (it->second != NULL) | ||||
|                 if (it->second != nullptr) | ||||
|                     delete it->second; | ||||
|             } | ||||
|         } | ||||
|         T *dv = NULL; | ||||
|         T *dv = nullptr; | ||||
|         DoMultimap(x, dv); | ||||
|     } | ||||
| 
 | ||||
|  | @ -320,7 +334,7 @@ public: | |||
|     template<class T> | ||||
|     void Do(std::vector<T *> &x) | ||||
|     { | ||||
|         T *dv = NULL; | ||||
|         T *dv = nullptr; | ||||
|         DoVector(x, dv); | ||||
|     } | ||||
| 
 | ||||
|  | @ -369,7 +383,7 @@ public: | |||
|     template<class T> | ||||
|     void Do(std::deque<T *> &x) | ||||
|     { | ||||
|         T *dv = NULL; | ||||
|         T *dv = nullptr; | ||||
|         DoDeque(x, dv); | ||||
|     } | ||||
| 
 | ||||
|  | @ -395,7 +409,7 @@ public: | |||
|     template<class T> | ||||
|     void Do(std::list<T *> &x) | ||||
|     { | ||||
|         T *dv = NULL; | ||||
|         T *dv = nullptr; | ||||
|         Do(x, dv); | ||||
|     } | ||||
| 
 | ||||
|  | @ -433,7 +447,7 @@ public: | |||
|         { | ||||
|             for (auto it = x.begin(), end = x.end(); it != end; ++it) | ||||
|             { | ||||
|                 if (*it != NULL) | ||||
|                 if (*it != nullptr) | ||||
|                     delete *it; | ||||
|             } | ||||
|         } | ||||
|  | @ -476,7 +490,7 @@ public: | |||
|             break; | ||||
| 
 | ||||
|         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_WRITE:    memcpy(*ptr, x.c_str(), stringLen); 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; | ||||
|     } | ||||
|  | @ -504,7 +523,11 @@ public: | |||
|         case MODE_READ:        x = (wchar_t*)*ptr; break; | ||||
|         case MODE_WRITE:    memcpy(*ptr, x.c_str(), stringLen); 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; | ||||
|     } | ||||
|  | @ -518,7 +541,7 @@ public: | |||
|     void DoClass(T *&x) { | ||||
|         if (mode == MODE_READ) | ||||
|         { | ||||
|             if (x != NULL) | ||||
|             if (x != nullptr) | ||||
|                 delete x; | ||||
|             x = new T(); | ||||
|         } | ||||
|  | @ -567,7 +590,7 @@ public: | |||
|                 { | ||||
|                     if (mode == MODE_READ) | ||||
|                     { | ||||
|                         cur->next = 0; | ||||
|                         cur->next = nullptr; | ||||
|                         list_cur = cur; | ||||
|                         if (prev) | ||||
|                             prev->next = cur; | ||||
|  | @ -586,13 +609,13 @@ public: | |||
|                 if (mode == MODE_READ) | ||||
|                 { | ||||
|                     if (prev) | ||||
|                         prev->next = 0; | ||||
|                         prev->next = nullptr; | ||||
|                     if (list_end) | ||||
|                         *list_end = prev; | ||||
|                     if (list_cur) | ||||
|                     { | ||||
|                         if (list_start == list_cur) | ||||
|                             list_start = 0; | ||||
|                             list_start = nullptr; | ||||
|                         do | ||||
|                         { | ||||
|                             LinkedListItem<T>* next = list_cur->next; | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2013 Dolphin Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
|  |  | |||
|  | @ -1,9 +1,12 @@ | |||
| // Copyright 2013 Dolphin Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common_types.h" | ||||
| #include <cstdlib> | ||||
| 
 | ||||
| #ifdef _WIN32 | ||||
| #define SLEEP(x) Sleep(x) | ||||
| #else | ||||
|  | @ -73,6 +76,8 @@ inline u64 _rotr64(u64 x, unsigned int shift){ | |||
| } | ||||
| 
 | ||||
| #else // _MSC_VER
 | ||||
| #include <locale.h> | ||||
| 
 | ||||
| // Function Cross-Compatibility
 | ||||
|     #define strcasecmp _stricmp | ||||
|     #define strncasecmp _strnicmp | ||||
|  | @ -106,7 +111,7 @@ inline u64 _rotr64(u64 x, unsigned int shift){ | |||
|             // Restore the global 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
 | ||||
|             _configthreadlocale(_ENABLE_PER_THREAD_LOCALE); | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2013 Dolphin Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
|  | @ -29,19 +29,6 @@ | |||
|     #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
 | ||||
| #define EUR_DIR "EUR" | ||||
| #define USA_DIR "USA" | ||||
|  | @ -53,6 +40,9 @@ | |||
| #define MAPS_DIR          "maps" | ||||
| #define CACHE_DIR         "cache" | ||||
| #define SDMC_DIR          "sdmc" | ||||
| #define SAVEDATA_DIR      "savedata" | ||||
| #define SYSDATA_DIR       "sysdata" | ||||
| #define SYSSAVEDATA_DIR   "syssavedata" | ||||
| #define SHADERCACHE_DIR   "shader_cache" | ||||
| #define STATESAVES_DIR    "state_saves" | ||||
| #define SCREENSHOTS_DIR   "screenShots" | ||||
|  | @ -70,6 +60,9 @@ | |||
| #define DEBUGGER_CONFIG   "debugger.ini" | ||||
| #define LOGGER_CONFIG     "logger.ini" | ||||
| 
 | ||||
| // Sys files
 | ||||
| #define SHARED_FONT       "shared_font.bin" | ||||
| 
 | ||||
| // Files in the directory returned by GetUserPath(D_LOGS_IDX)
 | ||||
| #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 double  f64; ///< 64-bit floating point
 | ||||
| 
 | ||||
| #include "common/common.h" | ||||
| 
 | ||||
| /// Union for fast 16-bit type casting
 | ||||
| union t16 { | ||||
|     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
 | ||||
| // Licensed under GPLv2
 | ||||
| // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include "emu_window.h" | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
|  |  | |||
|  | @ -82,7 +82,7 @@ static void InitSymbolPath( PSTR lpszSymbolPath, PCSTR lpszIniPath ) | |||
|     } | ||||
| 
 | ||||
|     // Add user defined path
 | ||||
|     if ( lpszIniPath != NULL ) | ||||
|     if ( lpszIniPath != nullptr ) | ||||
|         if ( lpszIniPath[0] != '\0' ) | ||||
|         { | ||||
|             strcat( lpszSymbolPath, ";" ); | ||||
|  | @ -138,7 +138,7 @@ static BOOL GetFunctionInfoFromAddresses( ULONG fnAddress, ULONG stackAddress, L | |||
|     DWORD             dwSymSize = 10000; | ||||
|     TCHAR             lpszUnDSymbol[BUFFERSIZE]=_T("?"); | ||||
|     CHAR              lpszNonUnicodeUnDSymbol[BUFFERSIZE]="?"; | ||||
|     LPTSTR            lpszParamSep = NULL; | ||||
|     LPTSTR            lpszParamSep = nullptr; | ||||
|     LPTSTR            lpszParsed = lpszUnDSymbol; | ||||
|     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
 | ||||
|         // 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; | ||||
|             for( ; ; index++ ) | ||||
|             { | ||||
|                 lpszParamSep = _tcschr( lpszParsed, _T(',') ); | ||||
|                 if ( lpszParamSep == NULL ) | ||||
|                 if ( lpszParamSep == nullptr ) | ||||
|                     break; | ||||
| 
 | ||||
|                 *lpszParamSep = _T('\0'); | ||||
|  | @ -205,7 +205,7 @@ static BOOL GetFunctionInfoFromAddresses( ULONG fnAddress, ULONG stackAddress, L | |||
|             } | ||||
| 
 | ||||
|             lpszParamSep = _tcschr( lpszParsed, _T(')') ); | ||||
|             if ( lpszParamSep != NULL ) | ||||
|             if ( lpszParamSep != nullptr ) | ||||
|             { | ||||
|                 *lpszParamSep = _T('\0'); | ||||
| 
 | ||||
|  | @ -248,7 +248,7 @@ static BOOL GetSourceInfoFromAddress( UINT address, LPTSTR lpszSourceInfo ) | |||
|         PCSTR2LPTSTR( lineInfo.FileName, lpszFileName ); | ||||
|         TCHAR fname[_MAX_FNAME]; | ||||
|         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 ); | ||||
|         ret = TRUE; | ||||
|     } | ||||
|  | @ -332,11 +332,11 @@ void StackTrace( HANDLE hThread, const char* lpszMessage, FILE *file ) | |||
|                 hProcess, | ||||
|                 hThread, | ||||
|                 &callStack, | ||||
|                 NULL, | ||||
|                 NULL, | ||||
|                 nullptr, | ||||
|                 nullptr, | ||||
|                 SymFunctionTableAccess, | ||||
|                 SymGetModuleBase, | ||||
|                 NULL); | ||||
|                 nullptr); | ||||
| 
 | ||||
|             if ( index == 0 ) | ||||
|                 continue; | ||||
|  | @ -389,11 +389,11 @@ void StackTrace(HANDLE hThread, const char* lpszMessage, FILE *file, DWORD eip, | |||
|                 hProcess, | ||||
|                 hThread, | ||||
|                 &callStack, | ||||
|                 NULL, | ||||
|                 NULL, | ||||
|                 nullptr, | ||||
|                 nullptr, | ||||
|                 SymFunctionTableAccess, | ||||
|                 SymGetModuleBase, | ||||
|                 NULL); | ||||
|                 nullptr); | ||||
| 
 | ||||
|             if ( index == 0 ) | ||||
|                 continue; | ||||
|  |  | |||
|  | @ -57,7 +57,7 @@ public: | |||
|         // advance the read pointer
 | ||||
|         m_read_ptr = m_read_ptr->next; | ||||
|         // set the next element to NULL to stop the recursive deletion
 | ||||
|         tmpptr->next = NULL; | ||||
|         tmpptr->next = nullptr; | ||||
|         delete tmpptr;    // this also deletes the element
 | ||||
|     } | ||||
| 
 | ||||
|  | @ -86,7 +86,7 @@ private: | |||
|     class ElementPtr | ||||
|     { | ||||
|     public: | ||||
|         ElementPtr() : current(NULL), next(NULL) {} | ||||
|         ElementPtr() : current(nullptr), next(nullptr) {} | ||||
| 
 | ||||
|         ~ElementPtr() | ||||
|         { | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2013 Dolphin Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2013 Dolphin Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2013 Dolphin Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| 
 | ||||
|  | @ -88,7 +88,7 @@ bool IsDirectory(const std::string &filename) | |||
| #endif | ||||
| 
 | ||||
|     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()); | ||||
|         return false; | ||||
|     } | ||||
|  | @ -100,33 +100,33 @@ bool IsDirectory(const std::string &filename) | |||
| // Doesn't supports deleting a directory
 | ||||
| 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
 | ||||
|     // being there, not the actual delete.
 | ||||
|     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; | ||||
|     } | ||||
| 
 | ||||
|     // We can't delete a directory
 | ||||
|     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; | ||||
|     } | ||||
| 
 | ||||
| #ifdef _WIN32 | ||||
|     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()); | ||||
|         return false; | ||||
|     } | ||||
| #else | ||||
|     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()); | ||||
|         return false; | ||||
|     } | ||||
|  | @ -138,17 +138,17 @@ bool Delete(const std::string &filename) | |||
| // Returns true if successful, or path already exists.
 | ||||
| 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 | ||||
|     if (::CreateDirectory(Common::UTF8ToTStr(path).c_str(), NULL)) | ||||
|     if (::CreateDirectory(Common::UTF8ToTStr(path).c_str(), nullptr)) | ||||
|         return true; | ||||
|     DWORD error = GetLastError(); | ||||
|     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; | ||||
|     } | ||||
|     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; | ||||
| #else | ||||
|     if (mkdir(path.c_str(), 0755) == 0) | ||||
|  | @ -158,11 +158,11 @@ bool CreateDir(const std::string &path) | |||
| 
 | ||||
|     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; | ||||
|     } | ||||
| 
 | ||||
|     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; | ||||
| #endif | ||||
| } | ||||
|  | @ -171,11 +171,11 @@ bool CreateDir(const std::string &path) | |||
| bool CreateFullPath(const std::string &fullPath) | ||||
| { | ||||
|     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)) | ||||
|     { | ||||
|         INFO_LOG(COMMON, "CreateFullPath: path exists %s", fullPath.c_str()); | ||||
|         LOG_WARNING(Common_Filesystem, "path exists %s", fullPath.c_str()); | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|  | @ -192,7 +192,7 @@ bool CreateFullPath(const std::string &fullPath) | |||
|         // Include the '/' so the first call is CreateDir("/") rather than CreateDir("")
 | ||||
|         std::string const subPath(fullPath.substr(0, position + 1)); | ||||
|         if (!FileUtil::IsDirectory(subPath) && !FileUtil::CreateDir(subPath)) { | ||||
|             ERROR_LOG(COMMON, "CreateFullPath: directory creation failed"); | ||||
|             LOG_ERROR(Common, "CreateFullPath: directory creation failed"); | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|  | @ -200,7 +200,7 @@ bool CreateFullPath(const std::string &fullPath) | |||
|         panicCounter--; | ||||
|         if (panicCounter <= 0) | ||||
|         { | ||||
|             ERROR_LOG(COMMON, "CreateFullPath: directory structure is too deep"); | ||||
|             LOG_ERROR(Common, "CreateFullPath: directory structure is too deep"); | ||||
|             return false; | ||||
|         } | ||||
|         position++; | ||||
|  | @ -211,12 +211,12 @@ bool CreateFullPath(const std::string &fullPath) | |||
| // Deletes a directory filename, returns true on success
 | ||||
| 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
 | ||||
|     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; | ||||
|     } | ||||
| 
 | ||||
|  | @ -227,7 +227,7 @@ bool DeleteDir(const std::string &filename) | |||
|     if (rmdir(filename.c_str()) == 0) | ||||
|         return true; | ||||
| #endif | ||||
|     ERROR_LOG(COMMON, "DeleteDir: %s: %s", filename.c_str(), GetLastErrorMsg()); | ||||
|     LOG_ERROR(Common_Filesystem, "failed %s: %s", filename.c_str(), GetLastErrorMsg()); | ||||
| 
 | ||||
|     return false; | ||||
| } | ||||
|  | @ -235,11 +235,11 @@ bool DeleteDir(const std::string &filename) | |||
| // renames file srcFilename to destFilename, returns true on success
 | ||||
| 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()); | ||||
|     if (rename(srcFilename.c_str(), destFilename.c_str()) == 0) | ||||
|         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()); | ||||
|     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
 | ||||
| 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()); | ||||
| #ifdef _WIN32 | ||||
|     if (CopyFile(Common::UTF8ToTStr(srcFilename).c_str(), Common::UTF8ToTStr(destFilename).c_str(), FALSE)) | ||||
|         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()); | ||||
|     return false; | ||||
| #else | ||||
|  | @ -267,7 +267,7 @@ bool Copy(const std::string &srcFilename, const std::string &destFilename) | |||
|     FILE *input = fopen(srcFilename.c_str(), "rb"); | ||||
|     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()); | ||||
|         return false; | ||||
|     } | ||||
|  | @ -277,7 +277,7 @@ bool Copy(const std::string &srcFilename, const std::string &destFilename) | |||
|     if (!output) | ||||
|     { | ||||
|         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()); | ||||
|         return false; | ||||
|     } | ||||
|  | @ -291,8 +291,8 @@ bool Copy(const std::string &srcFilename, const std::string &destFilename) | |||
|         { | ||||
|             if (ferror(input) != 0) | ||||
|             { | ||||
|                 ERROR_LOG(COMMON, | ||||
|                         "Copy: failed reading from source, %s --> %s: %s", | ||||
|                 LOG_ERROR(Common_Filesystem, | ||||
|                         "failed reading from source, %s --> %s: %s", | ||||
|                         srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); | ||||
|                 goto bail; | ||||
|             } | ||||
|  | @ -302,8 +302,8 @@ bool Copy(const std::string &srcFilename, const std::string &destFilename) | |||
|         int wnum = fwrite(buffer, sizeof(char), rnum, output); | ||||
|         if (wnum != rnum) | ||||
|         { | ||||
|             ERROR_LOG(COMMON, | ||||
|                     "Copy: failed writing to output, %s --> %s: %s", | ||||
|             LOG_ERROR(Common_Filesystem, | ||||
|                     "failed writing to output, %s --> %s: %s", | ||||
|                     srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); | ||||
|             goto bail; | ||||
|         } | ||||
|  | @ -326,13 +326,13 @@ u64 GetSize(const std::string &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; | ||||
|     } | ||||
| 
 | ||||
|     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; | ||||
|     } | ||||
| 
 | ||||
|  | @ -343,12 +343,12 @@ u64 GetSize(const std::string &filename) | |||
|     if (stat64(filename.c_str(), &buf) == 0) | ||||
| #endif | ||||
|     { | ||||
|         DEBUG_LOG(COMMON, "GetSize: %s: %lld", | ||||
|         LOG_TRACE(Common_Filesystem, "%s: %lld", | ||||
|                 filename.c_str(), (long long)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()); | ||||
|     return 0; | ||||
| } | ||||
|  | @ -358,7 +358,7 @@ u64 GetSize(const int fd) | |||
| { | ||||
|     struct stat64 buf; | ||||
|     if (fstat64(fd, &buf) != 0) { | ||||
|         ERROR_LOG(COMMON, "GetSize: stat failed %i: %s", | ||||
|         LOG_ERROR(Common_Filesystem, "GetSize: stat failed %i: %s", | ||||
|             fd, GetLastErrorMsg()); | ||||
|         return 0; | ||||
|     } | ||||
|  | @ -371,13 +371,13 @@ u64 GetSize(FILE *f) | |||
|     // can't use off_t here because it can be 32-bit
 | ||||
|     u64 pos = ftello(f); | ||||
|     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()); | ||||
|         return 0; | ||||
|     } | ||||
|     u64 size = ftello(f); | ||||
|     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()); | ||||
|         return 0; | ||||
|     } | ||||
|  | @ -387,11 +387,11 @@ u64 GetSize(FILE *f) | |||
| // creates an empty file filename, returns true on success
 | ||||
| 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")) | ||||
|     { | ||||
|         ERROR_LOG(COMMON, "CreateEmptyFile: failed %s: %s", | ||||
|         LOG_ERROR(Common_Filesystem, "failed %s: %s", | ||||
|                   filename.c_str(), GetLastErrorMsg()); | ||||
|         return false; | ||||
|     } | ||||
|  | @ -404,7 +404,7 @@ bool CreateEmptyFile(const std::string &filename) | |||
| // results into parentEntry. Returns the number of files+directories found
 | ||||
| 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
 | ||||
|     u32 foundEntries = 0; | ||||
| #ifdef _WIN32 | ||||
|  | @ -423,7 +423,7 @@ u32 ScanDirectoryTree(const std::string &directory, FSTEntry& parentEntry) | |||
|         FSTEntry entry; | ||||
|         const std::string virtualName(Common::TStrToUTF8(ffd.cFileName)); | ||||
| #else | ||||
|     struct dirent dirent, *result = NULL; | ||||
|     struct dirent dirent, *result = nullptr; | ||||
| 
 | ||||
|     DIR *dirp = opendir(directory.c_str()); | ||||
|     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.
 | ||||
| bool DeleteDirRecursively(const std::string &directory) | ||||
| { | ||||
|     INFO_LOG(COMMON, "DeleteDirRecursively: %s", directory.c_str()); | ||||
|     LOG_TRACE(Common_Filesystem, "%s", directory.c_str()); | ||||
| #ifdef _WIN32 | ||||
|     // Find the first file in the directory.
 | ||||
|     WIN32_FIND_DATA ffd; | ||||
|  | @ -491,7 +491,7 @@ bool DeleteDirRecursively(const std::string &directory) | |||
|     { | ||||
|         const std::string virtualName(Common::TStrToUTF8(ffd.cFileName)); | ||||
| #else | ||||
|     struct dirent dirent, *result = NULL; | ||||
|     struct dirent dirent, *result = nullptr; | ||||
|     DIR *dirp = opendir(directory.c_str()); | ||||
|     if (!dirp) | ||||
|         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(dest_path)) FileUtil::CreateFullPath(dest_path); | ||||
| 
 | ||||
|     struct dirent dirent, *result = NULL; | ||||
|     struct dirent dirent, *result = nullptr; | ||||
|     DIR *dirp = opendir(source_path.c_str()); | ||||
|     if (!dirp) return; | ||||
| 
 | ||||
|  | @ -586,11 +586,11 @@ std::string GetCurrentDir() | |||
| { | ||||
|     char *dir; | ||||
|     // 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()); | ||||
|         return NULL; | ||||
|         return nullptr; | ||||
|     } | ||||
|     std::string strDir = dir; | ||||
|     free(dir); | ||||
|  | @ -626,7 +626,7 @@ std::string& GetExeDirectory() | |||
|     if (DolphinPath.empty()) | ||||
|     { | ||||
|         TCHAR Dolphin_exe_Path[2048]; | ||||
|         GetModuleFileName(NULL, Dolphin_exe_Path, 2048); | ||||
|         GetModuleFileName(nullptr, Dolphin_exe_Path, 2048); | ||||
|         DolphinPath = Common::TStrToUTF8(Dolphin_exe_Path); | ||||
|         DolphinPath = DolphinPath.substr(0, DolphinPath.find_last_of('\\')); | ||||
|     } | ||||
|  | @ -647,7 +647,7 @@ std::string GetSysDirectory() | |||
| #endif | ||||
|     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; | ||||
| } | ||||
| 
 | ||||
|  | @ -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_CACHE_IDX]          = paths[D_USER_IDX] + CACHE_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_SHADERS_IDX]        = paths[D_USER_IDX] + SHADERS_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)) | ||||
|         { | ||||
|             WARN_LOG(COMMON, "Invalid path specified %s", newPath.c_str()); | ||||
|             LOG_ERROR(Common_Filesystem, "Invalid path specified %s", newPath.c_str()); | ||||
|             return paths[DirIDX]; | ||||
|         } | ||||
|         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_CACHE_IDX]          = paths[D_USER_IDX] + CACHE_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_SHADERS_IDX]        = paths[D_USER_IDX] + SHADERS_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]; | ||||
| } | ||||
| 
 | ||||
| //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) | ||||
| { | ||||
|     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() | ||||
|     : m_file(NULL), m_good(true) | ||||
|     : m_file(nullptr), m_good(true) | ||||
| {} | ||||
| 
 | ||||
| IOFile::IOFile(std::FILE* file) | ||||
|  | @ -834,7 +826,7 @@ IOFile::IOFile(std::FILE* file) | |||
| {} | ||||
| 
 | ||||
| IOFile::IOFile(const std::string& filename, const char openmode[]) | ||||
|     : m_file(NULL), m_good(true) | ||||
|     : m_file(nullptr), m_good(true) | ||||
| { | ||||
|     Open(filename, openmode); | ||||
| } | ||||
|  | @ -845,7 +837,7 @@ IOFile::~IOFile() | |||
| } | ||||
| 
 | ||||
| IOFile::IOFile(IOFile&& other) | ||||
|     : m_file(NULL), m_good(true) | ||||
|     : m_file(nullptr), m_good(true) | ||||
| { | ||||
|     Swap(other); | ||||
| } | ||||
|  | @ -880,14 +872,14 @@ bool IOFile::Close() | |||
|     if (!IsOpen() || 0 != std::fclose(m_file)) | ||||
|         m_good = false; | ||||
| 
 | ||||
|     m_file = NULL; | ||||
|     m_file = nullptr; | ||||
|     return m_good; | ||||
| } | ||||
| 
 | ||||
| std::FILE* IOFile::ReleaseHandle() | ||||
| { | ||||
|     std::FILE* const ret = m_file; | ||||
|     m_file = NULL; | ||||
|     m_file = nullptr; | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2013 Dolphin Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
|  | @ -27,6 +27,9 @@ enum { | |||
|     D_STATESAVES_IDX, | ||||
|     D_SCREENSHOTS_IDX, | ||||
|     D_SDMC_IDX, | ||||
|     D_SAVEDATA_IDX, | ||||
|     D_SYSDATA_IDX, | ||||
|     D_SYSSAVEDATA_IDX, | ||||
|     D_HIRESTEXTURES_IDX, | ||||
|     D_DUMP_IDX, | ||||
|     D_DUMPFRAMES_IDX, | ||||
|  | @ -202,11 +205,11 @@ public: | |||
|         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
 | ||||
|     bool IsGood() {    return m_good; } | ||||
|     operator void*() { return m_good ? m_file : NULL; } | ||||
|     operator void*() { return m_good ? m_file : nullptr; } | ||||
| 
 | ||||
|     std::FILE* ReleaseHandle(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2013 Dolphin Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2013 Dolphin Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include "key_map.h" | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2013 Dolphin Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
|  | @ -70,7 +70,7 @@ public: | |||
|             // good header, read some key/value pairs
 | ||||
|             K key; | ||||
| 
 | ||||
|             V *value = NULL; | ||||
|             V *value = nullptr; | ||||
|             u32 value_size; | ||||
|             u32 entry_number; | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										133
									
								
								src/common/log.h
									
										
									
									
									
								
							
							
						
						
									
										133
									
								
								src/common/log.h
									
										
									
									
									
								
							|  | @ -1,108 +1,12 @@ | |||
| // Copyright 2013 Dolphin Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #ifndef LOGGING | ||||
| #define LOGGING | ||||
| #endif | ||||
| 
 | ||||
| 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
 | ||||
| #include "common/common_funcs.h" | ||||
| #include "common/msg_handler.h" | ||||
| #include "common/logging/log.h" | ||||
| 
 | ||||
| #ifdef MSVC_VER | ||||
| #ifndef __func__ | ||||
|  | @ -110,29 +14,16 @@ void GenericLog(LOGTYPES_LEVELS level, LOGTYPES_TYPE type, const char*file, int | |||
| #endif | ||||
| #endif | ||||
| 
 | ||||
| // Let the compiler optimize this out
 | ||||
| #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 | ||||
| #if _DEBUG | ||||
| #define _dbg_assert_(_t_, _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__); \ | ||||
|         if (!PanicYesNo("*** Assertion (see log)***\n")) {Crash();} \ | ||||
|     } | ||||
| #define _dbg_assert_msg_(_t_, _a_, ...)\ | ||||
|     if (!(_a_)) {\ | ||||
|         ERROR_LOG(_t_, __VA_ARGS__); \ | ||||
|         LOG_CRITICAL(_t_, __VA_ARGS__); \ | ||||
|         if (!PanicYesNo(__VA_ARGS__)) {Crash();} \ | ||||
|     } | ||||
| #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_msg_(_t_, _a_, _desc_, ...) {} | ||||
| #endif // dbg_assert
 | ||||
| #endif // MAX_LOGLEVEL DEBUG
 | ||||
| #endif | ||||
| 
 | ||||
| #define _assert_(_a_) _dbg_assert_(MASTER_LOG, _a_) | ||||
| 
 | ||||
| #ifndef GEKKO | ||||
| #ifdef MSVC_VER | ||||
| #ifdef _WIN32 | ||||
| #define _assert_msg_(_t_, _a_, _fmt_, ...)        \ | ||||
|     if (!(_a_)) {\ | ||||
|         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 (!PanicYesNo(_fmt_, ##__VA_ARGS__)) {Crash();} \ | ||||
|     } | ||||
| #endif // MSVC_VER
 | ||||
| #endif // _WIN32
 | ||||
| #else // GEKKO
 | ||||
| #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
 | ||||
| // Licensed under GPLv2
 | ||||
| // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2013 Dolphin Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
|  |  | |||
|  | @ -30,7 +30,7 @@ | |||
| #endif | ||||
| 
 | ||||
| #ifdef IOS | ||||
| void* globalbase = NULL; | ||||
| void* globalbase = nullptr; | ||||
| #endif | ||||
| 
 | ||||
| #ifdef ANDROID | ||||
|  | @ -71,7 +71,7 @@ int ashmem_create_region(const char *name, size_t size) | |||
|     return fd; | ||||
| 
 | ||||
| error: | ||||
|     ERROR_LOG(MEMMAP, "NASTY ASHMEM ERROR: ret = %08x", ret); | ||||
|     LOG_ERROR(Common_Memory, "NASTY ASHMEM ERROR: ret = %08x", ret); | ||||
|     close(fd); | ||||
|     return ret; | ||||
| } | ||||
|  | @ -121,7 +121,7 @@ void MemArena::GrabLowMemSpace(size_t size) | |||
| { | ||||
| #ifdef _WIN32 | ||||
| #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); | ||||
| #endif | ||||
| #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.
 | ||||
|     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; | ||||
|     } | ||||
| #else | ||||
|  | @ -148,12 +148,12 @@ void MemArena::GrabLowMemSpace(size_t size) | |||
|         } | ||||
|         else if (errno != EEXIST) | ||||
|         { | ||||
|             ERROR_LOG(MEMMAP, "shm_open failed: %s", strerror(errno)); | ||||
|             LOG_ERROR(Common_Memory, "shm_open failed: %s", strerror(errno)); | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
|     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 | ||||
| } | ||||
| 
 | ||||
|  | @ -178,7 +178,7 @@ void *MemArena::CreateView(s64 offset, size_t size, void *base) | |||
| #ifdef _XBOX | ||||
|     size = roundup(size); | ||||
|     // 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; | ||||
| #else | ||||
|     size = roundup(size); | ||||
|  | @ -197,7 +197,7 @@ void *MemArena::CreateView(s64 offset, size_t size, void *base) | |||
| 
 | ||||
|     if (retval == MAP_FAILED) | ||||
|     { | ||||
|         NOTICE_LOG(MEMMAP, "mmap failed"); | ||||
|         LOG_ERROR(Common_Memory, "mmap failed"); | ||||
|         return nullptr; | ||||
|     } | ||||
|     return retval; | ||||
|  | @ -243,8 +243,8 @@ u8* MemArena::Find4GBBase() | |||
|     return base; | ||||
| #else | ||||
| #ifdef IOS | ||||
|     void* base = NULL; | ||||
|     if (globalbase == NULL){ | ||||
|     void* base = nullptr; | ||||
|     if (globalbase == nullptr){ | ||||
|         base = mmap(0, 0x08000000, PROT_READ | PROT_WRITE, | ||||
|             MAP_ANON | MAP_SHARED, -1, 0); | ||||
|         if (base == MAP_FAILED) { | ||||
|  | @ -357,7 +357,7 @@ bail: | |||
|         if (views[j].out_ptr_low && *views[j].out_ptr_low) | ||||
|         { | ||||
|             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) | ||||
|         { | ||||
|  | @ -369,7 +369,7 @@ bail: | |||
|                 arena->ReleaseView(*views[j].out_ptr, views[j].size); | ||||
|             } | ||||
| #endif | ||||
|             *views[j].out_ptr = NULL; | ||||
|             *views[j].out_ptr = nullptr; | ||||
|         } | ||||
|     } | ||||
|     return false; | ||||
|  | @ -415,7 +415,7 @@ u8 *MemoryMap_Setup(const MemoryView *views, int num_views, u32 flags, MemArena | |||
| #elif defined(_WIN32) | ||||
|     // Try a whole range of possible bases. Return once we got a valid one.
 | ||||
|     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) | ||||
|     { | ||||
|  | @ -423,7 +423,7 @@ u8 *MemoryMap_Setup(const MemoryView *views, int num_views, u32 flags, MemArena | |||
|         base = (u8 *)base_addr; | ||||
|         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; | ||||
|             break; | ||||
|         } | ||||
|  | @ -442,7 +442,7 @@ u8 *MemoryMap_Setup(const MemoryView *views, int num_views, u32 flags, MemArena | |||
|     u8 *base = MemArena::Find4GBBase(); | ||||
|     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."); | ||||
|         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); | ||||
|         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); | ||||
|         *views[i].out_ptr = NULL; | ||||
|         *views[i].out_ptr = nullptr; | ||||
|         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
 | ||||
| // Licensed under GPLv2
 | ||||
| // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // 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,
 | ||||
|     //    (unsigned long)size);
 | ||||
| 
 | ||||
|     if (ptr == NULL) | ||||
|     if (ptr == nullptr) | ||||
|         PanicAlert("Failed to allocate raw memory"); | ||||
| 
 | ||||
|     return ptr; | ||||
|  | @ -104,19 +104,19 @@ void* AllocateAlignedMemory(size_t size,size_t alignment) | |||
| #ifdef _WIN32 | ||||
|     void* ptr =  _aligned_malloc(size,alignment); | ||||
| #else | ||||
|     void* ptr = NULL; | ||||
|     void* ptr = nullptr; | ||||
| #ifdef ANDROID | ||||
|     ptr = memalign(alignment, size); | ||||
| #else | ||||
|     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 | ||||
| 
 | ||||
|     // printf("Mapped memory at %p (size %ld)\n", ptr,
 | ||||
|     //    (unsigned long)size);
 | ||||
| 
 | ||||
|     if (ptr == NULL) | ||||
|     if (ptr == nullptr) | ||||
|         PanicAlert("Failed to allocate aligned memory"); | ||||
| 
 | ||||
|     return ptr; | ||||
|  | @ -130,7 +130,7 @@ void FreeMemoryPages(void* ptr, size_t size) | |||
| 
 | ||||
|         if (!VirtualFree(ptr, 0, MEM_RELEASE)) | ||||
|             PanicAlert("FreeMemoryPages failed!\n%s", GetLastErrorMsg()); | ||||
|         ptr = NULL; // Is this our responsibility?
 | ||||
|         ptr = nullptr; // Is this our responsibility?
 | ||||
| 
 | ||||
| #else | ||||
|         munmap(ptr, size); | ||||
|  | @ -184,7 +184,7 @@ std::string MemUsage() | |||
|     // Print information about the memory usage of the process.
 | ||||
| 
 | ||||
|     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))) | ||||
|         Ret = Common::StringFromFormat("%s K", Common::ThousandSeparate(pmc.WorkingSetSize / 1024, 7).c_str()); | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2013 Dolphin Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2013 Dolphin Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include "common/common.h" | ||||
|  | @ -23,9 +23,9 @@ const char* GetLastErrorMsg() | |||
| #ifdef _WIN32 | ||||
|     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), | ||||
|         err_str, buff_size, NULL); | ||||
|         err_str, buff_size, nullptr); | ||||
| #else | ||||
|     static __thread char err_str[buff_size] = {}; | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2013 Dolphin Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #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); | ||||
|     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
 | ||||
|     if (msg_handler && (AlertEnabled || Style == QUESTION || Style == CRITICAL)) | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2013 Dolphin Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
|  |  | |||
|  | @ -80,7 +80,7 @@ | |||
| inline struct tm* localtime_r(const time_t *clock, struct tm *result) { | ||||
|     if (localtime_s(result, clock) == 0) | ||||
|         return result; | ||||
|     return NULL; | ||||
|     return nullptr; | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #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
 | ||||
| // Licensed under GPLv2
 | ||||
| // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <algorithm> | ||||
| #include <boost/range/algorithm.hpp> | ||||
| 
 | ||||
| #include "common/common.h" | ||||
| #include "common/string_util.h" | ||||
|  | @ -18,20 +18,20 @@ namespace Common { | |||
| 
 | ||||
| /// Make a string lowercase
 | ||||
| std::string ToLower(std::string str) { | ||||
|     std::transform(str.begin(), str.end(), str.begin(), ::tolower); | ||||
|     boost::transform(str, str.begin(), ::tolower); | ||||
|     return str; | ||||
| } | ||||
| 
 | ||||
| /// Make a string uppercase
 | ||||
| std::string ToUpper(std::string str) { | ||||
|     std::transform(str.begin(), str.end(), str.begin(), ::toupper); | ||||
|     boost::transform(str, str.begin(), ::toupper); | ||||
|     return str; | ||||
| } | ||||
| 
 | ||||
| // faster than sscanf
 | ||||
| bool AsciiToHex(const char* _szValue, u32& result) | ||||
| { | ||||
|     char *endptr = NULL; | ||||
|     char *endptr = nullptr; | ||||
|     const u32 value = strtoul(_szValue, &endptr, 16); | ||||
| 
 | ||||
|     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.
 | ||||
|     //
 | ||||
|     // 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) | ||||
|         c_locale = _create_locale(LC_ALL, ".1252"); | ||||
|     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, ...) | ||||
| { | ||||
|     va_list args; | ||||
|     char *buf = NULL; | ||||
|     char *buf = nullptr; | ||||
| #ifdef _WIN32 | ||||
|     int required = 0; | ||||
| 
 | ||||
|  | @ -107,7 +107,7 @@ std::string StringFromFormat(const char* format, ...) | |||
| #else | ||||
|     va_start(args, format); | ||||
|     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); | ||||
| 
 | ||||
|     std::string temp = buf; | ||||
|  | @ -162,7 +162,7 @@ std::string StripQuotes(const std::string& s) | |||
| 
 | ||||
| bool TryParse(const std::string &str, u32 *const output) | ||||
| { | ||||
|     char *endptr = NULL; | ||||
|     char *endptr = nullptr; | ||||
| 
 | ||||
|     // Reset errno to a value other than ERANGE
 | ||||
|     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); | ||||
|     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); | ||||
|         return {}; | ||||
|     } | ||||
|  | @ -510,7 +510,7 @@ static std::string CodeToUTF8(const char* fromcode, const std::basic_string<T>& | |||
|             } | ||||
|             else | ||||
|             { | ||||
|                 ERROR_LOG(COMMON, "iconv failure [%s]: %s", fromcode, strerror(errno)); | ||||
|                 LOG_ERROR(Common, "iconv failure [%s]: %s", fromcode, strerror(errno)); | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|  | @ -528,10 +528,10 @@ std::u16string UTF8ToUTF16(const std::string& input) | |||
| { | ||||
|     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) | ||||
|     { | ||||
|         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); | ||||
|         return {}; | ||||
|     } | ||||
|  | @ -566,7 +566,7 @@ std::u16string UTF8ToUTF16(const std::string& input) | |||
|             } | ||||
|             else | ||||
|             { | ||||
|                 ERROR_LOG(COMMON, "iconv failure [UTF-8]: %s", strerror(errno)); | ||||
|                 LOG_ERROR(Common, "iconv failure [UTF-8]: %s", strerror(errno)); | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|  | @ -582,7 +582,7 @@ std::u16string UTF8ToUTF16(const std::string& 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) | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2013 Dolphin Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
|  | @ -115,4 +115,19 @@ inline std::string UTF8ToTStr(const std::string& str) | |||
| 
 | ||||
| #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
 | ||||
| // Licensed under GPLv2
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include "common/symbols.h" | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2013 Dolphin Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include "common/thread.h" | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2013 Dolphin Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
|  | @ -21,6 +21,7 @@ | |||
| //for gettimeofday and struct time(spec|val)
 | ||||
| #include <time.h> | ||||
| #include <sys/time.h> | ||||
| #include <unistd.h> | ||||
| #endif | ||||
| 
 | ||||
| namespace Common | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2014 Citra Emulator Project / PPSSPP Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
|  | @ -37,7 +37,7 @@ struct ThreadQueueList { | |||
|     ~ThreadQueueList() { | ||||
|         for (int i = 0; i < NUM_QUEUES; ++i) | ||||
|         { | ||||
|             if (queues[i].data != NULL) | ||||
|             if (queues[i].data != nullptr) | ||||
|                 free(queues[i].data); | ||||
|         } | ||||
|     } | ||||
|  | @ -46,7 +46,7 @@ struct ThreadQueueList { | |||
|     int contains(const IdType uid) { | ||||
|         for (int i = 0; i < NUM_QUEUES; ++i) | ||||
|         { | ||||
|             if (queues[i].data == NULL) | ||||
|             if (queues[i].data == nullptr) | ||||
|                 continue; | ||||
| 
 | ||||
|             Queue *cur = &queues[i]; | ||||
|  | @ -133,7 +133,7 @@ struct ThreadQueueList { | |||
|     inline void clear() { | ||||
|         for (int i = 0; i < NUM_QUEUES; ++i) | ||||
|         { | ||||
|             if (queues[i].data != NULL) | ||||
|             if (queues[i].data != nullptr) | ||||
|                 free(queues[i].data); | ||||
|         } | ||||
|         memset(queues, 0, sizeof(queues)); | ||||
|  | @ -147,7 +147,7 @@ struct ThreadQueueList { | |||
| 
 | ||||
|     inline void prepare(u32 priority) { | ||||
|         Queue *cur = &queues[priority]; | ||||
|         if (cur->next == NULL) | ||||
|         if (cur->next == nullptr) | ||||
|             link(priority, INITIAL_CAPACITY); | ||||
|     } | ||||
| 
 | ||||
|  | @ -176,7 +176,7 @@ private: | |||
| 
 | ||||
|         for (int i = (int) priority - 1; i >= 0; --i) | ||||
|         { | ||||
|             if (queues[i].next != NULL) | ||||
|             if (queues[i].next != nullptr) | ||||
|             { | ||||
|                 cur->next = queues[i].next; | ||||
|                 queues[i].next = cur; | ||||
|  | @ -193,7 +193,7 @@ private: | |||
|         int size = cur->end - cur->first; | ||||
|         if (size >= cur->capacity - 2)  { | ||||
|             IdType *new_data = (IdType *)realloc(cur->data, cur->capacity * 2 * sizeof(IdType)); | ||||
|             if (new_data != NULL)  { | ||||
|             if (new_data != nullptr)  { | ||||
|                 cur->capacity *= 2; | ||||
|                 cur->data = new_data; | ||||
|             } | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2013 Dolphin Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2013 Dolphin Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <time.h> | ||||
|  | @ -25,7 +25,7 @@ u32 Timer::GetTimeMs() | |||
|     return timeGetTime(); | ||||
| #else | ||||
|     struct timeval t; | ||||
|     (void)gettimeofday(&t, NULL); | ||||
|     (void)gettimeofday(&t, nullptr); | ||||
|     return ((u32)(t.tv_sec * 1000 + t.tv_usec / 1000)); | ||||
| #endif | ||||
| } | ||||
|  | @ -183,7 +183,7 @@ std::string Timer::GetTimeFormatted() | |||
|     return StringFromFormat("%s:%03i", tmp, tp.millitm); | ||||
| #else | ||||
|     struct timeval t; | ||||
|     (void)gettimeofday(&t, NULL); | ||||
|     (void)gettimeofday(&t, nullptr); | ||||
|     return StringFromFormat("%s:%03d", tmp, (int)(t.tv_usec / 1000)); | ||||
| #endif | ||||
| } | ||||
|  | @ -197,7 +197,7 @@ double Timer::GetDoubleTime() | |||
|     (void)::ftime(&tp); | ||||
| #else | ||||
|     struct timeval t; | ||||
|     (void)gettimeofday(&t, NULL); | ||||
|     (void)gettimeofday(&t, nullptr); | ||||
| #endif | ||||
|     // Get continuous timestamp
 | ||||
|     u64 TmpSeconds = Common::Timer::GetTimeSinceJan1970(); | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2013 Dolphin Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
|  |  | |||
|  | @ -281,28 +281,28 @@ int u8_read_escape_sequence(const char *str, u32 *dest) | |||
|     do { | ||||
|       digs[dno++] = str[i++]; | ||||
|     } while (octal_digit(str[i]) && dno < 3); | ||||
|     ch = strtol(digs, NULL, 8); | ||||
|     ch = strtol(digs, nullptr, 8); | ||||
|   } | ||||
|   else if (str[0] == 'x') { | ||||
|     while (hex_digit(str[i]) && dno < 2) { | ||||
|       digs[dno++] = str[i++]; | ||||
|     } | ||||
|     if (dno > 0) | ||||
|       ch = strtol(digs, NULL, 16); | ||||
|       ch = strtol(digs, nullptr, 16); | ||||
|   } | ||||
|   else if (str[0] == 'u') { | ||||
|     while (hex_digit(str[i]) && dno < 4) { | ||||
|       digs[dno++] = str[i++]; | ||||
|     } | ||||
|     if (dno > 0) | ||||
|       ch = strtol(digs, NULL, 16); | ||||
|       ch = strtol(digs, nullptr, 16); | ||||
|   } | ||||
|   else if (str[0] == 'U') { | ||||
|     while (hex_digit(str[i]) && dno < 8) { | ||||
|       digs[dno++] = str[i++]; | ||||
|     } | ||||
|     if (dno > 0) | ||||
|       ch = strtol(digs, NULL, 16); | ||||
|       ch = strtol(digs, nullptr, 16); | ||||
|   } | ||||
|   *dest = ch; | ||||
| 
 | ||||
|  | @ -353,7 +353,7 @@ const char *u8_strchr(const char *s, u32 ch, int *charn) | |||
|     lasti = i; | ||||
|     (*charn)++; | ||||
|   } | ||||
|   return NULL; | ||||
|   return nullptr; | ||||
| } | ||||
| 
 | ||||
| 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; | ||||
|     (*charn)++; | ||||
|   } | ||||
|   return NULL; | ||||
|   return nullptr; | ||||
| } | ||||
| 
 | ||||
| int u8_is_locale_utf8(const char *locale) | ||||
|  | @ -419,35 +419,35 @@ bool UTF8StringHasNonASCII(const char *utf8string) { | |||
| 
 | ||||
| std::string ConvertWStringToUTF8(const wchar_t *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; | ||||
|     s.resize(size); | ||||
|     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; | ||||
| } | ||||
| 
 | ||||
| std::string ConvertWStringToUTF8(const std::wstring &wstr) { | ||||
|     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; | ||||
|     s.resize(size); | ||||
|     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; | ||||
| } | ||||
| 
 | ||||
| void ConvertUTF8ToWString(wchar_t *dest, size_t destSize, const std::string &source) { | ||||
|     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)); | ||||
| } | ||||
| 
 | ||||
| std::wstring ConvertUTF8ToWString(const std::string &source) { | ||||
|     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; | ||||
|     str.resize(size); | ||||
|     if (size > 0) { | ||||
|  |  | |||
|  | @ -18,35 +18,46 @@ set(SRCS | |||
|             arm/skyeye_common/vfp/vfpinstr.cpp | ||||
|             arm/skyeye_common/vfp/vfpsingle.cpp | ||||
|             file_sys/archive_romfs.cpp | ||||
|             file_sys/archive_savedata.cpp | ||||
|             file_sys/archive_sdmc.cpp | ||||
|             file_sys/archive_systemsavedata.cpp | ||||
|             file_sys/disk_archive.cpp | ||||
|             file_sys/file_romfs.cpp | ||||
|             file_sys/file_sdmc.cpp | ||||
|             file_sys/directory_romfs.cpp | ||||
|             file_sys/directory_sdmc.cpp | ||||
|             hle/kernel/address_arbiter.cpp | ||||
|             hle/kernel/archive.cpp | ||||
|             hle/kernel/event.cpp | ||||
|             hle/kernel/kernel.cpp | ||||
|             hle/kernel/mutex.cpp | ||||
|             hle/kernel/semaphore.cpp | ||||
|             hle/kernel/shared_memory.cpp | ||||
|             hle/kernel/thread.cpp | ||||
|             hle/service/ac_u.cpp | ||||
|             hle/service/act_u.cpp | ||||
|             hle/service/am_app.cpp | ||||
|             hle/service/am_net.cpp | ||||
|             hle/service/apt_a.cpp | ||||
|             hle/service/apt_u.cpp | ||||
|             hle/service/boss_u.cpp | ||||
|             hle/service/cfg_i.cpp | ||||
|             hle/service/cfg_u.cpp | ||||
|             hle/service/cecd_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/dsp_dsp.cpp | ||||
|             hle/service/err_f.cpp | ||||
|             hle/service/fs_user.cpp | ||||
|             hle/service/frd_u.cpp | ||||
|             hle/service/fs/archive.cpp | ||||
|             hle/service/fs/fs_user.cpp | ||||
|             hle/service/gsp_gpu.cpp | ||||
|             hle/service/hid_user.cpp | ||||
|             hle/service/http_c.cpp | ||||
|             hle/service/ir_rst.cpp | ||||
|             hle/service/ir_u.cpp | ||||
|             hle/service/ldr_ro.cpp | ||||
|             hle/service/mic_u.cpp | ||||
|             hle/service/ndm_u.cpp | ||||
|             hle/service/news_u.cpp | ||||
|             hle/service/nim_aoc.cpp | ||||
|             hle/service/nwm_uds.cpp | ||||
|             hle/service/pm_app.cpp | ||||
|             hle/service/ptm_u.cpp | ||||
|  | @ -59,10 +70,10 @@ set(SRCS | |||
|             hle/svc.cpp | ||||
|             hw/gpu.cpp | ||||
|             hw/hw.cpp | ||||
|             hw/ndma.cpp | ||||
|             loader/elf.cpp | ||||
|             loader/loader.cpp | ||||
|             loader/ncch.cpp | ||||
|             loader/3dsx.cpp | ||||
|             core.cpp | ||||
|             core_timing.cpp | ||||
|             mem_map.cpp | ||||
|  | @ -92,39 +103,51 @@ set(HEADERS | |||
|             arm/skyeye_common/vfp/vfp.h | ||||
|             arm/skyeye_common/vfp/vfp_helper.h | ||||
|             arm/arm_interface.h | ||||
|             file_sys/archive.h | ||||
|             file_sys/archive_backend.h | ||||
|             file_sys/archive_romfs.h | ||||
|             file_sys/archive_savedata.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_sdmc.h | ||||
|             file_sys/directory.h | ||||
|             file_sys/directory_backend.h | ||||
|             file_sys/directory_romfs.h | ||||
|             file_sys/directory_sdmc.h | ||||
|             hle/kernel/address_arbiter.h | ||||
|             hle/kernel/archive.h | ||||
|             hle/kernel/event.h | ||||
|             hle/kernel/kernel.h | ||||
|             hle/kernel/mutex.h | ||||
|             hle/kernel/semaphore.h | ||||
|             hle/kernel/session.h | ||||
|             hle/kernel/shared_memory.h | ||||
|             hle/kernel/thread.h | ||||
|             hle/service/ac_u.h | ||||
|             hle/service/act_u.h | ||||
|             hle/service/am_app.h | ||||
|             hle/service/am_net.h | ||||
|             hle/service/apt_a.h | ||||
|             hle/service/apt_u.h | ||||
|             hle/service/boss_u.h | ||||
|             hle/service/cfg_i.h | ||||
|             hle/service/cfg_u.h | ||||
|             hle/service/cecd_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/dsp_dsp.h | ||||
|             hle/service/err_f.h | ||||
|             hle/service/fs_user.h | ||||
|             hle/service/frd_u.h | ||||
|             hle/service/fs/archive.h | ||||
|             hle/service/fs/fs_user.h | ||||
|             hle/service/gsp_gpu.h | ||||
|             hle/service/hid_user.h | ||||
|             hle/service/http_c.h | ||||
|             hle/service/ir_rst.h | ||||
|             hle/service/ir_u.h | ||||
|             hle/service/ldr_ro.h | ||||
|             hle/service/mic_u.h | ||||
|             hle/service/ndm_u.h | ||||
|             hle/service/news_u.h | ||||
|             hle/service/nim_aoc.h | ||||
|             hle/service/nwm_uds.h | ||||
|             hle/service/pm_app.h | ||||
|             hle/service/ptm_u.h | ||||
|  | @ -139,10 +162,10 @@ set(HEADERS | |||
|             hle/svc.h | ||||
|             hw/gpu.h | ||||
|             hw/hw.h | ||||
|             hw/ndma.h | ||||
|             loader/elf.h | ||||
|             loader/loader.h | ||||
|             loader/ncch.h | ||||
|             loader/3dsx.h | ||||
|             core.h | ||||
|             core_timing.h | ||||
|             mem_map.h | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
|  | @ -77,6 +77,12 @@ public: | |||
|      */ | ||||
|     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 | ||||
|      * @param ctx Thread context to save | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <string> | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
|  |  | |||
Some files were not shown because too many files have changed in this diff Show more
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue