├── src ├── Player.ico ├── ConfigTool.rc ├── ConfigTool.h ├── config.h ├── resource.h ├── config.h.in ├── LockGuard.h ├── CmdlineParser.h ├── Splash.h ├── GameInfo.h ├── Logger.h ├── CMakeLists.txt ├── Utils.h ├── InterfaceManager.h ├── Player.rc ├── Logger.cpp ├── Player.rc.in ├── GamePlayer.h ├── CmdlineParser.cpp ├── GameConfig.h ├── ConfigToolResource.h ├── Splash.cpp ├── GameConfig.cpp ├── Hotfix.cpp ├── Player.cpp ├── Utils.cpp └── ScriptUtils.h ├── .gitignore ├── Player.dsw ├── LICENSE ├── CMakeLists.txt ├── cmake └── FindVirtoolsSDK.cmake ├── .github └── workflows │ └── build.yml ├── ConfigTool.dsp ├── Player.dsp ├── README_zh-CN.md └── README.md /src/Player.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/doyaGu/BallancePlayer/HEAD/src/Player.ico -------------------------------------------------------------------------------- /src/ConfigTool.rc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/doyaGu/BallancePlayer/HEAD/src/ConfigTool.rc -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea/ 2 | /build*/ 3 | /cmake-build-*/ 4 | /Debug/ 5 | /Release/ 6 | /Bin/ 7 | CMakeLists.txt.user 8 | CMakeCache.txt 9 | CMakeFiles 10 | CMakeScripts 11 | Testing 12 | Makefile 13 | cmake_install.cmake 14 | install_manifest.txt 15 | compile_commands.json 16 | CTestTestfile.cmake 17 | _deps 18 | *.ncb 19 | *.opt 20 | *.plg 21 | -------------------------------------------------------------------------------- /src/ConfigTool.h: -------------------------------------------------------------------------------- 1 | #ifndef CONFIGTOOL_H 2 | #define CONFIGTOOL_H 3 | 4 | #ifndef WIN32_LEAN_AND_MEAN 5 | #define WIN32_LEAN_AND_MEAN 6 | #endif 7 | #include 8 | 9 | // Forward declarations 10 | class CGameConfig; 11 | 12 | // Public functions 13 | bool ShowConfigTool(HINSTANCE hInstance, CGameConfig &config, bool loadIni = true); 14 | 15 | #endif // CONFIGTOOL_H 16 | -------------------------------------------------------------------------------- /src/config.h: -------------------------------------------------------------------------------- 1 | #ifndef PLAYER_CONFIG_H 2 | #define PLAYER_CONFIG_H 3 | 4 | #define PLAYER_VERSION_MAJOR 0 5 | #define PLAYER_VERSION_MINOR 3 6 | #define PLAYER_VERSION_PATCH 8 7 | #define PLAYER_VERSION_TWEAK 0 8 | #define PLAYER_VERSION "0.3.8.0" 9 | #define PLAYER_DESCRIPTION "The brand-new player for Ballance" 10 | 11 | #define PLAYER_DEFAULT_WIDTH 640 12 | #define PLAYER_DEFAULT_HEIGHT 480 13 | #define PLAYER_DEFAULT_BPP 32 14 | 15 | #endif // PLAYER_CONFIG_H 16 | -------------------------------------------------------------------------------- /src/resource.h: -------------------------------------------------------------------------------- 1 | #define VS_VERSION_INFO 1 2 | #define IDS_MAIN_WND_NAME 102 3 | #define IDS_RENDER_WND_NAME 103 4 | #define IDS_MAIN_WND_CLASS_NAME 104 5 | #define IDS_RENDER_WND_CLASS_NAME 105 6 | #define IDI_PLAYER 106 7 | #define IDR_ACCEL 128 8 | #define IDM_APP_ABOUT 129 9 | #define IDD_FULLSCREEN_SETUP 130 10 | #define IDD_ABOUT 131 11 | #define IDC_LB_DRIVER 1007 12 | #define IDC_LB_SCREEN_MODE 1008 13 | -------------------------------------------------------------------------------- /src/config.h.in: -------------------------------------------------------------------------------- 1 | #ifndef PLAYER_CONFIG_H 2 | #define PLAYER_CONFIG_H 3 | 4 | #define PLAYER_VERSION_MAJOR @CMAKE_PROJECT_VERSION_MAJOR@ 5 | #define PLAYER_VERSION_MINOR @CMAKE_PROJECT_VERSION_MINOR@ 6 | #define PLAYER_VERSION_PATCH @CMAKE_PROJECT_VERSION_PATCH@ 7 | #define PLAYER_VERSION_TWEAK @CMAKE_PROJECT_VERSION_TWEAK@ 8 | #define PLAYER_VERSION "@CMAKE_PROJECT_VERSION@" 9 | #define PLAYER_DESCRIPTION "@CMAKE_PROJECT_DESCRIPTION@" 10 | 11 | #define PLAYER_DEFAULT_WIDTH @PLAYER_SCREEN_WIDTH@ 12 | #define PLAYER_DEFAULT_HEIGHT @PLAYER_SCREEN_HEIGHT@ 13 | #define PLAYER_DEFAULT_BPP @PLAYER_SCREEN_BPP@ 14 | 15 | #endif // PLAYER_CONFIG_H 16 | -------------------------------------------------------------------------------- /src/LockGuard.h: -------------------------------------------------------------------------------- 1 | #ifndef PLAYER_LOCKGUARD_H 2 | #define PLAYER_LOCKGUARD_H 3 | 4 | #ifndef WIN32_LEAN_AND_MEAN 5 | #define WIN32_LEAN_AND_MEAN 6 | #endif 7 | #include 8 | 9 | class LockGuard 10 | { 11 | public: 12 | explicit LockGuard(HANDLE mutex) : m_Mutex(mutex) {} 13 | 14 | ~LockGuard() 15 | { 16 | Release(); 17 | } 18 | 19 | void Release() 20 | { 21 | if (m_Mutex) 22 | { 23 | ::CloseHandle(m_Mutex); 24 | m_Mutex = NULL; 25 | } 26 | } 27 | 28 | private: 29 | LockGuard(const LockGuard &); 30 | LockGuard &operator=(const LockGuard &); 31 | 32 | HANDLE m_Mutex; 33 | }; 34 | 35 | #endif // PLAYER_LOCKGUARD_H -------------------------------------------------------------------------------- /Player.dsw: -------------------------------------------------------------------------------- 1 | Microsoft Developer Studio Workspace File, Format Version 6.00 2 | # WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! 3 | 4 | ############################################################################### 5 | 6 | Project: "ConfigTool"=.\ConfigTool.dsp - Package Owner=<4> 7 | 8 | Package=<5> 9 | {{{ 10 | }}} 11 | 12 | Package=<4> 13 | {{{ 14 | }}} 15 | 16 | ############################################################################### 17 | 18 | Project: "Player"=.\Player.dsp - Package Owner=<4> 19 | 20 | Package=<5> 21 | {{{ 22 | }}} 23 | 24 | Package=<4> 25 | {{{ 26 | }}} 27 | 28 | ############################################################################### 29 | 30 | Global: 31 | 32 | Package=<5> 33 | {{{ 34 | }}} 35 | 36 | Package=<3> 37 | {{{ 38 | }}} 39 | 40 | ############################################################################### 41 | 42 | -------------------------------------------------------------------------------- /src/CmdlineParser.h: -------------------------------------------------------------------------------- 1 | #ifndef PLAYER_CMDLINEPARSER_H 2 | #define PLAYER_CMDLINEPARSER_H 3 | 4 | #ifdef WIN32 5 | #pragma warning (disable: 4514 4786) 6 | #endif 7 | 8 | #include 9 | #include 10 | 11 | class CmdlineArg 12 | { 13 | public: 14 | CmdlineArg() : m_Values(NULL), m_Size(0), m_Jointed(false) {} 15 | CmdlineArg(const std::string *values, int size, bool jointed = false) : m_Values(values), m_Size(size), m_Jointed(jointed) {} 16 | 17 | bool GetValue(int i, std::string &value) const; 18 | bool GetValue(int i, long &value) const; 19 | 20 | int GetSize() const; 21 | 22 | private: 23 | const std::string *m_Values; 24 | int m_Size; 25 | bool m_Jointed; 26 | }; 27 | 28 | class CmdlineParser 29 | { 30 | public: 31 | CmdlineParser(int argc, char **argv); 32 | 33 | bool Next(CmdlineArg &arg, const char *longopt, char opt = '\0', int maxValueCount = 0); 34 | 35 | bool Skip(); 36 | 37 | bool Done() const; 38 | 39 | void Reset(); 40 | 41 | private: 42 | std::vector m_Args; 43 | int m_Index; 44 | }; 45 | 46 | #endif // PLAYER_CMDLINEPARSER_H -------------------------------------------------------------------------------- /src/Splash.h: -------------------------------------------------------------------------------- 1 | #ifndef PLAYER_SPLASH_H 2 | #define PLAYER_SPLASH_H 3 | 4 | #ifndef WIN32_LEAN_AND_MEAN 5 | #define WIN32_LEAN_AND_MEAN 6 | #endif 7 | #include 8 | #ifdef WIN32_LEAN_AND_MEAN 9 | #undef WIN32_LEAN_AND_MEAN 10 | #endif 11 | 12 | class CSplash 13 | { 14 | public: 15 | CSplash(); 16 | explicit CSplash(HINSTANCE hInstance); 17 | ~CSplash(); 18 | 19 | bool Show(); 20 | bool LoadBMP(LPCSTR lpFileName); 21 | DWORD GetWidth() const; 22 | DWORD GetHeight() const; 23 | DWORD GetBPP() const; 24 | DWORD GetDIBHeaderSize() const; 25 | DWORD GetPaletteNumColors() const; 26 | DWORD GetPaletteNumEntries() const; 27 | DWORD GetPaletteSize() const; 28 | BYTE *GetPaletteData() const; 29 | BYTE *GetPaletteEntry(DWORD index) const; 30 | BITMAPINFO *GetBitmapInfo() const; 31 | BYTE *GetBitmapData() const; 32 | HPALETTE GetPalette() const; 33 | 34 | private: 35 | CSplash(const CSplash &); 36 | CSplash &operator=(const CSplash &); 37 | 38 | BYTE *m_Data; 39 | HWND m_hWnd; 40 | HINSTANCE m_hInstance; 41 | }; 42 | 43 | #endif /* PLAYER_SPLASH_H */ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 kakuty 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/GameInfo.h: -------------------------------------------------------------------------------- 1 | #ifndef PLAYER_GAMEINFO_H 2 | #define PLAYER_GAMEINFO_H 3 | 4 | #include 5 | 6 | class CGameInfo 7 | { 8 | public: 9 | CGameInfo() 10 | : next(NULL), 11 | hkRoot(0x80000001), 12 | gameScore(0), 13 | levelScore(0), 14 | type(0), 15 | gameID(0), 16 | levelID(0), 17 | gameBonus(0), 18 | levelBonus(0), 19 | levelReached(0) 20 | { 21 | memset(gameName, 0, sizeof(gameName)); 22 | memset(levelName, 0, sizeof(levelName)); 23 | memset(fileName, 0, sizeof(fileName)); 24 | memset(path, 0, sizeof(path)); 25 | strcpy(regSubkey, "\"Software\\\\Ballance\\\\Settings\""); 26 | } 27 | 28 | virtual ~CGameInfo() {} 29 | 30 | CGameInfo *next; 31 | char gameName[128]; 32 | char levelName[128]; 33 | char fileName[128]; 34 | char path[128]; 35 | char regSubkey[512]; 36 | unsigned long hkRoot; 37 | int gameScore; 38 | int levelScore; 39 | int type; 40 | int gameID; 41 | int levelID; 42 | int gameBonus; 43 | int levelBonus; 44 | int levelReached; 45 | }; 46 | 47 | #endif // PLAYER_GAMEINFO_H -------------------------------------------------------------------------------- /src/Logger.h: -------------------------------------------------------------------------------- 1 | #ifndef PLAYER_LOGGER_H 2 | #define PLAYER_LOGGER_H 3 | 4 | #include 5 | #include 6 | 7 | class CLogger 8 | { 9 | public: 10 | enum Level 11 | { 12 | LEVEL_OFF = 0, 13 | LEVEL_ERROR = 1, 14 | LEVEL_WARN = 2, 15 | LEVEL_INFO = 3, 16 | LEVEL_DEBUG = 4, 17 | }; 18 | 19 | static CLogger &Get(); 20 | 21 | ~CLogger(); 22 | 23 | void Open(const char *filename, bool overwrite = true, int level = LEVEL_INFO); 24 | void Close(); 25 | 26 | bool IsConsoleOpened() const { return m_ConsoleOpened; } 27 | void OpenConsole(bool opened); 28 | 29 | int GetLevel() const { return m_Level; } 30 | void SetLevel(int level) { m_Level = level; } 31 | 32 | void Debug(const char *fmt, ...); 33 | void Info(const char *fmt, ...); 34 | void Warn(const char *fmt, ...); 35 | void Error(const char *fmt, ...); 36 | 37 | private: 38 | void Log(const char *level, const char *fmt, va_list args); 39 | 40 | CLogger(); 41 | CLogger(const CLogger &); 42 | CLogger &operator=(const CLogger &); 43 | 44 | int m_Level; 45 | bool m_ConsoleOpened; 46 | FILE *m_File; 47 | }; 48 | 49 | #endif // PLAYER_LOGGER_H 50 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_SOURCE_DIR}/config.h) 2 | configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Player.rc.in ${CMAKE_CURRENT_SOURCE_DIR}/Player.rc) 3 | 4 | set(PLAYER_HEADERS 5 | GamePlayer.h 6 | GameConfig.h 7 | Splash.h 8 | ScriptUtils.h 9 | CmdlineParser.h 10 | LockGuard.h 11 | Logger.h 12 | Utils.h 13 | ) 14 | 15 | set(PLAYER_SOURCES 16 | Player.cpp 17 | GamePlayer.cpp 18 | GameConfig.cpp 19 | Hotfix.cpp 20 | Splash.cpp 21 | CmdlineParser.cpp 22 | Logger.cpp 23 | Utils.cpp 24 | Player.rc 25 | ) 26 | 27 | add_executable(${PLAYER_NAME} ${PLAYER_HEADERS} ${PLAYER_SOURCES}) 28 | 29 | set_target_properties(${PLAYER_NAME} PROPERTIES 30 | OUTPUT_NAME ${PLAYER_NAME} 31 | WIN32_EXECUTABLE TRUE 32 | ) 33 | 34 | target_compile_definitions(${PLAYER_NAME} PRIVATE 35 | UNICODE _UNICODE 36 | ) 37 | 38 | target_link_libraries(${PLAYER_NAME} PRIVATE CK2 VxMath) 39 | 40 | add_executable(ConfigTool 41 | ConfigTool.cpp 42 | GameConfig.cpp 43 | Utils.cpp 44 | ConfigTool.rc 45 | 46 | ConfigTool.h 47 | GameConfig.h 48 | Utils.h 49 | ) 50 | 51 | set_target_properties(ConfigTool PROPERTIES 52 | WIN32_EXECUTABLE TRUE 53 | ) 54 | 55 | target_compile_definitions(ConfigTool PRIVATE 56 | UNICODE _UNICODE 57 | CONFIGTOOL_STANDALONE 58 | ) 59 | 60 | target_link_libraries(ConfigTool PRIVATE VxMath) 61 | 62 | install(TARGETS ${PLAYER_NAME} ConfigTool RUNTIME DESTINATION Bin) 63 | 64 | set(CPACK_PACKAGE_NAME "${PROJECT_NAME}") 65 | set(CPACK_PACKAGE_VENDOR "Kakuty") 66 | set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CMAKE_PROJECT_VERSION}") 67 | set(CPACK_PACKAGE_INSTALL_DIRECTORY "${CPACK_PACKAGE_NAME}") 68 | set(CPACK_SOURCE_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}") 69 | set(CPACK_GENERATOR "ZIP") 70 | include(CPack) -------------------------------------------------------------------------------- /src/Utils.h: -------------------------------------------------------------------------------- 1 | #ifndef PLAYER_UTILS_H 2 | #define PLAYER_UTILS_H 3 | 4 | #include 5 | 6 | #include "VxMathDefines.h" 7 | 8 | namespace utils 9 | { 10 | bool FileOrDirectoryExists(const char *file); 11 | bool DirectoryExists(const char *dir); 12 | 13 | size_t GetCurrentPath(char *buffer, size_t size); 14 | 15 | bool IsAbsolutePath(const char *path); 16 | bool GetAbsolutePath(char *buffer, size_t size, const char *path, bool trailing = false); 17 | 18 | char *ConcatPath(char *buffer, size_t size, const char *path1, const char *path2); 19 | 20 | const char *FindLastPathSeparator(const char *path); 21 | bool HasTrailingPathSeparator(const char *path); 22 | bool RemoveTrailingPathSeparator(char *path); 23 | 24 | int CharToWchar(const char *charStr, wchar_t *wcharStr, int size); 25 | int WcharToChar(const wchar_t *wcharStr, char *charStr, int size); 26 | 27 | void CRC32(const void *key, size_t len, size_t seed, void *out); 28 | 29 | VX_PIXELFORMAT String2PixelFormat(const char *str, size_t max); 30 | const char *PixelFormat2String(VX_PIXELFORMAT format); 31 | 32 | bool IniGetString(const char *section, const char *name, char *str, int size, const char *filename); 33 | bool IniGetInteger(const char *section, const char *name, int &value, const char *filename); 34 | bool IniGetBoolean(const char *section, const char *name, bool &value, const char *filename); 35 | bool IniGetPixelFormat(const char *section, const char *name, VX_PIXELFORMAT &value, const char *filename); 36 | bool IniSetString(const char *section, const char *name, const char *str, const char *filename); 37 | bool IniSetInteger(const char *section, const char *name, int value, const char *filename); 38 | bool IniSetBoolean(const char *section, const char *name, bool value, const char *filename); 39 | bool IniSetPixelFormat(const char *section, const char *name, VX_PIXELFORMAT value, const char *filename); 40 | } 41 | 42 | #endif // PLAYER_UTILS_H 43 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | 3 | project(BallancePlayer 4 | VERSION 0.3.8.0 5 | DESCRIPTION "The brand-new player for Ballance" 6 | HOMEPAGE_URL https://github.com/doyaGu/BallancePlayer 7 | ) 8 | 9 | set(CMAKE_CXX_STANDARD 98) 10 | set(CMAKE_CXX_STANDARD_REQUIRED NO) 11 | set(CMAKE_CXX_EXTENSIONS YES) 12 | 13 | # Add path for custom modules 14 | list(INSERT CMAKE_MODULE_PATH 0 "${CMAKE_CURRENT_SOURCE_DIR}/cmake") 15 | 16 | include(GenerateExportHeader) 17 | include(GNUInstallDirs) 18 | include(CMakePackageConfigHelpers) 19 | 20 | # Use folders to organize targets in an IDE 21 | set_property(GLOBAL PROPERTY USE_FOLDERS ON) 22 | set_property(GLOBAL PROPERTY PREDEFINED_TARGETS_FOLDER "CMakeTargets") 23 | 24 | if (NOT WIN32) 25 | message(FATAL_ERROR "Only Windows is supported.") 26 | endif () 27 | 28 | # Use relative paths 29 | if (WIN32) 30 | set(CMAKE_USE_RELATIVE_PATHS TRUE) 31 | set(CMAKE_SUPPRESS_REGENERATION TRUE) 32 | endif () 33 | 34 | if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 35 | message(STATUS "Setting build type to 'Release' as no build type was specified") 36 | set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the build type (Debug/Release)" FORCE) 37 | endif () 38 | 39 | if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 40 | set(CMAKE_INSTALL_PREFIX "${PROJECT_SOURCE_DIR}" CACHE PATH "The root directory of the installation" FORCE) 41 | message(STATUS "Setting default install directory to ${CMAKE_INSTALL_PREFIX} as no install directory was specified") 42 | endif () 43 | 44 | # Disable msvc unsafe warnings 45 | add_compile_definitions( 46 | $<$:_CRT_SECURE_NO_WARNINGS> 47 | $<$:_CRT_NONSTDC_NO_WARNINGS> 48 | ) 49 | 50 | find_package(VirtoolsSDK REQUIRED) 51 | 52 | if (NOT DEFINED PLAYER_NAME) 53 | set(PLAYER_NAME "Player") 54 | endif () 55 | 56 | set(PLAYER_VERSION ${CMAKE_PROJECT_VERSION}) 57 | 58 | set(PLAYER_SCREEN_WIDTH 640 CACHE STRING "Player screen width default value" FORCE) 59 | set(PLAYER_SCREEN_HEIGHT 480 CACHE STRING "Player screen height default value" FORCE) 60 | set(PLAYER_SCREEN_BPP 32 CACHE STRING "Player screen bpp default value" FORCE) 61 | 62 | add_subdirectory(src) -------------------------------------------------------------------------------- /cmake/FindVirtoolsSDK.cmake: -------------------------------------------------------------------------------- 1 | # Find the Virtools SDK header + import library 2 | # 3 | # VIRTOOLS_SDK_PATH - Path to Virtools SDK. 4 | # VIRTOOLS_SDK_INCLUDE_DIR - Where to find CKAll.h 5 | # VIRTOOLS_SDK_LIBRARIES - List of libraries when using VirtoolsSDK. 6 | # VirtoolsSDK_FOUND - True if Virtools SDK found. 7 | # VirtoolsSDK::CK2 - Imported library of CK2 8 | # VirtoolsSDK::VxMath - Imported library of VxMath 9 | 10 | find_path(VIRTOOLS_SDK_INCLUDE_DIR CKAll.h 11 | HINTS $ENV{VIRTOOLS_SDK_PATH} ${VIRTOOLS_SDK_PATH} 12 | PATH_SUFFIXES Include Includes 13 | DOC "Where the Virtools SDK headers can be found" 14 | ) 15 | 16 | set(VIRTOOLS_SDK_LIBRARY_NAMES CK2 VxMath) 17 | 18 | set(VIRTOOLS_SDK_LIBRARIES) 19 | foreach (LIBNAME IN LISTS VIRTOOLS_SDK_LIBRARY_NAMES) 20 | find_library(VIRTOOLS_SDK_${LIBNAME} NAMES ${LIBNAME} 21 | HINTS $ENV{VIRTOOLS_SDK_PATH} ${VIRTOOLS_SDK_PATH} 22 | PATH_SUFFIXES Lib 23 | DOC "Where the Virtools SDK libraries can be found" 24 | ) 25 | list(APPEND VIRTOOLS_SDK_LIBRARIES ${VIRTOOLS_SDK_${LIBNAME}}) 26 | endforeach () 27 | 28 | include(FindPackageHandleStandardArgs) 29 | find_package_handle_standard_args(VirtoolsSDK DEFAULT_MSG VIRTOOLS_SDK_LIBRARIES VIRTOOLS_SDK_INCLUDE_DIR) 30 | 31 | if (VirtoolsSDK_FOUND) 32 | foreach (LIBNAME IN LISTS VIRTOOLS_SDK_LIBRARY_NAMES) 33 | if (NOT TARGET ${LIBNAME}) 34 | add_library(${LIBNAME} UNKNOWN IMPORTED) 35 | add_library(VirtoolsSDK::${LIBNAME} ALIAS ${LIBNAME}) 36 | set_target_properties(${LIBNAME} PROPERTIES 37 | IMPORTED_LOCATION ${VIRTOOLS_SDK_${LIBNAME}} 38 | INTERFACE_INCLUDE_DIRECTORIES ${VIRTOOLS_SDK_INCLUDE_DIR} 39 | ) 40 | # Disable strict const-qualification conformance for pointers initialized by using string literals 41 | target_compile_options(${LIBNAME} INTERFACE 42 | $<$:$<$:/Zc:strictStrings->> 43 | $<$:$<$:-fpermissive>> 44 | $<$:$<$:-Wno-write-strings>> 45 | ) 46 | endif () 47 | endforeach () 48 | endif () -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | branches: [ "main" ] 8 | 9 | jobs: 10 | build: 11 | runs-on: windows-latest 12 | 13 | strategy: 14 | matrix: 15 | sdk_version: ["2.1", "2.5", "3.0", "3.5"] 16 | configuration: [Release, Debug] 17 | 18 | env: 19 | PROJECT_NAME: Player 20 | BASE_BUILD_DIR: ${{ github.workspace }}/build 21 | BASE_DIST_DIR: ${{ github.workspace }}/dist 22 | CMAKE_GENERATOR: "Ninja" 23 | 24 | steps: 25 | - name: Checkout source code 26 | uses: actions/checkout@v4 27 | 28 | - name: Checkout Virtools SDK 29 | uses: actions/checkout@v4 30 | with: 31 | repository: doyaGu/Virtools-SDK-${{ matrix.sdk_version }} 32 | path: ${{ github.workspace }}/Virtools-SDK-${{ matrix.sdk_version }} 33 | 34 | - name: Get current branch and commit hash 35 | shell: bash 36 | run: | 37 | echo "GIT_BRANCH=$(echo ${GITHUB_REF#refs/heads/})" >> "$GITHUB_ENV" 38 | echo "GIT_SHA=$(git rev-parse --short "$GITHUB_SHA")" >> "$GITHUB_ENV" 39 | 40 | - name: Setup CMake 41 | uses: lukka/get-cmake@latest 42 | 43 | - name: Setup MSVC 44 | uses: TheMrMilchmann/setup-msvc-dev@v3.0.0 45 | with: 46 | arch: x86 47 | 48 | - name: Configure CMake 49 | run: > 50 | cmake -G "${{ env.CMAKE_GENERATOR }}" -B ${{ env.BASE_BUILD_DIR }}/${{ matrix.sdk_version }}/${{ matrix.configuration }} 51 | -DCMAKE_BUILD_TYPE=${{ matrix.configuration }} 52 | -DVIRTOOLS_SDK_PATH=${{ github.workspace }}/Virtools-SDK-${{ matrix.sdk_version }} 53 | -DCMAKE_INSTALL_PREFIX=${{ env.BASE_DIST_DIR }}/${{ matrix.sdk_version }}/${{ matrix.configuration }} 54 | 55 | - name: Build the project 56 | run: cmake --build ${{ env.BASE_BUILD_DIR }}/${{ matrix.sdk_version }}/${{ matrix.configuration }} 57 | 58 | - name: Install binaries to distribution folder 59 | run: cmake --install ${{ env.BASE_BUILD_DIR }}/${{ matrix.sdk_version }}/${{ matrix.configuration }} --prefix ${{ env.BASE_DIST_DIR }}/${{ matrix.sdk_version }}/${{ matrix.configuration }} 60 | 61 | - name: Upload build artifacts 62 | uses: actions/upload-artifact@v4.3.3 63 | with: 64 | name: ${{ env.PROJECT_NAME }}-${{ matrix.sdk_version }}-${{ env.GIT_BRANCH }}-${{ env.GIT_SHA }}-${{ matrix.configuration }} 65 | path: ${{ env.BASE_DIST_DIR }}/${{ matrix.sdk_version }}/${{ matrix.configuration }} 66 | -------------------------------------------------------------------------------- /src/InterfaceManager.h: -------------------------------------------------------------------------------- 1 | #ifndef PLAYER_INTERFACEMANAGER_H 2 | #define PLAYER_INTERFACEMANAGER_H 3 | 4 | #include "CKBaseManager.h" 5 | #include "CKContext.h" 6 | 7 | #include "GameInfo.h" 8 | 9 | #define TT_INTERFACE_MANAGER_GUID CKGUID(0x30833801, 0x6DEE620D) 10 | 11 | enum TT_MSG 12 | { 13 | TT_MSG_NO_GAMEINFO = 0x5F7, 14 | TT_MSG_CMO_RESTART = 0x5F8, 15 | TT_MSG_CMO_LOAD = 0x5F9, 16 | TT_MSG_EXIT_TO_SYS = 0x5FA, 17 | TT_MSG_EXIT_TO_TITLE = 0x5FB, 18 | TT_MSG_LIMIT_FPS = 0x5FC, 19 | TT_MSG_SCREEN_MODE_CHG = 0x5FD, 20 | TT_MSG_GO_FULLSCREEN = 0x678, 21 | TT_MSG_STOP_FULLSCREEN = 0x679, 22 | }; 23 | 24 | class InterfaceManager : public CKBaseManager 25 | { 26 | public: 27 | int GetScreenMode() const 28 | { 29 | return m_ScreenMode; 30 | } 31 | 32 | void SetScreenMode(int screenMode) 33 | { 34 | m_ScreenMode = screenMode; 35 | } 36 | 37 | int GetDriver() const 38 | { 39 | return m_Driver; 40 | } 41 | 42 | void SetDriver(int driver) 43 | { 44 | m_Driver = driver; 45 | } 46 | 47 | bool IsTaskSwitchEnabled() const 48 | { 49 | return m_TaskSwitchEnabled; 50 | } 51 | 52 | void SetTaskSwitchEnabled(bool enable) 53 | { 54 | m_TaskSwitchEnabled = enable; 55 | } 56 | 57 | bool IsRookie() const 58 | { 59 | return m_Rookie; 60 | } 61 | 62 | void SetRookie(bool rookie) 63 | { 64 | m_Rookie = rookie; 65 | } 66 | 67 | CGameInfo *GetGameInfo() const 68 | { 69 | return m_GameInfo; 70 | } 71 | 72 | void SetGameInfo(CGameInfo *gameInfo) 73 | { 74 | m_GameInfo = gameInfo; 75 | } 76 | 77 | char *GetCmoName() 78 | { 79 | return &m_CmoName[0]; 80 | } 81 | 82 | void SetCmoName(const char *name) 83 | { 84 | strncpy(m_CmoName, name, sizeof(m_CmoName)); 85 | } 86 | 87 | bool IsWindowActivated() const 88 | { 89 | return m_WindowActivated; 90 | } 91 | 92 | void SetWindowActivated(bool enable = true) 93 | { 94 | m_WindowActivated = enable; 95 | } 96 | 97 | static InterfaceManager *GetManager(CKContext *context) 98 | { 99 | return (InterfaceManager *)context->GetManagerByGuid(TT_INTERFACE_MANAGER_GUID); 100 | } 101 | 102 | protected: 103 | int m_ScreenMode; 104 | int m_Driver; 105 | bool m_TaskSwitchEnabled; 106 | bool m_Rookie; 107 | CGameInfo *m_GameInfo; 108 | char m_CmoName[640]; 109 | bool m_WindowActivated; 110 | int m_ArrayList[4]; 111 | }; 112 | 113 | #endif // PLAYER_INTERFACEMANAGER_H -------------------------------------------------------------------------------- /src/Player.rc: -------------------------------------------------------------------------------- 1 | #include "resource.h" 2 | 3 | #include 4 | 5 | ///////////////////////////////////////////////////////////////////////////// 6 | // 7 | // Accelerator 8 | // 9 | 10 | IDR_ACCEL ACCELERATORS MOVEABLE PURE 11 | BEGIN 12 | "/", IDM_APP_ABOUT, ASCII, ALT 13 | END 14 | 15 | ///////////////////////////////////////////////////////////////////////////// 16 | // 17 | // Icon 18 | // 19 | 20 | // Icon with lower ID value placed first to ensure application icon 21 | // remains consistent on all systems. 22 | IDI_PLAYER ICON DISCARDABLE "Player.ico" 23 | 24 | ///////////////////////////////////////////////////////////////////////////// 25 | // 26 | // Dialog 27 | // 28 | 29 | IDD_FULLSCREEN_SETUP DIALOGEX 0, 0, 400, 200 30 | STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION 31 | CAPTION "FullScreen Setup" 32 | FONT 8, "MS Sans Serif" 33 | BEGIN 34 | DEFPUSHBUTTON "OK",1,100,144,80,16 35 | LTEXT "Driver",-1,10,10,24,8 36 | LTEXT "Screen Mode",-1,270,10,48,8 37 | LISTBOX IDC_LB_DRIVER,10,20,250,120,LBS_SORT | 38 | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP 39 | LISTBOX IDC_LB_SCREEN_MODE,270,20,120,160,LBS_SORT | 40 | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP 41 | PUSHBUTTON "Cancel",2,100,164,80,16 42 | END 43 | 44 | IDD_ABOUT DIALOGEX 0, 0, 150, 60 45 | STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU 46 | CAPTION "About" 47 | FONT 8, "MS Sans Serif" 48 | BEGIN 49 | CTEXT "The Brand New Ballance Player",-1,0,10,150,8 50 | CTEXT "By Kakuty",-1,0,20,150,8 51 | DEFPUSHBUTTON "OK",IDOK,60,35,30,15,WS_GROUP 52 | END 53 | 54 | ///////////////////////////////////////////////////////////////////////////// 55 | // 56 | // Version 57 | // 58 | 59 | VS_VERSION_INFO VERSIONINFO 60 | FILEVERSION 0,3,8,0 61 | PRODUCTVERSION 0,3,8,0 62 | FILEFLAGSMASK 0x3fL 63 | #ifdef _DEBUG 64 | FILEFLAGS 0x1L 65 | #else 66 | FILEFLAGS 0x0L 67 | #endif 68 | FILEOS 0x40004L 69 | FILETYPE 0x1L 70 | FILESUBTYPE 0x0L 71 | BEGIN 72 | BLOCK "StringFileInfo" 73 | BEGIN 74 | BLOCK "040904b0" 75 | BEGIN 76 | VALUE "Comments", "\0" 77 | VALUE "CompanyName", "Kakuty\0" 78 | VALUE "FileDescription", "Ballance Player\0" 79 | VALUE "FileVersion", "0.3.8.0\0" 80 | VALUE "InternalName", "Player\0" 81 | VALUE "LegalCopyright", "Copyright (C) 2025\0" 82 | VALUE "LegalTrademarks", "\0" 83 | VALUE "OriginalFilename", "Player.exe\0" 84 | VALUE "PrivateBuild", "\0" 85 | VALUE "ProductName", "Ballance Player\0" 86 | VALUE "ProductVersion", "0.3.8.0\0" 87 | VALUE "SpecialBuild", "\0" 88 | END 89 | END 90 | BLOCK "VarFileInfo" 91 | BEGIN 92 | VALUE "Translation", 0x409, 1200 93 | END 94 | END 95 | -------------------------------------------------------------------------------- /src/Logger.cpp: -------------------------------------------------------------------------------- 1 | #include "Logger.h" 2 | 3 | #ifndef WIN32_LEAN_AND_MEAN 4 | #define WIN32_LEAN_AND_MEAN 5 | #endif 6 | #include 7 | 8 | CLogger &CLogger::Get() 9 | { 10 | static CLogger logger; 11 | return logger; 12 | } 13 | 14 | CLogger::~CLogger() 15 | { 16 | Close(); 17 | } 18 | 19 | void CLogger::Open(const char *filename, bool overwrite, int level) 20 | { 21 | m_Level = level; 22 | 23 | if (m_File) 24 | return; 25 | 26 | if (overwrite) 27 | m_File = fopen(filename, "w"); 28 | else 29 | m_File = fopen(filename, "a"); 30 | } 31 | 32 | void CLogger::Close() 33 | { 34 | if (m_ConsoleOpened) 35 | { 36 | ::FreeConsole(); 37 | m_ConsoleOpened = false; 38 | } 39 | 40 | if (m_File) 41 | { 42 | fclose(m_File); 43 | m_File = NULL; 44 | } 45 | } 46 | 47 | void CLogger::OpenConsole(bool opened) 48 | { 49 | if (opened) 50 | { 51 | ::AllocConsole(); 52 | freopen("CONOUT$", "w", stdout); 53 | m_ConsoleOpened = true; 54 | } 55 | else 56 | { 57 | freopen("CON", "w", stdout); 58 | ::FreeConsole(); 59 | m_ConsoleOpened = false; 60 | } 61 | } 62 | 63 | void CLogger::Debug(const char *fmt, ...) 64 | { 65 | if (m_Level >= LEVEL_DEBUG) 66 | { 67 | va_list args; 68 | va_start(args, fmt); 69 | Log("DEBUG", fmt, args); 70 | va_end(args); 71 | } 72 | } 73 | 74 | void CLogger::Info(const char *fmt, ...) 75 | { 76 | if (m_Level >= LEVEL_INFO) 77 | { 78 | va_list args; 79 | va_start(args, fmt); 80 | Log("INFO", fmt, args); 81 | va_end(args); 82 | } 83 | } 84 | 85 | void CLogger::Warn(const char *fmt, ...) 86 | { 87 | if (m_Level >= LEVEL_WARN) 88 | { 89 | va_list args; 90 | va_start(args, fmt); 91 | Log("WARN", fmt, args); 92 | va_end(args); 93 | } 94 | } 95 | 96 | void CLogger::Error(const char *fmt, ...) 97 | { 98 | if (m_Level >= LEVEL_ERROR) 99 | { 100 | va_list args; 101 | va_start(args, fmt); 102 | Log("ERROR", fmt, args); 103 | va_end(args); 104 | } 105 | } 106 | 107 | void CLogger::Log(const char *level, const char *fmt, va_list args) 108 | { 109 | SYSTEMTIME sys; 110 | GetLocalTime(&sys); 111 | 112 | FILE *outs[] = {stdout, m_File}; 113 | 114 | for (int i = 0; i < 2; ++i) 115 | { 116 | FILE *out = outs[i]; 117 | if (!out) 118 | continue; 119 | 120 | fprintf(out, "[%02d/%02d/%d %02d:%02d:%02d.%03d] ", 121 | sys.wMonth, sys.wDay, sys.wYear, 122 | sys.wHour, sys.wMinute, sys.wSecond, sys.wMilliseconds); 123 | fprintf(out, "[%s]: ", level); 124 | vfprintf(out, fmt, args); 125 | fputc('\n', out); 126 | fflush(out); 127 | } 128 | } 129 | 130 | CLogger::CLogger() : m_Level(LEVEL_OFF), m_ConsoleOpened(false), m_File(NULL) {} -------------------------------------------------------------------------------- /src/Player.rc.in: -------------------------------------------------------------------------------- 1 | #include "resource.h" 2 | 3 | #include 4 | 5 | ///////////////////////////////////////////////////////////////////////////// 6 | // 7 | // Accelerator 8 | // 9 | 10 | IDR_ACCEL ACCELERATORS MOVEABLE PURE 11 | BEGIN 12 | "/", IDM_APP_ABOUT, ASCII, ALT 13 | END 14 | 15 | ///////////////////////////////////////////////////////////////////////////// 16 | // 17 | // Icon 18 | // 19 | 20 | // Icon with lower ID value placed first to ensure application icon 21 | // remains consistent on all systems. 22 | IDI_PLAYER ICON DISCARDABLE "Player.ico" 23 | 24 | ///////////////////////////////////////////////////////////////////////////// 25 | // 26 | // Dialog 27 | // 28 | 29 | IDD_FULLSCREEN_SETUP DIALOGEX 0, 0, 400, 200 30 | STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION 31 | CAPTION "FullScreen Setup" 32 | FONT 8, "MS Sans Serif" 33 | BEGIN 34 | DEFPUSHBUTTON "OK",1,100,144,80,16 35 | LTEXT "Driver",-1,10,10,24,8 36 | LTEXT "Screen Mode",-1,270,10,48,8 37 | LISTBOX IDC_LB_DRIVER,10,20,250,120,LBS_SORT | 38 | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP 39 | LISTBOX IDC_LB_SCREEN_MODE,270,20,120,160,LBS_SORT | 40 | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP 41 | PUSHBUTTON "Cancel",2,100,164,80,16 42 | END 43 | 44 | IDD_ABOUT DIALOGEX 0, 0, 150, 60 45 | STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU 46 | CAPTION "About" 47 | FONT 8, "MS Sans Serif" 48 | BEGIN 49 | CTEXT "The Brand New Ballance Player",-1,0,10,150,8 50 | CTEXT "By Kakuty",-1,0,20,150,8 51 | DEFPUSHBUTTON "OK",IDOK,60,35,30,15,WS_GROUP 52 | END 53 | 54 | ///////////////////////////////////////////////////////////////////////////// 55 | // 56 | // Version 57 | // 58 | 59 | VS_VERSION_INFO VERSIONINFO 60 | FILEVERSION ${PROJECT_VERSION_MAJOR},${PROJECT_VERSION_MINOR},${PROJECT_VERSION_PATCH},${PROJECT_VERSION_TWEAK} 61 | PRODUCTVERSION ${PROJECT_VERSION_MAJOR},${PROJECT_VERSION_MINOR},${PROJECT_VERSION_PATCH},${PROJECT_VERSION_TWEAK} 62 | FILEFLAGSMASK 0x3fL 63 | #ifdef _DEBUG 64 | FILEFLAGS 0x1L 65 | #else 66 | FILEFLAGS 0x0L 67 | #endif 68 | FILEOS 0x40004L 69 | FILETYPE 0x1L 70 | FILESUBTYPE 0x0L 71 | BEGIN 72 | BLOCK "StringFileInfo" 73 | BEGIN 74 | BLOCK "040904b0" 75 | BEGIN 76 | VALUE "Comments", "\0" 77 | VALUE "CompanyName", "Kakuty\0" 78 | VALUE "FileDescription", "Ballance Player\0" 79 | VALUE "FileVersion", "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}.${PROJECT_VERSION_TWEAK}\0" 80 | VALUE "InternalName", "Player\0" 81 | VALUE "LegalCopyright", "Copyright (C) 2025\0" 82 | VALUE "LegalTrademarks", "\0" 83 | VALUE "OriginalFilename", "Player.exe\0" 84 | VALUE "PrivateBuild", "\0" 85 | VALUE "ProductName", "Ballance Player\0" 86 | VALUE "ProductVersion", "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}.${PROJECT_VERSION_TWEAK}\0" 87 | VALUE "SpecialBuild", "\0" 88 | END 89 | END 90 | BLOCK "VarFileInfo" 91 | BEGIN 92 | VALUE "Translation", 0x409, 1200 93 | END 94 | END -------------------------------------------------------------------------------- /src/GamePlayer.h: -------------------------------------------------------------------------------- 1 | #ifndef PLAYER_GAMEPLAYER_H 2 | #define PLAYER_GAMEPLAYER_H 3 | 4 | #ifndef WIN32_LEAN_AND_MEAN 5 | #define WIN32_LEAN_AND_MEAN 6 | #endif 7 | #include 8 | #ifdef WIN32_LEAN_AND_MEAN 9 | #undef WIN32_LEAN_AND_MEAN 10 | #endif 11 | 12 | #include "CKAll.h" 13 | 14 | #include "GameConfig.h" 15 | 16 | class CGameInfo; 17 | 18 | class CGamePlayer 19 | { 20 | public: 21 | CGamePlayer(); 22 | ~CGamePlayer(); 23 | 24 | bool Init(const CGameConfig &config, HINSTANCE hInstance = NULL); 25 | bool Load(const char *filename = NULL); 26 | 27 | void Run(); 28 | bool Update(); 29 | void Process(); 30 | void Render(); 31 | void Shutdown(); 32 | 33 | void Play(); 34 | void Pause(); 35 | void Reset(); 36 | 37 | CGameConfig &GetConfig() { return m_Config; } 38 | CKContext *GetCKContext() const { return m_CKContext; } 39 | CKRenderContext *GetRenderContext() const { return m_RenderContext; } 40 | CKRenderManager *GetRenderManager() const { return m_RenderManager; } 41 | 42 | private: 43 | enum PlayerState 44 | { 45 | eInitial = 0, 46 | eReady, 47 | ePlaying, 48 | ePaused, 49 | eFocusLost, 50 | }; 51 | 52 | CGamePlayer(const CGamePlayer &); 53 | CGamePlayer &operator=(const CGamePlayer &); 54 | 55 | bool InitWindow(HINSTANCE hInstance); 56 | void ShutdownWindow(); 57 | 58 | bool InitEngine(HWND mainWindow); 59 | void ShutdownEngine(); 60 | 61 | bool InitDriver(); 62 | 63 | bool FinishLoad(const char *filename); 64 | void ReportMissingGuids(CKFile *file, const char *resolvedFile); 65 | 66 | bool InitPlugins(CKPluginManager *pluginManager); 67 | bool LoadRenderEngines(CKPluginManager *pluginManager); 68 | bool LoadManagers(CKPluginManager *pluginManager); 69 | bool LoadBuildingBlocks(CKPluginManager *pluginManager); 70 | bool LoadPlugins(CKPluginManager *pluginManager); 71 | bool UnloadPlugins(CKPluginManager *pluginManager, CK_PLUGIN_TYPE type, CKGUID guid); 72 | 73 | int FindRenderEngine(CKPluginManager *pluginManager); 74 | 75 | bool SetupManagers(); 76 | bool SetupPaths(); 77 | 78 | void ResizeWindow(); 79 | 80 | int FindScreenMode(int width, int height, int bpp, int driver); 81 | bool GetDisplayMode(int &width, int &height, int &bpp, int driver, int screenMode); 82 | void SetDefaultValuesForDriver(); 83 | 84 | bool IsRenderFullscreen() const; 85 | bool GoFullscreen(); 86 | bool StopFullscreen(); 87 | 88 | bool ClipCursor(); 89 | bool ReleaseCursorClip(); 90 | 91 | bool OpenSetupDialog(); 92 | bool OpenAboutDialog(); 93 | 94 | void OnDestroy(); 95 | void OnMove(); 96 | void OnSize(); 97 | void OnPaint(); 98 | void OnClose(); 99 | void OnActivateApp(bool active); 100 | void OnSetCursor(); 101 | void OnGetMinMaxInfo(LPMINMAXINFO lpmmi); 102 | int OnSysKeyDown(UINT uKey); 103 | void OnClick(bool dblClk = false); 104 | int OnCommand(UINT id, UINT code); 105 | void OnExceptionCMO(); 106 | void OnReturn(); 107 | bool OnLoadCMO(const char *filename); 108 | void OnExitToSystem(); 109 | void OnExitToTitle(); 110 | int OnChangeScreenMode(int driver, int screenMode); 111 | void OnGoFullscreen(); 112 | void OnStopFullscreen(); 113 | void OnSwitchFullscreen(); 114 | 115 | LRESULT HandleMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); 116 | 117 | bool FillDriverList(HWND hWnd); 118 | bool FillScreenModeList(HWND hWnd, int driver); 119 | 120 | static bool RegisterMainWindowClass(HINSTANCE hInstance); 121 | static bool RegisterRenderWindowClass(HINSTANCE hInstance); 122 | static bool UnregisterMainWindowClass(HINSTANCE hInstance); 123 | static bool UnregisterRenderWindowClass(HINSTANCE hInstance); 124 | 125 | static LRESULT CALLBACK MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); 126 | static BOOL CALLBACK FullscreenSetupDlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); 127 | static BOOL CALLBACK AboutDlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); 128 | 129 | int m_State; 130 | HINSTANCE m_hInstance; 131 | HACCEL m_hAccelTable; 132 | HWND m_MainWindow; 133 | HWND m_RenderWindow; 134 | 135 | CKContext *m_CKContext; 136 | CKRenderContext *m_RenderContext; 137 | CKRenderManager *m_RenderManager; 138 | CKMessageManager *m_MessageManager; 139 | CKTimeManager *m_TimeManager; 140 | CKAttributeManager *m_AttributeManager; 141 | CKInputManager *m_InputManager; 142 | 143 | bool m_CursorClipActive; 144 | 145 | CKMessageType m_MsgClick; 146 | CKMessageType m_MsgDoubleClick; 147 | 148 | CGameInfo *m_GameInfo; 149 | CGameConfig m_Config; 150 | }; 151 | 152 | #endif /* PLAYER_GAMEPLAYER_H */ -------------------------------------------------------------------------------- /ConfigTool.dsp: -------------------------------------------------------------------------------- 1 | # Microsoft Developer Studio Project File - Name="ConfigTool" - Package Owner=<4> 2 | # Microsoft Developer Studio Generated Build File, Format Version 6.00 3 | # ** DO NOT EDIT ** 4 | 5 | # TARGTYPE "Win32 (x86) Application" 0x0101 6 | 7 | CFG=ConfigTool - Win32 Debug 8 | !MESSAGE This is not a valid makefile. To build this project using NMAKE, 9 | !MESSAGE use the Export Makefile command and run 10 | !MESSAGE 11 | !MESSAGE NMAKE /f "ConfigTool.mak". 12 | !MESSAGE 13 | !MESSAGE You can specify a configuration when running NMAKE 14 | !MESSAGE by defining the macro CFG on the command line. For example: 15 | !MESSAGE 16 | !MESSAGE NMAKE /f "ConfigTool.mak" CFG="ConfigTool - Win32 Debug" 17 | !MESSAGE 18 | !MESSAGE Possible choices for configuration are: 19 | !MESSAGE 20 | !MESSAGE "ConfigTool - Win32 Release" (based on "Win32 (x86) Application") 21 | !MESSAGE "ConfigTool - Win32 Debug" (based on "Win32 (x86) Application") 22 | !MESSAGE 23 | 24 | # Begin Project 25 | # PROP AllowPerConfigDependencies 0 26 | # PROP Scc_ProjName "" 27 | # PROP Scc_LocalPath "" 28 | CPP=cl.exe 29 | MTL=midl.exe 30 | RSC=rc.exe 31 | 32 | !IF "$(CFG)" == "ConfigTool - Win32 Release" 33 | 34 | # PROP BASE Use_MFC 0 35 | # PROP BASE Use_Debug_Libraries 0 36 | # PROP BASE Output_Dir "ConfigTool___Win32_Release" 37 | # PROP BASE Intermediate_Dir "ConfigTool___Win32_Release" 38 | # PROP BASE Target_Dir "" 39 | # PROP Use_MFC 0 40 | # PROP Use_Debug_Libraries 0 41 | # PROP Output_Dir "Release" 42 | # PROP Intermediate_Dir "Release" 43 | # PROP Ignore_Export_Lib 0 44 | # PROP Target_Dir "" 45 | # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c 46 | # ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "UNICODE" /D "_UNICODE" /D "CONFIGTOOL_STANDALONE" /YX /FD /c 47 | # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 48 | # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 49 | # ADD BASE RSC /l 0x804 /d "NDEBUG" 50 | # ADD RSC /l 0x804 /d "NDEBUG" 51 | BSC32=bscmake.exe 52 | # ADD BASE BSC32 /nologo 53 | # ADD BSC32 /nologo 54 | LINK32=link.exe 55 | # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 56 | # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 /out:"Bin/ConfigTool.exe" 57 | 58 | !ELSEIF "$(CFG)" == "ConfigTool - Win32 Debug" 59 | 60 | # PROP BASE Use_MFC 0 61 | # PROP BASE Use_Debug_Libraries 1 62 | # PROP BASE Output_Dir "ConfigTool___Win32_Debug" 63 | # PROP BASE Intermediate_Dir "ConfigTool___Win32_Debug" 64 | # PROP BASE Target_Dir "" 65 | # PROP Use_MFC 0 66 | # PROP Use_Debug_Libraries 1 67 | # PROP Output_Dir "Debug" 68 | # PROP Intermediate_Dir "Debug" 69 | # PROP Ignore_Export_Lib 0 70 | # PROP Target_Dir "" 71 | # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c 72 | # ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "UNICODE" /D "_UNICODE" /D "CONFIGTOOL_STANDALONE" /FR /YX /FD /GZ /c 73 | # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 74 | # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 75 | # ADD BASE RSC /l 0x804 /d "_DEBUG" 76 | # ADD RSC /l 0x804 /d "_DEBUG" 77 | BSC32=bscmake.exe 78 | # ADD BASE BSC32 /nologo 79 | # ADD BSC32 /nologo 80 | LINK32=link.exe 81 | # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept 82 | # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /out:"Bin/ConfigTool.exe" /pdbtype:sept 83 | 84 | !ENDIF 85 | 86 | # Begin Target 87 | 88 | # Name "ConfigTool - Win32 Release" 89 | # Name "ConfigTool - Win32 Debug" 90 | # Begin Group "Source Files" 91 | 92 | # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" 93 | # Begin Source File 94 | 95 | SOURCE=.\src\ConfigTool.cpp 96 | # End Source File 97 | # Begin Source File 98 | 99 | SOURCE=.\src\GameConfig.cpp 100 | # End Source File 101 | # Begin Source File 102 | 103 | SOURCE=.\src\Utils.cpp 104 | # End Source File 105 | # End Group 106 | # Begin Group "Header Files" 107 | 108 | # PROP Default_Filter "h;hpp;hxx;hm;inl" 109 | # Begin Source File 110 | 111 | SOURCE=.\src\ConfigTool.h 112 | # End Source File 113 | # Begin Source File 114 | 115 | SOURCE=.\src\ConfigToolResource.h 116 | # End Source File 117 | # Begin Source File 118 | 119 | SOURCE=.\src\GameConfig.h 120 | # End Source File 121 | # Begin Source File 122 | 123 | SOURCE=.\src\Utils.h 124 | # End Source File 125 | # End Group 126 | # Begin Group "Resource Files" 127 | 128 | # PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" 129 | # Begin Source File 130 | 131 | SOURCE=.\src\ConfigTool.rc 132 | # End Source File 133 | # Begin Source File 134 | 135 | SOURCE=.\src\Player.ico 136 | # End Source File 137 | # End Group 138 | # End Target 139 | # End Project 140 | -------------------------------------------------------------------------------- /src/CmdlineParser.cpp: -------------------------------------------------------------------------------- 1 | #include "CmdlineParser.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | bool CmdlineArg::GetValue(int i, std::string &value) const 8 | { 9 | if (i >= m_Size || i < 0) 10 | return false; 11 | 12 | const std::string &val = m_Values[i]; 13 | const int sz = (int)val.length(); 14 | 15 | bool inQuote = false; 16 | 17 | if (!m_Jointed) 18 | { 19 | if (val[0] != '"') 20 | { 21 | value = val; 22 | } 23 | else 24 | { 25 | value = ""; 26 | value.resize(sz); 27 | int k = 0; 28 | 29 | for (int vi = 0; vi < sz; ++vi) 30 | { 31 | if (val[vi] == '"') 32 | { 33 | inQuote = !inQuote; 34 | continue; 35 | } 36 | if (inQuote) 37 | value[k++] = val[vi]; 38 | } 39 | 40 | value.resize(k); 41 | } 42 | } 43 | else 44 | { 45 | value = ""; 46 | value.resize(sz); 47 | int k = 0; 48 | 49 | int n = 0; 50 | size_t foundPos = val.find('='); 51 | if (foundPos == std::string::npos) 52 | return false; 53 | int j = (int)foundPos + 1; 54 | for (int vi = j; vi < sz; ++vi) 55 | { 56 | if (val[vi] == ';' && vi != j) 57 | { 58 | ++n; 59 | j = vi + 1; 60 | continue; 61 | } 62 | 63 | if (i == n) 64 | { 65 | if (val[vi] != '"') 66 | { 67 | for (; vi < sz && val[vi] != ';'; ++vi) 68 | value[k++] = val[vi]; 69 | } 70 | else 71 | { 72 | for (; vi < sz && val[vi] != ';'; ++vi) 73 | { 74 | if (val[vi] == '"') 75 | { 76 | inQuote = !inQuote; 77 | continue; 78 | } 79 | if (inQuote) 80 | value[k++] = val[vi]; 81 | } 82 | } 83 | } 84 | } 85 | 86 | value.resize(k); 87 | } 88 | 89 | return true; 90 | } 91 | 92 | bool CmdlineArg::GetValue(int i, long &value) const 93 | { 94 | if (i >= m_Size || i < 0) 95 | return false; 96 | 97 | const std::string &v = m_Values[i]; 98 | const char *s = v.c_str(); 99 | char *e = NULL; 100 | long val = strtol(s, &e, 10); 101 | if (s == e) 102 | return false; 103 | value = val; 104 | return true; 105 | } 106 | 107 | int CmdlineArg::GetSize() const 108 | { 109 | return m_Size; 110 | } 111 | 112 | CmdlineParser::CmdlineParser(int argc, char **argv) : m_Index(0) 113 | { 114 | for (int i = 1; i < argc; ++i) 115 | m_Args.push_back(argv[i]); 116 | } 117 | 118 | bool CmdlineParser::Next(CmdlineArg &arg, const char *longopt, char opt, int maxValueCount) 119 | { 120 | if (Done()) 121 | return false; 122 | 123 | const std::string &s = m_Args[m_Index]; 124 | const int sz = (int)s.length(); 125 | if (sz < 2 || s[0] != '-') 126 | return false; 127 | 128 | bool match = false; 129 | 130 | int optLen = 2; 131 | if (opt != '\0' && isalnum(opt) && 132 | s[0] == '-' && s[1] == opt) 133 | match = true; 134 | 135 | if (!match) 136 | { 137 | if (!longopt) return false; 138 | 139 | optLen = (int)strlen(longopt); 140 | if (optLen > 0) 141 | { 142 | for (int l = 0; l < optLen; ++l) 143 | if (!isalnum(longopt[l]) && longopt[l] != '-' && longopt[l] != '_') 144 | return false; 145 | } 146 | 147 | if (strncmp(s.c_str(), longopt, optLen) != 0) 148 | return false; 149 | } 150 | 151 | if (maxValueCount != 0) 152 | { 153 | const std::string *values = NULL; 154 | int valueCount = 0; 155 | 156 | if (sz > optLen + 1 && s[optLen] == '=') 157 | { 158 | values = &s; 159 | ++valueCount; 160 | int j = optLen + 1; 161 | for (int vi = j; vi < sz; ++vi) 162 | { 163 | if (s[vi] == ';' && vi != j) 164 | { 165 | ++valueCount; 166 | j = vi + 1; 167 | } 168 | } 169 | arg = CmdlineArg(values, valueCount, true); 170 | } 171 | else 172 | { 173 | if (maxValueCount == -1) 174 | maxValueCount = (int)(m_Args.size() - m_Index); 175 | 176 | ++m_Index; // Skip the option itself 177 | if (m_Index >= (int)m_Args.size() || maxValueCount == 0) 178 | { 179 | arg = CmdlineArg(NULL, 0); 180 | } 181 | else 182 | { 183 | values = &m_Args[m_Index]; // Start from after the option 184 | while (valueCount < maxValueCount && m_Index < (int)m_Args.size()) 185 | { 186 | const std::string &next = m_Args[m_Index]; 187 | if (next.length() != 0 && next[0] == '-') 188 | break; 189 | ++m_Index; 190 | ++valueCount; 191 | } 192 | arg = CmdlineArg(values, valueCount); 193 | } 194 | } 195 | } 196 | 197 | ++m_Index; 198 | return true; 199 | } 200 | 201 | bool CmdlineParser::Skip() 202 | { 203 | if (m_Index < (int)m_Args.size()) 204 | { 205 | ++m_Index; 206 | return true; 207 | } 208 | return false; 209 | } 210 | 211 | bool CmdlineParser::Done() const 212 | { 213 | return m_Index >= (int)m_Args.size(); 214 | } 215 | 216 | void CmdlineParser::Reset() 217 | { 218 | m_Index = 0; 219 | } -------------------------------------------------------------------------------- /Player.dsp: -------------------------------------------------------------------------------- 1 | # Microsoft Developer Studio Project File - Name="Player" - Package Owner=<4> 2 | # Microsoft Developer Studio Generated Build File, Format Version 6.00 3 | # ** DO NOT EDIT ** 4 | 5 | # TARGTYPE "Win32 (x86) Application" 0x0101 6 | 7 | CFG=Player - Win32 Debug 8 | !MESSAGE This is not a valid makefile. To build this project using NMAKE, 9 | !MESSAGE use the Export Makefile command and run 10 | !MESSAGE 11 | !MESSAGE NMAKE /f "Player.mak". 12 | !MESSAGE 13 | !MESSAGE You can specify a configuration when running NMAKE 14 | !MESSAGE by defining the macro CFG on the command line. For example: 15 | !MESSAGE 16 | !MESSAGE NMAKE /f "Player.mak" CFG="Player - Win32 Debug" 17 | !MESSAGE 18 | !MESSAGE Possible choices for configuration are: 19 | !MESSAGE 20 | !MESSAGE "Player - Win32 Release" (based on "Win32 (x86) Application") 21 | !MESSAGE "Player - Win32 Debug" (based on "Win32 (x86) Application") 22 | !MESSAGE 23 | 24 | # Begin Project 25 | # PROP AllowPerConfigDependencies 0 26 | # PROP Scc_ProjName "" 27 | # PROP Scc_LocalPath "" 28 | CPP=cl.exe 29 | MTL=midl.exe 30 | RSC=rc.exe 31 | 32 | !IF "$(CFG)" == "Player - Win32 Release" 33 | 34 | # PROP BASE Use_MFC 0 35 | # PROP BASE Use_Debug_Libraries 0 36 | # PROP BASE Output_Dir "Release" 37 | # PROP BASE Intermediate_Dir "Release" 38 | # PROP BASE Target_Dir "" 39 | # PROP Use_MFC 0 40 | # PROP Use_Debug_Libraries 0 41 | # PROP Output_Dir "Release" 42 | # PROP Intermediate_Dir "Release" 43 | # PROP Ignore_Export_Lib 0 44 | # PROP Target_Dir "" 45 | # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /Yu"stdafx.h" /FD /c 46 | # ADD CPP /nologo /W3 /GX /O2 /I "include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "UNICODE" /D "_UNICODE" /FD /c 47 | # SUBTRACT CPP /YX /Yc /Yu 48 | # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 49 | # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 50 | # ADD BASE RSC /l 0x804 /d "NDEBUG" 51 | # ADD RSC /l 0x409 /d "NDEBUG" 52 | BSC32=bscmake.exe 53 | # ADD BASE BSC32 /nologo 54 | # ADD BSC32 /nologo 55 | LINK32=link.exe 56 | # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 57 | # ADD LINK32 CK2.lib VxMath.lib kernel32.lib user32.lib gdi32.lib shell32.lib delayimp.lib /nologo /subsystem:windows /machine:I386 /out:"Bin/Player.exe" /delayload:CK2.dll /delayload:VxMath.dll 58 | # SUBTRACT LINK32 /pdb:none 59 | 60 | !ELSEIF "$(CFG)" == "Player - Win32 Debug" 61 | 62 | # PROP BASE Use_MFC 0 63 | # PROP BASE Use_Debug_Libraries 1 64 | # PROP BASE Output_Dir "Debug" 65 | # PROP BASE Intermediate_Dir "Debug" 66 | # PROP BASE Target_Dir "" 67 | # PROP Use_MFC 0 68 | # PROP Use_Debug_Libraries 1 69 | # PROP Output_Dir "Debug" 70 | # PROP Intermediate_Dir "Debug" 71 | # PROP Ignore_Export_Lib 0 72 | # PROP Target_Dir "" 73 | # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /Yu"stdafx.h" /FD /GZ /c 74 | # ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "UNICODE" /D "_UNICODE" /FR /FD /GZ /c 75 | # SUBTRACT CPP /YX /Yc /Yu 76 | # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 77 | # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 78 | # ADD BASE RSC /l 0x804 /d "_DEBUG" 79 | # ADD RSC /l 0x409 /d "_DEBUG" 80 | BSC32=bscmake.exe 81 | # ADD BASE BSC32 /nologo 82 | # ADD BSC32 /nologo 83 | LINK32=link.exe 84 | # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept 85 | # ADD LINK32 CK2.lib VxMath.lib kernel32.lib user32.lib gdi32.lib shell32.lib delayimp.lib /nologo /subsystem:windows /debug /machine:I386 /out:"Bin/Player.exe" /pdbtype:sept /delayload:CK2.dll /delayload:VxMath.dll 86 | # SUBTRACT LINK32 /pdb:none 87 | 88 | !ENDIF 89 | 90 | # Begin Target 91 | 92 | # Name "Player - Win32 Release" 93 | # Name "Player - Win32 Debug" 94 | # Begin Group "Source Files" 95 | 96 | # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" 97 | # Begin Source File 98 | 99 | SOURCE=.\src\CmdlineParser.cpp 100 | # End Source File 101 | # Begin Source File 102 | 103 | SOURCE=.\src\GameConfig.cpp 104 | # End Source File 105 | # Begin Source File 106 | 107 | SOURCE=.\src\GamePlayer.cpp 108 | # End Source File 109 | # Begin Source File 110 | 111 | SOURCE=.\src\Hotfix.cpp 112 | # End Source File 113 | # Begin Source File 114 | 115 | SOURCE=.\src\Logger.cpp 116 | # End Source File 117 | # Begin Source File 118 | 119 | SOURCE=.\src\Player.cpp 120 | # End Source File 121 | # Begin Source File 122 | 123 | SOURCE=.\src\Splash.cpp 124 | # End Source File 125 | # Begin Source File 126 | 127 | SOURCE=.\src\Utils.cpp 128 | # End Source File 129 | # End Group 130 | # Begin Group "Header Files" 131 | 132 | # PROP Default_Filter "h;hpp;hxx;hm;inl" 133 | # Begin Source File 134 | 135 | SOURCE=.\src\CmdlineParser.h 136 | # End Source File 137 | # Begin Source File 138 | 139 | SOURCE=.\src\config.h 140 | # End Source File 141 | # Begin Source File 142 | 143 | SOURCE=.\src\GameConfig.h 144 | # End Source File 145 | # Begin Source File 146 | 147 | SOURCE=.\src\GameInfo.h 148 | # End Source File 149 | # Begin Source File 150 | 151 | SOURCE=.\src\GamePlayer.h 152 | # End Source File 153 | # Begin Source File 154 | 155 | SOURCE=.\src\InterfaceManager.h 156 | # End Source File 157 | # Begin Source File 158 | 159 | SOURCE=.\src\Logger.h 160 | # End Source File 161 | # Begin Source File 162 | 163 | SOURCE=.\src\resource.h 164 | # End Source File 165 | # Begin Source File 166 | 167 | SOURCE=.\src\ScriptUtils.h 168 | # End Source File 169 | # Begin Source File 170 | 171 | SOURCE=.\src\Splash.h 172 | # End Source File 173 | # Begin Source File 174 | 175 | SOURCE=.\src\Utils.h 176 | # End Source File 177 | # End Group 178 | # Begin Group "Resource Files" 179 | 180 | # PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" 181 | # Begin Source File 182 | 183 | SOURCE=.\src\Player.ico 184 | # End Source File 185 | # Begin Source File 186 | 187 | SOURCE=.\src\Player.rc 188 | # End Source File 189 | # End Group 190 | # End Target 191 | # End Project 192 | -------------------------------------------------------------------------------- /src/GameConfig.h: -------------------------------------------------------------------------------- 1 | #ifndef PLAYER_GAMECONFIG_H 2 | #define PLAYER_GAMECONFIG_H 3 | 4 | #include 5 | 6 | #include "config.h" 7 | 8 | #include "VxMathDefines.h" 9 | 10 | #ifndef MAX_PATH 11 | #define MAX_PATH 260 12 | #endif 13 | 14 | class CmdlineParser; 15 | 16 | enum PathCategory 17 | { 18 | eConfigPath = 0, 19 | eLogPath, 20 | eCmoPath, 21 | eRootPath, 22 | ePluginPath, 23 | eRenderEnginePath, 24 | eManagerPath, 25 | eBuildingBlockPath, 26 | eSoundPath, 27 | eBitmapPath, 28 | eDataPath, 29 | ePathCategoryCount 30 | }; 31 | 32 | enum LogMode 33 | { 34 | eLogAppend = 0, 35 | eLogOverwrite, 36 | }; 37 | 38 | // Master list of all configuration fields 39 | // Format: X_TYPE(section, key, member_name, default_value) 40 | #define GAMECONFIG_FIELDS \ 41 | X_INT ("Startup", "LogMode", logMode, 1) \ 42 | X_BOOL ("Startup", "Verbose", verbose, false) \ 43 | X_BOOL ("Startup", "ManualSetup", manualSetup, false) \ 44 | X_INT ("Graphics", "Driver", driver, 0) \ 45 | X_INT ("Graphics", "BitsPerPixel", bpp, PLAYER_DEFAULT_BPP) \ 46 | X_INT ("Graphics", "Width", width, PLAYER_DEFAULT_WIDTH) \ 47 | X_INT ("Graphics", "Height", height, PLAYER_DEFAULT_HEIGHT) \ 48 | X_INT ("Graphics", "Antialias", antialias, 0) \ 49 | X_INT ("Graphics", "VertexCache", vertexCache, 16) \ 50 | X_INT ("Window", "X", posX, 2147483647) \ 51 | X_INT ("Window", "Y", posY, 2147483647) \ 52 | X_INT ("Game", "Language", langId, 1) \ 53 | X_BOOL ("Graphics", "FullScreen", fullscreen, false) \ 54 | X_BOOL ("Graphics", "DisablePerspectiveCorrection", disablePerspectiveCorrection, false) \ 55 | X_BOOL ("Graphics", "ForceLinearFog", forceLinearFog, false) \ 56 | X_BOOL ("Graphics", "ForceSoftware", forceSoftware, false) \ 57 | X_BOOL ("Graphics", "DisableFilter", disableFilter, false) \ 58 | X_BOOL ("Graphics", "EnsureVertexShader", ensureVertexShader, false) \ 59 | X_BOOL ("Graphics", "UseIndexBuffers", useIndexBuffers, false) \ 60 | X_BOOL ("Graphics", "DisableDithering", disableDithering, false) \ 61 | X_BOOL ("Graphics", "DisableMipmap", disableMipmap, false) \ 62 | X_BOOL ("Graphics", "DisableSpecular", disableSpecular, false) \ 63 | X_BOOL ("Graphics", "EnableScreenDump", enableScreenDump, false) \ 64 | X_BOOL ("Graphics", "EnableDebugMode", enableDebugMode, false) \ 65 | X_BOOL ("Graphics", "TextureCacheManagement", textureCacheManagement, true) \ 66 | X_BOOL ("Graphics", "SortTransparentObjects", sortTransparentObjects, true) \ 67 | X_BOOL ("Window", "ChildWindowRendering", childWindowRendering, false) \ 68 | X_BOOL ("Window", "Borderless", borderless, false) \ 69 | X_BOOL ("Window", "ClipCursor", clipCursor, false) \ 70 | X_BOOL ("Window", "AlwaysHandleInput", alwaysHandleInput, false) \ 71 | X_BOOL ("Game", "SkipOpening", skipOpening, false) \ 72 | X_BOOL ("Game", "ApplyHotfix", applyHotfix, true) \ 73 | X_BOOL ("Game", "UnlockFramerate", unlockFramerate, false) \ 74 | X_BOOL ("Game", "UnlockWidescreen", unlockWidescreen, false) \ 75 | X_BOOL ("Game", "UnlockHighResolution", unlockHighResolution, false) \ 76 | X_PF ("Graphics", "TextureVideoFormat", textureVideoFormat, UNKNOWN_PF) \ 77 | X_PF ("Graphics", "SpriteVideoFormat", spriteVideoFormat, UNKNOWN_PF) 78 | 79 | class CGameConfig 80 | { 81 | public: 82 | // Auto-generated data members from the master list 83 | #define X_BOOL(sec,key,member,def) bool member; 84 | #define X_INT(sec,key,member,def) int member; 85 | #define X_PF(sec,key,member,def) VX_PIXELFORMAT member; 86 | GAMECONFIG_FIELDS 87 | #undef X_BOOL 88 | #undef X_INT 89 | #undef X_PF 90 | 91 | // Non-INI members (not persisted) 92 | int screenMode; 93 | bool debug; 94 | bool rookie; 95 | 96 | CGameConfig(); 97 | CGameConfig &operator=(const CGameConfig &config); 98 | 99 | bool HasPath(PathCategory category) const; 100 | const char *GetPath(PathCategory category) const; 101 | void SetPath(PathCategory category, const char *path); 102 | bool ResetPath(PathCategory category = ePathCategoryCount); 103 | 104 | void LoadFromIni(const char *filename = ""); 105 | void SaveToIni(const char *filename = ""); 106 | 107 | private: 108 | std::string m_Paths[ePathCategoryCount]; 109 | 110 | // Auto-generated enum indices for loaded value snapshots 111 | enum { 112 | #define X_BOOL(sec,key,member,def) eLoadedBool_##member, 113 | #define X_INT(sec,key,member,def) eLoadedInt_##member, 114 | #define X_PF(sec,key,member,def) eLoadedPixel_##member, 115 | GAMECONFIG_FIELDS 116 | #undef X_BOOL 117 | #undef X_INT 118 | #undef X_PF 119 | eLoadedSentinel 120 | }; 121 | 122 | struct LoadedIntValue 123 | { 124 | bool hasValue; 125 | int value; 126 | }; 127 | 128 | struct LoadedBoolValue 129 | { 130 | bool hasValue; 131 | bool value; 132 | }; 133 | 134 | struct LoadedPixelValue 135 | { 136 | bool hasValue; 137 | VX_PIXELFORMAT value; 138 | }; 139 | 140 | // Single arrays for all loaded values (indexed by enum) 141 | LoadedIntValue m_LoadedInts[eLoadedSentinel]; 142 | LoadedBoolValue m_LoadedBools[eLoadedSentinel]; 143 | LoadedPixelValue m_LoadedPixels[eLoadedSentinel]; 144 | 145 | unsigned long m_ConfigTimestampLow; 146 | unsigned long m_ConfigTimestampHigh; 147 | bool m_ConfigTimestampValid; 148 | std::string m_LastConfigAbsolutePath; 149 | 150 | void ResetLoadedSnapshots(); 151 | void StoreLoadedInt(int index, int value); 152 | void StoreLoadedBool(int index, bool value); 153 | void StoreLoadedPixel(int index, VX_PIXELFORMAT value); 154 | bool ShouldOverrideInt(int index, int newValue) const; 155 | bool ShouldOverrideBool(int index, bool newValue) const; 156 | bool ShouldOverridePixel(int index, VX_PIXELFORMAT newValue) const; 157 | void CaptureCurrentValuesAsLoaded(); 158 | void MergeExternalChanges(const char *filename); 159 | void SetLastConfigAbsolutePath(const char *path); 160 | bool IsSameConfigPath(const char *path) const; 161 | }; 162 | 163 | #endif // PLAYER_GAMECONFIG_H 164 | -------------------------------------------------------------------------------- /src/ConfigToolResource.h: -------------------------------------------------------------------------------- 1 | #ifndef CONFIGTOOL_RESOURCE_H 2 | #define CONFIGTOOL_RESOURCE_H 3 | 4 | // Dialog resource ID 5 | #define IDD_CONFIG 101 6 | #define IDI_ICON 102 7 | 8 | // String Table IDs for English (1000-1999) 9 | #define IDS_DIALOG_TITLE 1000 10 | #define IDS_BTN_OK 1001 11 | #define IDS_BTN_CANCEL 1002 12 | #define IDS_BTN_DEFAULTS 1003 13 | #define IDS_RESET_CONFIRM 1004 14 | #define IDS_RESET_TITLE 1005 15 | #define IDS_GROUP_STARTUP 1006 16 | #define IDS_GROUP_GRAPHICS 1007 17 | #define IDS_GROUP_WINDOW 1008 18 | #define IDS_GROUP_GAME 1009 19 | #define IDS_GROUP_INTERFACE 1010 20 | #define IDS_LOG_MODE 1011 21 | #define IDS_LOG_APPEND 1012 22 | #define IDS_LOG_OVERWRITE 1013 23 | #define IDS_VERBOSE 1014 24 | #define IDS_MANUAL_SETUP 1015 25 | #define IDS_LOAD_ALL_MGR 1016 26 | #define IDS_LOAD_ALL_BB 1017 27 | #define IDS_LOAD_ALL_PLUGINS 1018 28 | #define IDS_DRIVER_ID 1019 29 | #define IDS_BPP 1020 30 | #define IDS_WIDTH 1021 31 | #define IDS_HEIGHT 1022 32 | #define IDS_FULLSCREEN 1023 33 | #define IDS_DISABLE_PERSP_CORRECT 1024 34 | #define IDS_FORCE_LINEAR_FOG 1025 35 | #define IDS_FORCE_SOFTWARE 1026 36 | #define IDS_DISABLE_FILTER 1027 37 | #define IDS_ENSURE_VS 1028 38 | #define IDS_USE_INDEX_BUFFERS 1029 39 | #define IDS_DISABLE_DITHER 1030 40 | #define IDS_ANTIALIAS 1031 41 | #define IDS_DISABLE_MIPMAP 1032 42 | #define IDS_DISABLE_SPECULAR 1033 43 | #define IDS_ENABLE_SCREEN_DUMP 1034 44 | #define IDS_ENABLE_DEBUG_GFX 1035 45 | #define IDS_VERTEX_CACHE 1036 46 | #define IDS_TEXTURE_CACHE_MGMT 1037 47 | #define IDS_SORT_TRANSPARENT 1038 48 | #define IDS_TEXTURE_VIDEO_FORMAT 1039 49 | #define IDS_SPRITE_VIDEO_FORMAT 1040 50 | #define IDS_CHILD_WINDOW_RENDER 1041 51 | #define IDS_BORDERLESS 1042 52 | #define IDS_CLIP_CURSOR 1043 53 | #define IDS_ALWAYS_HANDLE_INPUT 1044 54 | #define IDS_POSITION_X 1045 55 | #define IDS_POSITION_Y 1046 56 | #define IDS_LANGUAGE 1047 57 | #define IDS_UI_LANGUAGE 1048 58 | #define IDS_LANG_GERMAN 1049 59 | #define IDS_LANG_ENGLISH 1050 60 | #define IDS_LANG_SPANISH 1051 61 | #define IDS_LANG_ITALIAN 1052 62 | #define IDS_LANG_FRENCH 1053 63 | #define IDS_UI_ENGLISH 1054 64 | #define IDS_UI_CHINESE 1055 65 | #define IDS_SKIP_OPENING 1056 66 | #define IDS_APPLY_HOTFIX 1057 67 | #define IDS_UNLOCK_FRAMERATE 1058 68 | #define IDS_UNLOCK_WIDESCREEN 1059 69 | #define IDS_UNLOCK_HIGHRES 1060 70 | #define IDS_DEBUG 1061 71 | #define IDS_ROOKIE 1062 72 | #define IDS_ERR_NO_CONFIG 1063 73 | #define IDS_ERR_CANNOT_SAVE 1064 74 | #define IDS_ERR_DIALOG_CREATE 1065 75 | #define IDS_ERR_CONFIG_TITLE 1066 76 | #define IDS_WARN_CONFIG_PATH 1067 77 | #define IDS_WARN_CONFIG_TITLE 1068 78 | #define IDS_SAVE_SUCCESS 1069 79 | #define IDS_SAVE_TITLE 1070 80 | 81 | // String Table IDs for Chinese (2000-2999) 82 | #define IDS_CN_DIALOG_TITLE 2000 83 | #define IDS_CN_BTN_OK 2001 84 | #define IDS_CN_BTN_CANCEL 2002 85 | #define IDS_CN_BTN_DEFAULTS 2003 86 | #define IDS_CN_RESET_CONFIRM 2004 87 | #define IDS_CN_RESET_TITLE 2005 88 | #define IDS_CN_GROUP_STARTUP 2006 89 | #define IDS_CN_GROUP_GRAPHICS 2007 90 | #define IDS_CN_GROUP_WINDOW 2008 91 | #define IDS_CN_GROUP_GAME 2009 92 | #define IDS_CN_GROUP_INTERFACE 2010 93 | #define IDS_CN_LOG_MODE 2011 94 | #define IDS_CN_LOG_APPEND 2012 95 | #define IDS_CN_LOG_OVERWRITE 2013 96 | #define IDS_CN_VERBOSE 2014 97 | #define IDS_CN_MANUAL_SETUP 2015 98 | #define IDS_CN_LOAD_ALL_MGR 2016 99 | #define IDS_CN_LOAD_ALL_BB 2017 100 | #define IDS_CN_LOAD_ALL_PLUGINS 2018 101 | #define IDS_CN_DRIVER_ID 2019 102 | #define IDS_CN_BPP 2020 103 | #define IDS_CN_WIDTH 2021 104 | #define IDS_CN_HEIGHT 2022 105 | #define IDS_CN_FULLSCREEN 2023 106 | #define IDS_CN_DISABLE_PERSP_CORRECT 2024 107 | #define IDS_CN_FORCE_LINEAR_FOG 2025 108 | #define IDS_CN_FORCE_SOFTWARE 2026 109 | #define IDS_CN_DISABLE_FILTER 2027 110 | #define IDS_CN_ENSURE_VS 2028 111 | #define IDS_CN_USE_INDEX_BUFFERS 2029 112 | #define IDS_CN_DISABLE_DITHER 2030 113 | #define IDS_CN_ANTIALIAS 2031 114 | #define IDS_CN_DISABLE_MIPMAP 2032 115 | #define IDS_CN_DISABLE_SPECULAR 2033 116 | #define IDS_CN_ENABLE_SCREEN_DUMP 2034 117 | #define IDS_CN_ENABLE_DEBUG_GFX 2035 118 | #define IDS_CN_VERTEX_CACHE 2036 119 | #define IDS_CN_TEXTURE_CACHE_MGMT 2037 120 | #define IDS_CN_SORT_TRANSPARENT 2038 121 | #define IDS_CN_TEXTURE_VIDEO_FORMAT 2039 122 | #define IDS_CN_SPRITE_VIDEO_FORMAT 2040 123 | #define IDS_CN_CHILD_WINDOW_RENDER 2041 124 | #define IDS_CN_BORDERLESS 2042 125 | #define IDS_CN_CLIP_CURSOR 2043 126 | #define IDS_CN_ALWAYS_HANDLE_INPUT 2044 127 | #define IDS_CN_POSITION_X 2045 128 | #define IDS_CN_POSITION_Y 2046 129 | #define IDS_CN_LANGUAGE 2047 130 | #define IDS_CN_UI_LANGUAGE 2048 131 | #define IDS_CN_LANG_GERMAN 2049 132 | #define IDS_CN_LANG_ENGLISH 2050 133 | #define IDS_CN_LANG_SPANISH 2051 134 | #define IDS_CN_LANG_ITALIAN 2052 135 | #define IDS_CN_LANG_FRENCH 2053 136 | #define IDS_CN_UI_ENGLISH 2054 137 | #define IDS_CN_UI_CHINESE 2055 138 | #define IDS_CN_SKIP_OPENING 2056 139 | #define IDS_CN_APPLY_HOTFIX 2057 140 | #define IDS_CN_UNLOCK_FRAMERATE 2058 141 | #define IDS_CN_UNLOCK_WIDESCREEN 2059 142 | #define IDS_CN_UNLOCK_HIGHRES 2060 143 | #define IDS_CN_DEBUG 2061 144 | #define IDS_CN_ROOKIE 2062 145 | #define IDS_CN_ERR_NO_CONFIG 2063 146 | #define IDS_CN_ERR_CANNOT_SAVE 2064 147 | #define IDS_CN_ERR_DIALOG_CREATE 2065 148 | #define IDS_CN_ERR_CONFIG_TITLE 2066 149 | #define IDS_CN_WARN_CONFIG_PATH 2067 150 | #define IDS_CN_WARN_CONFIG_TITLE 2068 151 | #define IDS_CN_SAVE_SUCCESS 2069 152 | #define IDS_CN_SAVE_TITLE 2070 153 | 154 | // Controls 155 | #define IDC_BUTTON_DEFAULTS 1000 156 | #define IDC_COMBO_LOGMODE 1001 157 | #define IDC_CHECK_VERBOSE 1002 158 | #define IDC_CHECK_MANUALSETUP 1003 159 | 160 | #define IDC_EDIT_DRIVER 1101 161 | #define IDC_COMBO_BPP 1102 162 | #define IDC_EDIT_WIDTH 1103 163 | #define IDC_EDIT_HEIGHT 1104 164 | #define IDC_CHECK_FULLSCREEN 1105 165 | #define IDC_CHECK_DISPERSPCORRECT 1106 166 | #define IDC_CHECK_FORCELINEARFOG 1107 167 | #define IDC_CHECK_FORCESOFTWARE 1108 168 | #define IDC_CHECK_DISABLEFILTER 1109 169 | #define IDC_CHECK_ENSUREVS 1110 170 | #define IDC_CHECK_USEINDEXBUFFERS 1111 171 | #define IDC_CHECK_DISABLEDITHER 1112 172 | #define IDC_EDIT_ANTIALIAS 1113 173 | #define IDC_CHECK_DISABLEMIPMAP 1114 174 | #define IDC_CHECK_DISABLESPECULAR 1115 175 | #define IDC_CHECK_ENABLESCREENDUMP 1116 176 | #define IDC_CHECK_ENABLEDEBUGMODE_GFX 1117 177 | #define IDC_EDIT_VERTEXCACHE 1118 178 | #define IDC_CHECK_TEXCACHEMGMT 1119 179 | #define IDC_CHECK_SORTTRANSPARENT 1120 180 | #define IDC_EDIT_TEXVIDFORMAT 1121 181 | #define IDC_EDIT_SPRVIDFORMAT 1122 182 | 183 | #define IDC_CHECK_CHILDWINRENDER 1201 184 | #define IDC_CHECK_BORDERLESS 1202 185 | #define IDC_CHECK_CLIPCURSOR 1203 186 | #define IDC_CHECK_ALWAYSHANDLEINPUT 1204 187 | #define IDC_EDIT_POSX 1205 188 | #define IDC_EDIT_POSY 1206 189 | 190 | #define IDC_COMBO_LANG 1301 191 | #define IDC_CHECK_SKIPOPENING 1302 192 | #define IDC_CHECK_APPLYHOTFIX 1303 193 | #define IDC_CHECK_UNLOCKFRAMERATE 1304 194 | #define IDC_CHECK_UNLOCKWIDESCREEN 1305 195 | #define IDC_CHECK_UNLOCKHIGHRES 1306 196 | #define IDC_CHECK_DEBUG 1307 197 | #define IDC_CHECK_ROOKIE 1308 198 | 199 | #define IDC_GROUP_STARTUP 1401 200 | #define IDC_GROUP_GRAPHICS 1402 201 | #define IDC_GROUP_WINDOW 1403 202 | #define IDC_GROUP_GAME 1404 203 | #define IDC_GROUP_INTERFACE 1500 204 | #define IDC_COMBO_LANGUAGE 1501 205 | 206 | #endif // CONFIGTOOL_RESOURCE_H 207 | -------------------------------------------------------------------------------- /src/Splash.cpp: -------------------------------------------------------------------------------- 1 | #include "Splash.h" 2 | 3 | #include 4 | #include 5 | 6 | #include "resource.h" 7 | 8 | #define PALVERSION 0x300 9 | 10 | static CSplash gSplash; 11 | static HPALETTE hPalette = NULL; 12 | 13 | LRESULT CALLBACK SplashWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 14 | { 15 | HDC hDc; 16 | PAINTSTRUCT ps; 17 | 18 | static HINSTANCE hInstance; 19 | static DWORD dwNewWidth, dwNewHeight; 20 | 21 | switch (uMsg) 22 | { 23 | case WM_CREATE: 24 | hInstance = ((LPCREATESTRUCT)lParam)->hInstance; 25 | hPalette = gSplash.GetPalette(); 26 | ::SetCursor(::LoadCursorA(NULL, (LPCSTR)IDC_ARROW)); 27 | return 0; 28 | 29 | case WM_DESTROY: 30 | ::DeleteObject(hPalette); 31 | return 0; 32 | 33 | case WM_SIZE: 34 | dwNewWidth = LOWORD(lParam); 35 | dwNewHeight = HIWORD(lParam); 36 | return 0; 37 | 38 | case WM_PAINT: 39 | hDc = ::BeginPaint(hWnd, &ps); 40 | if (hPalette) 41 | { 42 | ::SelectPalette(hDc, hPalette, FALSE); 43 | ::RealizePalette(hDc); 44 | } 45 | ::SetDIBitsToDevice(hDc, 46 | 0, 47 | 0, 48 | gSplash.GetWidth(), 49 | gSplash.GetHeight(), 50 | 0, 51 | 0, 52 | 0, 53 | gSplash.GetHeight(), 54 | gSplash.GetBitmapData(), 55 | gSplash.GetBitmapInfo(), 56 | DIB_RGB_COLORS); 57 | ::EndPaint(hWnd, &ps); 58 | return 0; 59 | 60 | case WM_QUERYNEWPALETTE: 61 | if (hPalette) 62 | { 63 | hDc = ::GetDC(hWnd); 64 | ::SelectPalette(hDc, hPalette, FALSE); 65 | ::RealizePalette(hDc); 66 | ::InvalidateRect(hWnd, NULL, TRUE); 67 | ::ReleaseDC(hWnd, hDc); 68 | return 1; 69 | } 70 | break; 71 | 72 | case WM_PALETTECHANGED: 73 | if (hPalette && (HWND)wParam != hWnd) 74 | { 75 | hDc = ::GetDC(hWnd); 76 | ::SelectPalette(hDc, hPalette, FALSE); 77 | ::RealizePalette(hDc); 78 | ::UpdateColors(hDc); 79 | ::ReleaseDC(hWnd, hDc); 80 | } 81 | break; 82 | 83 | default: 84 | break; 85 | } 86 | 87 | return DefWindowProc(hWnd, uMsg, wParam, lParam); 88 | } 89 | 90 | CSplash::CSplash() : m_Data(NULL), m_hWnd(NULL), m_hInstance(NULL) {} 91 | 92 | CSplash::CSplash(HINSTANCE hInstance) : m_Data(NULL), m_hWnd(NULL), m_hInstance(hInstance) {} 93 | 94 | CSplash::~CSplash() 95 | { 96 | if (m_Data) 97 | delete[] m_Data; 98 | } 99 | 100 | bool CSplash::Show() 101 | { 102 | m_Data = NULL; 103 | 104 | WNDCLASSA wndclass; 105 | memset(&wndclass, 0, sizeof(WNDCLASSA)); 106 | wndclass.style = CS_HREDRAW | CS_VREDRAW; 107 | wndclass.lpfnWndProc = SplashWndProc; 108 | wndclass.cbClsExtra = 0; 109 | wndclass.cbWndExtra = 0; 110 | wndclass.hInstance = m_hInstance; 111 | wndclass.hIcon = 0; 112 | wndclass.hCursor = 0; 113 | wndclass.hbrBackground = (HBRUSH)::GetStockObject(BLACK_BRUSH); 114 | wndclass.lpszMenuName = 0; 115 | wndclass.lpszClassName = "SPLASH"; 116 | wndclass.hIcon = ::LoadIconA(m_hInstance, (LPCSTR)IDI_PLAYER); 117 | 118 | if (!::RegisterClassA(&wndclass)) 119 | return false; 120 | 121 | char buffer[MAX_PATH]; 122 | char drive[4]; 123 | char dir[MAX_PATH]; 124 | char filename[MAX_PATH]; 125 | ::GetModuleFileNameA(NULL, buffer, 1024); 126 | _splitpath(buffer, drive, dir, filename, NULL); 127 | _snprintf(buffer, MAX_PATH, "%s%ssplash.bmp", drive, dir); 128 | if (!gSplash.LoadBMP(buffer)) 129 | return false; 130 | 131 | int width = gSplash.GetWidth(); 132 | int height = gSplash.GetHeight(); 133 | 134 | m_hWnd = ::CreateWindowExA( 135 | WS_EX_LEFT, 136 | "SPLASH", 137 | "SPLASH", 138 | WS_POPUP | WS_BORDER, 139 | ::GetSystemMetrics(SM_CXSCREEN) / 2 - width / 2, 140 | ::GetSystemMetrics(SM_CYSCREEN) / 2 - height / 2, 141 | width, 142 | height, 143 | NULL, 144 | NULL, 145 | m_hInstance, 146 | NULL); 147 | ::UpdateWindow(m_hWnd); 148 | ::ShowWindow(m_hWnd, SW_SHOW); 149 | 150 | MSG msg; 151 | int counter = 0; 152 | while (::GetMessageA(&msg, NULL, 0, 0)) 153 | { 154 | if (msg.message == WM_QUIT) 155 | { 156 | break; 157 | } 158 | 159 | ::TranslateMessage(&msg); 160 | ::DispatchMessageA(&msg); 161 | ::InvalidateRect(m_hWnd, NULL, FALSE); 162 | 163 | if (counter++ == 500) 164 | { 165 | ::Sleep(100); 166 | ::DestroyWindow(m_hWnd); 167 | break; 168 | } 169 | } 170 | 171 | ::DeleteObject(hPalette); 172 | return true; 173 | } 174 | 175 | bool CSplash::LoadBMP(LPCSTR lpFileName) 176 | { 177 | if (m_Data) 178 | delete[] m_Data; 179 | 180 | HANDLE hFile = ::CreateFileA(lpFileName, 181 | GENERIC_READ, 182 | 1, 183 | NULL, 184 | OPEN_EXISTING, 185 | FILE_FLAG_SEQUENTIAL_SCAN, 186 | NULL); 187 | if (hFile == (HANDLE)-1) 188 | return false; 189 | 190 | DWORD dwBytesRead, dwBytesToRead; 191 | char buffer[16]; 192 | if (!::ReadFile(hFile, buffer, 14, &dwBytesRead, NULL) || dwBytesRead != 14 || 193 | (buffer[0] != 'B' || buffer[1] != 'M')) 194 | { 195 | ::CloseHandle(hFile); 196 | return false; 197 | } 198 | 199 | dwBytesToRead = *(DWORD *)&buffer[2] - 14; 200 | m_Data = new BYTE[dwBytesToRead]; 201 | if (!::ReadFile(hFile, m_Data, dwBytesToRead, &dwBytesRead, NULL) || dwBytesRead != dwBytesToRead) 202 | { 203 | ::CloseHandle(hFile); 204 | delete[] m_Data; 205 | return false; 206 | } 207 | 208 | ::CloseHandle(hFile); 209 | return true; 210 | } 211 | 212 | DWORD CSplash::GetWidth() const 213 | { 214 | DWORD dwHeaderSize = *(DWORD *)&m_Data[0]; 215 | return (dwHeaderSize == 12) ? *(WORD *)&m_Data[4] : *(DWORD *)&m_Data[4]; 216 | } 217 | 218 | DWORD CSplash::GetHeight() const 219 | { 220 | DWORD dwHeaderSize = *(DWORD *)&m_Data[0]; 221 | return (dwHeaderSize == 12) ? *(WORD *)&m_Data[6] : *(DWORD *)&m_Data[8]; 222 | } 223 | 224 | DWORD CSplash::GetBPP() const 225 | { 226 | DWORD dwHeaderSize = *(DWORD *)&m_Data[0]; 227 | return (dwHeaderSize == 12) ? *(WORD *)&m_Data[10] : *(DWORD *)&m_Data[14]; 228 | } 229 | 230 | DWORD CSplash::GetDIBHeaderSize() const 231 | { 232 | DWORD dwHeaderSize = *(DWORD *)&m_Data[0]; 233 | if (dwHeaderSize == 40 && *(DWORD *)&m_Data[16] == 3) 234 | return 52; 235 | return dwHeaderSize; 236 | } 237 | 238 | DWORD CSplash::GetPaletteNumColors() const 239 | { 240 | DWORD dwHeaderSize = *(DWORD *)&m_Data[0]; 241 | return (dwHeaderSize == 12) ? 0 : *(DWORD *)&m_Data[32]; 242 | } 243 | 244 | DWORD CSplash::GetPaletteNumEntries() const 245 | { 246 | DWORD dwPaletteNumColors = GetPaletteNumColors(); 247 | if (dwPaletteNumColors || GetBPP() >= 16) 248 | return dwPaletteNumColors; 249 | else 250 | return 1 << GetBPP(); 251 | } 252 | 253 | DWORD CSplash::GetPaletteSize() const 254 | { 255 | DWORD dwHeaderSize = *(DWORD *)&m_Data[0]; 256 | return (dwHeaderSize == 12) ? 3 * GetPaletteNumEntries() : 4 * GetPaletteNumEntries(); 257 | } 258 | 259 | BYTE *CSplash::GetPaletteData() const 260 | { 261 | if (!GetPaletteNumEntries()) 262 | return NULL; 263 | 264 | return &m_Data[GetDIBHeaderSize()]; 265 | } 266 | 267 | BYTE *CSplash::GetPaletteEntry(DWORD index) const 268 | { 269 | if (!GetPaletteNumEntries()) 270 | return NULL; 271 | 272 | DWORD dwHeaderSize = *(DWORD *)&m_Data[0]; 273 | return (dwHeaderSize == 12) ? &GetPaletteData()[3 * index] : &GetPaletteData()[4 * index]; 274 | } 275 | 276 | BITMAPINFO *CSplash::GetBitmapInfo() const 277 | { 278 | static BITMAPINFO bmi; 279 | memset(&bmi, 0, sizeof(bmi)); 280 | bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); 281 | bmi.bmiHeader.biWidth = GetWidth(); 282 | bmi.bmiHeader.biHeight = GetHeight(); 283 | bmi.bmiHeader.biPlanes = 1; 284 | bmi.bmiHeader.biBitCount = (WORD)GetBPP(); 285 | bmi.bmiHeader.biCompression = BI_RGB; 286 | bmi.bmiHeader.biSizeImage = 0; 287 | return &bmi; 288 | } 289 | 290 | BYTE *CSplash::GetBitmapData() const 291 | { 292 | return &m_Data[GetDIBHeaderSize() + GetPaletteSize()]; 293 | } 294 | 295 | HPALETTE CSplash::GetPalette() const 296 | { 297 | WORD palNumEntries = (WORD)GetPaletteNumEntries(); 298 | if (palNumEntries == 0) 299 | return NULL; 300 | 301 | LPLOGPALETTE lpLogPalette = (LPLOGPALETTE)malloc(sizeof(LOGPALETTE) + palNumEntries * sizeof(PALETTEENTRY)); 302 | if (!lpLogPalette) 303 | return NULL; 304 | lpLogPalette->palVersion = PALVERSION; 305 | lpLogPalette->palNumEntries = palNumEntries; 306 | 307 | BYTE *lpEntry = NULL; 308 | LPPALETTEENTRY lpPalEntry = &lpLogPalette->palPalEntry[0]; 309 | for (int i = 0; i < palNumEntries; i++) 310 | { 311 | lpEntry = GetPaletteEntry(i); 312 | lpPalEntry->peRed = lpEntry[0]; 313 | lpPalEntry->peGreen = lpEntry[1]; 314 | lpPalEntry->peBlue = lpEntry[2]; 315 | lpPalEntry->peFlags = PC_NONE; 316 | ++lpPalEntry; 317 | } 318 | 319 | HPALETTE hPal = ::CreatePalette(lpLogPalette); 320 | free(lpLogPalette); 321 | return hPal; 322 | } -------------------------------------------------------------------------------- /README_zh-CN.md: -------------------------------------------------------------------------------- 1 | # BallancePlayer 2 | 3 | ## 概述 4 | 5 | BallancePlayer 是为游戏《Ballance》设计的现代化增强版主程序,通过反编译和改进原始版本开发而成。它在保持与原版游戏兼容的同时,提供了多项新功能和优化,带来更好的游戏体验。 6 | 7 | ## 特点 8 | 9 | - 开箱即用,无需配置即可启动游戏 10 | - 支持 `.ini` 文件配置 11 | - 支持灵活的命令行选项 12 | - 支持 32 位色彩模式 13 | - 兼容不支持 640x480 分辨率的显示器 14 | - 支持高刷新率显示器 15 | - 支持游戏内分辨率切换 16 | - 提供切换全屏模式和关闭游戏的快捷键 17 | - 无需修改注册表 18 | - 不依赖于 `Dsetup.dll` 19 | - 集成 `ResDll.dll`,便捷运行 20 | - 修复了原版播放器中的一些问题 21 | - 提升了性能 22 | 23 | ## 系统要求 24 | 25 | 支持以下 Windows 版本: 26 | - Windows XP 27 | - Windows Vista 28 | - Windows 7 29 | - Windows 8 / 8.1 30 | - Windows 10 31 | - Windows 11 32 | 33 | ## 安装说明 34 | 35 | 1. 将提供的压缩包解压至游戏文件夹中的 `Bin` 目录。 36 | 2. 运行 `Player.exe` 启动游戏,无需设置兼容模式。 37 | 38 | ## 从源码构建 39 | 40 | ### 依赖项 41 | 42 | 构建 BallancePlayer 需要 Virtools SDK,可以从 [Virtools-SDK-2.1](https://github.com/doyaGu/Virtools-SDK-2.1) 获取。在开始构建之前,请将环境变量 `VIRTOOLS_SDK_PATH` 设置为 SDK 的安装路径。 43 | 44 | ### 使用 CMake 构建 45 | 46 | 1. **安装 CMake**:确保你的系统上已安装 CMake。 47 | 2. **进入项目目录**:打开控制台,进入包含 BallancePlayer 源代码的目录。 48 | 3. **生成构建文件**:运行以下命令生成 Visual Studio 项目文件: 49 | ``` 50 | cmake -B build -G "Visual Studio 16 2022" -A Win32 51 | ``` 52 | 4. **在 Visual Studio 中打开**:进入 `build` 目录,打开 `BallancePlayer.sln` 解决方案文件。 53 | 5. **构建解决方案**:使用 Visual Studio 的构建工具编译项目。 54 | 55 | ### 使用 Visual Studio 6.0 构建 56 | 57 | 1. **安装 Visual Studio 6.0**:确保已安装 Visual Studio 6.0。 58 | 2. **打开项目**:在项目目录中找到 `Player.dsw` 并使用 Visual Studio 6.0 打开。 59 | 3. **构建项目**:使用 Visual Studio 6.0 的构建工具编译项目。 60 | 61 | ### 注意事项 62 | 63 | 官方发行版本为最大限度地提高兼容性,使用 Visual Studio 6.0 进行构建。 64 | 65 | ## 快捷键 66 | 67 | - **[Alt] + [Enter]**:切换窗口模式和全屏模式。 68 | - **[Alt] + [F4]**:强制关闭游戏。 69 | - **[Alt] + [/]**:显示关于窗口。 70 | 71 | ## INI 设置 72 | 73 | `Player.ini` 文件包含多个用于控制游戏行为的设置。 74 | 75 | ### 启动 76 | 77 | - `LogMode`:控制日志处理方式。 78 | - `0`:追加到日志文件。 79 | - `1`:覆盖日志文件。 80 | - `Verbose`:控制是否启用详细日志。 81 | - `0`:禁用。 82 | - `1`:启用。 83 | - `ManualSetup`:控制是否在启动时显示设置对话框。 84 | - `0`:禁用。 85 | - `1`:启用。 86 | 87 | ### 图形设置 88 | 89 | - `Driver`:指定要使用的显卡驱动 ID。 90 | - `BitsPerPixel`:指定色彩深度。 91 | - `32`:32 位色。 92 | - `16`:16 位色。 93 | - `Width`:指定屏幕宽度(根据显示器分辨率)。 94 | - `Height`:指定屏幕高度(根据显示器分辨率)。 95 | - `FullScreen`:控制显示模式。 96 | - `0`:窗口模式。 97 | - `1`:全屏模式。 98 | - `DisablePerspectiveCorrection`:控制是否禁用透视修正。 99 | - `0`:关闭。 100 | - `1`:开启。 101 | - `ForceLinearFog`:强制使用线性雾模式。 102 | - `0`:关闭。 103 | - `1`:开启。 104 | - `ForceSoftware`:禁用硬件渲染。 105 | - `0`:关闭。 106 | - `1`:开启。 107 | - `DisableFilter`:禁用纹理过滤。 108 | - `0`:关闭。 109 | - `1`:开启。 110 | - `EnsureVertexShader`:确保启用顶点着色器支持。 111 | - `0`:关闭。 112 | - `1`:开启。 113 | - `UseIndexBuffers`:启用索引缓冲区。 114 | - `0`:关闭。 115 | - `1`:开启。 116 | - `DisableDithering`:禁用抖动。 117 | - `0`:关闭。 118 | - `1`:开启。 119 | - `Antialias`:设置抗锯齿级别(最小值为 2)。 120 | - `DisableMipmap`:禁用 Mipmap。 121 | - `0`:关闭。 122 | - `1`:开启。 123 | - `DisableSpecular`:禁用高光。 124 | - `0`:关闭。 125 | - `1`:开启。 126 | - `EnableScreenDump`:启用屏幕转储功能。 127 | - `0`:关闭。 128 | - `1`:开启。 129 | - `EnableDebugMode`:启用逐步渲染的调试模式。 130 | - `0`:关闭。 131 | - `1`:开启。 132 | - `VertexCache`:设置顶点缓存大小。`0` 值表示禁用排序。 133 | - `TextureCacheManagement`:控制是否启用纹理缓存管理。 134 | - `0`:关闭。 135 | - `1`:开启。 136 | - `SortTransparentObjects`:控制是否对透明对象进行排序。 137 | - `0`:关闭。 138 | - `1`:开启。 139 | - `TextureVideoFormat`:指定纹理的默认像素格式(例如 `_32_ARGB8888`)。 140 | - `SpriteVideoFormat`:指定精灵的默认像素格式(例如 `_32_ARGB8888`)。 141 | 142 | ### 窗口设置 143 | 144 | - `ChildWindowRendering`:控制是否在子窗口中渲染。 145 | - `0`:禁用。 146 | - `1`:启用。 147 | - `Borderless`:控制窗口是否无边框。 148 | - `0`:禁用。 149 | - `1`:启用。 150 | - `ClipCursor`:控制是否限制鼠标光标在窗口内。 151 | - `0`:禁用。 152 | - `1`:启用。 153 | - `AlwaysHandleInput`:允许窗口在后台时仍然处理输入。 154 | - `0`:禁用。 155 | - `1`:启用。 156 | - `X`:设置窗口的水平坐标(可以为负值)。 157 | - `Y`:设置窗口的垂直坐标(可以为负值)。 158 | 159 | ### 游戏设置 160 | 161 | - `Language`:设置游戏语言。 162 | - `0`:德语。 163 | - `1`:英语。 164 | - `2`:西班牙语。 165 | - `3`:意大利语。 166 | - `4`:法语。 167 | - `SkipOpening`:控制是否跳过开场动画。 168 | - `0`:禁用。 169 | - `1`:启用。 170 | - `ApplyHotfix`:控制是否应用热修复。 171 | - `0`:禁用。 172 | - `1`:启用。 173 | - `UnlockFramerate`:解锁帧率限制。 174 | - `0`:禁用。 175 | - `1`:启用。 176 | - `UnlockWidescreen`:解锁非 4:3 分辨率。 177 | - `0`:禁用。 178 | - `1`:启用。 179 | - `UnlockHighResolution`:解锁高于 1600x1200 的分辨率。 180 | - `0`:禁用。 181 | - `1`:启用。 182 | - `Debug`:启用游戏内调试模式。 183 | - `0`:禁用。 184 | - `1`:启用。 185 | - `Rookie`:启用游戏内新手模式。 186 | - `0`:禁用。 187 | - `1`:启用。 188 | 189 | ## 命令行选项 190 | 191 | 你还可以使用命令行选项来自定义游戏行为: 192 | 193 | ```bash 194 | Player.exe [OPTIONS] 195 | ``` 196 | - `--verbose`:启用详细日志记录。 197 | - `-m`, `--manual-setup`:启动时总是显示设置对话框。 198 | - `-v `, `--video-driver `:设置显卡驱动 ID。 199 | - `-b `, `--bpp `:设置屏幕的色彩深度(32 或 16)。 200 | - `-w `, `--width `:设置屏幕宽度。 201 | - `-h `, `--height `:设置屏幕高度。 202 | - `-f`, `--fullscreen`:启动时进入全屏模式。 203 | - `--disable-perspective-correction`:禁用透视修正。 204 | - `--force-linear-fog`:强制使用线性雾。 205 | - `--force-software`:禁用硬件渲染并强制使用软件模式。 206 | - `--disable-filter`:禁用纹理过滤。 207 | - `--ensure-vertex-shader`:确保顶点着色器支持。 208 | - `--use-index-buffers`:启用索引缓冲区。 209 | - `--disable-dithering`:禁用抖动。 210 | - `--antialias `:启用抗锯齿(最小值:2)。 211 | - `--disable-mipmap`:禁用 Mipmap。 212 | - `--disable-specular`:禁用高光。 213 | - `--enable-screen-dump`:使用 (CTRL + ALT + F10) 保存屏幕内容。 214 | - `--enable-debug-mode`:使用 (CTRL + ALT + F11) 启用调试模式。 215 | - `--vertex-cache `:设置顶点缓存大小(0 禁用排序)。 216 | - `--disable-texture-cache-management`:禁用纹理缓存管理。 217 | - `--disable-sort-transparent-objects`:禁用透明对象排序。 218 | - `--texture-video-format `:设置纹理的像素格式(如 `_32_ARGB8888`)。 219 | - `--sprite-video-format `:设置精灵的像素格式(如 `_32_ARGB8888`)。 220 | - `-s`, `--child-window-rendering`:启用子窗口渲染。 221 | - `-c`, `--borderless`:启动时进入无边框模式。 222 | - `--clip-cursor`:限制鼠标光标在窗口内。 223 | - `--always-handle-input`:允许窗口在后台时处理输入。 224 | - `-x `, `--position-x `:设置窗口的 X 坐标。 225 | - `-y `, `--position-y `:设置窗口的 Y 坐标。 226 | - `-l `, `--lang `:设置游戏语言。 227 | - `--skip-opening`:跳过开场动画。 228 | - `--disable-hotfix`:禁用脚本热修复。 229 | - `-u`, `--unlock-framerate`:解除帧率限制。 230 | - `--unlock-widescreen`:解锁非 4:3 分辨率。 231 | - `--unlock-high-resolution`:解锁高于 1600x1200 的分辨率。 232 | - `d`, `--debug`:启用游戏内调试模式。 233 | - `r`, `--rookie`:启用游戏内新手模式。 234 | 235 | ## 联系方式 236 | 237 | 如果你有任何问题或功能请求,请在 GitHub 上进行反馈:[BallancePlayer](https://github.com/doyaGu/BallancePlayer)。 238 | 239 | ## 更新日志 240 | 241 | ### v0.3.8 (2025-09-25) 242 | 243 | **Bug Fixes** 244 | 245 | - 修复从全屏切换到窗口模式时窗口不可见的问题。 246 | 247 | ### v0.3.7 (2025-09-24) 248 | 249 | **新功能** 250 | 251 | - 添加了外部配置文件修改检测合并功能。 252 | 253 | **问题修复** 254 | 255 | - 修复了当 ClipCursor 设置未启用时,插件调用 ClipCursor 可能会受到干扰的问题。 256 | 257 | ### v0.3.6 (2025-08-20) 258 | 259 | **变更** 260 | 261 | - 由 ANSI 编码切换为 Unicode,以提升兼容性与国际化支持。 262 | 263 | ### v0.3.5 (2025-05-23) 264 | 265 | **新功能** 266 | 267 | - 引入了一个独立的配置工具,便于更轻松地管理游戏设置。 268 | - 增加了对加载自定义游戏文件的支持。 269 | 270 | **问题修复** 271 | 272 | - 修复了启用详细日志记录时可能导致的崩溃问题。 273 | 274 | **变更** 275 | 276 | - 移除了已弃用的设置项:`LoadAllManagers`、`LoadAllBuildingBlocks` 和 `LoadAllPlugins`。 277 | - 游戏在窗口失去焦点时默认不再暂停。 278 | 279 | ### v0.3.4 (2025-04-24) 280 | 281 | **新功能** 282 | 283 | - 添加了 DPI 感知处理支持,改善在不同显示器配置下的显示缩放效果。 284 | 285 | **问题修复** 286 | 287 | - 修复了命令行解析器可能导致参数处理错误的问题。 288 | - 解决了频繁切换窗口模式和全屏模式时导致的游戏加速异常问题。 289 | 290 | **变更** 291 | 292 | - 移除了已弃用的 `PauseOnDeactivated` 设置。 293 | - 增强了错误处理机制,提供更详细的错误信息并改进了从常见故障状态中的恢复能力。 294 | 295 | ### v0.3.3 (2024-10-01) 296 | 297 | **新功能** 298 | 299 | - 添加了 `LogMode` 设置用于控制日志模式。 300 | - 添加了 `Verbose` 设置和 `--verbose` 选项用于控制调试日志输出。 301 | - 引入了 `ApplyHotfix` 设置和 `--no-hotfix` 选项用于控制是否应用脚本热修复。 302 | - 添加了 `ClipCursor` 设置和 `--clip-cursor` 选项用于管理鼠标光标限制行为。 303 | - 增加了更多图形设置和选项。 304 | - 改进了错误消息提示,使其更加清晰。 305 | 306 | **变更** 307 | 308 | - 日志现在会覆盖现有文件而不是追加到其中。 309 | - 重新组织了配置类别以获得更好的结构。 310 | - 尽可能使用相对路径。 311 | 312 | ### v0.3.2 (2024-07-16) 313 | 314 | **问题修复** 315 | 316 | - 修复了插件注册中断的问题。 317 | 318 | **变更** 319 | 320 | - 应用程序窗口现在默认在屏幕中央打开。 321 | 322 | ### v0.3.1 (2024-03-04) 323 | 324 | **问题修复** 325 | 326 | - 修复了异常分辨率设置可能导致的潜在问题。 327 | 328 | ### v0.3.0 (2023-05-20) 329 | 330 | **新功能** 331 | 332 | - 添加了 `ChildWindowRendering` 设置和 `--child-window-rendering` 选项,用于控制是否在子窗口中进行渲染。 333 | 334 | **问题修复** 335 | 336 | - 解决了切换到全屏模式时可能出现的黑屏问题。 337 | 338 | **变更** 339 | 340 | - 默认情况下,不再在单独的窗口中进行渲染。 341 | 342 | ### v0.2.4 (2023-03-23) 343 | 344 | **问题修复** 345 | 346 | - 修复了在设置对话框中未选择屏幕模式时游戏初始化失败的问题。 347 | - 修正了渲染驱动程序初始化过程。 348 | 349 | ### v0.2.3 (2023-03-13) 350 | 351 | **新功能** 352 | 353 | - 添加了运行游戏多个实例的支持。 354 | 355 | **问题修复** 356 | 357 | - 修复了退出游戏时发生的崩溃问题。 358 | 359 | **变更** 360 | 361 | - 弃用了 `Resizable` 和 `ClipMouse` 设置。 362 | - 增强了错误消息以提高清晰度。 363 | - 重构了代码库以简化实现。 364 | 365 | ### v0.2.2 (2023-01-23) 366 | 367 | **新功能** 368 | 369 | - 添加了 `ManualSetup` 设置和相应的 `--manual-setup` 命令行选项,用于控制是否在启动时显示设置对话框。 370 | 371 | **问题修复** 372 | 373 | - 修复了命令行短选项解析中的问题。 374 | 375 | **变更** 376 | 377 | - 弃用了 `AdaptiveCamera` 设置。 378 | - 弃用了延迟加载的 DLL。 379 | - 弃用了路径自定义。 380 | - 简化了驱动程序和屏幕模式枚举的变通方法。 381 | 382 | ### v0.2.1 (2022-11-24) 383 | 384 | **新功能** 385 | 386 | - 添加了控制台日志支持。 387 | - 添加了 `LoadAllManagers` 设置和 `--load-all-managers` 命令行选项,用于控制是否加载所有管理器。 388 | - 添加了 `LoadAllBuildingBlocks` 设置和 `--load-all-building-blocks` 选项,用于控制是否加载所有构建模块。 389 | - 添加了 `LoadAllPlugins` 设置和 `--load-all-plugins` 命令行选项,用于控制是否加载所有插件。 390 | 391 | **问题修复** 392 | 393 | - 修复了屏幕模式可能被错误更改的问题。 394 | - 解决了由于路径设置不正确导致的启动失败。 395 | 396 | **变更** 397 | 398 | - 移除了对修改版 `TT_InterfaceManager_RT.dll` 的依赖。 399 | 400 | ### v0.2.0 (2022-10-15) 401 | 402 | **新功能** 403 | 404 | - 添加了中文版 README。 405 | 406 | ### v0.2.0-rc1 (2022-10-09) 407 | 408 | **问题修复** 409 | 410 | - 修复了退出游戏时可能发生的潜在崩溃问题。 411 | 412 | **变更** 413 | 414 | - 改进了游戏循环以获得更好的性能。 415 | 416 | ### v0.2.0-beta4 (2022-10-02) 417 | 418 | **问题修复** 419 | 420 | - 修复了在使用 OpenGL 时无法在全屏和窗口模式之间切换的问题。 421 | - 解决了可能导致错误的任务切换问题。 422 | 423 | ### v0.2.0-beta3 (2022-10-01) 424 | 425 | **问题修复** 426 | 427 | - 修复了全屏模式下任务切换时出现的 UI 崩溃。 428 | - 解决了指定显示驱动程序无效的问题。 429 | 430 | **变更** 431 | 432 | - 在 `全屏设置` 对话框中交换了 `驱动程序` 和 `屏幕模式` 的位置。 433 | 434 | ### v0.2.0-beta2 (2022-09-28) 435 | 436 | **问题修复** 437 | 438 | - 修复了退出后弹出黑屏的问题。 439 | 440 | ### v0.2.0-beta1 (2022-09-28) 441 | 442 | **问题修复** 443 | 444 | - 修复了强制全屏垂直同步。 445 | - 修复了窗口位置无法正确恢复的问题。 446 | 447 | ### v0.2.0-alpha4 (2022-09-25) 448 | 449 | **问题修复** 450 | 451 | - 解决了在不支持 640x480 分辨率的显示器上出现黑屏错误的问题。 452 | 453 | ### v0.2.0-alpha2 (2022-09-23) 454 | 455 | **新功能** 456 | 457 | - 添加了游戏内调试模式支持。 458 | - 引入了新的命令行选项,如 `--root-path=`。 459 | 460 | ### v0.2.0-alpha1 (2022-09-17) 461 | 462 | **新功能** 463 | 464 | - 添加了对更多分辨率、跳过开场动画、鼠标限制、非本地启动、游戏路径自定义和 Virtools 控制台输出的支持。 465 | - 引入了许多新设置和相应的命令行选项。 466 | 467 | **问题修复** 468 | 469 | - 修复了全屏设置的问题。 470 | - 解决了使用自定义分辨率时播放器会卡住的问题。 471 | 472 | **变更** 473 | 474 | - 统一了日志和错误输出。 475 | - 重新安排了设置和相应的命令行选项。 476 | 477 | ### v0.1.9 (2022-08-30) 478 | 479 | **问题修复** 480 | 481 | - 修复了退出全屏模式时的位置保存机制问题。 482 | 483 | **变更** 484 | 485 | - 如果窗口可调整大小(带有标题),当窗口位置设置为 0 时,现在会出现在左上角,与屏幕边缘保持较小的边距。 486 | - 保存的窗口位置值现在可以为负值。 487 | - 游戏现在会保存最后使用的游戏模式(全屏或窗口),并在下次启动时恢复。 488 | 489 | ### v0.1.8 (2022-08-17) 490 | 491 | **问题修复** 492 | 493 | - 修复了在启用垂直同步的情况下,游戏在 144Hz 显示器上被锁定在 60 FPS 的问题。 494 | - 解决了阻止游戏找到多个显示驱动程序的错误。 495 | 496 | ### v0.1.7 (2022-08-15) 497 | 498 | **新功能** 499 | 500 | - 添加了 `X` 和 `Y` 设置以及相应的 `-x` 和 `-y` 选项,用于设置窗口左上角的屏幕坐标。 501 | 502 | **问题修复** 503 | 504 | - 修复了游戏窗口在启动前不自然调整大小的问题。 505 | 506 | **变更** 507 | 508 | - 现在会保存最后的窗口位置并在下次启动时恢复。 509 | 510 | ### v0.1.6 (2022-08-05) 511 | 512 | **新功能** 513 | 514 | - 添加了 `Borderless` 设置和 `-c` 选项,用于以无边框模式启动。 515 | - 添加了 `Resizable` 设置和 `-s` 选项,使窗口可以调整大小。 516 | 517 | **变更** 518 | 519 | - 窗口现在默认不可调整大小。 520 | 521 | ### v0.1.5 (2022-07-20) 522 | 523 | **新功能** 524 | 525 | - 添加了对 32 位色彩模式的支持。 526 | - 添加了对命令行长选项的支持。 527 | - 添加了 `Language` 设置和 `-l` 选项用于设置游戏语言。 528 | - 添加了 `UnlockFramerate` 设置和 `-u` 选项用于解锁帧率限制。 529 | 530 | **问题修复** 531 | 532 | - 修复了任务切换期间的 UI 混乱错误。 533 | 534 | **变更** 535 | 536 | - 将禁用任务切换的命令行选项从 `-d` 更改为 `-e`。 537 | - 重新实现了初始化配置机制。 538 | - 重写了命令行支持。 539 | - 集成了 `ResDll` 的功能。 540 | 541 | ### v0.1.4 (2022-05-12) 542 | 543 | **新功能** 544 | 545 | - 添加了 `PauseOnTaskSwitch` 设置和 `-p` 选项,用于在任务切换期间启用游戏暂停。 546 | 547 | **问题修复** 548 | 549 | - 修复了游戏过早退出和退出时崩溃的错误。 550 | 551 | **变更** 552 | 553 | - 移除了不必要的虚拟函数以提高性能。 554 | - 移除了未使用的界面精灵。 555 | - 改进了错误处理。 556 | 557 | ### v0.1.3 (2022-05-09) 558 | 559 | **新功能** 560 | 561 | - 添加了对命令行选项的支持。 562 | 563 | **问题修复** 564 | 565 | - 修复了全屏崩溃。 566 | - 修复了任务切换问题。 567 | - 修复了图形选项中的重复分辨率。 568 | 569 | **变更** 570 | 571 | - 移除了对 `Dsetup.dll` 的依赖,因为在现代电脑上检查 DirectX 是不必要的。 572 | - 移除了异常处理以提高性能。 573 | 574 | ### v0.1.2 (2022-05-08) 575 | 576 | **问题修复** 577 | 578 | - 修复了游戏在退出时可能崩溃的问题。 579 | 580 | **变更** 581 | 582 | - 在未找到配置文件时,现在会生成默认配置文件。 583 | - 替换了主函数中剩余的注册表操作。 584 | - 移除了 `FixedString`。 585 | 586 | ### v0.1.1 (2022-05-04) 587 | 588 | **新功能** 589 | 590 | - 添加了对初始化文件配置的支持。 591 | 592 | **问题修复** 593 | 594 | - 修复了游戏在窗口模式下显示不完整的问题。 595 | 596 | **变更** 597 | 598 | - 移除了一些未知的类成员。 599 | 600 | ### v0.1.0 (2022-05-03) 601 | 602 | **问题修复** 603 | 604 | - 修复了导致不正确结果的内存检查错误。 -------------------------------------------------------------------------------- /src/GameConfig.cpp: -------------------------------------------------------------------------------- 1 | #include "GameConfig.h" 2 | 3 | #include "Utils.h" 4 | 5 | #ifndef WIN32_LEAN_AND_MEAN 6 | #define WIN32_LEAN_AND_MEAN 7 | #endif 8 | #include 9 | 10 | static bool GetLastWriteTime(const char *filename, FILETIME &outTime) 11 | { 12 | WIN32_FIND_DATAA findData; 13 | HANDLE handle = ::FindFirstFileA(filename, &findData); 14 | if (handle == INVALID_HANDLE_VALUE) 15 | return false; 16 | outTime = findData.ftLastWriteTime; 17 | ::FindClose(handle); 18 | return true; 19 | } 20 | 21 | static bool SameFileTimeComponents(const FILETIME &timeValue, DWORD low, DWORD high) 22 | { 23 | return (timeValue.dwLowDateTime == low) && (timeValue.dwHighDateTime == high); 24 | } 25 | 26 | static const char *const DefaultPaths[] = { 27 | "Player.ini", 28 | "Player.log", 29 | "base.cmo", 30 | "..\\", 31 | "Plugins\\", 32 | "RenderEngines\\", 33 | "Managers\\", 34 | "BuildingBlocks\\", 35 | "Sounds\\", 36 | "Textures\\", 37 | "", 38 | }; 39 | 40 | CGameConfig::CGameConfig() 41 | { 42 | // Auto-generated initialization from master list 43 | #define X_BOOL(sec,key,member,def) member = def; 44 | #define X_INT(sec,key,member,def) member = def; 45 | #define X_PF(sec,key,member,def) member = def; 46 | GAMECONFIG_FIELDS 47 | #undef X_BOOL 48 | #undef X_INT 49 | #undef X_PF 50 | 51 | // Non-INI members 52 | screenMode = -1; 53 | debug = false; 54 | rookie = false; 55 | 56 | ResetPath(); 57 | ResetLoadedSnapshots(); 58 | } 59 | 60 | CGameConfig &CGameConfig::operator=(const CGameConfig &config) 61 | { 62 | if (this == &config) 63 | return *this; 64 | 65 | // Auto-generated copying from master list 66 | #define X_BOOL(sec,key,member,def) member = config.member; 67 | #define X_INT(sec,key,member,def) member = config.member; 68 | #define X_PF(sec,key,member,def) member = config.member; 69 | GAMECONFIG_FIELDS 70 | #undef X_BOOL 71 | #undef X_INT 72 | #undef X_PF 73 | 74 | // Non-INI members 75 | screenMode = config.screenMode; 76 | debug = config.debug; 77 | rookie = config.rookie; 78 | 79 | // Copy paths 80 | int i; 81 | for (i = 0; i < ePathCategoryCount; ++i) 82 | m_Paths[i] = config.m_Paths[i]; 83 | 84 | // Copy loaded snapshots 85 | for (i = 0; i < eLoadedSentinel; ++i) 86 | { 87 | m_LoadedInts[i] = config.m_LoadedInts[i]; 88 | m_LoadedBools[i] = config.m_LoadedBools[i]; 89 | m_LoadedPixels[i] = config.m_LoadedPixels[i]; 90 | } 91 | 92 | m_ConfigTimestampLow = config.m_ConfigTimestampLow; 93 | m_ConfigTimestampHigh = config.m_ConfigTimestampHigh; 94 | m_ConfigTimestampValid = config.m_ConfigTimestampValid; 95 | m_LastConfigAbsolutePath = config.m_LastConfigAbsolutePath; 96 | 97 | return *this; 98 | } 99 | 100 | bool CGameConfig::HasPath(PathCategory category) const 101 | { 102 | if (category < 0 || category >= ePathCategoryCount) 103 | return false; 104 | return m_Paths[category].size() != 0; 105 | } 106 | 107 | const char *CGameConfig::GetPath(PathCategory category) const 108 | { 109 | if (category < 0 || category >= ePathCategoryCount) 110 | return NULL; 111 | return m_Paths[category].c_str(); 112 | } 113 | 114 | void CGameConfig::SetPath(PathCategory category, const char *path) 115 | { 116 | if (category < 0 || category >= ePathCategoryCount || !path) 117 | return; 118 | m_Paths[category] = path; 119 | } 120 | 121 | bool CGameConfig::ResetPath(PathCategory category) 122 | { 123 | if (category < 0 || category > ePathCategoryCount) 124 | return false; 125 | 126 | if (category == ePathCategoryCount) 127 | { 128 | // Reset all paths 129 | for (int i = 0; i < ePathCategoryCount; ++i) 130 | { 131 | if (i < ePluginPath) 132 | { 133 | SetPath((PathCategory)i, DefaultPaths[i]); 134 | } 135 | else 136 | { 137 | char szPath[MAX_PATH]; 138 | utils::ConcatPath(szPath, MAX_PATH, GetPath(eRootPath), DefaultPaths[i]); 139 | SetPath((PathCategory)i, szPath); 140 | } 141 | } 142 | } 143 | else 144 | { 145 | // Reset specific path 146 | if (category < ePluginPath) 147 | { 148 | SetPath(category, DefaultPaths[category]); 149 | } 150 | else 151 | { 152 | char szPath[MAX_PATH]; 153 | utils::ConcatPath(szPath, MAX_PATH, GetPath(eRootPath), DefaultPaths[category]); 154 | SetPath(category, szPath); 155 | } 156 | } 157 | 158 | return true; 159 | } 160 | 161 | void CGameConfig::LoadFromIni(const char *filename) 162 | { 163 | if (!filename) 164 | return; 165 | 166 | if (filename[0] == '\0') 167 | { 168 | if (m_Paths[eConfigPath].size() == 0 || !utils::FileOrDirectoryExists(m_Paths[eConfigPath].c_str())) 169 | return; 170 | filename = m_Paths[eConfigPath].c_str(); 171 | } 172 | 173 | char path[MAX_PATH]; 174 | if (!utils::IsAbsolutePath(filename)) 175 | { 176 | utils::GetCurrentPath(path, MAX_PATH); 177 | utils::ConcatPath(path, MAX_PATH, path, filename); 178 | filename = path; 179 | } 180 | 181 | ResetLoadedSnapshots(); 182 | 183 | FILETIME fileTime; 184 | if (GetLastWriteTime(filename, fileTime)) 185 | { 186 | m_ConfigTimestampLow = fileTime.dwLowDateTime; 187 | m_ConfigTimestampHigh = fileTime.dwHighDateTime; 188 | m_ConfigTimestampValid = true; 189 | } 190 | 191 | SetLastConfigAbsolutePath(filename); 192 | 193 | // Auto-generated loading from master list 194 | #define X_BOOL(sec,key,member,def) \ 195 | do { \ 196 | bool tmp = member; \ 197 | if (utils::IniGetBoolean(sec, key, tmp, filename)) { \ 198 | member = tmp; \ 199 | StoreLoadedBool(eLoadedBool_##member, tmp); \ 200 | } \ 201 | } while(0); 202 | 203 | #define X_INT(sec,key,member,def) \ 204 | do { \ 205 | int tmp = member; \ 206 | if (utils::IniGetInteger(sec, key, tmp, filename)) { \ 207 | member = tmp; \ 208 | StoreLoadedInt(eLoadedInt_##member, tmp); \ 209 | } \ 210 | } while(0); 211 | 212 | #define X_PF(sec,key,member,def) \ 213 | do { \ 214 | VX_PIXELFORMAT tmp = member; \ 215 | if (utils::IniGetPixelFormat(sec, key, tmp, filename)) { \ 216 | member = tmp; \ 217 | StoreLoadedPixel(eLoadedPixel_##member, tmp); \ 218 | } \ 219 | } while(0); 220 | 221 | GAMECONFIG_FIELDS 222 | 223 | #undef X_BOOL 224 | #undef X_INT 225 | #undef X_PF 226 | } 227 | 228 | void CGameConfig::SaveToIni(const char *filename) 229 | { 230 | if (!filename) 231 | return; 232 | 233 | if (filename[0] == '\0') 234 | { 235 | if (m_Paths[eConfigPath].size() == 0) 236 | return; 237 | filename = m_Paths[eConfigPath].c_str(); 238 | } 239 | 240 | char path[MAX_PATH]; 241 | if (!utils::IsAbsolutePath(filename)) 242 | { 243 | utils::GetCurrentPath(path, MAX_PATH); 244 | utils::ConcatPath(path, MAX_PATH, path, filename); 245 | filename = path; 246 | } 247 | 248 | bool shouldMerge = false; 249 | FILETIME fileTime; 250 | if (m_ConfigTimestampValid && IsSameConfigPath(filename) && GetLastWriteTime(filename, fileTime)) 251 | { 252 | if (!SameFileTimeComponents(fileTime, m_ConfigTimestampLow, m_ConfigTimestampHigh)) 253 | shouldMerge = true; 254 | } 255 | 256 | if (shouldMerge) 257 | MergeExternalChanges(filename); 258 | 259 | // Auto-generated saving from master list 260 | #define X_BOOL(sec,key,member,def) utils::IniSetBoolean(sec, key, member, filename); 261 | #define X_INT(sec,key,member,def) utils::IniSetInteger(sec, key, member, filename); 262 | #define X_PF(sec,key,member,def) utils::IniSetPixelFormat(sec, key, member, filename); 263 | GAMECONFIG_FIELDS 264 | #undef X_BOOL 265 | #undef X_INT 266 | #undef X_PF 267 | 268 | CaptureCurrentValuesAsLoaded(); 269 | 270 | if (GetLastWriteTime(filename, fileTime)) 271 | { 272 | m_ConfigTimestampLow = fileTime.dwLowDateTime; 273 | m_ConfigTimestampHigh = fileTime.dwHighDateTime; 274 | m_ConfigTimestampValid = true; 275 | } 276 | else 277 | { 278 | m_ConfigTimestampValid = false; 279 | } 280 | 281 | SetLastConfigAbsolutePath(filename); 282 | } 283 | 284 | void CGameConfig::ResetLoadedSnapshots() 285 | { 286 | for (int i = 0; i < eLoadedSentinel; ++i) 287 | { 288 | m_LoadedInts[i].hasValue = false; 289 | m_LoadedInts[i].value = 0; 290 | m_LoadedBools[i].hasValue = false; 291 | m_LoadedBools[i].value = false; 292 | m_LoadedPixels[i].hasValue = false; 293 | m_LoadedPixels[i].value = UNKNOWN_PF; 294 | } 295 | 296 | m_ConfigTimestampLow = 0; 297 | m_ConfigTimestampHigh = 0; 298 | m_ConfigTimestampValid = false; 299 | m_LastConfigAbsolutePath.erase(); 300 | } 301 | 302 | void CGameConfig::StoreLoadedInt(int index, int value) 303 | { 304 | if (index < 0 || index >= eLoadedSentinel) 305 | return; 306 | m_LoadedInts[index].hasValue = true; 307 | m_LoadedInts[index].value = value; 308 | } 309 | 310 | void CGameConfig::StoreLoadedBool(int index, bool value) 311 | { 312 | if (index < 0 || index >= eLoadedSentinel) 313 | return; 314 | m_LoadedBools[index].hasValue = true; 315 | m_LoadedBools[index].value = value; 316 | } 317 | 318 | void CGameConfig::StoreLoadedPixel(int index, VX_PIXELFORMAT value) 319 | { 320 | if (index < 0 || index >= eLoadedSentinel) 321 | return; 322 | m_LoadedPixels[index].hasValue = true; 323 | m_LoadedPixels[index].value = value; 324 | } 325 | 326 | bool CGameConfig::ShouldOverrideInt(int index, int newValue) const 327 | { 328 | if (index < 0 || index >= eLoadedSentinel) 329 | return false; 330 | if (!m_LoadedInts[index].hasValue) 331 | return true; 332 | // Return true if the current in-memory value is unchanged from what we loaded 333 | // This means we should accept the external change 334 | return m_LoadedInts[index].value == newValue; 335 | } 336 | 337 | bool CGameConfig::ShouldOverrideBool(int index, bool newValue) const 338 | { 339 | if (index < 0 || index >= eLoadedSentinel) 340 | return false; 341 | if (!m_LoadedBools[index].hasValue) 342 | return true; 343 | // Return true if the current in-memory value is unchanged from what we loaded 344 | // This means we should accept the external change 345 | return m_LoadedBools[index].value == newValue; 346 | } 347 | 348 | bool CGameConfig::ShouldOverridePixel(int index, VX_PIXELFORMAT newValue) const 349 | { 350 | if (index < 0 || index >= eLoadedSentinel) 351 | return false; 352 | if (!m_LoadedPixels[index].hasValue) 353 | return true; 354 | // Return true if the current in-memory value is unchanged from what we loaded 355 | // This means we should accept the external change 356 | return m_LoadedPixels[index].value == newValue; 357 | } 358 | 359 | void CGameConfig::CaptureCurrentValuesAsLoaded() 360 | { 361 | // Auto-generated snapshot capture from master list 362 | #define X_BOOL(sec,key,member,def) \ 363 | m_LoadedBools[eLoadedBool_##member].hasValue = true; \ 364 | m_LoadedBools[eLoadedBool_##member].value = member; 365 | 366 | #define X_INT(sec,key,member,def) \ 367 | m_LoadedInts[eLoadedInt_##member].hasValue = true; \ 368 | m_LoadedInts[eLoadedInt_##member].value = member; 369 | 370 | #define X_PF(sec,key,member,def) \ 371 | m_LoadedPixels[eLoadedPixel_##member].hasValue = true; \ 372 | m_LoadedPixels[eLoadedPixel_##member].value = member; 373 | 374 | GAMECONFIG_FIELDS 375 | 376 | #undef X_BOOL 377 | #undef X_INT 378 | #undef X_PF 379 | } 380 | 381 | void CGameConfig::MergeExternalChanges(const char *filename) 382 | { 383 | // Auto-generated merging from master list 384 | #define X_BOOL(sec,key,member,def) \ 385 | do { \ 386 | bool tmp; \ 387 | if (utils::IniGetBoolean(sec, key, tmp, filename)) { \ 388 | if (ShouldOverrideBool(eLoadedBool_##member, member)) \ 389 | member = tmp; \ 390 | } \ 391 | } while(0); 392 | 393 | #define X_INT(sec,key,member,def) \ 394 | do { \ 395 | int tmp; \ 396 | if (utils::IniGetInteger(sec, key, tmp, filename)) { \ 397 | if (ShouldOverrideInt(eLoadedInt_##member, member)) \ 398 | member = tmp; \ 399 | } \ 400 | } while(0); 401 | 402 | #define X_PF(sec,key,member,def) \ 403 | do { \ 404 | VX_PIXELFORMAT tmp; \ 405 | if (utils::IniGetPixelFormat(sec, key, tmp, filename)) { \ 406 | if (ShouldOverridePixel(eLoadedPixel_##member, member)) \ 407 | member = tmp; \ 408 | } \ 409 | } while(0); 410 | 411 | GAMECONFIG_FIELDS 412 | 413 | #undef X_BOOL 414 | #undef X_INT 415 | #undef X_PF 416 | } 417 | 418 | void CGameConfig::SetLastConfigAbsolutePath(const char *path) 419 | { 420 | if (path && path[0] != '\0') 421 | m_LastConfigAbsolutePath = path; 422 | else 423 | m_LastConfigAbsolutePath.erase(); 424 | } 425 | 426 | bool CGameConfig::IsSameConfigPath(const char *path) const 427 | { 428 | if (!path) 429 | return false; 430 | if (m_LastConfigAbsolutePath.size() == 0) 431 | return false; 432 | 433 | const char *stored = m_LastConfigAbsolutePath.c_str(); 434 | while (*stored && *path) 435 | { 436 | unsigned char c1 = static_cast(*stored); 437 | unsigned char c2 = static_cast(*path); 438 | if (toupper(c1) != toupper(c2)) 439 | return false; 440 | ++stored; 441 | ++path; 442 | } 443 | 444 | return (*stored == '\0') && (*path == '\0'); 445 | } -------------------------------------------------------------------------------- /src/Hotfix.cpp: -------------------------------------------------------------------------------- 1 | #include "CKAll.h" 2 | 3 | #include "Logger.h" 4 | #include "ScriptUtils.h" 5 | #include "InterfaceManager.h" 6 | #include "GameConfig.h" 7 | 8 | static bool SetDebugMode(CKBehavior *setDebugMode) 9 | { 10 | if (!setDebugMode) 11 | return false; 12 | 13 | CKBehavior *bs = scriptutils::GetBehavior(setDebugMode, "Binary Switch"); 14 | if (!bs) 15 | return false; 16 | 17 | CKBOOL debug = TRUE; 18 | scriptutils::GenerateInputParameter(setDebugMode, bs, 0, debug); 19 | 20 | return true; 21 | } 22 | 23 | static bool SetLanguage(CKBehavior *setLanguage, int langId) 24 | { 25 | if (!setLanguage) 26 | return false; 27 | 28 | CKBehavior *gc = scriptutils::GetBehavior(setLanguage, "Get Cell"); 29 | if (!gc) 30 | return false; 31 | 32 | CKBehaviorLink *linkIn0 = NULL; 33 | const int linkCount = setLanguage->GetSubBehaviorLinkCount(); 34 | for (int i = 0; i < linkCount; ++i) 35 | { 36 | CKBehaviorLink *link = setLanguage->GetSubBehaviorLink(i); 37 | CKBehaviorIO *outIO = link->GetOutBehaviorIO(); 38 | if (outIO->GetOwner() == gc && gc->GetInput(0) == outIO) 39 | linkIn0 = link; 40 | } 41 | if (!linkIn0) 42 | return false; 43 | 44 | CKBehaviorLink *linkRrOp = scriptutils::RemoveBehaviorLink(setLanguage, "TT_ReadRegistry", "Op", 0, 0); 45 | if (!linkRrOp) 46 | return false; 47 | 48 | CKBehavior *op2 = linkRrOp->GetOutBehaviorIO()->GetOwner(); 49 | scriptutils::SetInputParameterValue(op2, 0, langId); 50 | CKDestroyObject(linkRrOp); 51 | 52 | linkIn0->SetOutBehaviorIO(op2->GetInput(0)); 53 | 54 | return true; 55 | } 56 | 57 | static int ListDriver(const CKBehaviorContext &behcontext) 58 | { 59 | CKBehavior *beh = behcontext.Behavior; 60 | CKContext *context = behcontext.Context; 61 | 62 | CKDataArray *drivers = (CKDataArray *)beh->GetInputParameterObject(0); 63 | if (!drivers) 64 | { 65 | context->OutputToConsoleExBeep("ListDriver: No DataArray Object is found."); 66 | beh->ActivateOutput(1); 67 | return CKBR_OK; 68 | } 69 | 70 | drivers->GetColumnCount(); 71 | drivers->Clear(); 72 | while (drivers->GetColumnCount() > 0) 73 | drivers->RemoveColumn(0); 74 | 75 | InterfaceManager *man = InterfaceManager::GetManager(context); 76 | if (!man) 77 | { 78 | context->OutputToConsoleExBeep("ListDriver: im == NULL"); 79 | beh->ActivateOutput(1); 80 | return CKBR_OK; 81 | } 82 | 83 | drivers->InsertColumn(-1, CKARRAYTYPE_STRING, "DriverDesc"); 84 | drivers->InsertColumn(0, CKARRAYTYPE_STRING, "DriverName"); 85 | drivers->InsertColumn(1, CKARRAYTYPE_INT, "DriverID"); 86 | 87 | const int driverCount = context->GetRenderManager()->GetRenderDriverCount(); 88 | for (int i = 0; i < driverCount; ++i) 89 | { 90 | VxDriverDesc *drDesc = context->GetRenderManager()->GetRenderDriverDescription(i); 91 | drivers->InsertRow(); 92 | #if CKVERSION == 0x13022002 93 | drivers->SetElementStringValue(i, 0, drDesc->DriverName); 94 | #else 95 | drivers->SetElementStringValue(i, 0, drDesc->DriverName.Str()); 96 | #endif 97 | drivers->SetElementValue(i, 1, &i, sizeof(int)); 98 | #if CKVERSION == 0x13022002 99 | drivers->SetElementStringValue(i, 2, drDesc->DriverDesc); 100 | #else 101 | drivers->SetElementStringValue(i, 2, drDesc->DriverDesc.Str()); 102 | #endif 103 | } 104 | 105 | int driver = man->GetDriver(); 106 | beh->SetOutputParameterValue(0, &driver, sizeof(int)); 107 | beh->ActivateOutput(0); 108 | return CKBR_OK; 109 | } 110 | 111 | static bool ReplaceListDriver(CKBehavior *screenModes) 112 | { 113 | if (!screenModes) 114 | return false; 115 | 116 | CKBehavior *ld = scriptutils::GetBehavior(screenModes, "TT List Driver"); 117 | if (!ld) 118 | return false; 119 | 120 | ld->SetFunction(ListDriver); 121 | return true; 122 | } 123 | 124 | static int ListScreenModes(const CKBehaviorContext &behcontext) 125 | { 126 | CKBehavior *beh = behcontext.Behavior; 127 | CKContext *context = behcontext.Context; 128 | 129 | int driverId = 0; 130 | beh->GetInputParameterValue(0, &driverId); 131 | 132 | CKDataArray *screenModes = (CKDataArray *)beh->GetInputParameterObject(1); 133 | if (!screenModes) 134 | { 135 | context->OutputToConsoleExBeep("ListScreenModes: No DataArray Object is found."); 136 | beh->ActivateOutput(1); 137 | return CKBR_OK; 138 | } 139 | 140 | screenModes->Clear(); 141 | while (screenModes->GetColumnCount() > 0) 142 | screenModes->RemoveColumn(0); 143 | 144 | screenModes->InsertColumn(-1, CKARRAYTYPE_INT, "Bpp"); 145 | screenModes->InsertColumn(0, CKARRAYTYPE_INT, "Mode"); 146 | screenModes->InsertColumn(1, CKARRAYTYPE_INT, "Width"); 147 | screenModes->InsertColumn(2, CKARRAYTYPE_INT, "Height"); 148 | 149 | VxDriverDesc *drDesc = context->GetRenderManager()->GetRenderDriverDescription(driverId); 150 | if (!drDesc) 151 | { 152 | context->OutputToConsoleExBeep("ListScreenModes: No Driver Description for Driver-ID '%d' is found", driverId); 153 | beh->ActivateOutput(1); 154 | return CKBR_OK; 155 | } 156 | 157 | #if CKVERSION == 0x13022002 158 | VxDisplayMode *dm = drDesc->DisplayModes; 159 | const int dmCount = drDesc->DisplayModeCount; 160 | #else 161 | XArray &dm = drDesc->DisplayModes; 162 | const int dmCount = dm.Size(); 163 | #endif 164 | int i = 0, row = 0; 165 | while (i < dmCount) 166 | { 167 | int width = dm[i].Width; 168 | int height = dm[i].Height; 169 | 170 | int maxRefreshRate = 0; 171 | for (int j = i; j < dmCount && dm[j].Width == width && dm[j].Height == height; ++j) 172 | { 173 | if (dm[j].Bpp > 8 && dm[j].RefreshRate > maxRefreshRate) 174 | maxRefreshRate = dm[j].RefreshRate; 175 | } 176 | 177 | while (i < dmCount && dm[i].Width == width && dm[i].Height == height) 178 | { 179 | if (dm[i].Bpp > 8 && dm[i].RefreshRate == maxRefreshRate) 180 | { 181 | screenModes->InsertRow(); 182 | screenModes->SetElementValue(row, 0, &i, sizeof(int)); 183 | screenModes->SetElementValue(row, 1, &dm[i].Width, sizeof(int)); 184 | screenModes->SetElementValue(row, 2, &dm[i].Height, sizeof(int)); 185 | screenModes->SetElementValue(row, 3, &dm[i].Bpp, sizeof(int)); 186 | ++row; 187 | } 188 | ++i; 189 | } 190 | } 191 | 192 | InterfaceManager *man = InterfaceManager::GetManager(context); 193 | if (!man) 194 | { 195 | context->OutputToConsoleExBeep("ListScreenModes: im == NULL"); 196 | beh->ActivateOutput(1); 197 | return CKBR_OK; 198 | } 199 | 200 | int screenMode = man->GetScreenMode(); 201 | beh->SetOutputParameterValue(0, &screenMode, sizeof(int)); 202 | beh->ActivateOutput(0); 203 | return CKBR_OK; 204 | } 205 | 206 | static bool ReplaceListScreenModes(CKBehavior *screenModes) 207 | { 208 | if (!screenModes) 209 | return false; 210 | 211 | CKBehavior *ls = scriptutils::GetBehavior(screenModes, "TT List ScreenModes"); 212 | if (!ls) 213 | return false; 214 | 215 | ls->SetFunction(ListScreenModes); 216 | return true; 217 | } 218 | 219 | static bool UnlockWidescreen(CKBehavior *screenModes, CKBehavior *minWidth) 220 | { 221 | if (!screenModes || !minWidth) 222 | return false; 223 | 224 | CKBehavior *ic = scriptutils::GetBehavior(screenModes, "Insert Column", "ScreenModes"); 225 | if (!ic) 226 | return false; 227 | 228 | CKBehaviorLink *linkScIc = scriptutils::RemoveBehaviorLink(screenModes, "Set Cell", ic, 0, 0); 229 | if (!linkScIc) 230 | return false; 231 | 232 | CKBehavior *sc = linkScIc->GetInBehaviorIO()->GetOwner(); 233 | screenModes->RemoveSubBehaviorLink(linkScIc); 234 | CKDestroyObject(linkScIc); 235 | 236 | scriptutils::RemoveBehaviorLink(screenModes, "Remove Column", minWidth, 0, 0, true); 237 | scriptutils::CreateBehaviorLink(screenModes, sc, minWidth, 0, 0); 238 | 239 | return true; 240 | } 241 | 242 | static bool UnlockHighResolution(CKBehavior *screenModes, CKBehavior *bppFilter, CKBehavior *minWidth, CKBehavior *maxWidth, bool unlockWidescreen) 243 | { 244 | if (!screenModes || !bppFilter || !minWidth || !maxWidth) 245 | return false; 246 | 247 | const char *inBehName = (unlockWidescreen) ? "Set Cell" : "Remove Column"; 248 | CKBehaviorLink *linkRri = scriptutils::RemoveBehaviorLink(screenModes, inBehName, minWidth, 0, 0); 249 | if (!linkRri) 250 | return false; 251 | 252 | CKBehavior *inBeh = linkRri->GetInBehaviorIO()->GetOwner(); 253 | CKDestroyObject(linkRri); 254 | 255 | scriptutils::RemoveBehaviorLink(screenModes, maxWidth, bppFilter, 0, 0, true); 256 | scriptutils::CreateBehaviorLink(screenModes, inBeh, bppFilter, 0, 0); 257 | 258 | return true; 259 | } 260 | 261 | static bool UnlockFramerate(CKBehavior *synchToScreen) 262 | { 263 | if (!synchToScreen) 264 | return false; 265 | 266 | bool res = false; 267 | 268 | for (int i = 0; i < synchToScreen->GetSubBehaviorCount(); ++i) 269 | { 270 | CKBehavior *beh = synchToScreen->GetSubBehavior(i); 271 | if (strcmp(beh->GetName(), "Time Settings") == 0) 272 | { 273 | CKDWORD *frameRate = (CKDWORD *)beh->GetInputParameterReadDataPtr(0); 274 | if (*frameRate == 3) // Frame Rate == Limit 275 | { 276 | *frameRate = 1; // Frame Rate = Free 277 | res = true; 278 | break; 279 | } 280 | } 281 | } 282 | 283 | return res; 284 | } 285 | 286 | static bool SkipResolutionCheck(CKBehavior *synchToScreen) 287 | { 288 | if (!synchToScreen) 289 | return false; 290 | 291 | CKBehavior *ii = scriptutils::GetBehavior(synchToScreen, "Iterator If"); 292 | CKBehavior *delayer = scriptutils::GetBehavior(synchToScreen, "Delayer"); 293 | if (!(ii && delayer)) 294 | return false; 295 | 296 | CKBehaviorLink *linkTsIi1 = scriptutils::GetBehaviorLink(synchToScreen, "Time Settings", ii, 0, 0); 297 | CKBehaviorLink *linkTsIi2 = scriptutils::GetBehaviorLink(synchToScreen, "Time Settings", ii, 0, 0, linkTsIi1); 298 | if (!(linkTsIi1 && linkTsIi2)) 299 | return false; 300 | 301 | CKBehaviorLink *linkDelayerCsm = scriptutils::RemoveBehaviorLink(synchToScreen, delayer, "TT Change ScreenMode", 0, 0); 302 | if (!linkDelayerCsm) 303 | return false; 304 | 305 | linkTsIi1->SetOutBehaviorIO(linkDelayerCsm->GetOutBehaviorIO()); 306 | linkTsIi2->SetOutBehaviorIO(linkDelayerCsm->GetOutBehaviorIO()); 307 | CKDestroyObject(linkDelayerCsm); 308 | 309 | return true; 310 | } 311 | 312 | static bool SkipOpeningAnimation(CKBehavior *defaultLevel, CKBehavior *synchToScreen) 313 | { 314 | if (!defaultLevel || !synchToScreen) 315 | return false; 316 | 317 | CKBehavior *is = scriptutils::GetBehavior(defaultLevel, "Intro Start"); 318 | CKBehavior *ie = scriptutils::GetBehavior(defaultLevel, "Intro Ende"); 319 | CKBehavior *ml = scriptutils::GetBehavior(defaultLevel, "Main Loading"); 320 | CKBehavior *ps = scriptutils::GetBehavior(defaultLevel, "Preload Sound"); 321 | if (!(is && ie && ml && ps)) 322 | return false; 323 | 324 | CKBehaviorLink *linkStsIs = scriptutils::GetBehaviorLink(defaultLevel, synchToScreen, is, 0, 0); 325 | if (!linkStsIs) 326 | return false; 327 | 328 | linkStsIs->SetOutBehaviorIO(ml->GetInput(0)); 329 | 330 | CKBehaviorLink *linkIeAs = scriptutils::GetBehaviorLink(defaultLevel, ie, "Activate Script", 0, 0); 331 | if (!linkIeAs) 332 | return false; 333 | 334 | CKBehaviorLink *linkPsWfa = scriptutils::GetBehaviorLink(defaultLevel, ps, "Wait For All", 0, 0); 335 | if (!linkPsWfa) 336 | return false; 337 | 338 | linkPsWfa->SetOutBehaviorIO(linkIeAs->GetOutBehaviorIO()); 339 | return true; 340 | } 341 | 342 | bool EditScript(CKLevel *level, const CGameConfig &config) 343 | { 344 | if (!level) 345 | return false; 346 | 347 | CKBehavior *defaultLevel = scriptutils::GetBehavior(level->ComputeObjectList(CKCID_BEHAVIOR), "Default Level"); 348 | if (!defaultLevel) 349 | { 350 | CLogger::Get().Warn("Unable to find Default Level"); 351 | return false; 352 | } 353 | 354 | // Set debug mode 355 | if (config.debug) 356 | { 357 | if (!SetDebugMode(scriptutils::GetBehavior(defaultLevel, "set DebugMode"))) 358 | CLogger::Get().Warn("Failed to set debug mode"); 359 | } 360 | 361 | // Bypass "Set Language" script and set our language id 362 | if (!SetLanguage(scriptutils::GetBehavior(defaultLevel, "Set Language"), config.langId)) 363 | CLogger::Get().Warn("Failed to set language id"); 364 | 365 | int i; 366 | 367 | CKBehavior *sm = scriptutils::GetBehavior(defaultLevel, "Screen Modes"); 368 | if (!sm) 369 | { 370 | CLogger::Get().Warn("Unable to find script Screen Modes"); 371 | return false; 372 | } 373 | 374 | if (!ReplaceListDriver(sm)) 375 | { 376 | CLogger::Get().Warn("Failed to set driver"); 377 | return false; 378 | } 379 | 380 | if (!ReplaceListScreenModes(sm)) 381 | { 382 | CLogger::Get().Warn("Failed to set screen mode"); 383 | return false; 384 | } 385 | 386 | CKBehavior *bbpFilter = NULL; 387 | CKBehavior *minWidth = NULL; 388 | CKBehavior *maxWidth = NULL; 389 | for (i = 0; i < sm->GetSubBehaviorCount(); ++i) 390 | { 391 | CKBehavior *beh = sm->GetSubBehavior(i); 392 | if (strcmp(beh->GetName(), "Remove Row If") == 0) 393 | { 394 | switch (scriptutils::GetInputParameterValue(beh, 2)) 395 | { 396 | case 16: // BBP filter 397 | bbpFilter = beh; 398 | break; 399 | case 640: // Minimum width 400 | minWidth = beh; 401 | break; 402 | case 1600: // Maximum width 403 | maxWidth = beh; 404 | break; 405 | default: 406 | break; 407 | } 408 | } 409 | } 410 | 411 | // Correct the bbp filter 412 | if (!bbpFilter) 413 | { 414 | CLogger::Get().Warn("Failed to correct the bbp filter"); 415 | } 416 | else 417 | { 418 | scriptutils::SetInputParameterValue(bbpFilter, 2, config.bpp); 419 | } 420 | 421 | // Unlock widescreen (Not 4:3) 422 | if (config.unlockWidescreen) 423 | { 424 | if (!UnlockWidescreen(sm, minWidth)) 425 | CLogger::Get().Warn("Failed to unlock widescreen"); 426 | } 427 | 428 | // Unlock high resolution 429 | if (config.unlockHighResolution) 430 | { 431 | if (!UnlockHighResolution(sm, bbpFilter, minWidth, maxWidth, config.unlockWidescreen)) 432 | CLogger::Get().Warn("Failed to unlock high resolution"); 433 | } 434 | 435 | CKBehavior *sts = scriptutils::GetBehavior(defaultLevel, "Synch to Screen"); 436 | if (!sts) 437 | { 438 | CLogger::Get().Warn("Unable to find script Synch to Screen"); 439 | return false; 440 | } 441 | 442 | // Unlock frame rate limitation 443 | if (config.unlockFramerate) 444 | { 445 | if (!UnlockFramerate(sts)) 446 | CLogger::Get().Warn("Failed to unlock frame rate limitation"); 447 | } 448 | 449 | // Make it not to test 640x480 resolution 450 | if (!SkipResolutionCheck(sts)) 451 | CLogger::Get().Warn("Failed to bypass 640x480 resolution test"); 452 | 453 | // Skip Opening Animation 454 | if (config.skipOpening) 455 | { 456 | if (!SkipOpeningAnimation(defaultLevel, sts)) 457 | CLogger::Get().Warn("Failed to skip opening animation"); 458 | } 459 | 460 | return true; 461 | } 462 | -------------------------------------------------------------------------------- /src/Player.cpp: -------------------------------------------------------------------------------- 1 | #ifndef WIN32_LEAN_AND_MEAN 2 | #define WIN32_LEAN_AND_MEAN 3 | #endif 4 | #include 5 | #include 6 | 7 | #include "CmdlineParser.h" 8 | #include "GameConfig.h" 9 | #include "GamePlayer.h" 10 | #include "Splash.h" 11 | #include "LockGuard.h" 12 | #include "Logger.h" 13 | #include "Utils.h" 14 | 15 | static HANDLE CreateNamedMutex(); 16 | static void ParseConfigsFromCmdline(CGameConfig &config, CmdlineParser &parser); 17 | static void LoadPaths(CGameConfig &config, CmdlineParser &parser); 18 | static void EnableDpiAwareness(); 19 | 20 | int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) 21 | { 22 | HANDLE hMutex = CreateNamedMutex(); 23 | if (!hMutex) 24 | { 25 | ::MessageBox(NULL, TEXT("Another player is running!"), TEXT("Error"), MB_OK); 26 | return -1; 27 | } 28 | 29 | LockGuard guard(hMutex); 30 | 31 | CmdlineParser parser(__argc, __argv); 32 | CGameConfig config; 33 | 34 | // Load paths from command line 35 | LoadPaths(config, parser); 36 | 37 | // Flush ini file if it doesn't exist 38 | if (!utils::FileOrDirectoryExists(config.GetPath(eConfigPath))) 39 | config.SaveToIni(); 40 | 41 | // Load configurations 42 | config.LoadFromIni(); 43 | ParseConfigsFromCmdline(config, parser); 44 | 45 | bool overwrite = true; 46 | if (config.logMode == eLogAppend) 47 | overwrite = false; 48 | 49 | CLogger::Get().Open(config.GetPath(eLogPath), overwrite); 50 | if (config.verbose) 51 | CLogger::Get().SetLevel(CLogger::LEVEL_DEBUG); 52 | 53 | EnableDpiAwareness(); 54 | 55 | CSplash splash(hInstance); 56 | splash.Show(); 57 | 58 | CGamePlayer player; 59 | if (!player.Init(config, hInstance)) 60 | { 61 | CLogger::Get().Error("Failed to initialize player!"); 62 | ::MessageBox(NULL, TEXT("Failed to initialize player!"), TEXT("Error"), MB_OK); 63 | return -1; 64 | } 65 | 66 | if (!player.Load()) 67 | { 68 | CLogger::Get().Error("Failed to load game composition!"); 69 | ::MessageBox(NULL, TEXT("Failed to load game composition!"), TEXT("Error"), MB_OK); 70 | player.Shutdown(); 71 | return -1; 72 | } 73 | 74 | player.Play(); 75 | player.Run(); 76 | player.Shutdown(); 77 | 78 | return 0; 79 | } 80 | 81 | static HANDLE CreateNamedMutex() 82 | { 83 | char buf[MAX_PATH]; 84 | char drive[4]; 85 | char dir[MAX_PATH]; 86 | char filename[MAX_PATH]; 87 | 88 | if (!::GetModuleFileNameA(NULL, buf, MAX_PATH)) 89 | { 90 | CLogger::Get().Error("Failed to get module filename, error code: %d", GetLastError()); 91 | return NULL; 92 | } 93 | 94 | _splitpath(buf, drive, dir, filename, NULL); 95 | _snprintf(buf, MAX_PATH, "%s%s", drive, dir); 96 | 97 | size_t crc = 0; 98 | utils::CRC32(buf, strlen(buf), 0, &crc); 99 | _snprintf(buf, MAX_PATH, "Ballance-%X", crc); 100 | 101 | HANDLE hMutex = ::CreateMutexA(NULL, FALSE, buf); 102 | DWORD error = ::GetLastError(); 103 | 104 | if (!hMutex) 105 | { 106 | CLogger::Get().Error("Failed to create mutex, error code: %d", error); 107 | return NULL; 108 | } 109 | 110 | if (error == ERROR_ALREADY_EXISTS) 111 | { 112 | ::CloseHandle(hMutex); 113 | return NULL; 114 | } 115 | 116 | return hMutex; 117 | } 118 | 119 | static void ParseConfigsFromCmdline(CGameConfig &config, CmdlineParser &parser) 120 | { 121 | CmdlineArg arg; 122 | long value = 0; 123 | std::string str; 124 | 125 | while (!parser.Done()) 126 | { 127 | if (parser.Next(arg, "--verbose", '\0')) 128 | { 129 | config.verbose = true; 130 | continue; 131 | } 132 | if (parser.Next(arg, "--manual-setup", 'm')) 133 | { 134 | config.manualSetup = true; 135 | continue; 136 | } 137 | if (parser.Next(arg, "--video-driver", 'v', 1)) 138 | { 139 | if (arg.GetValue(0, value)) 140 | config.driver = value; 141 | continue; 142 | } 143 | if (parser.Next(arg, "--bpp", 'b', 1)) 144 | { 145 | if (arg.GetValue(0, value)) 146 | config.bpp = value; 147 | continue; 148 | } 149 | if (parser.Next(arg, "--width", 'w', 1)) 150 | { 151 | if (arg.GetValue(0, value)) 152 | config.width = value; 153 | continue; 154 | } 155 | if (parser.Next(arg, "--height", 'h', 1)) 156 | { 157 | if (arg.GetValue(0, value)) 158 | config.height = value; 159 | continue; 160 | } 161 | if (parser.Next(arg, "--fullscreen", 'f')) 162 | { 163 | config.fullscreen = true; 164 | continue; 165 | } 166 | if (parser.Next(arg, "--disable--perspective-correction", '\0')) 167 | { 168 | config.disablePerspectiveCorrection = true; 169 | continue; 170 | } 171 | if (parser.Next(arg, "--force-linear-fog", '\0')) 172 | { 173 | config.forceLinearFog = true; 174 | continue; 175 | } 176 | if (parser.Next(arg, "--force-software", '\0')) 177 | { 178 | config.forceSoftware = true; 179 | continue; 180 | } 181 | if (parser.Next(arg, "--disable-filter", '\0')) 182 | { 183 | config.disableFilter = true; 184 | continue; 185 | } 186 | if (parser.Next(arg, "--ensure-vertex-shader", '\0')) 187 | { 188 | config.ensureVertexShader = true; 189 | continue; 190 | } 191 | if (parser.Next(arg, "--use-index-buffers", '\0')) 192 | { 193 | config.useIndexBuffers = true; 194 | continue; 195 | } 196 | if (parser.Next(arg, "--disable-dithering", '\0')) 197 | { 198 | config.disableDithering = true; 199 | continue; 200 | } 201 | if (parser.Next(arg, "--antialias", '\0', 1)) 202 | { 203 | if (arg.GetValue(0, value)) 204 | config.antialias = value; 205 | continue; 206 | } 207 | if (parser.Next(arg, "--disable-mipmap", '\0')) 208 | { 209 | config.disableMipmap = true; 210 | continue; 211 | } 212 | if (parser.Next(arg, "--disable-specular", '\0')) 213 | { 214 | config.disableSpecular = true; 215 | continue; 216 | } 217 | if (parser.Next(arg, "--enable-screen-dump", '\0')) 218 | { 219 | config.enableScreenDump = true; 220 | continue; 221 | } 222 | if (parser.Next(arg, "--enable-debug-mode", '\0')) 223 | { 224 | config.enableDebugMode = true; 225 | continue; 226 | } 227 | if (parser.Next(arg, "--vertex-cache", '\0', 1)) 228 | { 229 | if (arg.GetValue(0, value)) 230 | config.vertexCache = value; 231 | continue; 232 | } 233 | if (parser.Next(arg, "--disable-texture-cache-management", 's')) 234 | { 235 | config.textureCacheManagement = false; 236 | continue; 237 | } 238 | if (parser.Next(arg, "--disable-sort-transparent-objects", 's')) 239 | { 240 | config.sortTransparentObjects = false; 241 | continue; 242 | } 243 | if (parser.Next(arg, "--texture-video-format", '\0', 1)) 244 | { 245 | if (arg.GetValue(0, str)) 246 | config.textureVideoFormat = utils::String2PixelFormat(str.c_str(), 16); 247 | continue; 248 | } 249 | if (parser.Next(arg, "--sprite-video-format", '\0', 1)) 250 | { 251 | if (arg.GetValue(0, str)) 252 | config.spriteVideoFormat = utils::String2PixelFormat(str.c_str(), 16); 253 | continue; 254 | } 255 | if (parser.Next(arg, "--child-window-rendering", 's')) 256 | { 257 | config.childWindowRendering = true; 258 | continue; 259 | } 260 | if (parser.Next(arg, "--borderless", 'c')) 261 | { 262 | config.borderless = true; 263 | continue; 264 | } 265 | if (parser.Next(arg, "--clip-cursor", '\0')) 266 | { 267 | config.clipCursor = true; 268 | continue; 269 | } 270 | if (parser.Next(arg, "--always-handle-input", '\0')) 271 | { 272 | config.alwaysHandleInput = true; 273 | continue; 274 | } 275 | if (parser.Next(arg, "--position-x", 'x', 1)) 276 | { 277 | if (arg.GetValue(0, value)) 278 | config.posX = value; 279 | continue; 280 | } 281 | if (parser.Next(arg, "--position-y", 'y', 1)) 282 | { 283 | if (arg.GetValue(0, value)) 284 | config.posY = value; 285 | continue; 286 | } 287 | if (parser.Next(arg, "--lang", 'l')) 288 | { 289 | if (arg.GetValue(0, value)) 290 | config.langId = value; 291 | continue; 292 | } 293 | if (parser.Next(arg, "--skip-opening", '\0')) 294 | { 295 | config.skipOpening = true; 296 | continue; 297 | } 298 | if (parser.Next(arg, "--disable-hotfix", '\0')) 299 | { 300 | config.applyHotfix = false; 301 | continue; 302 | } 303 | if (parser.Next(arg, "--unlock-framerate", 'u')) 304 | { 305 | config.unlockFramerate = true; 306 | continue; 307 | } 308 | if (parser.Next(arg, "--unlock-widescreen", '\0')) 309 | { 310 | config.unlockWidescreen = true; 311 | continue; 312 | } 313 | if (parser.Next(arg, "--unlock-high-resolution", '\0')) 314 | { 315 | config.unlockHighResolution = true; 316 | continue; 317 | } 318 | if (parser.Next(arg, "--debug", 'd')) 319 | { 320 | config.debug = true; 321 | continue; 322 | } 323 | if (parser.Next(arg, "--rookie", 'r')) 324 | { 325 | config.rookie = true; 326 | continue; 327 | } 328 | parser.Skip(); 329 | } 330 | parser.Reset(); 331 | } 332 | 333 | static void LoadPaths(CGameConfig &config, CmdlineParser &parser) 334 | { 335 | // Load paths 336 | CmdlineArg arg; 337 | std::string path; 338 | while (!parser.Done()) 339 | { 340 | if (parser.Next(arg, "--config", '\0', 1)) 341 | { 342 | if (arg.GetValue(0, path)) 343 | config.SetPath(eConfigPath, path.c_str()); 344 | break; 345 | } 346 | if (parser.Next(arg, "--log", '\0', 1)) 347 | { 348 | if (arg.GetValue(0, path)) 349 | config.SetPath(eLogPath, path.c_str()); 350 | continue; 351 | } 352 | if (parser.Next(arg, "--cmo", '\0', 1)) 353 | { 354 | if (arg.GetValue(0, path)) 355 | config.SetPath(eCmoPath, path.c_str()); 356 | continue; 357 | } 358 | if (parser.Next(arg, "--root-path", '\0', 1)) 359 | { 360 | if (arg.GetValue(0, path)) 361 | config.SetPath(eRootPath, path.c_str()); 362 | continue; 363 | } 364 | if (parser.Next(arg, "--plugin-path", '\0', 1)) 365 | { 366 | if (arg.GetValue(0, path)) 367 | config.SetPath(ePluginPath, path.c_str()); 368 | continue; 369 | } 370 | if (parser.Next(arg, "--render-engine-path", '\0', 1)) 371 | { 372 | if (arg.GetValue(0, path)) 373 | config.SetPath(eRenderEnginePath, path.c_str()); 374 | continue; 375 | } 376 | if (parser.Next(arg, "--manager-path", '\0', 1)) 377 | { 378 | if (arg.GetValue(0, path)) 379 | config.SetPath(eManagerPath, path.c_str()); 380 | continue; 381 | } 382 | if (parser.Next(arg, "--building-block-path", '\0', 1)) 383 | { 384 | if (arg.GetValue(0, path)) 385 | config.SetPath(eBuildingBlockPath, path.c_str()); 386 | continue; 387 | } 388 | if (parser.Next(arg, "--sound-path", '\0', 1)) 389 | { 390 | if (arg.GetValue(0, path)) 391 | config.SetPath(eSoundPath, path.c_str()); 392 | continue; 393 | } 394 | if (parser.Next(arg, "--bitmap-path", '\0', 1)) 395 | { 396 | if (arg.GetValue(0, path)) 397 | config.SetPath(eBitmapPath, path.c_str()); 398 | continue; 399 | } 400 | if (parser.Next(arg, "--data-path", '\0', 1)) 401 | { 402 | if (arg.GetValue(0, path)) 403 | config.SetPath(eDataPath, path.c_str()); 404 | continue; 405 | } 406 | parser.Skip(); 407 | } 408 | parser.Reset(); 409 | 410 | // Set default value for the path if it was not specified in command line 411 | for (int p = eConfigPath; p < ePathCategoryCount; ++p) 412 | { 413 | if (!utils::DirectoryExists(config.GetPath((PathCategory)p))) 414 | { 415 | CLogger::Get().Warn("%s does not exist, using default path", config.GetPath((PathCategory)p)); 416 | config.ResetPath((PathCategory)p); 417 | } 418 | } 419 | } 420 | 421 | static void EnableDpiAwareness() 422 | { 423 | // DPI awareness enums and types for VC6.0 compatibility 424 | #ifndef PROCESS_DPI_UNAWARE 425 | #define PROCESS_DPI_UNAWARE 0 426 | #define PROCESS_SYSTEM_DPI_AWARE 1 427 | #define PROCESS_PER_MONITOR_DPI_AWARE 2 428 | #endif 429 | 430 | #ifndef DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE 431 | typedef HANDLE DPI_AWARENESS_CONTEXT; 432 | #define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE ((DPI_AWARENESS_CONTEXT) - 3) 433 | #define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 ((DPI_AWARENESS_CONTEXT) - 4) 434 | #endif 435 | 436 | // Function pointer types 437 | typedef HRESULT(WINAPI *PFN_SetProcessDpiAwareness)(int); 438 | typedef DPI_AWARENESS_CONTEXT(WINAPI *PFN_SetThreadDpiAwarenessContext)(DPI_AWARENESS_CONTEXT); 439 | typedef BOOL(WINAPI *PFN_SetProcessDPIAware)(); 440 | 441 | // Since VC6.0 lacks many modern Windows types, we'll use a simpler approach 442 | // to check the Windows version using GetVersionEx() 443 | BOOL isWin10OrGreater = FALSE; 444 | BOOL isWin81OrGreater = FALSE; 445 | 446 | OSVERSIONINFO osvi; 447 | ::ZeroMemory(&osvi, sizeof(OSVERSIONINFO)); 448 | osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); 449 | 450 | if (::GetVersionEx(&osvi)) 451 | { 452 | // Windows 10 is 6.2+ (reported as 6.2 due to compatibility) 453 | // Windows 8.1 is 6.3 454 | if (osvi.dwMajorVersion > 6 || 455 | (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion >= 3)) 456 | { 457 | isWin81OrGreater = TRUE; 458 | } 459 | 460 | // For Windows 10, we can't reliably detect it this way, 461 | // but we'll try loading the Windows 10 specific API 462 | isWin10OrGreater = (osvi.dwMajorVersion > 6); 463 | } 464 | 465 | // Try Windows 10+ API first 466 | if (isWin10OrGreater) 467 | { 468 | HINSTANCE user32_dll = ::LoadLibraryA("user32.dll"); 469 | if (user32_dll) 470 | { 471 | PFN_SetThreadDpiAwarenessContext SetThreadDpiAwarenessContextFn = 472 | (PFN_SetThreadDpiAwarenessContext)::GetProcAddress(user32_dll, "SetThreadDpiAwarenessContext"); 473 | if (SetThreadDpiAwarenessContextFn) 474 | { 475 | SetThreadDpiAwarenessContextFn(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); 476 | ::FreeLibrary(user32_dll); 477 | return; 478 | } 479 | ::FreeLibrary(user32_dll); 480 | } 481 | } 482 | 483 | // Try Windows 8.1+ API 484 | if (isWin81OrGreater) 485 | { 486 | HINSTANCE shcore_dll = ::LoadLibraryA("shcore.dll"); 487 | if (shcore_dll) 488 | { 489 | PFN_SetProcessDpiAwareness SetProcessDpiAwarenessFn = 490 | (PFN_SetProcessDpiAwareness)::GetProcAddress(shcore_dll, "SetProcessDpiAwareness"); 491 | if (SetProcessDpiAwarenessFn) 492 | { 493 | SetProcessDpiAwarenessFn(PROCESS_PER_MONITOR_DPI_AWARE); 494 | ::FreeLibrary(shcore_dll); 495 | return; 496 | } 497 | ::FreeLibrary(shcore_dll); 498 | } 499 | } 500 | 501 | // Fall back to Windows Vista+ API 502 | HINSTANCE user32_dll = ::LoadLibraryA("user32.dll"); 503 | if (user32_dll) 504 | { 505 | PFN_SetProcessDPIAware SetProcessDPIAwareFn = 506 | (PFN_SetProcessDPIAware)::GetProcAddress(user32_dll, "SetProcessDPIAware"); 507 | if (SetProcessDPIAwareFn) 508 | { 509 | SetProcessDPIAwareFn(); 510 | } 511 | ::FreeLibrary(user32_dll); 512 | } 513 | } 514 | -------------------------------------------------------------------------------- /src/Utils.cpp: -------------------------------------------------------------------------------- 1 | #include "Utils.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #ifndef WIN32_LEAN_AND_MEAN 8 | #define WIN32_LEAN_AND_MEAN 9 | #endif 10 | #include 11 | 12 | #ifndef INVALID_FILE_ATTRIBUTES 13 | #define INVALID_FILE_ATTRIBUTES ((DWORD)-1) 14 | #endif 15 | 16 | namespace utils 17 | { 18 | bool FileOrDirectoryExists(const char *file) 19 | { 20 | if (!file || file[0] == '\0') 21 | return false; 22 | const DWORD attributes = ::GetFileAttributesA(file); 23 | return attributes != INVALID_FILE_ATTRIBUTES; 24 | } 25 | 26 | bool DirectoryExists(const char *dir) 27 | { 28 | if (!dir || dir[0] == '\0') 29 | return false; 30 | const DWORD attributes = ::GetFileAttributesA(dir); 31 | return attributes != INVALID_FILE_ATTRIBUTES && (attributes & FILE_ATTRIBUTE_DIRECTORY); 32 | } 33 | 34 | size_t GetCurrentPath(char *buffer, size_t size) 35 | { 36 | return ::GetCurrentDirectoryA(size, buffer); 37 | } 38 | 39 | bool IsAbsolutePath(const char *path) 40 | { 41 | if (!path || path[0] == '\0') 42 | return false; 43 | 44 | if (strlen(path) < 2 || !isalpha(path[0]) || path[1] != ':') 45 | return false; 46 | 47 | return true; 48 | } 49 | 50 | bool GetAbsolutePath(char *buffer, size_t size, const char *path, bool trailing) 51 | { 52 | if (!path || path[0] == '\0') 53 | return false; 54 | 55 | if (!buffer) 56 | return false; 57 | 58 | size_t len; 59 | if (IsAbsolutePath(path)) 60 | { 61 | len = strlen(path); 62 | strncpy(buffer, path, size); 63 | } 64 | else 65 | { 66 | size_t n = GetCurrentPath(buffer, size); 67 | n = size - 1 - n; 68 | strncat(buffer, "\\", n); 69 | --n; 70 | strncat(buffer, path, n); 71 | len = strlen(path); 72 | } 73 | 74 | if (trailing && !HasTrailingPathSeparator(path)) 75 | { 76 | if (size > len + 2) 77 | { 78 | buffer[len] = '\\'; 79 | buffer[len + 1] = '\0'; 80 | } 81 | } 82 | else if (!trailing && HasTrailingPathSeparator(path)) 83 | { 84 | buffer[len - 1] = '\0'; 85 | } 86 | return true; 87 | } 88 | 89 | char *ConcatPath(char *buffer, size_t size, const char *path1, const char *path2) 90 | { 91 | if (!buffer) 92 | return NULL; 93 | 94 | if (!path1 || path1[0] == '\0') 95 | { 96 | strncpy(buffer, path2, size); 97 | } 98 | else if (path2) 99 | { 100 | strncpy(buffer, path1, size); 101 | RemoveTrailingPathSeparator(buffer); 102 | size_t len2 = strlen(path2); 103 | size_t n = size - 1 - len2; 104 | strncat(buffer, "\\", n); 105 | --n; 106 | strncat(buffer, path2, n); 107 | } 108 | 109 | return buffer; 110 | } 111 | 112 | const char *FindLastPathSeparator(const char *path) 113 | { 114 | if (!path || path[0] == '\0') 115 | return NULL; 116 | 117 | const char *const lastSep = strrchr(path, '\\'); 118 | const char *const lastAltSep = strrchr(path, '/'); 119 | return (lastAltSep && (!lastSep || lastAltSep > lastSep)) ? lastAltSep : lastSep; 120 | } 121 | 122 | bool HasTrailingPathSeparator(const char *path) 123 | { 124 | if (!path || path[0] == '\0') 125 | return false; 126 | 127 | size_t len = strlen(path); 128 | return (path[len - 1] == '\\' || path[len - 1] == '/'); 129 | } 130 | 131 | bool RemoveTrailingPathSeparator(char *path) 132 | { 133 | if (!HasTrailingPathSeparator(path)) 134 | return false; 135 | path[strlen(path) - 1] = '\0'; 136 | return true; 137 | } 138 | 139 | int CharToWchar(const char *charStr, wchar_t *wcharStr, int size) 140 | { 141 | return ::MultiByteToWideChar(CP_ACP, 0, charStr, -1, wcharStr, size); 142 | } 143 | 144 | int WcharToChar(const wchar_t *wcharStr, char *charStr, int size) 145 | { 146 | return ::WideCharToMultiByte(CP_ACP, 0, wcharStr, -1, charStr, size, NULL, NULL); 147 | } 148 | 149 | /* crc32.c -- compute the CRC-32 of a data stream 150 | * Copyright (C) 1995-1998 Mark Adler 151 | * For conditions of distribution and use, see copyright notice in zlib.h 152 | */ 153 | 154 | // Table of CRC-32's of all single-byte values (made by make_crc_table) 155 | static const size_t crc_table[256] = { 156 | 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, 157 | 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, 158 | 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, 159 | 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, 160 | 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, 161 | 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, 162 | 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, 163 | 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, 164 | 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, 165 | 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, 166 | 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, 167 | 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, 168 | 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, 169 | 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, 170 | 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, 171 | 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, 172 | 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, 173 | 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, 174 | 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, 175 | 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, 176 | 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, 177 | 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, 178 | 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, 179 | 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, 180 | 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, 181 | 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, 182 | 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, 183 | 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, 184 | 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, 185 | 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, 186 | 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, 187 | 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, 188 | 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, 189 | 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, 190 | 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, 191 | 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, 192 | 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, 193 | 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, 194 | 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, 195 | 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, 196 | 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, 197 | 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, 198 | 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, 199 | 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, 200 | 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, 201 | 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, 202 | 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, 203 | 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, 204 | 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, 205 | 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, 206 | 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, 207 | 0x2d02ef8dL}; 208 | 209 | #define DO1(buf) crc = crc_table[((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8) 210 | #define DO2(buf) DO1(buf); DO1(buf) 211 | #define DO4(buf) DO2(buf); DO2(buf) 212 | #define DO8(buf) DO4(buf); DO4(buf) 213 | 214 | void CRC32(const void *key, size_t len, size_t seed, void *out) 215 | { 216 | unsigned char *buf = (unsigned char *)key; 217 | size_t crc = seed ^ 0xffffffffL; 218 | 219 | while (len >= 8) 220 | { 221 | DO8(buf); 222 | len -= 8; 223 | } 224 | 225 | while (len--) 226 | { 227 | DO1(buf); 228 | } 229 | 230 | crc ^= 0xffffffffL; 231 | 232 | *(size_t *)out = crc; 233 | } 234 | 235 | VX_PIXELFORMAT String2PixelFormat(const char *str, size_t max) 236 | { 237 | if (!str || str[0] == '\0' || max == 0) 238 | return UNKNOWN_PF; 239 | 240 | VX_PIXELFORMAT format = UNKNOWN_PF; 241 | if (strncmp(str, "_32_ARGB8888", max) == 0) 242 | format = _32_ARGB8888; 243 | else if (strncmp(str, "_32_RGB888", max) == 0) 244 | format = _32_RGB888; 245 | else if (strncmp(str, "_24_RGB888", max) == 0) 246 | format = _24_RGB888; 247 | else if (strncmp(str, "_16_RGB565", max) == 0) 248 | format = _16_RGB565; 249 | else if (strncmp(str, "_16_RGB555", max) == 0) 250 | format = _16_RGB555; 251 | else if (strncmp(str, "_16_ARGB1555", max) == 0) 252 | format = _16_ARGB1555; 253 | else if (strncmp(str, "_16_ARGB4444", max) == 0) 254 | format = _16_ARGB4444; 255 | else if (strncmp(str, "_8_RGB332", max) == 0) 256 | format = _8_RGB332; 257 | else if (strncmp(str, "_8_ARGB2222", max) == 0) 258 | format = _8_ARGB2222; 259 | else if (strncmp(str, "_32_ABGR8888", max) == 0) 260 | format = _32_ABGR8888; 261 | else if (strncmp(str, "_32_RGBA8888", max) == 0) 262 | format = _32_RGBA8888; 263 | else if (strncmp(str, "_32_BGRA8888", max) == 0) 264 | format = _32_BGRA8888; 265 | else if (strncmp(str, "_32_BGR888", max) == 0) 266 | format = _32_BGR888; 267 | else if (strncmp(str, "_24_BGR888", max) == 0) 268 | format = _24_BGR888; 269 | else if (strncmp(str, "_16_BGR565", max) == 0) 270 | format = _16_BGR565; 271 | else if (strncmp(str, "_16_BGR555", max) == 0) 272 | format = _16_BGR555; 273 | else if (strncmp(str, "_16_ABGR1555", max) == 0) 274 | format = _16_ABGR1555; 275 | else if (strncmp(str, "_16_ABGR4444", max) == 0) 276 | format = _16_ABGR4444; 277 | else if (strncmp(str, "_DXT1", max) == 0) 278 | format = _DXT1; 279 | else if (strncmp(str, "_DXT2", max) == 0) 280 | format = _DXT2; 281 | else if (strncmp(str, "_DXT3", max) == 0) 282 | format = _DXT3; 283 | else if (strncmp(str, "_DXT4", max) == 0) 284 | format = _DXT4; 285 | else if (strncmp(str, "_DXT5", max) == 0) 286 | format = _DXT5; 287 | else if (strncmp(str, "_16_V8U8", max) == 0) 288 | format = _16_V8U8; 289 | else if (strncmp(str, "_32_V16U16", max) == 0) 290 | format = _32_V16U16; 291 | else if (strncmp(str, "_16_L6V5U5", max) == 0) 292 | format = _16_L6V5U5; 293 | else if (strncmp(str, "_32_X8L8V8U8", max) == 0) 294 | format = _32_X8L8V8U8; 295 | 296 | return format; 297 | } 298 | 299 | const char *PixelFormat2String(VX_PIXELFORMAT format) 300 | { 301 | const char *str; 302 | switch (format) { 303 | case _32_ARGB8888: 304 | str = "_32_ARGB8888"; 305 | break; 306 | case _32_RGB888: 307 | str = "_32_RGB888"; 308 | break; 309 | case _24_RGB888: 310 | str = "_24_RGB888"; 311 | break; 312 | case _16_RGB565: 313 | str = "_16_RGB565"; 314 | break; 315 | case _16_RGB555: 316 | str = "_16_RGB555"; 317 | break; 318 | case _16_ARGB1555: 319 | str = "_16_ARGB1555"; 320 | break; 321 | case _16_ARGB4444: 322 | str = "_16_ARGB4444"; 323 | break; 324 | case _8_RGB332: 325 | str = "_8_RGB332"; 326 | break; 327 | case _8_ARGB2222: 328 | str = "_8_ARGB2222"; 329 | break; 330 | case _32_ABGR8888: 331 | str = "_32_ABGR8888"; 332 | break; 333 | case _32_RGBA8888: 334 | str = "_32_RGBA8888"; 335 | break; 336 | case _32_BGRA8888: 337 | str = "_32_BGRA8888"; 338 | break; 339 | case _32_BGR888: 340 | str = "_32_BGR888"; 341 | break; 342 | case _24_BGR888: 343 | str = "_24_BGR888"; 344 | break; 345 | case _16_BGR565: 346 | str = "_16_BGR565"; 347 | break; 348 | case _16_BGR555: 349 | str = "_16_BGR555"; 350 | break; 351 | case _16_ABGR1555: 352 | str = "_16_ABGR1555"; 353 | break; 354 | case _16_ABGR4444: 355 | str = "_16_ABGR4444"; 356 | break; 357 | case _DXT1: 358 | str = "_DXT1"; 359 | break; 360 | case _DXT2: 361 | str = "_DXT2"; 362 | break; 363 | case _DXT3: 364 | str = "_DXT3"; 365 | break; 366 | case _DXT4: 367 | str = "_DXT4"; 368 | break; 369 | case _DXT5: 370 | str = "_DXT5"; 371 | break; 372 | case _16_V8U8: 373 | str = "_16_V8U8"; 374 | break; 375 | case _32_V16U16: 376 | str = "_32_V16U16"; 377 | break; 378 | case _16_L6V5U5: 379 | str = "_16_L6V5U5"; 380 | break; 381 | case _32_X8L8V8U8: 382 | str = "_32_X8L8V8U8"; 383 | break; 384 | default: 385 | str = "UNKNOWN_PF"; 386 | break; 387 | } 388 | 389 | return str; 390 | } 391 | 392 | bool IniGetString(const char *section, const char *name, char *str, int size, const char *filename) 393 | { 394 | return ::GetPrivateProfileStringA(section, name, "", str, size, filename) != 0; 395 | } 396 | 397 | bool IniGetInteger(const char *section, const char *name, int &value, const char *filename) 398 | { 399 | char buf[512]; 400 | ::GetPrivateProfileStringA(section, name, "", buf, 512, filename); 401 | if (strcmp(buf, "") == 0) 402 | return false; 403 | int val = strtol(buf, NULL, 10); 404 | if (val == 0 && strcmp(buf, "0") != 0) 405 | return false; 406 | value = val; 407 | return true; 408 | } 409 | 410 | bool IniGetBoolean(const char *section, const char *name, bool &value, const char *filename) 411 | { 412 | UINT val = ::GetPrivateProfileIntA(section, name, -1, filename); 413 | if (val == -1) 414 | return false; 415 | value = val != 0; 416 | return true; 417 | } 418 | 419 | bool IniGetPixelFormat(const char *section, const char *name, VX_PIXELFORMAT &value, const char *filename) 420 | { 421 | char buf[16]; 422 | ::GetPrivateProfileStringA(section, name, "", buf, 16, filename); 423 | if (strcmp(buf, "") == 0) 424 | return false; 425 | 426 | value = String2PixelFormat(buf, sizeof(buf)); 427 | return true; 428 | } 429 | 430 | bool IniSetString(const char *section, const char *name, const char *str, const char *filename) 431 | { 432 | return ::WritePrivateProfileStringA(section, name, str, filename) != 0; 433 | } 434 | 435 | bool IniSetInteger(const char *section, const char *name, int value, const char *filename) 436 | { 437 | char buf[64]; 438 | sprintf(buf, "%d", value); 439 | return ::WritePrivateProfileStringA(section, name, buf, filename) != 0; 440 | } 441 | 442 | bool IniSetBoolean(const char *section, const char *name, bool value, const char *filename) 443 | { 444 | const char *buf = (value) ? "1" : "0"; 445 | return ::WritePrivateProfileStringA(section, name, buf, filename) != 0; 446 | } 447 | 448 | bool IniSetPixelFormat(const char *section, const char *name, VX_PIXELFORMAT value, const char *filename) 449 | { 450 | return ::WritePrivateProfileStringA(section, name, utils::PixelFormat2String(value), filename) != 0; 451 | } 452 | } 453 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BallancePlayer 2 | 3 | ## Overview 4 | 5 | BallancePlayer is a modern, enhanced player for the game Ballance, developed by decompiling and improving the original version. It offers various new features and optimizations to deliver an improved gaming experience while maintaining compatibility with the original game. 6 | 7 | ## Features 8 | 9 | - Portable and ready to use, no configuration needed to start the game 10 | - Support for `.ini` file configurations 11 | - Command-line options for flexibility 12 | - 32-bit color mode support 13 | - Compatibility with monitors that don't support 640x480 resolution 14 | - High refresh rate monitor support 15 | - In-game resolution switching support 16 | - Hotkeys for toggling fullscreen mode and closing the game 17 | - No registry modifications 18 | - No dependency on `Dsetup.dll` 19 | - Integrated `ResDll.dll` for seamless operation 20 | - Fixes for bugs present in the original player 21 | - Various performance improvements 22 | 23 | ## System Requirements 24 | 25 | Supports the following Windows versions: 26 | - Windows XP 27 | - Windows Vista 28 | - Windows 7 29 | - Windows 8 / 8.1 30 | - Windows 10 31 | - Windows 11 32 | 33 | ## Installation 34 | 35 | 1. Extract the provided package into the `Bin` directory of your game folder. 36 | 2. Launch the game by running `Player.exe`. No need to set compatibility mode. 37 | 38 | ## Building from Source 39 | 40 | ### Prerequisites 41 | 42 | To build BallancePlayer, you’ll need the Virtools SDK, which can be obtained from [Virtools-SDK-2.1](https://github.com/doyaGu/Virtools-SDK-2.1). Set the `VIRTOOLS_SDK_PATH` environment variable to the path where the SDK is installed before starting the build process. 43 | 44 | ### Building with CMake 45 | 46 | 1. **Install CMake**: Ensure CMake is installed on your system. 47 | 2. **Navigate to Project Directory**: Open a console and navigate to the directory containing the BallancePlayer source code. 48 | 3. **Generate Build Files**: Run the following command to generate Visual Studio project files for 32-bit architecture: 49 | ``` 50 | cmake -B build -G "Visual Studio 16 2022" -A Win32 51 | ``` 52 | 4. **Open in Visual Studio**: Navigate to the `build` directory and open the solution file `BallancePlayer.sln` in Visual Studio. 53 | 5. **Build the Solution**: Use Visual Studio to compile the project. 54 | 55 | ### Building with Visual Studio 6.0 56 | 57 | 1. **Install Visual Studio 6.0**: Ensure Visual Studio 6.0 is installed. 58 | 2. **Open the Project**: Locate `Player.dsw` in the project directory and open it with Visual Studio 6.0. 59 | 3. **Build the Project**: Use the build tools in Visual Studio 6.0 to compile the project. 60 | 61 | ### Notes 62 | 63 | The official release package is built with Visual Studio 6.0 for maximum compatibility with older systems. 64 | 65 | ## Hotkeys 66 | 67 | - **[Alt] + [Enter]**: Switch between windowed and fullscreen mode. 68 | - **[Alt] + [F4]**: Force close the game. 69 | - **[Alt] + [/]**: Display an about box. 70 | 71 | ## INI Settings 72 | 73 | The `Player.ini` file contains several settings that control the behavior of the game. 74 | 75 | ### Startup 76 | 77 | - `LogMode`: Controls how logs are handled. 78 | - `0`: Append to the log file. 79 | - `1`: Overwrite the log file. 80 | - `Verbose`: Toggles verbose logging. 81 | - `0`: Disabled. 82 | - `1`: Enabled. 83 | - `ManualSetup`: Controls whether the setup dialog appears on startup. 84 | - `0`: Disabled. 85 | - `1`: Enabled. 86 | 87 | ### Graphics 88 | 89 | - `Driver`: Specifies the driver ID of the graphics card to use. 90 | - `BitsPerPixel`: Specifies color depth. 91 | - `32`: 32-bit color. 92 | - `16`: 16-bit color. 93 | - `Width`: Specifies screen width (based on your monitor resolution). 94 | - `Height`: Specifies screen height (based on your monitor resolution). 95 | - `FullScreen`: Controls display mode. 96 | - `0`: Windowed mode. 97 | - `1`: Fullscreen mode. 98 | - `DisablePerspectiveCorrection`: Toggles perspective correction. 99 | - `0`: Off. 100 | - `1`: On. 101 | - `ForceLinearFog`: Forces linear fog modes. 102 | - `0`: Off. 103 | - `1`: On. 104 | - `ForceSoftware`: Disables hardware rendering. 105 | - `0`: Off. 106 | - `1`: On. 107 | - `DisableFilter`: Disables texture filtering. 108 | - `0`: Off. 109 | - `1`: On. 110 | - `EnsureVertexShader`: Ensures vertex shader support. 111 | - `0`: Off. 112 | - `1`: On. 113 | - `UseIndexBuffers`: Enables index buffer usage. 114 | - `0`: Off. 115 | - `1`: On. 116 | - `DisableDithering`: Disables dithering. 117 | - `0`: Off. 118 | - `1`: On. 119 | - `Antialias`: Sets the level of anti-aliasing (minimum value: 2). 120 | - `DisableMipmap`: Disables mipmaps. 121 | - `0`: Off. 122 | - `1`: On. 123 | - `DisableSpecular`: Disables specular highlights. 124 | - `0`: Off. 125 | - `1`: On. 126 | - `EnableScreenDump`: Enables screen dumping functionality. 127 | - `0`: Off. 128 | - `1`: On. 129 | - `EnableDebugMode`: Enables debug mode for step-by-step rendering. 130 | - `0`: Off. 131 | - `1`: On. 132 | - `VertexCache`: Sets the size of the vertex cache. A value of `0` disables sorting. 133 | - `TextureCacheManagement`: Enables or disables texture cache management. 134 | - `0`: Off. 135 | - `1`: On. 136 | - `SortTransparentObjects`: Toggles sorting of transparent objects. 137 | - `0`: Off. 138 | - `1`: On. 139 | - `TextureVideoFormat`: Specifies the default pixel format for textures (e.g., `_32_ARGB8888`). 140 | - `SpriteVideoFormat`: Specifies the default pixel format for sprites (e.g., `_32_ARGB8888`). 141 | 142 | ### Window 143 | 144 | - `ChildWindowRendering`: Controls whether rendering occurs in a child window. 145 | - `0`: Disabled. 146 | - `1`: Enabled. 147 | - `Borderless`: Controls window borders. 148 | - `0`: Disabled. 149 | - `1`: Enabled. 150 | - `ClipCursor`: Controls whether the cursor is clipped to the window. 151 | - `0`: Disabled. 152 | - `1`: Enabled. 153 | - `AlwaysHandleInput`: Allows the player to handle input even when the window is in the background. 154 | - `0`: Disabled. 155 | - `1`: Enabled. 156 | - `X`: Specifies the horizontal window coordinate (can be negative). 157 | - `Y`: Specifies the vertical window coordinate (can be negative). 158 | 159 | ### Game 160 | 161 | - `Language`: Sets the game language. 162 | - `0`: German. 163 | - `1`: English. 164 | - `2`: Spanish. 165 | - `3`: Italian. 166 | - `4`: French. 167 | - `SkipOpening`: Controls whether to skip the opening animation. 168 | - `0`: Disabled. 169 | - `1`: Enabled. 170 | - `ApplyHotfix`: Controls whether hotfixes are applied. 171 | - `0`: Disabled. 172 | - `1`: Enabled. 173 | - `UnlockFramerate`: Unlocks the frame rate limitation. 174 | - `0`: Disabled. 175 | - `1`: Enabled. 176 | - `UnlockWidescreen`: Unlocks non-4:3 resolutions. 177 | - `0`: Disabled. 178 | - `1`: Enabled. 179 | - `UnlockHighResolution`: Unlocks resolutions higher than 1600x1200. 180 | - `0`: Disabled. 181 | - `1`: Enabled. 182 | - `Debug`: Enables in-game debug mode. 183 | - `0`: Disabled. 184 | - `1`: Enabled. 185 | - `Rookie`: Enables in-game rookie mode. 186 | - `0`: Disabled. 187 | - `1`: Enabled. 188 | 189 | ## Command-line Options 190 | 191 | You can also use command-line options to customize game behavior: 192 | 193 | ```bash 194 | Player.exe [OPTIONS] 195 | ``` 196 | - `--verbose`: Enable verbose logging. 197 | - `-m`, `--manual-setup`: Always show the setup dialog box at startup. 198 | - `-v `, `--video-driver `: Set the graphics card driver ID. 199 | - `-b `, `--bpp `: Set the bits per pixel (32 or 16). 200 | - `-w `, `--width `: Set the screen width. 201 | - `-h `, `--height `: Set the screen height. 202 | - `-f`, `--fullscreen`: Start the game in fullscreen mode. 203 | - `--disable--perspective-correction`: Disable perspective correction. 204 | - `--force-linear-fog`: Force the fog mode to linear. 205 | - `--force-software`: Disable hardware rendering and force software mode. 206 | - `--disable-filter`: Disable texture filtering. 207 | - `--ensure-vertex-shader`: Ensure vertex shader support. 208 | - `--use-index-buffers`: Enable index buffer usage. 209 | - `--disable-dithering`: Disable image dithering. 210 | - `--antialias `: Enable image antialiasing (minimum: 2). 211 | - `--disable-mipmap`: Disable mipmaps. 212 | - `--disable-specular`: Disable specular highlights. 213 | - `--enable-screen-dump`: Dump screen content using (CTRL + ALT + F10). 214 | - `--enable-debug-mode`: Enable debug mode using (CTRL + ALT + F11). 215 | - `--vertex-cache `: Set the vertex cache size (0 disables sorting). 216 | - `--disable-texture-cache-management`: Disable texture cache management. 217 | - `--disable-sort-transparent-objects`: Disable sorting of transparent objects. 218 | - `--texture-video-format `: Set the texture pixel format (e.g., `_32_ARGB8888`). 219 | - `--sprite-video-format `: Set the sprite pixel format (e.g., `_32_ARGB8888`). 220 | - `-s`, `--child-window-rendering`:Enable child window rendering. 221 | - `-c`, `--borderless`: Start the game in borderless mode. 222 | - `--clip-cursor`: Clip the cursor to the window. 223 | - `--always-handle-input`: Allow input handling when the window is in the background. 224 | - `-x `, `--position-x `: Set the window's X coordinate. 225 | - `-y `, `--position-y `: Set the window's Y coordinate. 226 | - `-l `, `--lang `: Set the game language. 227 | - `--skip-opening`: Skip the opening animation. 228 | - `--disable-hotfix`: Disable script hotfixes. 229 | - `-u`, `--unlock-framerate`: Unlock the frame rate limitation. 230 | - `--unlock-widescreen`: Unlock non-4:3 resolutions. 231 | - `--unlock-high-resolution`: Unlock resolutions higher than 1600x1200. 232 | - `d`, `--debug`: Enable in-game debug mode. 233 | - `r`, `--rookie`: Enable in-game rookie mode. 234 | 235 | ## Contact 236 | 237 | If you have any bugs or requests, please open an issue in this repository: [BallancePlayer](https://github.com/doyaGu/BallancePlayer). 238 | 239 | ## ChangeLog 240 | 241 | ### v0.3.8 (2025-09-25) 242 | 243 | **Bug Fixes** 244 | 245 | - Fixed an issue where the window became invisible when switching from fullscreen to windowed mode. 246 | 247 | ### v0.3.7 (2025-09-24) 248 | 249 | **New Features** 250 | 251 | - Added support for external configuration file change detection and merging. 252 | 253 | **Bug Fixes** 254 | 255 | - Fixed an issue where plugin calls to ClipCursor could be disrupted when the ClipCursor setting was disabled. 256 | 257 | ### v0.3.6 (2025-08-20) 258 | 259 | **Changes** 260 | 261 | - Switched from ANSI to Unicode to improve compatibility and internationalization support. 262 | 263 | ### v0.3.5 (2025-05-23) 264 | 265 | **New Features** 266 | 267 | - Introduced a standalone configuration tool for easier management of game settings. 268 | - Added support for loading custom game compositions. 269 | 270 | **Bug Fixes** 271 | 272 | - Fixed a crash that could occur when enabling verbose logging. 273 | 274 | **Changes** 275 | 276 | - Removed deprecated settings: `LoadAllManagers`, `LoadAllBuildingBlocks`, and `LoadAllPlugins`. 277 | - The game no longer pauses by default when the window is deactivated. 278 | 279 | ### v0.3.4 (2025-04-24) 280 | 281 | **New Features** 282 | 283 | - Added support for DPI awareness handling to improve display scaling across different monitor configurations. 284 | 285 | **Bug Fixes** 286 | 287 | - Fixed an issue with the command line parser that could cause incorrect parameter handling. 288 | - Resolved an irregular game acceleration issue that occurred when frequently switching between windowed and fullscreen modes. 289 | 290 | **Changes** 291 | 292 | - Removed the deprecated `PauseOnDeactivated` setting. 293 | - Enhanced error handling with more descriptive messages and improved recovery from common failure states. 294 | 295 | ### v0.3.3 (2024-10-01) 296 | 297 | **New Features** 298 | 299 | - Added the `LogMode` setting to control log mode. 300 | - Added the `Verbose` setting with the `--verbose` option to control debug log output. 301 | - Introduced the `ApplyHotfix` setting with the `--no-hotfix` option to control whether script hotfixes are applied. 302 | - Added the `ClipCursor` setting with the `--clip-cursor` option to manage cursor clipping behavior. 303 | - Added more graphics settings and options. 304 | - Improved error messaging for better clarity. 305 | 306 | **Changes** 307 | 308 | - Logs now overwrite the existing file instead of appending to it. 309 | - Reorganized configuration categories for better structure. 310 | - Where possible, relative paths are now used. 311 | 312 | ### v0.3.2 (2024-07-16) 313 | 314 | **Bug Fixes** 315 | 316 | - Fixed an issue with broken plugin registration. 317 | 318 | **Changes** 319 | 320 | - The application window now opens in the center of the screen by default. 321 | 322 | ### v0.3.1 (2024-03-04) 323 | 324 | **Bug Fixes** 325 | 326 | - Fixed a potential issue with abnormal resolution settings. 327 | 328 | ### v0.3.0 (2023-05-20) 329 | 330 | **New Features** 331 | 332 | - Added the `ChildWindowRendering` setting with the `--child-window-rendering` option to control rendering in a child window. 333 | 334 | **Bug Fixes** 335 | 336 | - Resolved a possible black screen issue when switching to full-screen mode. 337 | 338 | **Changes** 339 | 340 | - By default, rendering is no longer done in a separate window. 341 | 342 | ### v0.2.4 (2023-03-23) 343 | 344 | **Bug Fixes** 345 | 346 | - Fixed a game initialization failure that occurred when no screen mode was selected in the setup dialog. 347 | - Corrected the render driver initialization process. 348 | 349 | ### v0.2.3 (2023-03-13) 350 | 351 | **New Features** 352 | 353 | - Added support for running multiple instances of the game. 354 | 355 | **Bug Fixes** 356 | 357 | - Fixed a crash that occurred when exiting the game. 358 | 359 | **Changes** 360 | 361 | - Deprecated the `Resizable` and `ClipMouse` settings. 362 | - Enhanced error messages for better clarity. 363 | - Refactored the codebase to simplify the implementation. 364 | 365 | ### v0.2.2 (2023-01-23) 366 | 367 | **New Features** 368 | 369 | - Added the `ManualSetup` setting and corresponding `--manual-setup` command-line option to control whether the setup dialog box is shown at startup. 370 | 371 | **Bug Fixes** 372 | 373 | - Fixed issues with short option parsing in the command line. 374 | 375 | **Changes** 376 | 377 | - Deprecated the `AdaptiveCamera` setting. 378 | - Deprecated delay-loaded DLL. 379 | - Deprecated path customization. 380 | - Simplified the workaround for driver and screen mode enumeration. 381 | 382 | ### v0.2.1 (2022-11-24) 383 | 384 | **New Features** 385 | 386 | - Added support for console logging. 387 | - Added the `LoadAllManagers` setting with the `--load-all-managers` command-line option to control whether all managers are loaded. 388 | - Added the `LoadAllBuildingBlocks` setting with the `--load-all-building-blocks` option to control whether all building blocks are loaded. 389 | - Added the `LoadAllPlugins` setting with the `--load-all-plugins` command-line option to control whether all plugins are loaded. 390 | 391 | **Bug Fixes** 392 | 393 | - Fixed an issue where the screen modes will be changed incorrectly. 394 | - Resolved a startup failure caused by incorrect path settings. 395 | 396 | **Changes** 397 | 398 | - Removed the dependency on the modified version of `TT_InterfaceManager_RT.dll`. 399 | 400 | ### v0.2.0 (2022-10-15) 401 | 402 | **New Features** 403 | 404 | - Added a Chinese version of the README. 405 | 406 | ### v0.2.0-rc1 (2022-10-09) 407 | 408 | **Bug Fixes** 409 | 410 | - Fixed a potential crash issue that occurred upon exiting the game. 411 | 412 | **Changes** 413 | 414 | - Improved the game loop for better performance. 415 | 416 | ### v0.2.0-beta4 (2022-10-02) 417 | 418 | **Bug Fixes** 419 | 420 | - Fixed an issue preventing the player from switching between fullscreen and windowed mode when using OpenGL. 421 | - Resolved problems with task switching that could lead to errors. 422 | 423 | ### v0.2.0-beta3 (2022-10-01) 424 | 425 | **Bug Fixes** 426 | 427 | - Fixed a UI crash that occurred during task switching in fullscreen mode. 428 | - Resolved an issue where specifying a display driver had no effect. 429 | 430 | **Changes** 431 | 432 | - Swapped the positions of `Driver` and `Screen Mode` in the `FullScreen Setup` dialog. 433 | 434 | ### v0.2.0-beta2 (2022-09-28) 435 | 436 | **Bug Fixes** 437 | 438 | - Fixed a pop-up black screen issue after exiting. 439 | 440 | ### v0.2.0-beta1 (2022-09-28) 441 | 442 | **Bug Fixes** 443 | 444 | - Fixed forced fullscreen Vsync. 445 | - Fixed the issue that window position can not be restored correctly. 446 | 447 | ### v0.2.0-alpha4 (2022-09-25) 448 | 449 | **Bug Fixes** 450 | 451 | - Resolved a black screen error on monitors that do not support 640x480 resolution. 452 | 453 | ### v0.2.0-alpha2 (2022-09-23) 454 | 455 | **New Features** 456 | 457 | - Added support for in-game debug mode. 458 | - Introduced new command-line options like `--root-path=`. 459 | 460 | ### v0.2.0-alpha1 (2022-09-17) 461 | 462 | **New Features** 463 | 464 | - Added support for more resolutions, skipping the opening animation, mouse clipping, off-site startup, game path customization, and Virtools console output. 465 | - Introduced many new settings and corresponding command-line options. 466 | 467 | **Bug Fixes** 468 | 469 | - Fixed issues with fullscreen setup. 470 | - Resolved an issue where the player would get stuck when using custom resolutions. 471 | 472 | **Changes** 473 | 474 | - Unified log and error output. 475 | - Rearranged settings and corresponding command-line options. 476 | 477 | ### v0.1.9 (2022-08-30) 478 | 479 | **Bug Fixes** 480 | 481 | - Fixed an issue with the position-saving mechanism when exiting fullscreen mode. 482 | 483 | **Changes** 484 | 485 | - If the window is resizable (with a title), it now appears in the upper-left corner with a slim margin from the screen edge when the window position is set to 0. 486 | - The saved window position value can now be negative. 487 | - The game will now save the last used game mode (fullscreen or windowed) and restore it the next time it starts up. 488 | 489 | ### v0.1.8 (2022-08-17) 490 | 491 | **Bug Fixes** 492 | 493 | - Fixed an issue where the game was locked at 60 FPS on 144Hz monitors when v-sync was enabled. 494 | - Resolved a bug preventing the game from finding multiple display drivers. 495 | 496 | ### v0.1.7 (2022-08-15) 497 | 498 | **New Features** 499 | 500 | - Added `X` and `Y` settings with corresponding `-x` and `-y` options to set the screen coordinates of the upper-left corner of the window. 501 | 502 | **Bug Fixes** 503 | 504 | - Fixed an issue where the game window resized unnaturally before starting. 505 | 506 | **Changes** 507 | 508 | - The last window position is now saved and restored on the next startup. 509 | 510 | ### v0.1.6 (2022-08-05) 511 | 512 | **New Features** 513 | 514 | - Added the `Borderless` setting with the `-c` option to start in borderless mode. 515 | - Added the `Resizable` setting with the `-s` option to make the window resizable. 516 | 517 | **Changes** 518 | 519 | - The window is now non-resizable by default. 520 | 521 | ### v0.1.5 (2022-07-20) 522 | 523 | **New Features** 524 | 525 | - Added support for 32-bit color mode. 526 | - Added support for command-line long options. 527 | - Added the `Language` setting with the `-l` option to set the game language. 528 | - Added the `UnlockFramerate` setting with the `-u` option to unlock the frame-rate limit. 529 | 530 | **Bug Fixes** 531 | 532 | - Fixed a UI disorder bug during task switching. 533 | 534 | **Changes** 535 | 536 | - Changed the command-line option for disabling task switching from `-d` to `-e`. 537 | - Reimplemented the initialization configuration mechanism. 538 | - Rewrote command-line support. 539 | - Integrated the functions of `ResDll`. 540 | 541 | ### v0.1.4 (2022-05-12) 542 | 543 | **New Features** 544 | 545 | - Added the `PauseOnTaskSwitch` setting with the `-p` option to enable game pause during task switching. 546 | 547 | **Bug Fixes** 548 | 549 | - Fixed bugs where the game would exit early and crash upon exit. 550 | 551 | **Changes** 552 | 553 | - Removed unnecessary virtual functions to improve performance. 554 | - Removed the unused Interface Sprite. 555 | - Improved error handling. 556 | 557 | ### v0.1.3 (2022-05-09) 558 | 559 | **New Features** 560 | 561 | - Added support for command-line options. 562 | 563 | **Bug Fixes** 564 | 565 | - Fixed fullscreen crash. 566 | - Fixed task switching issues. 567 | - Fixed resolution duplicates in Graphics Options. 568 | 569 | **Changes** 570 | 571 | - Removed dependency on `Dsetup.dll` since checking DirectX is unnecessary on modern PCs. 572 | - Removed exception handling to improve performance. 573 | 574 | ### v0.1.2 (2022-05-08) 575 | 576 | **Bug Fixes** 577 | 578 | - Fixed an issue where the game might crash upon exit. 579 | 580 | **Changes** 581 | 582 | - Default config files are now generated when no configuration file is found. 583 | - Replaced remaining registry operations in the main function. 584 | - Removed `FixedString`. 585 | 586 | ### v0.1.1 (2022-05-04) 587 | 588 | **New Features** 589 | 590 | - Added support for initialization file configuration. 591 | 592 | **Bug Fixes** 593 | 594 | - Fixed an issue where the game displayed incompletely in windowed mode. 595 | 596 | **Changes** 597 | 598 | - Removed some unknown class members. 599 | 600 | ### v0.1.0 (2022-05-03) 601 | 602 | **Bug Fixes** 603 | 604 | - Fixed a memory checking bug that caused incorrect results. 605 | -------------------------------------------------------------------------------- /src/ScriptUtils.h: -------------------------------------------------------------------------------- 1 | #ifndef PLAYER_SCRIPTUTILS_H 2 | #define PLAYER_SCRIPTUTILS_H 3 | 4 | #include 5 | 6 | #include "CKGlobals.h" 7 | #include "CKBeObject.h" 8 | #include "CKBehavior.h" 9 | #include "CKBehaviorIO.h" 10 | #include "CKBehaviorLink.h" 11 | #include "CKParameter.h" 12 | #include "CKParameterIn.h" 13 | #include "CKParameterOut.h" 14 | #include "CKParameterLocal.h" 15 | #include "CKParameterManager.h" 16 | 17 | namespace scriptutils 18 | { 19 | inline CKBehaviorLink *CreateBehaviorLink(CKBehavior *script, CKBehaviorIO *in, CKBehaviorIO *out, int delay = 0) 20 | { 21 | assert(script != NULL); 22 | assert(in != NULL); 23 | assert(out != NULL); 24 | 25 | CKBehaviorLink *link = (CKBehaviorLink *)script->GetCKContext()->CreateObject(CKCID_BEHAVIORLINK); 26 | link->SetInBehaviorIO(in); 27 | link->SetOutBehaviorIO(out); 28 | link->SetInitialActivationDelay(delay); 29 | link->ResetActivationDelay(); 30 | script->AddSubBehaviorLink(link); 31 | return link; 32 | } 33 | 34 | inline CKBehaviorLink *CreateBehaviorLink(CKBehavior *script, CKBehavior *inBeh, CKBehaviorIO *out, int inPos, int delay = 0) 35 | { 36 | assert(script != NULL); 37 | assert(inBeh != NULL); 38 | assert(out != NULL); 39 | 40 | return CreateBehaviorLink(script, inBeh->GetOutput(inPos), out, delay); 41 | } 42 | 43 | inline CKBehaviorLink *CreateBehaviorLink(CKBehavior *script, CKBehaviorIO *in, CKBehavior *outBeh, int outPos, int delay = 0) 44 | { 45 | assert(script != NULL); 46 | assert(in != NULL); 47 | assert(outBeh != NULL); 48 | 49 | return CreateBehaviorLink(script, in, outBeh->GetInput(outPos), delay); 50 | } 51 | 52 | inline CKBehaviorLink *CreateBehaviorLink(CKBehavior *script, CKBehavior *inBeh, CKBehavior *outBeh, int inPos = 0, int outPos = 0, int delay = 0) 53 | { 54 | return CreateBehaviorLink(script, inBeh->GetOutput(inPos), outBeh->GetInput(outPos), delay); 55 | } 56 | 57 | inline CKBehaviorLink *GetBehaviorLink(CKBehavior *script, CKBehaviorIO *in, CKBehaviorIO *out, CKBehaviorLink *previous = NULL) 58 | { 59 | assert(script != NULL); 60 | assert(in != NULL); 61 | assert(out != NULL); 62 | 63 | const int linkCount = script->GetSubBehaviorLinkCount(); 64 | int i = 0; 65 | 66 | if (previous) 67 | { 68 | for (; i < linkCount; ++i) 69 | { 70 | CKBehaviorLink *link = script->GetSubBehaviorLink(i); 71 | if (link == previous) 72 | { 73 | ++i; 74 | break; 75 | } 76 | } 77 | } 78 | 79 | for (; i < linkCount; ++i) 80 | { 81 | CKBehaviorLink *link = script->GetSubBehaviorLink(i); 82 | CKBehaviorIO *inIO = link->GetInBehaviorIO(); 83 | CKBehaviorIO *outIO = link->GetOutBehaviorIO(); 84 | if (inIO == in && outIO == out) 85 | return link; 86 | } 87 | 88 | return NULL; 89 | } 90 | 91 | inline CKBehaviorLink *GetBehaviorLink(CKBehavior *script, CKBehavior *inBeh, CKBehavior *outBeh, int inPos = 0, int outPos = 0, 92 | CKBehaviorLink *previous = NULL) 93 | { 94 | assert(script != NULL); 95 | assert(inBeh != NULL); 96 | assert(outBeh != NULL); 97 | 98 | const int linkCount = script->GetSubBehaviorLinkCount(); 99 | int i = 0; 100 | 101 | if (previous) 102 | { 103 | for (; i < linkCount; ++i) 104 | { 105 | CKBehaviorLink *link = script->GetSubBehaviorLink(i); 106 | if (link == previous) 107 | { 108 | ++i; 109 | break; 110 | } 111 | } 112 | } 113 | 114 | for (; i < linkCount; ++i) 115 | { 116 | CKBehaviorLink *link = script->GetSubBehaviorLink(i); 117 | CKBehaviorIO *inIO = link->GetInBehaviorIO(); 118 | CKBehaviorIO *outIO = link->GetOutBehaviorIO(); 119 | if (inIO->GetOwner() == inBeh && inBeh->GetOutput(inPos) == inIO && 120 | outIO->GetOwner() == outBeh && outBeh->GetInput(outPos) == outIO) 121 | return link; 122 | } 123 | 124 | return NULL; 125 | } 126 | 127 | inline CKBehaviorLink *GetBehaviorLink(CKBehavior *script, const char *inBehName, CKBehavior *outBeh, int inPos = 0, int outPos = 0, 128 | CKBehaviorLink *previous = NULL) 129 | { 130 | assert(script != NULL); 131 | assert(inBehName != NULL); 132 | assert(outBeh != NULL); 133 | 134 | const int linkCount = script->GetSubBehaviorLinkCount(); 135 | int i = 0; 136 | 137 | if (previous) 138 | { 139 | for (; i < linkCount; ++i) 140 | { 141 | CKBehaviorLink *link = script->GetSubBehaviorLink(i); 142 | if (link == previous) 143 | { 144 | ++i; 145 | break; 146 | } 147 | } 148 | } 149 | 150 | for (; i < linkCount; ++i) 151 | { 152 | CKBehaviorLink *link = script->GetSubBehaviorLink(i); 153 | CKBehaviorIO *inIO = link->GetInBehaviorIO(); 154 | CKBehaviorIO *outIO = link->GetOutBehaviorIO(); 155 | CKBehavior *inBeh = inIO->GetOwner(); 156 | if (strcmp(inBeh->GetName(), inBehName) == 0 && inBeh->GetOutput(inPos) == inIO && 157 | outIO->GetOwner() == outBeh && outBeh->GetInput(outPos) == outIO) 158 | return link; 159 | } 160 | 161 | return NULL; 162 | } 163 | 164 | inline CKBehaviorLink *GetBehaviorLink(CKBehavior *script, CKBehavior *inBeh, const char *outBehName, int inPos = 0, int outPos = 0, 165 | CKBehaviorLink *previous = NULL) 166 | { 167 | assert(script != NULL); 168 | assert(inBeh != NULL); 169 | assert(outBehName != NULL); 170 | 171 | const int linkCount = script->GetSubBehaviorLinkCount(); 172 | int i = 0; 173 | 174 | if (previous) 175 | { 176 | for (; i < linkCount; ++i) 177 | { 178 | CKBehaviorLink *link = script->GetSubBehaviorLink(i); 179 | if (link == previous) 180 | { 181 | ++i; 182 | break; 183 | } 184 | } 185 | } 186 | 187 | for (; i < linkCount; ++i) 188 | { 189 | CKBehaviorLink *link = script->GetSubBehaviorLink(i); 190 | CKBehaviorIO *inIO = link->GetInBehaviorIO(); 191 | CKBehaviorIO *outIO = link->GetOutBehaviorIO(); 192 | CKBehavior *outBeh = outIO->GetOwner(); 193 | if (inIO->GetOwner() == inBeh && inBeh->GetOutput(inPos) == inIO && 194 | strcmp(outBeh->GetName(), outBehName) == 0 && outBeh->GetInput(outPos) == outIO) 195 | return link; 196 | } 197 | 198 | return NULL; 199 | } 200 | 201 | inline CKBehaviorLink *GetBehaviorLink(CKBehavior *script, const char *inBehName, const char *outBehName, int inPos = 0, int outPos = 0, 202 | CKBehaviorLink *previous = NULL) 203 | { 204 | assert(script != NULL); 205 | assert(inBehName != NULL); 206 | assert(outBehName != NULL); 207 | 208 | const int linkCount = script->GetSubBehaviorLinkCount(); 209 | int i = 0; 210 | 211 | if (previous) 212 | { 213 | for (; i < linkCount; ++i) 214 | { 215 | CKBehaviorLink *link = script->GetSubBehaviorLink(i); 216 | if (link == previous) 217 | { 218 | ++i; 219 | break; 220 | } 221 | } 222 | } 223 | 224 | for (; i < linkCount; ++i) 225 | { 226 | CKBehaviorLink *link = script->GetSubBehaviorLink(i); 227 | CKBehaviorIO *inIO = link->GetInBehaviorIO(); 228 | CKBehaviorIO *outIO = link->GetOutBehaviorIO(); 229 | CKBehavior *inBeh = inIO->GetOwner(); 230 | CKBehavior *outBeh = outIO->GetOwner(); 231 | if (strcmp(inBeh->GetName(), inBehName) == 0 && inBeh->GetOutput(inPos) == inIO && 232 | strcmp(outBeh->GetName(), outBehName) == 0 && outBeh->GetInput(outPos) == outIO) 233 | return link; 234 | } 235 | 236 | return NULL; 237 | } 238 | 239 | inline void SetBehaviorLink(CKBehavior *script, CKBehaviorLink *link, CKBehavior *inBeh, CKBehavior *outBeh, int inPos = 0, int outPos = 0) 240 | { 241 | assert(link != NULL); 242 | assert(inBeh != NULL); 243 | assert(outBeh != NULL); 244 | 245 | link->SetInBehaviorIO(inBeh->GetOutput(inPos)); 246 | link->SetOutBehaviorIO(outBeh->GetInput(outPos)); 247 | } 248 | 249 | inline CKBehaviorLink *RemoveBehaviorLink(CKBehavior *script, CKBehaviorIO *in, CKBehaviorIO *out, bool destroy = false) 250 | { 251 | assert(script != NULL); 252 | assert(in != NULL); 253 | assert(out != NULL); 254 | 255 | CKBehaviorLink *link = GetBehaviorLink(script, in, out); 256 | if (!link) 257 | return NULL; 258 | 259 | script->RemoveSubBehaviorLink(link); 260 | if (destroy) 261 | { 262 | CKDestroyObject(link); 263 | return NULL; 264 | } 265 | return link; 266 | } 267 | 268 | inline CKBehaviorLink *RemoveBehaviorLink(CKBehavior *script, CKBehavior *inBeh, CKBehavior *outBeh, int inPos = 0, int outPos = 0, 269 | bool destroy = false) 270 | { 271 | assert(script != NULL); 272 | assert(inBeh != NULL); 273 | assert(outBeh != NULL); 274 | 275 | CKBehaviorLink *link = GetBehaviorLink(script, inBeh, outBeh, inPos, outPos); 276 | if (!link) 277 | return NULL; 278 | 279 | script->RemoveSubBehaviorLink(link); 280 | if (destroy) 281 | { 282 | CKDestroyObject(link); 283 | return NULL; 284 | } 285 | return link; 286 | } 287 | 288 | inline CKBehaviorLink *RemoveBehaviorLink(CKBehavior *script, const char *inBehName, CKBehavior *outBeh, int inPos = 0, int outPos = 0, 289 | bool destroy = false) 290 | { 291 | assert(script != NULL); 292 | assert(inBehName != NULL); 293 | assert(outBeh != NULL); 294 | 295 | CKBehaviorLink *link = GetBehaviorLink(script, inBehName, outBeh, inPos, outPos); 296 | if (!link) 297 | return NULL; 298 | 299 | script->RemoveSubBehaviorLink(link); 300 | if (destroy) 301 | { 302 | CKDestroyObject(link); 303 | return NULL; 304 | } 305 | return link; 306 | } 307 | 308 | inline CKBehaviorLink *RemoveBehaviorLink(CKBehavior *script, CKBehavior *inBeh, const char *outBehName, int inPos = 0, int outPos = 0, 309 | bool destroy = false) 310 | { 311 | assert(script != NULL); 312 | assert(inBeh != NULL); 313 | assert(outBehName != NULL); 314 | 315 | CKBehaviorLink *link = GetBehaviorLink(script, inBeh, outBehName, inPos, outPos); 316 | if (!link) 317 | return NULL; 318 | 319 | script->RemoveSubBehaviorLink(link); 320 | if (destroy) 321 | { 322 | CKDestroyObject(link); 323 | return NULL; 324 | } 325 | return link; 326 | } 327 | 328 | inline CKBehaviorLink *RemoveBehaviorLink(CKBehavior *script, const char *inBehName, const char *outBehName, int inPos = 0, int outPos = 0, 329 | bool destroy = false) 330 | { 331 | assert(script != NULL); 332 | assert(inBehName != NULL); 333 | assert(outBehName != NULL); 334 | 335 | CKBehaviorLink *link = GetBehaviorLink(script, inBehName, outBehName, inPos, outPos); 336 | if (!link) 337 | return NULL; 338 | 339 | script->RemoveSubBehaviorLink(link); 340 | if (destroy) 341 | { 342 | CKDestroyObject(link); 343 | return NULL; 344 | } 345 | return link; 346 | } 347 | 348 | inline CKBehavior *CreateBehavior(CKBehavior *script, CKGUID guid, bool useTarget = false) 349 | { 350 | assert(script != NULL); 351 | assert(guid.d1 != 0 && guid.d2 != 0); 352 | 353 | CKBehavior *behavior = (CKBehavior *)script->GetCKContext()->CreateObject(CKCID_BEHAVIOR); 354 | behavior->InitFromGuid(guid); 355 | if (useTarget) 356 | behavior->UseTarget(); 357 | script->AddSubBehavior(behavior); 358 | return behavior; 359 | } 360 | 361 | inline CKBehavior *RemoveBehavior(CKBehavior *script, CKBehavior *beh, bool destroy = false) 362 | { 363 | assert(script); 364 | assert(beh); 365 | 366 | const int count = script->GetSubBehaviorLinkCount(); 367 | for (int i = 0; i < count; i++) 368 | { 369 | CKBehaviorLink *link = script->GetSubBehaviorLink(i); 370 | if (link->GetInBehaviorIO()->GetOwner() == beh || link->GetOutBehaviorIO()->GetOwner() == beh) 371 | { 372 | script->RemoveSubBehaviorLink(link); 373 | if (destroy) 374 | CKDestroyObject(link); 375 | } 376 | } 377 | 378 | beh->Activate(false); 379 | script->RemoveSubBehavior(beh); 380 | if (destroy) 381 | { 382 | CKDestroyObject(beh); 383 | return NULL; 384 | } 385 | return beh; 386 | } 387 | 388 | inline CKBehavior *MoveBehavior(CKBehavior *script, CKBehavior *beh, CKBehavior *dest) 389 | { 390 | assert(script != NULL); 391 | assert(beh != NULL); 392 | assert(dest != NULL); 393 | 394 | RemoveBehavior(script, beh, false); 395 | dest->AddSubBehavior(beh); 396 | return beh; 397 | } 398 | 399 | inline CKBehavior *CopyBehavior(CKBehavior *script, CKBehavior *beh, CKBehavior *dest) 400 | { 401 | assert(script != NULL); 402 | assert(beh != NULL); 403 | assert(dest != NULL); 404 | 405 | CKBehavior *newBeh = (CKBehavior *)script->GetCKContext()->CopyObject(beh); 406 | MoveBehavior(script, newBeh, dest); 407 | dest->AddSubBehavior(newBeh); 408 | return newBeh; 409 | } 410 | 411 | inline CKBehavior *GetBehavior(const XObjectPointerArray &array, const char *name, CKBehavior *previous = NULL) 412 | { 413 | assert(name != NULL); 414 | 415 | CKBehavior *behavior = NULL; 416 | CKObject **it = array.Begin(); 417 | 418 | if (previous) 419 | { 420 | for (; it != array.End(); ++it) 421 | { 422 | if (*it == previous) 423 | { 424 | ++it; 425 | break; 426 | } 427 | } 428 | } 429 | 430 | for (; it != array.End(); ++it) 431 | { 432 | CKObject *obj = *it; 433 | if (obj->GetClassID() == CKCID_BEHAVIOR && strcmp(obj->GetName(), name) == 0) 434 | { 435 | behavior = (CKBehavior *)obj; 436 | break; 437 | } 438 | } 439 | 440 | return behavior; 441 | } 442 | 443 | inline CKBehavior *GetBehavior(CKBehavior *script, const char *name, CKBehavior *previous = NULL) 444 | { 445 | assert(script != NULL); 446 | assert(name != NULL); 447 | 448 | CKBehavior *behavior = NULL; 449 | const int count = script->GetSubBehaviorCount(); 450 | int i = 0; 451 | 452 | if (previous) 453 | { 454 | for (; i < count; ++i) 455 | { 456 | CKBehavior *b = script->GetSubBehavior(i); 457 | if (b == previous) 458 | { 459 | ++i; 460 | break; 461 | } 462 | } 463 | } 464 | 465 | for (; i < count; ++i) 466 | { 467 | CKBehavior *b = script->GetSubBehavior(i); 468 | if (strcmp(b->GetName(), name) == 0) 469 | { 470 | behavior = b; 471 | break; 472 | } 473 | } 474 | 475 | return behavior; 476 | } 477 | 478 | inline CKBehavior *GetBehavior(CKBehavior *script, const char *name, const char *targetName, CKBehavior *previous = NULL) 479 | { 480 | assert(script != NULL); 481 | assert(name != NULL); 482 | assert(targetName != NULL); 483 | 484 | CKBehavior *behavior = NULL; 485 | const int count = script->GetSubBehaviorCount(); 486 | int i = 0; 487 | 488 | if (previous) 489 | { 490 | for (; i < count; ++i) 491 | { 492 | CKBehavior *b = script->GetSubBehavior(i); 493 | if (b == previous) 494 | { 495 | ++i; 496 | break; 497 | } 498 | } 499 | } 500 | 501 | for (; i < count; ++i) 502 | { 503 | CKBehavior *b = script->GetSubBehavior(i); 504 | if (strcmp(b->GetName(), name) == 0) 505 | { 506 | CKObject *target = b->GetTarget(); 507 | if (target && strcmp(target->GetName(), targetName) == 0) 508 | { 509 | behavior = b; 510 | break; 511 | } 512 | } 513 | } 514 | 515 | return behavior; 516 | } 517 | 518 | inline XObjectPointerArray &GetBehaviors(CKBehavior *script, XObjectPointerArray &behaviors, const char *name, bool hierarchy = false) 519 | { 520 | const int count = script->GetSubBehaviorCount(); 521 | for (int i = 0; i < count; ++i) 522 | { 523 | CKBehavior *b = script->GetSubBehavior(i); 524 | if (strcmp(b->GetName(), name) == 0) 525 | behaviors.PushBack(b); 526 | if (hierarchy && b->GetSubBehaviorCount() != 0) 527 | GetBehaviors(b, behaviors, name, hierarchy); 528 | } 529 | 530 | return behaviors; 531 | } 532 | 533 | inline CKParameterLocal *CreateLocalParameter(CKBehavior *script, const char *name = "", CKGUID type = CKPGUID_STRING) 534 | { 535 | assert(script != NULL); 536 | return script->CreateLocalParameter((CKSTRING)name, type); 537 | } 538 | 539 | template 540 | CKParameterLocal *CreateLocalParameter(CKBehavior *script, const char *name, T value, CKGUID type) 541 | { 542 | CKParameterLocal *param = CreateLocalParameter(script, name, type); 543 | param->SetValue(&value, sizeof(T)); 544 | return param; 545 | } 546 | 547 | template <> 548 | CKParameterLocal *CreateLocalParameter(CKBehavior *script, const char *name, CKObject *value, CKGUID type) 549 | { 550 | return CreateLocalParameter(script, name, CKOBJID(value), type); 551 | } 552 | 553 | template <> 554 | CKParameterLocal *CreateLocalParameter(CKBehavior *script, const char *name, const char *value, CKGUID type) 555 | { 556 | CKParameterLocal *param = CreateLocalParameter(script, name, type); 557 | param->SetStringValue((CKSTRING)value); 558 | return param; 559 | } 560 | 561 | inline CKParameterLocal *CreateLocalParameter(CKBehavior *script, const char *name = "", const char *value = "") 562 | { 563 | return CreateLocalParameter(script, name, value, CKPGUID_STRING); 564 | } 565 | 566 | inline CKParameterLocal *GenerateInputParameter(CKBehavior *script, CKBehavior *beh, int inPos) 567 | { 568 | assert(script != NULL); 569 | assert(beh != NULL); 570 | 571 | if (inPos < 0 || inPos > beh->GetInputParameterCount()) 572 | return NULL; 573 | 574 | CKParameterIn *inParam = beh->GetInputParameter(inPos); 575 | CKParameterManager *pm = beh->GetCKContext()->GetParameterManager(); 576 | CKParameterLocal *param = CreateLocalParameter(script, inParam->GetName(), pm->ParameterTypeToGuid(inParam->GetType())); 577 | inParam->SetDirectSource(param); 578 | return param; 579 | } 580 | 581 | template 582 | CKParameterLocal *GenerateInputParameter(CKBehavior *script, CKBehavior *beh, int inPos, T value) 583 | { 584 | CKParameterLocal *param = GenerateInputParameter(script, beh, inPos); 585 | param->SetValue(&value, sizeof(T)); 586 | return param; 587 | } 588 | 589 | template <> 590 | CKParameterLocal *GenerateInputParameter(CKBehavior *script, CKBehavior *beh, int inPos, CKObject *value) 591 | { 592 | return GenerateInputParameter(script, beh, inPos, CKOBJID(value)); 593 | } 594 | 595 | template <> 596 | CKParameterLocal *GenerateInputParameter(CKBehavior *script, CKBehavior *beh, int inPos, const char *value) 597 | { 598 | CKParameterLocal *param = GenerateInputParameter(script, beh, inPos); 599 | param->SetValue((CKSTRING)value); 600 | return param; 601 | } 602 | 603 | template <> 604 | CKParameterLocal *GenerateInputParameter(CKBehavior *script, CKBehavior *beh, int inPos, CKSTRING value) 605 | { 606 | CKParameterLocal *param = GenerateInputParameter(script, beh, inPos); 607 | param->SetValue(value); 608 | return param; 609 | } 610 | 611 | inline CKParameterLocal *GenerateTargetParameter(CKBehavior *script, CKBehavior *beh) 612 | { 613 | assert(script != NULL); 614 | assert(beh != NULL); 615 | 616 | CKParameterIn *targetParam = beh->GetTargetParameter(); 617 | CKParameterManager *pm = beh->GetCKContext()->GetParameterManager(); 618 | CKParameterLocal *param = CreateLocalParameter(script, targetParam->GetName(), pm->ParameterTypeToGuid(targetParam->GetType())); 619 | targetParam->SetDirectSource(param); 620 | return param; 621 | } 622 | 623 | template 624 | CKParameterLocal *GenerateTargetParameter(CKBehavior *script, CKBehavior *beh, T value) 625 | { 626 | CKParameterLocal *param = GenerateTargetParameter(script, beh); 627 | param->SetValue(&value, sizeof(T)); 628 | return param; 629 | } 630 | 631 | template <> 632 | CKParameterLocal *GenerateTargetParameter(CKBehavior *script, CKBehavior *beh, CKObject *value) 633 | { 634 | return GenerateTargetParameter(script, beh, CKOBJID(value)); 635 | } 636 | 637 | template <> 638 | CKParameterLocal *GenerateTargetParameter(CKBehavior *script, CKBehavior *beh, const char *value) 639 | { 640 | CKParameterLocal *param = GenerateTargetParameter(script, beh); 641 | param->SetValue((CKSTRING)value); 642 | return param; 643 | } 644 | 645 | template <> 646 | CKParameterLocal *GenerateTargetParameter(CKBehavior *script, CKBehavior *beh, CKSTRING value) 647 | { 648 | CKParameterLocal *param = GenerateTargetParameter(script, beh); 649 | param->SetValue(value); 650 | return param; 651 | } 652 | 653 | template 654 | void SetParameterValue(CKParameter *param, T value) 655 | { 656 | param->SetValue(&value, sizeof(T)); 657 | } 658 | 659 | inline void SetParameterObject(CKParameter *param, CKObject *value) 660 | { 661 | CK_ID obj = CKOBJID(value); 662 | SetParameterValue(param, obj); 663 | } 664 | 665 | inline void SetParameterString(CKParameter *param, const char *value) 666 | { 667 | param->SetStringValue((CKSTRING)value); 668 | } 669 | 670 | template 671 | void SetTargetParameterValue(CKBehavior *beh, T value) 672 | { 673 | assert(beh != NULL); 674 | CKParameter *param = beh->GetTargetParameter()->GetDirectSource(); 675 | param->SetValue(&value, sizeof(T)); 676 | } 677 | 678 | inline void SetTargetParameterObject(CKBehavior *beh, CKObject *value) 679 | { 680 | SetTargetParameterValue(beh, CKOBJID(value)); 681 | } 682 | 683 | inline void SetTargetParameterString(CKBehavior *beh, const char *value) 684 | { 685 | assert(beh != NULL); 686 | CKParameter *param = beh->GetTargetParameter()->GetDirectSource(); 687 | param->SetValue((CKSTRING)value); 688 | } 689 | 690 | template 691 | void SetInputParameterValue(CKBehavior *beh, int inPos, T value) 692 | { 693 | assert(beh != NULL); 694 | if (0 <= inPos && inPos < beh->GetInputParameterCount()) 695 | { 696 | CKParameter *param = beh->GetInputParameter(inPos)->GetDirectSource(); 697 | param->SetValue(&value, sizeof(T)); 698 | } 699 | } 700 | 701 | inline void SetInputParameterObject(CKBehavior *beh, int inPos, CKObject *value) 702 | { 703 | SetInputParameterValue(beh, inPos, CKOBJID(value)); 704 | } 705 | 706 | inline void SetInputParameterString(CKBehavior *beh, int inPos, const char *value) 707 | { 708 | assert(beh != NULL); 709 | if (0 <= inPos && inPos < beh->GetInputParameterCount()) 710 | { 711 | CKParameter *param = beh->GetInputParameter(inPos)->GetDirectSource(); 712 | param->SetValue((CKSTRING)value); 713 | } 714 | } 715 | 716 | template 717 | void SetLocalParameterValue(CKBehavior *beh, int pos, T value) 718 | { 719 | assert(beh != NULL); 720 | if (0 <= pos && pos < beh->GetLocalParameterCount()) 721 | { 722 | CKParameterLocal *param = beh->GetLocalParameter(pos); 723 | param->SetValue(&value, sizeof(T)); 724 | } 725 | } 726 | 727 | inline void SetLocalParameterObject(CKBehavior *beh, int pos, CKObject *value) 728 | { 729 | SetLocalParameterValue(beh, pos, CKOBJID(value)); 730 | } 731 | 732 | inline void SetLocalParameterString(CKBehavior *beh, int pos, const char *value) 733 | { 734 | assert(beh != NULL); 735 | if (0 <= pos && pos < beh->GetLocalParameterCount()) 736 | { 737 | CKParameterLocal *param = beh->GetLocalParameter(pos); 738 | param->SetValue((CKSTRING)value); 739 | } 740 | } 741 | 742 | template 743 | T GetParameterValue(CKParameter *param) 744 | { 745 | T res; 746 | param->GetValue(&res); 747 | return res; 748 | } 749 | 750 | inline CKObject *GetParameterObject(CKParameter *param) 751 | { 752 | return param->GetValueObject(); 753 | } 754 | 755 | inline const char *GetParameterString(CKParameter *param) 756 | { 757 | return (const char *)param->GetReadDataPtr(); 758 | } 759 | 760 | template 761 | T GetInputParameterValue(CKBehavior *beh, int inPos) 762 | { 763 | assert(beh != NULL); 764 | if (0 <= inPos && inPos < beh->GetInputParameterCount()) 765 | { 766 | T res; 767 | beh->GetInputParameter(inPos)->GetValue(&res); 768 | return res; 769 | } 770 | return T(); 771 | } 772 | 773 | inline CKObject *GetInputParameterObject(CKBehavior *beh, int inPos) 774 | { 775 | assert(beh != NULL); 776 | if (0 <= inPos && inPos < beh->GetInputParameterCount()) 777 | { 778 | CKParameter *param = beh->GetInputParameter(inPos)->GetDirectSource(); 779 | return param->GetValueObject(); 780 | } 781 | return NULL; 782 | } 783 | 784 | inline const char *GetInputParameterString(CKBehavior *beh, int inPos) 785 | { 786 | assert(beh != NULL); 787 | if (0 <= inPos && inPos < beh->GetInputParameterCount()) 788 | { 789 | return (const char *)beh->GetInputParameter(inPos)->GetReadDataPtr(); 790 | } 791 | return NULL; 792 | } 793 | 794 | template 795 | T GetOutputParameterValue(CKBehavior *beh, int outPos) 796 | { 797 | assert(beh != NULL); 798 | if (0 <= outPos && outPos < beh->GetOutputParameterCount()) 799 | { 800 | T res; 801 | beh->GetOutputParameter(outPos)->GetValue(&res); 802 | return res; 803 | } 804 | return T(); 805 | } 806 | 807 | inline CKObject *GetOutputParameterObject(CKBehavior *beh, int outPos) 808 | { 809 | assert(beh != NULL); 810 | if (0 <= outPos && outPos < beh->GetOutputParameterCount()) 811 | { 812 | return beh->GetOutputParameter(outPos)->GetValueObject(); 813 | } 814 | return NULL; 815 | } 816 | 817 | inline const char *GetOutputParameterString(CKBehavior *beh, int outPos) 818 | { 819 | assert(beh != NULL); 820 | if (0 <= outPos && outPos < beh->GetOutputParameterCount()) 821 | { 822 | return (const char *)beh->GetOutputParameter(outPos)->GetReadDataPtr(); 823 | } 824 | return NULL; 825 | } 826 | 827 | template 828 | T GetLocalParameterValue(CKBehavior *beh, int pos) 829 | { 830 | assert(beh != NULL); 831 | if (0 <= pos && pos < beh->GetLocalParameterCount()) 832 | { 833 | T res; 834 | beh->GetLocalParameter(pos)->GetValue(&res); 835 | return res; 836 | } 837 | return T(); 838 | } 839 | 840 | inline CKObject *GetLocalParameterObject(CKBehavior *beh, int pos) 841 | { 842 | assert(beh != NULL); 843 | if (0 <= pos && pos < beh->GetLocalParameterCount()) 844 | { 845 | CKParameterLocal *param = beh->GetLocalParameter(pos); 846 | return param->GetValueObject(); 847 | } 848 | return NULL; 849 | } 850 | 851 | inline const char *GetLocalParameterString(CKBehavior *beh, int pos) 852 | { 853 | assert(beh != NULL); 854 | if (0 <= pos && pos < beh->GetLocalParameterCount()) 855 | { 856 | return (const char *)beh->GetLocalParameter(pos)->GetReadDataPtr(); 857 | } 858 | return NULL; 859 | } 860 | } 861 | 862 | #endif // PLAYER_SCRIPTUTILS_H --------------------------------------------------------------------------------