From 27474060e1287a67c45cd790d29b9095b35b2bdf Mon Sep 17 00:00:00 2001 From: ShizZy Date: Thu, 29 Aug 2013 23:35:09 -0400 Subject: adding initial project layout --- src/CMakeLists.txt | 7 + src/akiru/CMakeLists.txt | 12 + src/akiru/akiru.rc | Bin 0 -> 3284 bytes src/akiru/akiru.vcxproj | 208 ++++++++++++ src/akiru/akiru.vcxproj.filters | 34 ++ src/akiru/resource.h | Bin 0 -> 898 bytes src/akiru/src/akiru.cpp | 124 ++++++++ src/akiru/src/akiru.h | 35 +++ src/akiru/src/emuwindow/emuwindow_glfw.cpp | 100 ++++++ src/akiru/src/emuwindow/emuwindow_glfw.h | 56 ++++ src/akiru/src/version.h | 6 + src/common/CMakeLists.txt | 11 + src/common/common.vcxproj | 188 +++++++++++ src/common/common.vcxproj.filters | 35 +++ src/common/src/atomic.h | 36 +++ src/common/src/atomic_gcc.h | 72 +++++ src/common/src/atomic_win32.h | 76 +++++ src/common/src/common.h | 246 +++++++++++++++ src/common/src/config.cpp | 118 +++++++ src/common/src/config.h | 345 ++++++++++++++++++++ src/common/src/crc.cpp | 86 +++++ src/common/src/crc.h | 81 +++++ src/common/src/file_utils.cpp | 451 ++++++++++++++++++++++++++ src/common/src/file_utils.h | 90 ++++++ src/common/src/hash.cpp | 241 ++++++++++++++ src/common/src/hash.h | 46 +++ src/common/src/hash_container.h | 116 +++++++ src/common/src/log.cpp | 152 +++++++++ src/common/src/log.h | 216 +++++++++++++ src/common/src/misc_utils.cpp | 91 ++++++ src/common/src/misc_utils.h | 58 ++++ src/common/src/platform.h | 135 ++++++++ src/common/src/std_condition_variable.h | 152 +++++++++ src/common/src/std_mutex.h | 354 +++++++++++++++++++++ src/common/src/std_thread.h | 309 ++++++++++++++++++ src/common/src/timer.cpp | 46 +++ src/common/src/timer.h | 51 +++ src/common/src/types.h | 119 +++++++ src/common/src/x86_utils.cpp | 236 ++++++++++++++ src/common/src/x86_utils.h | 92 ++++++ src/common/src/xml.cpp | 487 +++++++++++++++++++++++++++++ src/common/src/xml.h | 41 +++ src/core/CMakeLists.txt | 10 + src/core/core.vcxproj | 186 +++++++++++ 44 files changed, 5555 insertions(+) create mode 100644 src/CMakeLists.txt create mode 100644 src/akiru/CMakeLists.txt create mode 100644 src/akiru/akiru.rc create mode 100644 src/akiru/akiru.vcxproj create mode 100644 src/akiru/akiru.vcxproj.filters create mode 100644 src/akiru/resource.h create mode 100644 src/akiru/src/akiru.cpp create mode 100644 src/akiru/src/akiru.h create mode 100644 src/akiru/src/emuwindow/emuwindow_glfw.cpp create mode 100644 src/akiru/src/emuwindow/emuwindow_glfw.h create mode 100644 src/akiru/src/version.h create mode 100644 src/common/CMakeLists.txt create mode 100644 src/common/common.vcxproj create mode 100644 src/common/common.vcxproj.filters create mode 100644 src/common/src/atomic.h create mode 100644 src/common/src/atomic_gcc.h create mode 100644 src/common/src/atomic_win32.h create mode 100644 src/common/src/common.h create mode 100644 src/common/src/config.cpp create mode 100644 src/common/src/config.h create mode 100644 src/common/src/crc.cpp create mode 100644 src/common/src/crc.h create mode 100644 src/common/src/file_utils.cpp create mode 100644 src/common/src/file_utils.h create mode 100644 src/common/src/hash.cpp create mode 100644 src/common/src/hash.h create mode 100644 src/common/src/hash_container.h create mode 100644 src/common/src/log.cpp create mode 100644 src/common/src/log.h create mode 100644 src/common/src/misc_utils.cpp create mode 100644 src/common/src/misc_utils.h create mode 100644 src/common/src/platform.h create mode 100644 src/common/src/std_condition_variable.h create mode 100644 src/common/src/std_mutex.h create mode 100644 src/common/src/std_thread.h create mode 100644 src/common/src/timer.cpp create mode 100644 src/common/src/timer.h create mode 100644 src/common/src/types.h create mode 100644 src/common/src/x86_utils.cpp create mode 100644 src/common/src/x86_utils.h create mode 100644 src/common/src/xml.cpp create mode 100644 src/common/src/xml.h create mode 100644 src/core/CMakeLists.txt create mode 100644 src/core/core.vcxproj (limited to 'src') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 00000000..62962f28 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,7 @@ +add_subdirectory(common) +add_subdirectory(core) +add_subdirectory(akiru) + +if(QT4_FOUND AND QT_QTCORE_FOUND AND QT_QTGUI_FOUND AND QT_QTOPENGL_FOUND AND NOT DISABLE_QT4) + add_subdirectory(akiru_qt) +endif() diff --git a/src/akiru/CMakeLists.txt b/src/akiru/CMakeLists.txt new file mode 100644 index 00000000..9517b11a --- /dev/null +++ b/src/akiru/CMakeLists.txt @@ -0,0 +1,12 @@ +set(SRCS src/akiru.cpp + src/emuwindow/emuwindow_glfw.cpp) + +# 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(akiru ${SRCS}) +target_link_libraries(akiru core common ${OPENGL_LIBRARIES} ${GLFW_LIBRARIES} GLEW rt ${X11_Xrandr_LIB} ${X11_xv86vmode_LIB}) + +#install(TARGETS akiru RUNTIME DESTINATION ${bindir}) diff --git a/src/akiru/akiru.rc b/src/akiru/akiru.rc new file mode 100644 index 00000000..db4fc80b Binary files /dev/null and b/src/akiru/akiru.rc differ diff --git a/src/akiru/akiru.vcxproj b/src/akiru/akiru.vcxproj new file mode 100644 index 00000000..f5ab8671 --- /dev/null +++ b/src/akiru/akiru.vcxproj @@ -0,0 +1,208 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {CE7D2C07-21CE-4590-81AB-2ADA88A2B85F} + Win32Proj + emu_win32 + akiru + + + + Application + true + + + Application + true + + + Application + false + + + Application + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + msvcrtd.lib;msvcrt.lib + + + + + + + + + + + + + + + + + msvcrtd.lib;msvcrt.lib + + + + + + + + + + + libcmt.lib + + + + + + + + + + + + + + + + + + + + + + + + + + libcmt.lib + + + + + + + + + + + + + + + + + + {dfe335fc-755d-4baa-8452-94434f8a1edb} + true + true + false + true + false + + + {8aea7f29-3466-4786-a10d-6a4bd0610977} + true + true + false + true + false + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/akiru/akiru.vcxproj.filters b/src/akiru/akiru.vcxproj.filters new file mode 100644 index 00000000..2fc1128a --- /dev/null +++ b/src/akiru/akiru.vcxproj.filters @@ -0,0 +1,34 @@ + + + + + + emuwindow + + + emuwindow + + + + + + + emuwindow + + + + emuwindow + + + + + + + + + + + {e3161526-9f53-4670-8dae-2be81ff01bc2} + + + \ No newline at end of file diff --git a/src/akiru/resource.h b/src/akiru/resource.h new file mode 100644 index 00000000..0d42c8a8 Binary files /dev/null and b/src/akiru/resource.h differ diff --git a/src/akiru/src/akiru.cpp b/src/akiru/src/akiru.cpp new file mode 100644 index 00000000..0d15c5b2 --- /dev/null +++ b/src/akiru/src/akiru.cpp @@ -0,0 +1,124 @@ +/*! + * Copyright (C) 2013 Akiru Emulator + * + * @file akiry.cpp + * @author ShizZy + * @date 2012-02-11 + * @brief Main entry point + * + * @section LICENSE + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details at + * http://www.gnu.org/copyleft/gpl.html + * + * Official project repository can be found at: + * http://code.google.com/p/gekko-gc-emu/ + */ + +#include "common.h" +#include "platform.h" + +#if EMU_PLATFORM == PLATFORM_LINUX +#include +#endif + +#include "config.h" +#include "xml.h" +#include "x86_utils.h" + +//#include "core.h" +//#include "dvd/loader.h" +//#include "powerpc/cpu_core.h" +//#include "hw/hw.h" +//#include "video_core.h" + +#include "emuwindow/emuwindow_glfw.h" + +#include "akiru.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// This is needed to fix SDL in certain build environments +#ifdef main +#undef main +#endif + +//#define PLAY_FIFO_RECORDING + +/// Application entry point +int __cdecl main(int argc, char **argv) { + u32 tight_loop; + + LOG_NOTICE(TMASTER, APP_NAME " starting...\n"); + + char program_dir[MAX_PATH]; + _getcwd(program_dir, MAX_PATH-1); + size_t cwd_len = strlen(program_dir); + program_dir[cwd_len] = '/'; + program_dir[cwd_len+1] = '\0'; + + common::ConfigManager config_manager; + config_manager.set_program_dir(program_dir, MAX_PATH); + config_manager.ReloadConfig(NULL); + core::SetConfigManager(&config_manager); + + EmuWindow_GLFW* emu_window = new EmuWindow_GLFW; + + if (E_OK != core::Init(emu_window)) { + LOG_ERROR(TMASTER, "core initialization failed, exiting..."); + core::Kill(); + exit(1); + } + +#ifndef PLAY_FIFO_RECORDING + // Load a game or die... + if (E_OK == dvd::LoadBootableFile(common::g_config->default_boot_file())) { + if (common::g_config->enable_auto_boot()) { + core::Start(); + } else { + LOG_ERROR(TMASTER, "Autoboot required in no-GUI mode... Exiting!\n"); + } + } else { + LOG_ERROR(TMASTER, "Failed to load a bootable file... Exiting!\n"); + exit(E_ERR); + } + // run the game + while(core::SYS_DIE != core::g_state) { + if (core::SYS_RUNNING == core::g_state) { + if(!(cpu->is_on)) { + cpu->Start(); // Initialize and start CPU. + } else { + for(tight_loop = 0; tight_loop < 10000; ++tight_loop) { + cpu->execStep(); + } + } + } else if (core::SYS_HALTED == core::g_state) { + core::Stop(); + } + } + core::Kill(); +#else + // load fifo log and replay it + + // TODO: Restructure initialization process - Fix Flipper_Open being called from dvd loaders (wtf?) + Flipper_Open(); + video_core::Start(emu_window); + core::SetState(core::SYS_RUNNING); + + fifo_player::FPFile file; + fifo_player::Load("/home/tony/20_frames.gff", file); + fifo_player::PlayFile(file); + + // TODO: Wait for video core to finish - PlayFile should handle this + while (1); +#endif + delete emu_window; + + return E_OK; +} diff --git a/src/akiru/src/akiru.h b/src/akiru/src/akiru.h new file mode 100644 index 00000000..a3b21092 --- /dev/null +++ b/src/akiru/src/akiru.h @@ -0,0 +1,35 @@ +/*! + * Copyright (C) 2005-2012 Gekko Emulator + * + * \file pcafe.h + * \author ShizZy + * \date 2012-02-11 + * \brief Main entry point + * + * \section LICENSE + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details at + * http://www.gnu.org/copyleft/gpl.html + * + * Official project repository can be found at: + * http://code.google.com/p/gekko-gc-emu/ + */ + +#ifndef PCAFE_PCAFE_H_ +#define PCAFE_PCAFE_H_ + +#include "version.h" + +#define APP_NAME "gekko" +#define APP_VERSION "0.31-" VERSION +#define APP_TITLE APP_NAME " " APP_VERSION +#define COPYRIGHT "Copyright (C) 2005-2012 Gekko Team" + +#endif // PCAFE_PCAFE_H_ \ No newline at end of file diff --git a/src/akiru/src/emuwindow/emuwindow_glfw.cpp b/src/akiru/src/emuwindow/emuwindow_glfw.cpp new file mode 100644 index 00000000..884c10ad --- /dev/null +++ b/src/akiru/src/emuwindow/emuwindow_glfw.cpp @@ -0,0 +1,100 @@ +/** + * Copyright (C) 2005-2012 Gekko Emulator + * + * @file emuwindow_glfw.h + * @author ShizZy + * @date 2012-04-20 + * @brief Implementation implementation of EmuWindow class for GLFW + * + * @section LICENSE + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details at + * http://www.gnu.org/copyleft/gpl.html + * + * Official project repository can be found at: + * http://code.google.com/p/gekko-gc-emu/ + */ + +#include "common.h" +#include "video_core.h" +#include "emuwindow_glfw.h" +#include "gc_controller.h" +#include "keyboard_input/keyboard_input.h" + +static void OnKeyEvent(GLFWwindow win, int key, int action) { + EmuWindow_GLFW* emuwin = (EmuWindow_GLFW*)glfwGetWindowUserPointer(win); + input_common::GCController::GCButtonState state; + + if (action == GLFW_PRESS) { + state = input_common::GCController::PRESSED; + } else { + state = input_common::GCController::RELEASED; + } + for (int channel = 0; channel < 4 && emuwin->controller_interface(); ++channel) { + emuwin->controller_interface()->SetControllerStatus(channel, key, state); + } +} + +static void OnWindowSizeEvent(GLFWwindow win, int width, int height) { + EmuWindow_GLFW* emuwin = (EmuWindow_GLFW*)glfwGetWindowUserPointer(win); + emuwin->set_client_area_width(width); + emuwin->set_client_area_height(height); +} + +/// EmuWindow_GLFW constructor +EmuWindow_GLFW::EmuWindow_GLFW() { + // Initialize the window + if(glfwInit() != GL_TRUE) { + LOG_ERROR(TVIDEO, "Failed to initialize GLFW! Exiting..."); + exit(E_ERR); + } + glfwWindowHint(GLFW_OPENGL_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_OPENGL_VERSION_MINOR, 1); + render_window_ = glfwCreateWindow(640, 480, GLFW_WINDOWED, "gekko", 0); + + // Setup callbacks + glfwSetWindowUserPointer(render_window_, this); + glfwSetKeyCallback(render_window_, OnKeyEvent); + glfwSetWindowSizeCallback(render_window_, OnWindowSizeEvent); + + DoneCurrent(); +} + +/// EmuWindow_GLFW destructor +EmuWindow_GLFW::~EmuWindow_GLFW() { + glfwTerminate(); +} + +/// Swap buffers to display the next frame +void EmuWindow_GLFW::SwapBuffers() { + glfwSwapBuffers(render_window_); +} + +/// Polls window events +void EmuWindow_GLFW::PollEvents() { + // TODO(ShizZy): Does this belong here? This is a reasonable place to update the window title + // from the main thread, but this should probably be in an event handler... + static char title[128]; + sprintf(title, "%s (FPS: %02.02f)", window_title_.c_str(), + video_core::g_renderer->current_fps()); + glfwSetWindowTitle(render_window_, title); + + glfwPollEvents(); +} + +/// Makes the GLFW OpenGL context current for the caller thread +void EmuWindow_GLFW::MakeCurrent() { + glfwMakeContextCurrent(render_window_); +} + +/// Releases (dunno if this is the "right" word) the GLFW context from the caller thread +void EmuWindow_GLFW::DoneCurrent() { + glfwMakeContextCurrent(NULL); +} diff --git a/src/akiru/src/emuwindow/emuwindow_glfw.h b/src/akiru/src/emuwindow/emuwindow_glfw.h new file mode 100644 index 00000000..1256cabe --- /dev/null +++ b/src/akiru/src/emuwindow/emuwindow_glfw.h @@ -0,0 +1,56 @@ +/** + * Copyright (C) 2005-2012 Gekko Emulator + * + * @file emuwindow_glfw.h + * @author ShizZy + * @date 2012-04-20 + * @brief Implementation implementation of EmuWindow class for GLFW + * + * @section LICENSE + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details at + * http://www.gnu.org/copyleft/gpl.html + * + * Official project repository can be found at: + * http://code.google.com/p/gekko-gc-emu/ + */ + +#ifndef VIDEO_CORE_EMUWINDOW_GLFW_ +#define VIDEO_CORE_EMUWINDOW_GLFW_ + +#include +#include + +#include "video/emuwindow.h" + +class EmuWindow_GLFW : public EmuWindow { +public: + EmuWindow_GLFW(); + ~EmuWindow_GLFW(); + + /// Swap buffers to display the next frame + void SwapBuffers(); + + /// Polls window events + void PollEvents(); + + /// Makes the graphics context current for the caller thread + void MakeCurrent(); + + /// Releases (dunno if this is the "right" word) the GLFW context from the caller thread + void DoneCurrent(); + + GLFWwindow render_window_; ///< Internal GLFW render window + +private: + +}; + +#endif // VIDEO_CORE_EMUWINDOW_GLFW_ diff --git a/src/akiru/src/version.h b/src/akiru/src/version.h new file mode 100644 index 00000000..07b88c64 --- /dev/null +++ b/src/akiru/src/version.h @@ -0,0 +1,6 @@ +// GENERATED - Do not edit! +#ifndef VERSION_H_ +#define VERSION_H_ +#define __BUILD__ "122" +#define VERSION __BUILD__ +#endif // VERSION_H_ diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt new file mode 100644 index 00000000..ace0c409 --- /dev/null +++ b/src/common/CMakeLists.txt @@ -0,0 +1,11 @@ +set(SRCS src/config.cpp + src/crc.cpp + src/file_utils.cpp + src/hash.cpp + src/log.cpp + src/misc_utils.cpp + src/timer.cpp + src/x86_utils.cpp + src/xml.cpp) + +add_library(common STATIC ${SRCS}) diff --git a/src/common/common.vcxproj b/src/common/common.vcxproj new file mode 100644 index 00000000..c61ba6d9 --- /dev/null +++ b/src/common/common.vcxproj @@ -0,0 +1,188 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {DFE335FC-755D-4BAA-8452-94434F8A1EDB} + common + + + + true + StaticLibrary + + + StaticLibrary + true + + + StaticLibrary + false + + + StaticLibrary + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + + + + + + + + + + + + true + + + + + + + + + + + + + + true + true + true + + + + + + + + + + + + + + + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/common/common.vcxproj.filters b/src/common/common.vcxproj.filters new file mode 100644 index 00000000..9d2b5c51 --- /dev/null +++ b/src/common/common.vcxproj.filters @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/common/src/atomic.h b/src/common/src/atomic.h new file mode 100644 index 00000000..a6a03fee --- /dev/null +++ b/src/common/src/atomic.h @@ -0,0 +1,36 @@ +/** + * Copyright (C) 2005-2012 Gekko Emulator + * + * @file atomic.h + * @author ShizZy + * @date 2012-02-11 + * @brief Cross-platform atomic operations + * + * @section LICENSE + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details at + * http://www.gnu.org/copyleft/gpl.html + * + * Official project repository can be found at: + * http://code.google.com/p/gekko-gc-emu/ + */ + +#ifndef COMMON_ATOMIC_H_ +#define COMMON_ATOMIC_H_ + +#include "platform.h" + +#ifdef _WIN32 +#include "atomic_win32.h" +#else +#include "atomic_gcc.h" +#endif + +#endif // COMMON_ATOMIC_H_ \ No newline at end of file diff --git a/src/common/src/atomic_gcc.h b/src/common/src/atomic_gcc.h new file mode 100644 index 00000000..66092656 --- /dev/null +++ b/src/common/src/atomic_gcc.h @@ -0,0 +1,72 @@ +/** + * Copyright (C) 2005-2012 Gekko Emulator + * + * @file atomic_gcc.h + * @author ShizZy + * @date 2012-06-28 + * @brief Cross-platform atomic operations - GCC + * + * @section LICENSE + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details at + * http://www.gnu.org/copyleft/gpl.html + * + * Official project repository can be found at: + * http://code.google.com/p/gekko-gc-emu/ + */ + +#ifndef COMMON_ATOMIC_GCC_H_ +#define COMMON_ATOMIC_GCC_H_ + +#include "types.h" + +namespace common { + +inline void AtomicAdd(volatile u32& target, u32 value) { + __sync_add_and_fetch(&target, value); +} + +inline void AtomicAnd(volatile u32& target, u32 value) { + __sync_and_and_fetch(&target, value); +} + +inline void AtomicDecrement(volatile u32& target) { + __sync_add_and_fetch(&target, -1); +} + +inline void AtomicIncrement(volatile u32& target) { + __sync_add_and_fetch(&target, 1); +} + +inline u32 AtomicLoad(volatile u32& src) { + return src; +} + +inline u32 AtomicLoadAcquire(volatile u32& src) { + u32 result = src; + __asm__ __volatile__ ( "":::"memory" ); + return result; +} + +inline void AtomicOr(volatile u32& target, u32 value) { + __sync_or_and_fetch(&target, value); +} + +inline void AtomicStore(volatile u32& dest, u32 value) { + dest = value; +} + +inline void AtomicStoreRelease(volatile u32& dest, u32 value) { + __sync_lock_test_and_set(&dest, value); +} + +} // namespace + +#endif // COMMON_ATOMIC_GCC_H_ \ No newline at end of file diff --git a/src/common/src/atomic_win32.h b/src/common/src/atomic_win32.h new file mode 100644 index 00000000..9dc4506c --- /dev/null +++ b/src/common/src/atomic_win32.h @@ -0,0 +1,76 @@ +/** + * Copyright (C) 2005-2012 Gekko Emulator + * + * @file atomic_win32.h + * @author ShizZy + * @date 2012-06-28 + * @brief Cross-platform atomic operations - Windows/Visual C++ + * @remark Taken from Dolphin Emulator (http://code.google.com/p/dolphin-emu/) + * + * @section LICENSE + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details at + * http://www.gnu.org/copyleft/gpl.html + * + * Official project repository can be found at: + * http://code.google.com/p/gekko-gc-emu/ + */ + +#ifndef COMMON_ATOMIC_WIN32_H_ +#define COMMON_ATOMIC_WIN32_H_ + +#include "types.h" + +#include +#include + +namespace common { + +inline void AtomicAdd(volatile u32& target, u32 value) { + InterlockedExchangeAdd((volatile LONG*)&target, (LONG)value); +} + +inline void AtomicAnd(volatile u32& target, u32 value) { + _InterlockedAnd((volatile LONG*)&target, (LONG)value); +} + +inline void AtomicIncrement(volatile u32& target) { + InterlockedIncrement((volatile LONG*)&target); +} + +inline void AtomicDecrement(volatile u32& target) { + InterlockedDecrement((volatile LONG*)&target); +} + +inline u32 AtomicLoad(volatile u32& src) { + return src; +} + +inline u32 AtomicLoadAcquire(volatile u32& src) { + u32 result = src; + _ReadBarrier(); + return result; +} + +inline void AtomicOr(volatile u32& target, u32 value) { + _InterlockedOr((volatile LONG*)&target, (LONG)value); +} + +inline void AtomicStore(volatile u32& dest, u32 value) { + dest = value; +} +inline void AtomicStoreRelease(volatile u32& dest, u32 value) { + _WriteBarrier(); + dest = value; +} + +} // namespace + +#endif // COMMON_ATOMIC_WIN32_H_ \ No newline at end of file diff --git a/src/common/src/common.h b/src/common/src/common.h new file mode 100644 index 00000000..f12cd461 --- /dev/null +++ b/src/common/src/common.h @@ -0,0 +1,246 @@ +/*! + * Copyright (C) 2005-2012 Gekko Emulator + * + * \file common.h + * \author ShizZy + * \date 2012-02-04 + * \brief Common header for using the common library + * + * \section LICENSE + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details at + * http://www.gnu.org/copyleft/gpl.html + * + * Official project repository can be found at: + * http://code.google.com/p/gekko-gc-emu/ + */ + +#ifndef COMMON_COMMON_H_ +#define COMMON_COMMON_H_ + +#include "platform.h" + +//////////////////////////////////////////////////////////////////////////////// +// Preprocessor stuff + +#define GEKKO_QUOTE_INPLACE(x) # x +#define GEKKO_QUOTE(x) GEKKO_QUOTE_INPLACE(x) +#define __FILE__LINE__ __FILE__ "(" GEKKO_QUOTE(__LINE__) ") : " +#define GEKKO_TODO(x) __FILE__LINE__ x "\n" + +#if EMU_PLATFORM == PLATFORM_WINDOWS + +// All inline assembly is x86 right now! +#ifdef EMU_ARCHITECTURE_X86 +#define USE_INLINE_ASM_X86 +#endif // EMU_ARCHITECTURE_X86 + +#pragma warning( disable : 4786 ) //disable the truncated 255 character limit warning for debug identifiers + +#ifdef LEAK_DETECT +#define _CRTDBG_MAP_ALLOC +#define _INC_MALLOC +#include +#include +#endif // LEAK_DETECT + +#define TODO( x ) message( __FILE__LINE__" TODO : " #x "\n" ) +#define todo( x ) message( __FILE__LINE__" TODO : " #x "\n" ) + +#endif // PLATFORM_WINDOWS + +#define E_OK 0 +#define E_ERR 1 + +//////////////////////////////////////////////////////////////////////////////// +// Includes +#include + +//////////////////////////////////////////////////////////////////////////////// +// C Includes +#include +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////////////////// +// C++ Includes +#include +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////////////////// +// OS-Specific Includes +#if EMU_PLATFORM == PLATFORM_WINDOWS + #include + #include + #include + #include + #include + #include +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Big Endian bit Access (Bits numbered ascending from leftmost to rightmost) +#define BIT_0 0x80000000 +#define BIT_1 0x40000000 +#define BIT_2 0x20000000 +#define BIT_3 0x10000000 +#define BIT_4 0x8000000 +#define BIT_5 0x4000000 +#define BIT_6 0x2000000 +#define BIT_7 0x1000000 +#define BIT_8 0x800000 +#define BIT_9 0x400000 +#define BIT_10 0x200000 +#define BIT_11 0x100000 +#define BIT_12 0x80000 +#define BIT_13 0x40000 +#define BIT_14 0x20000 +#define BIT_15 0x10000 +#define BIT_16 0x8000 +#define BIT_17 0x4000 +#define BIT_18 0x2000 +#define BIT_19 0x1000 +#define BIT_20 0x800 +#define BIT_21 0x400 +#define BIT_22 0x200 +#define BIT_23 0x100 +#define BIT_24 0x80 +#define BIT_25 0x40 +#define BIT_26 0x20 +#define BIT_27 0x10 +#define BIT_28 0x8 +#define BIT_29 0x4 +#define BIT_30 0x2 +#define BIT_31 0x1 + +#define SIGNED_BIT8 ((u8) 1 << 7) +#define SIGNED_BIT16 ((u16) 1 << 15) +#define SIGNED_BIT32 ((u32) 1 << 31) +#define SIGNED_BIT64 ((u64) 1 << 63) + +// A macro to disallow the copy constructor and operator= functions +// This should be used in the private: declarations for a class +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&); \ + void operator=(const TypeName&) + +#ifdef _DEBUG + #ifndef _DEBUGSPEED + #define DEBUG_EMU + #define DEBUG_GX + #endif +#endif + +#ifdef DEBUG_EMU +#define ASSERT_T(cond,str) if((cond)) printf("#!\tERROR: ASSERTION FAILED: %s !\n", str); +#define ASSERT_F(cond,str) if(!(cond)) printf("#!\tERROR: ASSERTION FAILED: %s !\n", str); +#else +#define ASSERT_T(cond,str) +#define ASSERT_F(cond,str) +#endif + +//////////////////////////////////////////////////////////////////////////////// + +void DisplayError (char * Message, ...); + +#ifdef _MSC_VER +# ifdef LEAK_DETECT +# undef malloc +# define DEBUG_NEW new(_NORMAL_BLOCK,__FILE__, __LINE__) +# define new DEBUG_NEW +# define malloc(s) _malloc_dbg(s,_NORMAL_BLOCK,__FILE__,__LINE__) +# define realloc(p, s) _realloc_dbg(p, s, _NORMAL_BLOCK, __FILE__, __LINE__) +# define free(p) _free_dbg(p, _NORMAL_BLOCK) +# endif +# define U64(a) a ## ui64 +# define S64(a) a ## si64 +#else //gcc +# define U64(a) a ## ull +# define S64(a) a ## sll +#endif + +//////////////////////////////////////////////////////////////////////////////// + +#include "types.h" +#include "log.h" +#include "atomic.h" +#include "misc_utils.h" +#include "x86_utils.h" + +//////////////////////////////////////////////////////////////////////////////// + +__inline static s16 toSHORT(u16 x) +{ + return *(s16*)&x; +} + +__inline static f32 toFLOAT(u32 x) +{ + return *(f32*)&x; +} + +__inline static f32 toFLOATS(s32 x) +{ + return *(f32*)&x; +} + +__inline static f64 toDOUBLE(u64 x) +{ + return *(f64*)&x; +} + +typedef void(*optable)(void); +typedef void(EMU_FASTCALL *hwtable)(u32, u32*); + +//////////////////////////////////////////////////////////////////////////////// +// Fast Macros + +#define MIN(a,b) ((a)<(b)?(a):(b)) +#define MAX(a,b) ((a)>(b)?(a):(b)) + +#define CLAMP(X,min,max) (((X) > max) ? max : (((X) < min) ? min : (X))) + +__inline static u32 BSWAP24(u32 x) +{ + return (((x & 0xff0000) >> 16) | (x & 0xff00) | ((x & 0xff) << 16)); +} + +#if _MSC_VER > 1200 + +#define BSWAP16(x) _byteswap_ushort(x) +#define BSWAP32(x) _byteswap_ulong(x) +#define BSWAP64(x) _byteswap_uint64(x) + +#else +__inline static u16 BSWAP16(u16 x) +{ + return ((x)>>8) | ((x)<<8); +} + +__inline static u32 BSWAP32(u32 x) +{ + return (BSWAP16((x)&0xffff)<<16) | (BSWAP16((x)>>16)); +} + +__inline static u64 BSWAP64(u64 x) +{ + return (u64)(((u64)BSWAP32((u32)(x&0xffffffff)))<<32) | (BSWAP32((u32)(x>>32))); +} +#endif + + +#endif // COMMON_COMMON_H_ diff --git a/src/common/src/config.cpp b/src/common/src/config.cpp new file mode 100644 index 00000000..c26789bf --- /dev/null +++ b/src/common/src/config.cpp @@ -0,0 +1,118 @@ +/** + * Copyright (C) 2005-2012 Gekko Emulator + * + * @file config.cpp + * @author ShizZy + * @date 2012-02-19 + * @brief Emulator configuration class - all config settings stored here + * + * @section LICENSE + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details at + * http://www.gnu.org/copyleft/gpl.html + * + * Official project repository can be found at: + * http://code.google.com/p/gekko-gc-emu/ + */ + +#include "common.h" +#include "config.h" +#include "xml.h" + +namespace common { + +Config* g_config; + +Config::Config() { + ResolutionType default_res; + RendererConfig default_renderer_config; + + default_renderer_config.enable_wireframe = false; + default_renderer_config.enable_shaders = true; + default_renderer_config.enable_texture_dumping = false; + default_renderer_config.enable_textures = true; + default_renderer_config.anti_aliasing_mode = 0; + default_renderer_config.anistropic_filtering_mode = 0; + + default_res.width = 640; + default_res.height = 480; + + set_program_dir("", MAX_PATH); + set_enable_multicore(true); + set_enable_idle_skipping(false); + set_enable_hle(true); + set_enable_auto_boot(true); + set_enable_cheats(false); + set_default_boot_file("", MAX_PATH); + memset(dvd_image_paths_, 0, sizeof(dvd_image_paths_)); + set_enable_show_fps(true); + set_enable_dump_opcode0(false); + set_enable_pause_on_unknown_opcode(true); + set_enable_dump_gcm_reads(false); + set_enable_ipl(false); + set_powerpc_core(CPU_INTERPRETER); + set_powerpc_frequency(486); + + memset(renderer_config_, 0, sizeof(renderer_config_)); + set_renderer_config(RENDERER_OPENGL_3, default_renderer_config); + set_current_renderer(RENDERER_OPENGL_3); + + set_enable_fullscreen(false); + set_window_resolution(default_res); + set_fullscreen_resolution(default_res); + + memset(controller_ports_, 0, sizeof(controller_ports_)); + memset(mem_slots_, 0, sizeof(mem_slots_)); + + memset(patches_, 0, sizeof(patches_)); + memset(cheats_, 0, sizeof(patches_)); +} + +Config::~Config() { +} + +ConfigManager::ConfigManager() { + set_program_dir("", MAX_PATH); +} + +ConfigManager::~ConfigManager() { +} + +/** + * @brief Reload a game-specific configuration + * @param id Game id (to load game specific configuration) + */ +void ConfigManager::ReloadGameConfig(const char* id) { + char full_filename[MAX_PATH]; + sprintf(full_filename, "user/games/%s.xml", id); + common::LoadXMLConfig(*g_config, full_filename); +} + +/// Reload the userconfig file +void ConfigManager::ReloadUserConfig() { + common::LoadXMLConfig(*g_config, "userconf.xml"); +} + +/// Reload the sysconfig file +void ConfigManager::ReloadSysConfig() { + common::LoadXMLConfig(*g_config, "sysconf.xml"); +} + +/// Reload all configurations +void ConfigManager::ReloadConfig(const char* game_id) { + delete g_config; + g_config = new Config(); + g_config->set_program_dir(program_dir_, MAX_PATH); + ReloadSysConfig(); + ReloadUserConfig(); + ReloadGameConfig(game_id); +} + +} // namspace \ No newline at end of file diff --git a/src/common/src/config.h b/src/common/src/config.h new file mode 100644 index 00000000..843d0d31 --- /dev/null +++ b/src/common/src/config.h @@ -0,0 +1,345 @@ +/** + * Copyright (C) 2005-2012 Gekko Emulator + * + * @file config.h + * @author ShizZy + * @date 2012-02-11 + * @brief Emulator configuration class - all config settings stored here + * + * @section LICENSE + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details at + * http://www.gnu.org/copyleft/gpl.html + * + * Official project repository can be found at: + * http://code.google.com/p/gekko-gc-emu/ + */ + +#ifndef COMMON_CONFIG_H_ +#define COMMON_CONFIG_H_ + +#include "common.h" + +#define MAX_SEARCH_PATHS 16 ///< Maximum paths to search for files in + +/// If you need more than this... you're just lazy ;-) +#define MAX_PATCHES_PER_GAME 128 ///< Maximum patches allowed per game + +namespace common { + +/// Class for storing emulator configuration(s) +class Config { +public: + Config(); + ~Config(); + + /// Struct used for defining game-specific patches + struct Patch { + u32 address; ///< Address to patch + u32 data; ///< Data to write at the specified address + }; + + /// Struct used for configuring what is inserted in a memory slot + struct MemSlot { + u8 device; ///< Memory slot device (0 - memcard) + bool enable; ///< Enable (plugged in?) + }; + + enum Control { + BUTTON_A = 0, + BUTTON_B, + BUTTON_X, + BUTTON_Y, + TRIGGER_L, + TRIGGER_R, + BUTTON_Z, + BUTTON_START, + ANALOG_UP, + ANALOG_DOWN, + ANALOG_LEFT, + ANALOG_RIGHT, + C_UP, + C_DOWN, + C_LEFT, + C_RIGHT, + DPAD_UP, + DPAD_DOWN, + DPAD_LEFT, + DPAD_RIGHT, + NUM_CONTROLS + }; + + /// Struct used for defining a keyboard configuration for a GameCube controller + /// Reads/Writes from/to members should be atomic + struct KeyboardController { + bool enable; ///< Is the keyboard configation enabled? + int key_code[NUM_CONTROLS]; + }; + + /// Struct used for defining a joypad configuration for a GameCube controller + /// We'll make another struct in case the user wants seperate joypad config + struct JoypadController { + bool enable; ///< Is the joypad configation enabled? + int key_code[NUM_CONTROLS]; + }; + + /// Struct used for configuring what is inserted in a controller port + typedef struct { + u8 device; ///< Controller port device (0 - controller) + bool enable; ///< Enable (plugged in?) + KeyboardController keys; ///< Keyboard configuration for controller (if used) + JoypadController pads; ///< Joypad configuration for controller (if used) + } ControllerPort; + + /// Enum for supported CPU types + enum CPUCoreType { + CPU_NULL = 0, ///< No CPU core + CPU_INTERPRETER, ///< Interpreter CPU core + CPU_DYNAREC, ///< Dynamic recompiler CPU core + NUMBER_OF_CPU_CONFIGS + }; + + /// Struct used for defining a renderer configuration + struct RendererConfig { + bool enable_wireframe; + bool enable_shaders; + bool enable_texture_dumping; + bool enable_textures; + int anti_aliasing_mode; + int anistropic_filtering_mode; + } ; + + /// Struct used for configuring a screen resolution + struct ResolutionType { + int width; + int height; + }; + + /// Enum for supported video cores + enum RendererType { + RENDERER_NULL, ///< No video core + RENDERER_OPENGL_2, ///< OpenGL 2.0 core + RENDERER_OPENGL_3, ///< OpenGL 3.0 core (not implemented) + RENDERER_DIRECTX9, ///< DirectX9 core (not implemented) + RENDERER_DIRECTX10, ///< DirectX10 core (not implemented) + RENDERER_DIRECTX11, ///< DirectX11 core (not implemented) + RENDERER_SOFTWARE, ///< Software core (not implemented) + RENDERER_HARDWARE, ///< Hardware core (not implemented- this would be a driver) + NUMBER_OF_VIDEO_CONFIGS + }; + + char* program_dir() { return program_dir_; } + void set_program_dir(const char* val, size_t size) { strcpy(program_dir_, val); } + + bool enable_multicore() { return enable_multicore_; } + bool enable_idle_skipping() {return enable_idle_skipping_; } + bool enable_hle() { return enable_hle_; } + bool enable_auto_boot() { return enable_auto_boot_; } + bool enable_cheats() { return enable_cheats_; } + void set_enable_multicore(bool val) { enable_multicore_ = val; } + void set_enable_idle_skipping(bool val) {enable_idle_skipping_ = val; } + void set_enable_hle(bool val) { enable_hle_ = val; } + void set_enable_auto_boot(bool val) { enable_auto_boot_ = val; } + void set_enable_cheats(bool val) { enable_cheats_ = val; } + + char* default_boot_file() { return default_boot_file_; } + char* dvd_image_path(int path) { return dvd_image_paths_[path]; } + void set_default_boot_file(const char* val, size_t size) { strcpy(default_boot_file_, val); } + void set_dvd_image_path(int path, char* val, size_t size) { strcpy(dvd_image_paths_[path], val); } + + bool enable_show_fps() { return enable_show_fps_; } + bool enable_dump_opcode0() { return enable_dump_opcode0_; } + bool enable_pause_on_unknown_opcode() { return enable_pause_on_unknown_opcode_; } + bool enable_dump_gcm_reads() { return enable_dump_gcm_reads_; } + void set_enable_show_fps(bool val) { enable_show_fps_ = val; } + void set_enable_dump_opcode0(bool val) { enable_dump_opcode0_ = val; } + void set_enable_pause_on_unknown_opcode(bool val) { enable_pause_on_unknown_opcode_ = val; } + void set_enable_dump_gcm_reads(bool val) { enable_dump_gcm_reads_ = val; } + + bool enable_ipl() { return enable_ipl_; } + void set_enable_ipl(bool val) { enable_ipl_ = val; } + + Patch patches(int patch) { return patches_[patch]; } + Patch cheats(int cheat) { return cheats_[cheat]; } + void set_patches(int patch, Patch val) { patches_[patch] = val; } + void set_cheats(int cheat, Patch val) { cheats_[cheat] = val; } + + CPUCoreType powerpc_core() { return powerpc_core_; } + void set_powerpc_core(CPUCoreType val) { powerpc_core_ = val; } + + int powerpc_frequency() { return powerpc_frequency_; } + void set_powerpc_frequency(int val) { powerpc_frequency_ = val; } + + RendererType current_renderer() { return current_renderer_; } + void set_current_renderer(RendererType val) { current_renderer_ = val; } + + RendererConfig renderer_config(RendererType val) { return renderer_config_[val]; } + RendererConfig current_renderer_config() { return renderer_config_[current_renderer_]; } + void set_renderer_config(RendererType renderer, RendererConfig config) { + renderer_config_[renderer] = config; + } + + bool enable_fullscreen() { return enable_fullscreen_; } + void set_enable_fullscreen(bool val) { enable_fullscreen_ = val; } + + ResolutionType window_resolution() { return window_resolution_; } + ResolutionType fullscreen_resolution() { return fullscreen_resolution_; } + void set_window_resolution(ResolutionType val) { window_resolution_ = val; } + void set_fullscreen_resolution(ResolutionType val) { fullscreen_resolution_ = val; } + + // TODO: Should be const, but pending removal of some gekko_qt hacks + /*const */ControllerPort& controller_ports(int port) { return controller_ports_[port]; } + void set_controller_ports(int port, ControllerPort val) { controller_ports_[port] = val; } + + MemSlot mem_slots(int slot) { return mem_slots_[slot]; } + void set_mem_slots(int slot, MemSlot val) { mem_slots_[slot] = val; } + + /** + * @brief Gets a RenderType from a string (used from XML) + * @param renderer_str Renderer name string, see XML schema for list + * @return Corresponding RenderType + */ + static inline RendererType StringToRenderType(const char* renderer_str) { + if (E_OK == _stricmp(renderer_str, "opengl2")) { + return RENDERER_OPENGL_2; + } else if (E_OK == _stricmp(renderer_str, "opengl3")) { + return RENDERER_OPENGL_3; + } else if (E_OK == _stricmp(renderer_str, "directx9")) { + return RENDERER_DIRECTX9; + } else if (E_OK == _stricmp(renderer_str, "directx10")) { + return RENDERER_DIRECTX10; + } else if (E_OK == _stricmp(renderer_str, "directx11")) { + return RENDERER_DIRECTX11; + } else if (E_OK == _stricmp(renderer_str, "software")) { + return RENDERER_SOFTWARE; + } else if (E_OK == _stricmp(renderer_str, "hardware")) { + return RENDERER_HARDWARE; + } else { + return RENDERER_NULL; + } + } + + /** + * @brief Gets the renderer string from the type + * @param renderer Renderer to get string for + * @return Renderer string name + */ + static std::string RenderTypeToString(RendererType renderer) { + switch (renderer) { + case RENDERER_OPENGL_2: + return "opengl2"; + case RENDERER_OPENGL_3: + return "opengl3"; + case RENDERER_DIRECTX9: + return "directx9"; + case RENDERER_DIRECTX10: + return "directx10"; + case RENDERER_DIRECTX11: + return "directx11"; + case RENDERER_SOFTWARE: + return "software"; + case RENDERER_HARDWARE: + return "hardware"; + } + return "null"; + } + + /** + * @brief Gets the CPU string from the type + * @param cpu CPU to get string for + * @param cpu_str String result + * @param size Max size to write to string + */ + static std::string CPUCoreTypeToString(CPUCoreType cpu) { + switch (cpu) { + case CPU_INTERPRETER: + return "interpreter"; + case CPU_DYNAREC: + return "dynarec"; + } + return "null"; + } + +private: + char program_dir_[MAX_PATH]; + + bool enable_multicore_; + bool enable_idle_skipping_; + bool enable_hle_; + bool enable_auto_boot_; + bool enable_cheats_; + + char default_boot_file_[MAX_PATH]; + char dvd_image_paths_[MAX_SEARCH_PATHS][MAX_PATH]; + + bool enable_show_fps_; + bool enable_dump_opcode0_; + bool enable_pause_on_unknown_opcode_; + bool enable_dump_gcm_reads_; + + bool enable_ipl_; + + Patch patches_[MAX_PATCHES_PER_GAME]; + Patch cheats_[MAX_PATCHES_PER_GAME]; + + CPUCoreType powerpc_core_; + + int powerpc_frequency_; + + bool enable_fullscreen_; + + RendererType current_renderer_; + + ResolutionType window_resolution_; + ResolutionType fullscreen_resolution_; + + RendererConfig renderer_config_[NUMBER_OF_VIDEO_CONFIGS]; + + MemSlot mem_slots_[2]; + ControllerPort controller_ports_[4]; + + DISALLOW_COPY_AND_ASSIGN(Config); +}; + +class ConfigManager { +public: + ConfigManager(); + ~ConfigManager(); + + /** + * @brief Reload a game-specific configuration + * @param id Game id (to load game specific configuration) + */ + void ReloadGameConfig(const char* id); + + /// Reload the userconfig file + void ReloadUserConfig(); + + // Reload the sysconfig file + void ReloadSysConfig(); + + /// Reload all configurations + void ReloadConfig(const char* game_id); + + char* program_dir() { return program_dir_; } + + void set_program_dir(const char* val, size_t size) { strcpy(program_dir_, val); } + +private: + char program_dir_[MAX_PATH]; ///< Program directory, used for loading config files + + DISALLOW_COPY_AND_ASSIGN(ConfigManager); +}; + +extern Config* g_config; ///< Global configuration for emulator + +} // namspace + +#endif // COMMON_CONFIG_H_ diff --git a/src/common/src/crc.cpp b/src/common/src/crc.cpp new file mode 100644 index 00000000..a36dfdad --- /dev/null +++ b/src/common/src/crc.cpp @@ -0,0 +1,86 @@ +#include "common.h" +#include "crc.h" + +u32 crc32_table[4][256]; + +u32 Reflect(u32 ref, u8 Count) +{ + u32 value = 0; + + // Swap bit 0 for bit 7 + // bit 1 for bit 6, etc. + for(int i = 1; i < (Count + 1); i++) + { + if(ref & 1) + value |= 1 << (Count - i); + ref >>= 1; + } + return value; +} + +void Init_CRC32_Table() +{ + // This is the official polynomial used by CRC-32 + // in PKZip, WinZip and Ethernet. + u32 ulPolynomial = 0x04c11db7; + + // 256 values representing ASCII character codes. + for(int x = 0; x < 4; x++) + { + for(int i = 0; i <= 0xFF; i++) + { + crc32_table[x][i]=Reflect(i, 8) << 24; + for (int j = 0; j < 8; j++) + crc32_table[x][i] = (crc32_table[x][i] << 1) ^ (crc32_table[x][i] & (1 << 31) ? (ulPolynomial + (x * 8)) : 0); + crc32_table[x][i] = Reflect(crc32_table[x][i], 32); + } + } +} + +u32 GenerateCRC(u8 *StartAddr, u32 len) +{ + u32 ulCRC = -1; + + // Perform the algorithm on each character + // in the string, using the lookup table values. + for(; len > 7; len-=8) + { + ulCRC ^= *(u32 *)StartAddr; + ulCRC = crc32_table[3][((ulCRC) & 0xFF)] ^ + crc32_table[2][((ulCRC >> 8) & 0xFF)] ^ + crc32_table[1][((ulCRC >> 16) & 0xFF)] ^ + crc32_table[0][((ulCRC >> 24))]; + ulCRC ^= *(u32 *)(StartAddr + 4); + ulCRC = crc32_table[3][((ulCRC) & 0xFF)] ^ + crc32_table[2][((ulCRC >> 8) & 0xFF)] ^ + crc32_table[1][((ulCRC >> 16) & 0xFF)] ^ + crc32_table[0][((ulCRC >> 24))]; + StartAddr+=8; + } + + if(len > 3) + { + ulCRC ^= *(u32 *)StartAddr; + ulCRC = crc32_table[3][((ulCRC) & 0xFF)] ^ + crc32_table[2][((ulCRC >> 8) & 0xFF)] ^ + crc32_table[1][((ulCRC >> 16) & 0xFF)] ^ + crc32_table[0][((ulCRC >> 24))]; + StartAddr+=4; + len -= 4; + } + + switch(len) + { + case 3: + ulCRC = crc32_table[0][(ulCRC & 0xFF) ^ *StartAddr] ^ (ulCRC >> 8); + StartAddr++; + case 2: + ulCRC = crc32_table[0][(ulCRC & 0xFF) ^ *StartAddr] ^ (ulCRC >> 8); + StartAddr++; + case 1: + ulCRC = crc32_table[0][(ulCRC & 0xFF) ^ *StartAddr] ^ (ulCRC >> 8); + StartAddr++; + } + + return ulCRC; +} diff --git a/src/common/src/crc.h b/src/common/src/crc.h new file mode 100644 index 00000000..2bb3210d --- /dev/null +++ b/src/common/src/crc.h @@ -0,0 +1,81 @@ +#ifndef COMMON_CRC_H_ +#define COMMON_CRC_H_ + +#include "types.h" +#include "platform.h" + +#define CRC_ROTL(crc) crc32_table[3][((crc) & 0xFF)] ^ crc32_table[2][((crc >> 8) & 0xFF)] ^ \ + crc32_table[1][((crc >> 16) & 0xFF)] ^ crc32_table[0][((crc >> 24))] + +// Some definitions for using the X86 CRC32 instruction on different platforms. Keep in mind, you +// should check for X86/X64 architecture support before using these, as well as for SSE 4.2 (see the +// x86_utils module). + +#if defined(EMU_ARCHITECTURE_X86) || defined(EMU_ARCHITECTURE_X64) + +#if EMU_PLATFORM == PLATFORM_WINDOWS + +#include + +#ifdef EMU_ARCHITECTURE_X64 +static inline u64 InlineCrc32_U64(u64 crc, u64 value) { + return _mm_crc32_u64(crc, value); +} +#endif +static inline u32 InlineCrc32_U32(u32 crc, u64 value) { + return _mm_crc32_u32(crc, static_cast(value)); +} + +static inline u32 InlineCrc32_U8(u32 crc, u8 value) { + return _mm_crc32_u8(crc, value); +} + +#elif GCC_VERSION_AVAILABLE(4, 5) && defined(__SSE4_2__) + +extern inline unsigned int __attribute__(( + __gnu_inline__, __always_inline__, __artificial__)) +InlineCrc32_U8(unsigned int __C, unsigned char __V) { + return __builtin_ia32_crc32qi(__C, __V); +} +#ifdef EMU_ARCHITECTURE_X64 +extern inline unsigned long long __attribute__(( + __gnu_inline__, __always_inline__, __artificial__)) +InlineCrc32_U64(unsigned long long __C, unsigned long long __V) { + return __builtin_ia32_crc32di(__C, __V); +} +#else +extern inline unsigned int __attribute__(( + __gnu_inline__, __always_inline__, __artificial__)) +InlineCrc32_U32(unsigned int __C, unsigned int __V) { + return __builtin_ia32_crc32si (__C, __V); +} +#endif // EMU_ARCHITECTURE_X64 + +#else + +// GCC 4.4.x and earlier: use inline asm, or msse4.2 flag not set + +static inline u64 InlineCrc32_U64(u64 crc, u64 value) { + asm("crc32q %[value], %[crc]\n" : [crc] "+r" (crc) : [value] "rm" (value)); + return crc; +} + +static inline u32 InlineCrc32_U32(u32 crc, u64 value) { + asm("crc32l %[value], %[crc]\n" : [crc] "+r" (crc) : [value] "rm" (value)); + return crc; +} + +static inline u32 InlineCrc32_U8(u32 crc, u8 value) { + asm("crc32b %[value], %[crc]\n" : [crc] "+r" (crc) : [value] "rm" (value)); + return crc; +} +#endif + +#endif // EMU_ARCHITECTURE_X86 or EMU_ARCHITECTURE_X64 + +extern u32 crc32_table[4][256]; + +void Init_CRC32_Table(); +u32 GenerateCRC(u8 *StartAddr, u32 Len); + +#endif // COMMON_CRC_H_ diff --git a/src/common/src/file_utils.cpp b/src/common/src/file_utils.cpp new file mode 100644 index 00000000..6e792fcd --- /dev/null +++ b/src/common/src/file_utils.cpp @@ -0,0 +1,451 @@ +/** +* Copyright (C) 2005-2013 Gekko Emulator +* +* @file file_utils.cpp +* @author ShizZy +* @date 2013-01-27 +* @brief Crossplatform file utility functions +* @remark Borrowed from Dolphin Emulator +* +* @section LICENSE +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details at +* http://www.gnu.org/copyleft/gpl.html +* +* Official project repository can be found at: +* http://code.google.com/p/gekko-gc-emu/ +*/ + +#include "types.h" +#include "file_utils.h" + +#ifdef _WIN32 +#include +#include // for SHGetFolderPath +#include +#include // for GetSaveFileName +#include +#include // getcwd +#else +#include +#include +#include +#include +#include +#include +#endif + +#if defined(__APPLE__) +#include +#include +#include +#endif + +#include +#include + +#ifndef S_ISDIR +#define S_ISDIR(m) (((m)&S_IFMT) == S_IFDIR) +#endif + +#ifdef BSD4_4 +#define stat64 stat +#define fstat64 fstat +#endif + +#ifdef _MSC_VER +#define __strdup _strdup +#define __getcwd _getcwd +#define __chdir _chdir + +#define fseeko _fseeki64 +#define ftello _ftelli64 +#define atoll _atoi64 +#define stat64 _stat64 +#define fstat64 _fstat64 +#define fileno _fileno + +#else +#define __strdup strdup +#define __getcwd getcwd +#define __chdir chdir +#endif + +namespace common { + +// Remove any ending forward slashes from directory paths +// Modifies argument. +static void StripTailDirSlashes(std::string &fname) { + if (fname.length() > 1) { + size_t i = fname.length() - 1; + while (fname[i] == '/') { + fname[i--] = '\0'; + } + } +} + +// Returns true if file filename exists +bool FileExists(const std::string &filename) { + struct stat64 file_info; + std::string copy(filename); + StripTailDirSlashes(copy); + return (stat64(copy.c_str(), &file_info) == 0); +} + +// Returns true if filename is a directory +bool IsDirectory(const std::string &filename) { + struct stat64 file_info; + std::string copy(filename); + StripTailDirSlashes(copy); + if (stat64(copy.c_str(), &file_info) < 0) { + LOG_WARNING(TCOMMON, "IsDirectory: stat failed on %s", filename.c_str()); + return false; + } + return S_ISDIR(file_info.st_mode); +} + +// Deletes a given filename, return true on success +// Doesn't supports deleting a directory +bool DeleteFile(const std::string &filename) { + LOG_INFO(TCOMMON, "Delete: file %s", filename.c_str()); + // Return true because we care about the file no + // being there, not the actual delete. + if (!FileExists(filename)) { + LOG_WARNING(TCOMMON, "Delete: %s does not exists", filename.c_str()); + return true; + } + // We can't delete a directory + if (IsDirectory(filename)) { + LOG_WARNING(TCOMMON, "Delete failed: %s is a directory", filename.c_str()); + return false; + } +#ifdef _WIN32 + if (!DeleteFile(filename.c_str())) { + LOG_WARNING(TCOMMON, "Delete: DeleteFile failed on %s", filename.c_str()); + return false; + } +#else + if (unlink(filename.c_str()) == -1) { + LOG_WARNING(TCOMMON, "Delete: unlink failed on %s", filename.c_str()); + return false; + } +#endif + return true; +} + +// Returns true if successful, or path already exists. +bool CreateDir(const std::string &path) { + LOG_INFO(TCOMMON, "CreateDir: directory %s", path.c_str()); +#ifdef _WIN32 + if (::CreateDirectory(path.c_str(), NULL)) { + return true; + } + DWORD error = GetLastError(); + if (error == ERROR_ALREADY_EXISTS) + { + LOG_WARNING(TCOMMON, "CreateDir: CreateDirectory failed on %s: already exists", path.c_str()); + return true; + } + LOG_ERROR(TCOMMON, "CreateDir: CreateDirectory failed on %s: %i", path.c_str(), error); + return false; +#else + if (mkdir(path.c_str(), 0755) == 0) { + return true; + } + int err = errno; + if (err == EEXIST) { + LOG_WARNING(TCOMMON, "CreateDir: mkdir failed on %s: already exists", path.c_str()); + return true; + } + LOG_ERROR(TCOMMON, "CreateDir: mkdir failed on %s: %s", path.c_str(), strerror(err)); + return false; +#endif +} + +// Creates the full path of fullPath returns true on success +bool CreateFullPath(const std::string &fullPath) { + int panicCounter = 100; + LOG_INFO(TCOMMON, "CreateFullPath: path %s", fullPath.c_str()); + + if (FileExists(fullPath)) { + LOG_INFO(TCOMMON, "CreateFullPath: path exists %s", fullPath.c_str()); + return true; + } + + size_t position = 0; + while (1) { + // Find next sub path + position = fullPath.find('/', position); + + // we're done, yay! + if (position == fullPath.npos) { + return true; + } + std::string subPath = fullPath.substr(0, position); + if (!IsDirectory(subPath)) CreateDir(subPath); + + // A safety check + panicCounter--; + if (panicCounter <= 0) { + LOG_ERROR(TCOMMON, "CreateFullPath: directory structure too deep"); + return false; + } + position++; + } +} + +// Deletes a directory filename, returns true on success +bool DeleteDir(const std::string &filename) { + LOG_INFO(TCOMMON, "DeleteDir: directory %s", filename.c_str()); + // check if a directory + if (!IsDirectory(filename)) { + LOG_ERROR(TCOMMON, "DeleteDir: Not a directory %s", filename.c_str()); + return false; + } +#ifdef _WIN32 + if (::RemoveDirectory(filename.c_str())) + return true; +#else + if (rmdir(filename.c_str()) == 0) + return true; +#endif + LOG_ERROR(TCOMMON, "DeleteDir: %s", filename.c_str()); + return false; +} + +// renames file srcFilename to destFilename, returns true on success +bool RenameFile(const std::string &srcFilename, const std::string &destFilename) { + LOG_INFO(TCOMMON, "Rename: %s --> %s", + srcFilename.c_str(), destFilename.c_str()); + if (rename(srcFilename.c_str(), destFilename.c_str()) == 0) + return true; + LOG_ERROR(TCOMMON, "Rename: failed %s --> %s", srcFilename.c_str(), destFilename.c_str()); + return false; +} + +// copies file srcFilename to destFilename, returns true on success +bool CopyFile(const std::string &srcFilename, const std::string &destFilename) { + LOG_INFO(TCOMMON, "Copy: %s --> %s", + srcFilename.c_str(), destFilename.c_str()); +#ifdef _WIN32 + if (::CopyFile(srcFilename.c_str(), destFilename.c_str(), FALSE)) + return true; + + LOG_ERROR(TCOMMON, "Copy: failed %s --> %s", srcFilename.c_str(), destFilename.c_str()); + return false; +#else + char buffer[1024]; + + // Open input file + FILE *input = fopen(srcFilename.c_str(), "rb"); + if (!input) { + LOG_ERROR(TCOMMON, "Copy: input failed %s --> %s", srcFilename.c_str(), + destFilename.c_str()); + return false; + } + // open output file + FILE *output = fopen(destFilename.c_str(), "wb"); + if (!output) { + fclose(input); + LOG_ERROR(TCOMMON, "Copy: output failed %s --> %s", srcFilename.c_str(), + destFilename.c_str()); + return false; + } + // copy loop + while (!feof(input)) { + // read input + int rnum = fread(buffer, sizeof(char), 1024, input); + if (rnum != 1024) { + if (ferror(input) != 0) { + LOG_ERROR(TCOMMON, "Copy: failed reading from source, %s --> %s", + srcFilename.c_str(), destFilename.c_str()); + goto bail; + } + } + // write output + int wnum = fwrite(buffer, sizeof(char), rnum, output); + if (wnum != rnum) { + LOG_ERROR(TCOMMON, "Copy: failed writing to output, %s --> %s", + srcFilename.c_str(), destFilename.c_str()); + goto bail; + } + } + // close flushs + fclose(input); + fclose(output); + return true; +bail: + if (input) + fclose(input); + if (output) + fclose(output); + return false; +#endif +} + +// Returns the size of filename (64bit) +u64 GetFileSize(const std::string &filename) { + if (!FileExists(filename)) { + LOG_WARNING(TCOMMON, "GetSize: failed %s: No such file", filename.c_str()); + return 0; + } + if (IsDirectory(filename)) { + LOG_WARNING(TCOMMON, "GetSize: failed %s: is a directory", filename.c_str()); + return 0; + } + struct stat64 buf; + if (stat64(filename.c_str(), &buf) == 0) { + LOG_DEBUG(TCOMMON, "GetSize: %s: %lld", filename.c_str(), (long long)buf.st_size); + return buf.st_size; + } + LOG_ERROR(TCOMMON, "GetSize: Stat failed %s", filename.c_str()); + return 0; +} + +// Overloaded GetSize, accepts file descriptor +u64 GetFileSize(const int fd) { + struct stat64 buf; + if (fstat64(fd, &buf) != 0) { + LOG_ERROR(TCOMMON, "GetSize: stat failed %i", fd); + return 0; + } + return buf.st_size; +} + +// Overloaded GetSize, accepts FILE* +u64 GetFileSize(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) { + LOG_ERROR(TCOMMON, "GetSize: seek failed %p", f); + return 0; + } + u64 size = ftello(f); + if ((size != pos) && (fseeko(f, pos, SEEK_SET) != 0)) { + LOG_ERROR(TCOMMON, "GetSize: seek failed %p", f); + return 0; + } + return size; +} + +// creates an empty file filename, returns true on success +bool CreateEmptyFile(const std::string &filename) { + LOG_INFO(TCOMMON, "CreateEmptyFile: %s", filename.c_str()); + + FILE *pFile = fopen(filename.c_str(), "wb"); + if (!pFile) { + LOG_ERROR(TCOMMON, "CreateEmptyFile: failed %s", filename.c_str()); + return false; + } + fclose(pFile); + return true; +} + +// Deletes the given directory and anything under it. Returns true on success. +bool DeleteDirRecursively(const std::string &directory) { + LOG_INFO(TCOMMON, "DeleteDirRecursively: %s", directory.c_str()); +#ifdef _WIN32 + // Find the first file in the directory. + WIN32_FIND_DATA ffd; + HANDLE hFind = FindFirstFile((directory + "\\*").c_str(), &ffd); + + if (hFind == INVALID_HANDLE_VALUE) { + FindClose(hFind); + return false; + } + + // windows loop + do { + const std::string virtualName = ffd.cFileName; +#else + struct dirent dirent, *result = NULL; + DIR *dirp = opendir(directory.c_str()); + if (!dirp) { + return false; + } + // non windows loop + while (!readdir_r(dirp, &dirent, &result) && result) { + const std::string virtualName = result->d_name; +#endif + // check for "." and ".." + if (((virtualName[0] == '.') && (virtualName[1] == '\0')) || + ((virtualName[0] == '.') && (virtualName[1] == '.') && + (virtualName[2] == '\0'))) { + continue; + } + std::string newPath = directory + '/' + virtualName; + if (IsDirectory(newPath)) { + if (!DeleteDirRecursively(newPath)) + return false; + } else { + if (!DeleteFile(newPath)) + return false; + } + +#ifdef _WIN32 + } while (FindNextFile(hFind, &ffd) != 0); + FindClose(hFind); +#else + } + closedir(dirp); +#endif + DeleteDir(directory); + + return true; +} + +// Returns the current directory +std::string GetCurrentDir() { + char *dir; + // Get the current working directory (getcwd uses malloc) + if (!(dir = __getcwd(NULL, 0))) { + + LOG_ERROR(TCOMMON, "GetCurrentDirectory failed:"); + return NULL; + } + std::string strDir = dir; + free(dir); + return strDir; +} + +// Sets the current directory to the given directory +bool SetCurrentDir(const std::string &directory) { + return __chdir(directory.c_str()) == 0; +} + +bool WriteStringToFile(bool text_file, const std::string &str, const char *filename) { + FILE *f = fopen(filename, text_file ? "w" : "wb"); + if (!f) { + return false; + } + size_t len = str.size(); + if (len != fwrite(str.data(), 1, str.size(), f)) { + fclose(f); + return false; + } + fclose(f); + return true; +} + +bool ReadFileToString(bool text_file, const char *filename, std::string &str) { + FILE *f = fopen(filename, text_file ? "r" : "rb"); + if (!f) { + return false; + } + size_t len = (size_t)GetFileSize(f); + char *buf = new char[len + 1]; + buf[fread(buf, 1, len, f)] = 0; + str = std::string(buf, len); + fclose(f); + delete [] buf; + return true; +} + +} // namespace diff --git a/src/common/src/file_utils.h b/src/common/src/file_utils.h new file mode 100644 index 00000000..6b93710b --- /dev/null +++ b/src/common/src/file_utils.h @@ -0,0 +1,90 @@ +/** +* Copyright (C) 2005-2013 Gekko Emulator +* +* @file file_utils.h +* @author ShizZy +* @date 2013-01-27 +* @brief Crossplatform file utility functions +* @remark Borrowed from Dolphin Emulator +* +* @section LICENSE +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details at +* http://www.gnu.org/copyleft/gpl.html +* +* Official project repository can be found at: +* http://code.google.com/p/gekko-gc-emu/ +*/ + +#ifndef COMMON_FILE_UTILS_H_ +#define COMMON_FILE_UTILS_H_ + +#include +#include +#include +#include +#include + +#include "common.h" + +namespace common { + +// Returns true if file filename exists +bool FileExists(const std::string &filename); + +// Returns true if filename is a directory +bool IsDirectory(const std::string &filename); + +// Returns the size of filename (64bit) +u64 GetFileSize(const std::string &filename); + +// Overloaded GetSize, accepts file descriptor +u64 GetFileSize(const int fd); + +// Overloaded GetSize, accepts FILE* +u64 GetFileSize(FILE *f); + +// Returns true if successful, or path already exists. +bool CreateDir(const std::string &filename); + +// Creates the full path of fullPath returns true on success +bool CreateFullPath(const std::string &fullPath); + +// Deletes a given filename, return true on success +// Doesn't supports deleting a directory +bool DeleteFile(const std::string &filename); + +// Deletes a directory filename, returns true on success +bool DeleteDir(const std::string &filename); + +// renames file srcFilename to destFilename, returns true on success +bool RenameFile(const std::string &srcFilename, const std::string &destFilename); + +// copies file srcFilename to destFilename, returns true on success +bool CopyFile(const std::string &srcFilename, const std::string &destFilename); + +// creates an empty file filename, returns true on success +bool CreateEmptyFile(const std::string &filename); + +// deletes the given directory and anything under it. Returns true on success. +bool DeleteDirRecursively(const std::string &directory); + +// Returns the current directory +std::string GetCurrentDir(); + +// Set the current directory to given directory +bool SetCurrentDir(const std::string &directory); + +bool WriteStringToFile(bool text_file, const std::string &str, const char *filename); +bool ReadFileToString(bool text_file, const char *filename, std::string &str); + +} // namespace + +#endif // COMMON_FILE_UTILS_H_ diff --git a/src/common/src/hash.cpp b/src/common/src/hash.cpp new file mode 100644 index 00000000..738d3d35 --- /dev/null +++ b/src/common/src/hash.cpp @@ -0,0 +1,241 @@ +/** + * Copyright (C) 2005-2012 Gekko Emulator + * + * @file hash.cpp + * @author ShizZy + * @date 2012-12-05 + * @brief General purpose hash function + * @remark Some functions borrowed from Dolphin Emulator + * + * @section LICENSE + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details at + * http://www.gnu.org/copyleft/gpl.html + * + * Official project repository can be found at: + * http://code.google.com/p/gekko-gc-emu/ + */ + +#include "crc.h" +#include "hash.h" +#include "common.h" + +namespace common { + +/// Block mix - combine the key bits with the hash bits and scramble everything +inline void bmix64(u64& h1, u64& h2, u64& k1, u64& k2, u64& c1, u64& c2) { + k1 *= c1; + k1 = _rotl64(k1,23); + k1 *= c2; + h1 ^= k1; + h1 += h2; + + h2 = _rotl64(h2,41); + + k2 *= c2; + k2 = _rotl64(k2,23); + k2 *= c1; + h2 ^= k2; + h2 += h1; + + h1 = h1*3 + 0x52dce729; + h2 = h2*3 + 0x38495ab5; + + c1 = c1*5 + 0x7b7d159c; + c2 = c2*5 + 0x6bce6396; +} + +/// Finalization mix - avalanches all bits to within 0.05% bias +inline u64 fmix64(u64 k) { + k ^= k >> 33; + k *= 0xff51afd7ed558ccd; + k ^= k >> 33; + k *= 0xc4ceb9fe1a85ec53; + k ^= k >> 33; + return k; +} + +#define ROTL32(x,y) rotl32(x,y) + +inline uint32_t fmix ( uint32_t h ) +{ + h ^= h >> 16; + h *= 0x85ebca6b; + h ^= h >> 13; + h *= 0xc2b2ae35; + h ^= h >> 16; + + return h; +} + +u32 __compute_murmur_hash3_32(const u8 *src, int len, u32 samples) { + u32 h = len; + u32 step = (len >> 2); + const u32 *data = (const u32*)src; + const u32 *end = data + step; + if (samples == 0) { + samples = std::max(step, 1u); + } + step = step / samples; + if(step < 1) { + step = 1; + } + u32 h1 = 0x2f6af274; + const u32 c1 = 0xcc9e2d51; + const u32 c2 = 0x1b873593; + + while (data < end) { + u32 k1 = data[0]; + + k1 *= c1; + k1 = (k1 << 15) | (k1 >> (32 - 15)); + k1 *= c2; + + h1 ^= k1; + h1 = (h1 << 15) | (h1 >> (32 - 13)); + h1 = h1*5+0xe6546b64; + + data += step; + } + const u8 * tail = (const u8*)(data); + + u32 k1 = 0; + + switch(len & 3) { + case 3: + k1 ^= tail[2] << 16; + case 2: + k1 ^= tail[1] << 8; + case 1: + k1 ^= tail[0]; + k1 *= c1; + k1 = (k1 << 15) | (k1 >> (32 - 15)); + k1 *= c2; + h1 ^= k1; + }; + h1 ^= len; + h1 = fmix(h1); + + return h1; +} + + +/// MurmurHash is a non-cryptographic hash function suitable for general hash-based lookup +u64 __compute_murmur_hash3_64(const u8 *src, int len, u32 samples) { + const u8 * data = (const u8*)src; + const int nblocks = len / 16; + u32 step = (len / 8); + if(samples == 0) { + samples = std::max(step, 1u); + } + step = step / samples; + if(step < 1) { + step = 1; + } + + u64 h1 = 0x9368e53c2f6af274; + u64 h2 = 0x586dcd208f7cd3fd; + + u64 c1 = 0x87c37b91114253d5; + u64 c2 = 0x4cf5ad432745937f; + + const u64* blocks = (const u64*)(data); + + for (int i = 0; i < nblocks; i+=step) { + u64 k1 = blocks[(i * 2) + 0]; + u64 k2 = blocks[(i * 2) + 1]; + + bmix64(h1,h2,k1,k2,c1,c2); + } + const u8* tail = (const u8*)(data + nblocks * 16); + + u64 k1 = 0; + u64 k2 = 0; + + switch (len & 15) { + case 15: k2 ^= u64(tail[14]) << 48; + case 14: k2 ^= u64(tail[13]) << 40; + case 13: k2 ^= u64(tail[12]) << 32; + case 12: k2 ^= u64(tail[11]) << 24; + case 11: k2 ^= u64(tail[10]) << 16; + case 10: k2 ^= u64(tail[ 9]) << 8; + case 9: k2 ^= u64(tail[ 8]) << 0; + + case 8: k1 ^= u64(tail[ 7]) << 56; + case 7: k1 ^= u64(tail[ 6]) << 48; + case 6: k1 ^= u64(tail[ 5]) << 40; + case 5: k1 ^= u64(tail[ 4]) << 32; + case 4: k1 ^= u64(tail[ 3]) << 24; + case 3: k1 ^= u64(tail[ 2]) << 16; + case 2: k1 ^= u64(tail[ 1]) << 8; + case 1: k1 ^= u64(tail[ 0]) << 0; + bmix64(h1, h2, k1, k2, c1, c2); + }; + h2 ^= len; + + h1 += h2; + h2 += h1; + + h1 = fmix64(h1); + h2 = fmix64(h2); + + h1 += h2; + + return h1; +} + +/// CRC32 hash using the SSE4.2 instruction +u64 __compute_crc32_sse4(const u8 *src, int len, u32 samples) { + u32 h = len; + u32 step = (len >> 2); + const u32 *data = (const u32*)src; + const u32 *end = data + step; + if (samples == 0) { + samples = std::max(step, 1u); + } + step = step / samples; + if(step < 1) { + step = 1; + } + while (data < end) { + h = InlineCrc32_U32(h, data[0]); + data += step; + } + const u8 *data2 = (const u8*)end; + return (u64)InlineCrc32_U32(h, u32(data2[0])); +} + +/** + * Compute an efficient 64-bit hash (optimized for Intel hardware) + * @param src Source data buffer to compute hash for + * @param len Length of data buffer to compute hash for + * @param samples Number of samples to compute hash for + * @remark Borrowed from Dolphin Emulator + */ +Hash64 GetHash64(const u8 *src, int len, u32 samples) { +#if defined(EMU_ARCHITECTURE_X86) || defined(EMU_ARCHITECTURE_X64) + // TODO(ShizZy): Move somewhere common so we dont need to instantiate this more than once + static X86Utils x86_utils; + if (x86_utils.IsExtensionSupported(X86Utils::kExtensionX86_SSE4_2)) { + return __compute_crc32_sse4(src, len, samples); + } else { + +#ifdef EMU_ARCHITECTURE_X64 + return __compute_murmur_hash3_64(src, len, samples); +#else + return __compute_murmur_hash3_32(src, len, samples); +#endif + } +#else + return __compute_murmur_hash3_32(src, len, samples); +#endif +} + +} // namespace diff --git a/src/common/src/hash.h b/src/common/src/hash.h new file mode 100644 index 00000000..37cbe823 --- /dev/null +++ b/src/common/src/hash.h @@ -0,0 +1,46 @@ +/** + * Copyright (C) 2005-2012 Gekko Emulator + * + * @file hash.h + * @author ShizZy + * @date 2012-12-05 + * @brief General purpose hash function + * + * @section LICENSE + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details at + * http://www.gnu.org/copyleft/gpl.html + * + * Official project repository can be found at: + * http://code.google.com/p/gekko-gc-emu/ + */ + +#ifndef COMMON_HASH_H_ +#define COMMON_HASH_H_ + +#include "types.h" + +namespace common { + +typedef u64 Hash64; + +/** + * Compute an efficient 64-bit hash (optimized for Intel hardware) + * @param src Source data buffer to compute hash for + * @param len Length of data buffer to compute hash for + * @param samples Number of samples to compute hash for + * @remark Borrowed from Dolphin Emulator + */ +Hash64 GetHash64(const u8 *src, int len, u32 samples); + +} // namespace + + +#endif // COMMON_HASH_H_ \ No newline at end of file diff --git a/src/common/src/hash_container.h b/src/common/src/hash_container.h new file mode 100644 index 00000000..ab832f3c --- /dev/null +++ b/src/common/src/hash_container.h @@ -0,0 +1,116 @@ +/** + * Copyright (C) 2005-2012 Gekko Emulator + * + * @file hash_container.h + * @author ShizZy + * @date 2012-11-29 + * @brief Container object for storing a hash lookup + * + * @section LICENSE + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details at + * http://www.gnu.org/copyleft/gpl.html + * + * Official project repository can be found at: + * http://code.google.com/p/gekko-gc-emu/ + */ + +#ifndef COMMON_HASH_CONTAINER_H_ +#define COMMON_HASH_CONTAINER_H_ + +#include +#include "common.h" + +/// Hash container generic interface - Don't use directly, use a derived class +template class HashContainer { + /** + * Add (or update if already exists) a value at the specified hash in the container + * @param hash Hash to use + * @param value Value to update at given hash in the container + */ + ValueType* Update(HashType hash, ValueType value); + + /** + * Remove a hash entry in the hash container + * @param hash Hash value of entry to remove + */ + void Remove(HashType hash); + + /** + * Fetch the value at at the given hash from the hash container + * @param hash Hash value of entry to fetch + * @return Pointer to value stored at hash location on success (index was found), otherwise NULL + */ + ValueType* FetchFromHash(HashType hash); + + /** + * Fetch the value at at the given integer index from the hash container + * @param hash Hash value of entry to fetch + * @return Pointer to value stored at hash location on success (index was found), otherwise NULL + */ + ValueType* FetchFromIndex(int index); + + /** + * Get the size of the hash container + * @return Number of elements in the hash container + */ + int Size(); +}; + +/// Hash container implemented using STL map +template class HashContainer_STLMap : + public HashContainer { + +public: + HashContainer_STLMap() { + } + ~HashContainer_STLMap() { + } + + ValueType* Update(HashType hash, ValueType value) { + map_[hash] = value; + return &map_[hash]; + } + + void Remove(HashType hash) { + map_.erase(hash); + } + + ValueType* FetchFromHash(HashType hash) { + typename std::map::iterator itr = map_.find(hash); + if (itr == map_.end()) { + return NULL; + } + return &itr->second; + } + + ValueType* FetchFromIndex(int index) { + typename std::map::iterator itr = map_.begin(); + int i = 0; + for (; i < index; ++i) { + ++itr; + } + if (i < index) { + return NULL; + } + return &itr->second; + } + + int Size() { + return static_cast(map_.size()); + } + +private: + std::map map_; + + DISALLOW_COPY_AND_ASSIGN(HashContainer_STLMap); +}; + +#endif // COMMON_HASH_CONTAINER_H_ diff --git a/src/common/src/log.cpp b/src/common/src/log.cpp new file mode 100644 index 00000000..c365b2f7 --- /dev/null +++ b/src/common/src/log.cpp @@ -0,0 +1,152 @@ +/** + * Copyright (C) 2005-2012 Gekko Emulator + * + * @file log.cpp + * @author ShizZy + * @date 2012-02-11 + * @brief Common logging routines used throughout the project + * + * @section LICENSE + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details at + * http://www.gnu.org/copyleft/gpl.html + * + * Official project repository can be found at: + * http://code.google.com/p/gekko-gc-emu/ + */ + +#include +#include + +#include "common.h" +#include "timer.h" + +namespace logger { + +LogContainer* g_logs[NUMBER_OF_LOGS]; ///< List of pointers to all logs + +/// LogContainer constructor +LogContainer::LogContainer(const char* name, const char* desc, bool enable = false) { + strncpy(name_, name, 128); + strncpy(desc_, desc, 32); + level_ = LWARNING; +} + +/// Asks the user a yes or no question +SysUserResponse AskYesNo(const char* fmt, ...) { + char c; + va_list arg; + + va_start(arg, fmt); + printf("\n** Question **\n"); + vprintf(fmt, arg); + va_end(arg); + + printf(" Response? (y/n) "); + while (1) { + c = getchar(); + if (c == 'y' || c == 'Y') { + return SYS_USER_YES; + } + if (c == 'n' || c == 'N') { + return SYS_USER_NO; + } + } + return SYS_USER_NO; +} + +//// Log routine used by everything +void LogGeneric(LogLevel level, LogType type, const char *file, int line, bool append, const char* fmt, ...) +{ + char msg[kMaxMsgLength]; + static const char level_to_char[8] = "-NECWID"; + static char last_char = '\n'; + static LogType last_type; + + va_list arg; + va_start(arg, fmt); + + if (type >= NUMBER_OF_LOGS) { + LOG_ERROR(TCOMMON, "Unknown logger type %d", type); + return; + } + + // Format the log message + if (append) { + sprintf(msg, "%s", fmt); + } else { + // char time_str[16]; + // u32 time_elapsed = common::GetTimeElapsed(); + // common::TicksToFormattedString(time_elapsed, time_str); + sprintf(msg, "%c[%s] %s", level_to_char[(int)level], g_logs[type]->name(), fmt); + } + + // If the last message didn't have a line break, print one + if ('\n' != last_char && '\r' != last_char && !append && last_type != TOS_REPORT && + last_type != TOS_HLE) { + printf("\n"); + } + last_char = msg[strlen(msg)-1]; + last_type = type; + + // Print the log message to stdout + vprintf(msg, arg); + va_end(arg); +} + +/// Forces a controlled system crash rather before it catches fire (debug) +void Crash() { + LOG_CRASH(TCOMMON, "*** SYSTEM CRASHED ***\n"); + LOG_CRASH(TCOMMON, "Fatal error, system could not recover.\n"); +#ifdef _MSC_VER +#ifdef USE_INLINE_ASM_X86 + __asm int 3 +#endif +#elif defined(__GNUC__) + asm("int $3"); +#else + LOG_CRASH(TCOMMON, "Exiting...\n"); + exit(0); +#endif +} + +/// Initialize the logging system +void Init() { + g_logs[TNULL] = new LogContainer("NULL", "Null"); + g_logs[TAI] = new LogContainer("AI", "AudioInterface"); + g_logs[TBOOT] = new LogContainer("BOOT", "Boot"); + g_logs[TCOMMON] = new LogContainer("COMMON", "Common"); + g_logs[TCONFIG] = new LogContainer("CONFIG", "Configuration"); + g_logs[TCORE] = new LogContainer("CORE", "SysCore"); + g_logs[TCP] = new LogContainer("CP", "CommandProcessor"); + g_logs[TDI] = new LogContainer("DI", "DVDInterface"); + g_logs[TDSP] = new LogContainer("DSP", "DSP"); + g_logs[TDVD] = new LogContainer("DVD", "GCM/ISO"); + g_logs[TEXI] = new LogContainer("EXI", "ExternalInterface"); + g_logs[TGP] = new LogContainer("GP", "GraphicsProcessor"); + g_logs[THLE] = new LogContainer("HLE", "HLE"); + g_logs[THW] = new LogContainer("HW", "Hardware"); + g_logs[TJOYPAD] = new LogContainer("JOYPAD", "Joypad"); + g_logs[TMASTER] = new LogContainer("*", "Master Log"); + g_logs[TMEM] = new LogContainer("MEM", "Memory"); + g_logs[TMI] = new LogContainer("MI", "MemoryInterface"); + g_logs[TOS_HLE] = new LogContainer("OSHLE", "OSHLE"); + g_logs[TOS_REPORT] = new LogContainer("OSREPORT", "OSREPORT"); + g_logs[TPE] = new LogContainer("PE", "PixelEngine"); + g_logs[TPI] = new LogContainer("PI", "ProcessorInterface"); + g_logs[TPOWERPC] = new LogContainer("PPC", "PowerPC"); + g_logs[TSI] = new LogContainer("SI", "SerialInterface"); + g_logs[TVI] = new LogContainer("VI", "VideoInterface"); + g_logs[TVIDEO] = new LogContainer("VIDEO", "VideoCore"); + + LOG_NOTICE(TCOMMON, "%d logger(s) initalized ok", NUMBER_OF_LOGS); +} + +} // namespace diff --git a/src/common/src/log.h b/src/common/src/log.h new file mode 100644 index 00000000..9ce3a30a --- /dev/null +++ b/src/common/src/log.h @@ -0,0 +1,216 @@ +/** + * Copyright (C) 2005-2012 Gekko Emulator + * + * @file log.h + * @author ShizZy + * @date 2012-02-11 + * @brief Common logging routines used throughout the project + * + * @section LICENSE + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details at + * http://www.gnu.org/copyleft/gpl.html + * + * Official project repository can be found at: + * http://code.google.com/p/gekko-gc-emu/ + */ + +#ifndef COMMON_LOG_H_ +#define COMMON_LOG_H_ + +#include "SDL.h" // Used for threading/mutexes + +#include "common.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Logging Macros + +#if defined(_DEBUG) || defined(DEBUG) || defined(LOGGING) +/// Debug mode, show all logs +#define MAX_LOG_LEVEL logger::LDEBUG +#else +/// Non debug mode, only show critical logs +#define MAX_LOG_LEVEL logger::LWARNING +#endif + +/// Logs a message ** Don't use directly ** +#define _LOG_GENERIC(level, type, ...) \ + if (level <= MAX_LOG_LEVEL) { \ + LogGeneric(level, type, __FILE__, __LINE__, false, __VA_ARGS__); \ + } + +/// Used for appending to the last logged message +#define LOG_APPEND(level, type, ...) \ + if (logger::level <= MAX_LOG_LEVEL) { \ + logger::LogGeneric(logger::level, logger::type, __FILE__, __LINE__, true, __VA_ARGS__); \ + } + +/// Use this for printing an IMPORTANT notice to the logger +#define LOG_NOTICE(type, ...) _LOG_GENERIC(logger::LNOTICE, logger::type, __VA_ARGS__) + +/// Use this for printing an error message to the logger +#define LOG_ERROR(type, ...) _LOG_GENERIC(logger::LERROR, logger::type, __VA_ARGS__) + +/// Use this for printing a crash report to the logger +#define LOG_CRASH(type, ...) _LOG_GENERIC(logger::LCRASH, logger::type, __VA_ARGS__) + +/// Use this for printing a warning to the logger +#define LOG_WARNING(type, ...) _LOG_GENERIC(logger::LWARNING, logger::type, __VA_ARGS__) + +/// Use this for printing general information to the logger +#define LOG_INFO(type, ...) _LOG_GENERIC(logger::LINFO, logger::type, __VA_ARGS__) + +#if defined(_DEBUG) || defined(DEBUG) || defined(LOGGING) + +/// Use this for printing a debug message to the logger +#define LOG_DEBUG(type, ...) _LOG_GENERIC(logger::LDEBUG, logger::type, __VA_ARGS__) + +/// Used for debug-mode assertions +#define _ASSERT_DBG(_type_, _cond_) \ + if (!(_cond_)) { \ + LOG_ERROR(_type_, "Error...\n\n Line: %d\n File: %s\n Time: %s\n", \ + __LINE__, __FILE__, __TIME__); \ + if (!logger::AskYesNo("*** Assertion (see log)***\n")) logger::Crash(); \ + } + +/// Used for message-specified debug-mode assertions +#define _ASSERT_DBG_MSG(_type_, _cond_, ...) \ + if (!(_cond_)) { \ + LOG_ERROR(_type_, __VA_ARGS__); \ + if (!logger::AskYesNo(__VA_ARGS__)) logger::Crash(); \ + } +#else +#define _ASSERT_DBG(_type_, _cond_, ...) +#define _ASSERT_DBG_MSG(_type_, _cond_, ...) +#define LOG_DEBUG(type, ...) +#endif + +/// Used for general purpose assertions, CRITICAL operations only +#define _ASSERT_MSG(_type_, _cond_, ...) \ + if (!(_cond_)) { \ + if (!logger::AskYesNo(__VA_ARGS__)) logger::Crash(); \ + } + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Logger + +namespace logger { + +const int kMaxMsgLength = 1024; ///< Maximum message length + +/// Used for handling responses to system functions that require them +typedef enum { + SYS_USER_NO = 0, ///< User response for 'No' + SYS_USER_YES, ///< User response for 'Yes' + SYS_USER_OK, ///< User response for 'Okay' + SYS_USER_ABORT, ///< User response for 'Abort' + SYS_USER_RETRY, ///< User response for 'Retry' + SYS_USER_CANCEL, ///< User response for 'Cancel' +} SysUserResponse; + +/// Level of logging +typedef enum { + LNULL = 0, ///< Logs with this level won't get logged + LNOTICE, ///< Notice: A general message to the user + LERROR, ///< Error: For failure messages + LCRASH, ///< Crash: Used for crash reports + LWARNING, ///< Warning: For potentially bad things, but not fatal + LINFO, ///< Info: Information message + LDEBUG ///< Debug: Debug-only information +} LogLevel; + +/// Type of logging +typedef enum { + TNULL = 0, + TAI, + TBOOT, + TCOMMON, + TCONFIG, + TCORE, + TCP, + TDI, + TDSP, + TDVD, + TEXI, + TGP, + THLE, + THW, + TJOYPAD, + TMASTER, + TMEM, + TMI, + TOS_HLE, + TOS_REPORT, + TPE, + TPI, + TPOWERPC, + TSI, + TVI, + TVIDEO, + NUMBER_OF_LOGS ///< Number of logs - must be last +} LogType; + +/// Used for implementing a logger for a subsystem +class LogContainer +{ +public: + LogContainer(const char* name, const char* desc, bool enable); + ~LogContainer() {} + + const char* name() const { return name_; } + const char* desc() const { return desc_; } + + bool enabled() const { return enabled_; } + void set_enabled(bool enabled) { enabled_ = enabled; } + + LogLevel level() const { return level_; } + void set_level(LogLevel level) { level_ = level; } + +private: + char name_[32]; ///< Name of the logger (e.g. "SI") + char desc_[128]; ///< Description of the logger (e.g. "Serial Interface") + bool enabled_; ///< Whether or not the logger is enabled + + LogLevel level_; ///< Level of the logger (e.g. Notice, Error, Warning, etc.) + + SDL_mutex* listener_lock_; ///< Mutex for multithreaded access + + DISALLOW_COPY_AND_ASSIGN(LogContainer); +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Function Prototypes + +/*! + * \brief Log routine used by everything + * \param level Log level to use + * \param type Log type to use + * \param file Filename of file where error occured + * \param line Linenumber of file where error occured + * \param fmt Formatted message + */ +void LogGeneric(LogLevel level, LogType type, const char *file, int line, bool append, const char* fmt, ...); + +/// Forces a controlled system crash rather before it catches fire (debug) +void Crash(); + +/*! + * \brief Asks the user a yes or no question + * \param fmt Question formatted message + * \return SysUserResponse response + */ +SysUserResponse AskYesNo(const char* fmt, ...); + +/// Initialize the logging system +void Init(); + +} // namespace log + +#endif // COMMON_LOG_H diff --git a/src/common/src/misc_utils.cpp b/src/common/src/misc_utils.cpp new file mode 100644 index 00000000..d2a50fb3 --- /dev/null +++ b/src/common/src/misc_utils.cpp @@ -0,0 +1,91 @@ +/** + * Copyright (C) 2005-2012 Gekko Emulator + * + * @file misc_utils.cpp + * @author ShizZy + * @date 2012-03-06 + * @brief Miscellaneous functions/utilities that are used everywhere + * + * @section LICENSE + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details at + * http://www.gnu.org/copyleft/gpl.html + * + * Official project repository can be found at: + * http://code.google.com/p/gekko-gc-emu/ + */ + +#include "misc_utils.h" + +namespace common { + +/// Make a string lowercase +void LowerStr(char* str) { + for (int i = 0; str[i]; i++) { + str[i] = tolower(str[ i ]); + } +} + +/// Make a string uppercase +void UpperStr(char* str) { + for (int i=0; i < strlen(str); i++) { + if(str[i] >= 'a' && str[i] <= 'z') { + str[i] &= 0xDF; + } + } +} + +/// Format a std::string using C-style sprintf formatting +std::string FormatStr(const char* format, ...) { + va_list args; + char *buf = NULL; +#if EMU_PLATFORM == PLATFORM_WINDOWS + int required = 0; + + va_start(args, format); + required = _vscprintf(format, args); + buf = new char[required + 1]; + vsnprintf(buf, required, format, args); + va_end(args); + + buf[required] = '\0'; + std::string temp = buf; + delete[] buf; +#else + va_start(args, format); + vasprintf(&buf, format, args); + va_end(args); + + std::string temp = buf; + free(buf); +#endif + return temp; +} + +/// Check if a file exists +bool FileExists(char* filename) { + std::ifstream ifile(filename); + if (!ifile) { + return false; + } + ifile.close(); + return true; +} + +/// Gets the size of a file +size_t FileSize(FILE* file) { + size_t pos = ftell(file); + fseek(file, 0L, SEEK_END); + size_t res = ftell(file); + fseek(file, pos, SEEK_SET); + return res; +} + +} // namespace diff --git a/src/common/src/misc_utils.h b/src/common/src/misc_utils.h new file mode 100644 index 00000000..b003336c --- /dev/null +++ b/src/common/src/misc_utils.h @@ -0,0 +1,58 @@ +/** + * Copyright (C) 2005-2012 Gekko Emulator + * + * @file misc_utils.h + * @author ShizZy + * @date 2012-03-06 + * @brief Miscellaneous functions/utilities that are used everywhere + * + * @section LICENSE + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details at + * http://www.gnu.org/copyleft/gpl.html + * + * Official project repository can be found at: + * http://code.google.com/p/gekko-gc-emu/ + */ + +#include "common.h" + +namespace common { + +/** + * @brief Make a string lowercase + * @param str String to make lowercase + */ +void LowerStr(char* str); + +/** + * @brief Make a string uppercase + * @param str String to make uppercase + */ +void UpperStr(char* str); + +/// Format a std::string using C-style sprintf formatting +std::string FormatStr(const char* format, ...); + +/** + * @brief Check if a file exists on the users computer + * @param filename Filename of file to check for + * @return true on exists, false otherwise + */ +bool FileExists(char* filename); + +/** + * @brief Gets the size of a file + * @param file Pointer to file to get size of + * @return true Size of file, in bytes + */ +size_t FileSize(FILE* file); + +} // namespace diff --git a/src/common/src/platform.h b/src/common/src/platform.h new file mode 100644 index 00000000..efbf3824 --- /dev/null +++ b/src/common/src/platform.h @@ -0,0 +1,135 @@ +/** + * Copyright (C) 2005-2012 Gekko Emulator + * + * @file platform.h + * @author ShizZy + * @date 2012-02-11 + * @brief Platform detection macros for portable compilation + * + * @section LICENSE + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details at + * http://www.gnu.org/copyleft/gpl.html + * + * Official project repository can be found at: + * http://code.google.com/p/gekko-gc-emu/ + */ + +#ifndef COMMON_PLATFORM_H_ +#define COMMON_PLATFORM_H_ + +#include "types.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Platform definitions + +/// Enumeration for defining the supported platforms +#define PLATFORM_NULL 0 +#define PLATFORM_WINDOWS 1 +#define PLATFORM_MACOSX 2 +#define PLATFORM_LINUX 3 +#define PLATFORM_ANDROID 4 +#define PLATFORM_IOS 5 + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Platform detection +extern char *kGekkoOS; + +#ifndef EMU_PLATFORM + +#if defined( __WIN32__ ) || defined( _WIN32 ) +#define EMU_PLATFORM PLATFORM_WINDOWS + +#elif defined( __APPLE__ ) || defined( __APPLE_CC__ ) +#define EMU_PLATFORM PLATFORM_MAXOSX + +#elif defined(__linux__) +#define EMU_PLATFORM PLATFORM_LINUX + +#else // Assume linux otherwise +#define EMU_PLATFORM PLATFORM_LINUX + +#endif + +#endif + +#if defined(__x86_64__) || defined(_M_X64) || defined(__alpha__) || defined(__ia64__) +#define EMU_ARCHITECTURE_X64 +#else +#define EMU_ARCHITECTURE_X86 +#endif + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Compiler-Specific Definitions + +#if EMU_PLATFORM == PLATFORM_WINDOWS + +#define NOMINMAX +#define EMU_FASTCALL __fastcall + +#else + +#define EMU_FASTCALL __attribute__((fastcall)) +#define __stdcall +#define __cdecl + +#define LONG long +#define BOOL bool +#define DWORD u32 + +#endif + +#if EMU_PLATFORM != PLATFORM_WINDOWS + +// TODO: Hacks.. +#include +#define MAX_PATH PATH_MAX + +#include +#define stricmp(str1, str2) strcasecmp(str1, str2) +#define _stricmp(str1, str2) strcasecmp(str1, str2) +#define _snprintf snprintf +#define _getcwd getcwd +#define _tzset tzset + +typedef void EXCEPTION_POINTERS; + +inline u32 _rotl(u32 x, int shift) { + shift &= 31; + if (0 == shift) { + return x; + } + return (x << shift) | (x >> (32 - shift)); +} + +inline u64 _rotl64(u64 x, u32 shift){ + u32 n = shift % 64; + return (x << n) | (x >> (64 - n)); +} + +inline u32 _rotr(u32 x, int shift) { + shift &= 31; + if (0 == shift) { + return x; + } + return (x >> shift) | (x << (32 - shift)); +} + +inline u64 _rotr64(u64 x, u32 shift){ + u32 n = shift % 64; + return (x >> n) | (x << (64 - n)); +} + +#endif + +#define GCC_VERSION_AVAILABLE(major, minor) (defined(__GNUC__) && (__GNUC__ > (major) || \ + (__GNUC__ == (major) && __GNUC_MINOR__ >= (minor)))) + +#endif // COMMON_PLATFORM_H_ diff --git a/src/common/src/std_condition_variable.h b/src/common/src/std_condition_variable.h new file mode 100644 index 00000000..e7088725 --- /dev/null +++ b/src/common/src/std_condition_variable.h @@ -0,0 +1,152 @@ +#ifndef CONDITION_VARIABLE_H_ +#define CONDITION_VARIABLE_H_ + +#define GCC_VER(x,y,z) ((x) * 10000 + (y) * 100 + (z)) +#define GCC_VERSION GCC_VER(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) + +#if GCC_VERSION >= GCC_VER(4,4,0) && __GXX_EXPERIMENTAL_CXX0X__ +// GCC 4.4 provides +#include +#else + +// partial std::condition_variable implementation for win32/pthread + +#include "std_mutex.h" + +#if (_MSC_VER >= 1600) || (GCC_VERSION >= GCC_VER(4,3,0) && __GXX_EXPERIMENTAL_CXX0X__) +#define USE_RVALUE_REFERENCES +#endif + +#if defined(_WIN32) && defined(_M_X64) +#define USE_CONDITION_VARIABLES +#elif defined(_WIN32) +#define USE_EVENTS +#endif + +namespace std +{ + +class condition_variable +{ +#if defined(_WIN32) && defined(USE_CONDITION_VARIABLES) + typedef CONDITION_VARIABLE native_type; +#elif defined(_WIN32) + typedef HANDLE native_type; +#else + typedef pthread_cond_t native_type; +#endif + +public: + +#ifdef USE_EVENTS + typedef native_type native_handle_type; +#else + typedef native_type* native_handle_type; +#endif + + condition_variable() + { +#if defined(_WIN32) && defined(USE_CONDITION_VARIABLES) + InitializeConditionVariable(&m_handle); +#elif defined(_WIN32) + m_handle = CreateEvent(NULL, false, false, NULL); +#else + pthread_cond_init(&m_handle, NULL); +#endif + } + + ~condition_variable() + { +#if defined(_WIN32) && !defined(USE_CONDITION_VARIABLES) + CloseHandle(m_handle); +#elif !defined(_WIN32) + pthread_cond_destroy(&m_handle); +#endif + } + + condition_variable(const condition_variable&) /*= delete*/; + condition_variable& operator=(const condition_variable&) /*= delete*/; + + void notify_one() + { +#if defined(_WIN32) && defined(USE_CONDITION_VARIABLES) + WakeConditionVariable(&m_handle); +#elif defined(_WIN32) + SetEvent(m_handle); +#else + pthread_cond_signal(&m_handle); +#endif + } + + void notify_all() + { +#if defined(_WIN32) && defined(USE_CONDITION_VARIABLES) + WakeAllConditionVariable(&m_handle); +#elif defined(_WIN32) + // TODO: broken + SetEvent(m_handle); +#else + pthread_cond_broadcast(&m_handle); +#endif + } + + void wait(unique_lock& lock) + { +#ifdef _WIN32 +#ifdef USE_SRWLOCKS + SleepConditionVariableSRW(&m_handle, lock.mutex()->native_handle(), INFINITE, 0); +#elif defined(USE_CONDITION_VARIABLES) + SleepConditionVariableCS(&m_handle, lock.mutex()->native_handle(), INFINITE); +#else + // TODO: broken, the unlock and wait need to be atomic + lock.unlock(); + WaitForSingleObject(m_handle, INFINITE); + lock.lock(); +#endif +#else + pthread_cond_wait(&m_handle, lock.mutex()->native_handle()); +#endif + } + + template + void wait(unique_lock& lock, Predicate pred) + { + while (!pred()) + wait(lock); + } + + //template + //cv_status wait_until(unique_lock& lock, + // const chrono::time_point& abs_time); + + //template + // bool wait_until(unique_lock& lock, + // const chrono::time_point& abs_time, + // Predicate pred); + + //template + //cv_status wait_for(unique_lock& lock, + // const chrono::duration& rel_time); + + //template + // bool wait_for(unique_lock& lock, + // const chrono::duration& rel_time, + // Predicate pred); + + native_handle_type native_handle() + { +#ifdef USE_EVENTS + return m_handle; +#else + return &m_handle; +#endif + } + +private: + native_type m_handle; +}; + +} + +#endif +#endif diff --git a/src/common/src/std_mutex.h b/src/common/src/std_mutex.h new file mode 100644 index 00000000..27c5833f --- /dev/null +++ b/src/common/src/std_mutex.h @@ -0,0 +1,354 @@ +#ifndef MUTEX_H_ +#define MUTEX_H_ + +#define GCC_VER(x,y,z) ((x) * 10000 + (y) * 100 + (z)) +#define GCC_VERSION GCC_VER(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) + +#if GCC_VERSION >= GCC_VER(4,4,0) && __GXX_EXPERIMENTAL_CXX0X__ +// GCC 4.4 provides +#include +#else + +// partial implementation for win32/pthread + +#include + +#if defined(_WIN32) +// WIN32 +#define WIN32_LEAN_AND_MEAN +#include + +#else +// POSIX +#include + +#endif + +#if (_MSC_VER >= 1600) || (GCC_VERSION >= GCC_VER(4,3,0) && __GXX_EXPERIMENTAL_CXX0X__) +#define USE_RVALUE_REFERENCES +#endif + +#if defined(_WIN32) && defined(_M_X64) +#define USE_SRWLOCKS +#endif + +namespace std +{ + +class recursive_mutex +{ +#ifdef _WIN32 + typedef CRITICAL_SECTION native_type; +#else + typedef pthread_mutex_t native_type; +#endif + +public: + typedef native_type* native_handle_type; + + recursive_mutex(const recursive_mutex&) /*= delete*/; + recursive_mutex& operator=(const recursive_mutex&) /*= delete*/; + + recursive_mutex() + { +#ifdef _WIN32 + InitializeCriticalSection(&m_handle); +#else + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&m_handle, &attr); +#endif + } + + ~recursive_mutex() + { +#ifdef _WIN32 + DeleteCriticalSection(&m_handle); +#else + pthread_mutex_destroy(&m_handle); +#endif + } + + void lock() + { +#ifdef _WIN32 + EnterCriticalSection(&m_handle); +#else + pthread_mutex_lock(&m_handle); +#endif + } + + void unlock() + { +#ifdef _WIN32 + LeaveCriticalSection(&m_handle); +#else + pthread_mutex_unlock(&m_handle); +#endif + } + + bool try_lock() + { +#ifdef _WIN32 + return (0 != TryEnterCriticalSection(&m_handle)); +#else + return !pthread_mutex_trylock(&m_handle); +#endif + } + + native_handle_type native_handle() + { + return &m_handle; + } + +private: + native_type m_handle; +}; + +#if !defined(_WIN32) || defined(USE_SRWLOCKS) + +class mutex +{ +#ifdef _WIN32 + typedef SRWLOCK native_type; +#else + typedef pthread_mutex_t native_type; +#endif + +public: + typedef native_type* native_handle_type; + + mutex(const mutex&) /*= delete*/; + mutex& operator=(const mutex&) /*= delete*/; + + mutex() + { +#ifdef _WIN32 + InitializeSRWLock(&m_handle); +#else + pthread_mutex_init(&m_handle, NULL); +#endif + } + + ~mutex() + { +#ifdef _WIN32 +#else + pthread_mutex_destroy(&m_handle); +#endif + } + + void lock() + { +#ifdef _WIN32 + AcquireSRWLockExclusive(&m_handle); +#else + pthread_mutex_lock(&m_handle); +#endif + } + + void unlock() + { +#ifdef _WIN32 + ReleaseSRWLockExclusive(&m_handle); +#else + pthread_mutex_unlock(&m_handle); +#endif + } + + bool try_lock() + { +#ifdef _WIN32 + // XXX TryAcquireSRWLockExclusive requires Windows 7! + // return (0 != TryAcquireSRWLockExclusive(&m_handle)); + return false; +#else + return !pthread_mutex_trylock(&m_handle); +#endif + } + + native_handle_type native_handle() + { + return &m_handle; + } + +private: + native_type m_handle; +}; + +#else +typedef recursive_mutex mutex; // just use CriticalSections + +#endif + +enum defer_lock_t { defer_lock }; +enum try_to_lock_t { try_to_lock }; +enum adopt_lock_t { adopt_lock }; + +template +class lock_guard +{ +public: + typedef Mutex mutex_type; + + explicit lock_guard(mutex_type& m) + : pm(m) + { + m.lock(); + } + + lock_guard(mutex_type& m, adopt_lock_t) + : pm(m) + { + } + + ~lock_guard() + { + pm.unlock(); + } + + lock_guard(lock_guard const&) /*= delete*/; + lock_guard& operator=(lock_guard const&) /*= delete*/; + +private: + mutex_type& pm; +}; + +template +class unique_lock +{ +public: + typedef Mutex mutex_type; + + unique_lock() + : pm(NULL), owns(false) + {} + + /*explicit*/ unique_lock(mutex_type& m) + : pm(&m), owns(true) + { + m.lock(); + } + + unique_lock(mutex_type& m, defer_lock_t) + : pm(&m), owns(false) + {} + + unique_lock(mutex_type& m, try_to_lock_t) + : pm(&m), owns(m.try_lock()) + {} + + unique_lock(mutex_type& m, adopt_lock_t) + : pm(&m), owns(true) + {} + + //template + //unique_lock(mutex_type& m, const chrono::time_point& abs_time); + + //template + //unique_lock(mutex_type& m, const chrono::duration& rel_time); + + ~unique_lock() + { + if (owns_lock()) + mutex()->unlock(); + } + +#ifdef USE_RVALUE_REFERENCES + unique_lock& operator=(const unique_lock&) /*= delete*/; + + unique_lock& operator=(unique_lock&& other) + { +#else + unique_lock& operator=(const unique_lock& u) + { + // ugly const_cast to get around lack of rvalue references + unique_lock& other = const_cast(u); +#endif + swap(other); + return *this; + } + +#ifdef USE_RVALUE_REFERENCES + unique_lock(const unique_lock&) /*= delete*/; + + unique_lock(unique_lock&& other) + : pm(NULL), owns(false) + { +#else + unique_lock(const unique_lock& u) + : pm(NULL), owns(false) + { + // ugly const_cast to get around lack of rvalue references + unique_lock& other = const_cast(u); +#endif + swap(other); + } + + void lock() + { + mutex()->lock(); + owns = true; + } + + bool try_lock() + { + owns = mutex()->try_lock(); + return owns; + } + + //template + //bool try_lock_for(const chrono::duration& rel_time); + //template + //bool try_lock_until(const chrono::time_point& abs_time); + + void unlock() + { + mutex()->unlock(); + owns = false; + } + + void swap(unique_lock& u) + { + std::swap(pm, u.pm); + std::swap(owns, u.owns); + } + + mutex_type* release() + { + return mutex(); + pm = NULL; + owns = false; + } + + bool owns_lock() const + { + return owns; + } + + //explicit operator bool () const + //{ + // return owns_lock(); + //} + + mutex_type* mutex() const + { + return pm; + } + +private: + mutex_type* pm; + bool owns; +}; + +template +void swap(unique_lock& x, unique_lock& y) +{ + x.swap(y); +} + +} + +#endif +#endif diff --git a/src/common/src/std_thread.h b/src/common/src/std_thread.h new file mode 100644 index 00000000..0580221b --- /dev/null +++ b/src/common/src/std_thread.h @@ -0,0 +1,309 @@ +#ifndef STD_THREAD_H_ +#define STD_THREAD_H_ + +#define GCC_VER(x,y,z) ((x) * 10000 + (y) * 100 + (z)) +#define GCC_VERSION GCC_VER(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) + +#if GCC_VERSION >= GCC_VER(4,4,0) && __GXX_EXPERIMENTAL_CXX0X__ +// GCC 4.4 provides +#ifndef _GLIBCXX_USE_SCHED_YIELD +#define _GLIBCXX_USE_SCHED_YIELD +#endif +#include +#else + +// partial std::thread implementation for win32/pthread + +#include + +#if (_MSC_VER >= 1600) || (GCC_VERSION >= GCC_VER(4,3,0) && __GXX_EXPERIMENTAL_CXX0X__) +#define USE_RVALUE_REFERENCES +#endif + +#ifdef __APPLE__ +#import +#endif + +#if defined(_WIN32) +// WIN32 + +#define WIN32_LEAN_AND_MEAN +#include + +#if defined(_MSC_VER) && defined(_MT) +// When linking with LIBCMT (the multithreaded C library), Microsoft recommends +// using _beginthreadex instead of CreateThread. +#define USE_BEGINTHREADEX +#include +#endif + +#ifdef USE_BEGINTHREADEX +#define THREAD_ID unsigned +#define THREAD_RETURN unsigned __stdcall +#else +#define THREAD_ID DWORD +#define THREAD_RETURN DWORD WINAPI +#endif +#define THREAD_HANDLE HANDLE + +#else +// PTHREAD + +#include + +#ifndef _POSIX_THREADS +#error unsupported platform (no pthreads?) +#endif + +#include + +#define THREAD_ID pthread_t +#define THREAD_HANDLE pthread_t +#define THREAD_RETURN void* + +#endif + +namespace std +{ + +class thread +{ +public: + typedef THREAD_HANDLE native_handle_type; + + class id + { + friend class thread; + public: + id() : m_thread(0) {} + id(THREAD_ID _id) : m_thread(_id) {} + + bool operator==(const id& rhs) const + { + return m_thread == rhs.m_thread; + } + + bool operator!=(const id& rhs) const + { + return !(*this == rhs); + } + + bool operator<(const id& rhs) const + { + return m_thread < rhs.m_thread; + } + + private: + THREAD_ID m_thread; + }; + + // no variadic template support in msvc + //template + //thread(C&& func, A&&... args); + + template + thread(C func) + { + StartThread(new Func(func)); + } + + template + thread(C func, A arg) + { + StartThread(new FuncArg(func, arg)); + } + + thread() /*= default;*/ {} + +#ifdef USE_RVALUE_REFERENCES + thread(const thread&) /*= delete*/; + + thread(thread&& other) + { +#else + thread(const thread& t) + { + // ugly const_cast to get around lack of rvalue references + thread& other = const_cast(t); +#endif + swap(other); + } + +#ifdef USE_RVALUE_REFERENCES + thread& operator=(const thread&) /*= delete*/; + + thread& operator=(thread&& other) + { +#else + thread& operator=(const thread& t) + { + // ugly const_cast to get around lack of rvalue references + thread& other = const_cast(t); +#endif + if (joinable()) + detach(); + swap(other); + return *this; + } + + ~thread() + { + if (joinable()) + detach(); + } + + bool joinable() const + { + return m_id != id(); + } + + id get_id() const + { + return m_id; + } + + native_handle_type native_handle() + { +#ifdef _WIN32 + return m_handle; +#else + return m_id.m_thread; +#endif + } + + void join() + { +#ifdef _WIN32 + WaitForSingleObject(m_handle, INFINITE); + detach(); +#else + pthread_join(m_id.m_thread, NULL); + m_id = id(); +#endif + } + + void detach() + { +#ifdef _WIN32 + CloseHandle(m_handle); +#else + pthread_detach(m_id.m_thread); +#endif + m_id = id(); + } + + void swap(thread& other) + { + std::swap(m_id, other.m_id); +#ifdef _WIN32 + std::swap(m_handle, other.m_handle); +#endif + } + + static unsigned hardware_concurrency() + { +#ifdef _WIN32 + SYSTEM_INFO sysinfo; + GetSystemInfo(&sysinfo); + return static_cast(sysinfo.dwNumberOfProcessors); +#else + return 0; +#endif + } + +private: + id m_id; + +#ifdef _WIN32 + native_handle_type m_handle; +#endif + + template + void StartThread(F* param) + { +#ifdef USE_BEGINTHREADEX + m_handle = (HANDLE)_beginthreadex(NULL, 0, &RunAndDelete, param, 0, &m_id.m_thread); +#elif defined(_WIN32) + m_handle = CreateThread(NULL, 0, &RunAndDelete, param, 0, &m_id.m_thread); +#else + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setstacksize(&attr, 1024 * 1024); + if (pthread_create(&m_id.m_thread, &attr, &RunAndDelete, param)) + m_id = id(); +#endif + } + + template + class Func + { + public: + Func(C _func) : func(_func) {} + + void Run() { func(); } + + private: + C const func; + }; + + template + class FuncArg + { + public: + FuncArg(C _func, A _arg) : func(_func), arg(_arg) {} + + void Run() { func(arg); } + + private: + C const func; + A arg; + }; + + template + static THREAD_RETURN RunAndDelete(void* param) + { +#ifdef __APPLE__ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; +#endif + static_cast(param)->Run(); + delete static_cast(param); +#ifdef __APPLE__ + [pool release]; +#endif + return 0; + } +}; + +namespace this_thread +{ + + inline void yield() + { +#ifdef _WIN32 + SwitchToThread(); +#else + sleep(0); +#endif + } + + inline thread::id get_id() + { +#ifdef _WIN32 + return GetCurrentThreadId(); +#else + return pthread_self(); +#endif + } + +} // namespace this_thread + +} // namespace std + +#undef USE_RVALUE_REFERENCES +#undef USE_BEGINTHREADEX +#undef THREAD_ID +#undef THREAD_RETURN +#undef THREAD_HANDLE + +#endif +#endif diff --git a/src/common/src/timer.cpp b/src/common/src/timer.cpp new file mode 100644 index 00000000..c731ffb9 --- /dev/null +++ b/src/common/src/timer.cpp @@ -0,0 +1,46 @@ +/*! +* Copyright (C) 2005-2012 Gekko Emulator +* +* \file timer.h +* \author ShizZy +* \date 2012-02-11 +* \brief Common time and timer routines +* + * \section LICENSE + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details at + * http://www.gnu.org/copyleft/gpl.html + * + * Official project repository can be found at: + * http://code.google.com/p/gekko-gc-emu/ + */ + +#include "SDL.h" + +#include "common.h" +#include "timer.h" + +namespace common { + +/// Converts a ticks (miliseconds) u64 to a formatted string +void TicksToFormattedString(u32 ticks, char* formatted_string) { + u32 hh = ticks / (1000 * 60 * 60); + ticks -= hh * (1000 * 60 * 60); + + u32 mm = ticks / (1000 * 60); + ticks -= mm * (1000 * 60); + + u32 ss = ticks / 1000; + ticks -= ss * 1000; + + sprintf(formatted_string, "%02d:%02d:%03d", mm, ss, ticks); +} + +} // namespace \ No newline at end of file diff --git a/src/common/src/timer.h b/src/common/src/timer.h new file mode 100644 index 00000000..dfbf87df --- /dev/null +++ b/src/common/src/timer.h @@ -0,0 +1,51 @@ +/*! +* Copyright (C) 2005-2012 Gekko Emulator +* +* \file timer.h +* \author ShizZy +* \date 2012-02-11 +* \brief Common time and timer routines +* + * \section LICENSE + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details at + * http://www.gnu.org/copyleft/gpl.html + * + * Official project repository can be found at: + * http://code.google.com/p/gekko-gc-emu/ + */ + +#ifndef COMMON_TIMER_H_ +#define COMMON_TIMER_H_ + +#include "types.h" + +namespace common { + +/*! + * \brief Gets Get the number of milliseconds since initialization + * \return Unsigned integer of ticks since software initialization + */ +static inline u32 GetTimeElapsed() { + return SDL_GetTicks(); +} + +/*! + * \brief Converts a ticks (miliseconds) u32 to a formatted string + * \param ticks Ticks (32-bit unsigned integer) + * \param formatted_string Pointer to formatted string result + */ +void TicksToFormattedString(u32 ticks, char* formatted_string); + + +} // namespace + + +#endif // COMMON_TIMER_H_ \ No newline at end of file diff --git a/src/common/src/types.h b/src/common/src/types.h new file mode 100644 index 00000000..d1e02795 --- /dev/null +++ b/src/common/src/types.h @@ -0,0 +1,119 @@ +/** + * Copyright (C) 2005-2012 Gekko Emulator + * + * @file types.h + * @author ShizZy + * @date 2012-02-11 + * @brief Common types used throughout the project + * + * @section LICENSE + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details at + * http://www.gnu.org/copyleft/gpl.html + * + * Official project repository can be found at: + * http://code.google.com/p/gekko-gc-emu/ + */ + +#ifndef COMMON_TYPES_H_ +#define COMMON_TYPES_H_ + +#include +#include // data_types__m128.cpp + +typedef unsigned char u8; ///< 8-bit unsigned byte +typedef unsigned short u16; ///< 16-bit unsigned short +typedef unsigned int u32; ///< 32-bit unsigned word + +typedef signed char s8; ///< 8-bit signed byte +typedef signed short s16; ///< 16-bit signed short +typedef signed int s32; ///< 32-bit signed word + +typedef signed int x32; ///< S15.16 fixed point int + +typedef float f32; ///< 32-bit floating point +typedef double f64; ///< 64-bit floating point + +#ifdef _MSC_VER + +typedef unsigned __int64 u64; ///< 64-bit unsigned int +typedef signed __int64 s64; ///< 64-bit signed int + +#elif defined(__GNUC__) + +typedef signed long long s64; ///< 64-bit unsigned int +typedef unsigned long long u64; ///< 64-bit signed int + +#define U64(a) a ## ull +#define S64(a) a ## sll + +#endif + +/// Union for fast 16-bit type casting +union t16 { + u8 _u8[2]; ///< 8-bit unsigned char(s) + u16 _u16; ///< 16-bit unsigned shorts(s) +}; + +/// Union for fast 32-bit type casting +union t32 { + f32 _f32; ///< 32-bit floating point(s) + u32 _u32; ///< 32-bit unsigned int(s) + x32 _x32; ///< 32-bit fixed point(s) + u16 _u16[2]; ///< 16-bit unsigned shorts(s) + u8 _u8[4]; ///< 8-bit unsigned char(s) +}; + +/// Union for fast 64-bit type casting +union t64 { + f64 _f64; ///< 64-bit floating point + u64 _u64; ///< 64-bit unsigned long + f32 _f32[2]; ///< 32-bit floating point(s) + u32 _u32[2]; ///< 32-bit unsigned int(s) + x32 _x32[2]; ///< 32-bit fixed point(s) + u16 _u16[4]; ///< 16-bit unsigned shorts(s) + u8 _u8[8]; ///< 8-bit unsigned char(s) +}; + +/// Union for fast 128-bit type casting +union t128 { + struct + { + t64 ps0; ///< 64-bit paired single 0 + t64 ps1; ///< 64-bit paired single 1 + }; + __m128 a; ///< 128-bit floating point (__m128 maps to the XMM[0-7] registers) +}; + +/// Rectangle data structure +class Rect { +public: + Rect(int x0=0, int y0=0, int x1=0, int y1=0) { + x0_ = x0; + y0_ = y0; + x1_ = x1; + y1_ = y1; + } + ~Rect() { } + + int x0_; ///< Rect top left X-coordinate + int y0_; ///< Rect top left Y-coordinate + int x1_; ///< Rect bottom left X-coordinate + int y1_; ///< Rect bottom right Y-coordinate + + inline u32 width() const { return abs(x1_ - x0_); } + inline u32 height() const { return abs(y1_ - y0_); } + + inline bool operator == (const Rect& val) const { + return (x0_ == val.x0_ && y0_ == val.y0_ && x1_ == val.x1_ && y1_ == val.y1_); + } +}; + +#endif // COMMON_TYPES_H_ \ No newline at end of file diff --git a/src/common/src/x86_utils.cpp b/src/common/src/x86_utils.cpp new file mode 100644 index 00000000..8af6b986 --- /dev/null +++ b/src/common/src/x86_utils.cpp @@ -0,0 +1,236 @@ +/** + * Copyright (C) 2005-2012 Gekko Emulator + * + * @file x86_utils.cpp + * @author ShizZy + * @date 2012-12-23 + * @brief Utilities for the x86 architecture + * + * @section LICENSE + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details at + * http://www.gnu.org/copyleft/gpl.html + * + * Official project repository can be found at: + * http://code.google.com/p/gekko-gc-emu/ + */ + +#include "common.h" +#include "x86_utils.h" + +#ifdef _WIN32 +#define _interlockedbittestandset workaround_ms_header_bug_platform_sdk6_set +#define _interlockedbittestandreset workaround_ms_header_bug_platform_sdk6_reset +#define _interlockedbittestandset64 workaround_ms_header_bug_platform_sdk6_set64 +#define _interlockedbittestandreset64 workaround_ms_header_bug_platform_sdk6_reset64 +#include +#undef _interlockedbittestandset +#undef _interlockedbittestandreset +#undef _interlockedbittestandset64 +#undef _interlockedbittestandreset64 +#else + +//#include +#include + +#if defined __FreeBSD__ +#include +#include +#else +static inline void do_cpuid(unsigned int *eax, unsigned int *ebx, + unsigned int *ecx, unsigned int *edx) +{ +#ifdef _LP64 + // Note: EBX is reserved on Mac OS X and in PIC on Linux, so it has to + // restored at the end of the asm block. + __asm__ ( + "cpuid;" + "movl %%ebx,%1;" + : "=a" (*eax), + "=S" (*ebx), + "=c" (*ecx), + "=d" (*edx) + : "a" (*eax) + : "rbx" + ); +#else + __asm__ ( + "cpuid;" + "movl %%ebx,%1;" + : "=a" (*eax), + "=S" (*ebx), + "=c" (*ecx), + "=d" (*edx) + : "a" (*eax) + : "ebx" + ); +#endif +} +#endif + +static void __cpuid(int info[4], int x) +{ +#if defined __FreeBSD__ + do_cpuid((unsigned int)x, (unsigned int*)info); +#else + unsigned int eax = x, ebx = 0, ecx = 0, edx = 0; + do_cpuid(&eax, &ebx, &ecx, &edx); + info[0] = eax; + info[1] = ebx; + info[2] = ecx; + info[3] = edx; +#endif +} + +#endif + +namespace common { + +X86Utils::X86Utils() { + memset(this, 0, sizeof(*this)); +#ifdef _M_IX86 + +#elif defined (_M_X64) + support_x64_os_ = true; + support_sse_ = true; + support_sse2_ = true; +#endif + num_cores_ = 1; +#ifdef _WIN32 +#ifdef _M_IX86 + int f64 = 0; + IsWow64Process(GetCurrentProcess(), &f64); + support_x64_os_ = (f64 == 1) ? true : false; +#endif +#endif + // Assume CPU supports the CPUID instruction. Those that don't can barely + // boot modern OS:es anyway. + int cpu_id[4]; + char cpu_string[32]; + memset(cpu_string, 0, sizeof(cpu_string)); + + // Detect CPU's CPUID capabilities, and grab cpu string + __cpuid(cpu_id, 0x00000000); + u32 max_std_fn = cpu_id[0]; // EAX + *((int *)cpu_string) = cpu_id[1]; + *((int *)(cpu_string + 4)) = cpu_id[3]; + *((int *)(cpu_string + 8)) = cpu_id[2]; + __cpuid(cpu_id, 0x80000000); + u32 max_ex_fn = cpu_id[0]; + if (!strcmp(cpu_string, "GenuineIntel")) { + cpu_vendor_ = kVendorX86_Intel; + } else if (!strcmp(cpu_string, "AuthenticAMD")) { + cpu_vendor_ = kVendorX86_AMD; + } else { + cpu_vendor_ = kVendorX86_None; + } + + // Detect family and other misc stuff. + bool ht = false; + support_hyper_threading_ = ht; + logical_cpu_count_ = 1; + if (max_std_fn >= 1) { + __cpuid(cpu_id, 0x00000001); + logical_cpu_count_ = (cpu_id[1] >> 16) & 0xFF; + ht = (cpu_id[3] >> 28) & 1; + + if ((cpu_id[3] >> 25) & 1) support_sse_ = true; + if ((cpu_id[3] >> 26) & 1) support_sse2_ = true; + if ((cpu_id[2]) & 1) support_sse3_ = true; + if ((cpu_id[2] >> 9) & 1) support_ssse3_ = true; + if ((cpu_id[2] >> 19) & 1) support_sse4_1_ = true; + if ((cpu_id[2] >> 20) & 1) support_sse4_2_ = true; + } + if (max_ex_fn >= 0x80000004) { + // Extract brand string + __cpuid(cpu_id, 0x80000002); +// memcpy(brand_string, cpu_id, sizeof(cpu_id)); + __cpuid(cpu_id, 0x80000003); +// memcpy(brand_string + 16, cpu_id, sizeof(cpu_id)); + __cpuid(cpu_id, 0x80000004); +// memcpy(brand_string + 32, cpu_id, sizeof(cpu_id)); + } + num_cores_ = (logical_cpu_count_ == 0) ? 1 : logical_cpu_count_; + + if (max_ex_fn >= 0x80000008) { + // Get number of cores. This is a bit complicated. Following AMD manual here. + __cpuid(cpu_id, 0x80000008); + int apic_id_core_id_size = (cpu_id[2] >> 12) & 0xF; + if (apic_id_core_id_size == 0) { + if (ht) { + // New mechanism for modern Intel CPUs. + if (cpu_vendor_ == kVendorX86_Intel) { + __cpuid(cpu_id, 0x00000004); + int cores_x_package = ((cpu_id[0] >> 26) & 0x3F) + 1; + support_hyper_threading_ = (cores_x_package < logical_cpu_count_); + cores_x_package = ((logical_cpu_count_ % cores_x_package) == 0) ? cores_x_package : 1; + num_cores_ = (cores_x_package > 1) ? cores_x_package : num_cores_; + logical_cpu_count_ /= cores_x_package; + } + } + } else { + // Use AMD's new method. + num_cores_ = (cpu_id[2] & 0xFF) + 1; + } + } + LOG_NOTICE(TCOMMON, "CPU detected (%s)", this->Summary().c_str()); +} + +X86Utils::~X86Utils() { +} + +/** + * Check if an X86 extension is supported by the current architecture + * @param extension ExtensionX86 extension support to check for + * @return True if the extension is supported, otherwise false + */ +bool X86Utils::IsExtensionSupported(X86Utils::ExtensionX86 extension) { + switch (extension) { + case kExtensionX86_SSE: + return support_sse_; + case kExtensionX86_SSE2: + return support_sse2_; + case kExtensionX86_SSE3: + return support_sse3_; + case kExtensionX86_SSSE3: + return support_ssse3_; + case kExtensionX86_SSE4_1: + return support_sse4_1_; + case kExtensionX86_SSE4_2: + return support_sse4_2_; + } + return false; +} + +/** + * Gets a string summary of the X86 CPU information, suitable for printing + * @return String summary + */ +std::string X86Utils::Summary() { + const char* cpu_vendors[] = { + "Unknown", "Intel", "AMD" + }; + std::string res; + res = FormatStr("%s, %d core%s", cpu_vendors[cpu_vendor_], num_cores_, (num_cores_ > 1) ? "s" : ""); + if (support_sse4_2_) { + res += FormatStr(" (%i logical threads per physical core)", logical_cpu_count_); + } + if (support_sse_) res += ", SSE"; + if (support_sse2_) res += ", SSE2"; + if (support_sse3_) res += ", SSE3"; + if (support_ssse3_) res += ", SSSE3"; + if (support_sse4_1_) res += ", SSE4.1"; + if (support_sse4_2_) res += ", SSE4.2"; + if (support_hyper_threading_) res += ", HTT"; + //if (bLongMode) res += ", 64-bit support"; + return res; +} + +} // namespace diff --git a/src/common/src/x86_utils.h b/src/common/src/x86_utils.h new file mode 100644 index 00000000..6dc8c0d9 --- /dev/null +++ b/src/common/src/x86_utils.h @@ -0,0 +1,92 @@ +/** + * Copyright (C) 2005-2012 Gekko Emulator + * + * @file x86_utils.h + * @author ShizZy + * @date 2012-02-11 + * @brief Utilities for the x86 architecture + * + * @section LICENSE + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details at + * http://www.gnu.org/copyleft/gpl.html + * + * Official project repository can be found at: + * http://code.google.com/p/gekko-gc-emu/ + */ + +#ifndef COMMON_X86_UTILS_ +#define COMMON_X86_UTILS_ + +#include +#include "types.h" + +// Common namespace +namespace common { + +class X86Utils { +public: + /// Enumeration of X86 vendors + enum VendorX86 { + kVendorX86_None = 0, + kVendorX86_Intel, + kVendorX86_AMD, + kVendorX86_NumberOf + }; + + /// Enumeration of X86 extensions + enum ExtensionX86 { + kExtensionX86_None = 0, + kExtensionX86_SSE, + kExtensionX86_SSE2, + kExtensionX86_SSE3, + kExtensionX86_SSSE3, + kExtensionX86_SSE4_1, + kExtensionX86_SSE4_2, + kExtensionX86_NumberOf + }; + + X86Utils(); + ~X86Utils(); + + /** + * Check if an X86 extension is supported by the current architecture + * @param extension ExtensionX86 extension support to check for + * @return True if the extension is supported, otherwise false + */ + bool IsExtensionSupported(ExtensionX86 extension); + + /** + * Gets a string summary of the X86 CPU information, suitable for printing + * @return String summary + */ + std::string Summary(); + +private: + bool support_x64_os_; + bool support_x64_cpu_; + bool support_hyper_threading_; + + int num_cores_; + int logical_cpu_count_; + + bool support_sse_; + bool support_sse2_; + bool support_sse3_; + bool support_ssse3_; + bool support_sse4_1_; + bool support_sse4_2_; + + VendorX86 cpu_vendor_; +}; + +} // namespace + +#endif diff --git a/src/common/src/xml.cpp b/src/common/src/xml.cpp new file mode 100644 index 00000000..f4b91389 --- /dev/null +++ b/src/common/src/xml.cpp @@ -0,0 +1,487 @@ +/** + * Copyright (C) 2005-2012 Gekko Emulator + * + * @file xml.h + * @author ShizZy + * @date 2012-02-12 + * @brief Used for parsing XML configurations + * + * @section LICENSE + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details at + * http://www.gnu.org/copyleft/gpl.html + * + * Official project repository can be found at: + * http://code.google.com/p/gekko-gc-emu/ + */ + +#include +#include + +#include + +#include "common.h" +#include "misc_utils.h" +#include "config.h" +#include "log.h" + +/// Gets a RapidXML boolean element value +static bool GetXMLElementAsBool(rapidxml::xml_node<> *node, const char* element_name) { + rapidxml::xml_node<> *sub_node = node->first_node(element_name); + if (sub_node) { + return (E_OK == _stricmp(sub_node->value(), "true")) ? true : false; + } + return false; +} + +/// Gets a RapidXML string element value +static char* GetXMLElementAsString(rapidxml::xml_node<> *node, const char* element_name, + char* element_value) { + rapidxml::xml_node<> *sub_node = node->first_node(element_name); + if (sub_node) { + strcpy(element_value, sub_node->value()); + return element_value; + } + return NULL; +} + +/// Gets a RapidXML integer element value +static int GetXMLElementAsInt(rapidxml::xml_node<> *node, const char* element_name) { + rapidxml::xml_node<> *sub_node = node->first_node(element_name); + if (sub_node) { + return atoi(sub_node->value()); + } + return 0; +} + +namespace common { + +/** + * @brief Parse the "General" XML group + * @param node RapidXML node for the "General" XML group + * @param config Config class object to parse data into + */ +void ParseGeneralNode(rapidxml::xml_node<> *node, Config& config) { + // Don't parse the node if it doesn't exist! + if (!node) { + return; + } + char temp_str[MAX_PATH]; + config.set_enable_multicore(GetXMLElementAsBool(node, "EnableMultiCore")); + config.set_enable_idle_skipping(GetXMLElementAsBool(node, "EnableIdleSkipping")); + config.set_enable_hle(GetXMLElementAsBool(node, "EnableHLE")); + config.set_enable_auto_boot(GetXMLElementAsBool(node, "EnableAutoBoot")); + config.set_enable_cheats(GetXMLElementAsBool(node, "EnableCheats")); + config.set_default_boot_file(GetXMLElementAsString(node, "DefaultBootFile", temp_str), MAX_PATH); + + // Parse all search paths in the DVDImagePaths node + rapidxml::xml_node<> *sub_node = node->first_node("DVDImagePaths"); + if (sub_node) { + int i = 0; + for (rapidxml::xml_node<> *elem = sub_node->first_node("Path"); elem; + elem = elem->next_sibling()) { + + config.set_dvd_image_path(i, elem->value(), MAX_PATH); + LOG_NOTICE(TCONFIG, "Adding %s to DVD image search paths...\n", + config.dvd_image_path(i)); + i++; + // Stop if we have parsed the maximum paths + if (MAX_SEARCH_PATHS < i) { + LOG_WARNING(TCONFIG, "Maximum number of DVDImagePath search paths is %d, not parsing" + " any more!", MAX_SEARCH_PATHS); + break; + } + } + } +} + +/** + * @brief Parse the "Debug" XML group + * @param node RapidXML node for the "Debug" XML group + * @param config Config class object to parse data into + */ +void ParseDebugNode(rapidxml::xml_node<> *node, Config& config) { + // Don't parse the node if it doesn't exist! + if (!node) { + return; + } + config.set_enable_show_fps(GetXMLElementAsBool(node, "EnableShowFPS")); + config.set_enable_dump_opcode0(GetXMLElementAsBool(node, "EnableDumpOpcode0")); + config.set_enable_pause_on_unknown_opcode(GetXMLElementAsBool(node, + "EnablePauseOnUnknownOpcode")); + config.set_enable_dump_gcm_reads(GetXMLElementAsBool(node, "EnableDumpGCMReads")); +} + +/** + * @brief Parse the "Patches" and "Cheats" XML group + * @param node RapidXML node for the "Patches" or "Cheats" XML group + * @param config Config class object to parse data into + */ +void ParsePatchesNode(rapidxml::xml_node<> *node, Config& config, const char* node_name) { + int i = 0; + char node_name_str[8]; + + // Get lowercase section name + strcpy(node_name_str, node_name); + + // TODO: not available on Unix + common::LowerStr(node_name_str); + + // Parse all search patches in the Patches node + rapidxml::xml_node<> *sub_node = node->first_node(node_name); + if (sub_node) { + for (rapidxml::xml_node<> *elem = sub_node->first_node("Patch"); elem; + elem = elem->next_sibling()) { + + // Get enable attribute (note: defaults to true) + rapidxml::xml_attribute<> *attr = elem->first_attribute("enable"); + if (attr) { + if (E_OK == _stricmp(attr->value(), "false")) { + continue; // Patch is disabled, skip it + } + } + // Get address attribute + attr = elem->first_attribute("address"); + if (!attr) { + LOG_ERROR(TCONFIG, "Patch without 'address' attribute illegal!"); + continue; + } else { + u32 data = 0; + u32 address = 0; + { + // Convert address hexstring to unsigned int + std::stringstream ss; + ss << std::hex << attr->value(); + ss >> address; + } + attr = elem->first_attribute("instr"); + + // Get "data" attribute if no "instr" attribute + if (!attr) { + attr = elem->first_attribute("data"); + // Neither found - error + if (!attr) { + LOG_ERROR(TCONFIG, "Patch without 'instr' or 'data' attributes " + "illegal!"); + continue; + } else { + // Found data, convert hexstring to unsigned int + std::stringstream ss; + ss << std::hex << attr->value(); + ss >> data; + } + } else { + // Found instr + char instr_str[4]; + + // Convert to lowercase + strcpy(instr_str, attr->value()); + // TODO: not available on Unix + common::LowerStr(instr_str); + + // Convert instruction to equivalent PPC bytecode + // TODO(ShizZy): Pull this out to the PowerPC modules at some point + if (E_OK == _stricmp(instr_str, "blr")) { + data = 0x4E800020; // PowerPC BLR instruction bytecode + } else if (E_OK == _stricmp(instr_str, "nop")) { + data = 0x60000000; // PowerPC NOP instruction bytecode + } else { + LOG_ERROR(TCONFIG, "Patch with invalid 'instr' attribute illegal!"); + continue; + } + } + Config::Patch patch = { address, data }; + + if (E_OK == _stricmp(node_name_str, "patches")) { + LOG_NOTICE(TCONFIG, "Adding patch addr=0x%08x data=0x%08x to patches...\n", + address, data, node_name_str); + config.set_patches(i, patch); + } else if (E_OK == _stricmp(node_name_str, "cheats")) { + LOG_NOTICE(TCONFIG, "Adding cheat addr=0x%08x data=0x%08x to cheats...\n", + address, data, node_name_str); + config.set_cheats(i, patch); + } else { + LOG_ERROR(TCONFIG, "Unexpected patch type %s, ignoring...", node_name_str); + } + + // Stop if we have parsed the maximum patches + if (MAX_PATCHES_PER_GAME < ++i) { + LOG_WARNING(TCONFIG, "Maximum number of patches search paths is %d, not parsing" + " any more!", MAX_PATCHES_PER_GAME); + break; + } + } + } + } +} + +/** + * @brief Parse the "Boot" XML group + * @param node RapidXML node for the "Boot" XML group + * @param config Config class object to parse data into + */ +void ParseBootNode(rapidxml::xml_node<> *node, Config& config) { + // Don't parse the node if it doesn't exist! + if (!node) { + return; + } + config.set_enable_ipl(GetXMLElementAsBool(node, "EnableIPL")); + + ParsePatchesNode(node, config, "Patches"); + ParsePatchesNode(node, config, "Cheats"); +} + +/** + * @brief Parse the "Video" XML group + * @param node RapidXML node for the "Video" XML group + * @param config Config class object to parse data into + */ +void ParsePowerPCNode(rapidxml::xml_node<> *node, Config& config) { + // Don't parse the node if it doesn't exist! + if (!node) { + return; + } + rapidxml::xml_attribute<> *attr = node->first_attribute("core"); + + // Attribute not found - error + if (!attr) { + LOG_ERROR(TCONFIG, "PowerPC without 'core' attribute illegal!"); + } else { + char core_str[12] = "null"; + + // Convert to lowercase + strcpy(core_str, attr->value()); + // TODO: not available on Unix + common::LowerStr(core_str); + + // Use interpreter core + if (E_OK == _stricmp(core_str, "interpreter")) { + config.set_powerpc_core(Config::CPU_INTERPRETER); // Interpreter selected + // Use dynarec core + } else if (E_OK == _stricmp(core_str, "dynarec")) { + config.set_powerpc_core(Config::CPU_DYNAREC); // Dynarec selected + // Unsupported type + } else { + LOG_ERROR(TCONFIG, "Invalid PowerPC type %s for attribute 'core' selected!", + core_str); + } + // Set frequency + attr = node->first_attribute("freq"); + if (attr) { + config.set_powerpc_frequency(atoi(attr->value())); + } + LOG_NOTICE(TCONFIG, "Configured core=%s freq=%d", core_str, config.powerpc_frequency()); + } +} + +/** + * @brief Parse the "Video" XML group + * @param node RapidXML node for the "Video" XML group + * @param config Config class object to parse data into + */ +void ParseVideoNode(rapidxml::xml_node<> *node, Config& config) { + char res_str[512]; + Config::ResolutionType res; + + // Don't parse the node if it doesn't exist! + if (!node) { + return; + } + config.set_enable_fullscreen(GetXMLElementAsBool(node, "EnableFullscreen")); + + // Set resolutions + GetXMLElementAsString(node, "WindowResolution", res_str); + sscanf(res_str, "%d_%d", &res.width, &res.height); + config.set_window_resolution(res); + GetXMLElementAsString(node, "FullscreenResolution", res_str); + sscanf(res_str, "%d_%d", &res.width, &res.height); + config.set_fullscreen_resolution(res); + + // Parse all search renderer nodes + for (rapidxml::xml_node<> *elem = node->first_node("Renderer"); 1; ) { + Config::RendererConfig renderer_config; + + rapidxml::xml_attribute<> *attr = elem->first_attribute("name"); + + Config::RendererType type = Config::StringToRenderType(attr->value()); + + renderer_config.enable_wireframe = GetXMLElementAsBool(elem, "EnableWireframe"); + renderer_config.enable_shaders = GetXMLElementAsBool(elem, "EnableShaders"); + renderer_config.enable_textures = GetXMLElementAsBool(elem, "EnableTextures"); + renderer_config.enable_texture_dumping = GetXMLElementAsBool(elem, "EnableTextureDumping"); + renderer_config.anti_aliasing_mode = GetXMLElementAsInt(elem, "AntiAliasingMode"); + renderer_config.anistropic_filtering_mode = GetXMLElementAsInt(elem, "AnistropicFilteringMode"); + + config.set_renderer_config(type, renderer_config); + + LOG_NOTICE(TCONFIG, "Renderer %s configured", attr->value()); + + break; + } +} + +/** + * @brief Parse the "Devices" XML group + * @param node RapidXML node for the "Devices" XML group + * @param config Config class object to parse data into + */ +void ParseDevicesNode(rapidxml::xml_node<> *node, Config& config) { + // Don't parse the node if it doesn't exist!c + if (!node) { + return; + } + // Parse GameCube section + rapidxml::xml_node<> *gamecube_node = node->first_node("GameCube"); + if (!gamecube_node) { + return; + } + // Parse all MemSlot nodes + for (rapidxml::xml_node<> *elem = gamecube_node->first_node("MemSlot"); elem; + elem = elem->next_sibling("MemSlot")) { + Config::MemSlot slot_config; + + // Select MemSlot a or b + rapidxml::xml_attribute<> *attr = elem->first_attribute("slot"); + int slot = (E_OK == _stricmp(attr->value(), "a")) ? 0 : 1; + + // Enable + attr = elem->first_attribute("enable"); + slot_config.enable = (E_OK == _stricmp(attr->value(), "true")) ? true : false; + + // Select device + attr = elem->first_attribute("device"); + slot_config.device = 0; // Only support memcards right now + + LOG_NOTICE(TCONFIG, "Configured MemSlot[%d]=%s enabled=%s", slot, attr->value(), + slot_config.enable ? "true" : "false"); + + config.set_mem_slots(slot, slot_config); + } + // Parse all ControlerPort nodes + for (rapidxml::xml_node<> *elem = gamecube_node->first_node("ControllerPort"); elem; + elem = elem->next_sibling("ControllerPort")) { + Config::ControllerPort port_config; + + // Select MemSlot a or b + rapidxml::xml_attribute<> *attr = elem->first_attribute("port"); + int port = atoi(attr->value()); + + // Enable + attr = elem->first_attribute("enable"); + port_config.enable = (E_OK == _stricmp(attr->value(), "true")) ? true : false; + + // Select device + attr = elem->first_attribute("device"); + port_config.device = 0; // Only support memcards right now + + LOG_NOTICE(TCONFIG, "Configured ControllerPort[%d]=%s enabled=%s", port, attr->value(), + port_config.enable ? "true" : "false"); + + // Parse keyboard configuration - TODO: Move to EmuWindow (?) + rapidxml::xml_node<> *keyboard_node = elem->first_node("KeyboardController"); + if (keyboard_node) { + attr = keyboard_node->first_attribute("enable"); + port_config.keys.enable = (E_OK == _stricmp(attr->value(), "true")) ? true : false; + port_config.keys.key_code[Config::BUTTON_A] = GetXMLElementAsInt(keyboard_node, "AKey"); + port_config.keys.key_code[Config::BUTTON_B] = GetXMLElementAsInt(keyboard_node, "BKey"); + port_config.keys.key_code[Config::BUTTON_X] = GetXMLElementAsInt(keyboard_node, "XKey"); + port_config.keys.key_code[Config::BUTTON_Y] = GetXMLElementAsInt(keyboard_node, "YKey"); + port_config.keys.key_code[Config::TRIGGER_L] = GetXMLElementAsInt(keyboard_node, "LKey"); + port_config.keys.key_code[Config::TRIGGER_R] = GetXMLElementAsInt(keyboard_node, "RKey"); + port_config.keys.key_code[Config::BUTTON_Z] = GetXMLElementAsInt(keyboard_node, "ZKey"); + port_config.keys.key_code[Config::BUTTON_START] = GetXMLElementAsInt(keyboard_node, "StartKey"); + port_config.keys.key_code[Config::ANALOG_UP] = GetXMLElementAsInt(keyboard_node, "AnalogUpKey"); + port_config.keys.key_code[Config::ANALOG_DOWN] = GetXMLElementAsInt(keyboard_node, "AnalogDownKey"); + port_config.keys.key_code[Config::ANALOG_LEFT] = GetXMLElementAsInt(keyboard_node, "AnalogLeftKey"); + port_config.keys.key_code[Config::ANALOG_RIGHT] = GetXMLElementAsInt(keyboard_node, "AnalogRightKey"); + port_config.keys.key_code[Config::C_UP] = GetXMLElementAsInt(keyboard_node, "CUpKey"); + port_config.keys.key_code[Config::C_DOWN] = GetXMLElementAsInt(keyboard_node, "CDownKey"); + port_config.keys.key_code[Config::C_LEFT] = GetXMLElementAsInt(keyboard_node, "CLeftKey"); + port_config.keys.key_code[Config::C_RIGHT] = GetXMLElementAsInt(keyboard_node, "CRightKey"); + port_config.keys.key_code[Config::DPAD_UP] = GetXMLElementAsInt(keyboard_node, "DPadUpKey"); + port_config.keys.key_code[Config::DPAD_DOWN] = GetXMLElementAsInt(keyboard_node, "DPadDownKey"); + port_config.keys.key_code[Config::DPAD_LEFT] = GetXMLElementAsInt(keyboard_node, "DPadLeftKey"); + port_config.keys.key_code[Config::DPAD_RIGHT] = GetXMLElementAsInt(keyboard_node, "DPadRightKey"); + } + + // Parse joypad configuration + rapidxml::xml_node<> *joypad_node = elem->first_node("JoypadController"); + if (joypad_node) { + attr = joypad_node->first_attribute("enable"); + port_config.pads.enable = (E_OK == _stricmp(attr->value(), "true")) ? true : false; + port_config.pads.key_code[Config::BUTTON_A] = GetXMLElementAsInt(joypad_node, "AKey"); + port_config.pads.key_code[Config::BUTTON_B] = GetXMLElementAsInt(joypad_node, "BKey"); + port_config.pads.key_code[Config::BUTTON_X] = GetXMLElementAsInt(joypad_node, "XKey"); + port_config.pads.key_code[Config::BUTTON_Y] = GetXMLElementAsInt(joypad_node, "YKey"); + port_config.pads.key_code[Config::TRIGGER_L] = GetXMLElementAsInt(joypad_node, "LKey"); + port_config.pads.key_code[Config::TRIGGER_R] = GetXMLElementAsInt(joypad_node, "RKey"); + port_config.pads.key_code[Config::BUTTON_Z] = GetXMLElementAsInt(joypad_node, "ZKey"); + port_config.pads.key_code[Config::BUTTON_START] = GetXMLElementAsInt(joypad_node, "StartKey"); + port_config.pads.key_code[Config::ANALOG_UP] = GetXMLElementAsInt(joypad_node, "AnalogUpKey"); + port_config.pads.key_code[Config::ANALOG_DOWN] = GetXMLElementAsInt(joypad_node, "AnalogDownKey"); + port_config.pads.key_code[Config::ANALOG_LEFT] = GetXMLElementAsInt(joypad_node, "AnalogLeftKey"); + port_config.pads.key_code[Config::ANALOG_RIGHT] = GetXMLElementAsInt(joypad_node, "AnalogRightKey"); + port_config.pads.key_code[Config::C_UP] = GetXMLElementAsInt(joypad_node, "CUpKey"); + port_config.pads.key_code[Config::C_DOWN] = GetXMLElementAsInt(joypad_node, "CDownKey"); + port_config.pads.key_code[Config::C_LEFT] = GetXMLElementAsInt(joypad_node, "CLeftKey"); + port_config.pads.key_code[Config::C_RIGHT] = GetXMLElementAsInt(joypad_node, "CRightKey"); + port_config.pads.key_code[Config::DPAD_UP] = GetXMLElementAsInt(joypad_node, "DPadUpKey"); + port_config.pads.key_code[Config::DPAD_DOWN] = GetXMLElementAsInt(joypad_node, "DPadDownKey"); + port_config.pads.key_code[Config::DPAD_LEFT] = GetXMLElementAsInt(joypad_node, "DPadLeftKey"); + port_config.pads.key_code[Config::DPAD_RIGHT] = GetXMLElementAsInt(joypad_node, "DPadRightKey"); + } + config.set_controller_ports(port, port_config); + } +} + +/// Loads/parses an XML configuration file +void LoadXMLConfig(Config& config, const char* filename) { + // Open the XML file + char full_filename[MAX_PATH]; + strcpy(full_filename, config.program_dir()); + strcat(full_filename, filename); + std::ifstream ifs(full_filename); + + // Check that the file is valid + if (ifs.fail()) { + LOG_ERROR(TCONFIG, "XML configuration file %s failed to open!", filename); + return; + } + // Read and parse XML string + std::string xml_str((std::istreambuf_iterator(ifs)), std::istreambuf_iterator()); + rapidxml::xml_document<> doc; + doc.parse<0>(const_cast(xml_str.c_str())); + + // Try to load a system configuration + rapidxml::xml_node<> *node = doc.first_node("SysConfig"); + + // Try to load a game configuation + if (!node) { + node = doc.first_node("GameConfig"); + } + // Try to load a user configuation + if (!node) { + node = doc.first_node("UserConfig"); + } + // Not proper XML format + if (!node) { + LOG_ERROR(TCONFIG, "XML configuration file incorrect format %s!", filename) + return; + } + // Parse all sub nodes into the config + ParseGeneralNode(node->first_node("General"), config); + ParseDebugNode(node->first_node("Debug"), config); + ParseBootNode(node->first_node("Boot"), config); + ParsePowerPCNode(node->first_node("PowerPC"), config); + ParseVideoNode(node->first_node("Video"), config); + ParseDevicesNode(node->first_node("Devices"), config); +} + +} // namespace diff --git a/src/common/src/xml.h b/src/common/src/xml.h new file mode 100644 index 00000000..30447649 --- /dev/null +++ b/src/common/src/xml.h @@ -0,0 +1,41 @@ +/** + * Copyright (C) 2005-2012 Gekko Emulator + * + * @file xml.h + * @author ShizZy + * @date 2012-02-12 + * @brief Used for parsing XML configurations + * + * @section LICENSE + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details at + * http://www.gnu.org/copyleft/gpl.html + * + * Official project repository can be found at: + * http://code.google.com/p/gekko-gc-emu/ + */ + +#ifndef COMMON_XML_H_ +#define COMMON_XML_H_ + +#include "common.h" + +namespace common { + +/** + * @brief Loads/parses an XML configuration file + * @param config Reference to configuration object to populate + * @param filename Filename of XMl file to load + */ +void LoadXMLConfig(Config& config, const char* filename); + +} // namespace + +#endif // COMMON_XML_H_ diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt new file mode 100644 index 00000000..29a6aea8 --- /dev/null +++ b/src/core/CMakeLists.txt @@ -0,0 +1,10 @@ +set(SRCS src/core.cpp + src/memory.cpp + src/boot/apploader.cpp + src/boot/bootrom.cpp + src/boot/loader.cpp + src/hle/hle.cpp + src/hw/hw.cpp + ) + +add_library(core STATIC ${SRCS}) diff --git a/src/core/core.vcxproj b/src/core/core.vcxproj new file mode 100644 index 00000000..c976d292 --- /dev/null +++ b/src/core/core.vcxproj @@ -0,0 +1,186 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {8AEA7F29-3466-4786-A10D-6A4BD0610977} + core + + + + StaticLibrary + true + + + StaticLibrary + true + + + StaticLibrary + false + + + StaticLibrary + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + + + + + + + + + + + + + + + + + true + + + + + + + + + + + + + + + + + + + true + true + true + + + + + + + + + + + + + + + + + Fast + false + StreamingSIMDExtensions2 + + + + + + true + true + true + + + + + + + + + + + + + + + + + + {dfe335fc-755d-4baa-8452-94434f8a1edb} + + + + + + \ No newline at end of file -- cgit v1.2.3