Fix merge conflicts

This commit is contained in:
darkf 2014-12-29 19:47:41 -08:00
commit 8ba9ac0f74
288 changed files with 17413 additions and 13969 deletions

16
.gitignore vendored
View File

@ -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
View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -4,6 +4,11 @@ os:
language: cpp
cache:
apt: true
directories:
- .homebrew-cache
before_install:
- sh .travis-deps.sh

View File

@ -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)

View File

@ -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 {

View File

@ -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.

View File

@ -1,17 +1,31 @@
citra emulator
Citra Emulator
==============
[![Travis CI Build Status](https://travis-ci.org/citra-emu/citra.svg)](https://travis-ci.org/citra-emu/citra)
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

@ -0,0 +1 @@
Subproject commit 97052c28acb141dbf3c5e14114af99045344b695

1
externals/nihstro vendored Submodule

@ -0,0 +1 @@
Subproject commit fc71f8684d26ccf277ad68809c8bd7273141fe89

View File

@ -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})

View File

@ -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;
}

View File

@ -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() {

View File

@ -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();

View File

@ -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
)";
}

View File

@ -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, &current_size.first, &current_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);

View File

@ -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

View File

@ -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)

View File

@ -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;

View File

@ -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() {

View File

@ -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();

View File

@ -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???

View File

@ -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()

View File

@ -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"

View File

@ -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

View 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"));
}
}

View 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;
};

View 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;
};

View File

@ -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"));
}
}

View File

@ -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;
};

View 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);
}

View 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;
};

View File

@ -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;
}

View File

@ -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();
}

View 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;
}

View 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;
};

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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"

View File

@ -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)

View 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

View File

@ -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
}

View File

@ -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;
};

View File

@ -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.

View File

@ -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"

View File

@ -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

View File

@ -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;

View File

@ -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()
{

View File

@ -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.

View File

@ -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

View File

@ -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;
}

View File

@ -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();

View File

@ -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.

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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
}

View File

@ -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();
};

View 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);
}
}
}

View 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();
}

View 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)]);
}
}

View 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
View 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__)

View 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);
}
}
}
}
}

View 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
View 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

View File

@ -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.

View File

@ -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

View File

@ -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;
}
}

View File

@ -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());

View File

@ -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

View File

@ -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] = {};

View File

@ -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))

View File

@ -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

View File

@ -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

View File

@ -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
View 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)

View File

@ -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)

View File

@ -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');
}
}

View File

@ -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"

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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();

View File

@ -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

View File

@ -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) {

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -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