mirror of
https://github.com/Mr-X-GTA/YimMenu.git
synced 2024-12-22 20:17:24 +08:00
Initial commit
This commit is contained in:
commit
d65c694d22
399
.gitignore
vendored
Normal file
399
.gitignore
vendored
Normal file
@ -0,0 +1,399 @@
|
|||||||
|
### C++ ###
|
||||||
|
# Prerequisites
|
||||||
|
*.d
|
||||||
|
|
||||||
|
# Compiled Object files
|
||||||
|
*.slo
|
||||||
|
*.lo
|
||||||
|
*.o
|
||||||
|
*.obj
|
||||||
|
|
||||||
|
# Precompiled Headers
|
||||||
|
*.gch
|
||||||
|
*.pch
|
||||||
|
|
||||||
|
# Compiled Dynamic libraries
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
*.dll
|
||||||
|
|
||||||
|
# Fortran module files
|
||||||
|
*.mod
|
||||||
|
*.smod
|
||||||
|
|
||||||
|
# Compiled Static libraries
|
||||||
|
*.lai
|
||||||
|
*.la
|
||||||
|
*.a
|
||||||
|
*.lib
|
||||||
|
|
||||||
|
# Executables
|
||||||
|
*.exe
|
||||||
|
*.out
|
||||||
|
*.app
|
||||||
|
|
||||||
|
### Windows ###
|
||||||
|
# Windows thumbnail cache files
|
||||||
|
Thumbs.db
|
||||||
|
ehthumbs.db
|
||||||
|
ehthumbs_vista.db
|
||||||
|
|
||||||
|
# Dump file
|
||||||
|
*.stackdump
|
||||||
|
|
||||||
|
# Folder config file
|
||||||
|
[Dd]esktop.ini
|
||||||
|
|
||||||
|
# Recycle Bin used on file shares
|
||||||
|
$RECYCLE.BIN/
|
||||||
|
|
||||||
|
# Windows Installer files
|
||||||
|
*.cab
|
||||||
|
*.msi
|
||||||
|
*.msix
|
||||||
|
*.msm
|
||||||
|
*.msp
|
||||||
|
|
||||||
|
# Windows shortcuts
|
||||||
|
*.lnk
|
||||||
|
|
||||||
|
### VisualStudio ###
|
||||||
|
## Ignore Visual Studio temporary files, build results, and
|
||||||
|
## files generated by popular Visual Studio add-ons.
|
||||||
|
##
|
||||||
|
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
||||||
|
|
||||||
|
# User-specific files
|
||||||
|
*.rsuser
|
||||||
|
*.suo
|
||||||
|
*.user
|
||||||
|
*.userosscache
|
||||||
|
*.sln.docstates
|
||||||
|
|
||||||
|
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||||
|
*.userprefs
|
||||||
|
|
||||||
|
# Build results
|
||||||
|
[Dd]ebug/
|
||||||
|
[Dd]ebugPublic/
|
||||||
|
[Rr]elease/
|
||||||
|
[Rr]eleases/
|
||||||
|
x64/
|
||||||
|
x86/
|
||||||
|
[Aa][Rr][Mm]/
|
||||||
|
[Aa][Rr][Mm]64/
|
||||||
|
bld/
|
||||||
|
[Bb]in/
|
||||||
|
[Oo]bj/
|
||||||
|
[Ll]og/
|
||||||
|
bin/
|
||||||
|
|
||||||
|
# Visual Studio 2015/2017 cache/options directory
|
||||||
|
.vs/
|
||||||
|
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||||
|
#wwwroot/
|
||||||
|
|
||||||
|
# Visual Studio 2017 auto generated files
|
||||||
|
Generated\ Files/
|
||||||
|
|
||||||
|
# MSTest test Results
|
||||||
|
[Tt]est[Rr]esult*/
|
||||||
|
[Bb]uild[Ll]og.*
|
||||||
|
|
||||||
|
# NUNIT
|
||||||
|
*.VisualState.xml
|
||||||
|
TestResult.xml
|
||||||
|
|
||||||
|
# Build Results of an ATL Project
|
||||||
|
[Dd]ebugPS/
|
||||||
|
[Rr]eleasePS/
|
||||||
|
dlldata.c
|
||||||
|
|
||||||
|
# Benchmark Results
|
||||||
|
BenchmarkDotNet.Artifacts/
|
||||||
|
|
||||||
|
# .NET Core
|
||||||
|
project.lock.json
|
||||||
|
project.fragment.lock.json
|
||||||
|
artifacts/
|
||||||
|
|
||||||
|
# StyleCop
|
||||||
|
StyleCopReport.xml
|
||||||
|
|
||||||
|
# Files built by Visual Studio
|
||||||
|
*_i.c
|
||||||
|
*_p.c
|
||||||
|
*_h.h
|
||||||
|
*.ilk
|
||||||
|
*.meta
|
||||||
|
*.iobj
|
||||||
|
*.pdb
|
||||||
|
*.ipdb
|
||||||
|
*.pgc
|
||||||
|
*.pgd
|
||||||
|
*.rsp
|
||||||
|
*.sbr
|
||||||
|
*.tlb
|
||||||
|
*.tli
|
||||||
|
*.tlh
|
||||||
|
*.tmp
|
||||||
|
*.tmp_proj
|
||||||
|
*_wpftmp.csproj
|
||||||
|
*.log
|
||||||
|
*.vspscc
|
||||||
|
*.vssscc
|
||||||
|
.builds
|
||||||
|
*.pidb
|
||||||
|
*.svclog
|
||||||
|
*.scc
|
||||||
|
|
||||||
|
# Chutzpah Test files
|
||||||
|
_Chutzpah*
|
||||||
|
|
||||||
|
# Visual C++ cache files
|
||||||
|
ipch/
|
||||||
|
*.aps
|
||||||
|
*.ncb
|
||||||
|
*.opendb
|
||||||
|
*.opensdf
|
||||||
|
*.sdf
|
||||||
|
*.cachefile
|
||||||
|
*.VC.db
|
||||||
|
*.VC.VC.opendb
|
||||||
|
|
||||||
|
# Visual Studio profiler
|
||||||
|
*.psess
|
||||||
|
*.vsp
|
||||||
|
*.vspx
|
||||||
|
*.sap
|
||||||
|
|
||||||
|
# Visual Studio Trace Files
|
||||||
|
*.e2e
|
||||||
|
|
||||||
|
# TFS 2012 Local Workspace
|
||||||
|
$tf/
|
||||||
|
|
||||||
|
# Guidance Automation Toolkit
|
||||||
|
*.gpState
|
||||||
|
|
||||||
|
# ReSharper is a .NET coding add-in
|
||||||
|
_ReSharper*/
|
||||||
|
*.[Rr]e[Ss]harper
|
||||||
|
*.DotSettings.user
|
||||||
|
|
||||||
|
# JustCode is a .NET coding add-in
|
||||||
|
.JustCode
|
||||||
|
|
||||||
|
# TeamCity is a build add-in
|
||||||
|
_TeamCity*
|
||||||
|
|
||||||
|
# DotCover is a Code Coverage Tool
|
||||||
|
*.dotCover
|
||||||
|
|
||||||
|
# AxoCover is a Code Coverage Tool
|
||||||
|
.axoCover/*
|
||||||
|
!.axoCover/settings.json
|
||||||
|
|
||||||
|
# Visual Studio code coverage results
|
||||||
|
*.coverage
|
||||||
|
*.coveragexml
|
||||||
|
|
||||||
|
# NCrunch
|
||||||
|
_NCrunch_*
|
||||||
|
.*crunch*.local.xml
|
||||||
|
nCrunchTemp_*
|
||||||
|
|
||||||
|
# MightyMoose
|
||||||
|
*.mm.*
|
||||||
|
AutoTest.Net/
|
||||||
|
|
||||||
|
# Web workbench (sass)
|
||||||
|
.sass-cache/
|
||||||
|
|
||||||
|
# Installshield output folder
|
||||||
|
[Ee]xpress/
|
||||||
|
|
||||||
|
# DocProject is a documentation generator add-in
|
||||||
|
DocProject/buildhelp/
|
||||||
|
DocProject/Help/*.HxT
|
||||||
|
DocProject/Help/*.HxC
|
||||||
|
DocProject/Help/*.hhc
|
||||||
|
DocProject/Help/*.hhk
|
||||||
|
DocProject/Help/*.hhp
|
||||||
|
DocProject/Help/Html2
|
||||||
|
DocProject/Help/html
|
||||||
|
|
||||||
|
# Click-Once directory
|
||||||
|
publish/
|
||||||
|
|
||||||
|
# Publish Web Output
|
||||||
|
*.[Pp]ublish.xml
|
||||||
|
*.azurePubxml
|
||||||
|
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||||
|
# but database connection strings (with potential passwords) will be unencrypted
|
||||||
|
*.pubxml
|
||||||
|
*.publishproj
|
||||||
|
|
||||||
|
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||||
|
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||||
|
# in these scripts will be unencrypted
|
||||||
|
PublishScripts/
|
||||||
|
|
||||||
|
# NuGet Packages
|
||||||
|
*.nupkg
|
||||||
|
# The packages folder can be ignored because of Package Restore
|
||||||
|
**/[Pp]ackages/*
|
||||||
|
# except build/, which is used as an MSBuild target.
|
||||||
|
!**/[Pp]ackages/build/
|
||||||
|
# Uncomment if necessary however generally it will be regenerated when needed
|
||||||
|
#!**/[Pp]ackages/repositories.config
|
||||||
|
# NuGet v3's project.json files produces more ignorable files
|
||||||
|
*.nuget.props
|
||||||
|
*.nuget.targets
|
||||||
|
|
||||||
|
# Microsoft Azure Build Output
|
||||||
|
csx/
|
||||||
|
*.build.csdef
|
||||||
|
|
||||||
|
# Microsoft Azure Emulator
|
||||||
|
ecf/
|
||||||
|
rcf/
|
||||||
|
|
||||||
|
# Windows Store app package directories and files
|
||||||
|
AppPackages/
|
||||||
|
BundleArtifacts/
|
||||||
|
Package.StoreAssociation.xml
|
||||||
|
_pkginfo.txt
|
||||||
|
*.appx
|
||||||
|
|
||||||
|
# Visual Studio cache files
|
||||||
|
# files ending in .cache can be ignored
|
||||||
|
*.[Cc]ache
|
||||||
|
# but keep track of directories ending in .cache
|
||||||
|
!*.[Cc]ache/
|
||||||
|
|
||||||
|
# Others
|
||||||
|
ClientBin/
|
||||||
|
~$*
|
||||||
|
*~
|
||||||
|
*.dbmdl
|
||||||
|
*.dbproj.schemaview
|
||||||
|
*.jfm
|
||||||
|
*.pfx
|
||||||
|
*.publishsettings
|
||||||
|
orleans.codegen.cs
|
||||||
|
|
||||||
|
# Including strong name files can present a security risk
|
||||||
|
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||||
|
#*.snk
|
||||||
|
|
||||||
|
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||||
|
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||||
|
#bower_components/
|
||||||
|
# ASP.NET Core default setup: bower directory is configured as wwwroot/lib/ and bower restore is true
|
||||||
|
**/wwwroot/lib/
|
||||||
|
|
||||||
|
# RIA/Silverlight projects
|
||||||
|
Generated_Code/
|
||||||
|
|
||||||
|
# Backup & report files from converting an old project file
|
||||||
|
# to a newer Visual Studio version. Backup files are not needed,
|
||||||
|
# because we have git ;-)
|
||||||
|
_UpgradeReport_Files/
|
||||||
|
Backup*/
|
||||||
|
UpgradeLog*.XML
|
||||||
|
UpgradeLog*.htm
|
||||||
|
ServiceFabricBackup/
|
||||||
|
*.rptproj.bak
|
||||||
|
|
||||||
|
# SQL Server files
|
||||||
|
*.mdf
|
||||||
|
*.ldf
|
||||||
|
*.ndf
|
||||||
|
|
||||||
|
# Business Intelligence projects
|
||||||
|
*.rdl.data
|
||||||
|
*.bim.layout
|
||||||
|
*.bim_*.settings
|
||||||
|
*.rptproj.rsuser
|
||||||
|
|
||||||
|
# Microsoft Fakes
|
||||||
|
FakesAssemblies/
|
||||||
|
|
||||||
|
# GhostDoc plugin setting file
|
||||||
|
*.GhostDoc.xml
|
||||||
|
|
||||||
|
# Node.js Tools for Visual Studio
|
||||||
|
.ntvs_analysis.dat
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# Visual Studio 6 build log
|
||||||
|
*.plg
|
||||||
|
|
||||||
|
# Visual Studio 6 workspace options file
|
||||||
|
*.opt
|
||||||
|
|
||||||
|
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||||
|
*.vbw
|
||||||
|
|
||||||
|
# Visual Studio LightSwitch build output
|
||||||
|
**/*.HTMLClient/GeneratedArtifacts
|
||||||
|
**/*.DesktopClient/GeneratedArtifacts
|
||||||
|
**/*.DesktopClient/ModelManifest.xml
|
||||||
|
**/*.Server/GeneratedArtifacts
|
||||||
|
**/*.Server/ModelManifest.xml
|
||||||
|
_Pvt_Extensions
|
||||||
|
|
||||||
|
# Paket dependency manager
|
||||||
|
.paket/paket.exe
|
||||||
|
paket-files/
|
||||||
|
|
||||||
|
# FAKE - F# Make
|
||||||
|
.fake/
|
||||||
|
|
||||||
|
# JetBrains Rider
|
||||||
|
.idea/
|
||||||
|
*.sln.iml
|
||||||
|
|
||||||
|
# CodeRush personal settings
|
||||||
|
.cr/personal
|
||||||
|
|
||||||
|
# Python Tools for Visual Studio (PTVS)
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
|
||||||
|
# Cake - Uncomment if you are using it
|
||||||
|
# tools/**
|
||||||
|
# !tools/packages.config
|
||||||
|
|
||||||
|
# Tabs Studio
|
||||||
|
*.tss
|
||||||
|
|
||||||
|
# Telerik's JustMock configuration file
|
||||||
|
*.jmconfig
|
||||||
|
|
||||||
|
# BizTalk build output
|
||||||
|
*.btp.cs
|
||||||
|
*.btm.cs
|
||||||
|
*.odx.cs
|
||||||
|
*.xsd.cs
|
||||||
|
|
||||||
|
# OpenCover UI analysis results
|
||||||
|
OpenCover/
|
||||||
|
|
||||||
|
# Azure Stream Analytics local run output
|
||||||
|
ASALocalRun/
|
||||||
|
|
||||||
|
# MSBuild Binary and Structured Log
|
||||||
|
*.binlog
|
||||||
|
|
||||||
|
# NVidia Nsight GPU debugger configuration file
|
||||||
|
*.nvuser
|
||||||
|
|
||||||
|
# MFractors (Xamarin productivity tool) working folder
|
||||||
|
.mfractor/
|
||||||
|
|
||||||
|
# Local History for Visual Studio
|
||||||
|
.localhistory/
|
||||||
|
|
||||||
|
# End of https://www.gitignore.io/api/c++,windows,visualstudio
|
25
BigBaseV2.sln
Normal file
25
BigBaseV2.sln
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio 15
|
||||||
|
VisualStudioVersion = 15.0.28307.168
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BigBaseV2", "src\BigBaseV2.vcxproj", "{D436EE4E-DBF5-4B87-9715-A1C2774BC25F}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|x64 = Debug|x64
|
||||||
|
Release|x64 = Release|x64
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{D436EE4E-DBF5-4B87-9715-A1C2774BC25F}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{D436EE4E-DBF5-4B87-9715-A1C2774BC25F}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{D436EE4E-DBF5-4B87-9715-A1C2774BC25F}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{D436EE4E-DBF5-4B87-9715-A1C2774BC25F}.Release|x64.Build.0 = Release|x64
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {942A81A5-406C-413A-83B5-B8BCB6B7FE26}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
255
lib/include/StackWalker/StackWalker.h
Normal file
255
lib/include/StackWalker/StackWalker.h
Normal file
@ -0,0 +1,255 @@
|
|||||||
|
#ifndef __STACKWALKER_H__
|
||||||
|
#define __STACKWALKER_H__
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
|
||||||
|
/**********************************************************************
|
||||||
|
*
|
||||||
|
* StackWalker.h
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* LICENSE (http://www.opensource.org/licenses/bsd-license.php)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2005-2009, Jochen Kalmbach
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
* are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
* Neither the name of Jochen Kalmbach nor the names of its contributors may be
|
||||||
|
* used to endorse or promote products derived from this software without
|
||||||
|
* specific prior written permission.
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* **********************************************************************/
|
||||||
|
// #pragma once is supported starting with _MSC_VER 1000,
|
||||||
|
// so we need not to check the version (because we only support _MSC_VER >= 1100)!
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#if _MSC_VER >= 1900
|
||||||
|
#pragma warning(disable : 4091)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// special defines for VC5/6 (if no actual PSDK is installed):
|
||||||
|
#if _MSC_VER < 1300
|
||||||
|
typedef unsigned __int64 DWORD64, *PDWORD64;
|
||||||
|
#if defined(_WIN64)
|
||||||
|
typedef unsigned __int64 SIZE_T, *PSIZE_T;
|
||||||
|
#else
|
||||||
|
typedef unsigned long SIZE_T, *PSIZE_T;
|
||||||
|
#endif
|
||||||
|
#endif // _MSC_VER < 1300
|
||||||
|
|
||||||
|
class StackWalkerInternal; // forward
|
||||||
|
class StackWalker
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef enum StackWalkOptions
|
||||||
|
{
|
||||||
|
// No addition info will be retrieved
|
||||||
|
// (only the address is available)
|
||||||
|
RetrieveNone = 0,
|
||||||
|
|
||||||
|
// Try to get the symbol-name
|
||||||
|
RetrieveSymbol = 1,
|
||||||
|
|
||||||
|
// Try to get the line for this symbol
|
||||||
|
RetrieveLine = 2,
|
||||||
|
|
||||||
|
// Try to retrieve the module-infos
|
||||||
|
RetrieveModuleInfo = 4,
|
||||||
|
|
||||||
|
// Also retrieve the version for the DLL/EXE
|
||||||
|
RetrieveFileVersion = 8,
|
||||||
|
|
||||||
|
// Contains all the above
|
||||||
|
RetrieveVerbose = 0xF,
|
||||||
|
|
||||||
|
// Generate a "good" symbol-search-path
|
||||||
|
SymBuildPath = 0x10,
|
||||||
|
|
||||||
|
// Also use the public Microsoft-Symbol-Server
|
||||||
|
SymUseSymSrv = 0x20,
|
||||||
|
|
||||||
|
// Contains all the above "Sym"-options
|
||||||
|
SymAll = 0x30,
|
||||||
|
|
||||||
|
// Contains all options (default)
|
||||||
|
OptionsAll = 0x3F
|
||||||
|
} StackWalkOptions;
|
||||||
|
|
||||||
|
StackWalker(int options = OptionsAll, // 'int' is by design, to combine the enum-flags
|
||||||
|
LPCSTR szSymPath = NULL,
|
||||||
|
DWORD dwProcessId = GetCurrentProcessId(),
|
||||||
|
HANDLE hProcess = GetCurrentProcess());
|
||||||
|
StackWalker(DWORD dwProcessId, HANDLE hProcess);
|
||||||
|
virtual ~StackWalker();
|
||||||
|
|
||||||
|
typedef BOOL(__stdcall* PReadProcessMemoryRoutine)(
|
||||||
|
HANDLE hProcess,
|
||||||
|
DWORD64 qwBaseAddress,
|
||||||
|
PVOID lpBuffer,
|
||||||
|
DWORD nSize,
|
||||||
|
LPDWORD lpNumberOfBytesRead,
|
||||||
|
LPVOID pUserData // optional data, which was passed in "ShowCallstack"
|
||||||
|
);
|
||||||
|
|
||||||
|
BOOL LoadModules();
|
||||||
|
|
||||||
|
BOOL ShowCallstack(
|
||||||
|
HANDLE hThread = GetCurrentThread(),
|
||||||
|
const CONTEXT* context = NULL,
|
||||||
|
PReadProcessMemoryRoutine readMemoryFunction = NULL,
|
||||||
|
LPVOID pUserData = NULL // optional to identify some data in the 'readMemoryFunction'-callback
|
||||||
|
);
|
||||||
|
|
||||||
|
BOOL ShowObject(LPVOID pObject);
|
||||||
|
|
||||||
|
#if _MSC_VER >= 1300
|
||||||
|
// due to some reasons, the "STACKWALK_MAX_NAMELEN" must be declared as "public"
|
||||||
|
// in older compilers in order to use it... starting with VC7 we can declare it as "protected"
|
||||||
|
protected:
|
||||||
|
#endif
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
STACKWALK_MAX_NAMELEN = 1024
|
||||||
|
}; // max name length for found symbols
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Entry for each Callstack-Entry
|
||||||
|
typedef struct CallstackEntry
|
||||||
|
{
|
||||||
|
DWORD64 offset; // if 0, we have no valid entry
|
||||||
|
CHAR name[STACKWALK_MAX_NAMELEN];
|
||||||
|
CHAR undName[STACKWALK_MAX_NAMELEN];
|
||||||
|
CHAR undFullName[STACKWALK_MAX_NAMELEN];
|
||||||
|
DWORD64 offsetFromSmybol;
|
||||||
|
DWORD offsetFromLine;
|
||||||
|
DWORD lineNumber;
|
||||||
|
CHAR lineFileName[STACKWALK_MAX_NAMELEN];
|
||||||
|
DWORD symType;
|
||||||
|
LPCSTR symTypeString;
|
||||||
|
CHAR moduleName[STACKWALK_MAX_NAMELEN];
|
||||||
|
DWORD64 baseOfImage;
|
||||||
|
CHAR loadedImageName[STACKWALK_MAX_NAMELEN];
|
||||||
|
} CallstackEntry;
|
||||||
|
|
||||||
|
typedef enum CallstackEntryType
|
||||||
|
{
|
||||||
|
firstEntry,
|
||||||
|
nextEntry,
|
||||||
|
lastEntry
|
||||||
|
} CallstackEntryType;
|
||||||
|
|
||||||
|
virtual void OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName);
|
||||||
|
virtual void OnLoadModule(LPCSTR img,
|
||||||
|
LPCSTR mod,
|
||||||
|
DWORD64 baseAddr,
|
||||||
|
DWORD size,
|
||||||
|
DWORD result,
|
||||||
|
LPCSTR symType,
|
||||||
|
LPCSTR pdbName,
|
||||||
|
ULONGLONG fileVersion);
|
||||||
|
virtual void OnCallstackEntry(CallstackEntryType eType, CallstackEntry& entry);
|
||||||
|
virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr);
|
||||||
|
virtual void OnOutput(LPCSTR szText);
|
||||||
|
|
||||||
|
StackWalkerInternal* m_sw;
|
||||||
|
HANDLE m_hProcess;
|
||||||
|
DWORD m_dwProcessId;
|
||||||
|
BOOL m_modulesLoaded;
|
||||||
|
LPSTR m_szSymPath;
|
||||||
|
|
||||||
|
int m_options;
|
||||||
|
int m_MaxRecursionCount;
|
||||||
|
|
||||||
|
static BOOL __stdcall myReadProcMem(HANDLE hProcess,
|
||||||
|
DWORD64 qwBaseAddress,
|
||||||
|
PVOID lpBuffer,
|
||||||
|
DWORD nSize,
|
||||||
|
LPDWORD lpNumberOfBytesRead);
|
||||||
|
|
||||||
|
friend StackWalkerInternal;
|
||||||
|
}; // class StackWalker
|
||||||
|
|
||||||
|
// The "ugly" assembler-implementation is needed for systems before XP
|
||||||
|
// If you have a new PSDK and you only compile for XP and later, then you can use
|
||||||
|
// the "RtlCaptureContext"
|
||||||
|
// Currently there is no define which determines the PSDK-Version...
|
||||||
|
// So we just use the compiler-version (and assumes that the PSDK is
|
||||||
|
// the one which was installed by the VS-IDE)
|
||||||
|
|
||||||
|
// INFO: If you want, you can use the RtlCaptureContext if you only target XP and later...
|
||||||
|
// But I currently use it in x64/IA64 environments...
|
||||||
|
//#if defined(_M_IX86) && (_WIN32_WINNT <= 0x0500) && (_MSC_VER < 1400)
|
||||||
|
|
||||||
|
#if defined(_M_IX86)
|
||||||
|
#ifdef CURRENT_THREAD_VIA_EXCEPTION
|
||||||
|
// TODO: The following is not a "good" implementation,
|
||||||
|
// because the callstack is only valid in the "__except" block...
|
||||||
|
#define GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, contextFlags) \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
memset(&c, 0, sizeof(CONTEXT)); \
|
||||||
|
EXCEPTION_POINTERS* pExp = NULL; \
|
||||||
|
__try \
|
||||||
|
{ \
|
||||||
|
throw 0; \
|
||||||
|
} \
|
||||||
|
__except (((pExp = GetExceptionInformation()) ? EXCEPTION_EXECUTE_HANDLER \
|
||||||
|
: EXCEPTION_EXECUTE_HANDLER)) \
|
||||||
|
{ \
|
||||||
|
} \
|
||||||
|
if (pExp != NULL) \
|
||||||
|
memcpy(&c, pExp->ContextRecord, sizeof(CONTEXT)); \
|
||||||
|
c.ContextFlags = contextFlags; \
|
||||||
|
} while (0);
|
||||||
|
#else
|
||||||
|
// clang-format off
|
||||||
|
// The following should be enough for walking the callstack...
|
||||||
|
#define GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, contextFlags) \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
memset(&c, 0, sizeof(CONTEXT)); \
|
||||||
|
c.ContextFlags = contextFlags; \
|
||||||
|
__asm call x \
|
||||||
|
__asm x: pop eax \
|
||||||
|
__asm mov c.Eip, eax \
|
||||||
|
__asm mov c.Ebp, ebp \
|
||||||
|
__asm mov c.Esp, esp \
|
||||||
|
} while (0)
|
||||||
|
// clang-format on
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
// The following is defined for x86 (XP and higher), x64 and IA64:
|
||||||
|
#define GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, contextFlags) \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
memset(&c, 0, sizeof(CONTEXT)); \
|
||||||
|
c.ContextFlags = contextFlags; \
|
||||||
|
RtlCaptureContext(&c); \
|
||||||
|
} while (0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif //defined(_MSC_VER)
|
||||||
|
|
||||||
|
#endif // __STACKWALKER_H__
|
3668
lib/include/cpptoml/cpptoml.h
Normal file
3668
lib/include/cpptoml/cpptoml.h
Normal file
File diff suppressed because it is too large
Load Diff
452
lib/include/fmt/chrono.h
Normal file
452
lib/include/fmt/chrono.h
Normal file
@ -0,0 +1,452 @@
|
|||||||
|
// Formatting library for C++ - chrono support
|
||||||
|
//
|
||||||
|
// Copyright (c) 2012 - present, Victor Zverovich
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
|
#ifndef FMT_CHRONO_H_
|
||||||
|
#define FMT_CHRONO_H_
|
||||||
|
|
||||||
|
#include "format.h"
|
||||||
|
#include "locale.h"
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <ctime>
|
||||||
|
#include <locale>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
namespace internal{
|
||||||
|
|
||||||
|
enum class numeric_system {
|
||||||
|
standard,
|
||||||
|
// Alternative numeric system, e.g. 十二 instead of 12 in ja_JP locale.
|
||||||
|
alternative
|
||||||
|
};
|
||||||
|
|
||||||
|
// Parses a put_time-like format string and invokes handler actions.
|
||||||
|
template <typename Char, typename Handler>
|
||||||
|
FMT_CONSTEXPR const Char *parse_chrono_format(
|
||||||
|
const Char *begin, const Char *end, Handler &&handler) {
|
||||||
|
auto ptr = begin;
|
||||||
|
while (ptr != end) {
|
||||||
|
auto c = *ptr;
|
||||||
|
if (c == '}') break;
|
||||||
|
if (c != '%') {
|
||||||
|
++ptr;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (begin != ptr)
|
||||||
|
handler.on_text(begin, ptr);
|
||||||
|
++ptr; // consume '%'
|
||||||
|
if (ptr == end)
|
||||||
|
throw format_error("invalid format");
|
||||||
|
c = *ptr++;
|
||||||
|
switch (c) {
|
||||||
|
case '%':
|
||||||
|
handler.on_text(ptr - 1, ptr);
|
||||||
|
break;
|
||||||
|
case 'n': {
|
||||||
|
const char newline[] = "\n";
|
||||||
|
handler.on_text(newline, newline + 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 't': {
|
||||||
|
const char tab[] = "\t";
|
||||||
|
handler.on_text(tab, tab + 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Day of the week:
|
||||||
|
case 'a':
|
||||||
|
handler.on_abbr_weekday();
|
||||||
|
break;
|
||||||
|
case 'A':
|
||||||
|
handler.on_full_weekday();
|
||||||
|
break;
|
||||||
|
case 'w':
|
||||||
|
handler.on_dec0_weekday(numeric_system::standard);
|
||||||
|
break;
|
||||||
|
case 'u':
|
||||||
|
handler.on_dec1_weekday(numeric_system::standard);
|
||||||
|
break;
|
||||||
|
// Month:
|
||||||
|
case 'b':
|
||||||
|
handler.on_abbr_month();
|
||||||
|
break;
|
||||||
|
case 'B':
|
||||||
|
handler.on_full_month();
|
||||||
|
break;
|
||||||
|
// Hour, minute, second:
|
||||||
|
case 'H':
|
||||||
|
handler.on_24_hour(numeric_system::standard);
|
||||||
|
break;
|
||||||
|
case 'I':
|
||||||
|
handler.on_12_hour(numeric_system::standard);
|
||||||
|
break;
|
||||||
|
case 'M':
|
||||||
|
handler.on_minute(numeric_system::standard);
|
||||||
|
break;
|
||||||
|
case 'S':
|
||||||
|
handler.on_second(numeric_system::standard);
|
||||||
|
break;
|
||||||
|
// Other:
|
||||||
|
case 'c':
|
||||||
|
handler.on_datetime(numeric_system::standard);
|
||||||
|
break;
|
||||||
|
case 'x':
|
||||||
|
handler.on_loc_date(numeric_system::standard);
|
||||||
|
break;
|
||||||
|
case 'X':
|
||||||
|
handler.on_loc_time(numeric_system::standard);
|
||||||
|
break;
|
||||||
|
case 'D':
|
||||||
|
handler.on_us_date();
|
||||||
|
break;
|
||||||
|
case 'F':
|
||||||
|
handler.on_iso_date();
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
handler.on_12_hour_time();
|
||||||
|
break;
|
||||||
|
case 'R':
|
||||||
|
handler.on_24_hour_time();
|
||||||
|
break;
|
||||||
|
case 'T':
|
||||||
|
handler.on_iso_time();
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
handler.on_am_pm();
|
||||||
|
break;
|
||||||
|
case 'z':
|
||||||
|
handler.on_utc_offset();
|
||||||
|
break;
|
||||||
|
case 'Z':
|
||||||
|
handler.on_tz_name();
|
||||||
|
break;
|
||||||
|
// Alternative representation:
|
||||||
|
case 'E': {
|
||||||
|
if (ptr == end)
|
||||||
|
throw format_error("invalid format");
|
||||||
|
c = *ptr++;
|
||||||
|
switch (c) {
|
||||||
|
case 'c':
|
||||||
|
handler.on_datetime(numeric_system::alternative);
|
||||||
|
break;
|
||||||
|
case 'x':
|
||||||
|
handler.on_loc_date(numeric_system::alternative);
|
||||||
|
break;
|
||||||
|
case 'X':
|
||||||
|
handler.on_loc_time(numeric_system::alternative);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw format_error("invalid format");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'O':
|
||||||
|
if (ptr == end)
|
||||||
|
throw format_error("invalid format");
|
||||||
|
c = *ptr++;
|
||||||
|
switch (c) {
|
||||||
|
case 'w':
|
||||||
|
handler.on_dec0_weekday(numeric_system::alternative);
|
||||||
|
break;
|
||||||
|
case 'u':
|
||||||
|
handler.on_dec1_weekday(numeric_system::alternative);
|
||||||
|
break;
|
||||||
|
case 'H':
|
||||||
|
handler.on_24_hour(numeric_system::alternative);
|
||||||
|
break;
|
||||||
|
case 'I':
|
||||||
|
handler.on_12_hour(numeric_system::alternative);
|
||||||
|
break;
|
||||||
|
case 'M':
|
||||||
|
handler.on_minute(numeric_system::alternative);
|
||||||
|
break;
|
||||||
|
case 'S':
|
||||||
|
handler.on_second(numeric_system::alternative);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw format_error("invalid format");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw format_error("invalid format");
|
||||||
|
}
|
||||||
|
begin = ptr;
|
||||||
|
}
|
||||||
|
if (begin != ptr)
|
||||||
|
handler.on_text(begin, ptr);
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct chrono_format_checker {
|
||||||
|
void report_no_date() { throw format_error("no date"); }
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
void on_text(const Char *, const Char *) {}
|
||||||
|
void on_abbr_weekday() { report_no_date(); }
|
||||||
|
void on_full_weekday() { report_no_date(); }
|
||||||
|
void on_dec0_weekday(numeric_system) { report_no_date(); }
|
||||||
|
void on_dec1_weekday(numeric_system) { report_no_date(); }
|
||||||
|
void on_abbr_month() { report_no_date(); }
|
||||||
|
void on_full_month() { report_no_date(); }
|
||||||
|
void on_24_hour(numeric_system) {}
|
||||||
|
void on_12_hour(numeric_system) {}
|
||||||
|
void on_minute(numeric_system) {}
|
||||||
|
void on_second(numeric_system) {}
|
||||||
|
void on_datetime(numeric_system) { report_no_date(); }
|
||||||
|
void on_loc_date(numeric_system) { report_no_date(); }
|
||||||
|
void on_loc_time(numeric_system) { report_no_date(); }
|
||||||
|
void on_us_date() { report_no_date(); }
|
||||||
|
void on_iso_date() { report_no_date(); }
|
||||||
|
void on_12_hour_time() {}
|
||||||
|
void on_24_hour_time() {}
|
||||||
|
void on_iso_time() {}
|
||||||
|
void on_am_pm() {}
|
||||||
|
void on_utc_offset() { report_no_date(); }
|
||||||
|
void on_tz_name() { report_no_date(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Int>
|
||||||
|
inline int to_int(Int value) {
|
||||||
|
FMT_ASSERT(value >= (std::numeric_limits<int>::min)() &&
|
||||||
|
value <= (std::numeric_limits<int>::max)(), "invalid value");
|
||||||
|
return static_cast<int>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FormatContext, typename OutputIt>
|
||||||
|
struct chrono_formatter {
|
||||||
|
FormatContext &context;
|
||||||
|
OutputIt out;
|
||||||
|
std::chrono::seconds s;
|
||||||
|
std::chrono::milliseconds ms;
|
||||||
|
|
||||||
|
typedef typename FormatContext::char_type char_type;
|
||||||
|
|
||||||
|
explicit chrono_formatter(FormatContext &ctx, OutputIt o)
|
||||||
|
: context(ctx), out(o) {}
|
||||||
|
|
||||||
|
int hour() const { return to_int((s.count() / 3600) % 24); }
|
||||||
|
|
||||||
|
int hour12() const {
|
||||||
|
auto hour = to_int((s.count() / 3600) % 12);
|
||||||
|
return hour > 0 ? hour : 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
int minute() const { return to_int((s.count() / 60) % 60); }
|
||||||
|
int second() const { return to_int(s.count() % 60); }
|
||||||
|
|
||||||
|
std::tm time() const {
|
||||||
|
auto time = std::tm();
|
||||||
|
time.tm_hour = hour();
|
||||||
|
time.tm_min = minute();
|
||||||
|
time.tm_sec = second();
|
||||||
|
return time;
|
||||||
|
}
|
||||||
|
|
||||||
|
void write(int value, int width) {
|
||||||
|
typedef typename int_traits<int>::main_type main_type;
|
||||||
|
main_type n = to_unsigned(value);
|
||||||
|
int num_digits = internal::count_digits(n);
|
||||||
|
if (width > num_digits)
|
||||||
|
out = std::fill_n(out, width - num_digits, '0');
|
||||||
|
out = format_decimal<char_type>(out, n, num_digits);
|
||||||
|
}
|
||||||
|
|
||||||
|
void format_localized(const tm &time, const char *format) {
|
||||||
|
auto locale = context.locale().template get<std::locale>();
|
||||||
|
auto &facet = std::use_facet<std::time_put<char_type>>(locale);
|
||||||
|
std::basic_ostringstream<char_type> os;
|
||||||
|
os.imbue(locale);
|
||||||
|
facet.put(os, os, ' ', &time, format, format + std::strlen(format));
|
||||||
|
auto str = os.str();
|
||||||
|
std::copy(str.begin(), str.end(), out);
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_text(const char_type *begin, const char_type *end) {
|
||||||
|
std::copy(begin, end, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
// These are not implemented because durations don't have date information.
|
||||||
|
void on_abbr_weekday() {}
|
||||||
|
void on_full_weekday() {}
|
||||||
|
void on_dec0_weekday(numeric_system) {}
|
||||||
|
void on_dec1_weekday(numeric_system) {}
|
||||||
|
void on_abbr_month() {}
|
||||||
|
void on_full_month() {}
|
||||||
|
void on_datetime(numeric_system) {}
|
||||||
|
void on_loc_date(numeric_system) {}
|
||||||
|
void on_loc_time(numeric_system) {}
|
||||||
|
void on_us_date() {}
|
||||||
|
void on_iso_date() {}
|
||||||
|
void on_utc_offset() {}
|
||||||
|
void on_tz_name() {}
|
||||||
|
|
||||||
|
void on_24_hour(numeric_system ns) {
|
||||||
|
if (ns == numeric_system::standard)
|
||||||
|
return write(hour(), 2);
|
||||||
|
auto time = tm();
|
||||||
|
time.tm_hour = hour();
|
||||||
|
format_localized(time, "%OH");
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_12_hour(numeric_system ns) {
|
||||||
|
if (ns == numeric_system::standard)
|
||||||
|
return write(hour12(), 2);
|
||||||
|
auto time = tm();
|
||||||
|
time.tm_hour = hour();
|
||||||
|
format_localized(time, "%OI");
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_minute(numeric_system ns) {
|
||||||
|
if (ns == numeric_system::standard)
|
||||||
|
return write(minute(), 2);
|
||||||
|
auto time = tm();
|
||||||
|
time.tm_min = minute();
|
||||||
|
format_localized(time, "%OM");
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_second(numeric_system ns) {
|
||||||
|
if (ns == numeric_system::standard) {
|
||||||
|
write(second(), 2);
|
||||||
|
if (ms != std::chrono::milliseconds(0)) {
|
||||||
|
*out++ = '.';
|
||||||
|
write(to_int(ms.count()), 3);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto time = tm();
|
||||||
|
time.tm_sec = second();
|
||||||
|
format_localized(time, "%OS");
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_12_hour_time() { format_localized(time(), "%r"); }
|
||||||
|
|
||||||
|
void on_24_hour_time() {
|
||||||
|
write(hour(), 2);
|
||||||
|
*out++ = ':';
|
||||||
|
write(minute(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_iso_time() {
|
||||||
|
on_24_hour_time();
|
||||||
|
*out++ = ':';
|
||||||
|
write(second(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_am_pm() { format_localized(time(), "%p"); }
|
||||||
|
};
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
template <typename Period> FMT_CONSTEXPR const char *get_units() {
|
||||||
|
return FMT_NULL;
|
||||||
|
}
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::atto>() { return "as"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::femto>() { return "fs"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::pico>() { return "ps"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::nano>() { return "ns"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::micro>() { return "µs"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::milli>() { return "ms"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::centi>() { return "cs"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::deci>() { return "ds"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::ratio<1>>() { return "s"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::deca>() { return "das"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::hecto>() { return "hs"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::kilo>() { return "ks"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::mega>() { return "Ms"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::giga>() { return "Gs"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::tera>() { return "Ts"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::peta>() { return "Ps"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::exa>() { return "Es"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::ratio<60>>() {
|
||||||
|
return "m";
|
||||||
|
}
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::ratio<3600>>() {
|
||||||
|
return "h";
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Rep, typename Period, typename Char>
|
||||||
|
struct formatter<std::chrono::duration<Rep, Period>, Char> {
|
||||||
|
private:
|
||||||
|
align_spec spec;
|
||||||
|
internal::arg_ref<Char> width_ref;
|
||||||
|
mutable basic_string_view<Char> format_str;
|
||||||
|
typedef std::chrono::duration<Rep, Period> duration;
|
||||||
|
|
||||||
|
struct spec_handler {
|
||||||
|
formatter &f;
|
||||||
|
basic_parse_context<Char> &context;
|
||||||
|
|
||||||
|
typedef internal::arg_ref<Char> arg_ref_type;
|
||||||
|
|
||||||
|
template <typename Id>
|
||||||
|
FMT_CONSTEXPR arg_ref_type make_arg_ref(Id arg_id) {
|
||||||
|
context.check_arg_id(arg_id);
|
||||||
|
return arg_ref_type(arg_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR arg_ref_type make_arg_ref(internal::auto_id) {
|
||||||
|
return arg_ref_type(context.next_arg_id());
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_error(const char *msg) { throw format_error(msg); }
|
||||||
|
void on_fill(Char fill) { f.spec.fill_ = fill; }
|
||||||
|
void on_align(alignment align) { f.spec.align_ = align; }
|
||||||
|
void on_width(unsigned width) { f.spec.width_ = width; }
|
||||||
|
|
||||||
|
template <typename Id>
|
||||||
|
void on_dynamic_width(Id arg_id) {
|
||||||
|
f.width_ref = make_arg_ref(arg_id);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
formatter() : spec() {}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR auto parse(basic_parse_context<Char> &ctx)
|
||||||
|
-> decltype(ctx.begin()) {
|
||||||
|
auto begin = ctx.begin(), end = ctx.end();
|
||||||
|
if (begin == end) return begin;
|
||||||
|
spec_handler handler{*this, ctx};
|
||||||
|
begin = internal::parse_align(begin, end, handler);
|
||||||
|
if (begin == end) return begin;
|
||||||
|
begin = internal::parse_width(begin, end, handler);
|
||||||
|
end = parse_chrono_format(begin, end, internal::chrono_format_checker());
|
||||||
|
format_str = basic_string_view<Char>(&*begin, internal::to_unsigned(end - begin));
|
||||||
|
return end;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(const duration &d, FormatContext &ctx)
|
||||||
|
-> decltype(ctx.out()) {
|
||||||
|
auto begin = format_str.begin(), end = format_str.end();
|
||||||
|
memory_buffer buf;
|
||||||
|
typedef output_range<decltype(ctx.out()), Char> range;
|
||||||
|
basic_writer<range> w(range(ctx.out()));
|
||||||
|
if (begin == end || *begin == '}') {
|
||||||
|
if (const char *unit = get_units<Period>())
|
||||||
|
format_to(buf, "{}{}", d.count(), unit);
|
||||||
|
else if (Period::den == 1)
|
||||||
|
format_to(buf, "{}[{}]s", d.count(), Period::num);
|
||||||
|
else
|
||||||
|
format_to(buf, "{}[{}/{}]s", d.count(), Period::num, Period::den);
|
||||||
|
internal::handle_dynamic_spec<internal::width_checker>(
|
||||||
|
spec.width_, width_ref, ctx);
|
||||||
|
} else {
|
||||||
|
auto out = std::back_inserter(buf);
|
||||||
|
internal::chrono_formatter<FormatContext, decltype(out)> f(ctx, out);
|
||||||
|
f.s = std::chrono::duration_cast<std::chrono::seconds>(d);
|
||||||
|
f.ms = std::chrono::duration_cast<std::chrono::milliseconds>(d - f.s);
|
||||||
|
parse_chrono_format(begin, end, f);
|
||||||
|
}
|
||||||
|
w.write(buf.data(), buf.size(), spec);
|
||||||
|
return w.out();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // FMT_CHRONO_H_
|
577
lib/include/fmt/color.h
Normal file
577
lib/include/fmt/color.h
Normal file
@ -0,0 +1,577 @@
|
|||||||
|
// Formatting library for C++ - color support
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018 - present, Victor Zverovich and fmt contributors
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
|
#ifndef FMT_COLOR_H_
|
||||||
|
#define FMT_COLOR_H_
|
||||||
|
|
||||||
|
#include "format.h"
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
#ifdef FMT_DEPRECATED_COLORS
|
||||||
|
|
||||||
|
// color and (v)print_colored are deprecated.
|
||||||
|
enum color { black, red, green, yellow, blue, magenta, cyan, white };
|
||||||
|
FMT_API void vprint_colored(color c, string_view format, format_args args);
|
||||||
|
FMT_API void vprint_colored(color c, wstring_view format, wformat_args args);
|
||||||
|
template <typename... Args>
|
||||||
|
inline void print_colored(color c, string_view format_str,
|
||||||
|
const Args & ... args) {
|
||||||
|
vprint_colored(c, format_str, make_format_args(args...));
|
||||||
|
}
|
||||||
|
template <typename... Args>
|
||||||
|
inline void print_colored(color c, wstring_view format_str,
|
||||||
|
const Args & ... args) {
|
||||||
|
vprint_colored(c, format_str, make_format_args<wformat_context>(args...));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void vprint_colored(color c, string_view format, format_args args) {
|
||||||
|
char escape[] = "\x1b[30m";
|
||||||
|
escape[3] = static_cast<char>('0' + c);
|
||||||
|
std::fputs(escape, stdout);
|
||||||
|
vprint(format, args);
|
||||||
|
std::fputs(internal::data::RESET_COLOR, stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void vprint_colored(color c, wstring_view format, wformat_args args) {
|
||||||
|
wchar_t escape[] = L"\x1b[30m";
|
||||||
|
escape[3] = static_cast<wchar_t>('0' + c);
|
||||||
|
std::fputws(escape, stdout);
|
||||||
|
vprint(format, args);
|
||||||
|
std::fputws(internal::data::WRESET_COLOR, stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
enum class color : uint32_t {
|
||||||
|
alice_blue = 0xF0F8FF, // rgb(240,248,255)
|
||||||
|
antique_white = 0xFAEBD7, // rgb(250,235,215)
|
||||||
|
aqua = 0x00FFFF, // rgb(0,255,255)
|
||||||
|
aquamarine = 0x7FFFD4, // rgb(127,255,212)
|
||||||
|
azure = 0xF0FFFF, // rgb(240,255,255)
|
||||||
|
beige = 0xF5F5DC, // rgb(245,245,220)
|
||||||
|
bisque = 0xFFE4C4, // rgb(255,228,196)
|
||||||
|
black = 0x000000, // rgb(0,0,0)
|
||||||
|
blanched_almond = 0xFFEBCD, // rgb(255,235,205)
|
||||||
|
blue = 0x0000FF, // rgb(0,0,255)
|
||||||
|
blue_violet = 0x8A2BE2, // rgb(138,43,226)
|
||||||
|
brown = 0xA52A2A, // rgb(165,42,42)
|
||||||
|
burly_wood = 0xDEB887, // rgb(222,184,135)
|
||||||
|
cadet_blue = 0x5F9EA0, // rgb(95,158,160)
|
||||||
|
chartreuse = 0x7FFF00, // rgb(127,255,0)
|
||||||
|
chocolate = 0xD2691E, // rgb(210,105,30)
|
||||||
|
coral = 0xFF7F50, // rgb(255,127,80)
|
||||||
|
cornflower_blue = 0x6495ED, // rgb(100,149,237)
|
||||||
|
cornsilk = 0xFFF8DC, // rgb(255,248,220)
|
||||||
|
crimson = 0xDC143C, // rgb(220,20,60)
|
||||||
|
cyan = 0x00FFFF, // rgb(0,255,255)
|
||||||
|
dark_blue = 0x00008B, // rgb(0,0,139)
|
||||||
|
dark_cyan = 0x008B8B, // rgb(0,139,139)
|
||||||
|
dark_golden_rod = 0xB8860B, // rgb(184,134,11)
|
||||||
|
dark_gray = 0xA9A9A9, // rgb(169,169,169)
|
||||||
|
dark_green = 0x006400, // rgb(0,100,0)
|
||||||
|
dark_khaki = 0xBDB76B, // rgb(189,183,107)
|
||||||
|
dark_magenta = 0x8B008B, // rgb(139,0,139)
|
||||||
|
dark_olive_green = 0x556B2F, // rgb(85,107,47)
|
||||||
|
dark_orange = 0xFF8C00, // rgb(255,140,0)
|
||||||
|
dark_orchid = 0x9932CC, // rgb(153,50,204)
|
||||||
|
dark_red = 0x8B0000, // rgb(139,0,0)
|
||||||
|
dark_salmon = 0xE9967A, // rgb(233,150,122)
|
||||||
|
dark_sea_green = 0x8FBC8F, // rgb(143,188,143)
|
||||||
|
dark_slate_blue = 0x483D8B, // rgb(72,61,139)
|
||||||
|
dark_slate_gray = 0x2F4F4F, // rgb(47,79,79)
|
||||||
|
dark_turquoise = 0x00CED1, // rgb(0,206,209)
|
||||||
|
dark_violet = 0x9400D3, // rgb(148,0,211)
|
||||||
|
deep_pink = 0xFF1493, // rgb(255,20,147)
|
||||||
|
deep_sky_blue = 0x00BFFF, // rgb(0,191,255)
|
||||||
|
dim_gray = 0x696969, // rgb(105,105,105)
|
||||||
|
dodger_blue = 0x1E90FF, // rgb(30,144,255)
|
||||||
|
fire_brick = 0xB22222, // rgb(178,34,34)
|
||||||
|
floral_white = 0xFFFAF0, // rgb(255,250,240)
|
||||||
|
forest_green = 0x228B22, // rgb(34,139,34)
|
||||||
|
fuchsia = 0xFF00FF, // rgb(255,0,255)
|
||||||
|
gainsboro = 0xDCDCDC, // rgb(220,220,220)
|
||||||
|
ghost_white = 0xF8F8FF, // rgb(248,248,255)
|
||||||
|
gold = 0xFFD700, // rgb(255,215,0)
|
||||||
|
golden_rod = 0xDAA520, // rgb(218,165,32)
|
||||||
|
gray = 0x808080, // rgb(128,128,128)
|
||||||
|
green = 0x008000, // rgb(0,128,0)
|
||||||
|
green_yellow = 0xADFF2F, // rgb(173,255,47)
|
||||||
|
honey_dew = 0xF0FFF0, // rgb(240,255,240)
|
||||||
|
hot_pink = 0xFF69B4, // rgb(255,105,180)
|
||||||
|
indian_red = 0xCD5C5C, // rgb(205,92,92)
|
||||||
|
indigo = 0x4B0082, // rgb(75,0,130)
|
||||||
|
ivory = 0xFFFFF0, // rgb(255,255,240)
|
||||||
|
khaki = 0xF0E68C, // rgb(240,230,140)
|
||||||
|
lavender = 0xE6E6FA, // rgb(230,230,250)
|
||||||
|
lavender_blush = 0xFFF0F5, // rgb(255,240,245)
|
||||||
|
lawn_green = 0x7CFC00, // rgb(124,252,0)
|
||||||
|
lemon_chiffon = 0xFFFACD, // rgb(255,250,205)
|
||||||
|
light_blue = 0xADD8E6, // rgb(173,216,230)
|
||||||
|
light_coral = 0xF08080, // rgb(240,128,128)
|
||||||
|
light_cyan = 0xE0FFFF, // rgb(224,255,255)
|
||||||
|
light_golden_rod_yellow = 0xFAFAD2, // rgb(250,250,210)
|
||||||
|
light_gray = 0xD3D3D3, // rgb(211,211,211)
|
||||||
|
light_green = 0x90EE90, // rgb(144,238,144)
|
||||||
|
light_pink = 0xFFB6C1, // rgb(255,182,193)
|
||||||
|
light_salmon = 0xFFA07A, // rgb(255,160,122)
|
||||||
|
light_sea_green = 0x20B2AA, // rgb(32,178,170)
|
||||||
|
light_sky_blue = 0x87CEFA, // rgb(135,206,250)
|
||||||
|
light_slate_gray = 0x778899, // rgb(119,136,153)
|
||||||
|
light_steel_blue = 0xB0C4DE, // rgb(176,196,222)
|
||||||
|
light_yellow = 0xFFFFE0, // rgb(255,255,224)
|
||||||
|
lime = 0x00FF00, // rgb(0,255,0)
|
||||||
|
lime_green = 0x32CD32, // rgb(50,205,50)
|
||||||
|
linen = 0xFAF0E6, // rgb(250,240,230)
|
||||||
|
magenta = 0xFF00FF, // rgb(255,0,255)
|
||||||
|
maroon = 0x800000, // rgb(128,0,0)
|
||||||
|
medium_aquamarine = 0x66CDAA, // rgb(102,205,170)
|
||||||
|
medium_blue = 0x0000CD, // rgb(0,0,205)
|
||||||
|
medium_orchid = 0xBA55D3, // rgb(186,85,211)
|
||||||
|
medium_purple = 0x9370DB, // rgb(147,112,219)
|
||||||
|
medium_sea_green = 0x3CB371, // rgb(60,179,113)
|
||||||
|
medium_slate_blue = 0x7B68EE, // rgb(123,104,238)
|
||||||
|
medium_spring_green = 0x00FA9A, // rgb(0,250,154)
|
||||||
|
medium_turquoise = 0x48D1CC, // rgb(72,209,204)
|
||||||
|
medium_violet_red = 0xC71585, // rgb(199,21,133)
|
||||||
|
midnight_blue = 0x191970, // rgb(25,25,112)
|
||||||
|
mint_cream = 0xF5FFFA, // rgb(245,255,250)
|
||||||
|
misty_rose = 0xFFE4E1, // rgb(255,228,225)
|
||||||
|
moccasin = 0xFFE4B5, // rgb(255,228,181)
|
||||||
|
navajo_white = 0xFFDEAD, // rgb(255,222,173)
|
||||||
|
navy = 0x000080, // rgb(0,0,128)
|
||||||
|
old_lace = 0xFDF5E6, // rgb(253,245,230)
|
||||||
|
olive = 0x808000, // rgb(128,128,0)
|
||||||
|
olive_drab = 0x6B8E23, // rgb(107,142,35)
|
||||||
|
orange = 0xFFA500, // rgb(255,165,0)
|
||||||
|
orange_red = 0xFF4500, // rgb(255,69,0)
|
||||||
|
orchid = 0xDA70D6, // rgb(218,112,214)
|
||||||
|
pale_golden_rod = 0xEEE8AA, // rgb(238,232,170)
|
||||||
|
pale_green = 0x98FB98, // rgb(152,251,152)
|
||||||
|
pale_turquoise = 0xAFEEEE, // rgb(175,238,238)
|
||||||
|
pale_violet_red = 0xDB7093, // rgb(219,112,147)
|
||||||
|
papaya_whip = 0xFFEFD5, // rgb(255,239,213)
|
||||||
|
peach_puff = 0xFFDAB9, // rgb(255,218,185)
|
||||||
|
peru = 0xCD853F, // rgb(205,133,63)
|
||||||
|
pink = 0xFFC0CB, // rgb(255,192,203)
|
||||||
|
plum = 0xDDA0DD, // rgb(221,160,221)
|
||||||
|
powder_blue = 0xB0E0E6, // rgb(176,224,230)
|
||||||
|
purple = 0x800080, // rgb(128,0,128)
|
||||||
|
rebecca_purple = 0x663399, // rgb(102,51,153)
|
||||||
|
red = 0xFF0000, // rgb(255,0,0)
|
||||||
|
rosy_brown = 0xBC8F8F, // rgb(188,143,143)
|
||||||
|
royal_blue = 0x4169E1, // rgb(65,105,225)
|
||||||
|
saddle_brown = 0x8B4513, // rgb(139,69,19)
|
||||||
|
salmon = 0xFA8072, // rgb(250,128,114)
|
||||||
|
sandy_brown = 0xF4A460, // rgb(244,164,96)
|
||||||
|
sea_green = 0x2E8B57, // rgb(46,139,87)
|
||||||
|
sea_shell = 0xFFF5EE, // rgb(255,245,238)
|
||||||
|
sienna = 0xA0522D, // rgb(160,82,45)
|
||||||
|
silver = 0xC0C0C0, // rgb(192,192,192)
|
||||||
|
sky_blue = 0x87CEEB, // rgb(135,206,235)
|
||||||
|
slate_blue = 0x6A5ACD, // rgb(106,90,205)
|
||||||
|
slate_gray = 0x708090, // rgb(112,128,144)
|
||||||
|
snow = 0xFFFAFA, // rgb(255,250,250)
|
||||||
|
spring_green = 0x00FF7F, // rgb(0,255,127)
|
||||||
|
steel_blue = 0x4682B4, // rgb(70,130,180)
|
||||||
|
tan = 0xD2B48C, // rgb(210,180,140)
|
||||||
|
teal = 0x008080, // rgb(0,128,128)
|
||||||
|
thistle = 0xD8BFD8, // rgb(216,191,216)
|
||||||
|
tomato = 0xFF6347, // rgb(255,99,71)
|
||||||
|
turquoise = 0x40E0D0, // rgb(64,224,208)
|
||||||
|
violet = 0xEE82EE, // rgb(238,130,238)
|
||||||
|
wheat = 0xF5DEB3, // rgb(245,222,179)
|
||||||
|
white = 0xFFFFFF, // rgb(255,255,255)
|
||||||
|
white_smoke = 0xF5F5F5, // rgb(245,245,245)
|
||||||
|
yellow = 0xFFFF00, // rgb(255,255,0)
|
||||||
|
yellow_green = 0x9ACD32 // rgb(154,205,50)
|
||||||
|
}; // enum class color
|
||||||
|
|
||||||
|
enum class terminal_color : uint8_t {
|
||||||
|
black = 30,
|
||||||
|
red,
|
||||||
|
green,
|
||||||
|
yellow,
|
||||||
|
blue,
|
||||||
|
magenta,
|
||||||
|
cyan,
|
||||||
|
white,
|
||||||
|
bright_black = 90,
|
||||||
|
bright_red,
|
||||||
|
bright_green,
|
||||||
|
bright_yellow,
|
||||||
|
bright_blue,
|
||||||
|
bright_magenta,
|
||||||
|
bright_cyan,
|
||||||
|
bright_white
|
||||||
|
}; // enum class terminal_color
|
||||||
|
|
||||||
|
enum class emphasis : uint8_t {
|
||||||
|
bold = 1,
|
||||||
|
italic = 1 << 1,
|
||||||
|
underline = 1 << 2,
|
||||||
|
strikethrough = 1 << 3
|
||||||
|
}; // enum class emphasis
|
||||||
|
|
||||||
|
// rgb is a struct for red, green and blue colors.
|
||||||
|
// We use rgb as name because some editors will show it as color direct in the
|
||||||
|
// editor.
|
||||||
|
struct rgb {
|
||||||
|
FMT_CONSTEXPR_DECL rgb() : r(0), g(0), b(0) {}
|
||||||
|
FMT_CONSTEXPR_DECL rgb(uint8_t r_, uint8_t g_, uint8_t b_)
|
||||||
|
: r(r_), g(g_), b(b_) {}
|
||||||
|
FMT_CONSTEXPR_DECL rgb(uint32_t hex)
|
||||||
|
: r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b((hex) & 0xFF) {}
|
||||||
|
FMT_CONSTEXPR_DECL rgb(color hex)
|
||||||
|
: r((uint32_t(hex) >> 16) & 0xFF), g((uint32_t(hex) >> 8) & 0xFF),
|
||||||
|
b(uint32_t(hex) & 0xFF) {}
|
||||||
|
uint8_t r;
|
||||||
|
uint8_t g;
|
||||||
|
uint8_t b;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
// color is a struct of either a rgb color or a terminal color.
|
||||||
|
struct color_type {
|
||||||
|
FMT_CONSTEXPR color_type() FMT_NOEXCEPT
|
||||||
|
: is_rgb(), value{} {}
|
||||||
|
FMT_CONSTEXPR color_type(color rgb_color) FMT_NOEXCEPT
|
||||||
|
: is_rgb(true), value{} {
|
||||||
|
value.rgb_color = static_cast<uint32_t>(rgb_color);
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR color_type(rgb rgb_color) FMT_NOEXCEPT
|
||||||
|
: is_rgb(true), value{} {
|
||||||
|
value.rgb_color = (static_cast<uint32_t>(rgb_color.r) << 16)
|
||||||
|
| (static_cast<uint32_t>(rgb_color.g) << 8) | rgb_color.b;
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR color_type(terminal_color term_color) FMT_NOEXCEPT
|
||||||
|
: is_rgb(), value{} {
|
||||||
|
value.term_color = static_cast<uint8_t>(term_color);
|
||||||
|
}
|
||||||
|
bool is_rgb;
|
||||||
|
union color_union {
|
||||||
|
uint8_t term_color;
|
||||||
|
uint32_t rgb_color;
|
||||||
|
} value;
|
||||||
|
};
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
// Experimental text formatting support.
|
||||||
|
class text_style {
|
||||||
|
public:
|
||||||
|
FMT_CONSTEXPR text_style(emphasis em = emphasis()) FMT_NOEXCEPT
|
||||||
|
: set_foreground_color(), set_background_color(), ems(em) {}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR text_style &operator|=(const text_style &rhs) {
|
||||||
|
if (!set_foreground_color) {
|
||||||
|
set_foreground_color = rhs.set_foreground_color;
|
||||||
|
foreground_color = rhs.foreground_color;
|
||||||
|
} else if (rhs.set_foreground_color) {
|
||||||
|
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
|
||||||
|
throw format_error("can't OR a terminal color");
|
||||||
|
foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!set_background_color) {
|
||||||
|
set_background_color = rhs.set_background_color;
|
||||||
|
background_color = rhs.background_color;
|
||||||
|
} else if (rhs.set_background_color) {
|
||||||
|
if (!background_color.is_rgb || !rhs.background_color.is_rgb)
|
||||||
|
throw format_error("can't OR a terminal color");
|
||||||
|
background_color.value.rgb_color |= rhs.background_color.value.rgb_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
ems = static_cast<emphasis>(static_cast<uint8_t>(ems) |
|
||||||
|
static_cast<uint8_t>(rhs.ems));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend FMT_CONSTEXPR
|
||||||
|
text_style operator|(text_style lhs, const text_style &rhs) {
|
||||||
|
return lhs |= rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR text_style &operator&=(const text_style &rhs) {
|
||||||
|
if (!set_foreground_color) {
|
||||||
|
set_foreground_color = rhs.set_foreground_color;
|
||||||
|
foreground_color = rhs.foreground_color;
|
||||||
|
} else if (rhs.set_foreground_color) {
|
||||||
|
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
|
||||||
|
throw format_error("can't AND a terminal color");
|
||||||
|
foreground_color.value.rgb_color &= rhs.foreground_color.value.rgb_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!set_background_color) {
|
||||||
|
set_background_color = rhs.set_background_color;
|
||||||
|
background_color = rhs.background_color;
|
||||||
|
} else if (rhs.set_background_color) {
|
||||||
|
if (!background_color.is_rgb || !rhs.background_color.is_rgb)
|
||||||
|
throw format_error("can't AND a terminal color");
|
||||||
|
background_color.value.rgb_color &= rhs.background_color.value.rgb_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
ems = static_cast<emphasis>(static_cast<uint8_t>(ems) &
|
||||||
|
static_cast<uint8_t>(rhs.ems));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend FMT_CONSTEXPR
|
||||||
|
text_style operator&(text_style lhs, const text_style &rhs) {
|
||||||
|
return lhs &= rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR bool has_foreground() const FMT_NOEXCEPT {
|
||||||
|
return set_foreground_color;
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR bool has_background() const FMT_NOEXCEPT {
|
||||||
|
return set_background_color;
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR bool has_emphasis() const FMT_NOEXCEPT {
|
||||||
|
return static_cast<uint8_t>(ems) != 0;
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR internal::color_type get_foreground() const FMT_NOEXCEPT {
|
||||||
|
assert(has_foreground() && "no foreground specified for this style");
|
||||||
|
return foreground_color;
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR internal::color_type get_background() const FMT_NOEXCEPT {
|
||||||
|
assert(has_background() && "no background specified for this style");
|
||||||
|
return background_color;
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR emphasis get_emphasis() const FMT_NOEXCEPT {
|
||||||
|
assert(has_emphasis() && "no emphasis specified for this style");
|
||||||
|
return ems;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
FMT_CONSTEXPR text_style(bool is_foreground,
|
||||||
|
internal::color_type text_color) FMT_NOEXCEPT
|
||||||
|
: set_foreground_color(),
|
||||||
|
set_background_color(),
|
||||||
|
ems() {
|
||||||
|
if (is_foreground) {
|
||||||
|
foreground_color = text_color;
|
||||||
|
set_foreground_color = true;
|
||||||
|
} else {
|
||||||
|
background_color = text_color;
|
||||||
|
set_background_color = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
friend FMT_CONSTEXPR_DECL text_style fg(internal::color_type foreground)
|
||||||
|
FMT_NOEXCEPT;
|
||||||
|
friend FMT_CONSTEXPR_DECL text_style bg(internal::color_type background)
|
||||||
|
FMT_NOEXCEPT;
|
||||||
|
|
||||||
|
internal::color_type foreground_color;
|
||||||
|
internal::color_type background_color;
|
||||||
|
bool set_foreground_color;
|
||||||
|
bool set_background_color;
|
||||||
|
emphasis ems;
|
||||||
|
};
|
||||||
|
|
||||||
|
FMT_CONSTEXPR text_style fg(internal::color_type foreground) FMT_NOEXCEPT {
|
||||||
|
return text_style(/*is_foreground=*/true, foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR text_style bg(internal::color_type background) FMT_NOEXCEPT {
|
||||||
|
return text_style(/*is_foreground=*/false, background);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR text_style operator|(emphasis lhs, emphasis rhs) FMT_NOEXCEPT {
|
||||||
|
return text_style(lhs) | rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
struct ansi_color_escape {
|
||||||
|
FMT_CONSTEXPR ansi_color_escape(internal::color_type text_color,
|
||||||
|
const char * esc) FMT_NOEXCEPT {
|
||||||
|
// If we have a terminal color, we need to output another escape code
|
||||||
|
// sequence.
|
||||||
|
if (!text_color.is_rgb) {
|
||||||
|
bool is_background = esc == internal::data::BACKGROUND_COLOR;
|
||||||
|
uint32_t value = text_color.value.term_color;
|
||||||
|
// Background ASCII codes are the same as the foreground ones but with
|
||||||
|
// 10 more.
|
||||||
|
if (is_background)
|
||||||
|
value += 10u;
|
||||||
|
|
||||||
|
std::size_t index = 0;
|
||||||
|
buffer[index++] = static_cast<Char>('\x1b');
|
||||||
|
buffer[index++] = static_cast<Char>('[');
|
||||||
|
|
||||||
|
if (value >= 100u) {
|
||||||
|
buffer[index++] = static_cast<Char>('1');
|
||||||
|
value %= 100u;
|
||||||
|
}
|
||||||
|
buffer[index++] = static_cast<Char>('0' + value / 10u);
|
||||||
|
buffer[index++] = static_cast<Char>('0' + value % 10u);
|
||||||
|
|
||||||
|
buffer[index++] = static_cast<Char>('m');
|
||||||
|
buffer[index++] = static_cast<Char>('\0');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 7; i++) {
|
||||||
|
buffer[i] = static_cast<Char>(esc[i]);
|
||||||
|
}
|
||||||
|
rgb color(text_color.value.rgb_color);
|
||||||
|
to_esc(color.r, buffer + 7, ';');
|
||||||
|
to_esc(color.g, buffer + 11, ';');
|
||||||
|
to_esc(color.b, buffer + 15, 'm');
|
||||||
|
buffer[19] = static_cast<Char>(0);
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR ansi_color_escape(emphasis em) FMT_NOEXCEPT {
|
||||||
|
uint8_t em_codes[4] = {};
|
||||||
|
uint8_t em_bits = static_cast<uint8_t>(em);
|
||||||
|
if (em_bits & static_cast<uint8_t>(emphasis::bold))
|
||||||
|
em_codes[0] = 1;
|
||||||
|
if (em_bits & static_cast<uint8_t>(emphasis::italic))
|
||||||
|
em_codes[1] = 3;
|
||||||
|
if (em_bits & static_cast<uint8_t>(emphasis::underline))
|
||||||
|
em_codes[2] = 4;
|
||||||
|
if (em_bits & static_cast<uint8_t>(emphasis::strikethrough))
|
||||||
|
em_codes[3] = 9;
|
||||||
|
|
||||||
|
std::size_t index = 0;
|
||||||
|
for (int i = 0; i < 4; ++i) {
|
||||||
|
if (!em_codes[i])
|
||||||
|
continue;
|
||||||
|
buffer[index++] = static_cast<Char>('\x1b');
|
||||||
|
buffer[index++] = static_cast<Char>('[');
|
||||||
|
buffer[index++] = static_cast<Char>('0' + em_codes[i]);
|
||||||
|
buffer[index++] = static_cast<Char>('m');
|
||||||
|
}
|
||||||
|
buffer[index++] = static_cast<Char>(0);
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR operator const Char *() const FMT_NOEXCEPT { return buffer; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Char buffer[7u + 3u * 4u + 1u];
|
||||||
|
|
||||||
|
static FMT_CONSTEXPR void to_esc(uint8_t c, Char *out,
|
||||||
|
char delimiter) FMT_NOEXCEPT {
|
||||||
|
out[0] = static_cast<Char>('0' + c / 100);
|
||||||
|
out[1] = static_cast<Char>('0' + c / 10 % 10);
|
||||||
|
out[2] = static_cast<Char>('0' + c % 10);
|
||||||
|
out[3] = static_cast<Char>(delimiter);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
FMT_CONSTEXPR ansi_color_escape<Char>
|
||||||
|
make_foreground_color(internal::color_type foreground) FMT_NOEXCEPT {
|
||||||
|
return ansi_color_escape<Char>(foreground, internal::data::FOREGROUND_COLOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
FMT_CONSTEXPR ansi_color_escape<Char>
|
||||||
|
make_background_color(internal::color_type background) FMT_NOEXCEPT {
|
||||||
|
return ansi_color_escape<Char>(background, internal::data::BACKGROUND_COLOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
FMT_CONSTEXPR ansi_color_escape<Char>
|
||||||
|
make_emphasis(emphasis em) FMT_NOEXCEPT {
|
||||||
|
return ansi_color_escape<Char>(em);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
inline void fputs(const Char *chars, FILE *stream) FMT_NOEXCEPT {
|
||||||
|
std::fputs(chars, stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline void fputs<wchar_t>(const wchar_t *chars, FILE *stream) FMT_NOEXCEPT {
|
||||||
|
std::fputws(chars, stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
inline void reset_color(FILE *stream) FMT_NOEXCEPT {
|
||||||
|
fputs(internal::data::RESET_COLOR, stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline void reset_color<wchar_t>(FILE *stream) FMT_NOEXCEPT {
|
||||||
|
fputs(internal::data::WRESET_COLOR, stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The following specialiazation disables using std::FILE as a character type,
|
||||||
|
// which is needed because or else
|
||||||
|
// fmt::print(stderr, fmt::emphasis::bold, "");
|
||||||
|
// would take stderr (a std::FILE *) as the format string.
|
||||||
|
template <>
|
||||||
|
struct is_string<std::FILE *> : std::false_type {};
|
||||||
|
template <>
|
||||||
|
struct is_string<const std::FILE *> : std::false_type {};
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
template <
|
||||||
|
typename S, typename Char = typename internal::char_t<S>::type>
|
||||||
|
void vprint(std::FILE *f, const text_style &ts, const S &format,
|
||||||
|
basic_format_args<typename buffer_context<Char>::type> args) {
|
||||||
|
bool has_style = false;
|
||||||
|
if (ts.has_emphasis()) {
|
||||||
|
has_style = true;
|
||||||
|
internal::fputs<Char>(
|
||||||
|
internal::make_emphasis<Char>(ts.get_emphasis()), f);
|
||||||
|
}
|
||||||
|
if (ts.has_foreground()) {
|
||||||
|
has_style = true;
|
||||||
|
internal::fputs<Char>(
|
||||||
|
internal::make_foreground_color<Char>(ts.get_foreground()), f);
|
||||||
|
}
|
||||||
|
if (ts.has_background()) {
|
||||||
|
has_style = true;
|
||||||
|
internal::fputs<Char>(
|
||||||
|
internal::make_background_color<Char>(ts.get_background()), f);
|
||||||
|
}
|
||||||
|
vprint(f, format, args);
|
||||||
|
if (has_style) {
|
||||||
|
internal::reset_color<Char>(f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Formats a string and prints it to the specified file stream using ANSI
|
||||||
|
escape sequences to specify text formatting.
|
||||||
|
Example:
|
||||||
|
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
|
||||||
|
"Elapsed time: {0:.2f} seconds", 1.23);
|
||||||
|
*/
|
||||||
|
template <typename String, typename... Args>
|
||||||
|
typename std::enable_if<internal::is_string<String>::value>::type print(
|
||||||
|
std::FILE *f, const text_style &ts, const String &format_str,
|
||||||
|
const Args &... args) {
|
||||||
|
internal::check_format_string<Args...>(format_str);
|
||||||
|
typedef typename internal::char_t<String>::type char_t;
|
||||||
|
typedef typename buffer_context<char_t>::type context_t;
|
||||||
|
format_arg_store<context_t, Args...> as{args...};
|
||||||
|
vprint(f, ts, format_str, basic_format_args<context_t>(as));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Formats a string and prints it to stdout using ANSI escape sequences to
|
||||||
|
specify text formatting.
|
||||||
|
Example:
|
||||||
|
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
|
||||||
|
"Elapsed time: {0:.2f} seconds", 1.23);
|
||||||
|
*/
|
||||||
|
template <typename String, typename... Args>
|
||||||
|
typename std::enable_if<internal::is_string<String>::value>::type print(
|
||||||
|
const text_style &ts, const String &format_str,
|
||||||
|
const Args &... args) {
|
||||||
|
return print(stdout, ts, format_str, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // FMT_COLOR_H_
|
1502
lib/include/fmt/core.h
Normal file
1502
lib/include/fmt/core.h
Normal file
File diff suppressed because it is too large
Load Diff
972
lib/include/fmt/format-inl.h
Normal file
972
lib/include/fmt/format-inl.h
Normal file
@ -0,0 +1,972 @@
|
|||||||
|
// Formatting library for C++
|
||||||
|
//
|
||||||
|
// Copyright (c) 2012 - 2016, Victor Zverovich
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
|
#ifndef FMT_FORMAT_INL_H_
|
||||||
|
#define FMT_FORMAT_INL_H_
|
||||||
|
|
||||||
|
#include "format.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <cctype>
|
||||||
|
#include <cerrno>
|
||||||
|
#include <climits>
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstdarg>
|
||||||
|
#include <cstddef> // for std::ptrdiff_t
|
||||||
|
#include <cstring> // for std::memmove
|
||||||
|
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
|
||||||
|
# include <locale>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if FMT_USE_WINDOWS_H
|
||||||
|
# if !defined(FMT_HEADER_ONLY) && !defined(WIN32_LEAN_AND_MEAN)
|
||||||
|
# define WIN32_LEAN_AND_MEAN
|
||||||
|
# endif
|
||||||
|
# if defined(NOMINMAX) || defined(FMT_WIN_MINMAX)
|
||||||
|
# include <windows.h>
|
||||||
|
# else
|
||||||
|
# define NOMINMAX
|
||||||
|
# include <windows.h>
|
||||||
|
# undef NOMINMAX
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if FMT_EXCEPTIONS
|
||||||
|
# define FMT_TRY try
|
||||||
|
# define FMT_CATCH(x) catch (x)
|
||||||
|
#else
|
||||||
|
# define FMT_TRY if (true)
|
||||||
|
# define FMT_CATCH(x) if (false)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
# pragma warning(push)
|
||||||
|
# pragma warning(disable: 4127) // conditional expression is constant
|
||||||
|
# pragma warning(disable: 4702) // unreachable code
|
||||||
|
// Disable deprecation warning for strerror. The latter is not called but
|
||||||
|
// MSVC fails to detect it.
|
||||||
|
# pragma warning(disable: 4996)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Dummy implementations of strerror_r and strerror_s called if corresponding
|
||||||
|
// system functions are not available.
|
||||||
|
inline fmt::internal::null<> strerror_r(int, char *, ...) {
|
||||||
|
return fmt::internal::null<>();
|
||||||
|
}
|
||||||
|
inline fmt::internal::null<> strerror_s(char *, std::size_t, ...) {
|
||||||
|
return fmt::internal::null<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
#ifndef _MSC_VER
|
||||||
|
# define FMT_SNPRINTF snprintf
|
||||||
|
#else // _MSC_VER
|
||||||
|
inline int fmt_snprintf(char *buffer, size_t size, const char *format, ...) {
|
||||||
|
va_list args;
|
||||||
|
va_start(args, format);
|
||||||
|
int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args);
|
||||||
|
va_end(args);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
# define FMT_SNPRINTF fmt_snprintf
|
||||||
|
#endif // _MSC_VER
|
||||||
|
|
||||||
|
#if defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT)
|
||||||
|
# define FMT_SWPRINTF snwprintf
|
||||||
|
#else
|
||||||
|
# define FMT_SWPRINTF swprintf
|
||||||
|
#endif // defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT)
|
||||||
|
|
||||||
|
typedef void (*FormatFunc)(internal::buffer &, int, string_view);
|
||||||
|
|
||||||
|
// Portable thread-safe version of strerror.
|
||||||
|
// Sets buffer to point to a string describing the error code.
|
||||||
|
// This can be either a pointer to a string stored in buffer,
|
||||||
|
// or a pointer to some static immutable string.
|
||||||
|
// Returns one of the following values:
|
||||||
|
// 0 - success
|
||||||
|
// ERANGE - buffer is not large enough to store the error message
|
||||||
|
// other - failure
|
||||||
|
// Buffer should be at least of size 1.
|
||||||
|
int safe_strerror(
|
||||||
|
int error_code, char *&buffer, std::size_t buffer_size) FMT_NOEXCEPT {
|
||||||
|
FMT_ASSERT(buffer != FMT_NULL && buffer_size != 0, "invalid buffer");
|
||||||
|
|
||||||
|
class dispatcher {
|
||||||
|
private:
|
||||||
|
int error_code_;
|
||||||
|
char *&buffer_;
|
||||||
|
std::size_t buffer_size_;
|
||||||
|
|
||||||
|
// A noop assignment operator to avoid bogus warnings.
|
||||||
|
void operator=(const dispatcher &) {}
|
||||||
|
|
||||||
|
// Handle the result of XSI-compliant version of strerror_r.
|
||||||
|
int handle(int result) {
|
||||||
|
// glibc versions before 2.13 return result in errno.
|
||||||
|
return result == -1 ? errno : result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle the result of GNU-specific version of strerror_r.
|
||||||
|
int handle(char *message) {
|
||||||
|
// If the buffer is full then the message is probably truncated.
|
||||||
|
if (message == buffer_ && strlen(buffer_) == buffer_size_ - 1)
|
||||||
|
return ERANGE;
|
||||||
|
buffer_ = message;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle the case when strerror_r is not available.
|
||||||
|
int handle(internal::null<>) {
|
||||||
|
return fallback(strerror_s(buffer_, buffer_size_, error_code_));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to strerror_s when strerror_r is not available.
|
||||||
|
int fallback(int result) {
|
||||||
|
// If the buffer is full then the message is probably truncated.
|
||||||
|
return result == 0 && strlen(buffer_) == buffer_size_ - 1 ?
|
||||||
|
ERANGE : result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !FMT_MSC_VER
|
||||||
|
// Fallback to strerror if strerror_r and strerror_s are not available.
|
||||||
|
int fallback(internal::null<>) {
|
||||||
|
errno = 0;
|
||||||
|
buffer_ = strerror(error_code_);
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public:
|
||||||
|
dispatcher(int err_code, char *&buf, std::size_t buf_size)
|
||||||
|
: error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {}
|
||||||
|
|
||||||
|
int run() {
|
||||||
|
return handle(strerror_r(error_code_, buffer_, buffer_size_));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return dispatcher(error_code, buffer, buffer_size).run();
|
||||||
|
}
|
||||||
|
|
||||||
|
void format_error_code(internal::buffer &out, int error_code,
|
||||||
|
string_view message) FMT_NOEXCEPT {
|
||||||
|
// Report error code making sure that the output fits into
|
||||||
|
// inline_buffer_size to avoid dynamic memory allocation and potential
|
||||||
|
// bad_alloc.
|
||||||
|
out.resize(0);
|
||||||
|
static const char SEP[] = ": ";
|
||||||
|
static const char ERROR_STR[] = "error ";
|
||||||
|
// Subtract 2 to account for terminating null characters in SEP and ERROR_STR.
|
||||||
|
std::size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2;
|
||||||
|
typedef internal::int_traits<int>::main_type main_type;
|
||||||
|
main_type abs_value = static_cast<main_type>(error_code);
|
||||||
|
if (internal::is_negative(error_code)) {
|
||||||
|
abs_value = 0 - abs_value;
|
||||||
|
++error_code_size;
|
||||||
|
}
|
||||||
|
error_code_size += internal::to_unsigned(internal::count_digits(abs_value));
|
||||||
|
writer w(out);
|
||||||
|
if (message.size() <= inline_buffer_size - error_code_size) {
|
||||||
|
w.write(message);
|
||||||
|
w.write(SEP);
|
||||||
|
}
|
||||||
|
w.write(ERROR_STR);
|
||||||
|
w.write(error_code);
|
||||||
|
assert(out.size() <= inline_buffer_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void report_error(FormatFunc func, int error_code,
|
||||||
|
string_view message) FMT_NOEXCEPT {
|
||||||
|
memory_buffer full_message;
|
||||||
|
func(full_message, error_code, message);
|
||||||
|
// Use Writer::data instead of Writer::c_str to avoid potential memory
|
||||||
|
// allocation.
|
||||||
|
std::fwrite(full_message.data(), full_message.size(), 1, stderr);
|
||||||
|
std::fputc('\n', stderr);
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
FMT_FUNC size_t internal::count_code_points(basic_string_view<char8_t> s) {
|
||||||
|
const char8_t *data = s.data();
|
||||||
|
size_t num_code_points = 0;
|
||||||
|
for (size_t i = 0, size = s.size(); i != size; ++i) {
|
||||||
|
if ((data[i] & 0xc0) != 0x80)
|
||||||
|
++num_code_points;
|
||||||
|
}
|
||||||
|
return num_code_points;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
template <typename Locale>
|
||||||
|
locale_ref::locale_ref(const Locale &loc) : locale_(&loc) {
|
||||||
|
static_assert(std::is_same<Locale, std::locale>::value, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Locale>
|
||||||
|
Locale locale_ref::get() const {
|
||||||
|
static_assert(std::is_same<Locale, std::locale>::value, "");
|
||||||
|
return locale_ ? *static_cast<const std::locale*>(locale_) : std::locale();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
FMT_FUNC Char thousands_sep_impl(locale_ref loc) {
|
||||||
|
return std::use_facet<std::numpunct<Char> >(
|
||||||
|
loc.get<std::locale>()).thousands_sep();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
template <typename Char>
|
||||||
|
FMT_FUNC Char internal::thousands_sep_impl(locale_ref) {
|
||||||
|
return FMT_STATIC_THOUSANDS_SEPARATOR;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
FMT_FUNC void system_error::init(
|
||||||
|
int err_code, string_view format_str, format_args args) {
|
||||||
|
error_code_ = err_code;
|
||||||
|
memory_buffer buffer;
|
||||||
|
format_system_error(buffer, err_code, vformat(format_str, args));
|
||||||
|
std::runtime_error &base = *this;
|
||||||
|
base = std::runtime_error(to_string(buffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
template <typename T>
|
||||||
|
int char_traits<char>::format_float(
|
||||||
|
char *buf, std::size_t size, const char *format, int precision, T value) {
|
||||||
|
return precision < 0 ?
|
||||||
|
FMT_SNPRINTF(buf, size, format, value) :
|
||||||
|
FMT_SNPRINTF(buf, size, format, precision, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
int char_traits<wchar_t>::format_float(
|
||||||
|
wchar_t *buf, std::size_t size, const wchar_t *format, int precision,
|
||||||
|
T value) {
|
||||||
|
return precision < 0 ?
|
||||||
|
FMT_SWPRINTF(buf, size, format, value) :
|
||||||
|
FMT_SWPRINTF(buf, size, format, precision, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
const char basic_data<T>::DIGITS[] =
|
||||||
|
"0001020304050607080910111213141516171819"
|
||||||
|
"2021222324252627282930313233343536373839"
|
||||||
|
"4041424344454647484950515253545556575859"
|
||||||
|
"6061626364656667686970717273747576777879"
|
||||||
|
"8081828384858687888990919293949596979899";
|
||||||
|
|
||||||
|
#define FMT_POWERS_OF_10(factor) \
|
||||||
|
factor * 10, \
|
||||||
|
factor * 100, \
|
||||||
|
factor * 1000, \
|
||||||
|
factor * 10000, \
|
||||||
|
factor * 100000, \
|
||||||
|
factor * 1000000, \
|
||||||
|
factor * 10000000, \
|
||||||
|
factor * 100000000, \
|
||||||
|
factor * 1000000000
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
const uint32_t basic_data<T>::POWERS_OF_10_32[] = {
|
||||||
|
1, FMT_POWERS_OF_10(1)
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
const uint32_t basic_data<T>::ZERO_OR_POWERS_OF_10_32[] = {
|
||||||
|
0, FMT_POWERS_OF_10(1)
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
const uint64_t basic_data<T>::ZERO_OR_POWERS_OF_10_64[] = {
|
||||||
|
0,
|
||||||
|
FMT_POWERS_OF_10(1),
|
||||||
|
FMT_POWERS_OF_10(1000000000ull),
|
||||||
|
10000000000000000000ull
|
||||||
|
};
|
||||||
|
|
||||||
|
// Normalized 64-bit significands of pow(10, k), for k = -348, -340, ..., 340.
|
||||||
|
// These are generated by support/compute-powers.py.
|
||||||
|
template <typename T>
|
||||||
|
const uint64_t basic_data<T>::POW10_SIGNIFICANDS[] = {
|
||||||
|
0xfa8fd5a0081c0288, 0xbaaee17fa23ebf76, 0x8b16fb203055ac76,
|
||||||
|
0xcf42894a5dce35ea, 0x9a6bb0aa55653b2d, 0xe61acf033d1a45df,
|
||||||
|
0xab70fe17c79ac6ca, 0xff77b1fcbebcdc4f, 0xbe5691ef416bd60c,
|
||||||
|
0x8dd01fad907ffc3c, 0xd3515c2831559a83, 0x9d71ac8fada6c9b5,
|
||||||
|
0xea9c227723ee8bcb, 0xaecc49914078536d, 0x823c12795db6ce57,
|
||||||
|
0xc21094364dfb5637, 0x9096ea6f3848984f, 0xd77485cb25823ac7,
|
||||||
|
0xa086cfcd97bf97f4, 0xef340a98172aace5, 0xb23867fb2a35b28e,
|
||||||
|
0x84c8d4dfd2c63f3b, 0xc5dd44271ad3cdba, 0x936b9fcebb25c996,
|
||||||
|
0xdbac6c247d62a584, 0xa3ab66580d5fdaf6, 0xf3e2f893dec3f126,
|
||||||
|
0xb5b5ada8aaff80b8, 0x87625f056c7c4a8b, 0xc9bcff6034c13053,
|
||||||
|
0x964e858c91ba2655, 0xdff9772470297ebd, 0xa6dfbd9fb8e5b88f,
|
||||||
|
0xf8a95fcf88747d94, 0xb94470938fa89bcf, 0x8a08f0f8bf0f156b,
|
||||||
|
0xcdb02555653131b6, 0x993fe2c6d07b7fac, 0xe45c10c42a2b3b06,
|
||||||
|
0xaa242499697392d3, 0xfd87b5f28300ca0e, 0xbce5086492111aeb,
|
||||||
|
0x8cbccc096f5088cc, 0xd1b71758e219652c, 0x9c40000000000000,
|
||||||
|
0xe8d4a51000000000, 0xad78ebc5ac620000, 0x813f3978f8940984,
|
||||||
|
0xc097ce7bc90715b3, 0x8f7e32ce7bea5c70, 0xd5d238a4abe98068,
|
||||||
|
0x9f4f2726179a2245, 0xed63a231d4c4fb27, 0xb0de65388cc8ada8,
|
||||||
|
0x83c7088e1aab65db, 0xc45d1df942711d9a, 0x924d692ca61be758,
|
||||||
|
0xda01ee641a708dea, 0xa26da3999aef774a, 0xf209787bb47d6b85,
|
||||||
|
0xb454e4a179dd1877, 0x865b86925b9bc5c2, 0xc83553c5c8965d3d,
|
||||||
|
0x952ab45cfa97a0b3, 0xde469fbd99a05fe3, 0xa59bc234db398c25,
|
||||||
|
0xf6c69a72a3989f5c, 0xb7dcbf5354e9bece, 0x88fcf317f22241e2,
|
||||||
|
0xcc20ce9bd35c78a5, 0x98165af37b2153df, 0xe2a0b5dc971f303a,
|
||||||
|
0xa8d9d1535ce3b396, 0xfb9b7cd9a4a7443c, 0xbb764c4ca7a44410,
|
||||||
|
0x8bab8eefb6409c1a, 0xd01fef10a657842c, 0x9b10a4e5e9913129,
|
||||||
|
0xe7109bfba19c0c9d, 0xac2820d9623bf429, 0x80444b5e7aa7cf85,
|
||||||
|
0xbf21e44003acdd2d, 0x8e679c2f5e44ff8f, 0xd433179d9c8cb841,
|
||||||
|
0x9e19db92b4e31ba9, 0xeb96bf6ebadf77d9, 0xaf87023b9bf0ee6b,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Binary exponents of pow(10, k), for k = -348, -340, ..., 340, corresponding
|
||||||
|
// to significands above.
|
||||||
|
template <typename T>
|
||||||
|
const int16_t basic_data<T>::POW10_EXPONENTS[] = {
|
||||||
|
-1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, -954,
|
||||||
|
-927, -901, -874, -847, -821, -794, -768, -741, -715, -688, -661,
|
||||||
|
-635, -608, -582, -555, -529, -502, -475, -449, -422, -396, -369,
|
||||||
|
-343, -316, -289, -263, -236, -210, -183, -157, -130, -103, -77,
|
||||||
|
-50, -24, 3, 30, 56, 83, 109, 136, 162, 189, 216,
|
||||||
|
242, 269, 295, 322, 348, 375, 402, 428, 455, 481, 508,
|
||||||
|
534, 561, 588, 614, 641, 667, 694, 720, 747, 774, 800,
|
||||||
|
827, 853, 880, 907, 933, 960, 986, 1013, 1039, 1066
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T> const char basic_data<T>::FOREGROUND_COLOR[] = "\x1b[38;2;";
|
||||||
|
template <typename T> const char basic_data<T>::BACKGROUND_COLOR[] = "\x1b[48;2;";
|
||||||
|
template <typename T> const char basic_data<T>::RESET_COLOR[] = "\x1b[0m";
|
||||||
|
template <typename T> const wchar_t basic_data<T>::WRESET_COLOR[] = L"\x1b[0m";
|
||||||
|
|
||||||
|
// A handmade floating-point number f * pow(2, e).
|
||||||
|
class fp {
|
||||||
|
private:
|
||||||
|
typedef uint64_t significand_type;
|
||||||
|
|
||||||
|
// All sizes are in bits.
|
||||||
|
static FMT_CONSTEXPR_DECL const int char_size =
|
||||||
|
std::numeric_limits<unsigned char>::digits;
|
||||||
|
// Subtract 1 to account for an implicit most significant bit in the
|
||||||
|
// normalized form.
|
||||||
|
static FMT_CONSTEXPR_DECL const int double_significand_size =
|
||||||
|
std::numeric_limits<double>::digits - 1;
|
||||||
|
static FMT_CONSTEXPR_DECL const uint64_t implicit_bit =
|
||||||
|
1ull << double_significand_size;
|
||||||
|
|
||||||
|
public:
|
||||||
|
significand_type f;
|
||||||
|
int e;
|
||||||
|
|
||||||
|
static FMT_CONSTEXPR_DECL const int significand_size =
|
||||||
|
sizeof(significand_type) * char_size;
|
||||||
|
|
||||||
|
fp(): f(0), e(0) {}
|
||||||
|
fp(uint64_t f_val, int e_val): f(f_val), e(e_val) {}
|
||||||
|
|
||||||
|
// Constructs fp from an IEEE754 double. It is a template to prevent compile
|
||||||
|
// errors on platforms where double is not IEEE754.
|
||||||
|
template <typename Double>
|
||||||
|
explicit fp(Double d) {
|
||||||
|
// Assume double is in the format [sign][exponent][significand].
|
||||||
|
typedef std::numeric_limits<Double> limits;
|
||||||
|
const int double_size = static_cast<int>(sizeof(Double) * char_size);
|
||||||
|
const int exponent_size =
|
||||||
|
double_size - double_significand_size - 1; // -1 for sign
|
||||||
|
const uint64_t significand_mask = implicit_bit - 1;
|
||||||
|
const uint64_t exponent_mask = (~0ull >> 1) & ~significand_mask;
|
||||||
|
const int exponent_bias = (1 << exponent_size) - limits::max_exponent - 1;
|
||||||
|
auto u = bit_cast<uint64_t>(d);
|
||||||
|
auto biased_e = (u & exponent_mask) >> double_significand_size;
|
||||||
|
f = u & significand_mask;
|
||||||
|
if (biased_e != 0)
|
||||||
|
f += implicit_bit;
|
||||||
|
else
|
||||||
|
biased_e = 1; // Subnormals use biased exponent 1 (min exponent).
|
||||||
|
e = static_cast<int>(biased_e - exponent_bias - double_significand_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalizes the value converted from double and multiplied by (1 << SHIFT).
|
||||||
|
template <int SHIFT = 0>
|
||||||
|
void normalize() {
|
||||||
|
// Handle subnormals.
|
||||||
|
auto shifted_implicit_bit = implicit_bit << SHIFT;
|
||||||
|
while ((f & shifted_implicit_bit) == 0) {
|
||||||
|
f <<= 1;
|
||||||
|
--e;
|
||||||
|
}
|
||||||
|
// Subtract 1 to account for hidden bit.
|
||||||
|
auto offset = significand_size - double_significand_size - SHIFT - 1;
|
||||||
|
f <<= offset;
|
||||||
|
e -= offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute lower and upper boundaries (m^- and m^+ in the Grisu paper), where
|
||||||
|
// a boundary is a value half way between the number and its predecessor
|
||||||
|
// (lower) or successor (upper). The upper boundary is normalized and lower
|
||||||
|
// has the same exponent but may be not normalized.
|
||||||
|
void compute_boundaries(fp &lower, fp &upper) const {
|
||||||
|
lower = f == implicit_bit ?
|
||||||
|
fp((f << 2) - 1, e - 2) : fp((f << 1) - 1, e - 1);
|
||||||
|
upper = fp((f << 1) + 1, e - 1);
|
||||||
|
upper.normalize<1>(); // 1 is to account for the exponent shift above.
|
||||||
|
lower.f <<= lower.e - upper.e;
|
||||||
|
lower.e = upper.e;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Returns an fp number representing x - y. Result may not be normalized.
|
||||||
|
inline fp operator-(fp x, fp y) {
|
||||||
|
FMT_ASSERT(x.f >= y.f && x.e == y.e, "invalid operands");
|
||||||
|
return fp(x.f - y.f, x.e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Computes an fp number r with r.f = x.f * y.f / pow(2, 64) rounded to nearest
|
||||||
|
// with half-up tie breaking, r.e = x.e + y.e + 64. Result may not be normalized.
|
||||||
|
FMT_API fp operator*(fp x, fp y);
|
||||||
|
|
||||||
|
// Returns cached power (of 10) c_k = c_k.f * pow(2, c_k.e) such that its
|
||||||
|
// (binary) exponent satisfies min_exponent <= c_k.e <= min_exponent + 3.
|
||||||
|
FMT_API fp get_cached_power(int min_exponent, int &pow10_exponent);
|
||||||
|
|
||||||
|
FMT_FUNC fp operator*(fp x, fp y) {
|
||||||
|
// Multiply 32-bit parts of significands.
|
||||||
|
uint64_t mask = (1ULL << 32) - 1;
|
||||||
|
uint64_t a = x.f >> 32, b = x.f & mask;
|
||||||
|
uint64_t c = y.f >> 32, d = y.f & mask;
|
||||||
|
uint64_t ac = a * c, bc = b * c, ad = a * d, bd = b * d;
|
||||||
|
// Compute mid 64-bit of result and round.
|
||||||
|
uint64_t mid = (bd >> 32) + (ad & mask) + (bc & mask) + (1U << 31);
|
||||||
|
return fp(ac + (ad >> 32) + (bc >> 32) + (mid >> 32), x.e + y.e + 64);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_FUNC fp get_cached_power(int min_exponent, int &pow10_exponent) {
|
||||||
|
const double one_over_log2_10 = 0.30102999566398114; // 1 / log2(10)
|
||||||
|
int index = static_cast<int>(std::ceil(
|
||||||
|
(min_exponent + fp::significand_size - 1) * one_over_log2_10));
|
||||||
|
// Decimal exponent of the first (smallest) cached power of 10.
|
||||||
|
const int first_dec_exp = -348;
|
||||||
|
// Difference between 2 consecutive decimal exponents in cached powers of 10.
|
||||||
|
const int dec_exp_step = 8;
|
||||||
|
index = (index - first_dec_exp - 1) / dec_exp_step + 1;
|
||||||
|
pow10_exponent = first_dec_exp + index * dec_exp_step;
|
||||||
|
return fp(data::POW10_SIGNIFICANDS[index], data::POW10_EXPONENTS[index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_FUNC bool grisu2_round(
|
||||||
|
char *buf, int &size, int max_digits, uint64_t delta,
|
||||||
|
uint64_t remainder, uint64_t exp, uint64_t diff, int &exp10) {
|
||||||
|
while (remainder < diff && delta - remainder >= exp &&
|
||||||
|
(remainder + exp < diff || diff - remainder > remainder + exp - diff)) {
|
||||||
|
--buf[size - 1];
|
||||||
|
remainder += exp;
|
||||||
|
}
|
||||||
|
if (size > max_digits) {
|
||||||
|
--size;
|
||||||
|
++exp10;
|
||||||
|
if (buf[size] >= '5')
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generates output using Grisu2 digit-gen algorithm.
|
||||||
|
FMT_FUNC bool grisu2_gen_digits(
|
||||||
|
char *buf, int &size, uint32_t hi, uint64_t lo, int &exp,
|
||||||
|
uint64_t delta, const fp &one, const fp &diff, int max_digits) {
|
||||||
|
// Generate digits for the most significant part (hi).
|
||||||
|
while (exp > 0) {
|
||||||
|
uint32_t digit = 0;
|
||||||
|
// This optimization by miloyip reduces the number of integer divisions by
|
||||||
|
// one per iteration.
|
||||||
|
switch (exp) {
|
||||||
|
case 10: digit = hi / 1000000000; hi %= 1000000000; break;
|
||||||
|
case 9: digit = hi / 100000000; hi %= 100000000; break;
|
||||||
|
case 8: digit = hi / 10000000; hi %= 10000000; break;
|
||||||
|
case 7: digit = hi / 1000000; hi %= 1000000; break;
|
||||||
|
case 6: digit = hi / 100000; hi %= 100000; break;
|
||||||
|
case 5: digit = hi / 10000; hi %= 10000; break;
|
||||||
|
case 4: digit = hi / 1000; hi %= 1000; break;
|
||||||
|
case 3: digit = hi / 100; hi %= 100; break;
|
||||||
|
case 2: digit = hi / 10; hi %= 10; break;
|
||||||
|
case 1: digit = hi; hi = 0; break;
|
||||||
|
default:
|
||||||
|
FMT_ASSERT(false, "invalid number of digits");
|
||||||
|
}
|
||||||
|
if (digit != 0 || size != 0)
|
||||||
|
buf[size++] = static_cast<char>('0' + digit);
|
||||||
|
--exp;
|
||||||
|
uint64_t remainder = (static_cast<uint64_t>(hi) << -one.e) + lo;
|
||||||
|
if (remainder <= delta || size > max_digits) {
|
||||||
|
return grisu2_round(
|
||||||
|
buf, size, max_digits, delta, remainder,
|
||||||
|
static_cast<uint64_t>(data::POWERS_OF_10_32[exp]) << -one.e,
|
||||||
|
diff.f, exp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Generate digits for the least significant part (lo).
|
||||||
|
for (;;) {
|
||||||
|
lo *= 10;
|
||||||
|
delta *= 10;
|
||||||
|
char digit = static_cast<char>(lo >> -one.e);
|
||||||
|
if (digit != 0 || size != 0)
|
||||||
|
buf[size++] = static_cast<char>('0' + digit);
|
||||||
|
lo &= one.f - 1;
|
||||||
|
--exp;
|
||||||
|
if (lo < delta || size > max_digits) {
|
||||||
|
return grisu2_round(buf, size, max_digits, delta, lo, one.f,
|
||||||
|
diff.f * data::POWERS_OF_10_32[-exp], exp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if FMT_CLANG_VERSION
|
||||||
|
# define FMT_FALLTHROUGH [[clang::fallthrough]];
|
||||||
|
#elif FMT_GCC_VERSION >= 700
|
||||||
|
# define FMT_FALLTHROUGH [[gnu::fallthrough]];
|
||||||
|
#else
|
||||||
|
# define FMT_FALLTHROUGH
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct gen_digits_params {
|
||||||
|
int num_digits;
|
||||||
|
bool fixed;
|
||||||
|
bool upper;
|
||||||
|
bool trailing_zeros;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct prettify_handler {
|
||||||
|
char *data;
|
||||||
|
ptrdiff_t size;
|
||||||
|
buffer &buf;
|
||||||
|
|
||||||
|
explicit prettify_handler(buffer &b, ptrdiff_t n)
|
||||||
|
: data(b.data()), size(n), buf(b) {}
|
||||||
|
~prettify_handler() {
|
||||||
|
assert(buf.size() >= to_unsigned(size));
|
||||||
|
buf.resize(to_unsigned(size));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
void insert(ptrdiff_t pos, ptrdiff_t n, F f) {
|
||||||
|
std::memmove(data + pos + n, data + pos, to_unsigned(size - pos));
|
||||||
|
f(data + pos);
|
||||||
|
size += n;
|
||||||
|
}
|
||||||
|
|
||||||
|
void insert(ptrdiff_t pos, char c) {
|
||||||
|
std::memmove(data + pos + 1, data + pos, to_unsigned(size - pos));
|
||||||
|
data[pos] = c;
|
||||||
|
++size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void append(ptrdiff_t n, char c) {
|
||||||
|
std::uninitialized_fill_n(data + size, n, c);
|
||||||
|
size += n;
|
||||||
|
}
|
||||||
|
|
||||||
|
void append(char c) { data[size++] = c; }
|
||||||
|
|
||||||
|
void remove_trailing(char c) {
|
||||||
|
while (data[size - 1] == c) --size;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Writes the exponent exp in the form "[+-]d{2,3}" to buffer.
|
||||||
|
template <typename Handler>
|
||||||
|
FMT_FUNC void write_exponent(int exp, Handler &&h) {
|
||||||
|
FMT_ASSERT(-1000 < exp && exp < 1000, "exponent out of range");
|
||||||
|
if (exp < 0) {
|
||||||
|
h.append('-');
|
||||||
|
exp = -exp;
|
||||||
|
} else {
|
||||||
|
h.append('+');
|
||||||
|
}
|
||||||
|
if (exp >= 100) {
|
||||||
|
h.append(static_cast<char>('0' + exp / 100));
|
||||||
|
exp %= 100;
|
||||||
|
const char *d = data::DIGITS + exp * 2;
|
||||||
|
h.append(d[0]);
|
||||||
|
h.append(d[1]);
|
||||||
|
} else {
|
||||||
|
const char *d = data::DIGITS + exp * 2;
|
||||||
|
h.append(d[0]);
|
||||||
|
h.append(d[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct fill {
|
||||||
|
size_t n;
|
||||||
|
void operator()(char *buf) const {
|
||||||
|
buf[0] = '0';
|
||||||
|
buf[1] = '.';
|
||||||
|
std::uninitialized_fill_n(buf + 2, n, '0');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// The number is given as v = f * pow(10, exp), where f has size digits.
|
||||||
|
template <typename Handler>
|
||||||
|
FMT_FUNC void grisu2_prettify(const gen_digits_params ¶ms,
|
||||||
|
int size, int exp, Handler &&handler) {
|
||||||
|
if (!params.fixed) {
|
||||||
|
// Insert a decimal point after the first digit and add an exponent.
|
||||||
|
handler.insert(1, '.');
|
||||||
|
exp += size - 1;
|
||||||
|
if (size < params.num_digits)
|
||||||
|
handler.append(params.num_digits - size, '0');
|
||||||
|
handler.append(params.upper ? 'E' : 'e');
|
||||||
|
write_exponent(exp, handler);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// pow(10, full_exp - 1) <= v <= pow(10, full_exp).
|
||||||
|
int full_exp = size + exp;
|
||||||
|
const int exp_threshold = 21;
|
||||||
|
if (size <= full_exp && full_exp <= exp_threshold) {
|
||||||
|
// 1234e7 -> 12340000000[.0+]
|
||||||
|
handler.append(full_exp - size, '0');
|
||||||
|
int num_zeros = params.num_digits - full_exp;
|
||||||
|
if (num_zeros > 0 && params.trailing_zeros) {
|
||||||
|
handler.append('.');
|
||||||
|
handler.append(num_zeros, '0');
|
||||||
|
}
|
||||||
|
} else if (full_exp > 0) {
|
||||||
|
// 1234e-2 -> 12.34[0+]
|
||||||
|
handler.insert(full_exp, '.');
|
||||||
|
if (!params.trailing_zeros) {
|
||||||
|
// Remove trailing zeros.
|
||||||
|
handler.remove_trailing('0');
|
||||||
|
} else if (params.num_digits > size) {
|
||||||
|
// Add trailing zeros.
|
||||||
|
ptrdiff_t num_zeros = params.num_digits - size;
|
||||||
|
handler.append(num_zeros, '0');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 1234e-6 -> 0.001234
|
||||||
|
handler.insert(0, 2 - full_exp, fill{to_unsigned(-full_exp)});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct char_counter {
|
||||||
|
ptrdiff_t size;
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
void insert(ptrdiff_t, ptrdiff_t n, F) { size += n; }
|
||||||
|
void insert(ptrdiff_t, char) { ++size; }
|
||||||
|
void append(ptrdiff_t n, char) { size += n; }
|
||||||
|
void append(char) { ++size; }
|
||||||
|
void remove_trailing(char) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Converts format specifiers into parameters for digit generation and computes
|
||||||
|
// output buffer size for a number in the range [pow(10, exp - 1), pow(10, exp)
|
||||||
|
// or 0 if exp == 1.
|
||||||
|
FMT_FUNC gen_digits_params process_specs(const core_format_specs &specs,
|
||||||
|
int exp, buffer &buf) {
|
||||||
|
auto params = gen_digits_params();
|
||||||
|
int num_digits = specs.precision >= 0 ? specs.precision : 6;
|
||||||
|
switch (specs.type) {
|
||||||
|
case 'G':
|
||||||
|
params.upper = true;
|
||||||
|
FMT_FALLTHROUGH
|
||||||
|
case '\0': case 'g':
|
||||||
|
params.trailing_zeros = (specs.flags & HASH_FLAG) != 0;
|
||||||
|
if (-4 <= exp && exp < num_digits + 1) {
|
||||||
|
params.fixed = true;
|
||||||
|
if (!specs.type && params.trailing_zeros && exp >= 0)
|
||||||
|
num_digits = exp + 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'F':
|
||||||
|
params.upper = true;
|
||||||
|
FMT_FALLTHROUGH
|
||||||
|
case 'f': {
|
||||||
|
params.fixed = true;
|
||||||
|
params.trailing_zeros = true;
|
||||||
|
int adjusted_min_digits = num_digits + exp;
|
||||||
|
if (adjusted_min_digits > 0)
|
||||||
|
num_digits = adjusted_min_digits;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'E':
|
||||||
|
params.upper = true;
|
||||||
|
FMT_FALLTHROUGH
|
||||||
|
case 'e':
|
||||||
|
++num_digits;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
params.num_digits = num_digits;
|
||||||
|
char_counter counter{num_digits};
|
||||||
|
grisu2_prettify(params, params.num_digits, exp - num_digits, counter);
|
||||||
|
buf.resize(to_unsigned(counter.size));
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Double>
|
||||||
|
FMT_FUNC typename std::enable_if<sizeof(Double) == sizeof(uint64_t), bool>::type
|
||||||
|
grisu2_format(Double value, buffer &buf, core_format_specs specs) {
|
||||||
|
FMT_ASSERT(value >= 0, "value is negative");
|
||||||
|
if (value == 0) {
|
||||||
|
gen_digits_params params = process_specs(specs, 1, buf);
|
||||||
|
const size_t size = 1;
|
||||||
|
buf[0] = '0';
|
||||||
|
grisu2_prettify(params, size, 0, prettify_handler(buf, size));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fp fp_value(value);
|
||||||
|
fp lower, upper; // w^- and w^+ in the Grisu paper.
|
||||||
|
fp_value.compute_boundaries(lower, upper);
|
||||||
|
|
||||||
|
// Find a cached power of 10 close to 1 / upper and use it to scale upper.
|
||||||
|
const int min_exp = -60; // alpha in Grisu.
|
||||||
|
int cached_exp = 0; // K in Grisu.
|
||||||
|
auto cached_pow = get_cached_power( // \tilde{c}_{-k} in Grisu.
|
||||||
|
min_exp - (upper.e + fp::significand_size), cached_exp);
|
||||||
|
cached_exp = -cached_exp;
|
||||||
|
upper = upper * cached_pow; // \tilde{M}^+ in Grisu.
|
||||||
|
--upper.f; // \tilde{M}^+ - 1 ulp -> M^+_{\downarrow}.
|
||||||
|
fp one(1ull << -upper.e, upper.e);
|
||||||
|
// hi (p1 in Grisu) contains the most significant digits of scaled_upper.
|
||||||
|
// hi = floor(upper / one).
|
||||||
|
uint32_t hi = static_cast<uint32_t>(upper.f >> -one.e);
|
||||||
|
int exp = count_digits(hi); // kappa in Grisu.
|
||||||
|
gen_digits_params params = process_specs(specs, cached_exp + exp, buf);
|
||||||
|
fp_value.normalize();
|
||||||
|
fp scaled_value = fp_value * cached_pow;
|
||||||
|
lower = lower * cached_pow; // \tilde{M}^- in Grisu.
|
||||||
|
++lower.f; // \tilde{M}^- + 1 ulp -> M^-_{\uparrow}.
|
||||||
|
uint64_t delta = upper.f - lower.f;
|
||||||
|
fp diff = upper - scaled_value; // wp_w in Grisu.
|
||||||
|
// lo (p2 in Grisu) contains the least significants digits of scaled_upper.
|
||||||
|
// lo = supper % one.
|
||||||
|
uint64_t lo = upper.f & (one.f - 1);
|
||||||
|
int size = 0;
|
||||||
|
if (!grisu2_gen_digits(buf.data(), size, hi, lo, exp, delta, one, diff,
|
||||||
|
params.num_digits)) {
|
||||||
|
buf.clear();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
grisu2_prettify(params, size, cached_exp + exp, prettify_handler(buf, size));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Double>
|
||||||
|
void sprintf_format(Double value, internal::buffer &buf,
|
||||||
|
core_format_specs spec) {
|
||||||
|
// Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail.
|
||||||
|
FMT_ASSERT(buf.capacity() != 0, "empty buffer");
|
||||||
|
|
||||||
|
// Build format string.
|
||||||
|
enum { MAX_FORMAT_SIZE = 10}; // longest format: %#-*.*Lg
|
||||||
|
char format[MAX_FORMAT_SIZE];
|
||||||
|
char *format_ptr = format;
|
||||||
|
*format_ptr++ = '%';
|
||||||
|
if (spec.has(HASH_FLAG))
|
||||||
|
*format_ptr++ = '#';
|
||||||
|
if (spec.precision >= 0) {
|
||||||
|
*format_ptr++ = '.';
|
||||||
|
*format_ptr++ = '*';
|
||||||
|
}
|
||||||
|
if (std::is_same<Double, long double>::value)
|
||||||
|
*format_ptr++ = 'L';
|
||||||
|
*format_ptr++ = spec.type;
|
||||||
|
*format_ptr = '\0';
|
||||||
|
|
||||||
|
// Format using snprintf.
|
||||||
|
char *start = FMT_NULL;
|
||||||
|
for (;;) {
|
||||||
|
std::size_t buffer_size = buf.capacity();
|
||||||
|
start = &buf[0];
|
||||||
|
int result = internal::char_traits<char>::format_float(
|
||||||
|
start, buffer_size, format, spec.precision, value);
|
||||||
|
if (result >= 0) {
|
||||||
|
unsigned n = internal::to_unsigned(result);
|
||||||
|
if (n < buf.capacity()) {
|
||||||
|
buf.resize(n);
|
||||||
|
break; // The buffer is large enough - continue with formatting.
|
||||||
|
}
|
||||||
|
buf.reserve(n + 1);
|
||||||
|
} else {
|
||||||
|
// If result is negative we ask to increase the capacity by at least 1,
|
||||||
|
// but as std::vector, the buffer grows exponentially.
|
||||||
|
buf.reserve(buf.capacity() + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
#if FMT_USE_WINDOWS_H
|
||||||
|
|
||||||
|
FMT_FUNC internal::utf8_to_utf16::utf8_to_utf16(string_view s) {
|
||||||
|
static const char ERROR_MSG[] = "cannot convert string from UTF-8 to UTF-16";
|
||||||
|
if (s.size() > INT_MAX)
|
||||||
|
FMT_THROW(windows_error(ERROR_INVALID_PARAMETER, ERROR_MSG));
|
||||||
|
int s_size = static_cast<int>(s.size());
|
||||||
|
if (s_size == 0) {
|
||||||
|
// MultiByteToWideChar does not support zero length, handle separately.
|
||||||
|
buffer_.resize(1);
|
||||||
|
buffer_[0] = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int length = MultiByteToWideChar(
|
||||||
|
CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, FMT_NULL, 0);
|
||||||
|
if (length == 0)
|
||||||
|
FMT_THROW(windows_error(GetLastError(), ERROR_MSG));
|
||||||
|
buffer_.resize(length + 1);
|
||||||
|
length = MultiByteToWideChar(
|
||||||
|
CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, &buffer_[0], length);
|
||||||
|
if (length == 0)
|
||||||
|
FMT_THROW(windows_error(GetLastError(), ERROR_MSG));
|
||||||
|
buffer_[length] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_FUNC internal::utf16_to_utf8::utf16_to_utf8(wstring_view s) {
|
||||||
|
if (int error_code = convert(s)) {
|
||||||
|
FMT_THROW(windows_error(error_code,
|
||||||
|
"cannot convert string from UTF-16 to UTF-8"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_FUNC int internal::utf16_to_utf8::convert(wstring_view s) {
|
||||||
|
if (s.size() > INT_MAX)
|
||||||
|
return ERROR_INVALID_PARAMETER;
|
||||||
|
int s_size = static_cast<int>(s.size());
|
||||||
|
if (s_size == 0) {
|
||||||
|
// WideCharToMultiByte does not support zero length, handle separately.
|
||||||
|
buffer_.resize(1);
|
||||||
|
buffer_[0] = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int length = WideCharToMultiByte(
|
||||||
|
CP_UTF8, 0, s.data(), s_size, FMT_NULL, 0, FMT_NULL, FMT_NULL);
|
||||||
|
if (length == 0)
|
||||||
|
return GetLastError();
|
||||||
|
buffer_.resize(length + 1);
|
||||||
|
length = WideCharToMultiByte(
|
||||||
|
CP_UTF8, 0, s.data(), s_size, &buffer_[0], length, FMT_NULL, FMT_NULL);
|
||||||
|
if (length == 0)
|
||||||
|
return GetLastError();
|
||||||
|
buffer_[length] = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_FUNC void windows_error::init(
|
||||||
|
int err_code, string_view format_str, format_args args) {
|
||||||
|
error_code_ = err_code;
|
||||||
|
memory_buffer buffer;
|
||||||
|
internal::format_windows_error(buffer, err_code, vformat(format_str, args));
|
||||||
|
std::runtime_error &base = *this;
|
||||||
|
base = std::runtime_error(to_string(buffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_FUNC void internal::format_windows_error(
|
||||||
|
internal::buffer &out, int error_code, string_view message) FMT_NOEXCEPT {
|
||||||
|
FMT_TRY {
|
||||||
|
wmemory_buffer buf;
|
||||||
|
buf.resize(inline_buffer_size);
|
||||||
|
for (;;) {
|
||||||
|
wchar_t *system_message = &buf[0];
|
||||||
|
int result = FormatMessageW(
|
||||||
|
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||||
|
FMT_NULL, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||||
|
system_message, static_cast<uint32_t>(buf.size()), FMT_NULL);
|
||||||
|
if (result != 0) {
|
||||||
|
utf16_to_utf8 utf8_message;
|
||||||
|
if (utf8_message.convert(system_message) == ERROR_SUCCESS) {
|
||||||
|
writer w(out);
|
||||||
|
w.write(message);
|
||||||
|
w.write(": ");
|
||||||
|
w.write(utf8_message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
|
||||||
|
break; // Can't get error message, report error code instead.
|
||||||
|
buf.resize(buf.size() * 2);
|
||||||
|
}
|
||||||
|
} FMT_CATCH(...) {}
|
||||||
|
format_error_code(out, error_code, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // FMT_USE_WINDOWS_H
|
||||||
|
|
||||||
|
FMT_FUNC void format_system_error(
|
||||||
|
internal::buffer &out, int error_code, string_view message) FMT_NOEXCEPT {
|
||||||
|
FMT_TRY {
|
||||||
|
memory_buffer buf;
|
||||||
|
buf.resize(inline_buffer_size);
|
||||||
|
for (;;) {
|
||||||
|
char *system_message = &buf[0];
|
||||||
|
int result = safe_strerror(error_code, system_message, buf.size());
|
||||||
|
if (result == 0) {
|
||||||
|
writer w(out);
|
||||||
|
w.write(message);
|
||||||
|
w.write(": ");
|
||||||
|
w.write(system_message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (result != ERANGE)
|
||||||
|
break; // Can't get error message, report error code instead.
|
||||||
|
buf.resize(buf.size() * 2);
|
||||||
|
}
|
||||||
|
} FMT_CATCH(...) {}
|
||||||
|
format_error_code(out, error_code, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_FUNC void internal::error_handler::on_error(const char *message) {
|
||||||
|
FMT_THROW(format_error(message));
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_FUNC void report_system_error(
|
||||||
|
int error_code, fmt::string_view message) FMT_NOEXCEPT {
|
||||||
|
report_error(format_system_error, error_code, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if FMT_USE_WINDOWS_H
|
||||||
|
FMT_FUNC void report_windows_error(
|
||||||
|
int error_code, fmt::string_view message) FMT_NOEXCEPT {
|
||||||
|
report_error(internal::format_windows_error, error_code, message);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
FMT_FUNC void vprint(std::FILE *f, string_view format_str, format_args args) {
|
||||||
|
memory_buffer buffer;
|
||||||
|
internal::vformat_to(buffer, format_str,
|
||||||
|
basic_format_args<buffer_context<char>::type>(args));
|
||||||
|
std::fwrite(buffer.data(), 1, buffer.size(), f);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_FUNC void vprint(std::FILE *f, wstring_view format_str, wformat_args args) {
|
||||||
|
wmemory_buffer buffer;
|
||||||
|
internal::vformat_to(buffer, format_str, args);
|
||||||
|
std::fwrite(buffer.data(), sizeof(wchar_t), buffer.size(), f);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_FUNC void vprint(string_view format_str, format_args args) {
|
||||||
|
vprint(stdout, format_str, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_FUNC void vprint(wstring_view format_str, wformat_args args) {
|
||||||
|
vprint(stdout, format_str, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
# pragma warning(pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // FMT_FORMAT_INL_H_
|
3555
lib/include/fmt/format.h
Normal file
3555
lib/include/fmt/format.h
Normal file
File diff suppressed because it is too large
Load Diff
77
lib/include/fmt/locale.h
Normal file
77
lib/include/fmt/locale.h
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
// Formatting library for C++ - std::locale support
|
||||||
|
//
|
||||||
|
// Copyright (c) 2012 - present, Victor Zverovich
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
|
#ifndef FMT_LOCALE_H_
|
||||||
|
#define FMT_LOCALE_H_
|
||||||
|
|
||||||
|
#include "format.h"
|
||||||
|
#include <locale>
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
template <typename Char>
|
||||||
|
typename buffer_context<Char>::type::iterator vformat_to(
|
||||||
|
const std::locale &loc, basic_buffer<Char> &buf,
|
||||||
|
basic_string_view<Char> format_str,
|
||||||
|
basic_format_args<typename buffer_context<Char>::type> args) {
|
||||||
|
typedef back_insert_range<basic_buffer<Char> > range;
|
||||||
|
return vformat_to<arg_formatter<range>>(
|
||||||
|
buf, to_string_view(format_str), args, internal::locale_ref(loc));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
std::basic_string<Char> vformat(
|
||||||
|
const std::locale &loc, basic_string_view<Char> format_str,
|
||||||
|
basic_format_args<typename buffer_context<Char>::type> args) {
|
||||||
|
basic_memory_buffer<Char> buffer;
|
||||||
|
internal::vformat_to(loc, buffer, format_str, args);
|
||||||
|
return fmt::to_string(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename S, typename Char = FMT_CHAR(S)>
|
||||||
|
inline std::basic_string<Char> vformat(
|
||||||
|
const std::locale &loc, const S &format_str,
|
||||||
|
basic_format_args<typename buffer_context<Char>::type> args) {
|
||||||
|
return internal::vformat(loc, to_string_view(format_str), args);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename S, typename... Args>
|
||||||
|
inline std::basic_string<FMT_CHAR(S)> format(
|
||||||
|
const std::locale &loc, const S &format_str, const Args &... args) {
|
||||||
|
return internal::vformat(
|
||||||
|
loc, to_string_view(format_str),
|
||||||
|
*internal::checked_args<S, Args...>(format_str, args...));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename String, typename OutputIt, typename... Args>
|
||||||
|
inline typename std::enable_if<internal::is_output_iterator<OutputIt>::value,
|
||||||
|
OutputIt>::type
|
||||||
|
vformat_to(OutputIt out, const std::locale &loc, const String &format_str,
|
||||||
|
typename format_args_t<OutputIt, FMT_CHAR(String)>::type args) {
|
||||||
|
typedef output_range<OutputIt, FMT_CHAR(String)> range;
|
||||||
|
return vformat_to<arg_formatter<range>>(
|
||||||
|
range(out), to_string_view(format_str), args, internal::locale_ref(loc));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt, typename S, typename... Args>
|
||||||
|
inline typename std::enable_if<
|
||||||
|
internal::is_string<S>::value &&
|
||||||
|
internal::is_output_iterator<OutputIt>::value, OutputIt>::type
|
||||||
|
format_to(OutputIt out, const std::locale &loc, const S &format_str,
|
||||||
|
const Args &... args) {
|
||||||
|
internal::check_format_string<Args...>(format_str);
|
||||||
|
typedef typename format_context_t<OutputIt, FMT_CHAR(S)>::type context;
|
||||||
|
format_arg_store<context, Args...> as{args...};
|
||||||
|
return vformat_to(out, loc, to_string_view(format_str),
|
||||||
|
basic_format_args<context>(as));
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // FMT_LOCALE_H_
|
153
lib/include/fmt/ostream.h
Normal file
153
lib/include/fmt/ostream.h
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
// Formatting library for C++ - std::ostream support
|
||||||
|
//
|
||||||
|
// Copyright (c) 2012 - present, Victor Zverovich
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
|
#ifndef FMT_OSTREAM_H_
|
||||||
|
#define FMT_OSTREAM_H_
|
||||||
|
|
||||||
|
#include "format.h"
|
||||||
|
#include <ostream>
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
template <class Char>
|
||||||
|
class formatbuf : public std::basic_streambuf<Char> {
|
||||||
|
private:
|
||||||
|
typedef typename std::basic_streambuf<Char>::int_type int_type;
|
||||||
|
typedef typename std::basic_streambuf<Char>::traits_type traits_type;
|
||||||
|
|
||||||
|
basic_buffer<Char> &buffer_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
formatbuf(basic_buffer<Char> &buffer) : buffer_(buffer) {}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// The put-area is actually always empty. This makes the implementation
|
||||||
|
// simpler and has the advantage that the streambuf and the buffer are always
|
||||||
|
// in sync and sputc never writes into uninitialized memory. The obvious
|
||||||
|
// disadvantage is that each call to sputc always results in a (virtual) call
|
||||||
|
// to overflow. There is no disadvantage here for sputn since this always
|
||||||
|
// results in a call to xsputn.
|
||||||
|
|
||||||
|
int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE {
|
||||||
|
if (!traits_type::eq_int_type(ch, traits_type::eof()))
|
||||||
|
buffer_.push_back(static_cast<Char>(ch));
|
||||||
|
return ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::streamsize xsputn(const Char *s, std::streamsize count) FMT_OVERRIDE {
|
||||||
|
buffer_.append(s, s + count);
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
struct test_stream : std::basic_ostream<Char> {
|
||||||
|
private:
|
||||||
|
struct null;
|
||||||
|
// Hide all operator<< from std::basic_ostream<Char>.
|
||||||
|
void operator<<(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Checks if T has a user-defined operator<< (e.g. not a member of std::ostream).
|
||||||
|
template <typename T, typename Char>
|
||||||
|
class is_streamable {
|
||||||
|
private:
|
||||||
|
template <typename U>
|
||||||
|
static decltype(
|
||||||
|
internal::declval<test_stream<Char>&>()
|
||||||
|
<< internal::declval<U>(), std::true_type()) test(int);
|
||||||
|
|
||||||
|
template <typename>
|
||||||
|
static std::false_type test(...);
|
||||||
|
|
||||||
|
typedef decltype(test<T>(0)) result;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static const bool value = result::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Write the content of buf to os.
|
||||||
|
template <typename Char>
|
||||||
|
void write(std::basic_ostream<Char> &os, basic_buffer<Char> &buf) {
|
||||||
|
const Char *data = buf.data();
|
||||||
|
typedef std::make_unsigned<std::streamsize>::type UnsignedStreamSize;
|
||||||
|
UnsignedStreamSize size = buf.size();
|
||||||
|
UnsignedStreamSize max_size =
|
||||||
|
internal::to_unsigned((std::numeric_limits<std::streamsize>::max)());
|
||||||
|
do {
|
||||||
|
UnsignedStreamSize n = size <= max_size ? size : max_size;
|
||||||
|
os.write(data, static_cast<std::streamsize>(n));
|
||||||
|
data += n;
|
||||||
|
size -= n;
|
||||||
|
} while (size != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char, typename T>
|
||||||
|
void format_value(basic_buffer<Char> &buffer, const T &value) {
|
||||||
|
internal::formatbuf<Char> format_buf(buffer);
|
||||||
|
std::basic_ostream<Char> output(&format_buf);
|
||||||
|
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
|
||||||
|
output << value;
|
||||||
|
buffer.resize(buffer.size());
|
||||||
|
}
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
// Disable conversion to int if T has an overloaded operator<< which is a free
|
||||||
|
// function (not a member of std::ostream).
|
||||||
|
template <typename T, typename Char>
|
||||||
|
struct convert_to_int<T, Char, void> {
|
||||||
|
static const bool value =
|
||||||
|
convert_to_int<T, Char, int>::value &&
|
||||||
|
!internal::is_streamable<T, Char>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Formats an object of type T that has an overloaded ostream operator<<.
|
||||||
|
template <typename T, typename Char>
|
||||||
|
struct formatter<T, Char,
|
||||||
|
typename std::enable_if<
|
||||||
|
internal::is_streamable<T, Char>::value &&
|
||||||
|
!internal::format_type<
|
||||||
|
typename buffer_context<Char>::type, T>::value>::type>
|
||||||
|
: formatter<basic_string_view<Char>, Char> {
|
||||||
|
|
||||||
|
template <typename Context>
|
||||||
|
auto format(const T &value, Context &ctx) -> decltype(ctx.out()) {
|
||||||
|
basic_memory_buffer<Char> buffer;
|
||||||
|
internal::format_value(buffer, value);
|
||||||
|
basic_string_view<Char> str(buffer.data(), buffer.size());
|
||||||
|
return formatter<basic_string_view<Char>, Char>::format(str, ctx);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
inline void vprint(std::basic_ostream<Char> &os,
|
||||||
|
basic_string_view<Char> format_str,
|
||||||
|
basic_format_args<typename buffer_context<Char>::type> args) {
|
||||||
|
basic_memory_buffer<Char> buffer;
|
||||||
|
internal::vformat_to(buffer, format_str, args);
|
||||||
|
internal::write(os, buffer);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Prints formatted data to the stream *os*.
|
||||||
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
|
fmt::print(cerr, "Don't {}!", "panic");
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename S, typename... Args>
|
||||||
|
inline typename std::enable_if<internal::is_string<S>::value>::type
|
||||||
|
print(std::basic_ostream<FMT_CHAR(S)> &os, const S &format_str,
|
||||||
|
const Args & ... args) {
|
||||||
|
internal::checked_args<S, Args...> ca(format_str, args...);
|
||||||
|
vprint(os, to_string_view(format_str), *ca);
|
||||||
|
}
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // FMT_OSTREAM_H_
|
324
lib/include/fmt/posix.h
Normal file
324
lib/include/fmt/posix.h
Normal file
@ -0,0 +1,324 @@
|
|||||||
|
// A C++ interface to POSIX functions.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2012 - 2016, Victor Zverovich
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
|
#ifndef FMT_POSIX_H_
|
||||||
|
#define FMT_POSIX_H_
|
||||||
|
|
||||||
|
#if defined(__MINGW32__) || defined(__CYGWIN__)
|
||||||
|
// Workaround MinGW bug https://sourceforge.net/p/mingw/bugs/2024/.
|
||||||
|
# undef __STRICT_ANSI__
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h> // for O_RDONLY
|
||||||
|
#include <locale.h> // for locale_t
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h> // for strtod_l
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
#if defined __APPLE__ || defined(__FreeBSD__)
|
||||||
|
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "format.h"
|
||||||
|
|
||||||
|
#ifndef FMT_POSIX
|
||||||
|
# if defined(_WIN32) && !defined(__MINGW32__)
|
||||||
|
// Fix warnings about deprecated symbols.
|
||||||
|
# define FMT_POSIX(call) _##call
|
||||||
|
# else
|
||||||
|
# define FMT_POSIX(call) call
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Calls to system functions are wrapped in FMT_SYSTEM for testability.
|
||||||
|
#ifdef FMT_SYSTEM
|
||||||
|
# define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
|
||||||
|
#else
|
||||||
|
# define FMT_SYSTEM(call) call
|
||||||
|
# ifdef _WIN32
|
||||||
|
// Fix warnings about deprecated symbols.
|
||||||
|
# define FMT_POSIX_CALL(call) ::_##call
|
||||||
|
# else
|
||||||
|
# define FMT_POSIX_CALL(call) ::call
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Retries the expression while it evaluates to error_result and errno
|
||||||
|
// equals to EINTR.
|
||||||
|
#ifndef _WIN32
|
||||||
|
# define FMT_RETRY_VAL(result, expression, error_result) \
|
||||||
|
do { \
|
||||||
|
result = (expression); \
|
||||||
|
} while (result == error_result && errno == EINTR)
|
||||||
|
#else
|
||||||
|
# define FMT_RETRY_VAL(result, expression, error_result) result = (expression)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
A reference to a null-terminated string. It can be constructed from a C
|
||||||
|
string or ``std::string``.
|
||||||
|
|
||||||
|
You can use one of the following typedefs for common character types:
|
||||||
|
|
||||||
|
+---------------+-----------------------------+
|
||||||
|
| Type | Definition |
|
||||||
|
+===============+=============================+
|
||||||
|
| cstring_view | basic_cstring_view<char> |
|
||||||
|
+---------------+-----------------------------+
|
||||||
|
| wcstring_view | basic_cstring_view<wchar_t> |
|
||||||
|
+---------------+-----------------------------+
|
||||||
|
|
||||||
|
This class is most useful as a parameter type to allow passing
|
||||||
|
different types of strings to a function, for example::
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
std::string format(cstring_view format_str, const Args & ... args);
|
||||||
|
|
||||||
|
format("{}", 42);
|
||||||
|
format(std::string("{}"), 42);
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename Char>
|
||||||
|
class basic_cstring_view {
|
||||||
|
private:
|
||||||
|
const Char *data_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/** Constructs a string reference object from a C string. */
|
||||||
|
basic_cstring_view(const Char *s) : data_(s) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Constructs a string reference from an ``std::string`` object.
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
basic_cstring_view(const std::basic_string<Char> &s) : data_(s.c_str()) {}
|
||||||
|
|
||||||
|
/** Returns the pointer to a C string. */
|
||||||
|
const Char *c_str() const { return data_; }
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef basic_cstring_view<char> cstring_view;
|
||||||
|
typedef basic_cstring_view<wchar_t> wcstring_view;
|
||||||
|
|
||||||
|
// An error code.
|
||||||
|
class error_code {
|
||||||
|
private:
|
||||||
|
int value_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit error_code(int value = 0) FMT_NOEXCEPT : value_(value) {}
|
||||||
|
|
||||||
|
int get() const FMT_NOEXCEPT { return value_; }
|
||||||
|
};
|
||||||
|
|
||||||
|
// A buffered file.
|
||||||
|
class buffered_file {
|
||||||
|
private:
|
||||||
|
FILE *file_;
|
||||||
|
|
||||||
|
friend class file;
|
||||||
|
|
||||||
|
explicit buffered_file(FILE *f) : file_(f) {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Constructs a buffered_file object which doesn't represent any file.
|
||||||
|
buffered_file() FMT_NOEXCEPT : file_(FMT_NULL) {}
|
||||||
|
|
||||||
|
// Destroys the object closing the file it represents if any.
|
||||||
|
FMT_API ~buffered_file() FMT_NOEXCEPT;
|
||||||
|
|
||||||
|
private:
|
||||||
|
buffered_file(const buffered_file &) = delete;
|
||||||
|
void operator=(const buffered_file &) = delete;
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
buffered_file(buffered_file &&other) FMT_NOEXCEPT : file_(other.file_) {
|
||||||
|
other.file_ = FMT_NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffered_file& operator=(buffered_file &&other) {
|
||||||
|
close();
|
||||||
|
file_ = other.file_;
|
||||||
|
other.file_ = FMT_NULL;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Opens a file.
|
||||||
|
FMT_API buffered_file(cstring_view filename, cstring_view mode);
|
||||||
|
|
||||||
|
// Closes the file.
|
||||||
|
FMT_API void close();
|
||||||
|
|
||||||
|
// Returns the pointer to a FILE object representing this file.
|
||||||
|
FILE *get() const FMT_NOEXCEPT { return file_; }
|
||||||
|
|
||||||
|
// We place parentheses around fileno to workaround a bug in some versions
|
||||||
|
// of MinGW that define fileno as a macro.
|
||||||
|
FMT_API int (fileno)() const;
|
||||||
|
|
||||||
|
void vprint(string_view format_str, format_args args) {
|
||||||
|
fmt::vprint(file_, format_str, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
inline void print(string_view format_str, const Args & ... args) {
|
||||||
|
vprint(format_str, make_format_args(args...));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// A file. Closed file is represented by a file object with descriptor -1.
|
||||||
|
// Methods that are not declared with FMT_NOEXCEPT may throw
|
||||||
|
// fmt::system_error in case of failure. Note that some errors such as
|
||||||
|
// closing the file multiple times will cause a crash on Windows rather
|
||||||
|
// than an exception. You can get standard behavior by overriding the
|
||||||
|
// invalid parameter handler with _set_invalid_parameter_handler.
|
||||||
|
class file {
|
||||||
|
private:
|
||||||
|
int fd_; // File descriptor.
|
||||||
|
|
||||||
|
// Constructs a file object with a given descriptor.
|
||||||
|
explicit file(int fd) : fd_(fd) {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Possible values for the oflag argument to the constructor.
|
||||||
|
enum {
|
||||||
|
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
|
||||||
|
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
|
||||||
|
RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing.
|
||||||
|
};
|
||||||
|
|
||||||
|
// Constructs a file object which doesn't represent any file.
|
||||||
|
file() FMT_NOEXCEPT : fd_(-1) {}
|
||||||
|
|
||||||
|
// Opens a file and constructs a file object representing this file.
|
||||||
|
FMT_API file(cstring_view path, int oflag);
|
||||||
|
|
||||||
|
private:
|
||||||
|
file(const file &) = delete;
|
||||||
|
void operator=(const file &) = delete;
|
||||||
|
|
||||||
|
public:
|
||||||
|
file(file &&other) FMT_NOEXCEPT : fd_(other.fd_) {
|
||||||
|
other.fd_ = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
file& operator=(file &&other) {
|
||||||
|
close();
|
||||||
|
fd_ = other.fd_;
|
||||||
|
other.fd_ = -1;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destroys the object closing the file it represents if any.
|
||||||
|
FMT_API ~file() FMT_NOEXCEPT;
|
||||||
|
|
||||||
|
// Returns the file descriptor.
|
||||||
|
int descriptor() const FMT_NOEXCEPT { return fd_; }
|
||||||
|
|
||||||
|
// Closes the file.
|
||||||
|
FMT_API void close();
|
||||||
|
|
||||||
|
// Returns the file size. The size has signed type for consistency with
|
||||||
|
// stat::st_size.
|
||||||
|
FMT_API long long size() const;
|
||||||
|
|
||||||
|
// Attempts to read count bytes from the file into the specified buffer.
|
||||||
|
FMT_API std::size_t read(void *buffer, std::size_t count);
|
||||||
|
|
||||||
|
// Attempts to write count bytes from the specified buffer to the file.
|
||||||
|
FMT_API std::size_t write(const void *buffer, std::size_t count);
|
||||||
|
|
||||||
|
// Duplicates a file descriptor with the dup function and returns
|
||||||
|
// the duplicate as a file object.
|
||||||
|
FMT_API static file dup(int fd);
|
||||||
|
|
||||||
|
// Makes fd be the copy of this file descriptor, closing fd first if
|
||||||
|
// necessary.
|
||||||
|
FMT_API void dup2(int fd);
|
||||||
|
|
||||||
|
// Makes fd be the copy of this file descriptor, closing fd first if
|
||||||
|
// necessary.
|
||||||
|
FMT_API void dup2(int fd, error_code &ec) FMT_NOEXCEPT;
|
||||||
|
|
||||||
|
// Creates a pipe setting up read_end and write_end file objects for reading
|
||||||
|
// and writing respectively.
|
||||||
|
FMT_API static void pipe(file &read_end, file &write_end);
|
||||||
|
|
||||||
|
// Creates a buffered_file object associated with this file and detaches
|
||||||
|
// this file object from the file.
|
||||||
|
FMT_API buffered_file fdopen(const char *mode);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Returns the memory page size.
|
||||||
|
long getpagesize();
|
||||||
|
|
||||||
|
#if (defined(LC_NUMERIC_MASK) || defined(_MSC_VER)) && \
|
||||||
|
!defined(__ANDROID__) && !defined(__CYGWIN__) && !defined(__OpenBSD__) && \
|
||||||
|
!defined(__NEWLIB_H__)
|
||||||
|
# define FMT_LOCALE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef FMT_LOCALE
|
||||||
|
// A "C" numeric locale.
|
||||||
|
class Locale {
|
||||||
|
private:
|
||||||
|
# ifdef _MSC_VER
|
||||||
|
typedef _locale_t locale_t;
|
||||||
|
|
||||||
|
enum { LC_NUMERIC_MASK = LC_NUMERIC };
|
||||||
|
|
||||||
|
static locale_t newlocale(int category_mask, const char *locale, locale_t) {
|
||||||
|
return _create_locale(category_mask, locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void freelocale(locale_t locale) {
|
||||||
|
_free_locale(locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
static double strtod_l(const char *nptr, char **endptr, _locale_t locale) {
|
||||||
|
return _strtod_l(nptr, endptr, locale);
|
||||||
|
}
|
||||||
|
# endif
|
||||||
|
|
||||||
|
locale_t locale_;
|
||||||
|
|
||||||
|
Locale(const Locale &) = delete;
|
||||||
|
void operator=(const Locale &) = delete;
|
||||||
|
|
||||||
|
public:
|
||||||
|
typedef locale_t Type;
|
||||||
|
|
||||||
|
Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", FMT_NULL)) {
|
||||||
|
if (!locale_)
|
||||||
|
FMT_THROW(system_error(errno, "cannot create locale"));
|
||||||
|
}
|
||||||
|
~Locale() { freelocale(locale_); }
|
||||||
|
|
||||||
|
Type get() const { return locale_; }
|
||||||
|
|
||||||
|
// Converts string to floating-point number and advances str past the end
|
||||||
|
// of the parsed input.
|
||||||
|
double strtod(const char *&str) const {
|
||||||
|
char *end = FMT_NULL;
|
||||||
|
double result = strtod_l(str, &end, locale_);
|
||||||
|
str = end;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#endif // FMT_LOCALE
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // FMT_POSIX_H_
|
855
lib/include/fmt/printf.h
Normal file
855
lib/include/fmt/printf.h
Normal file
@ -0,0 +1,855 @@
|
|||||||
|
// Formatting library for C++
|
||||||
|
//
|
||||||
|
// Copyright (c) 2012 - 2016, Victor Zverovich
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
|
#ifndef FMT_PRINTF_H_
|
||||||
|
#define FMT_PRINTF_H_
|
||||||
|
|
||||||
|
#include <algorithm> // std::fill_n
|
||||||
|
#include <limits> // std::numeric_limits
|
||||||
|
|
||||||
|
#include "ostream.h"
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
// An iterator that produces a null terminator on *end. This simplifies parsing
|
||||||
|
// and allows comparing the performance of processing a null-terminated string
|
||||||
|
// vs string_view.
|
||||||
|
template <typename Char>
|
||||||
|
class null_terminating_iterator {
|
||||||
|
public:
|
||||||
|
typedef std::ptrdiff_t difference_type;
|
||||||
|
typedef Char value_type;
|
||||||
|
typedef const Char* pointer;
|
||||||
|
typedef const Char& reference;
|
||||||
|
typedef std::random_access_iterator_tag iterator_category;
|
||||||
|
|
||||||
|
null_terminating_iterator() : ptr_(0), end_(0) {}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR null_terminating_iterator(const Char *ptr, const Char *end)
|
||||||
|
: ptr_(ptr), end_(end) {}
|
||||||
|
|
||||||
|
template <typename Range>
|
||||||
|
FMT_CONSTEXPR explicit null_terminating_iterator(const Range &r)
|
||||||
|
: ptr_(r.begin()), end_(r.end()) {}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR null_terminating_iterator &operator=(const Char *ptr) {
|
||||||
|
assert(ptr <= end_);
|
||||||
|
ptr_ = ptr;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR Char operator*() const {
|
||||||
|
return ptr_ != end_ ? *ptr_ : Char();
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR null_terminating_iterator operator++() {
|
||||||
|
++ptr_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR null_terminating_iterator operator++(int) {
|
||||||
|
null_terminating_iterator result(*this);
|
||||||
|
++ptr_;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR null_terminating_iterator operator--() {
|
||||||
|
--ptr_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR null_terminating_iterator operator+(difference_type n) {
|
||||||
|
return null_terminating_iterator(ptr_ + n, end_);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR null_terminating_iterator operator-(difference_type n) {
|
||||||
|
return null_terminating_iterator(ptr_ - n, end_);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR null_terminating_iterator operator+=(difference_type n) {
|
||||||
|
ptr_ += n;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR difference_type operator-(
|
||||||
|
null_terminating_iterator other) const {
|
||||||
|
return ptr_ - other.ptr_;
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR bool operator!=(null_terminating_iterator other) const {
|
||||||
|
return ptr_ != other.ptr_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator>=(null_terminating_iterator other) const {
|
||||||
|
return ptr_ >= other.ptr_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This should be a friend specialization pointer_from<Char> but the latter
|
||||||
|
// doesn't compile by gcc 5.1 due to a compiler bug.
|
||||||
|
template <typename CharT>
|
||||||
|
friend FMT_CONSTEXPR_DECL const CharT *pointer_from(
|
||||||
|
null_terminating_iterator<CharT> it);
|
||||||
|
|
||||||
|
private:
|
||||||
|
const Char *ptr_;
|
||||||
|
const Char *end_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
FMT_CONSTEXPR const T *pointer_from(const T *p) { return p; }
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
FMT_CONSTEXPR const Char *pointer_from(null_terminating_iterator<Char> it) {
|
||||||
|
return it.ptr_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// DEPRECATED: Parses the input as an unsigned integer. This function assumes
|
||||||
|
// that the first character is a digit and presence of a non-digit character at
|
||||||
|
// the end.
|
||||||
|
// it: an iterator pointing to the beginning of the input range.
|
||||||
|
template <typename Iterator, typename ErrorHandler>
|
||||||
|
FMT_CONSTEXPR unsigned parse_nonnegative_int(Iterator &it, ErrorHandler &&eh) {
|
||||||
|
assert('0' <= *it && *it <= '9');
|
||||||
|
if (*it == '0') {
|
||||||
|
++it;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
unsigned value = 0;
|
||||||
|
// Convert to unsigned to prevent a warning.
|
||||||
|
unsigned max_int = (std::numeric_limits<int>::max)();
|
||||||
|
unsigned big = max_int / 10;
|
||||||
|
do {
|
||||||
|
// Check for overflow.
|
||||||
|
if (value > big) {
|
||||||
|
value = max_int + 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
value = value * 10 + unsigned(*it - '0');
|
||||||
|
// Workaround for MSVC "setup_exception stack overflow" error:
|
||||||
|
auto next = it;
|
||||||
|
++next;
|
||||||
|
it = next;
|
||||||
|
} while ('0' <= *it && *it <= '9');
|
||||||
|
if (value > max_int)
|
||||||
|
eh.on_error("number is too big");
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks if a value fits in int - used to avoid warnings about comparing
|
||||||
|
// signed and unsigned integers.
|
||||||
|
template <bool IsSigned>
|
||||||
|
struct int_checker {
|
||||||
|
template <typename T>
|
||||||
|
static bool fits_in_int(T value) {
|
||||||
|
unsigned max = std::numeric_limits<int>::max();
|
||||||
|
return value <= max;
|
||||||
|
}
|
||||||
|
static bool fits_in_int(bool) { return true; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct int_checker<true> {
|
||||||
|
template <typename T>
|
||||||
|
static bool fits_in_int(T value) {
|
||||||
|
return value >= std::numeric_limits<int>::min() &&
|
||||||
|
value <= std::numeric_limits<int>::max();
|
||||||
|
}
|
||||||
|
static bool fits_in_int(int) { return true; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class printf_precision_handler: public function<int> {
|
||||||
|
public:
|
||||||
|
template <typename T>
|
||||||
|
typename std::enable_if<std::is_integral<T>::value, int>::type
|
||||||
|
operator()(T value) {
|
||||||
|
if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
|
||||||
|
FMT_THROW(format_error("number is too big"));
|
||||||
|
return static_cast<int>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename std::enable_if<!std::is_integral<T>::value, int>::type operator()(T) {
|
||||||
|
FMT_THROW(format_error("precision is not integer"));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// An argument visitor that returns true iff arg is a zero integer.
|
||||||
|
class is_zero_int: public function<bool> {
|
||||||
|
public:
|
||||||
|
template <typename T>
|
||||||
|
typename std::enable_if<std::is_integral<T>::value, bool>::type
|
||||||
|
operator()(T value) { return value == 0; }
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename std::enable_if<!std::is_integral<T>::value, bool>::type
|
||||||
|
operator()(T) { return false; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct make_unsigned_or_bool : std::make_unsigned<T> {};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct make_unsigned_or_bool<bool> {
|
||||||
|
typedef bool type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename Context>
|
||||||
|
class arg_converter: public function<void> {
|
||||||
|
private:
|
||||||
|
typedef typename Context::char_type Char;
|
||||||
|
|
||||||
|
basic_format_arg<Context> &arg_;
|
||||||
|
typename Context::char_type type_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
arg_converter(basic_format_arg<Context> &arg, Char type)
|
||||||
|
: arg_(arg), type_(type) {}
|
||||||
|
|
||||||
|
void operator()(bool value) {
|
||||||
|
if (type_ != 's')
|
||||||
|
operator()<bool>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename U>
|
||||||
|
typename std::enable_if<std::is_integral<U>::value>::type
|
||||||
|
operator()(U value) {
|
||||||
|
bool is_signed = type_ == 'd' || type_ == 'i';
|
||||||
|
typedef typename std::conditional<
|
||||||
|
std::is_same<T, void>::value, U, T>::type TargetType;
|
||||||
|
if (const_check(sizeof(TargetType) <= sizeof(int))) {
|
||||||
|
// Extra casts are used to silence warnings.
|
||||||
|
if (is_signed) {
|
||||||
|
arg_ = internal::make_arg<Context>(
|
||||||
|
static_cast<int>(static_cast<TargetType>(value)));
|
||||||
|
} else {
|
||||||
|
typedef typename make_unsigned_or_bool<TargetType>::type Unsigned;
|
||||||
|
arg_ = internal::make_arg<Context>(
|
||||||
|
static_cast<unsigned>(static_cast<Unsigned>(value)));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (is_signed) {
|
||||||
|
// glibc's printf doesn't sign extend arguments of smaller types:
|
||||||
|
// std::printf("%lld", -42); // prints "4294967254"
|
||||||
|
// but we don't have to do the same because it's a UB.
|
||||||
|
arg_ = internal::make_arg<Context>(static_cast<long long>(value));
|
||||||
|
} else {
|
||||||
|
arg_ = internal::make_arg<Context>(
|
||||||
|
static_cast<typename make_unsigned_or_bool<U>::type>(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename U>
|
||||||
|
typename std::enable_if<!std::is_integral<U>::value>::type operator()(U) {
|
||||||
|
// No coversion needed for non-integral types.
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Converts an integer argument to T for printf, if T is an integral type.
|
||||||
|
// If T is void, the argument is converted to corresponding signed or unsigned
|
||||||
|
// type depending on the type specifier: 'd' and 'i' - signed, other -
|
||||||
|
// unsigned).
|
||||||
|
template <typename T, typename Context, typename Char>
|
||||||
|
void convert_arg(basic_format_arg<Context> &arg, Char type) {
|
||||||
|
visit_format_arg(arg_converter<T, Context>(arg, type), arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Converts an integer argument to char for printf.
|
||||||
|
template <typename Context>
|
||||||
|
class char_converter: public function<void> {
|
||||||
|
private:
|
||||||
|
basic_format_arg<Context> &arg_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit char_converter(basic_format_arg<Context> &arg) : arg_(arg) {}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename std::enable_if<std::is_integral<T>::value>::type
|
||||||
|
operator()(T value) {
|
||||||
|
typedef typename Context::char_type Char;
|
||||||
|
arg_ = internal::make_arg<Context>(static_cast<Char>(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename std::enable_if<!std::is_integral<T>::value>::type operator()(T) {
|
||||||
|
// No coversion needed for non-integral types.
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Checks if an argument is a valid printf width specifier and sets
|
||||||
|
// left alignment if it is negative.
|
||||||
|
template <typename Char>
|
||||||
|
class printf_width_handler: public function<unsigned> {
|
||||||
|
private:
|
||||||
|
typedef basic_format_specs<Char> format_specs;
|
||||||
|
|
||||||
|
format_specs &spec_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit printf_width_handler(format_specs &spec) : spec_(spec) {}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename std::enable_if<std::is_integral<T>::value, unsigned>::type
|
||||||
|
operator()(T value) {
|
||||||
|
typedef typename internal::int_traits<T>::main_type UnsignedType;
|
||||||
|
UnsignedType width = static_cast<UnsignedType>(value);
|
||||||
|
if (internal::is_negative(value)) {
|
||||||
|
spec_.align_ = ALIGN_LEFT;
|
||||||
|
width = 0 - width;
|
||||||
|
}
|
||||||
|
unsigned int_max = std::numeric_limits<int>::max();
|
||||||
|
if (width > int_max)
|
||||||
|
FMT_THROW(format_error("number is too big"));
|
||||||
|
return static_cast<unsigned>(width);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename std::enable_if<!std::is_integral<T>::value, unsigned>::type
|
||||||
|
operator()(T) {
|
||||||
|
FMT_THROW(format_error("width is not integer"));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char, typename Context>
|
||||||
|
void printf(basic_buffer<Char> &buf, basic_string_view<Char> format,
|
||||||
|
basic_format_args<Context> args) {
|
||||||
|
Context(std::back_inserter(buf), format, args).format();
|
||||||
|
}
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
using internal::printf; // For printing into memory_buffer.
|
||||||
|
|
||||||
|
template <typename Range>
|
||||||
|
class printf_arg_formatter;
|
||||||
|
|
||||||
|
template <
|
||||||
|
typename OutputIt, typename Char,
|
||||||
|
typename ArgFormatter =
|
||||||
|
printf_arg_formatter<back_insert_range<internal::basic_buffer<Char>>>>
|
||||||
|
class basic_printf_context;
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
The ``printf`` argument formatter.
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename Range>
|
||||||
|
class printf_arg_formatter:
|
||||||
|
public internal::function<
|
||||||
|
typename internal::arg_formatter_base<Range>::iterator>,
|
||||||
|
public internal::arg_formatter_base<Range> {
|
||||||
|
private:
|
||||||
|
typedef typename Range::value_type char_type;
|
||||||
|
typedef decltype(internal::declval<Range>().begin()) iterator;
|
||||||
|
typedef internal::arg_formatter_base<Range> base;
|
||||||
|
typedef basic_printf_context<iterator, char_type> context_type;
|
||||||
|
|
||||||
|
context_type &context_;
|
||||||
|
|
||||||
|
void write_null_pointer(char) {
|
||||||
|
this->spec()->type = 0;
|
||||||
|
this->write("(nil)");
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_null_pointer(wchar_t) {
|
||||||
|
this->spec()->type = 0;
|
||||||
|
this->write(L"(nil)");
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
typedef typename base::format_specs format_specs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Constructs an argument formatter object.
|
||||||
|
*buffer* is a reference to the output buffer and *spec* contains format
|
||||||
|
specifier information for standard argument types.
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
printf_arg_formatter(internal::basic_buffer<char_type> &buffer,
|
||||||
|
format_specs &spec, context_type &ctx)
|
||||||
|
: base(back_insert_range<internal::basic_buffer<char_type>>(buffer), &spec,
|
||||||
|
ctx.locale()),
|
||||||
|
context_(ctx) {}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename std::enable_if<std::is_integral<T>::value, iterator>::type
|
||||||
|
operator()(T value) {
|
||||||
|
// MSVC2013 fails to compile separate overloads for bool and char_type so
|
||||||
|
// use std::is_same instead.
|
||||||
|
if (std::is_same<T, bool>::value) {
|
||||||
|
format_specs &fmt_spec = *this->spec();
|
||||||
|
if (fmt_spec.type != 's')
|
||||||
|
return base::operator()(value ? 1 : 0);
|
||||||
|
fmt_spec.type = 0;
|
||||||
|
this->write(value != 0);
|
||||||
|
} else if (std::is_same<T, char_type>::value) {
|
||||||
|
format_specs &fmt_spec = *this->spec();
|
||||||
|
if (fmt_spec.type && fmt_spec.type != 'c')
|
||||||
|
return (*this)(static_cast<int>(value));
|
||||||
|
fmt_spec.flags = 0;
|
||||||
|
fmt_spec.align_ = ALIGN_RIGHT;
|
||||||
|
return base::operator()(value);
|
||||||
|
} else {
|
||||||
|
return base::operator()(value);
|
||||||
|
}
|
||||||
|
return this->out();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename std::enable_if<std::is_floating_point<T>::value, iterator>::type
|
||||||
|
operator()(T value) {
|
||||||
|
return base::operator()(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Formats a null-terminated C string. */
|
||||||
|
iterator operator()(const char *value) {
|
||||||
|
if (value)
|
||||||
|
base::operator()(value);
|
||||||
|
else if (this->spec()->type == 'p')
|
||||||
|
write_null_pointer(char_type());
|
||||||
|
else
|
||||||
|
this->write("(null)");
|
||||||
|
return this->out();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Formats a null-terminated wide C string. */
|
||||||
|
iterator operator()(const wchar_t *value) {
|
||||||
|
if (value)
|
||||||
|
base::operator()(value);
|
||||||
|
else if (this->spec()->type == 'p')
|
||||||
|
write_null_pointer(char_type());
|
||||||
|
else
|
||||||
|
this->write(L"(null)");
|
||||||
|
return this->out();
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator operator()(basic_string_view<char_type> value) {
|
||||||
|
return base::operator()(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator operator()(monostate value) {
|
||||||
|
return base::operator()(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Formats a pointer. */
|
||||||
|
iterator operator()(const void *value) {
|
||||||
|
if (value)
|
||||||
|
return base::operator()(value);
|
||||||
|
this->spec()->type = 0;
|
||||||
|
write_null_pointer(char_type());
|
||||||
|
return this->out();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Formats an argument of a custom (user-defined) type. */
|
||||||
|
iterator operator()(typename basic_format_arg<context_type>::handle handle) {
|
||||||
|
handle.format(context_);
|
||||||
|
return this->out();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct printf_formatter {
|
||||||
|
template <typename ParseContext>
|
||||||
|
auto parse(ParseContext &ctx) -> decltype(ctx.begin()) { return ctx.begin(); }
|
||||||
|
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(const T &value, FormatContext &ctx) -> decltype(ctx.out()) {
|
||||||
|
internal::format_value(internal::get_container(ctx.out()), value);
|
||||||
|
return ctx.out();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** This template formats data and writes the output to a writer. */
|
||||||
|
template <typename OutputIt, typename Char, typename ArgFormatter>
|
||||||
|
class basic_printf_context :
|
||||||
|
// Inherit publicly as a workaround for the icc bug
|
||||||
|
// https://software.intel.com/en-us/forums/intel-c-compiler/topic/783476.
|
||||||
|
public internal::context_base<
|
||||||
|
OutputIt, basic_printf_context<OutputIt, Char, ArgFormatter>, Char> {
|
||||||
|
public:
|
||||||
|
/** The character type for the output. */
|
||||||
|
typedef Char char_type;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct formatter_type { typedef printf_formatter<T> type; };
|
||||||
|
|
||||||
|
private:
|
||||||
|
typedef internal::context_base<OutputIt, basic_printf_context, Char> base;
|
||||||
|
typedef typename base::format_arg format_arg;
|
||||||
|
typedef basic_format_specs<char_type> format_specs;
|
||||||
|
typedef internal::null_terminating_iterator<char_type> iterator;
|
||||||
|
|
||||||
|
void parse_flags(format_specs &spec, iterator &it);
|
||||||
|
|
||||||
|
// Returns the argument with specified index or, if arg_index is equal
|
||||||
|
// to the maximum unsigned value, the next argument.
|
||||||
|
format_arg get_arg(
|
||||||
|
iterator it,
|
||||||
|
unsigned arg_index = (std::numeric_limits<unsigned>::max)());
|
||||||
|
|
||||||
|
// Parses argument index, flags and width and returns the argument index.
|
||||||
|
unsigned parse_header(iterator &it, format_specs &spec);
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Constructs a ``printf_context`` object. References to the arguments and
|
||||||
|
the writer are stored in the context object so make sure they have
|
||||||
|
appropriate lifetimes.
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
basic_printf_context(OutputIt out, basic_string_view<char_type> format_str,
|
||||||
|
basic_format_args<basic_printf_context> args)
|
||||||
|
: base(out, format_str, args) {}
|
||||||
|
|
||||||
|
using base::parse_context;
|
||||||
|
using base::out;
|
||||||
|
using base::advance_to;
|
||||||
|
|
||||||
|
/** Formats stored arguments and writes the output to the range. */
|
||||||
|
void format();
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename OutputIt, typename Char, typename AF>
|
||||||
|
void basic_printf_context<OutputIt, Char, AF>::parse_flags(
|
||||||
|
format_specs &spec, iterator &it) {
|
||||||
|
for (;;) {
|
||||||
|
switch (*it++) {
|
||||||
|
case '-':
|
||||||
|
spec.align_ = ALIGN_LEFT;
|
||||||
|
break;
|
||||||
|
case '+':
|
||||||
|
spec.flags |= SIGN_FLAG | PLUS_FLAG;
|
||||||
|
break;
|
||||||
|
case '0':
|
||||||
|
spec.fill_ = '0';
|
||||||
|
break;
|
||||||
|
case ' ':
|
||||||
|
spec.flags |= SIGN_FLAG;
|
||||||
|
break;
|
||||||
|
case '#':
|
||||||
|
spec.flags |= HASH_FLAG;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
--it;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt, typename Char, typename AF>
|
||||||
|
typename basic_printf_context<OutputIt, Char, AF>::format_arg
|
||||||
|
basic_printf_context<OutputIt, Char, AF>::get_arg(
|
||||||
|
iterator it, unsigned arg_index) {
|
||||||
|
(void)it;
|
||||||
|
if (arg_index == std::numeric_limits<unsigned>::max())
|
||||||
|
return this->do_get_arg(this->parse_context().next_arg_id());
|
||||||
|
return base::get_arg(arg_index - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt, typename Char, typename AF>
|
||||||
|
unsigned basic_printf_context<OutputIt, Char, AF>::parse_header(
|
||||||
|
iterator &it, format_specs &spec) {
|
||||||
|
unsigned arg_index = std::numeric_limits<unsigned>::max();
|
||||||
|
char_type c = *it;
|
||||||
|
if (c >= '0' && c <= '9') {
|
||||||
|
// Parse an argument index (if followed by '$') or a width possibly
|
||||||
|
// preceded with '0' flag(s).
|
||||||
|
internal::error_handler eh;
|
||||||
|
unsigned value = parse_nonnegative_int(it, eh);
|
||||||
|
if (*it == '$') { // value is an argument index
|
||||||
|
++it;
|
||||||
|
arg_index = value;
|
||||||
|
} else {
|
||||||
|
if (c == '0')
|
||||||
|
spec.fill_ = '0';
|
||||||
|
if (value != 0) {
|
||||||
|
// Nonzero value means that we parsed width and don't need to
|
||||||
|
// parse it or flags again, so return now.
|
||||||
|
spec.width_ = value;
|
||||||
|
return arg_index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parse_flags(spec, it);
|
||||||
|
// Parse width.
|
||||||
|
if (*it >= '0' && *it <= '9') {
|
||||||
|
internal::error_handler eh;
|
||||||
|
spec.width_ = parse_nonnegative_int(it, eh);
|
||||||
|
} else if (*it == '*') {
|
||||||
|
++it;
|
||||||
|
spec.width_ = visit_format_arg(
|
||||||
|
internal::printf_width_handler<char_type>(spec), get_arg(it));
|
||||||
|
}
|
||||||
|
return arg_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt, typename Char, typename AF>
|
||||||
|
void basic_printf_context<OutputIt, Char, AF>::format() {
|
||||||
|
auto &buffer = internal::get_container(this->out());
|
||||||
|
auto start = iterator(this->parse_context());
|
||||||
|
auto it = start;
|
||||||
|
using internal::pointer_from;
|
||||||
|
while (*it) {
|
||||||
|
char_type c = *it++;
|
||||||
|
if (c != '%') continue;
|
||||||
|
if (*it == c) {
|
||||||
|
buffer.append(pointer_from(start), pointer_from(it));
|
||||||
|
start = ++it;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
buffer.append(pointer_from(start), pointer_from(it) - 1);
|
||||||
|
|
||||||
|
format_specs spec;
|
||||||
|
spec.align_ = ALIGN_RIGHT;
|
||||||
|
|
||||||
|
// Parse argument index, flags and width.
|
||||||
|
unsigned arg_index = parse_header(it, spec);
|
||||||
|
|
||||||
|
// Parse precision.
|
||||||
|
if (*it == '.') {
|
||||||
|
++it;
|
||||||
|
if ('0' <= *it && *it <= '9') {
|
||||||
|
internal::error_handler eh;
|
||||||
|
spec.precision = static_cast<int>(parse_nonnegative_int(it, eh));
|
||||||
|
} else if (*it == '*') {
|
||||||
|
++it;
|
||||||
|
spec.precision =
|
||||||
|
visit_format_arg(internal::printf_precision_handler(), get_arg(it));
|
||||||
|
} else {
|
||||||
|
spec.precision = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
format_arg arg = get_arg(it, arg_index);
|
||||||
|
if (spec.has(HASH_FLAG) && visit_format_arg(internal::is_zero_int(), arg))
|
||||||
|
spec.flags = static_cast<uint_least8_t>(spec.flags & (~internal::to_unsigned<int>(HASH_FLAG)));
|
||||||
|
if (spec.fill_ == '0') {
|
||||||
|
if (arg.is_arithmetic())
|
||||||
|
spec.align_ = ALIGN_NUMERIC;
|
||||||
|
else
|
||||||
|
spec.fill_ = ' '; // Ignore '0' flag for non-numeric types.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse length and convert the argument to the required type.
|
||||||
|
using internal::convert_arg;
|
||||||
|
switch (*it++) {
|
||||||
|
case 'h':
|
||||||
|
if (*it == 'h')
|
||||||
|
convert_arg<signed char>(arg, *++it);
|
||||||
|
else
|
||||||
|
convert_arg<short>(arg, *it);
|
||||||
|
break;
|
||||||
|
case 'l':
|
||||||
|
if (*it == 'l')
|
||||||
|
convert_arg<long long>(arg, *++it);
|
||||||
|
else
|
||||||
|
convert_arg<long>(arg, *it);
|
||||||
|
break;
|
||||||
|
case 'j':
|
||||||
|
convert_arg<intmax_t>(arg, *it);
|
||||||
|
break;
|
||||||
|
case 'z':
|
||||||
|
convert_arg<std::size_t>(arg, *it);
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
convert_arg<std::ptrdiff_t>(arg, *it);
|
||||||
|
break;
|
||||||
|
case 'L':
|
||||||
|
// printf produces garbage when 'L' is omitted for long double, no
|
||||||
|
// need to do the same.
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
--it;
|
||||||
|
convert_arg<void>(arg, *it);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse type.
|
||||||
|
if (!*it)
|
||||||
|
FMT_THROW(format_error("invalid format string"));
|
||||||
|
spec.type = static_cast<char>(*it++);
|
||||||
|
if (arg.is_integral()) {
|
||||||
|
// Normalize type.
|
||||||
|
switch (spec.type) {
|
||||||
|
case 'i': case 'u':
|
||||||
|
spec.type = 'd';
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
// TODO: handle wchar_t better?
|
||||||
|
visit_format_arg(
|
||||||
|
internal::char_converter<basic_printf_context>(arg), arg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
start = it;
|
||||||
|
|
||||||
|
// Format argument.
|
||||||
|
visit_format_arg(AF(buffer, spec, *this), arg);
|
||||||
|
}
|
||||||
|
buffer.append(pointer_from(start), pointer_from(it));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Buffer>
|
||||||
|
struct basic_printf_context_t {
|
||||||
|
typedef basic_printf_context<
|
||||||
|
std::back_insert_iterator<Buffer>, typename Buffer::value_type> type;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef basic_printf_context_t<internal::buffer>::type printf_context;
|
||||||
|
typedef basic_printf_context_t<internal::wbuffer>::type wprintf_context;
|
||||||
|
|
||||||
|
typedef basic_format_args<printf_context> printf_args;
|
||||||
|
typedef basic_format_args<wprintf_context> wprintf_args;
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Constructs an `~fmt::format_arg_store` object that contains references to
|
||||||
|
arguments and can be implicitly converted to `~fmt::printf_args`.
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template<typename... Args>
|
||||||
|
inline format_arg_store<printf_context, Args...>
|
||||||
|
make_printf_args(const Args &... args) { return {args...}; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Constructs an `~fmt::format_arg_store` object that contains references to
|
||||||
|
arguments and can be implicitly converted to `~fmt::wprintf_args`.
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template<typename... Args>
|
||||||
|
inline format_arg_store<wprintf_context, Args...>
|
||||||
|
make_wprintf_args(const Args &... args) { return {args...}; }
|
||||||
|
|
||||||
|
template <typename S, typename Char = FMT_CHAR(S)>
|
||||||
|
inline std::basic_string<Char>
|
||||||
|
vsprintf(const S &format,
|
||||||
|
basic_format_args<typename basic_printf_context_t<
|
||||||
|
internal::basic_buffer<Char>>::type> args) {
|
||||||
|
basic_memory_buffer<Char> buffer;
|
||||||
|
printf(buffer, to_string_view(format), args);
|
||||||
|
return to_string(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Formats arguments and returns the result as a string.
|
||||||
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
|
std::string message = fmt::sprintf("The answer is %d", 42);
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename S, typename... Args>
|
||||||
|
inline FMT_ENABLE_IF_T(
|
||||||
|
internal::is_string<S>::value, std::basic_string<FMT_CHAR(S)>)
|
||||||
|
sprintf(const S &format, const Args & ... args) {
|
||||||
|
internal::check_format_string<Args...>(format);
|
||||||
|
typedef internal::basic_buffer<FMT_CHAR(S)> buffer;
|
||||||
|
typedef typename basic_printf_context_t<buffer>::type context;
|
||||||
|
format_arg_store<context, Args...> as{ args... };
|
||||||
|
return vsprintf(to_string_view(format),
|
||||||
|
basic_format_args<context>(as));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename S, typename Char = FMT_CHAR(S)>
|
||||||
|
inline int vfprintf(std::FILE *f, const S &format,
|
||||||
|
basic_format_args<typename basic_printf_context_t<
|
||||||
|
internal::basic_buffer<Char>>::type> args) {
|
||||||
|
basic_memory_buffer<Char> buffer;
|
||||||
|
printf(buffer, to_string_view(format), args);
|
||||||
|
std::size_t size = buffer.size();
|
||||||
|
return std::fwrite(
|
||||||
|
buffer.data(), sizeof(Char), size, f) < size ? -1 : static_cast<int>(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Prints formatted data to the file *f*.
|
||||||
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
|
fmt::fprintf(stderr, "Don't %s!", "panic");
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename S, typename... Args>
|
||||||
|
inline FMT_ENABLE_IF_T(internal::is_string<S>::value, int)
|
||||||
|
fprintf(std::FILE *f, const S &format, const Args & ... args) {
|
||||||
|
internal::check_format_string<Args...>(format);
|
||||||
|
typedef internal::basic_buffer<FMT_CHAR(S)> buffer;
|
||||||
|
typedef typename basic_printf_context_t<buffer>::type context;
|
||||||
|
format_arg_store<context, Args...> as{ args... };
|
||||||
|
return vfprintf(f, to_string_view(format),
|
||||||
|
basic_format_args<context>(as));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename S, typename Char = FMT_CHAR(S)>
|
||||||
|
inline int vprintf(const S &format,
|
||||||
|
basic_format_args<typename basic_printf_context_t<
|
||||||
|
internal::basic_buffer<Char>>::type> args) {
|
||||||
|
return vfprintf(stdout, to_string_view(format), args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Prints formatted data to ``stdout``.
|
||||||
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
|
fmt::printf("Elapsed time: %.2f seconds", 1.23);
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename S, typename... Args>
|
||||||
|
inline FMT_ENABLE_IF_T(internal::is_string<S>::value, int)
|
||||||
|
printf(const S &format_str, const Args & ... args) {
|
||||||
|
internal::check_format_string<Args...>(format_str);
|
||||||
|
typedef internal::basic_buffer<FMT_CHAR(S)> buffer;
|
||||||
|
typedef typename basic_printf_context_t<buffer>::type context;
|
||||||
|
format_arg_store<context, Args...> as{ args... };
|
||||||
|
return vprintf(to_string_view(format_str),
|
||||||
|
basic_format_args<context>(as));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename S, typename Char = FMT_CHAR(S)>
|
||||||
|
inline int vfprintf(std::basic_ostream<Char> &os,
|
||||||
|
const S &format,
|
||||||
|
basic_format_args<typename basic_printf_context_t<
|
||||||
|
internal::basic_buffer<Char>>::type> args) {
|
||||||
|
basic_memory_buffer<Char> buffer;
|
||||||
|
printf(buffer, to_string_view(format), args);
|
||||||
|
internal::write(os, buffer);
|
||||||
|
return static_cast<int>(buffer.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Prints formatted data to the stream *os*.
|
||||||
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
|
fmt::fprintf(cerr, "Don't %s!", "panic");
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename S, typename... Args>
|
||||||
|
inline FMT_ENABLE_IF_T(internal::is_string<S>::value, int)
|
||||||
|
fprintf(std::basic_ostream<FMT_CHAR(S)> &os,
|
||||||
|
const S &format_str, const Args & ... args) {
|
||||||
|
internal::check_format_string<Args...>(format_str);
|
||||||
|
typedef internal::basic_buffer<FMT_CHAR(S)> buffer;
|
||||||
|
typedef typename basic_printf_context_t<buffer>::type context;
|
||||||
|
format_arg_store<context, Args...> as{ args... };
|
||||||
|
return vfprintf(os, to_string_view(format_str),
|
||||||
|
basic_format_args<context>(as));
|
||||||
|
}
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // FMT_PRINTF_H_
|
308
lib/include/fmt/ranges.h
Normal file
308
lib/include/fmt/ranges.h
Normal file
@ -0,0 +1,308 @@
|
|||||||
|
// Formatting library for C++ - the core API
|
||||||
|
//
|
||||||
|
// Copyright (c) 2012 - present, Victor Zverovich
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018 - present, Remotion (Igor Schulz)
|
||||||
|
// All Rights Reserved
|
||||||
|
// {fmt} support for ranges, containers and types tuple interface.
|
||||||
|
|
||||||
|
#ifndef FMT_RANGES_H_
|
||||||
|
#define FMT_RANGES_H_
|
||||||
|
|
||||||
|
#include "format.h"
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
// output only up to N items from the range.
|
||||||
|
#ifndef FMT_RANGE_OUTPUT_LENGTH_LIMIT
|
||||||
|
# define FMT_RANGE_OUTPUT_LENGTH_LIMIT 256
|
||||||
|
#endif
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
struct formatting_base {
|
||||||
|
template <typename ParseContext>
|
||||||
|
FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin()) {
|
||||||
|
return ctx.begin();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char, typename Enable = void>
|
||||||
|
struct formatting_range : formatting_base<Char> {
|
||||||
|
static FMT_CONSTEXPR_DECL const std::size_t range_length_limit =
|
||||||
|
FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the range.
|
||||||
|
Char prefix;
|
||||||
|
Char delimiter;
|
||||||
|
Char postfix;
|
||||||
|
formatting_range() : prefix('{'), delimiter(','), postfix('}') {}
|
||||||
|
static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true;
|
||||||
|
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char, typename Enable = void>
|
||||||
|
struct formatting_tuple : formatting_base<Char> {
|
||||||
|
Char prefix;
|
||||||
|
Char delimiter;
|
||||||
|
Char postfix;
|
||||||
|
formatting_tuple() : prefix('('), delimiter(','), postfix(')') {}
|
||||||
|
static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true;
|
||||||
|
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
template <typename RangeT, typename OutputIterator>
|
||||||
|
void copy(const RangeT &range, OutputIterator out) {
|
||||||
|
for (auto it = range.begin(), end = range.end(); it != end; ++it)
|
||||||
|
*out++ = *it;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIterator>
|
||||||
|
void copy(const char *str, OutputIterator out) {
|
||||||
|
const char *p_curr = str;
|
||||||
|
while (*p_curr) {
|
||||||
|
*out++ = *p_curr++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIterator>
|
||||||
|
void copy(char ch, OutputIterator out) {
|
||||||
|
*out++ = ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return true value if T has std::string interface, like std::string_view.
|
||||||
|
template <typename T>
|
||||||
|
class is_like_std_string {
|
||||||
|
template <typename U>
|
||||||
|
static auto check(U *p) ->
|
||||||
|
decltype(p->find('a'), p->length(), p->data(), int());
|
||||||
|
template <typename>
|
||||||
|
static void check(...);
|
||||||
|
|
||||||
|
public:
|
||||||
|
static FMT_CONSTEXPR_DECL const bool value =
|
||||||
|
!std::is_void<decltype(check<T>(FMT_NULL))>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
struct is_like_std_string<fmt::basic_string_view<Char>> : std::true_type {};
|
||||||
|
|
||||||
|
template <typename... Ts>
|
||||||
|
struct conditional_helper {};
|
||||||
|
|
||||||
|
template <typename T, typename _ = void>
|
||||||
|
struct is_range_ : std::false_type {};
|
||||||
|
|
||||||
|
#if !FMT_MSC_VER || FMT_MSC_VER > 1800
|
||||||
|
template <typename T>
|
||||||
|
struct is_range_<T, typename std::conditional<
|
||||||
|
false,
|
||||||
|
conditional_helper<decltype(internal::declval<T>().begin()),
|
||||||
|
decltype(internal::declval<T>().end())>,
|
||||||
|
void>::type> : std::true_type {};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// tuple_size and tuple_element check.
|
||||||
|
template <typename T>
|
||||||
|
class is_tuple_like_ {
|
||||||
|
template <typename U>
|
||||||
|
static auto check(U *p) ->
|
||||||
|
decltype(std::tuple_size<U>::value,
|
||||||
|
internal::declval<typename std::tuple_element<0, U>::type>(), int());
|
||||||
|
template <typename>
|
||||||
|
static void check(...);
|
||||||
|
|
||||||
|
public:
|
||||||
|
static FMT_CONSTEXPR_DECL const bool value =
|
||||||
|
!std::is_void<decltype(check<T>(FMT_NULL))>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check for integer_sequence
|
||||||
|
#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1900
|
||||||
|
template <typename T, T... N>
|
||||||
|
using integer_sequence = std::integer_sequence<T, N...>;
|
||||||
|
template <std::size_t... N>
|
||||||
|
using index_sequence = std::index_sequence<N...>;
|
||||||
|
template <std::size_t N>
|
||||||
|
using make_index_sequence = std::make_index_sequence<N>;
|
||||||
|
#else
|
||||||
|
template <typename T, T... N>
|
||||||
|
struct integer_sequence {
|
||||||
|
typedef T value_type;
|
||||||
|
|
||||||
|
static FMT_CONSTEXPR std::size_t size() {
|
||||||
|
return sizeof...(N);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <std::size_t... N>
|
||||||
|
using index_sequence = integer_sequence<std::size_t, N...>;
|
||||||
|
|
||||||
|
template <typename T, std::size_t N, T... Ns>
|
||||||
|
struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...> {};
|
||||||
|
template <typename T, T... Ns>
|
||||||
|
struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...> {};
|
||||||
|
|
||||||
|
template <std::size_t N>
|
||||||
|
using make_index_sequence = make_integer_sequence<std::size_t, N>;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <class Tuple, class F, size_t... Is>
|
||||||
|
void for_each(index_sequence<Is...>, Tuple &&tup, F &&f) FMT_NOEXCEPT {
|
||||||
|
using std::get;
|
||||||
|
// using free function get<I>(T) now.
|
||||||
|
const int _[] = {0, ((void)f(get<Is>(tup)), 0)...};
|
||||||
|
(void)_; // blocks warnings
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
FMT_CONSTEXPR make_index_sequence<std::tuple_size<T>::value>
|
||||||
|
get_indexes(T const &) { return {}; }
|
||||||
|
|
||||||
|
template <class Tuple, class F>
|
||||||
|
void for_each(Tuple &&tup, F &&f) {
|
||||||
|
const auto indexes = get_indexes(tup);
|
||||||
|
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Arg>
|
||||||
|
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&,
|
||||||
|
typename std::enable_if<
|
||||||
|
!is_like_std_string<typename std::decay<Arg>::type>::value>::type* = nullptr) {
|
||||||
|
return add_space ? " {}" : "{}";
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Arg>
|
||||||
|
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&,
|
||||||
|
typename std::enable_if<
|
||||||
|
is_like_std_string<typename std::decay<Arg>::type>::value>::type* = nullptr) {
|
||||||
|
return add_space ? " \"{}\"" : "\"{}\"";
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char*) {
|
||||||
|
return add_space ? " \"{}\"" : "\"{}\"";
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t*) {
|
||||||
|
return add_space ? L" \"{}\"" : L"\"{}\"";
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char) {
|
||||||
|
return add_space ? " '{}'" : "'{}'";
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t) {
|
||||||
|
return add_space ? L" '{}'" : L"'{}'";
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct is_tuple_like {
|
||||||
|
static FMT_CONSTEXPR_DECL const bool value =
|
||||||
|
internal::is_tuple_like_<T>::value && !internal::is_range_<T>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename TupleT, typename Char>
|
||||||
|
struct formatter<TupleT, Char,
|
||||||
|
typename std::enable_if<fmt::is_tuple_like<TupleT>::value>::type> {
|
||||||
|
private:
|
||||||
|
// C++11 generic lambda for format()
|
||||||
|
template <typename FormatContext>
|
||||||
|
struct format_each {
|
||||||
|
template <typename T>
|
||||||
|
void operator()(const T& v) {
|
||||||
|
if (i > 0) {
|
||||||
|
if (formatting.add_prepostfix_space) {
|
||||||
|
*out++ = ' ';
|
||||||
|
}
|
||||||
|
internal::copy(formatting.delimiter, out);
|
||||||
|
}
|
||||||
|
format_to(out,
|
||||||
|
internal::format_str_quoted(
|
||||||
|
(formatting.add_delimiter_spaces && i > 0), v),
|
||||||
|
v);
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
|
||||||
|
formatting_tuple<Char>& formatting;
|
||||||
|
std::size_t& i;
|
||||||
|
typename std::add_lvalue_reference<decltype(std::declval<FormatContext>().out())>::type out;
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
formatting_tuple<Char> formatting;
|
||||||
|
|
||||||
|
template <typename ParseContext>
|
||||||
|
FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin()) {
|
||||||
|
return formatting.parse(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FormatContext = format_context>
|
||||||
|
auto format(const TupleT &values, FormatContext &ctx) -> decltype(ctx.out()) {
|
||||||
|
auto out = ctx.out();
|
||||||
|
std::size_t i = 0;
|
||||||
|
internal::copy(formatting.prefix, out);
|
||||||
|
|
||||||
|
internal::for_each(values, format_each<FormatContext>{formatting, i, out});
|
||||||
|
if (formatting.add_prepostfix_space) {
|
||||||
|
*out++ = ' ';
|
||||||
|
}
|
||||||
|
internal::copy(formatting.postfix, out);
|
||||||
|
|
||||||
|
return ctx.out();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct is_range {
|
||||||
|
static FMT_CONSTEXPR_DECL const bool value =
|
||||||
|
internal::is_range_<T>::value && !internal::is_like_std_string<T>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename RangeT, typename Char>
|
||||||
|
struct formatter<RangeT, Char,
|
||||||
|
typename std::enable_if<fmt::is_range<RangeT>::value>::type> {
|
||||||
|
|
||||||
|
formatting_range<Char> formatting;
|
||||||
|
|
||||||
|
template <typename ParseContext>
|
||||||
|
FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin()) {
|
||||||
|
return formatting.parse(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FormatContext>
|
||||||
|
typename FormatContext::iterator format(
|
||||||
|
const RangeT &values, FormatContext &ctx) {
|
||||||
|
auto out = ctx.out();
|
||||||
|
internal::copy(formatting.prefix, out);
|
||||||
|
std::size_t i = 0;
|
||||||
|
for (auto it = values.begin(), end = values.end(); it != end; ++it) {
|
||||||
|
if (i > 0) {
|
||||||
|
if (formatting.add_prepostfix_space) {
|
||||||
|
*out++ = ' ';
|
||||||
|
}
|
||||||
|
internal::copy(formatting.delimiter, out);
|
||||||
|
}
|
||||||
|
format_to(out,
|
||||||
|
internal::format_str_quoted(
|
||||||
|
(formatting.add_delimiter_spaces && i > 0), *it),
|
||||||
|
*it);
|
||||||
|
if (++i > formatting.range_length_limit) {
|
||||||
|
format_to(out, " ... <other elements>");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (formatting.add_prepostfix_space) {
|
||||||
|
*out++ = ' ';
|
||||||
|
}
|
||||||
|
internal::copy(formatting.postfix, out);
|
||||||
|
return ctx.out();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // FMT_RANGES_H_
|
||||||
|
|
160
lib/include/fmt/time.h
Normal file
160
lib/include/fmt/time.h
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
// Formatting library for C++ - time formatting
|
||||||
|
//
|
||||||
|
// Copyright (c) 2012 - present, Victor Zverovich
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
|
#ifndef FMT_TIME_H_
|
||||||
|
#define FMT_TIME_H_
|
||||||
|
|
||||||
|
#include "format.h"
|
||||||
|
#include <ctime>
|
||||||
|
#include <locale>
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
// Prevents expansion of a preceding token as a function-style macro.
|
||||||
|
// Usage: f FMT_NOMACRO()
|
||||||
|
#define FMT_NOMACRO
|
||||||
|
|
||||||
|
namespace internal{
|
||||||
|
inline null<> localtime_r FMT_NOMACRO(...) { return null<>(); }
|
||||||
|
inline null<> localtime_s(...) { return null<>(); }
|
||||||
|
inline null<> gmtime_r(...) { return null<>(); }
|
||||||
|
inline null<> gmtime_s(...) { return null<>(); }
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
// Thread-safe replacement for std::localtime
|
||||||
|
inline std::tm localtime(std::time_t time) {
|
||||||
|
struct dispatcher {
|
||||||
|
std::time_t time_;
|
||||||
|
std::tm tm_;
|
||||||
|
|
||||||
|
dispatcher(std::time_t t): time_(t) {}
|
||||||
|
|
||||||
|
bool run() {
|
||||||
|
using namespace fmt::internal;
|
||||||
|
return handle(localtime_r(&time_, &tm_));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool handle(std::tm *tm) { return tm != FMT_NULL; }
|
||||||
|
|
||||||
|
bool handle(internal::null<>) {
|
||||||
|
using namespace fmt::internal;
|
||||||
|
return fallback(localtime_s(&tm_, &time_));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool fallback(int res) { return res == 0; }
|
||||||
|
|
||||||
|
#if !FMT_MSC_VER
|
||||||
|
bool fallback(internal::null<>) {
|
||||||
|
using namespace fmt::internal;
|
||||||
|
std::tm *tm = std::localtime(&time_);
|
||||||
|
if (tm) tm_ = *tm;
|
||||||
|
return tm != FMT_NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
dispatcher lt(time);
|
||||||
|
// Too big time values may be unsupported.
|
||||||
|
if (!lt.run())
|
||||||
|
FMT_THROW(format_error("time_t value out of range"));
|
||||||
|
return lt.tm_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Thread-safe replacement for std::gmtime
|
||||||
|
inline std::tm gmtime(std::time_t time) {
|
||||||
|
struct dispatcher {
|
||||||
|
std::time_t time_;
|
||||||
|
std::tm tm_;
|
||||||
|
|
||||||
|
dispatcher(std::time_t t): time_(t) {}
|
||||||
|
|
||||||
|
bool run() {
|
||||||
|
using namespace fmt::internal;
|
||||||
|
return handle(gmtime_r(&time_, &tm_));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool handle(std::tm *tm) { return tm != FMT_NULL; }
|
||||||
|
|
||||||
|
bool handle(internal::null<>) {
|
||||||
|
using namespace fmt::internal;
|
||||||
|
return fallback(gmtime_s(&tm_, &time_));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool fallback(int res) { return res == 0; }
|
||||||
|
|
||||||
|
#if !FMT_MSC_VER
|
||||||
|
bool fallback(internal::null<>) {
|
||||||
|
std::tm *tm = std::gmtime(&time_);
|
||||||
|
if (tm) tm_ = *tm;
|
||||||
|
return tm != FMT_NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
dispatcher gt(time);
|
||||||
|
// Too big time values may be unsupported.
|
||||||
|
if (!gt.run())
|
||||||
|
FMT_THROW(format_error("time_t value out of range"));
|
||||||
|
return gt.tm_;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
inline std::size_t strftime(char *str, std::size_t count, const char *format,
|
||||||
|
const std::tm *time) {
|
||||||
|
return std::strftime(str, count, format, time);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::size_t strftime(wchar_t *str, std::size_t count,
|
||||||
|
const wchar_t *format, const std::tm *time) {
|
||||||
|
return std::wcsftime(str, count, format, time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
struct formatter<std::tm, Char> {
|
||||||
|
template <typename ParseContext>
|
||||||
|
auto parse(ParseContext &ctx) -> decltype(ctx.begin()) {
|
||||||
|
auto it = ctx.begin();
|
||||||
|
if (it != ctx.end() && *it == ':')
|
||||||
|
++it;
|
||||||
|
auto end = it;
|
||||||
|
while (end != ctx.end() && *end != '}')
|
||||||
|
++end;
|
||||||
|
tm_format.reserve(internal::to_unsigned(end - it + 1));
|
||||||
|
tm_format.append(it, end);
|
||||||
|
tm_format.push_back('\0');
|
||||||
|
return end;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(const std::tm &tm, FormatContext &ctx) -> decltype(ctx.out()) {
|
||||||
|
basic_memory_buffer<Char> buf;
|
||||||
|
std::size_t start = buf.size();
|
||||||
|
for (;;) {
|
||||||
|
std::size_t size = buf.capacity() - start;
|
||||||
|
std::size_t count =
|
||||||
|
internal::strftime(&buf[start], size, &tm_format[0], &tm);
|
||||||
|
if (count != 0) {
|
||||||
|
buf.resize(start + count);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (size >= tm_format.size() * 256) {
|
||||||
|
// If the buffer is 256 times larger than the format string, assume
|
||||||
|
// that `strftime` gives an empty result. There doesn't seem to be a
|
||||||
|
// better way to distinguish the two cases:
|
||||||
|
// https://github.com/fmtlib/fmt/issues/367
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const std::size_t MIN_GROWTH = 10;
|
||||||
|
buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH));
|
||||||
|
}
|
||||||
|
return std::copy(buf.begin(), buf.end(), ctx.out());
|
||||||
|
}
|
||||||
|
|
||||||
|
basic_memory_buffer<Char> tm_format;
|
||||||
|
};
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // FMT_TIME_H_
|
186
lib/include/mh/MinHook.h
Normal file
186
lib/include/mh/MinHook.h
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
/*
|
||||||
|
* MinHook - The Minimalistic API Hooking Library for x64/x86
|
||||||
|
* Copyright (C) 2009-2017 Tsuda Kageyu.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||||
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||||
|
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
|
||||||
|
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if !(defined _M_IX86) && !(defined _M_X64) && !(defined __i386__) && !(defined __x86_64__)
|
||||||
|
#error MinHook supports only x86 and x64 systems.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
// MinHook Error Codes.
|
||||||
|
typedef enum MH_STATUS
|
||||||
|
{
|
||||||
|
// Unknown error. Should not be returned.
|
||||||
|
MH_UNKNOWN = -1,
|
||||||
|
|
||||||
|
// Successful.
|
||||||
|
MH_OK = 0,
|
||||||
|
|
||||||
|
// MinHook is already initialized.
|
||||||
|
MH_ERROR_ALREADY_INITIALIZED,
|
||||||
|
|
||||||
|
// MinHook is not initialized yet, or already uninitialized.
|
||||||
|
MH_ERROR_NOT_INITIALIZED,
|
||||||
|
|
||||||
|
// The hook for the specified target function is already created.
|
||||||
|
MH_ERROR_ALREADY_CREATED,
|
||||||
|
|
||||||
|
// The hook for the specified target function is not created yet.
|
||||||
|
MH_ERROR_NOT_CREATED,
|
||||||
|
|
||||||
|
// The hook for the specified target function is already enabled.
|
||||||
|
MH_ERROR_ENABLED,
|
||||||
|
|
||||||
|
// The hook for the specified target function is not enabled yet, or already
|
||||||
|
// disabled.
|
||||||
|
MH_ERROR_DISABLED,
|
||||||
|
|
||||||
|
// The specified pointer is invalid. It points the address of non-allocated
|
||||||
|
// and/or non-executable region.
|
||||||
|
MH_ERROR_NOT_EXECUTABLE,
|
||||||
|
|
||||||
|
// The specified target function cannot be hooked.
|
||||||
|
MH_ERROR_UNSUPPORTED_FUNCTION,
|
||||||
|
|
||||||
|
// Failed to allocate memory.
|
||||||
|
MH_ERROR_MEMORY_ALLOC,
|
||||||
|
|
||||||
|
// Failed to change the memory protection.
|
||||||
|
MH_ERROR_MEMORY_PROTECT,
|
||||||
|
|
||||||
|
// The specified module is not loaded.
|
||||||
|
MH_ERROR_MODULE_NOT_FOUND,
|
||||||
|
|
||||||
|
// The specified function is not found.
|
||||||
|
MH_ERROR_FUNCTION_NOT_FOUND
|
||||||
|
}
|
||||||
|
MH_STATUS;
|
||||||
|
|
||||||
|
// Can be passed as a parameter to MH_EnableHook, MH_DisableHook,
|
||||||
|
// MH_QueueEnableHook or MH_QueueDisableHook.
|
||||||
|
#define MH_ALL_HOOKS NULL
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Initialize the MinHook library. You must call this function EXACTLY ONCE
|
||||||
|
// at the beginning of your program.
|
||||||
|
MH_STATUS WINAPI MH_Initialize(VOID);
|
||||||
|
|
||||||
|
// Uninitialize the MinHook library. You must call this function EXACTLY
|
||||||
|
// ONCE at the end of your program.
|
||||||
|
MH_STATUS WINAPI MH_Uninitialize(VOID);
|
||||||
|
|
||||||
|
// Creates a Hook for the specified target function, in disabled state.
|
||||||
|
// Parameters:
|
||||||
|
// pTarget [in] A pointer to the target function, which will be
|
||||||
|
// overridden by the detour function.
|
||||||
|
// pDetour [in] A pointer to the detour function, which will override
|
||||||
|
// the target function.
|
||||||
|
// ppOriginal [out] A pointer to the trampoline function, which will be
|
||||||
|
// used to call the original target function.
|
||||||
|
// This parameter can be NULL.
|
||||||
|
MH_STATUS WINAPI MH_CreateHook(LPVOID pTarget, LPVOID pDetour, LPVOID *ppOriginal);
|
||||||
|
|
||||||
|
// Creates a Hook for the specified API function, in disabled state.
|
||||||
|
// Parameters:
|
||||||
|
// pszModule [in] A pointer to the loaded module name which contains the
|
||||||
|
// target function.
|
||||||
|
// pszTarget [in] A pointer to the target function name, which will be
|
||||||
|
// overridden by the detour function.
|
||||||
|
// pDetour [in] A pointer to the detour function, which will override
|
||||||
|
// the target function.
|
||||||
|
// ppOriginal [out] A pointer to the trampoline function, which will be
|
||||||
|
// used to call the original target function.
|
||||||
|
// This parameter can be NULL.
|
||||||
|
MH_STATUS WINAPI MH_CreateHookApi(
|
||||||
|
LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, LPVOID *ppOriginal);
|
||||||
|
|
||||||
|
// Creates a Hook for the specified API function, in disabled state.
|
||||||
|
// Parameters:
|
||||||
|
// pszModule [in] A pointer to the loaded module name which contains the
|
||||||
|
// target function.
|
||||||
|
// pszTarget [in] A pointer to the target function name, which will be
|
||||||
|
// overridden by the detour function.
|
||||||
|
// pDetour [in] A pointer to the detour function, which will override
|
||||||
|
// the target function.
|
||||||
|
// ppOriginal [out] A pointer to the trampoline function, which will be
|
||||||
|
// used to call the original target function.
|
||||||
|
// This parameter can be NULL.
|
||||||
|
// ppTarget [out] A pointer to the target function, which will be used
|
||||||
|
// with other functions.
|
||||||
|
// This parameter can be NULL.
|
||||||
|
MH_STATUS WINAPI MH_CreateHookApiEx(
|
||||||
|
LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, LPVOID *ppOriginal, LPVOID *ppTarget);
|
||||||
|
|
||||||
|
// Removes an already created hook.
|
||||||
|
// Parameters:
|
||||||
|
// pTarget [in] A pointer to the target function.
|
||||||
|
MH_STATUS WINAPI MH_RemoveHook(LPVOID pTarget);
|
||||||
|
|
||||||
|
// Enables an already created hook.
|
||||||
|
// Parameters:
|
||||||
|
// pTarget [in] A pointer to the target function.
|
||||||
|
// If this parameter is MH_ALL_HOOKS, all created hooks are
|
||||||
|
// enabled in one go.
|
||||||
|
MH_STATUS WINAPI MH_EnableHook(LPVOID pTarget);
|
||||||
|
|
||||||
|
// Disables an already created hook.
|
||||||
|
// Parameters:
|
||||||
|
// pTarget [in] A pointer to the target function.
|
||||||
|
// If this parameter is MH_ALL_HOOKS, all created hooks are
|
||||||
|
// disabled in one go.
|
||||||
|
MH_STATUS WINAPI MH_DisableHook(LPVOID pTarget);
|
||||||
|
|
||||||
|
// Queues to enable an already created hook.
|
||||||
|
// Parameters:
|
||||||
|
// pTarget [in] A pointer to the target function.
|
||||||
|
// If this parameter is MH_ALL_HOOKS, all created hooks are
|
||||||
|
// queued to be enabled.
|
||||||
|
MH_STATUS WINAPI MH_QueueEnableHook(LPVOID pTarget);
|
||||||
|
|
||||||
|
// Queues to disable an already created hook.
|
||||||
|
// Parameters:
|
||||||
|
// pTarget [in] A pointer to the target function.
|
||||||
|
// If this parameter is MH_ALL_HOOKS, all created hooks are
|
||||||
|
// queued to be disabled.
|
||||||
|
MH_STATUS WINAPI MH_QueueDisableHook(LPVOID pTarget);
|
||||||
|
|
||||||
|
// Applies all queued changes in one go.
|
||||||
|
MH_STATUS WINAPI MH_ApplyQueued(VOID);
|
||||||
|
|
||||||
|
// Translates the MH_STATUS to its name as a string.
|
||||||
|
const char * WINAPI MH_StatusToString(MH_STATUS status);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
20406
lib/include/nlohmann/json.hpp
Normal file
20406
lib/include/nlohmann/json.hpp
Normal file
File diff suppressed because it is too large
Load Diff
285
src/BigBaseV2.vcxproj
Normal file
285
src/BigBaseV2.vcxproj
Normal file
@ -0,0 +1,285 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<ItemGroup Label="ProjectConfigurations">
|
||||||
|
<ProjectConfiguration Include="Debug|x64">
|
||||||
|
<Configuration>Debug</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release|x64">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
</ItemGroup>
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<VCProjectVersion>15.0</VCProjectVersion>
|
||||||
|
<ProjectGuid>{D436EE4E-DBF5-4B87-9715-A1C2774BC25F}</ProjectGuid>
|
||||||
|
<Keyword>Win32Proj</Keyword>
|
||||||
|
<RootNamespace>BigBaseV2</RootNamespace>
|
||||||
|
<WindowsTargetPlatformVersion>10.0.17763.0</WindowsTargetPlatformVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||||
|
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||||
|
<UseDebugLibraries>true</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v141</PlatformToolset>
|
||||||
|
<CharacterSet>NotSet</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||||
|
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||||
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v141</PlatformToolset>
|
||||||
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
<CharacterSet>NotSet</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||||
|
<ImportGroup Label="ExtensionSettings">
|
||||||
|
<Import Project="$(VCTargetsPath)\BuildCustomizations\masm.props" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="Shared">
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<PropertyGroup Label="UserMacros" />
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
<LinkIncremental>true</LinkIncremental>
|
||||||
|
<IntDir>$(SolutionDir)bin\$(Configuration)\int\$(ProjectName)\</IntDir>
|
||||||
|
<OutDir>$(SolutionDir)bin\$(Configuration)\</OutDir>
|
||||||
|
<IncludePath>C:\boost_1_69_0;$(IncludePath)</IncludePath>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<LinkIncremental>false</LinkIncremental>
|
||||||
|
<IntDir>$(SolutionDir)bin\$(Configuration)\int\$(ProjectName)\</IntDir>
|
||||||
|
<OutDir>$(SolutionDir)bin\$(Configuration)\</OutDir>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
<ClCompile>
|
||||||
|
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||||
|
<WarningLevel>Level4</WarningLevel>
|
||||||
|
<Optimization>Disabled</Optimization>
|
||||||
|
<SDLCheck>true</SDLCheck>
|
||||||
|
<PreprocessorDefinitions>_DEBUG;BIGBASEV2_DEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<ConformanceMode>true</ConformanceMode>
|
||||||
|
<OmitFramePointers>false</OmitFramePointers>
|
||||||
|
<InlineFunctionExpansion>Disabled</InlineFunctionExpansion>
|
||||||
|
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
|
||||||
|
<StringPooling>true</StringPooling>
|
||||||
|
<EnforceTypeConversionRules>true</EnforceTypeConversionRules>
|
||||||
|
<RuntimeTypeInfo>true</RuntimeTypeInfo>
|
||||||
|
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||||
|
<PrecompiledHeaderFile>common.hpp</PrecompiledHeaderFile>
|
||||||
|
<AdditionalIncludeDirectories>$(SolutionDir)lib\include\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
<DisableSpecificWarnings>4307;4100;4201;</DisableSpecificWarnings>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Windows</SubSystem>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
<MapFileName>$(OutDir)$(TargetName).map</MapFileName>
|
||||||
|
<MapExports>true</MapExports>
|
||||||
|
<AdditionalLibraryDirectories>$(SolutionDir)lib\bin\$(Configuration)\;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||||
|
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;opengl32.lib;MinHook/MinHook.lib;StackWalker/StackWalker.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<ClCompile>
|
||||||
|
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||||
|
<WarningLevel>Level4</WarningLevel>
|
||||||
|
<Optimization>MaxSpeed</Optimization>
|
||||||
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
|
<SDLCheck>true</SDLCheck>
|
||||||
|
<PreprocessorDefinitions>NDEBUG;BIGBASEV2_RELEASE;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<ConformanceMode>true</ConformanceMode>
|
||||||
|
<InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
|
||||||
|
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
|
||||||
|
<OmitFramePointers>true</OmitFramePointers>
|
||||||
|
<StringPooling>true</StringPooling>
|
||||||
|
<EnforceTypeConversionRules>true</EnforceTypeConversionRules>
|
||||||
|
<RuntimeTypeInfo>true</RuntimeTypeInfo>
|
||||||
|
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||||
|
<PrecompiledHeaderFile>common.hpp</PrecompiledHeaderFile>
|
||||||
|
<AdditionalIncludeDirectories>$(SolutionDir)lib\include\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
<DisableSpecificWarnings>4307;4100;4201;</DisableSpecificWarnings>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Windows</SubSystem>
|
||||||
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
<MapFileName>$(OutDir)$(TargetName).map</MapFileName>
|
||||||
|
<MapExports>true</MapExports>
|
||||||
|
<AdditionalLibraryDirectories>$(SolutionDir)lib\bin\$(Configuration)\;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||||
|
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;opengl32.lib;MinHook/MinHook.lib;StackWalker/StackWalker.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClInclude Include="common.hpp" />
|
||||||
|
<ClInclude Include="crossmap.hpp" />
|
||||||
|
<ClInclude Include="detour_hook.hpp" />
|
||||||
|
<ClInclude Include="fiber_pool.hpp" />
|
||||||
|
<ClInclude Include="fonts.hpp" />
|
||||||
|
<ClInclude Include="function_types.hpp" />
|
||||||
|
<ClInclude Include="gta\array.hpp" />
|
||||||
|
<ClInclude Include="features.hpp" />
|
||||||
|
<ClInclude Include="gta\base.hpp" />
|
||||||
|
<ClInclude Include="gta\color.hpp" />
|
||||||
|
<ClInclude Include="gta\enums.hpp" />
|
||||||
|
<ClInclude Include="gta\extensible.hpp" />
|
||||||
|
<ClInclude Include="gta\fwddec.hpp" />
|
||||||
|
<ClInclude Include="gta\matrix.hpp" />
|
||||||
|
<ClInclude Include="gta\node_list.hpp" />
|
||||||
|
<ClInclude Include="gta\ped_factory.hpp" />
|
||||||
|
<ClInclude Include="gta\player.hpp" />
|
||||||
|
<ClInclude Include="gta\ref_aware.hpp" />
|
||||||
|
<ClInclude Include="gta\script_program.hpp" />
|
||||||
|
<ClInclude Include="gta\vector.hpp" />
|
||||||
|
<ClInclude Include="gta_util.hpp" />
|
||||||
|
<ClInclude Include="invoker.hpp" />
|
||||||
|
<ClInclude Include="gta\natives.hpp" />
|
||||||
|
<ClInclude Include="gta\joaat.hpp" />
|
||||||
|
<ClInclude Include="gta\net_game_event.hpp" />
|
||||||
|
<ClInclude Include="gta\script_handler.hpp" />
|
||||||
|
<ClInclude Include="gta\script_id.hpp" />
|
||||||
|
<ClInclude Include="gta\script_thread.hpp" />
|
||||||
|
<ClInclude Include="gta\tls_context.hpp" />
|
||||||
|
<ClInclude Include="gui.hpp" />
|
||||||
|
<ClInclude Include="hooking.hpp" />
|
||||||
|
<ClInclude Include="imgui\imconfig.h" />
|
||||||
|
<ClInclude Include="imgui\imgui.h" />
|
||||||
|
<ClInclude Include="imgui\imgui_impl_dx11.h" />
|
||||||
|
<ClInclude Include="imgui\imgui_impl_win32.h" />
|
||||||
|
<ClInclude Include="imgui\imgui_internal.h" />
|
||||||
|
<ClInclude Include="imgui\imstb_rectpack.h" />
|
||||||
|
<ClInclude Include="imgui\imstb_textedit.h" />
|
||||||
|
<ClInclude Include="imgui\imstb_truetype.h" />
|
||||||
|
<ClInclude Include="logger.hpp" />
|
||||||
|
<ClInclude Include="memory\fwddec.hpp" />
|
||||||
|
<ClInclude Include="memory\handle.hpp" />
|
||||||
|
<ClInclude Include="memory\all.hpp" />
|
||||||
|
<ClInclude Include="memory\module.hpp" />
|
||||||
|
<ClInclude Include="memory\pattern.hpp" />
|
||||||
|
<ClInclude Include="memory\pattern_batch.hpp" />
|
||||||
|
<ClInclude Include="memory\range.hpp" />
|
||||||
|
<ClInclude Include="natives.hpp" />
|
||||||
|
<ClInclude Include="pointers.hpp" />
|
||||||
|
<ClInclude Include="renderer.hpp" />
|
||||||
|
<ClInclude Include="script.hpp" />
|
||||||
|
<ClInclude Include="script_global.hpp" />
|
||||||
|
<ClInclude Include="script_hook.hpp" />
|
||||||
|
<ClInclude Include="script_mgr.hpp" />
|
||||||
|
<ClInclude Include="vmt_hook.hpp" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="detour_hook.cpp" />
|
||||||
|
<ClCompile Include="features.cpp" />
|
||||||
|
<ClCompile Include="fiber_pool.cpp" />
|
||||||
|
<ClCompile Include="fonts.cpp">
|
||||||
|
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
|
||||||
|
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
</PrecompiledHeaderFile>
|
||||||
|
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
|
||||||
|
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
</PrecompiledHeaderFile>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="invoker.cpp" />
|
||||||
|
<ClCompile Include="gui.cpp" />
|
||||||
|
<ClCompile Include="hooking.cpp" />
|
||||||
|
<ClCompile Include="imgui\imgui.cpp">
|
||||||
|
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
|
||||||
|
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
</PrecompiledHeaderFile>
|
||||||
|
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
|
||||||
|
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
</PrecompiledHeaderFile>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="imgui\imgui_demo.cpp">
|
||||||
|
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
|
||||||
|
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
</PrecompiledHeaderFile>
|
||||||
|
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
|
||||||
|
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
</PrecompiledHeaderFile>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="imgui\imgui_draw.cpp">
|
||||||
|
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
|
||||||
|
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
</PrecompiledHeaderFile>
|
||||||
|
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
|
||||||
|
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
</PrecompiledHeaderFile>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="imgui\imgui_impl_dx11.cpp">
|
||||||
|
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
|
||||||
|
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
</PrecompiledHeaderFile>
|
||||||
|
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
|
||||||
|
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
</PrecompiledHeaderFile>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="imgui\imgui_impl_win32.cpp">
|
||||||
|
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
|
||||||
|
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
</PrecompiledHeaderFile>
|
||||||
|
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
|
||||||
|
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
</PrecompiledHeaderFile>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="imgui\imgui_widgets.cpp">
|
||||||
|
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
|
||||||
|
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
</PrecompiledHeaderFile>
|
||||||
|
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
|
||||||
|
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
</PrecompiledHeaderFile>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="main.cpp" />
|
||||||
|
<ClCompile Include="common.cpp">
|
||||||
|
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
||||||
|
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="memory\module.cpp">
|
||||||
|
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
||||||
|
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">../common.hpp</PrecompiledHeaderFile>
|
||||||
|
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
||||||
|
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|x64'">../common.hpp</PrecompiledHeaderFile>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="memory\pattern.cpp">
|
||||||
|
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
|
||||||
|
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
</PrecompiledHeaderFile>
|
||||||
|
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
|
||||||
|
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
</PrecompiledHeaderFile>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="memory\pattern_batch.cpp">
|
||||||
|
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
|
||||||
|
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
</PrecompiledHeaderFile>
|
||||||
|
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
|
||||||
|
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
</PrecompiledHeaderFile>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="memory\range.cpp">
|
||||||
|
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
|
||||||
|
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
</PrecompiledHeaderFile>
|
||||||
|
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
|
||||||
|
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
</PrecompiledHeaderFile>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="pointers.cpp" />
|
||||||
|
<ClCompile Include="renderer.cpp" />
|
||||||
|
<ClCompile Include="script.cpp" />
|
||||||
|
<ClCompile Include="script_global.cpp" />
|
||||||
|
<ClCompile Include="script_hook.cpp" />
|
||||||
|
<ClCompile Include="script_mgr.cpp" />
|
||||||
|
<ClCompile Include="vmt_hook.cpp" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
<Import Project="$(VCTargetsPath)\BuildCustomizations\masm.targets" />
|
||||||
|
</ImportGroup>
|
||||||
|
</Project>
|
88
src/BigBaseV2.vcxproj.filters
Normal file
88
src/BigBaseV2.vcxproj.filters
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<ItemGroup>
|
||||||
|
<ClInclude Include="common.hpp" />
|
||||||
|
<ClInclude Include="logger.hpp" />
|
||||||
|
<ClInclude Include="pointers.hpp" />
|
||||||
|
<ClInclude Include="memory\fwddec.hpp" />
|
||||||
|
<ClInclude Include="memory\handle.hpp" />
|
||||||
|
<ClInclude Include="memory\module.hpp" />
|
||||||
|
<ClInclude Include="memory\pattern.hpp" />
|
||||||
|
<ClInclude Include="memory\pattern_batch.hpp" />
|
||||||
|
<ClInclude Include="memory\range.hpp" />
|
||||||
|
<ClInclude Include="memory\all.hpp" />
|
||||||
|
<ClInclude Include="imgui\imconfig.h" />
|
||||||
|
<ClInclude Include="imgui\imgui.h" />
|
||||||
|
<ClInclude Include="imgui\imgui_impl_dx11.h" />
|
||||||
|
<ClInclude Include="imgui\imgui_impl_win32.h" />
|
||||||
|
<ClInclude Include="imgui\imgui_internal.h" />
|
||||||
|
<ClInclude Include="imgui\imstb_rectpack.h" />
|
||||||
|
<ClInclude Include="imgui\imstb_textedit.h" />
|
||||||
|
<ClInclude Include="imgui\imstb_truetype.h" />
|
||||||
|
<ClInclude Include="renderer.hpp" />
|
||||||
|
<ClInclude Include="gui.hpp" />
|
||||||
|
<ClInclude Include="detour_hook.hpp" />
|
||||||
|
<ClInclude Include="script.hpp" />
|
||||||
|
<ClInclude Include="script_mgr.hpp" />
|
||||||
|
<ClInclude Include="function_types.hpp" />
|
||||||
|
<ClInclude Include="gta\fwddec.hpp" />
|
||||||
|
<ClInclude Include="gta\script_thread.hpp" />
|
||||||
|
<ClInclude Include="gta\tls_context.hpp" />
|
||||||
|
<ClInclude Include="gta\array.hpp" />
|
||||||
|
<ClInclude Include="gta\script_handler.hpp" />
|
||||||
|
<ClInclude Include="gta\script_id.hpp" />
|
||||||
|
<ClInclude Include="gta\net_game_event.hpp" />
|
||||||
|
<ClInclude Include="gta\joaat.hpp" />
|
||||||
|
<ClInclude Include="features.hpp" />
|
||||||
|
<ClInclude Include="gta\enums.hpp" />
|
||||||
|
<ClInclude Include="gta\natives.hpp" />
|
||||||
|
<ClInclude Include="invoker.hpp" />
|
||||||
|
<ClInclude Include="crossmap.hpp" />
|
||||||
|
<ClInclude Include="natives.hpp" />
|
||||||
|
<ClInclude Include="gta\vector.hpp" />
|
||||||
|
<ClInclude Include="gta\color.hpp" />
|
||||||
|
<ClInclude Include="vmt_hook.hpp" />
|
||||||
|
<ClInclude Include="fonts.hpp" />
|
||||||
|
<ClInclude Include="gta\ref_aware.hpp" />
|
||||||
|
<ClInclude Include="gta\base.hpp" />
|
||||||
|
<ClInclude Include="gta\matrix.hpp" />
|
||||||
|
<ClInclude Include="fiber_pool.hpp" />
|
||||||
|
<ClInclude Include="script_global.hpp" />
|
||||||
|
<ClInclude Include="gta\node_list.hpp" />
|
||||||
|
<ClInclude Include="hooking.hpp" />
|
||||||
|
<ClInclude Include="gta_util.hpp" />
|
||||||
|
<ClInclude Include="gta\player.hpp" />
|
||||||
|
<ClInclude Include="script_hook.hpp" />
|
||||||
|
<ClInclude Include="gta\script_program.hpp" />
|
||||||
|
<ClInclude Include="gta\ped_factory.hpp" />
|
||||||
|
<ClInclude Include="gta\extensible.hpp" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="common.cpp" />
|
||||||
|
<ClCompile Include="main.cpp" />
|
||||||
|
<ClCompile Include="hooking.cpp" />
|
||||||
|
<ClCompile Include="pointers.cpp" />
|
||||||
|
<ClCompile Include="memory\module.cpp" />
|
||||||
|
<ClCompile Include="memory\pattern.cpp" />
|
||||||
|
<ClCompile Include="memory\pattern_batch.cpp" />
|
||||||
|
<ClCompile Include="memory\range.cpp" />
|
||||||
|
<ClCompile Include="imgui\imgui.cpp" />
|
||||||
|
<ClCompile Include="imgui\imgui_demo.cpp" />
|
||||||
|
<ClCompile Include="imgui\imgui_draw.cpp" />
|
||||||
|
<ClCompile Include="imgui\imgui_impl_dx11.cpp" />
|
||||||
|
<ClCompile Include="imgui\imgui_impl_win32.cpp" />
|
||||||
|
<ClCompile Include="imgui\imgui_widgets.cpp" />
|
||||||
|
<ClCompile Include="renderer.cpp" />
|
||||||
|
<ClCompile Include="gui.cpp" />
|
||||||
|
<ClCompile Include="detour_hook.cpp" />
|
||||||
|
<ClCompile Include="script.cpp" />
|
||||||
|
<ClCompile Include="script_mgr.cpp" />
|
||||||
|
<ClCompile Include="features.cpp" />
|
||||||
|
<ClCompile Include="invoker.cpp" />
|
||||||
|
<ClCompile Include="vmt_hook.cpp" />
|
||||||
|
<ClCompile Include="fonts.cpp" />
|
||||||
|
<ClCompile Include="fiber_pool.cpp" />
|
||||||
|
<ClCompile Include="script_global.cpp" />
|
||||||
|
<ClCompile Include="script_hook.cpp" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
1
src/common.cpp
Normal file
1
src/common.cpp
Normal file
@ -0,0 +1 @@
|
|||||||
|
#include "common.hpp"
|
85
src/common.hpp
Normal file
85
src/common.hpp
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define NOMINMAX
|
||||||
|
#define _CRT_SECURE_NO_WARNINGS
|
||||||
|
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#define _WIN32_WINNT 0x0601
|
||||||
|
|
||||||
|
#include <SDKDDKVer.h>
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <D3D11.h>
|
||||||
|
#include <wrl/client.h>
|
||||||
|
|
||||||
|
#include <cinttypes>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <ctime>
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <iomanip>
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <mutex>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <new>
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <functional>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include <stack>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <typeinfo>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include <exception>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#include <any>
|
||||||
|
#include <optional>
|
||||||
|
#include <variant>
|
||||||
|
|
||||||
|
#define FMT_HEADER_ONLY
|
||||||
|
#include <cpptoml/cpptoml.h>
|
||||||
|
#include <fmt/format.h>
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
#include <StackWalker/StackWalker.h>
|
||||||
|
|
||||||
|
#include "logger.hpp"
|
||||||
|
|
||||||
|
namespace big
|
||||||
|
{
|
||||||
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using comptr = Microsoft::WRL::ComPtr<T>;
|
||||||
|
|
||||||
|
inline HMODULE g_hmodule{};
|
||||||
|
inline HANDLE g_main_thread{};
|
||||||
|
inline DWORD g_main_thread_id{};
|
||||||
|
inline std::atomic_bool g_running{ true };
|
||||||
|
|
||||||
|
struct stackwalker : public StackWalker
|
||||||
|
{
|
||||||
|
using StackWalker::StackWalker;
|
||||||
|
|
||||||
|
void OnOutput(LPCSTR szText) override
|
||||||
|
{
|
||||||
|
g_logger->raw(log_color::red, szText);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
inline stackwalker g_stackwalker;
|
||||||
|
}
|
5846
src/crossmap.hpp
Normal file
5846
src/crossmap.hpp
Normal file
File diff suppressed because it is too large
Load Diff
88
src/detour_hook.cpp
Normal file
88
src/detour_hook.cpp
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
#include "common.hpp"
|
||||||
|
#include "detour_hook.hpp"
|
||||||
|
#include "logger.hpp"
|
||||||
|
#include "memory/handle.hpp"
|
||||||
|
#include <mh/MinHook.h>
|
||||||
|
|
||||||
|
namespace big
|
||||||
|
{
|
||||||
|
detour_hook::detour_hook(std::string name, void *target, void *detour) :
|
||||||
|
m_name(std::move(name)),
|
||||||
|
m_target(target),
|
||||||
|
m_detour(detour)
|
||||||
|
{
|
||||||
|
fix_hook_address();
|
||||||
|
|
||||||
|
if (auto status = MH_CreateHook(m_target, m_detour, &m_original); status == MH_OK)
|
||||||
|
{
|
||||||
|
LOG_INFO("Created hook '{}'.", m_name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw std::runtime_error(fmt::format("Failed to create hook '{}' at 0x{:X} (error: {})", m_name, reinterpret_cast<std::uintptr_t>(m_target), MH_StatusToString(status)));
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
detour_hook::~detour_hook() noexcept
|
||||||
|
{
|
||||||
|
if (m_target)
|
||||||
|
{
|
||||||
|
MH_RemoveHook(m_target);
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_INFO("Removed hook '{}',", m_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void detour_hook::enable()
|
||||||
|
{
|
||||||
|
if (auto status = MH_EnableHook(m_target); status == MH_OK)
|
||||||
|
{
|
||||||
|
LOG_INFO("Enabled hook '{}'.", m_name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw std::runtime_error(fmt::format("Failed to enable hook 0x{:X} ({})", reinterpret_cast<std::uintptr_t>(m_target), MH_StatusToString(status)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void detour_hook::disable()
|
||||||
|
{
|
||||||
|
if (auto status = MH_DisableHook(m_target); status == MH_OK)
|
||||||
|
{
|
||||||
|
LOG_INFO("Disabled hook '{}'", m_name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG_ERROR("Failed to disable hook '{}'.", m_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD exp_handler(PEXCEPTION_POINTERS exp, std::string const &name)
|
||||||
|
{
|
||||||
|
return exp->ExceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION
|
||||||
|
? EXCEPTION_EXECUTE_HANDLER
|
||||||
|
: EXCEPTION_CONTINUE_SEARCH;
|
||||||
|
}
|
||||||
|
|
||||||
|
void detour_hook::fix_hook_address()
|
||||||
|
{
|
||||||
|
__try
|
||||||
|
{
|
||||||
|
auto ptr = memory::handle(m_target);
|
||||||
|
while (ptr.as<std::uint8_t&>() == 0xE9)
|
||||||
|
{
|
||||||
|
ptr = ptr.add(1).rip();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_target = ptr.as<void*>();
|
||||||
|
}
|
||||||
|
__except (exp_handler(GetExceptionInformation(), m_name))
|
||||||
|
{
|
||||||
|
[this]()
|
||||||
|
{
|
||||||
|
throw std::runtime_error(fmt::format("Failed to fix hook address for '{}'", m_name));
|
||||||
|
}();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
35
src/detour_hook.hpp
Normal file
35
src/detour_hook.hpp
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace big
|
||||||
|
{
|
||||||
|
class detour_hook
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit detour_hook(std::string name, void *target, void *detour);
|
||||||
|
~detour_hook() noexcept;
|
||||||
|
|
||||||
|
detour_hook(detour_hook&& that) = delete;
|
||||||
|
detour_hook& operator=(detour_hook&& that) = delete;
|
||||||
|
detour_hook(detour_hook const&) = delete;
|
||||||
|
detour_hook& operator=(detour_hook const&) = delete;
|
||||||
|
|
||||||
|
void enable();
|
||||||
|
void disable();
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T get_original();
|
||||||
|
|
||||||
|
void fix_hook_address();
|
||||||
|
private:
|
||||||
|
std::string m_name;
|
||||||
|
void *m_target;
|
||||||
|
void *m_detour;
|
||||||
|
void *m_original{};
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline T detour_hook::get_original()
|
||||||
|
{
|
||||||
|
return static_cast<T>(m_original);
|
||||||
|
}
|
||||||
|
}
|
21
src/features.cpp
Normal file
21
src/features.cpp
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#include "common.hpp"
|
||||||
|
#include "features.hpp"
|
||||||
|
#include "logger.hpp"
|
||||||
|
#include "natives.hpp"
|
||||||
|
#include "script.hpp"
|
||||||
|
|
||||||
|
namespace big
|
||||||
|
{
|
||||||
|
void features::run_tick()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void features::script_func()
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
run_tick();
|
||||||
|
script::get_current()->yield();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
8
src/features.hpp
Normal file
8
src/features.hpp
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "common.hpp"
|
||||||
|
|
||||||
|
namespace big::features
|
||||||
|
{
|
||||||
|
void run_tick();
|
||||||
|
void script_func();
|
||||||
|
}
|
53
src/fiber_pool.cpp
Normal file
53
src/fiber_pool.cpp
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
#include "common.hpp"
|
||||||
|
#include "fiber_pool.hpp"
|
||||||
|
#include "script.hpp"
|
||||||
|
#include "script_mgr.hpp"
|
||||||
|
|
||||||
|
namespace big
|
||||||
|
{
|
||||||
|
fiber_pool::fiber_pool(std::size_t num_fibers)
|
||||||
|
{
|
||||||
|
for (std::size_t i = 0; i < num_fibers; ++i)
|
||||||
|
{
|
||||||
|
g_script_mgr.add_script(std::make_unique<script>(&fiber_func));
|
||||||
|
}
|
||||||
|
|
||||||
|
g_fiber_pool = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
fiber_pool::~fiber_pool()
|
||||||
|
{
|
||||||
|
g_fiber_pool = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fiber_pool::queue_job(std::function<void()> func)
|
||||||
|
{
|
||||||
|
if (func)
|
||||||
|
{
|
||||||
|
std::lock_guard lock(m_mutex);
|
||||||
|
m_jobs.push(std::move(func));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void fiber_pool::fiber_tick()
|
||||||
|
{
|
||||||
|
std::unique_lock lock(m_mutex);
|
||||||
|
if (!m_jobs.empty())
|
||||||
|
{
|
||||||
|
auto job = std::move(m_jobs.top());
|
||||||
|
m_jobs.pop();
|
||||||
|
lock.unlock();
|
||||||
|
|
||||||
|
std::invoke(std::move(job));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void fiber_pool::fiber_func()
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
g_fiber_pool->fiber_tick();
|
||||||
|
script::get_current()->yield();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
22
src/fiber_pool.hpp
Normal file
22
src/fiber_pool.hpp
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "common.hpp"
|
||||||
|
|
||||||
|
namespace big
|
||||||
|
{
|
||||||
|
class fiber_pool
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit fiber_pool(std::size_t num_fibers);
|
||||||
|
~fiber_pool();
|
||||||
|
|
||||||
|
void queue_job(std::function<void()> func);
|
||||||
|
|
||||||
|
void fiber_tick();
|
||||||
|
static void fiber_func();
|
||||||
|
private:
|
||||||
|
std::recursive_mutex m_mutex;
|
||||||
|
std::stack<std::function<void()>> m_jobs;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline fiber_pool *g_fiber_pool{};
|
||||||
|
}
|
11734
src/fonts.cpp
Normal file
11734
src/fonts.cpp
Normal file
File diff suppressed because it is too large
Load Diff
4
src/fonts.hpp
Normal file
4
src/fonts.hpp
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
extern const std::uint8_t font_rubik[140732];
|
11
src/function_types.hpp
Normal file
11
src/function_types.hpp
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "common.hpp"
|
||||||
|
#include "gta/fwddec.hpp"
|
||||||
|
#include "gta/natives.hpp"
|
||||||
|
|
||||||
|
namespace big::functions
|
||||||
|
{
|
||||||
|
using run_script_threads_t = bool(*)(std::uint32_t ops_to_execute);
|
||||||
|
using get_native_handler_t = rage::scrNativeHandler(*)(rage::scrNativeRegistrationTable*, rage::scrNativeHash);
|
||||||
|
using fix_vectors_t = void(*)(rage::scrNativeCallContext*);
|
||||||
|
}
|
65
src/gta/array.hpp
Normal file
65
src/gta/array.hpp
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <cstdint>
|
||||||
|
#include "fwddec.hpp"
|
||||||
|
|
||||||
|
namespace rage
|
||||||
|
{
|
||||||
|
template <typename T>
|
||||||
|
class atArray
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
T *begin()
|
||||||
|
{
|
||||||
|
return m_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
T *end()
|
||||||
|
{
|
||||||
|
return m_data + m_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
const T *begin() const
|
||||||
|
{
|
||||||
|
return m_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
const T *end() const
|
||||||
|
{
|
||||||
|
return m_data + m_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
T *data()
|
||||||
|
{
|
||||||
|
return m_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
const T *data() const
|
||||||
|
{
|
||||||
|
return m_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint16_t size() const
|
||||||
|
{
|
||||||
|
return m_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint16_t capacity() const
|
||||||
|
{
|
||||||
|
return m_capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
T &operator[](std::uint16_t index)
|
||||||
|
{
|
||||||
|
return m_data[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
const T &operator[](std::uint16_t index) const
|
||||||
|
{
|
||||||
|
return m_data[index];
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
T *m_data;
|
||||||
|
std::uint16_t m_size;
|
||||||
|
std::uint16_t m_capacity;
|
||||||
|
};
|
||||||
|
}
|
18
src/gta/base.hpp
Normal file
18
src/gta/base.hpp
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace rage
|
||||||
|
{
|
||||||
|
class datBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~datBase() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
class pgBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~pgBase() = default;
|
||||||
|
private:
|
||||||
|
void *m_pgunk;
|
||||||
|
};
|
||||||
|
}
|
19
src/gta/color.hpp
Normal file
19
src/gta/color.hpp
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace rage
|
||||||
|
{
|
||||||
|
struct rgbaColor
|
||||||
|
{
|
||||||
|
rgbaColor(std::uint8_t r = 0, std::uint8_t g = 0, std::uint8_t b = 0, std::uint8_t a = 255) :
|
||||||
|
r(r), g(g), b(b), a(a)
|
||||||
|
{}
|
||||||
|
|
||||||
|
std::uint8_t r;
|
||||||
|
std::uint8_t g;
|
||||||
|
std::uint8_t b;
|
||||||
|
std::uint8_t a;
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(rgbaColor) == sizeof(std::uint8_t) * 4);
|
||||||
|
}
|
7
src/gta/enums.hpp
Normal file
7
src/gta/enums.hpp
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
enum eGameState : std::uint32_t
|
||||||
|
{
|
||||||
|
Playing = 0
|
||||||
|
};
|
44
src/gta/extensible.hpp
Normal file
44
src/gta/extensible.hpp
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <cstdint>
|
||||||
|
#include "ref_aware.hpp"
|
||||||
|
|
||||||
|
namespace rage
|
||||||
|
{
|
||||||
|
class fwExtension
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~fwExtension() = default;
|
||||||
|
virtual void unk_0x08() = 0;
|
||||||
|
virtual void unk_0x10() = 0;
|
||||||
|
virtual std::uint32_t get_id() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class fwExtensionContainer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
fwExtension *m_entry;
|
||||||
|
fwExtensionContainer* m_next;
|
||||||
|
};
|
||||||
|
|
||||||
|
class fwExtensibleBase : public fwRefAwareBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual bool is_of_type(std::uint32_t hash) = 0;
|
||||||
|
virtual std::uint32_t const &get_type() = 0;
|
||||||
|
public:
|
||||||
|
fwExtensionContainer* m_extension_container; // 0x10
|
||||||
|
private:
|
||||||
|
void *m_extensible_unk; // 0x18
|
||||||
|
public:
|
||||||
|
template <typename T>
|
||||||
|
bool is_of_type()
|
||||||
|
{
|
||||||
|
static auto name = (typeid(T).name()) + 6; // Skip "class "
|
||||||
|
static auto name_hash = util::joaat(name);
|
||||||
|
|
||||||
|
return is_of_type(name_hash);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(fwExtensibleBase) == 0x20);
|
||||||
|
}
|
68
src/gta/fwddec.hpp
Normal file
68
src/gta/fwddec.hpp
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace rage
|
||||||
|
{
|
||||||
|
template <typename T>
|
||||||
|
class atArray;
|
||||||
|
|
||||||
|
class datBitBuffer;
|
||||||
|
class sysMemAllocator;
|
||||||
|
|
||||||
|
class scriptIdBase;
|
||||||
|
class scriptId;
|
||||||
|
class scriptHandler;
|
||||||
|
class scriptHandlerNetComponent;
|
||||||
|
class scriptHandlerObject;
|
||||||
|
class scriptHandlerMgr;
|
||||||
|
|
||||||
|
class scrProgram;
|
||||||
|
class scrProgramTable;
|
||||||
|
|
||||||
|
class scrThreadContext;
|
||||||
|
class scrThread;
|
||||||
|
class tlsContext;
|
||||||
|
|
||||||
|
class netLoggingInterface;
|
||||||
|
class netLogStub;
|
||||||
|
|
||||||
|
class netPlayer;
|
||||||
|
class netPlayerMgr;
|
||||||
|
|
||||||
|
class netGameEvent;
|
||||||
|
class netEventMgr;
|
||||||
|
|
||||||
|
class netObject;
|
||||||
|
class netObjectMgrBase;
|
||||||
|
|
||||||
|
class scrNativeCallContext;
|
||||||
|
class scrNativeRegistration;
|
||||||
|
class scrNativeRegistrationTable;
|
||||||
|
|
||||||
|
class fwRefAwareBase;
|
||||||
|
class fwExtensibleBase;
|
||||||
|
class fwEntity;
|
||||||
|
class fwArchetype;
|
||||||
|
}
|
||||||
|
|
||||||
|
class GtaThread;
|
||||||
|
|
||||||
|
class CGameScriptId;
|
||||||
|
class CGameScriptHandler;
|
||||||
|
class CGameScriptHandlerNetwork;
|
||||||
|
class CGameScriptHandlerMgr;
|
||||||
|
|
||||||
|
class CEntity;
|
||||||
|
class CDynamicEntity;
|
||||||
|
class CPhysical;
|
||||||
|
|
||||||
|
class CPed;
|
||||||
|
class CVehicle;
|
||||||
|
class CObject;
|
||||||
|
class CPickup;
|
||||||
|
|
||||||
|
class CPedFactory;
|
||||||
|
class CVehicleFactory;
|
||||||
|
|
||||||
|
class CNetGamePlayer;
|
||||||
|
class CNetworkPlayerMgr;
|
||||||
|
class CPlayerInfo;
|
84
src/gta/joaat.hpp
Normal file
84
src/gta/joaat.hpp
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string_view>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace rage
|
||||||
|
{
|
||||||
|
using joaat_t = std::uint32_t;
|
||||||
|
|
||||||
|
inline constexpr char joaat_to_lower(char c)
|
||||||
|
{
|
||||||
|
return (c >= 'A' && c <= 'Z') ? c + ('a' - 'A') : c;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <std::size_t CharCount>
|
||||||
|
struct constexpr_joaat
|
||||||
|
{
|
||||||
|
char data[CharCount];
|
||||||
|
|
||||||
|
template <std::size_t... Indices>
|
||||||
|
constexpr constexpr_joaat(const char *str, std::index_sequence<Indices...>) :
|
||||||
|
data{ (str[Indices])... }
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr joaat_t operator()()
|
||||||
|
{
|
||||||
|
joaat_t hash = 0;
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < CharCount; ++i)
|
||||||
|
{
|
||||||
|
hash += joaat_to_lower(data[i]);
|
||||||
|
hash += (hash << 10);
|
||||||
|
hash ^= (hash >> 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
hash += (hash << 3);
|
||||||
|
hash ^= (hash >> 11);
|
||||||
|
hash += (hash << 15);
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
inline joaat_t joaat(std::string_view str)
|
||||||
|
{
|
||||||
|
joaat_t hash = 0;
|
||||||
|
|
||||||
|
for (char c : str)
|
||||||
|
{
|
||||||
|
hash += joaat_to_lower(c);
|
||||||
|
hash += (hash << 10);
|
||||||
|
hash ^= (hash >> 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
hash += (hash << 3);
|
||||||
|
hash ^= (hash >> 11);
|
||||||
|
hash += (hash << 15);
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline joaat_t joaat(const char *str)
|
||||||
|
{
|
||||||
|
joaat_t hash = 0;
|
||||||
|
|
||||||
|
while (*str)
|
||||||
|
{
|
||||||
|
hash += joaat_to_lower(*(str++));
|
||||||
|
hash += (hash << 10);
|
||||||
|
hash ^= (hash >> 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
hash += (hash << 3);
|
||||||
|
hash ^= (hash >> 11);
|
||||||
|
hash += (hash << 15);
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define RAGE_JOAAT_IMPL(str) (::rage::constexpr_joaat<sizeof(str) - 1>((str), std::make_index_sequence<sizeof(str) - 1>())())
|
||||||
|
#define RAGE_JOAAT(str) (std::integral_constant<rage::joaat_t, RAGE_JOAAT_IMPL(str)>::value)
|
22
src/gta/matrix.hpp
Normal file
22
src/gta/matrix.hpp
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "vector.hpp"
|
||||||
|
|
||||||
|
namespace rage
|
||||||
|
{
|
||||||
|
class matrix4x4
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
union
|
||||||
|
{
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
vector4 _1;
|
||||||
|
vector4 _2;
|
||||||
|
vector4 _3;
|
||||||
|
vector4 _4;
|
||||||
|
};
|
||||||
|
|
||||||
|
float raw[4 * 4] = {};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
89
src/gta/natives.hpp
Normal file
89
src/gta/natives.hpp
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <cstdint>
|
||||||
|
#include <utility>
|
||||||
|
#include "fwddec.hpp"
|
||||||
|
#include "vector.hpp"
|
||||||
|
|
||||||
|
namespace rage
|
||||||
|
{
|
||||||
|
class scrNativeCallContext
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void reset()
|
||||||
|
{
|
||||||
|
m_arg_count = 0;
|
||||||
|
m_data_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void push_arg(T &&value)
|
||||||
|
{
|
||||||
|
static_assert(sizeof(T) <= sizeof(std::uint64_t));
|
||||||
|
*reinterpret_cast<std::remove_cv_t<std::remove_reference_t<T>>*>(reinterpret_cast<std::uint64_t*>(m_args) + (m_arg_count++)) = std::forward<T>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T &get_arg(std::size_t index)
|
||||||
|
{
|
||||||
|
static_assert(sizeof(T) <= sizeof(std::uint64_t));
|
||||||
|
return *reinterpret_cast<T*>(reinterpret_cast<std::uint64_t*>(m_args) + index);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void set_arg(std::size_t index, T &&value)
|
||||||
|
{
|
||||||
|
static_assert(sizeof(T) <= sizeof(std::uint64_t));
|
||||||
|
*reinterpret_cast<std::remove_cv_t<std::remove_reference_t<T>>*>(reinterpret_cast<std::uint64_t*>(m_args) + index) = std::forward<T>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T *get_return_value()
|
||||||
|
{
|
||||||
|
return reinterpret_cast<T*>(m_return_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void set_return_value(T &&value)
|
||||||
|
{
|
||||||
|
*reinterpret_cast<std::remove_cv_t<std::remove_reference_t<T>>*>(m_return_value) = std::forward<T>(value);
|
||||||
|
}
|
||||||
|
protected:
|
||||||
|
void *m_return_value;
|
||||||
|
std::uint32_t m_arg_count;
|
||||||
|
void *m_args;
|
||||||
|
std::int32_t m_data_count;
|
||||||
|
std::uint32_t m_data[48];
|
||||||
|
};
|
||||||
|
|
||||||
|
using scrNativeHash = std::uint64_t;
|
||||||
|
using scrNativeMapping = std::pair<scrNativeHash, scrNativeHash>;
|
||||||
|
using scrNativeHandler = void(*)(scrNativeCallContext*);
|
||||||
|
|
||||||
|
class scrNativeRegistration;
|
||||||
|
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
class scrNativeRegistrationTable
|
||||||
|
{
|
||||||
|
scrNativeRegistration *m_entries[0xFF];
|
||||||
|
std::uint32_t m_unk;
|
||||||
|
bool m_initialized;
|
||||||
|
};
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
static_assert(sizeof(scrNativeCallContext) == 0xE0);
|
||||||
|
}
|
||||||
|
|
||||||
|
using Void = void;
|
||||||
|
using Any = std::uint32_t;
|
||||||
|
using Hash = std::uint32_t;
|
||||||
|
using Entity = std::int32_t;
|
||||||
|
using Player = std::int32_t;
|
||||||
|
using Ped = Entity;
|
||||||
|
using Vehicle = Entity;
|
||||||
|
using Cam = std::int32_t;
|
||||||
|
using Object = Entity;
|
||||||
|
using Pickup = Object;
|
||||||
|
using Blip = std::int32_t;
|
||||||
|
using Camera = Entity;
|
||||||
|
using ScrHandle = Entity;
|
||||||
|
using Vector3 = rage::scrVector;
|
58
src/gta/net_game_event.hpp
Normal file
58
src/gta/net_game_event.hpp
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <cstdint>
|
||||||
|
#include "fwddec.hpp"
|
||||||
|
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
namespace rage
|
||||||
|
{
|
||||||
|
class netGameEvent
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~netGameEvent() = default;
|
||||||
|
|
||||||
|
virtual const char *get_name() const = 0;
|
||||||
|
virtual bool is_applicable_to_player(netPlayer *player) = 0;
|
||||||
|
virtual bool time_to_resend(std::uint32_t time) = 0;
|
||||||
|
virtual bool needs_reply() = 0;
|
||||||
|
|
||||||
|
virtual void serialize(datBitBuffer *buffer, netPlayer* source_player, netPlayer* target_player) = 0;
|
||||||
|
virtual void deserialize(datBitBuffer *buffer, netPlayer* source_player, netPlayer* target_player) = 0;
|
||||||
|
|
||||||
|
virtual bool handle(netPlayer* source_player, netPlayer* target_player) = 0;
|
||||||
|
|
||||||
|
virtual void deserialize_reply(datBitBuffer *buffer, netPlayer* reply_player) = 0;
|
||||||
|
virtual void serialize_reply(datBitBuffer *buffer, netPlayer* souce_player) = 0;
|
||||||
|
|
||||||
|
virtual void deserialize_extra_data(datBitBuffer *buffer, bool is_reply, netPlayer *player, netPlayer *player2) = 0;
|
||||||
|
virtual void serialize_extra_data(datBitBuffer *buffer, bool is_reply, netPlayer *player, netPlayer *player2) = 0;
|
||||||
|
|
||||||
|
virtual void _0x60() = 0;
|
||||||
|
virtual void _0x68() = 0;
|
||||||
|
virtual void _0x70() = 0;
|
||||||
|
virtual void _0x78() = 0;
|
||||||
|
|
||||||
|
virtual bool operator==(netGameEvent const& other) = 0;
|
||||||
|
virtual bool operator!=(netGameEvent const& other) = 0;
|
||||||
|
|
||||||
|
virtual bool must_persist() = 0;
|
||||||
|
virtual bool must_persist_when_out_of_scope() = 0;
|
||||||
|
virtual bool has_timed_out() = 0;
|
||||||
|
public:
|
||||||
|
std::uint16_t m_id; // 0x08
|
||||||
|
bool m_requires_reply; // 0x0A
|
||||||
|
private:
|
||||||
|
char m_padding1[0x05]; // 0x0B
|
||||||
|
public:
|
||||||
|
netPlayer* m_source_player; // 0x10
|
||||||
|
netPlayer* m_target_player; // 0x18
|
||||||
|
std::uint32_t m_resend_time; // 0x20
|
||||||
|
std::uint16_t m_0x24; // 0x24
|
||||||
|
std::uint8_t m_0x26; // 0x26
|
||||||
|
std::uint8_t m_0x27; // 0x27
|
||||||
|
std::uint32_t m_0x28; // 0x28
|
||||||
|
char m_padding2[0x04]; // 0x2C
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(netGameEvent) == 0x30);
|
||||||
|
}
|
||||||
|
#pragma pack(pop)
|
23
src/gta/node_list.hpp
Normal file
23
src/gta/node_list.hpp
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "fwddec.hpp"
|
||||||
|
#include "base.hpp"
|
||||||
|
|
||||||
|
namespace rage
|
||||||
|
{
|
||||||
|
template <typename T, typename Base = datBase>
|
||||||
|
class atDNode : public Base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
T m_data;
|
||||||
|
void *m_unk;
|
||||||
|
atDNode<T, Base> *m_next;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Node>
|
||||||
|
class atDList
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Node *m_head;
|
||||||
|
Node *m_tail;
|
||||||
|
};
|
||||||
|
}
|
18
src/gta/ped_factory.hpp
Normal file
18
src/gta/ped_factory.hpp
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "fwddec.hpp"
|
||||||
|
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
class CPed
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
char m_padding[0x10B8];
|
||||||
|
CPlayerInfo *m_playerinfo;
|
||||||
|
};
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
class CPedFactory
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~CPedFactory() = default;
|
||||||
|
CPed *m_local_ped;
|
||||||
|
};
|
112
src/gta/player.hpp
Normal file
112
src/gta/player.hpp
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <cstdint>
|
||||||
|
#include "fwddec.hpp"
|
||||||
|
#include "extensible.hpp"
|
||||||
|
#include "vector.hpp"
|
||||||
|
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
namespace rage
|
||||||
|
{
|
||||||
|
# pragma warning(push)
|
||||||
|
# pragma warning(disable : 4201) // nonstandard extension used: nameless struct/union
|
||||||
|
union netAddress
|
||||||
|
{
|
||||||
|
std::uint32_t m_raw;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
std::uint8_t m_field4;
|
||||||
|
std::uint8_t m_field3;
|
||||||
|
std::uint8_t m_field2;
|
||||||
|
std::uint8_t m_field1;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
# pragma warning(pop)
|
||||||
|
|
||||||
|
class netPlayerData
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::uint64_t m_unk1; // 0x00
|
||||||
|
std::uint64_t m_unk2; // 0x08
|
||||||
|
std::uint32_t m_sec_key_time; // 0x10
|
||||||
|
netAddress m_lan_ip; // 0x14
|
||||||
|
std::uint16_t m_lan_port; // 0x18
|
||||||
|
char m_pad1[0x02]; // 0x1A
|
||||||
|
netAddress m_relay_ip; // 0x1C
|
||||||
|
std::uint16_t m_relay_port; // 0x20
|
||||||
|
char m_pad2[0x02]; // 0x22
|
||||||
|
netAddress m_online_ip; // 0x24
|
||||||
|
std::uint16_t m_online_port; // 0x26
|
||||||
|
char m_pad3[0x1E]; // 0x28
|
||||||
|
std::uint64_t m_rockstar_id; // 0x48
|
||||||
|
bool m_id_flag; // 0x50
|
||||||
|
char m_pad4[0x0B]; // 0x51
|
||||||
|
char m_name[0x14]; // 0x5C
|
||||||
|
};
|
||||||
|
|
||||||
|
class nonPhysicalPlayerDataBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~nonPhysicalPlayerDataBase() = default; // 0 (0x00)
|
||||||
|
virtual void unk_0x08() = 0; // 1 (0x08)
|
||||||
|
virtual void unk_0x10() = 0; // 2 (0x10)
|
||||||
|
virtual void unk_0x18() = 0; // 3 (0x18)
|
||||||
|
virtual void log(netLoggingInterface* logger) = 0; // 4 (0x20)
|
||||||
|
};
|
||||||
|
|
||||||
|
class netPlayer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~netPlayer() = default; // 0 (0x00)
|
||||||
|
virtual void reset() = 0; // 1 (0x08)
|
||||||
|
virtual bool is_valid() const = 0; // 2 (0x10)
|
||||||
|
virtual const char *get_name() const = 0; // 3 (0x18)
|
||||||
|
virtual void _0x20() = 0; // 4 (0x20)
|
||||||
|
virtual bool is_host() = 0; // 5 (0x28)
|
||||||
|
virtual netPlayerData *get_net_data() = 0; // 6 (0x30)
|
||||||
|
virtual void _0x38() = 0; // 7 (0x38)
|
||||||
|
};
|
||||||
|
|
||||||
|
class netPlayerMgrBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~netPlayerMgrBase() = default; // 0 (0x00)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace gta
|
||||||
|
{
|
||||||
|
inline constexpr auto num_players = 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
class CNonPhysicalPlayerData : public rage::nonPhysicalPlayerDataBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::int32_t m_bubble_id; // 0x08
|
||||||
|
std::int32_t m_player_id; // 0x0C
|
||||||
|
rage::vector3 m_position; // 0x10
|
||||||
|
};
|
||||||
|
|
||||||
|
class CNetGamePlayer : public rage::netPlayer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class CWantedData
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
char m_padding[0x98];
|
||||||
|
std::int32_t m_wanted_level;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CPlayerInfo : public rage::fwExtensibleBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
char m_padding1[0x1D8]; // 0x20
|
||||||
|
std::uint32_t m_frame_flags; // 0x1F8
|
||||||
|
char m_padding2[0x584]; // 0x1FC
|
||||||
|
CWantedData m_wanted_data; // 0x780
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(CNonPhysicalPlayerData) == 0x1C);
|
||||||
|
#pragma pack(pop)
|
18
src/gta/ref_aware.hpp
Normal file
18
src/gta/ref_aware.hpp
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "base.hpp"
|
||||||
|
|
||||||
|
namespace rage
|
||||||
|
{
|
||||||
|
template <typename T>
|
||||||
|
class fwRefAwareBaseImpl : public T
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
void *m_ref; // 0x08
|
||||||
|
};
|
||||||
|
|
||||||
|
class fwRefAwareBase : public fwRefAwareBaseImpl<datBase>
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(fwRefAwareBase) == 0x10);
|
||||||
|
}
|
185
src/gta/script_handler.hpp
Normal file
185
src/gta/script_handler.hpp
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <cstdint>
|
||||||
|
#include "fwddec.hpp"
|
||||||
|
#include "node_list.hpp"
|
||||||
|
#include "script_id.hpp"
|
||||||
|
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
namespace rage
|
||||||
|
{
|
||||||
|
class scriptResource
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~scriptResource() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
class scriptResourceEntry
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
scriptResource *m_data; // 0x00
|
||||||
|
std::uint32_t m_unk; // 0x04
|
||||||
|
char m_padding[0x0C]; // 0x0C
|
||||||
|
scriptResourceEntry *m_next; // 0x18
|
||||||
|
};
|
||||||
|
|
||||||
|
class scriptHandlerNetComponent
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~scriptHandlerNetComponent() = default;
|
||||||
|
|
||||||
|
public:
|
||||||
|
scriptHandler *m_script_handler; // 0x08
|
||||||
|
};
|
||||||
|
|
||||||
|
class scriptHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
class atDScriptObjectNode : public atDNode<scriptHandlerObject*>
|
||||||
|
{
|
||||||
|
};
|
||||||
|
public:
|
||||||
|
virtual ~scriptHandler() = default; // 0 (0x00)
|
||||||
|
|
||||||
|
virtual bool _0x08() = 0; // 1 (0x08)
|
||||||
|
|
||||||
|
virtual void _0x10() = 0; // 2 (0x10)
|
||||||
|
|
||||||
|
virtual void cleanup_objects() = 0; // 3 (0x18)
|
||||||
|
|
||||||
|
virtual scriptId *_0x20() = 0; // 4 (0x20)
|
||||||
|
|
||||||
|
virtual scriptId *get_id() = 0; // 5 (0x28)
|
||||||
|
|
||||||
|
// Returns whether the script handler belongs to a networked script.
|
||||||
|
virtual bool is_networked() = 0; // 6 (0x30)
|
||||||
|
|
||||||
|
// Initializes the network component for the script handler.
|
||||||
|
virtual void init_net_component() = 0; // 7 (0x38)
|
||||||
|
|
||||||
|
// Deletes the script handler's network component, if it exists.
|
||||||
|
virtual void reset_net_component() = 0; // 8 (0x40)
|
||||||
|
|
||||||
|
// Destroys the script handler.
|
||||||
|
virtual bool destroy() = 0; // 9 (0x48)
|
||||||
|
|
||||||
|
// Adds the object to the script handler's list of objects.
|
||||||
|
virtual void add_object(scriptHandlerObject*, bool is_network, bool is_network_and_scriptcheck) = 0; // 10 (0x50)
|
||||||
|
|
||||||
|
// Something related to reservations.
|
||||||
|
virtual void _0x58(void*) = 0; // 11 (0x58)
|
||||||
|
|
||||||
|
virtual void register_resource(scriptResource*, void*) = 0; // 12 (0x60)
|
||||||
|
|
||||||
|
virtual void _0x68() = 0; // 13 (0x68)
|
||||||
|
|
||||||
|
virtual void _0x70() = 0; // 14 (0x70)
|
||||||
|
|
||||||
|
virtual void _0x78() = 0; // 15 (0x78)
|
||||||
|
|
||||||
|
virtual void _0x80() = 0; // 16 (0x80)
|
||||||
|
|
||||||
|
virtual void _0x88() = 0; // 17 (0x88)
|
||||||
|
|
||||||
|
virtual void _0x90() = 0; // 18 (0x90)
|
||||||
|
|
||||||
|
virtual void _0x98() = 0; // 19 (0x98)
|
||||||
|
public:
|
||||||
|
void *m_0x08; // 0x08
|
||||||
|
void *m_0x10; // 0x10
|
||||||
|
scrThread *m_script_thread; // 0x18
|
||||||
|
atDList<atDScriptObjectNode> m_objects; // 0x20
|
||||||
|
scriptResource *m_resource_list_head; // 0x30
|
||||||
|
scriptResource *m_resource_list_tail; // 0x38
|
||||||
|
void *m_0x40; // 0x40
|
||||||
|
scriptHandlerNetComponent *m_net_component; // 0x48
|
||||||
|
std::uint32_t m_0x50; // 0x50
|
||||||
|
std::uint32_t m_0x54; // 0x54
|
||||||
|
std::uint32_t m_0x58; // 0x58
|
||||||
|
std::uint32_t m_0x60; // 0x5C
|
||||||
|
};
|
||||||
|
|
||||||
|
class scriptHandlerMgr
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~scriptHandlerMgr() = default;
|
||||||
|
|
||||||
|
// Initializes some scripting-related pools.
|
||||||
|
virtual bool initialize() = 0; // 1 (0x08)
|
||||||
|
|
||||||
|
// Called every tick.
|
||||||
|
virtual void _0x10() = 0; // 2 (0x10)
|
||||||
|
|
||||||
|
// Frees some scripting-related pools.
|
||||||
|
virtual void shutdown() = 0; // 3 (0x18)
|
||||||
|
|
||||||
|
virtual void _0x20() = 0; // 4 (0x20)
|
||||||
|
virtual void _0x28() = 0; // 5 (0x28)
|
||||||
|
virtual void _0x30() = 0; // 6 (0x30)
|
||||||
|
|
||||||
|
// Generates a rage::scriptId from the thread and copies it over to a global structure.
|
||||||
|
virtual void _0x38(scrThread*) = 0; // 7 (0x38)
|
||||||
|
|
||||||
|
// Allocates and constructs a script handler.
|
||||||
|
virtual scriptHandler *create_script_handler() = 0; // 8 (0x40)
|
||||||
|
|
||||||
|
// Finds the script handler for a given script id.
|
||||||
|
virtual scriptHandler *get_script_handler(scriptId*) = 0; // 9 (0x48)
|
||||||
|
|
||||||
|
// Attaches a script thread.
|
||||||
|
virtual void attach_thread(scrThread*) = 0; // 10 (0x50)
|
||||||
|
|
||||||
|
// Detaches a script thread.
|
||||||
|
virtual void detach_thread(scrThread*) = 0; // 11 (0x58)
|
||||||
|
|
||||||
|
// Called when a player joins.
|
||||||
|
virtual void on_player_join(netPlayer*) = 0; // 12 (0x60)
|
||||||
|
|
||||||
|
// Called when a player leaves.
|
||||||
|
virtual void on_player_left(netPlayer*) = 0; // 13 (0x68)
|
||||||
|
|
||||||
|
virtual std::int32_t _0x70() = 0; // 14 (0x70)
|
||||||
|
virtual void *_0x78() = 0; // 15 (0x78)
|
||||||
|
public:
|
||||||
|
char m_padding1[0x28]; // 0x08
|
||||||
|
bool m_initialized; // 0x30
|
||||||
|
bool m_initialized2; // 0x31
|
||||||
|
char m_padding2[0x0E]; // 0x32
|
||||||
|
rage::netLoggingInterface *m_logger; // 0x40
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
class CGameScriptHandler : public rage::scriptHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CGameScriptId m_script_id; // 0x60
|
||||||
|
};
|
||||||
|
|
||||||
|
class CGameScriptHandlerNetwork : public CGameScriptHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::uint8_t m_0xA0; // 0xA0
|
||||||
|
std::uint8_t m_0xA1; // 0xA1
|
||||||
|
std::uint8_t m_0xA2; // 0xA2
|
||||||
|
std::uint8_t m_0xA3; // 0xA3
|
||||||
|
std::uint8_t m_num_players; // 0xA4
|
||||||
|
std::uint8_t m_0xA5; // 0xA5
|
||||||
|
std::uint8_t m_0xA6; // 0xA6
|
||||||
|
std::uint8_t m_0xA7; // 0xA7
|
||||||
|
std::uint8_t m_0xA8; // 0xA8
|
||||||
|
std::uint8_t m_0xA9; // 0xA9
|
||||||
|
std::uint8_t m_0xAA; // 0xAA
|
||||||
|
std::uint8_t m_0xAB; // 0xAB
|
||||||
|
std::uint8_t m_0xAC; // 0xAC
|
||||||
|
std::uint8_t m_0xAD; // 0xAD
|
||||||
|
std::uint8_t m_0xAE; // 0xAE
|
||||||
|
std::uint8_t m_0xAF; // 0xAF
|
||||||
|
};
|
||||||
|
|
||||||
|
class CGameScriptHandlerMgr : public rage::scriptHandlerMgr
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(rage::scriptHandler) == 0x60);
|
||||||
|
static_assert(sizeof(CGameScriptHandler) == 0xA0);
|
||||||
|
static_assert(sizeof(CGameScriptHandlerNetwork) == 0xB0);
|
||||||
|
#pragma pack(pop)
|
72
src/gta/script_id.hpp
Normal file
72
src/gta/script_id.hpp
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <cstdint>
|
||||||
|
#include "fwddec.hpp"
|
||||||
|
#include "joaat.hpp"
|
||||||
|
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
namespace rage
|
||||||
|
{
|
||||||
|
class scriptIdBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~scriptIdBase() = default; // 0 (0x00)
|
||||||
|
|
||||||
|
// Assumes the script thread's identity.
|
||||||
|
virtual void assume_thread_identity(scrThread*) {}; // 1 (0x08)
|
||||||
|
|
||||||
|
// Returns whether the hash of the script id is valid.
|
||||||
|
virtual bool is_valid() {}; // 2 (0x10)
|
||||||
|
|
||||||
|
// Gets the hash of the script id.
|
||||||
|
virtual joaat_t *get_hash(joaat_t *out) {}; // 3 (0x18)
|
||||||
|
|
||||||
|
// Gets an unknown value from the script id.
|
||||||
|
virtual std::uint32_t *get_hash2(std::uint32_t *out) {}; // 4 (0x20)
|
||||||
|
|
||||||
|
// Gets the name of the script id.
|
||||||
|
virtual const char *get_name() {}; // 5 (0x28)
|
||||||
|
|
||||||
|
// Serializes the script id from the buffer.
|
||||||
|
virtual void deserialize(datBitBuffer* buffer) {}; // 6 (0x30)
|
||||||
|
|
||||||
|
// Serializes the script id to the buffer.
|
||||||
|
virtual void serialize(datBitBuffer* buffer) {}; // 7 (0x38)
|
||||||
|
|
||||||
|
// Calculates some information with the position hash & instance id.
|
||||||
|
virtual std::uint32_t _0x40() {}; // 8 (0x40)
|
||||||
|
|
||||||
|
// Calls _0x40 and returns it's value added to another value.
|
||||||
|
virtual std::uint32_t _0x48() {}; // 9 (0x48)
|
||||||
|
|
||||||
|
// Logs some information about the script id.
|
||||||
|
virtual void log_information(netLoggingInterface* logger) {}; // 10 (0x50)
|
||||||
|
|
||||||
|
// Copies the information of other to this object.
|
||||||
|
virtual void copy_data(scriptIdBase *other) {} // 11 (0x58)
|
||||||
|
|
||||||
|
// Returns whether the other script id is equal.
|
||||||
|
virtual bool operator==(scriptIdBase*) {}; // 12 (0x60)
|
||||||
|
|
||||||
|
virtual bool _0x68(void*) {}; // 13 (0x68)
|
||||||
|
};
|
||||||
|
|
||||||
|
class scriptId : public scriptIdBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
joaat_t m_hash; // 0x08
|
||||||
|
char m_name[0x20]; // 0x0C
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
class CGameScriptId : public rage::scriptId
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
char m_padding[0x04]; // 0x2C
|
||||||
|
std::int32_t m_timestamp; // 0x30
|
||||||
|
std::int32_t m_position_hash; // 0x34
|
||||||
|
std::int32_t m_instance_id; // 0x38
|
||||||
|
std::int32_t m_unk; // 0x3C
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(CGameScriptId) == 0x40);
|
||||||
|
#pragma pack(pop)
|
145
src/gta/script_program.hpp
Normal file
145
src/gta/script_program.hpp
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <cstdint>
|
||||||
|
#include "fwddec.hpp"
|
||||||
|
#include "base.hpp"
|
||||||
|
#include "joaat.hpp"
|
||||||
|
#include "script_id.hpp"
|
||||||
|
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
namespace rage
|
||||||
|
{
|
||||||
|
class scrProgram : public pgBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::uint8_t** m_code_blocks; // 0x10
|
||||||
|
std::uint32_t m_hash; // 0x18
|
||||||
|
std::uint32_t m_code_size; // 0x1C
|
||||||
|
std::uint32_t m_arg_count; // 0x20
|
||||||
|
std::uint32_t m_local_count; // 0x24
|
||||||
|
std::uint32_t m_global_count; // 0x28
|
||||||
|
std::uint32_t m_native_count; // 0x2C
|
||||||
|
void *m_local_data; // 0x30
|
||||||
|
std::int64_t **m_global_data; // 0x38
|
||||||
|
void **m_native_entrypoints; // 0x40
|
||||||
|
char m_padding6[0x10]; // 0x48
|
||||||
|
std::uint32_t m_name_hash; // 0x58
|
||||||
|
char m_padding7[0x04]; // 0x5C
|
||||||
|
const char* m_name; // 0x60
|
||||||
|
const char** m_strings_data; // 0x68
|
||||||
|
std::uint32_t m_strings_count; // 0x70
|
||||||
|
char m_padding8[0x0C]; // 0x74
|
||||||
|
|
||||||
|
bool is_valid() const
|
||||||
|
{
|
||||||
|
return m_code_size != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint32_t get_num_code_pages() const
|
||||||
|
{
|
||||||
|
return (m_code_size + 0x3FFF) >> 14;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint32_t get_code_page_size(std::uint32_t page) const
|
||||||
|
{
|
||||||
|
auto num = get_num_code_pages();
|
||||||
|
if (page < num)
|
||||||
|
{
|
||||||
|
if (page == num - 1)
|
||||||
|
return m_code_size & 0x3FFF;
|
||||||
|
return 0x4000;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint32_t get_full_code_size() const
|
||||||
|
{
|
||||||
|
auto numPages = get_num_code_pages();
|
||||||
|
if (!numPages)
|
||||||
|
return 0;
|
||||||
|
if (numPages == 1)
|
||||||
|
--numPages;
|
||||||
|
|
||||||
|
return (numPages * 0x4000) + (m_code_size & 0x3FFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint8_t* get_code_page(std::uint32_t page) const
|
||||||
|
{
|
||||||
|
return m_code_blocks[page];
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint8_t* get_code_address(std::uint32_t index) const
|
||||||
|
{
|
||||||
|
if (index < m_code_size)
|
||||||
|
return &m_code_blocks[index >> 14][index & 0x3FFF];
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* get_string(std::uint32_t index) const
|
||||||
|
{
|
||||||
|
if (index < m_strings_count)
|
||||||
|
return &m_strings_data[index >> 14][index & 0x3FFF];
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void** get_address_of_native_entrypoint(void* entrypoint)
|
||||||
|
{
|
||||||
|
for (std::uint32_t i = 0; i < m_native_count; ++i)
|
||||||
|
{
|
||||||
|
if (m_native_entrypoints[i] == entrypoint)
|
||||||
|
{
|
||||||
|
return m_native_entrypoints + i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class scrProgramTableEntry
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
scrProgram* m_program; // 0x00
|
||||||
|
char m_Pad1[0x04]; // 0x08
|
||||||
|
joaat_t m_hash; // 0x0C
|
||||||
|
};
|
||||||
|
|
||||||
|
class scrProgramTable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
scrProgramTableEntry* m_data; // 0x00
|
||||||
|
char m_padding[0x10]; // 0x08
|
||||||
|
std::uint32_t m_size; // 0x18
|
||||||
|
|
||||||
|
scrProgram* find_script(joaat_t hash)
|
||||||
|
{
|
||||||
|
for (std::uint32_t i = 0; i < m_size; ++i)
|
||||||
|
{
|
||||||
|
if (m_data[i].m_hash == hash)
|
||||||
|
{
|
||||||
|
return m_data[i].m_program;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
scrProgramTableEntry* begin()
|
||||||
|
{
|
||||||
|
return m_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
scrProgramTableEntry* end()
|
||||||
|
{
|
||||||
|
return m_data + m_size;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(scrProgram) == 0x80);
|
||||||
|
static_assert(sizeof(scrProgramTableEntry) == 0x10);
|
||||||
|
static_assert(sizeof(scrProgramTable) == 0x1C);
|
||||||
|
}
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
83
src/gta/script_thread.hpp
Normal file
83
src/gta/script_thread.hpp
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <cstdint>
|
||||||
|
#include "fwddec.hpp"
|
||||||
|
#include "joaat.hpp"
|
||||||
|
#include "tls_context.hpp"
|
||||||
|
|
||||||
|
namespace rage
|
||||||
|
{
|
||||||
|
enum class eThreadState : std::uint32_t
|
||||||
|
{
|
||||||
|
idle,
|
||||||
|
running,
|
||||||
|
killed,
|
||||||
|
unk_3,
|
||||||
|
unk_4,
|
||||||
|
};
|
||||||
|
|
||||||
|
class scrThreadContext
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::uint32_t m_thread_id; // 0x00
|
||||||
|
joaat_t m_script_hash; // 0x04
|
||||||
|
eThreadState m_state; // 0x08
|
||||||
|
std::uint32_t m_instruction_pointer; // 0x0C
|
||||||
|
std::uint32_t m_frame_pointer; // 0x10
|
||||||
|
std::uint32_t m_stack_pointer; // 0x14
|
||||||
|
float m_timer_a; // 0x18
|
||||||
|
float m_timer_b; // 0x1C
|
||||||
|
float m_timer_c; // 0x20
|
||||||
|
char m_padding1[0x2C]; // 0x24
|
||||||
|
std::uint32_t m_stack_size; // 0x50
|
||||||
|
char m_padding2[0x54]; // 0x54
|
||||||
|
|
||||||
|
static scrThread *get()
|
||||||
|
{
|
||||||
|
return rage::tlsContext::get()->m_script_thread;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class scrThread
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~scrThread() = default; // 0 (0x00)
|
||||||
|
virtual eThreadState reset(std::uint32_t script_hash, void *args, std::uint32_t arg_count) = 0; // 1 (0x08)
|
||||||
|
virtual eThreadState run() = 0; // 2 (0x10)
|
||||||
|
virtual eThreadState tick(std::uint32_t ops_to_execute) = 0; // 3 (0x18)
|
||||||
|
virtual void kill() = 0; // 4 (0x20)
|
||||||
|
public:
|
||||||
|
scrThreadContext m_context; // 0x08
|
||||||
|
void *m_stack; // 0xB0
|
||||||
|
char m_padding[0x10]; // 0xB8
|
||||||
|
const char *m_exit_message; // 0xC8
|
||||||
|
char m_name[0x40]; // 0xD0
|
||||||
|
scriptHandler *m_handler; // 0x110
|
||||||
|
scriptHandlerNetComponent *m_net_component; // 0x118
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(scrThreadContext) == 0xA8);
|
||||||
|
static_assert(sizeof(scrThread) == 0x120);
|
||||||
|
}
|
||||||
|
|
||||||
|
class GtaThread : public rage::scrThread
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
rage::joaat_t m_script_hash; // 0x120
|
||||||
|
char m_padding3[0x14]; // 0x124
|
||||||
|
std::int32_t m_instance_id; // 0x138
|
||||||
|
char m_padding4[0x04]; // 0x13C
|
||||||
|
std::uint8_t m_flag1; // 0x140
|
||||||
|
bool m_safe_for_network_game; // 0x141
|
||||||
|
char m_padding5[0x05]; // 0x142
|
||||||
|
bool m_can_be_paused; // 0x147
|
||||||
|
bool m_can_remove_blips_from_other_scripts; // 0x148
|
||||||
|
char m_padding6[0x0F]; // 0x149
|
||||||
|
};
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
auejk = FIELD_OFFSET(GtaThread, m_can_be_paused),
|
||||||
|
s9tifgjwkosmldxc = sizeof(GtaThread)
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(GtaThread) == 0x158);
|
23
src/gta/tls_context.hpp
Normal file
23
src/gta/tls_context.hpp
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <intrin.h>
|
||||||
|
#include "fwddec.hpp"
|
||||||
|
|
||||||
|
namespace rage
|
||||||
|
{
|
||||||
|
class tlsContext
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
char m_padding1[0xC8]; // 0x00
|
||||||
|
sysMemAllocator *m_allocator; // 0xC8
|
||||||
|
char m_padding2[0x760]; // 0xD0
|
||||||
|
scrThread *m_script_thread; // 0x830
|
||||||
|
bool m_is_script_thread_active; // 0x838
|
||||||
|
|
||||||
|
static tlsContext *get()
|
||||||
|
{
|
||||||
|
return *reinterpret_cast<tlsContext**>(__readgsqword(0x58));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(tlsContext) == 0x840);
|
||||||
|
}
|
49
src/gta/vector.hpp
Normal file
49
src/gta/vector.hpp
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace rage
|
||||||
|
{
|
||||||
|
struct vector2
|
||||||
|
{
|
||||||
|
float x{};
|
||||||
|
float y{};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct vector3
|
||||||
|
{
|
||||||
|
float x{};
|
||||||
|
float y{};
|
||||||
|
float z{};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct vector4
|
||||||
|
{
|
||||||
|
float x{};
|
||||||
|
float y{};
|
||||||
|
float z{};
|
||||||
|
float w{};
|
||||||
|
};
|
||||||
|
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
class scrVector
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
scrVector() = default;
|
||||||
|
|
||||||
|
scrVector(float x, float y, float z) :
|
||||||
|
x(x), y(y), z(z)
|
||||||
|
{}
|
||||||
|
public:
|
||||||
|
float x{};
|
||||||
|
private:
|
||||||
|
char m_padding1[0x04];
|
||||||
|
public:
|
||||||
|
float y{};
|
||||||
|
private:
|
||||||
|
char m_padding2[0x04];
|
||||||
|
public:
|
||||||
|
float z{};
|
||||||
|
private:
|
||||||
|
char m_padding3[0x04];
|
||||||
|
};
|
||||||
|
#pragma pack(pop)
|
||||||
|
}
|
57
src/gta_util.hpp
Normal file
57
src/gta_util.hpp
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "common.hpp"
|
||||||
|
#include "gta/array.hpp"
|
||||||
|
#include "gta/ped_factory.hpp"
|
||||||
|
#include "gta/player.hpp"
|
||||||
|
#include "gta/script_thread.hpp"
|
||||||
|
#include "gta/tls_context.hpp"
|
||||||
|
#include "pointers.hpp"
|
||||||
|
|
||||||
|
namespace big::gta_util
|
||||||
|
{
|
||||||
|
inline CPed *get_local_ped()
|
||||||
|
{
|
||||||
|
if (auto ped_factory = *g_pointers->m_ped_factory)
|
||||||
|
{
|
||||||
|
return ped_factory->m_local_ped;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline CPlayerInfo *get_local_playerinfo()
|
||||||
|
{
|
||||||
|
if (auto ped_factory = *g_pointers->m_ped_factory)
|
||||||
|
{
|
||||||
|
if (auto ped = ped_factory->m_local_ped)
|
||||||
|
{
|
||||||
|
return ped->m_playerinfo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename F, typename ...Args>
|
||||||
|
void execute_as_script(rage::joaat_t script_hash, F &&callback, Args &&...args)
|
||||||
|
{
|
||||||
|
auto tls_ctx = rage::tlsContext::get();
|
||||||
|
for (auto thread : *g_pointers->m_script_threads)
|
||||||
|
{
|
||||||
|
if (!thread || !thread->m_context.m_thread_id || thread->m_context.m_script_hash != script_hash)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
auto og_thread = tls_ctx->m_script_thread;
|
||||||
|
|
||||||
|
tls_ctx->m_script_thread = thread;
|
||||||
|
tls_ctx->m_is_script_thread_active = true;
|
||||||
|
|
||||||
|
std::invoke(std::forward<F>(callback), std::forward<Args>(args)...);
|
||||||
|
|
||||||
|
tls_ctx->m_script_thread = og_thread;
|
||||||
|
tls_ctx->m_is_script_thread_active = og_thread != nullptr;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
172
src/gui.cpp
Normal file
172
src/gui.cpp
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
#include "common.hpp"
|
||||||
|
#include "fiber_pool.hpp"
|
||||||
|
#include "gta/player.hpp"
|
||||||
|
#include "gta_util.hpp"
|
||||||
|
#include "gui.hpp"
|
||||||
|
#include "logger.hpp"
|
||||||
|
#include "memory/module.hpp"
|
||||||
|
#include "memory/pattern.hpp"
|
||||||
|
#include "natives.hpp"
|
||||||
|
#include "pointers.hpp"
|
||||||
|
#include "renderer.hpp"
|
||||||
|
#include "script.hpp"
|
||||||
|
|
||||||
|
#include "imgui/imgui.h"
|
||||||
|
#include <StackWalker/StackWalker.h>
|
||||||
|
|
||||||
|
namespace big
|
||||||
|
{
|
||||||
|
void gui::dx_init()
|
||||||
|
{
|
||||||
|
auto &style = ImGui::GetStyle();
|
||||||
|
style.WindowPadding = { 10.f, 10.f };
|
||||||
|
style.PopupRounding = 0.f;
|
||||||
|
style.FramePadding = { 8.f, 4.f };
|
||||||
|
style.ItemSpacing = { 10.f, 8.f };
|
||||||
|
style.ItemInnerSpacing = { 6.f, 6.f };
|
||||||
|
style.TouchExtraPadding = { 0.f, 0.f };
|
||||||
|
style.IndentSpacing = 21.f;
|
||||||
|
style.ScrollbarSize = 15.f;
|
||||||
|
style.GrabMinSize = 8.f;
|
||||||
|
style.WindowBorderSize = 1.f;
|
||||||
|
style.ChildBorderSize = 0.f;
|
||||||
|
style.PopupBorderSize = 1.f;
|
||||||
|
style.FrameBorderSize = 0.f;
|
||||||
|
style.TabBorderSize = 0.f;
|
||||||
|
style.WindowRounding = 0.f;
|
||||||
|
style.ChildRounding = 0.f;
|
||||||
|
style.FrameRounding = 0.f;
|
||||||
|
style.ScrollbarRounding = 0.f;
|
||||||
|
style.GrabRounding = 0.f;
|
||||||
|
style.TabRounding = 0.f;
|
||||||
|
style.WindowTitleAlign = { 0.5f, 0.5f };
|
||||||
|
style.ButtonTextAlign = { 0.5f, 0.5f };
|
||||||
|
style.DisplaySafeAreaPadding = { 3.f, 3.f };
|
||||||
|
|
||||||
|
auto &colors = style.Colors;
|
||||||
|
colors[ImGuiCol_Text] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
|
||||||
|
colors[ImGuiCol_TextDisabled] = ImVec4(1.00f, 0.90f, 0.19f, 1.00f);
|
||||||
|
colors[ImGuiCol_WindowBg] = ImVec4(0.06f, 0.06f, 0.06f, 1.00f);
|
||||||
|
colors[ImGuiCol_ChildBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
|
||||||
|
colors[ImGuiCol_PopupBg] = ImVec4(0.08f, 0.08f, 0.08f, 0.94f);
|
||||||
|
colors[ImGuiCol_Border] = ImVec4(0.30f, 0.30f, 0.30f, 0.50f);
|
||||||
|
colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
|
||||||
|
colors[ImGuiCol_FrameBg] = ImVec4(0.21f, 0.21f, 0.21f, 0.54f);
|
||||||
|
colors[ImGuiCol_FrameBgHovered] = ImVec4(0.21f, 0.21f, 0.21f, 0.78f);
|
||||||
|
colors[ImGuiCol_FrameBgActive] = ImVec4(0.28f, 0.27f, 0.27f, 0.54f);
|
||||||
|
colors[ImGuiCol_TitleBg] = ImVec4(0.17f, 0.17f, 0.17f, 1.00f);
|
||||||
|
colors[ImGuiCol_TitleBgActive] = ImVec4(0.19f, 0.19f, 0.19f, 1.00f);
|
||||||
|
colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.00f, 0.00f, 0.00f, 0.51f);
|
||||||
|
colors[ImGuiCol_MenuBarBg] = ImVec4(0.14f, 0.14f, 0.14f, 1.00f);
|
||||||
|
colors[ImGuiCol_ScrollbarBg] = ImVec4(0.02f, 0.02f, 0.02f, 0.53f);
|
||||||
|
colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.31f, 0.31f, 0.31f, 1.00f);
|
||||||
|
colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.41f, 0.41f, 0.41f, 1.00f);
|
||||||
|
colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.51f, 0.51f, 0.51f, 1.00f);
|
||||||
|
colors[ImGuiCol_CheckMark] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
|
||||||
|
colors[ImGuiCol_SliderGrab] = ImVec4(0.34f, 0.34f, 0.34f, 1.00f);
|
||||||
|
colors[ImGuiCol_SliderGrabActive] = ImVec4(0.39f, 0.38f, 0.38f, 1.00f);
|
||||||
|
colors[ImGuiCol_Button] = ImVec4(0.41f, 0.41f, 0.41f, 0.74f);
|
||||||
|
colors[ImGuiCol_ButtonHovered] = ImVec4(0.41f, 0.41f, 0.41f, 0.78f);
|
||||||
|
colors[ImGuiCol_ButtonActive] = ImVec4(0.41f, 0.41f, 0.41f, 0.87f);
|
||||||
|
colors[ImGuiCol_Header] = ImVec4(0.37f, 0.37f, 0.37f, 0.31f);
|
||||||
|
colors[ImGuiCol_HeaderHovered] = ImVec4(0.38f, 0.38f, 0.38f, 0.37f);
|
||||||
|
colors[ImGuiCol_HeaderActive] = ImVec4(0.37f, 0.37f, 0.37f, 0.51f);
|
||||||
|
colors[ImGuiCol_Separator] = ImVec4(0.38f, 0.38f, 0.38f, 0.50f);
|
||||||
|
colors[ImGuiCol_SeparatorHovered] = ImVec4(0.46f, 0.46f, 0.46f, 0.50f);
|
||||||
|
colors[ImGuiCol_SeparatorActive] = ImVec4(0.46f, 0.46f, 0.46f, 0.64f);
|
||||||
|
colors[ImGuiCol_ResizeGrip] = ImVec4(0.26f, 0.26f, 0.26f, 1.00f);
|
||||||
|
colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.31f, 0.31f, 0.31f, 1.00f);
|
||||||
|
colors[ImGuiCol_ResizeGripActive] = ImVec4(0.35f, 0.35f, 0.35f, 1.00f);
|
||||||
|
colors[ImGuiCol_Tab] = ImVec4(0.21f, 0.21f, 0.21f, 0.86f);
|
||||||
|
colors[ImGuiCol_TabHovered] = ImVec4(0.27f, 0.27f, 0.27f, 0.86f);
|
||||||
|
colors[ImGuiCol_TabActive] = ImVec4(0.34f, 0.34f, 0.34f, 0.86f);
|
||||||
|
colors[ImGuiCol_TabUnfocused] = ImVec4(0.10f, 0.10f, 0.10f, 0.97f);
|
||||||
|
colors[ImGuiCol_TabUnfocusedActive] = ImVec4(0.15f, 0.15f, 0.15f, 1.00f);
|
||||||
|
colors[ImGuiCol_PlotLines] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f);
|
||||||
|
colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f);
|
||||||
|
colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
|
||||||
|
colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f);
|
||||||
|
colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f);
|
||||||
|
colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f);
|
||||||
|
colors[ImGuiCol_NavHighlight] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
|
||||||
|
colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f);
|
||||||
|
colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f);
|
||||||
|
colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.35f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gui::dx_on_tick()
|
||||||
|
{
|
||||||
|
if (ImGui::Begin("BigBaseV2"))
|
||||||
|
{
|
||||||
|
static bool demo_bool = true;
|
||||||
|
static int demo_int = 1;
|
||||||
|
static float demo_float = 1.f;
|
||||||
|
|
||||||
|
static const char *demo_combo[]
|
||||||
|
{
|
||||||
|
"One",
|
||||||
|
"Two",
|
||||||
|
"Three"
|
||||||
|
};
|
||||||
|
static int demo_combo_pos = 0;
|
||||||
|
|
||||||
|
ImGui::Checkbox("Bool", &demo_bool);
|
||||||
|
ImGui::SliderInt("Int", &demo_int, 0, 10);
|
||||||
|
ImGui::SliderFloat("Float", &demo_float, 0.f, 10.f);
|
||||||
|
ImGui::Combo("Combo", &demo_combo_pos, demo_combo, sizeof(demo_combo) / sizeof(*demo_combo));
|
||||||
|
|
||||||
|
if (ImGui::Button("Spawn a vehicle"))
|
||||||
|
{
|
||||||
|
g_fiber_pool->queue_job([]
|
||||||
|
{
|
||||||
|
constexpr auto hash = RAGE_JOAAT("adder");
|
||||||
|
while (!STREAMING::HAS_MODEL_LOADED(hash))
|
||||||
|
{
|
||||||
|
STREAMING::REQUEST_MODEL(hash);
|
||||||
|
script::get_current()->yield();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto pos = ENTITY::GET_ENTITY_COORDS(PLAYER::PLAYER_PED_ID(), true);
|
||||||
|
auto vehicle = VEHICLE::CREATE_VEHICLE(hash, pos.x, pos.y, pos.z, 0.f, true, true);
|
||||||
|
|
||||||
|
if (*g_pointers->m_is_session_started)
|
||||||
|
{
|
||||||
|
DECORATOR::DECOR_SET_INT(vehicle, "MPBitset", 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
STREAMING::SET_MODEL_AS_NO_LONGER_NEEDED(hash);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
if (ImGui::Button("Unload"))
|
||||||
|
{
|
||||||
|
g_running = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::End();
|
||||||
|
}
|
||||||
|
|
||||||
|
void gui::script_init()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void gui::script_on_tick()
|
||||||
|
{
|
||||||
|
if (g_gui.m_opened)
|
||||||
|
{
|
||||||
|
CONTROLS::DISABLE_ALL_CONTROL_ACTIONS(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void gui::script_func()
|
||||||
|
{
|
||||||
|
g_gui.script_init();
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
g_gui.script_on_tick();
|
||||||
|
script::get_current()->yield();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
20
src/gui.hpp
Normal file
20
src/gui.hpp
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "common.hpp"
|
||||||
|
|
||||||
|
namespace big
|
||||||
|
{
|
||||||
|
class gui
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void dx_init();
|
||||||
|
void dx_on_tick();
|
||||||
|
|
||||||
|
void script_init();
|
||||||
|
void script_on_tick();
|
||||||
|
static void script_func();
|
||||||
|
public:
|
||||||
|
bool m_opened{};
|
||||||
|
};
|
||||||
|
|
||||||
|
inline gui g_gui;
|
||||||
|
}
|
194
src/hooking.cpp
Normal file
194
src/hooking.cpp
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
#include "common.hpp"
|
||||||
|
#include "function_types.hpp"
|
||||||
|
#include "logger.hpp"
|
||||||
|
#include "gta/array.hpp"
|
||||||
|
#include "gta/player.hpp"
|
||||||
|
#include "gta/script_thread.hpp"
|
||||||
|
#include "gui.hpp"
|
||||||
|
#include "hooking.hpp"
|
||||||
|
#include "memory/module.hpp"
|
||||||
|
#include "natives.hpp"
|
||||||
|
#include "pointers.hpp"
|
||||||
|
#include "renderer.hpp"
|
||||||
|
#include "script_mgr.hpp"
|
||||||
|
|
||||||
|
#include <mh/MinHook.h>
|
||||||
|
|
||||||
|
namespace big
|
||||||
|
{
|
||||||
|
static GtaThread *find_script_thread(rage::joaat_t hash)
|
||||||
|
{
|
||||||
|
for (auto thread : *g_pointers->m_script_threads)
|
||||||
|
{
|
||||||
|
if (thread
|
||||||
|
&& thread->m_context.m_thread_id
|
||||||
|
&& thread->m_handler
|
||||||
|
&& thread->m_script_hash == hash)
|
||||||
|
{
|
||||||
|
return thread;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
hooking::hooking() :
|
||||||
|
m_swapchain_hook(*g_pointers->m_swapchain, hooks::swapchain_num_funcs),
|
||||||
|
m_set_cursor_pos_hook("SetCursorPos", memory::module("user32.dll").get_export("SetCursorPos").as<void*>(), &hooks::set_cursor_pos),
|
||||||
|
|
||||||
|
m_run_script_threads_hook("Script hook", g_pointers->m_run_script_threads, &hooks::run_script_threads),
|
||||||
|
m_convert_thread_to_fiber_hook("ConvertThreadToFiber", memory::module("kernel32.dll").get_export("ConvertThreadToFiber").as<void*>(), &hooks::convert_thread_to_fiber)
|
||||||
|
|
||||||
|
{
|
||||||
|
m_swapchain_hook.hook(hooks::swapchain_present_index, &hooks::swapchain_present);
|
||||||
|
m_swapchain_hook.hook(hooks::swapchain_resizebuffers_index, &hooks::swapchain_resizebuffers);
|
||||||
|
|
||||||
|
g_hooking = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
hooking::~hooking()
|
||||||
|
{
|
||||||
|
if (m_enabled)
|
||||||
|
disable();
|
||||||
|
|
||||||
|
g_hooking = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void hooking::enable()
|
||||||
|
{
|
||||||
|
m_swapchain_hook.enable();
|
||||||
|
m_og_wndproc = reinterpret_cast<WNDPROC>(SetWindowLongPtrW(g_pointers->m_hwnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(&hooks::wndproc)));
|
||||||
|
m_set_cursor_pos_hook.enable();
|
||||||
|
|
||||||
|
m_run_script_threads_hook.enable();
|
||||||
|
m_convert_thread_to_fiber_hook.enable();
|
||||||
|
|
||||||
|
ensure_dynamic_hooks();
|
||||||
|
m_enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void hooking::disable()
|
||||||
|
{
|
||||||
|
m_enabled = false;
|
||||||
|
|
||||||
|
if (m_main_persistent_hook)
|
||||||
|
{
|
||||||
|
m_main_persistent_hook->disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_convert_thread_to_fiber_hook.disable();
|
||||||
|
m_run_script_threads_hook.disable();
|
||||||
|
|
||||||
|
m_set_cursor_pos_hook.disable();
|
||||||
|
SetWindowLongPtrW(g_pointers->m_hwnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(m_og_wndproc));
|
||||||
|
m_swapchain_hook.disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
void hooking::ensure_dynamic_hooks()
|
||||||
|
{
|
||||||
|
if (!m_main_persistent_hook)
|
||||||
|
{
|
||||||
|
if (auto main_persistent = find_script_thread(RAGE_JOAAT("main_persistent")))
|
||||||
|
{
|
||||||
|
m_main_persistent_hook = std::make_unique<vmt_hook>(main_persistent->m_handler, hooks::main_persistent_num_funcs);
|
||||||
|
m_main_persistent_hook->hook(hooks::main_persistent_dtor_index, &hooks::main_persistent_dtor);
|
||||||
|
m_main_persistent_hook->hook(hooks::main_persistent_is_networked_index, &hooks::main_persistent_is_networked);
|
||||||
|
m_main_persistent_hook->enable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
minhook_keepalive::minhook_keepalive()
|
||||||
|
{
|
||||||
|
MH_Initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
minhook_keepalive::~minhook_keepalive()
|
||||||
|
{
|
||||||
|
MH_Uninitialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hooks::run_script_threads(std::uint32_t ops_to_execute)
|
||||||
|
{
|
||||||
|
if (g_running)
|
||||||
|
{
|
||||||
|
g_script_mgr.tick();
|
||||||
|
}
|
||||||
|
|
||||||
|
return g_hooking->m_run_script_threads_hook.get_original<functions::run_script_threads_t>()(ops_to_execute);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *hooks::convert_thread_to_fiber(void *param)
|
||||||
|
{
|
||||||
|
if (IsThreadAFiber())
|
||||||
|
{
|
||||||
|
return GetCurrentFiber();
|
||||||
|
}
|
||||||
|
|
||||||
|
return g_hooking->m_convert_thread_to_fiber_hook.get_original<decltype(&convert_thread_to_fiber)>()(param);
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT hooks::swapchain_present(IDXGISwapChain *this_, UINT sync_interval, UINT flags)
|
||||||
|
{
|
||||||
|
if (g_running)
|
||||||
|
{
|
||||||
|
g_renderer->on_present();
|
||||||
|
}
|
||||||
|
|
||||||
|
return g_hooking->m_swapchain_hook.get_original<decltype(&swapchain_present)>(swapchain_present_index)(this_, sync_interval, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT hooks::swapchain_resizebuffers(IDXGISwapChain * this_, UINT buffer_count, UINT width, UINT height, DXGI_FORMAT new_format, UINT swapchain_flags)
|
||||||
|
{
|
||||||
|
if (g_running)
|
||||||
|
{
|
||||||
|
g_renderer->pre_reset();
|
||||||
|
|
||||||
|
auto result = g_hooking->m_swapchain_hook.get_original<decltype(&swapchain_resizebuffers)>(swapchain_resizebuffers_index)
|
||||||
|
(this_, buffer_count, width, height, new_format, swapchain_flags);
|
||||||
|
|
||||||
|
if (SUCCEEDED(result))
|
||||||
|
{
|
||||||
|
g_renderer->post_reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return g_hooking->m_swapchain_hook.get_original<decltype(&swapchain_resizebuffers)>(swapchain_resizebuffers_index)
|
||||||
|
(this_, buffer_count, width, height, new_format, swapchain_flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
LRESULT hooks::wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
|
||||||
|
{
|
||||||
|
if (g_running)
|
||||||
|
{
|
||||||
|
g_renderer->wndproc(hwnd, msg, wparam, lparam);
|
||||||
|
}
|
||||||
|
|
||||||
|
return CallWindowProcW(g_hooking->m_og_wndproc, hwnd, msg, wparam, lparam);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL hooks::set_cursor_pos(int x, int y)
|
||||||
|
{
|
||||||
|
if (g_gui.m_opened)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return g_hooking->m_set_cursor_pos_hook.get_original<decltype(&set_cursor_pos)>()(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hooks::main_persistent_dtor(CGameScriptHandler *this_, bool free_memory)
|
||||||
|
{
|
||||||
|
auto og_func = g_hooking->m_main_persistent_hook->get_original<decltype(&main_persistent_dtor)>(main_persistent_dtor_index);
|
||||||
|
g_hooking->m_main_persistent_hook->disable();
|
||||||
|
g_hooking->m_main_persistent_hook.reset();
|
||||||
|
return og_func(this_, free_memory);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hooks::main_persistent_is_networked(CGameScriptHandler *this_)
|
||||||
|
{
|
||||||
|
return *g_pointers->m_is_session_started;
|
||||||
|
}
|
||||||
|
}
|
62
src/hooking.hpp
Normal file
62
src/hooking.hpp
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "common.hpp"
|
||||||
|
#include "detour_hook.hpp"
|
||||||
|
#include "gta/fwddec.hpp"
|
||||||
|
#include "script_hook.hpp"
|
||||||
|
#include "vmt_hook.hpp"
|
||||||
|
|
||||||
|
namespace big
|
||||||
|
{
|
||||||
|
struct hooks
|
||||||
|
{
|
||||||
|
static bool run_script_threads(std::uint32_t ops_to_execute);
|
||||||
|
static void *convert_thread_to_fiber(void *param);
|
||||||
|
|
||||||
|
static constexpr auto swapchain_num_funcs = 19;
|
||||||
|
static constexpr auto swapchain_present_index = 8;
|
||||||
|
static constexpr auto swapchain_resizebuffers_index = 13;
|
||||||
|
static HRESULT swapchain_present(IDXGISwapChain *this_, UINT sync_interval, UINT flags);
|
||||||
|
static HRESULT swapchain_resizebuffers(IDXGISwapChain *this_, UINT buffer_count, UINT width, UINT height, DXGI_FORMAT new_format, UINT swapchain_flags);
|
||||||
|
|
||||||
|
static LRESULT wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
|
||||||
|
static BOOL set_cursor_pos(int x, int y);
|
||||||
|
|
||||||
|
static constexpr auto main_persistent_num_funcs = 16;
|
||||||
|
static constexpr auto main_persistent_dtor_index = 0;
|
||||||
|
static constexpr auto main_persistent_is_networked_index = 6;
|
||||||
|
static void main_persistent_dtor(CGameScriptHandler *this_, bool free_memory);
|
||||||
|
static bool main_persistent_is_networked(CGameScriptHandler *this_);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct minhook_keepalive
|
||||||
|
{
|
||||||
|
minhook_keepalive();
|
||||||
|
~minhook_keepalive();
|
||||||
|
};
|
||||||
|
|
||||||
|
class hooking
|
||||||
|
{
|
||||||
|
friend hooks;
|
||||||
|
public:
|
||||||
|
explicit hooking();
|
||||||
|
~hooking();
|
||||||
|
|
||||||
|
void enable();
|
||||||
|
void disable();
|
||||||
|
|
||||||
|
void ensure_dynamic_hooks();
|
||||||
|
private:
|
||||||
|
bool m_enabled{};
|
||||||
|
minhook_keepalive m_minhook_keepalive;
|
||||||
|
|
||||||
|
vmt_hook m_swapchain_hook;
|
||||||
|
WNDPROC m_og_wndproc;
|
||||||
|
detour_hook m_set_cursor_pos_hook;
|
||||||
|
|
||||||
|
detour_hook m_run_script_threads_hook;
|
||||||
|
detour_hook m_convert_thread_to_fiber_hook;
|
||||||
|
std::unique_ptr<vmt_hook> m_main_persistent_hook;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline hooking *g_hooking{};
|
||||||
|
}
|
73
src/imgui/imconfig.h
Normal file
73
src/imgui/imconfig.h
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// COMPILE-TIME OPTIONS FOR DEAR IMGUI
|
||||||
|
// Runtime options (clipboard callbacks, enabling various features, etc.) can generally be set via the ImGuiIO structure.
|
||||||
|
// You can use ImGui::SetAllocatorFunctions() before calling ImGui::CreateContext() to rewire memory allocation functions.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// A) You may edit imconfig.h (and not overwrite it when updating imgui, or maintain a patch/branch with your modifications to imconfig.h)
|
||||||
|
// B) or add configuration directives in your own file and compile with #define IMGUI_USER_CONFIG "myfilename.h"
|
||||||
|
// If you do so you need to make sure that configuration settings are defined consistently _everywhere_ dear imgui is used, which include
|
||||||
|
// the imgui*.cpp files but also _any_ of your code that uses imgui. This is because some compile-time options have an affect on data structures.
|
||||||
|
// Defining those options in imconfig.h will ensure every compilation unit gets to see the same data structure layouts.
|
||||||
|
// Call IMGUI_CHECKVERSION() from your .cpp files to verify that the data structures your files are using are matching the ones imgui.cpp is using.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
//---- Define assertion handler. Defaults to calling assert().
|
||||||
|
//#define IM_ASSERT(_EXPR) MyAssert(_EXPR)
|
||||||
|
//#define IM_ASSERT(_EXPR) ((void)(_EXPR)) // Disable asserts
|
||||||
|
|
||||||
|
//---- Define attributes of all API symbols declarations, e.g. for DLL under Windows.
|
||||||
|
//#define IMGUI_API __declspec( dllexport )
|
||||||
|
//#define IMGUI_API __declspec( dllimport )
|
||||||
|
|
||||||
|
//---- Don't define obsolete functions/enums names. Consider enabling from time to time after updating to avoid using soon-to-be obsolete function/names.
|
||||||
|
//#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
||||||
|
|
||||||
|
//---- Don't implement demo windows functionality (ShowDemoWindow()/ShowStyleEditor()/ShowUserGuide() methods will be empty)
|
||||||
|
//---- It is very strongly recommended to NOT disable the demo windows during development. Please read the comments in imgui_demo.cpp.
|
||||||
|
//#define IMGUI_DISABLE_DEMO_WINDOWS
|
||||||
|
|
||||||
|
//---- Don't implement some functions to reduce linkage requirements.
|
||||||
|
//#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc.
|
||||||
|
//#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] Don't implement default IME handler. Won't use and link with ImmGetContext/ImmSetCompositionWindow.
|
||||||
|
//#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function.
|
||||||
|
//#define IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself if you don't want to link with vsnprintf.
|
||||||
|
//#define IMGUI_DISABLE_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 wrapper so you can implement them yourself. Declare your prototypes in imconfig.h.
|
||||||
|
//#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions().
|
||||||
|
|
||||||
|
//---- Include imgui_user.h at the end of imgui.h as a convenience
|
||||||
|
//#define IMGUI_INCLUDE_IMGUI_USER_H
|
||||||
|
|
||||||
|
//---- Pack colors to BGRA8 instead of RGBA8 (to avoid converting from one to another)
|
||||||
|
//#define IMGUI_USE_BGRA_PACKED_COLOR
|
||||||
|
|
||||||
|
//---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version
|
||||||
|
// By default the embedded implementations are declared static and not available outside of imgui cpp files.
|
||||||
|
//#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h"
|
||||||
|
//#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h"
|
||||||
|
//#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION
|
||||||
|
//#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION
|
||||||
|
|
||||||
|
//---- Define constructor and implicit cast operators to convert back<>forth between your math types and ImVec2/ImVec4.
|
||||||
|
// This will be inlined as part of ImVec2 and ImVec4 class declarations.
|
||||||
|
/*
|
||||||
|
#define IM_VEC2_CLASS_EXTRA \
|
||||||
|
ImVec2(const MyVec2& f) { x = f.x; y = f.y; } \
|
||||||
|
operator MyVec2() const { return MyVec2(x,y); }
|
||||||
|
|
||||||
|
#define IM_VEC4_CLASS_EXTRA \
|
||||||
|
ImVec4(const MyVec4& f) { x = f.x; y = f.y; z = f.z; w = f.w; } \
|
||||||
|
operator MyVec4() const { return MyVec4(x,y,z,w); }
|
||||||
|
*/
|
||||||
|
|
||||||
|
//---- Use 32-bit vertex indices (default is 16-bit) to allow meshes with more than 64K vertices. Render function needs to support it.
|
||||||
|
//#define ImDrawIdx unsigned int
|
||||||
|
|
||||||
|
//---- Tip: You can add extra functions within the ImGui:: namespace, here or in your own headers files.
|
||||||
|
/*
|
||||||
|
namespace ImGui
|
||||||
|
{
|
||||||
|
void MyFunction(const char* name, const MyMatrix44& v);
|
||||||
|
}
|
||||||
|
*/
|
9355
src/imgui/imgui.cpp
Normal file
9355
src/imgui/imgui.cpp
Normal file
File diff suppressed because it is too large
Load Diff
2132
src/imgui/imgui.h
Normal file
2132
src/imgui/imgui.h
Normal file
File diff suppressed because it is too large
Load Diff
4215
src/imgui/imgui_demo.cpp
Normal file
4215
src/imgui/imgui_demo.cpp
Normal file
File diff suppressed because it is too large
Load Diff
3276
src/imgui/imgui_draw.cpp
Normal file
3276
src/imgui/imgui_draw.cpp
Normal file
File diff suppressed because it is too large
Load Diff
512
src/imgui/imgui_impl_dx11.cpp
Normal file
512
src/imgui/imgui_impl_dx11.cpp
Normal file
@ -0,0 +1,512 @@
|
|||||||
|
// dear imgui: Renderer for DirectX11
|
||||||
|
// This needs to be used along with a Platform Binding (e.g. Win32)
|
||||||
|
|
||||||
|
// Implemented features:
|
||||||
|
// [X] Renderer: User texture binding. Use 'ID3D11ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp.
|
||||||
|
|
||||||
|
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
|
||||||
|
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp
|
||||||
|
// https://github.com/ocornut/imgui
|
||||||
|
|
||||||
|
// CHANGELOG
|
||||||
|
// (minor and older changes stripped away, please see git history for details)
|
||||||
|
// 2018-12-03: Misc: Added #pragma comment statement to automatically link with d3dcompiler.lib when using D3DCompile().
|
||||||
|
// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window.
|
||||||
|
// 2018-08-01: DirectX11: Querying for IDXGIFactory instead of IDXGIFactory1 to increase compatibility.
|
||||||
|
// 2018-07-13: DirectX11: Fixed unreleased resources in Init and Shutdown functions.
|
||||||
|
// 2018-06-08: Misc: Extracted imgui_impl_dx11.cpp/.h away from the old combined DX11+Win32 example.
|
||||||
|
// 2018-06-08: DirectX11: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle.
|
||||||
|
// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplDX11_RenderDrawData() in the .h file so you can call it yourself.
|
||||||
|
// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves.
|
||||||
|
// 2016-05-07: DirectX11: Disabling depth-write.
|
||||||
|
|
||||||
|
#include "imgui.h"
|
||||||
|
#include "imgui_impl_dx11.h"
|
||||||
|
|
||||||
|
// DirectX
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <d3d11.h>
|
||||||
|
#include <d3dcompiler.h>
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma comment(lib, "d3dcompiler") // Automatically link with d3dcompiler.lib as we are using D3DCompile() below.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// DirectX data
|
||||||
|
static ID3D11Device* g_pd3dDevice = NULL;
|
||||||
|
static ID3D11DeviceContext* g_pd3dDeviceContext = NULL;
|
||||||
|
static IDXGIFactory* g_pFactory = NULL;
|
||||||
|
static ID3D11Buffer* g_pVB = NULL;
|
||||||
|
static ID3D11Buffer* g_pIB = NULL;
|
||||||
|
static ID3D10Blob* g_pVertexShaderBlob = NULL;
|
||||||
|
static ID3D11VertexShader* g_pVertexShader = NULL;
|
||||||
|
static ID3D11InputLayout* g_pInputLayout = NULL;
|
||||||
|
static ID3D11Buffer* g_pVertexConstantBuffer = NULL;
|
||||||
|
static ID3D10Blob* g_pPixelShaderBlob = NULL;
|
||||||
|
static ID3D11PixelShader* g_pPixelShader = NULL;
|
||||||
|
static ID3D11SamplerState* g_pFontSampler = NULL;
|
||||||
|
static ID3D11ShaderResourceView*g_pFontTextureView = NULL;
|
||||||
|
static ID3D11RasterizerState* g_pRasterizerState = NULL;
|
||||||
|
static ID3D11BlendState* g_pBlendState = NULL;
|
||||||
|
static ID3D11DepthStencilState* g_pDepthStencilState = NULL;
|
||||||
|
static int g_VertexBufferSize = 5000, g_IndexBufferSize = 10000;
|
||||||
|
|
||||||
|
struct VERTEX_CONSTANT_BUFFER
|
||||||
|
{
|
||||||
|
float mvp[4][4];
|
||||||
|
};
|
||||||
|
|
||||||
|
// Render function
|
||||||
|
// (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop)
|
||||||
|
void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data)
|
||||||
|
{
|
||||||
|
ID3D11DeviceContext* ctx = g_pd3dDeviceContext;
|
||||||
|
|
||||||
|
// Create and grow vertex/index buffers if needed
|
||||||
|
if (!g_pVB || g_VertexBufferSize < draw_data->TotalVtxCount)
|
||||||
|
{
|
||||||
|
if (g_pVB) { g_pVB->Release(); g_pVB = NULL; }
|
||||||
|
g_VertexBufferSize = draw_data->TotalVtxCount + 5000;
|
||||||
|
D3D11_BUFFER_DESC desc;
|
||||||
|
memset(&desc, 0, sizeof(D3D11_BUFFER_DESC));
|
||||||
|
desc.Usage = D3D11_USAGE_DYNAMIC;
|
||||||
|
desc.ByteWidth = g_VertexBufferSize * sizeof(ImDrawVert);
|
||||||
|
desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
|
||||||
|
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
|
||||||
|
desc.MiscFlags = 0;
|
||||||
|
if (g_pd3dDevice->CreateBuffer(&desc, NULL, &g_pVB) < 0)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!g_pIB || g_IndexBufferSize < draw_data->TotalIdxCount)
|
||||||
|
{
|
||||||
|
if (g_pIB) { g_pIB->Release(); g_pIB = NULL; }
|
||||||
|
g_IndexBufferSize = draw_data->TotalIdxCount + 10000;
|
||||||
|
D3D11_BUFFER_DESC desc;
|
||||||
|
memset(&desc, 0, sizeof(D3D11_BUFFER_DESC));
|
||||||
|
desc.Usage = D3D11_USAGE_DYNAMIC;
|
||||||
|
desc.ByteWidth = g_IndexBufferSize * sizeof(ImDrawIdx);
|
||||||
|
desc.BindFlags = D3D11_BIND_INDEX_BUFFER;
|
||||||
|
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
|
||||||
|
if (g_pd3dDevice->CreateBuffer(&desc, NULL, &g_pIB) < 0)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy and convert all vertices into a single contiguous buffer
|
||||||
|
D3D11_MAPPED_SUBRESOURCE vtx_resource, idx_resource;
|
||||||
|
if (ctx->Map(g_pVB, 0, D3D11_MAP_WRITE_DISCARD, 0, &vtx_resource) != S_OK)
|
||||||
|
return;
|
||||||
|
if (ctx->Map(g_pIB, 0, D3D11_MAP_WRITE_DISCARD, 0, &idx_resource) != S_OK)
|
||||||
|
return;
|
||||||
|
ImDrawVert* vtx_dst = (ImDrawVert*)vtx_resource.pData;
|
||||||
|
ImDrawIdx* idx_dst = (ImDrawIdx*)idx_resource.pData;
|
||||||
|
for (int n = 0; n < draw_data->CmdListsCount; n++)
|
||||||
|
{
|
||||||
|
const ImDrawList* cmd_list = draw_data->CmdLists[n];
|
||||||
|
memcpy(vtx_dst, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert));
|
||||||
|
memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx));
|
||||||
|
vtx_dst += cmd_list->VtxBuffer.Size;
|
||||||
|
idx_dst += cmd_list->IdxBuffer.Size;
|
||||||
|
}
|
||||||
|
ctx->Unmap(g_pVB, 0);
|
||||||
|
ctx->Unmap(g_pIB, 0);
|
||||||
|
|
||||||
|
// Setup orthographic projection matrix into our constant buffer
|
||||||
|
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right).
|
||||||
|
{
|
||||||
|
D3D11_MAPPED_SUBRESOURCE mapped_resource;
|
||||||
|
if (ctx->Map(g_pVertexConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped_resource) != S_OK)
|
||||||
|
return;
|
||||||
|
VERTEX_CONSTANT_BUFFER* constant_buffer = (VERTEX_CONSTANT_BUFFER*)mapped_resource.pData;
|
||||||
|
float L = draw_data->DisplayPos.x;
|
||||||
|
float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x;
|
||||||
|
float T = draw_data->DisplayPos.y;
|
||||||
|
float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y;
|
||||||
|
float mvp[4][4] =
|
||||||
|
{
|
||||||
|
{ 2.0f/(R-L), 0.0f, 0.0f, 0.0f },
|
||||||
|
{ 0.0f, 2.0f/(T-B), 0.0f, 0.0f },
|
||||||
|
{ 0.0f, 0.0f, 0.5f, 0.0f },
|
||||||
|
{ (R+L)/(L-R), (T+B)/(B-T), 0.5f, 1.0f },
|
||||||
|
};
|
||||||
|
memcpy(&constant_buffer->mvp, mvp, sizeof(mvp));
|
||||||
|
ctx->Unmap(g_pVertexConstantBuffer, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Backup DX state that will be modified to restore it afterwards (unfortunately this is very ugly looking and verbose. Close your eyes!)
|
||||||
|
struct BACKUP_DX11_STATE
|
||||||
|
{
|
||||||
|
UINT ScissorRectsCount, ViewportsCount;
|
||||||
|
D3D11_RECT ScissorRects[D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE];
|
||||||
|
D3D11_VIEWPORT Viewports[D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE];
|
||||||
|
ID3D11RasterizerState* RS;
|
||||||
|
ID3D11BlendState* BlendState;
|
||||||
|
FLOAT BlendFactor[4];
|
||||||
|
UINT SampleMask;
|
||||||
|
UINT StencilRef;
|
||||||
|
ID3D11DepthStencilState* DepthStencilState;
|
||||||
|
ID3D11ShaderResourceView* PSShaderResource;
|
||||||
|
ID3D11SamplerState* PSSampler;
|
||||||
|
ID3D11PixelShader* PS;
|
||||||
|
ID3D11VertexShader* VS;
|
||||||
|
UINT PSInstancesCount, VSInstancesCount;
|
||||||
|
ID3D11ClassInstance* PSInstances[256], *VSInstances[256]; // 256 is max according to PSSetShader documentation
|
||||||
|
D3D11_PRIMITIVE_TOPOLOGY PrimitiveTopology;
|
||||||
|
ID3D11Buffer* IndexBuffer, *VertexBuffer, *VSConstantBuffer;
|
||||||
|
UINT IndexBufferOffset, VertexBufferStride, VertexBufferOffset;
|
||||||
|
DXGI_FORMAT IndexBufferFormat;
|
||||||
|
ID3D11InputLayout* InputLayout;
|
||||||
|
};
|
||||||
|
BACKUP_DX11_STATE old;
|
||||||
|
old.ScissorRectsCount = old.ViewportsCount = D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE;
|
||||||
|
ctx->RSGetScissorRects(&old.ScissorRectsCount, old.ScissorRects);
|
||||||
|
ctx->RSGetViewports(&old.ViewportsCount, old.Viewports);
|
||||||
|
ctx->RSGetState(&old.RS);
|
||||||
|
ctx->OMGetBlendState(&old.BlendState, old.BlendFactor, &old.SampleMask);
|
||||||
|
ctx->OMGetDepthStencilState(&old.DepthStencilState, &old.StencilRef);
|
||||||
|
ctx->PSGetShaderResources(0, 1, &old.PSShaderResource);
|
||||||
|
ctx->PSGetSamplers(0, 1, &old.PSSampler);
|
||||||
|
old.PSInstancesCount = old.VSInstancesCount = 256;
|
||||||
|
ctx->PSGetShader(&old.PS, old.PSInstances, &old.PSInstancesCount);
|
||||||
|
ctx->VSGetShader(&old.VS, old.VSInstances, &old.VSInstancesCount);
|
||||||
|
ctx->VSGetConstantBuffers(0, 1, &old.VSConstantBuffer);
|
||||||
|
ctx->IAGetPrimitiveTopology(&old.PrimitiveTopology);
|
||||||
|
ctx->IAGetIndexBuffer(&old.IndexBuffer, &old.IndexBufferFormat, &old.IndexBufferOffset);
|
||||||
|
ctx->IAGetVertexBuffers(0, 1, &old.VertexBuffer, &old.VertexBufferStride, &old.VertexBufferOffset);
|
||||||
|
ctx->IAGetInputLayout(&old.InputLayout);
|
||||||
|
|
||||||
|
// Setup viewport
|
||||||
|
D3D11_VIEWPORT vp;
|
||||||
|
memset(&vp, 0, sizeof(D3D11_VIEWPORT));
|
||||||
|
vp.Width = draw_data->DisplaySize.x;
|
||||||
|
vp.Height = draw_data->DisplaySize.y;
|
||||||
|
vp.MinDepth = 0.0f;
|
||||||
|
vp.MaxDepth = 1.0f;
|
||||||
|
vp.TopLeftX = vp.TopLeftY = 0;
|
||||||
|
ctx->RSSetViewports(1, &vp);
|
||||||
|
|
||||||
|
// Bind shader and vertex buffers
|
||||||
|
unsigned int stride = sizeof(ImDrawVert);
|
||||||
|
unsigned int offset = 0;
|
||||||
|
ctx->IASetInputLayout(g_pInputLayout);
|
||||||
|
ctx->IASetVertexBuffers(0, 1, &g_pVB, &stride, &offset);
|
||||||
|
ctx->IASetIndexBuffer(g_pIB, sizeof(ImDrawIdx) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT, 0);
|
||||||
|
ctx->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
||||||
|
ctx->VSSetShader(g_pVertexShader, NULL, 0);
|
||||||
|
ctx->VSSetConstantBuffers(0, 1, &g_pVertexConstantBuffer);
|
||||||
|
ctx->PSSetShader(g_pPixelShader, NULL, 0);
|
||||||
|
ctx->PSSetSamplers(0, 1, &g_pFontSampler);
|
||||||
|
|
||||||
|
// Setup render state
|
||||||
|
const float blend_factor[4] = { 0.f, 0.f, 0.f, 0.f };
|
||||||
|
ctx->OMSetBlendState(g_pBlendState, blend_factor, 0xffffffff);
|
||||||
|
ctx->OMSetDepthStencilState(g_pDepthStencilState, 0);
|
||||||
|
ctx->RSSetState(g_pRasterizerState);
|
||||||
|
|
||||||
|
// Render command lists
|
||||||
|
int vtx_offset = 0;
|
||||||
|
int idx_offset = 0;
|
||||||
|
ImVec2 pos = draw_data->DisplayPos;
|
||||||
|
for (int n = 0; n < draw_data->CmdListsCount; n++)
|
||||||
|
{
|
||||||
|
const ImDrawList* cmd_list = draw_data->CmdLists[n];
|
||||||
|
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
|
||||||
|
{
|
||||||
|
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
|
||||||
|
if (pcmd->UserCallback)
|
||||||
|
{
|
||||||
|
// User callback (registered via ImDrawList::AddCallback)
|
||||||
|
pcmd->UserCallback(cmd_list, pcmd);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Apply scissor/clipping rectangle
|
||||||
|
const D3D11_RECT r = { (LONG)(pcmd->ClipRect.x - pos.x), (LONG)(pcmd->ClipRect.y - pos.y), (LONG)(pcmd->ClipRect.z - pos.x), (LONG)(pcmd->ClipRect.w - pos.y) };
|
||||||
|
ctx->RSSetScissorRects(1, &r);
|
||||||
|
|
||||||
|
// Bind texture, Draw
|
||||||
|
ID3D11ShaderResourceView* texture_srv = (ID3D11ShaderResourceView*)pcmd->TextureId;
|
||||||
|
ctx->PSSetShaderResources(0, 1, &texture_srv);
|
||||||
|
ctx->DrawIndexed(pcmd->ElemCount, idx_offset, vtx_offset);
|
||||||
|
}
|
||||||
|
idx_offset += pcmd->ElemCount;
|
||||||
|
}
|
||||||
|
vtx_offset += cmd_list->VtxBuffer.Size;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore modified DX state
|
||||||
|
ctx->RSSetScissorRects(old.ScissorRectsCount, old.ScissorRects);
|
||||||
|
ctx->RSSetViewports(old.ViewportsCount, old.Viewports);
|
||||||
|
ctx->RSSetState(old.RS); if (old.RS) old.RS->Release();
|
||||||
|
ctx->OMSetBlendState(old.BlendState, old.BlendFactor, old.SampleMask); if (old.BlendState) old.BlendState->Release();
|
||||||
|
ctx->OMSetDepthStencilState(old.DepthStencilState, old.StencilRef); if (old.DepthStencilState) old.DepthStencilState->Release();
|
||||||
|
ctx->PSSetShaderResources(0, 1, &old.PSShaderResource); if (old.PSShaderResource) old.PSShaderResource->Release();
|
||||||
|
ctx->PSSetSamplers(0, 1, &old.PSSampler); if (old.PSSampler) old.PSSampler->Release();
|
||||||
|
ctx->PSSetShader(old.PS, old.PSInstances, old.PSInstancesCount); if (old.PS) old.PS->Release();
|
||||||
|
for (UINT i = 0; i < old.PSInstancesCount; i++) if (old.PSInstances[i]) old.PSInstances[i]->Release();
|
||||||
|
ctx->VSSetShader(old.VS, old.VSInstances, old.VSInstancesCount); if (old.VS) old.VS->Release();
|
||||||
|
ctx->VSSetConstantBuffers(0, 1, &old.VSConstantBuffer); if (old.VSConstantBuffer) old.VSConstantBuffer->Release();
|
||||||
|
for (UINT i = 0; i < old.VSInstancesCount; i++) if (old.VSInstances[i]) old.VSInstances[i]->Release();
|
||||||
|
ctx->IASetPrimitiveTopology(old.PrimitiveTopology);
|
||||||
|
ctx->IASetIndexBuffer(old.IndexBuffer, old.IndexBufferFormat, old.IndexBufferOffset); if (old.IndexBuffer) old.IndexBuffer->Release();
|
||||||
|
ctx->IASetVertexBuffers(0, 1, &old.VertexBuffer, &old.VertexBufferStride, &old.VertexBufferOffset); if (old.VertexBuffer) old.VertexBuffer->Release();
|
||||||
|
ctx->IASetInputLayout(old.InputLayout); if (old.InputLayout) old.InputLayout->Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGui_ImplDX11_CreateFontsTexture()
|
||||||
|
{
|
||||||
|
// Build texture atlas
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
unsigned char* pixels;
|
||||||
|
int width, height;
|
||||||
|
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
|
||||||
|
|
||||||
|
// Upload texture to graphics system
|
||||||
|
{
|
||||||
|
D3D11_TEXTURE2D_DESC desc;
|
||||||
|
ZeroMemory(&desc, sizeof(desc));
|
||||||
|
desc.Width = width;
|
||||||
|
desc.Height = height;
|
||||||
|
desc.MipLevels = 1;
|
||||||
|
desc.ArraySize = 1;
|
||||||
|
desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||||
|
desc.SampleDesc.Count = 1;
|
||||||
|
desc.Usage = D3D11_USAGE_DEFAULT;
|
||||||
|
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
|
||||||
|
desc.CPUAccessFlags = 0;
|
||||||
|
|
||||||
|
ID3D11Texture2D *pTexture = NULL;
|
||||||
|
D3D11_SUBRESOURCE_DATA subResource;
|
||||||
|
subResource.pSysMem = pixels;
|
||||||
|
subResource.SysMemPitch = desc.Width * 4;
|
||||||
|
subResource.SysMemSlicePitch = 0;
|
||||||
|
g_pd3dDevice->CreateTexture2D(&desc, &subResource, &pTexture);
|
||||||
|
|
||||||
|
// Create texture view
|
||||||
|
D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
|
||||||
|
ZeroMemory(&srvDesc, sizeof(srvDesc));
|
||||||
|
srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||||
|
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
|
||||||
|
srvDesc.Texture2D.MipLevels = desc.MipLevels;
|
||||||
|
srvDesc.Texture2D.MostDetailedMip = 0;
|
||||||
|
g_pd3dDevice->CreateShaderResourceView(pTexture, &srvDesc, &g_pFontTextureView);
|
||||||
|
pTexture->Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store our identifier
|
||||||
|
io.Fonts->TexID = (ImTextureID)g_pFontTextureView;
|
||||||
|
|
||||||
|
// Create texture sampler
|
||||||
|
{
|
||||||
|
D3D11_SAMPLER_DESC desc;
|
||||||
|
ZeroMemory(&desc, sizeof(desc));
|
||||||
|
desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
|
||||||
|
desc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
|
||||||
|
desc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
|
||||||
|
desc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
|
||||||
|
desc.MipLODBias = 0.f;
|
||||||
|
desc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
|
||||||
|
desc.MinLOD = 0.f;
|
||||||
|
desc.MaxLOD = 0.f;
|
||||||
|
g_pd3dDevice->CreateSamplerState(&desc, &g_pFontSampler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ImGui_ImplDX11_CreateDeviceObjects()
|
||||||
|
{
|
||||||
|
if (!g_pd3dDevice)
|
||||||
|
return false;
|
||||||
|
if (g_pFontSampler)
|
||||||
|
ImGui_ImplDX11_InvalidateDeviceObjects();
|
||||||
|
|
||||||
|
// By using D3DCompile() from <d3dcompiler.h> / d3dcompiler.lib, we introduce a dependency to a given version of d3dcompiler_XX.dll (see D3DCOMPILER_DLL_A)
|
||||||
|
// If you would like to use this DX11 sample code but remove this dependency you can:
|
||||||
|
// 1) compile once, save the compiled shader blobs into a file or source code and pass them to CreateVertexShader()/CreatePixelShader() [preferred solution]
|
||||||
|
// 2) use code to detect any version of the DLL and grab a pointer to D3DCompile from the DLL.
|
||||||
|
// See https://github.com/ocornut/imgui/pull/638 for sources and details.
|
||||||
|
|
||||||
|
// Create the vertex shader
|
||||||
|
{
|
||||||
|
static const char* vertexShader =
|
||||||
|
"cbuffer vertexBuffer : register(b0) \
|
||||||
|
{\
|
||||||
|
float4x4 ProjectionMatrix; \
|
||||||
|
};\
|
||||||
|
struct VS_INPUT\
|
||||||
|
{\
|
||||||
|
float2 pos : POSITION;\
|
||||||
|
float4 col : COLOR0;\
|
||||||
|
float2 uv : TEXCOORD0;\
|
||||||
|
};\
|
||||||
|
\
|
||||||
|
struct PS_INPUT\
|
||||||
|
{\
|
||||||
|
float4 pos : SV_POSITION;\
|
||||||
|
float4 col : COLOR0;\
|
||||||
|
float2 uv : TEXCOORD0;\
|
||||||
|
};\
|
||||||
|
\
|
||||||
|
PS_INPUT main(VS_INPUT input)\
|
||||||
|
{\
|
||||||
|
PS_INPUT output;\
|
||||||
|
output.pos = mul( ProjectionMatrix, float4(input.pos.xy, 0.f, 1.f));\
|
||||||
|
output.col = input.col;\
|
||||||
|
output.uv = input.uv;\
|
||||||
|
return output;\
|
||||||
|
}";
|
||||||
|
|
||||||
|
D3DCompile(vertexShader, strlen(vertexShader), NULL, NULL, NULL, "main", "vs_4_0", 0, 0, &g_pVertexShaderBlob, NULL);
|
||||||
|
if (g_pVertexShaderBlob == NULL) // NB: Pass ID3D10Blob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob!
|
||||||
|
return false;
|
||||||
|
if (g_pd3dDevice->CreateVertexShader((DWORD*)g_pVertexShaderBlob->GetBufferPointer(), g_pVertexShaderBlob->GetBufferSize(), NULL, &g_pVertexShader) != S_OK)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Create the input layout
|
||||||
|
D3D11_INPUT_ELEMENT_DESC local_layout[] =
|
||||||
|
{
|
||||||
|
{ "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (size_t)(&((ImDrawVert*)0)->pos), D3D11_INPUT_PER_VERTEX_DATA, 0 },
|
||||||
|
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (size_t)(&((ImDrawVert*)0)->uv), D3D11_INPUT_PER_VERTEX_DATA, 0 },
|
||||||
|
{ "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, (size_t)(&((ImDrawVert*)0)->col), D3D11_INPUT_PER_VERTEX_DATA, 0 },
|
||||||
|
};
|
||||||
|
if (g_pd3dDevice->CreateInputLayout(local_layout, 3, g_pVertexShaderBlob->GetBufferPointer(), g_pVertexShaderBlob->GetBufferSize(), &g_pInputLayout) != S_OK)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Create the constant buffer
|
||||||
|
{
|
||||||
|
D3D11_BUFFER_DESC desc;
|
||||||
|
desc.ByteWidth = sizeof(VERTEX_CONSTANT_BUFFER);
|
||||||
|
desc.Usage = D3D11_USAGE_DYNAMIC;
|
||||||
|
desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
|
||||||
|
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
|
||||||
|
desc.MiscFlags = 0;
|
||||||
|
g_pd3dDevice->CreateBuffer(&desc, NULL, &g_pVertexConstantBuffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the pixel shader
|
||||||
|
{
|
||||||
|
static const char* pixelShader =
|
||||||
|
"struct PS_INPUT\
|
||||||
|
{\
|
||||||
|
float4 pos : SV_POSITION;\
|
||||||
|
float4 col : COLOR0;\
|
||||||
|
float2 uv : TEXCOORD0;\
|
||||||
|
};\
|
||||||
|
sampler sampler0;\
|
||||||
|
Texture2D texture0;\
|
||||||
|
\
|
||||||
|
float4 main(PS_INPUT input) : SV_Target\
|
||||||
|
{\
|
||||||
|
float4 out_col = input.col * texture0.Sample(sampler0, input.uv); \
|
||||||
|
return out_col; \
|
||||||
|
}";
|
||||||
|
|
||||||
|
D3DCompile(pixelShader, strlen(pixelShader), NULL, NULL, NULL, "main", "ps_4_0", 0, 0, &g_pPixelShaderBlob, NULL);
|
||||||
|
if (g_pPixelShaderBlob == NULL) // NB: Pass ID3D10Blob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob!
|
||||||
|
return false;
|
||||||
|
if (g_pd3dDevice->CreatePixelShader((DWORD*)g_pPixelShaderBlob->GetBufferPointer(), g_pPixelShaderBlob->GetBufferSize(), NULL, &g_pPixelShader) != S_OK)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the blending setup
|
||||||
|
{
|
||||||
|
D3D11_BLEND_DESC desc;
|
||||||
|
ZeroMemory(&desc, sizeof(desc));
|
||||||
|
desc.AlphaToCoverageEnable = false;
|
||||||
|
desc.RenderTarget[0].BlendEnable = true;
|
||||||
|
desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
|
||||||
|
desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
|
||||||
|
desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
|
||||||
|
desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA;
|
||||||
|
desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;
|
||||||
|
desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
|
||||||
|
desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
|
||||||
|
g_pd3dDevice->CreateBlendState(&desc, &g_pBlendState);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the rasterizer state
|
||||||
|
{
|
||||||
|
D3D11_RASTERIZER_DESC desc;
|
||||||
|
ZeroMemory(&desc, sizeof(desc));
|
||||||
|
desc.FillMode = D3D11_FILL_SOLID;
|
||||||
|
desc.CullMode = D3D11_CULL_NONE;
|
||||||
|
desc.ScissorEnable = true;
|
||||||
|
desc.DepthClipEnable = true;
|
||||||
|
g_pd3dDevice->CreateRasterizerState(&desc, &g_pRasterizerState);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create depth-stencil State
|
||||||
|
{
|
||||||
|
D3D11_DEPTH_STENCIL_DESC desc;
|
||||||
|
ZeroMemory(&desc, sizeof(desc));
|
||||||
|
desc.DepthEnable = false;
|
||||||
|
desc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
|
||||||
|
desc.DepthFunc = D3D11_COMPARISON_ALWAYS;
|
||||||
|
desc.StencilEnable = false;
|
||||||
|
desc.FrontFace.StencilFailOp = desc.FrontFace.StencilDepthFailOp = desc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
|
||||||
|
desc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
|
||||||
|
desc.BackFace = desc.FrontFace;
|
||||||
|
g_pd3dDevice->CreateDepthStencilState(&desc, &g_pDepthStencilState);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui_ImplDX11_CreateFontsTexture();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGui_ImplDX11_InvalidateDeviceObjects()
|
||||||
|
{
|
||||||
|
if (!g_pd3dDevice)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (g_pFontSampler) { g_pFontSampler->Release(); g_pFontSampler = NULL; }
|
||||||
|
if (g_pFontTextureView) { g_pFontTextureView->Release(); g_pFontTextureView = NULL; ImGui::GetIO().Fonts->TexID = NULL; } // We copied g_pFontTextureView to io.Fonts->TexID so let's clear that as well.
|
||||||
|
if (g_pIB) { g_pIB->Release(); g_pIB = NULL; }
|
||||||
|
if (g_pVB) { g_pVB->Release(); g_pVB = NULL; }
|
||||||
|
|
||||||
|
if (g_pBlendState) { g_pBlendState->Release(); g_pBlendState = NULL; }
|
||||||
|
if (g_pDepthStencilState) { g_pDepthStencilState->Release(); g_pDepthStencilState = NULL; }
|
||||||
|
if (g_pRasterizerState) { g_pRasterizerState->Release(); g_pRasterizerState = NULL; }
|
||||||
|
if (g_pPixelShader) { g_pPixelShader->Release(); g_pPixelShader = NULL; }
|
||||||
|
if (g_pPixelShaderBlob) { g_pPixelShaderBlob->Release(); g_pPixelShaderBlob = NULL; }
|
||||||
|
if (g_pVertexConstantBuffer) { g_pVertexConstantBuffer->Release(); g_pVertexConstantBuffer = NULL; }
|
||||||
|
if (g_pInputLayout) { g_pInputLayout->Release(); g_pInputLayout = NULL; }
|
||||||
|
if (g_pVertexShader) { g_pVertexShader->Release(); g_pVertexShader = NULL; }
|
||||||
|
if (g_pVertexShaderBlob) { g_pVertexShaderBlob->Release(); g_pVertexShaderBlob = NULL; }
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_context)
|
||||||
|
{
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
io.BackendRendererName = "imgui_impl_dx11";
|
||||||
|
|
||||||
|
// Get factory from device
|
||||||
|
IDXGIDevice* pDXGIDevice = NULL;
|
||||||
|
IDXGIAdapter* pDXGIAdapter = NULL;
|
||||||
|
IDXGIFactory* pFactory = NULL;
|
||||||
|
|
||||||
|
if (device->QueryInterface(IID_PPV_ARGS(&pDXGIDevice)) == S_OK)
|
||||||
|
if (pDXGIDevice->GetParent(IID_PPV_ARGS(&pDXGIAdapter)) == S_OK)
|
||||||
|
if (pDXGIAdapter->GetParent(IID_PPV_ARGS(&pFactory)) == S_OK)
|
||||||
|
{
|
||||||
|
g_pd3dDevice = device;
|
||||||
|
g_pd3dDeviceContext = device_context;
|
||||||
|
g_pFactory = pFactory;
|
||||||
|
}
|
||||||
|
if (pDXGIDevice) pDXGIDevice->Release();
|
||||||
|
if (pDXGIAdapter) pDXGIAdapter->Release();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGui_ImplDX11_Shutdown()
|
||||||
|
{
|
||||||
|
ImGui_ImplDX11_InvalidateDeviceObjects();
|
||||||
|
if (g_pFactory) { g_pFactory->Release(); g_pFactory = NULL; }
|
||||||
|
g_pd3dDevice = NULL;
|
||||||
|
g_pd3dDeviceContext = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGui_ImplDX11_NewFrame()
|
||||||
|
{
|
||||||
|
if (!g_pFontSampler)
|
||||||
|
ImGui_ImplDX11_CreateDeviceObjects();
|
||||||
|
}
|
23
src/imgui/imgui_impl_dx11.h
Normal file
23
src/imgui/imgui_impl_dx11.h
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
// dear imgui: Renderer for DirectX11
|
||||||
|
// This needs to be used along with a Platform Binding (e.g. Win32)
|
||||||
|
|
||||||
|
// Implemented features:
|
||||||
|
// [X] Renderer: User texture binding. Use 'ID3D11ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp.
|
||||||
|
|
||||||
|
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
|
||||||
|
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
|
||||||
|
// https://github.com/ocornut/imgui
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
struct ID3D11Device;
|
||||||
|
struct ID3D11DeviceContext;
|
||||||
|
|
||||||
|
IMGUI_IMPL_API bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_context);
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplDX11_Shutdown();
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplDX11_NewFrame();
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data);
|
||||||
|
|
||||||
|
// Use if you want to reset your rendering device without losing ImGui state.
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplDX11_InvalidateDeviceObjects();
|
||||||
|
IMGUI_IMPL_API bool ImGui_ImplDX11_CreateDeviceObjects();
|
251
src/imgui/imgui_impl_win32.cpp
Normal file
251
src/imgui/imgui_impl_win32.cpp
Normal file
@ -0,0 +1,251 @@
|
|||||||
|
// dear imgui: Platform Binding for Windows (standard windows API for 32 and 64 bits applications)
|
||||||
|
// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..)
|
||||||
|
|
||||||
|
// Implemented features:
|
||||||
|
// [X] Platform: Clipboard support (for Win32 this is actually part of core imgui)
|
||||||
|
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
|
||||||
|
// [X] Platform: Keyboard arrays indexed using VK_* Virtual Key Codes, e.g. ImGui::IsKeyPressed(VK_SPACE).
|
||||||
|
// Missing features:
|
||||||
|
// [ ] Platform: Gamepad support (best leaving it to user application to fill io.NavInputs[] with gamepad inputs from their source of choice).
|
||||||
|
|
||||||
|
#include "imgui.h"
|
||||||
|
#include "imgui_impl_win32.h"
|
||||||
|
#ifndef WIN32_LEAN_AND_MEAN
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#endif
|
||||||
|
#include <windows.h>
|
||||||
|
#include <tchar.h>
|
||||||
|
|
||||||
|
// CHANGELOG
|
||||||
|
// (minor and older changes stripped away, please see git history for details)
|
||||||
|
// 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window.
|
||||||
|
// 2018-06-29: Inputs: Added support for the ImGuiMouseCursor_Hand cursor.
|
||||||
|
// 2018-06-10: Inputs: Fixed handling of mouse wheel messages to support fine position messages (typically sent by track-pads).
|
||||||
|
// 2018-06-08: Misc: Extracted imgui_impl_win32.cpp/.h away from the old combined DX9/DX10/DX11/DX12 examples.
|
||||||
|
// 2018-03-20: Misc: Setup io.BackendFlags ImGuiBackendFlags_HasMouseCursors and ImGuiBackendFlags_HasSetMousePos flags + honor ImGuiConfigFlags_NoMouseCursorChange flag.
|
||||||
|
// 2018-02-20: Inputs: Added support for mouse cursors (ImGui::GetMouseCursor() value and WM_SETCURSOR message handling).
|
||||||
|
// 2018-02-06: Inputs: Added mapping for ImGuiKey_Space.
|
||||||
|
// 2018-02-06: Inputs: Honoring the io.WantSetMousePos by repositioning the mouse (when using navigation and ImGuiConfigFlags_NavMoveMouse is set).
|
||||||
|
// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves.
|
||||||
|
// 2018-01-20: Inputs: Added Horizontal Mouse Wheel support.
|
||||||
|
// 2018-01-08: Inputs: Added mapping for ImGuiKey_Insert.
|
||||||
|
// 2018-01-05: Inputs: Added WM_LBUTTONDBLCLK double-click handlers for window classes with the CS_DBLCLKS flag.
|
||||||
|
// 2017-10-23: Inputs: Added WM_SYSKEYDOWN / WM_SYSKEYUP handlers so e.g. the VK_MENU key can be read.
|
||||||
|
// 2017-10-23: Inputs: Using Win32 ::SetCapture/::GetCapture() to retrieve mouse positions outside the client area when dragging.
|
||||||
|
// 2016-11-12: Inputs: Only call Win32 ::SetCursor(NULL) when io.MouseDrawCursor is set.
|
||||||
|
|
||||||
|
// Win32 Data
|
||||||
|
static HWND g_hWnd = 0;
|
||||||
|
static INT64 g_Time = 0;
|
||||||
|
static INT64 g_TicksPerSecond = 0;
|
||||||
|
static ImGuiMouseCursor g_LastMouseCursor = ImGuiMouseCursor_COUNT;
|
||||||
|
|
||||||
|
// Functions
|
||||||
|
bool ImGui_ImplWin32_Init(void* hwnd)
|
||||||
|
{
|
||||||
|
if (!::QueryPerformanceFrequency((LARGE_INTEGER *)&g_TicksPerSecond))
|
||||||
|
return false;
|
||||||
|
if (!::QueryPerformanceCounter((LARGE_INTEGER *)&g_Time))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Setup back-end capabilities flags
|
||||||
|
g_hWnd = (HWND)hwnd;
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional)
|
||||||
|
io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used)
|
||||||
|
io.BackendPlatformName = "imgui_impl_win32";
|
||||||
|
io.ImeWindowHandle = hwnd;
|
||||||
|
|
||||||
|
// Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array that we will update during the application lifetime.
|
||||||
|
io.KeyMap[ImGuiKey_Tab] = VK_TAB;
|
||||||
|
io.KeyMap[ImGuiKey_LeftArrow] = VK_LEFT;
|
||||||
|
io.KeyMap[ImGuiKey_RightArrow] = VK_RIGHT;
|
||||||
|
io.KeyMap[ImGuiKey_UpArrow] = VK_UP;
|
||||||
|
io.KeyMap[ImGuiKey_DownArrow] = VK_DOWN;
|
||||||
|
io.KeyMap[ImGuiKey_PageUp] = VK_PRIOR;
|
||||||
|
io.KeyMap[ImGuiKey_PageDown] = VK_NEXT;
|
||||||
|
io.KeyMap[ImGuiKey_Home] = VK_HOME;
|
||||||
|
io.KeyMap[ImGuiKey_End] = VK_END;
|
||||||
|
io.KeyMap[ImGuiKey_Insert] = VK_INSERT;
|
||||||
|
io.KeyMap[ImGuiKey_Delete] = VK_DELETE;
|
||||||
|
io.KeyMap[ImGuiKey_Backspace] = VK_BACK;
|
||||||
|
io.KeyMap[ImGuiKey_Space] = VK_SPACE;
|
||||||
|
io.KeyMap[ImGuiKey_Enter] = VK_RETURN;
|
||||||
|
io.KeyMap[ImGuiKey_Escape] = VK_ESCAPE;
|
||||||
|
io.KeyMap[ImGuiKey_A] = 'A';
|
||||||
|
io.KeyMap[ImGuiKey_C] = 'C';
|
||||||
|
io.KeyMap[ImGuiKey_V] = 'V';
|
||||||
|
io.KeyMap[ImGuiKey_X] = 'X';
|
||||||
|
io.KeyMap[ImGuiKey_Y] = 'Y';
|
||||||
|
io.KeyMap[ImGuiKey_Z] = 'Z';
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGui_ImplWin32_Shutdown()
|
||||||
|
{
|
||||||
|
g_hWnd = (HWND)0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ImGui_ImplWin32_UpdateMouseCursor()
|
||||||
|
{
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor();
|
||||||
|
if (imgui_cursor == ImGuiMouseCursor_None || io.MouseDrawCursor)
|
||||||
|
{
|
||||||
|
// Hide OS mouse cursor if imgui is drawing it or if it wants no cursor
|
||||||
|
::SetCursor(NULL);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Show OS mouse cursor
|
||||||
|
LPTSTR win32_cursor = IDC_ARROW;
|
||||||
|
switch (imgui_cursor)
|
||||||
|
{
|
||||||
|
case ImGuiMouseCursor_Arrow: win32_cursor = IDC_ARROW; break;
|
||||||
|
case ImGuiMouseCursor_TextInput: win32_cursor = IDC_IBEAM; break;
|
||||||
|
case ImGuiMouseCursor_ResizeAll: win32_cursor = IDC_SIZEALL; break;
|
||||||
|
case ImGuiMouseCursor_ResizeEW: win32_cursor = IDC_SIZEWE; break;
|
||||||
|
case ImGuiMouseCursor_ResizeNS: win32_cursor = IDC_SIZENS; break;
|
||||||
|
case ImGuiMouseCursor_ResizeNESW: win32_cursor = IDC_SIZENESW; break;
|
||||||
|
case ImGuiMouseCursor_ResizeNWSE: win32_cursor = IDC_SIZENWSE; break;
|
||||||
|
case ImGuiMouseCursor_Hand: win32_cursor = IDC_HAND; break;
|
||||||
|
}
|
||||||
|
::SetCursor(::LoadCursor(NULL, win32_cursor));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGui_ImplWin32_UpdateMousePos()
|
||||||
|
{
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
|
||||||
|
// Set OS mouse position if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user)
|
||||||
|
if (io.WantSetMousePos)
|
||||||
|
{
|
||||||
|
POINT pos = { (int)io.MousePos.x, (int)io.MousePos.y };
|
||||||
|
::ClientToScreen(g_hWnd, &pos);
|
||||||
|
::SetCursorPos(pos.x, pos.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set mouse position
|
||||||
|
io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
|
||||||
|
POINT pos;
|
||||||
|
if (::GetActiveWindow() == g_hWnd && ::GetCursorPos(&pos))
|
||||||
|
if (::ScreenToClient(g_hWnd, &pos))
|
||||||
|
io.MousePos = ImVec2((float)pos.x, (float)pos.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGui_ImplWin32_NewFrame()
|
||||||
|
{
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
IM_ASSERT(io.Fonts->IsBuilt() && "Font atlas not built! It is generally built by the renderer back-end. Missing call to renderer _NewFrame() function? e.g. ImGui_ImplOpenGL3_NewFrame().");
|
||||||
|
|
||||||
|
// Setup display size (every frame to accommodate for window resizing)
|
||||||
|
RECT rect;
|
||||||
|
::GetClientRect(g_hWnd, &rect);
|
||||||
|
io.DisplaySize = ImVec2((float)(rect.right - rect.left), (float)(rect.bottom - rect.top));
|
||||||
|
|
||||||
|
// Setup time step
|
||||||
|
INT64 current_time;
|
||||||
|
::QueryPerformanceCounter((LARGE_INTEGER *)¤t_time);
|
||||||
|
io.DeltaTime = (float)(current_time - g_Time) / g_TicksPerSecond;
|
||||||
|
g_Time = current_time;
|
||||||
|
|
||||||
|
// Read keyboard modifiers inputs
|
||||||
|
io.KeyCtrl = (::GetKeyState(VK_CONTROL) & 0x8000) != 0;
|
||||||
|
io.KeyShift = (::GetKeyState(VK_SHIFT) & 0x8000) != 0;
|
||||||
|
io.KeyAlt = (::GetKeyState(VK_MENU) & 0x8000) != 0;
|
||||||
|
io.KeySuper = false;
|
||||||
|
// io.KeysDown[], io.MousePos, io.MouseDown[], io.MouseWheel: filled by the WndProc handler below.
|
||||||
|
|
||||||
|
// Update OS mouse position
|
||||||
|
ImGui_ImplWin32_UpdateMousePos();
|
||||||
|
|
||||||
|
// Update OS mouse cursor with the cursor requested by imgui
|
||||||
|
ImGuiMouseCursor mouse_cursor = io.MouseDrawCursor ? ImGuiMouseCursor_None : ImGui::GetMouseCursor();
|
||||||
|
if (g_LastMouseCursor != mouse_cursor)
|
||||||
|
{
|
||||||
|
g_LastMouseCursor = mouse_cursor;
|
||||||
|
ImGui_ImplWin32_UpdateMouseCursor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow compilation with old Windows SDK. MinGW doesn't have default _WIN32_WINNT/WINVER versions.
|
||||||
|
#ifndef WM_MOUSEHWHEEL
|
||||||
|
#define WM_MOUSEHWHEEL 0x020E
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Process Win32 mouse/keyboard inputs.
|
||||||
|
// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
|
||||||
|
// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application.
|
||||||
|
// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application.
|
||||||
|
// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
|
||||||
|
// PS: In this Win32 handler, we use the capture API (GetCapture/SetCapture/ReleaseCapture) to be able to read mouse coordinations when dragging mouse outside of our window bounds.
|
||||||
|
// PS: We treat DBLCLK messages as regular mouse down messages, so this code will work on windows classes that have the CS_DBLCLKS flag set. Our own example app code doesn't set this flag.
|
||||||
|
IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||||||
|
{
|
||||||
|
if (ImGui::GetCurrentContext() == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
switch (msg)
|
||||||
|
{
|
||||||
|
case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK:
|
||||||
|
case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK:
|
||||||
|
case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK:
|
||||||
|
{
|
||||||
|
int button = 0;
|
||||||
|
if (msg == WM_LBUTTONDOWN || msg == WM_LBUTTONDBLCLK) button = 0;
|
||||||
|
if (msg == WM_RBUTTONDOWN || msg == WM_RBUTTONDBLCLK) button = 1;
|
||||||
|
if (msg == WM_MBUTTONDOWN || msg == WM_MBUTTONDBLCLK) button = 2;
|
||||||
|
if (!ImGui::IsAnyMouseDown() && ::GetCapture() == NULL)
|
||||||
|
::SetCapture(hwnd);
|
||||||
|
io.MouseDown[button] = true;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
case WM_LBUTTONUP:
|
||||||
|
case WM_RBUTTONUP:
|
||||||
|
case WM_MBUTTONUP:
|
||||||
|
{
|
||||||
|
int button = 0;
|
||||||
|
if (msg == WM_LBUTTONUP) button = 0;
|
||||||
|
if (msg == WM_RBUTTONUP) button = 1;
|
||||||
|
if (msg == WM_MBUTTONUP) button = 2;
|
||||||
|
io.MouseDown[button] = false;
|
||||||
|
if (!ImGui::IsAnyMouseDown() && ::GetCapture() == hwnd)
|
||||||
|
::ReleaseCapture();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
case WM_MOUSEWHEEL:
|
||||||
|
io.MouseWheel += (float)GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA;
|
||||||
|
return 0;
|
||||||
|
case WM_MOUSEHWHEEL:
|
||||||
|
io.MouseWheelH += (float)GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA;
|
||||||
|
return 0;
|
||||||
|
case WM_KEYDOWN:
|
||||||
|
case WM_SYSKEYDOWN:
|
||||||
|
if (wParam < 256)
|
||||||
|
io.KeysDown[wParam] = 1;
|
||||||
|
return 0;
|
||||||
|
case WM_KEYUP:
|
||||||
|
case WM_SYSKEYUP:
|
||||||
|
if (wParam < 256)
|
||||||
|
io.KeysDown[wParam] = 0;
|
||||||
|
return 0;
|
||||||
|
case WM_CHAR:
|
||||||
|
// You can also use ToAscii()+GetKeyboardState() to retrieve characters.
|
||||||
|
if (wParam > 0 && wParam < 0x10000)
|
||||||
|
io.AddInputCharacter((unsigned short)wParam);
|
||||||
|
return 0;
|
||||||
|
case WM_SETCURSOR:
|
||||||
|
if (LOWORD(lParam) == HTCLIENT && ImGui_ImplWin32_UpdateMouseCursor())
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
22
src/imgui/imgui_impl_win32.h
Normal file
22
src/imgui/imgui_impl_win32.h
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// dear imgui: Platform Binding for Windows (standard windows API for 32 and 64 bits applications)
|
||||||
|
// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..)
|
||||||
|
|
||||||
|
// Implemented features:
|
||||||
|
// [X] Platform: Clipboard support (for Win32 this is actually part of core imgui)
|
||||||
|
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
|
||||||
|
// [X] Platform: Keyboard arrays indexed using VK_* Virtual Key Codes, e.g. ImGui::IsKeyPressed(VK_SPACE).
|
||||||
|
// Missing features:
|
||||||
|
// [ ] Platform: Gamepad support (best leaving it to user application to fill io.NavInputs[] with gamepad inputs from their source of choice).
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
IMGUI_IMPL_API bool ImGui_ImplWin32_Init(void* hwnd);
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplWin32_Shutdown();
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplWin32_NewFrame();
|
||||||
|
|
||||||
|
// Handler for Win32 messages, update mouse/keyboard data.
|
||||||
|
// You may or not need this for your implementation, but it can serve as reference for handling inputs.
|
||||||
|
// Intentionally commented out to avoid dragging dependencies on <windows.h> types. You can COPY this line into your .cpp code instead.
|
||||||
|
/*
|
||||||
|
IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
||||||
|
*/
|
1484
src/imgui/imgui_internal.h
Normal file
1484
src/imgui/imgui_internal.h
Normal file
File diff suppressed because it is too large
Load Diff
6564
src/imgui/imgui_widgets.cpp
Normal file
6564
src/imgui/imgui_widgets.cpp
Normal file
File diff suppressed because it is too large
Load Diff
623
src/imgui/imstb_rectpack.h
Normal file
623
src/imgui/imstb_rectpack.h
Normal file
@ -0,0 +1,623 @@
|
|||||||
|
// stb_rect_pack.h - v0.11 - public domain - rectangle packing
|
||||||
|
// Sean Barrett 2014
|
||||||
|
//
|
||||||
|
// Useful for e.g. packing rectangular textures into an atlas.
|
||||||
|
// Does not do rotation.
|
||||||
|
//
|
||||||
|
// Not necessarily the awesomest packing method, but better than
|
||||||
|
// the totally naive one in stb_truetype (which is primarily what
|
||||||
|
// this is meant to replace).
|
||||||
|
//
|
||||||
|
// Has only had a few tests run, may have issues.
|
||||||
|
//
|
||||||
|
// More docs to come.
|
||||||
|
//
|
||||||
|
// No memory allocations; uses qsort() and assert() from stdlib.
|
||||||
|
// Can override those by defining STBRP_SORT and STBRP_ASSERT.
|
||||||
|
//
|
||||||
|
// This library currently uses the Skyline Bottom-Left algorithm.
|
||||||
|
//
|
||||||
|
// Please note: better rectangle packers are welcome! Please
|
||||||
|
// implement them to the same API, but with a different init
|
||||||
|
// function.
|
||||||
|
//
|
||||||
|
// Credits
|
||||||
|
//
|
||||||
|
// Library
|
||||||
|
// Sean Barrett
|
||||||
|
// Minor features
|
||||||
|
// Martins Mozeiko
|
||||||
|
// github:IntellectualKitty
|
||||||
|
//
|
||||||
|
// Bugfixes / warning fixes
|
||||||
|
// Jeremy Jaussaud
|
||||||
|
//
|
||||||
|
// Version history:
|
||||||
|
//
|
||||||
|
// 0.11 (2017-03-03) return packing success/fail result
|
||||||
|
// 0.10 (2016-10-25) remove cast-away-const to avoid warnings
|
||||||
|
// 0.09 (2016-08-27) fix compiler warnings
|
||||||
|
// 0.08 (2015-09-13) really fix bug with empty rects (w=0 or h=0)
|
||||||
|
// 0.07 (2015-09-13) fix bug with empty rects (w=0 or h=0)
|
||||||
|
// 0.06 (2015-04-15) added STBRP_SORT to allow replacing qsort
|
||||||
|
// 0.05: added STBRP_ASSERT to allow replacing assert
|
||||||
|
// 0.04: fixed minor bug in STBRP_LARGE_RECTS support
|
||||||
|
// 0.01: initial release
|
||||||
|
//
|
||||||
|
// LICENSE
|
||||||
|
//
|
||||||
|
// See end of file for license information.
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// INCLUDE SECTION
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef STB_INCLUDE_STB_RECT_PACK_H
|
||||||
|
#define STB_INCLUDE_STB_RECT_PACK_H
|
||||||
|
|
||||||
|
#define STB_RECT_PACK_VERSION 1
|
||||||
|
|
||||||
|
#ifdef STBRP_STATIC
|
||||||
|
#define STBRP_DEF static
|
||||||
|
#else
|
||||||
|
#define STBRP_DEF extern
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct stbrp_context stbrp_context;
|
||||||
|
typedef struct stbrp_node stbrp_node;
|
||||||
|
typedef struct stbrp_rect stbrp_rect;
|
||||||
|
|
||||||
|
#ifdef STBRP_LARGE_RECTS
|
||||||
|
typedef int stbrp_coord;
|
||||||
|
#else
|
||||||
|
typedef unsigned short stbrp_coord;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
STBRP_DEF int stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects);
|
||||||
|
// Assign packed locations to rectangles. The rectangles are of type
|
||||||
|
// 'stbrp_rect' defined below, stored in the array 'rects', and there
|
||||||
|
// are 'num_rects' many of them.
|
||||||
|
//
|
||||||
|
// Rectangles which are successfully packed have the 'was_packed' flag
|
||||||
|
// set to a non-zero value and 'x' and 'y' store the minimum location
|
||||||
|
// on each axis (i.e. bottom-left in cartesian coordinates, top-left
|
||||||
|
// if you imagine y increasing downwards). Rectangles which do not fit
|
||||||
|
// have the 'was_packed' flag set to 0.
|
||||||
|
//
|
||||||
|
// You should not try to access the 'rects' array from another thread
|
||||||
|
// while this function is running, as the function temporarily reorders
|
||||||
|
// the array while it executes.
|
||||||
|
//
|
||||||
|
// To pack into another rectangle, you need to call stbrp_init_target
|
||||||
|
// again. To continue packing into the same rectangle, you can call
|
||||||
|
// this function again. Calling this multiple times with multiple rect
|
||||||
|
// arrays will probably produce worse packing results than calling it
|
||||||
|
// a single time with the full rectangle array, but the option is
|
||||||
|
// available.
|
||||||
|
//
|
||||||
|
// The function returns 1 if all of the rectangles were successfully
|
||||||
|
// packed and 0 otherwise.
|
||||||
|
|
||||||
|
struct stbrp_rect
|
||||||
|
{
|
||||||
|
// reserved for your use:
|
||||||
|
int id;
|
||||||
|
|
||||||
|
// input:
|
||||||
|
stbrp_coord w, h;
|
||||||
|
|
||||||
|
// output:
|
||||||
|
stbrp_coord x, y;
|
||||||
|
int was_packed; // non-zero if valid packing
|
||||||
|
|
||||||
|
}; // 16 bytes, nominally
|
||||||
|
|
||||||
|
|
||||||
|
STBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes);
|
||||||
|
// Initialize a rectangle packer to:
|
||||||
|
// pack a rectangle that is 'width' by 'height' in dimensions
|
||||||
|
// using temporary storage provided by the array 'nodes', which is 'num_nodes' long
|
||||||
|
//
|
||||||
|
// You must call this function every time you start packing into a new target.
|
||||||
|
//
|
||||||
|
// There is no "shutdown" function. The 'nodes' memory must stay valid for
|
||||||
|
// the following stbrp_pack_rects() call (or calls), but can be freed after
|
||||||
|
// the call (or calls) finish.
|
||||||
|
//
|
||||||
|
// Note: to guarantee best results, either:
|
||||||
|
// 1. make sure 'num_nodes' >= 'width'
|
||||||
|
// or 2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1'
|
||||||
|
//
|
||||||
|
// If you don't do either of the above things, widths will be quantized to multiples
|
||||||
|
// of small integers to guarantee the algorithm doesn't run out of temporary storage.
|
||||||
|
//
|
||||||
|
// If you do #2, then the non-quantized algorithm will be used, but the algorithm
|
||||||
|
// may run out of temporary storage and be unable to pack some rectangles.
|
||||||
|
|
||||||
|
STBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem);
|
||||||
|
// Optionally call this function after init but before doing any packing to
|
||||||
|
// change the handling of the out-of-temp-memory scenario, described above.
|
||||||
|
// If you call init again, this will be reset to the default (false).
|
||||||
|
|
||||||
|
|
||||||
|
STBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic);
|
||||||
|
// Optionally select which packing heuristic the library should use. Different
|
||||||
|
// heuristics will produce better/worse results for different data sets.
|
||||||
|
// If you call init again, this will be reset to the default.
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
STBRP_HEURISTIC_Skyline_default=0,
|
||||||
|
STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default,
|
||||||
|
STBRP_HEURISTIC_Skyline_BF_sortHeight
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// the details of the following structures don't matter to you, but they must
|
||||||
|
// be visible so you can handle the memory allocations for them
|
||||||
|
|
||||||
|
struct stbrp_node
|
||||||
|
{
|
||||||
|
stbrp_coord x,y;
|
||||||
|
stbrp_node *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct stbrp_context
|
||||||
|
{
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
int align;
|
||||||
|
int init_mode;
|
||||||
|
int heuristic;
|
||||||
|
int num_nodes;
|
||||||
|
stbrp_node *active_head;
|
||||||
|
stbrp_node *free_head;
|
||||||
|
stbrp_node extra[2]; // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2'
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// IMPLEMENTATION SECTION
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifdef STB_RECT_PACK_IMPLEMENTATION
|
||||||
|
#ifndef STBRP_SORT
|
||||||
|
#include <stdlib.h>
|
||||||
|
#define STBRP_SORT qsort
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef STBRP_ASSERT
|
||||||
|
#include <assert.h>
|
||||||
|
#define STBRP_ASSERT assert
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#define STBRP__NOTUSED(v) (void)(v)
|
||||||
|
#define STBRP__CDECL __cdecl
|
||||||
|
#else
|
||||||
|
#define STBRP__NOTUSED(v) (void)sizeof(v)
|
||||||
|
#define STBRP__CDECL
|
||||||
|
#endif
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
STBRP__INIT_skyline = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic)
|
||||||
|
{
|
||||||
|
switch (context->init_mode) {
|
||||||
|
case STBRP__INIT_skyline:
|
||||||
|
STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight);
|
||||||
|
context->heuristic = heuristic;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
STBRP_ASSERT(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem)
|
||||||
|
{
|
||||||
|
if (allow_out_of_mem)
|
||||||
|
// if it's ok to run out of memory, then don't bother aligning them;
|
||||||
|
// this gives better packing, but may fail due to OOM (even though
|
||||||
|
// the rectangles easily fit). @TODO a smarter approach would be to only
|
||||||
|
// quantize once we've hit OOM, then we could get rid of this parameter.
|
||||||
|
context->align = 1;
|
||||||
|
else {
|
||||||
|
// if it's not ok to run out of memory, then quantize the widths
|
||||||
|
// so that num_nodes is always enough nodes.
|
||||||
|
//
|
||||||
|
// I.e. num_nodes * align >= width
|
||||||
|
// align >= width / num_nodes
|
||||||
|
// align = ceil(width/num_nodes)
|
||||||
|
|
||||||
|
context->align = (context->width + context->num_nodes-1) / context->num_nodes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
#ifndef STBRP_LARGE_RECTS
|
||||||
|
STBRP_ASSERT(width <= 0xffff && height <= 0xffff);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (i=0; i < num_nodes-1; ++i)
|
||||||
|
nodes[i].next = &nodes[i+1];
|
||||||
|
nodes[i].next = NULL;
|
||||||
|
context->init_mode = STBRP__INIT_skyline;
|
||||||
|
context->heuristic = STBRP_HEURISTIC_Skyline_default;
|
||||||
|
context->free_head = &nodes[0];
|
||||||
|
context->active_head = &context->extra[0];
|
||||||
|
context->width = width;
|
||||||
|
context->height = height;
|
||||||
|
context->num_nodes = num_nodes;
|
||||||
|
stbrp_setup_allow_out_of_mem(context, 0);
|
||||||
|
|
||||||
|
// node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly)
|
||||||
|
context->extra[0].x = 0;
|
||||||
|
context->extra[0].y = 0;
|
||||||
|
context->extra[0].next = &context->extra[1];
|
||||||
|
context->extra[1].x = (stbrp_coord) width;
|
||||||
|
#ifdef STBRP_LARGE_RECTS
|
||||||
|
context->extra[1].y = (1<<30);
|
||||||
|
#else
|
||||||
|
context->extra[1].y = 65535;
|
||||||
|
#endif
|
||||||
|
context->extra[1].next = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// find minimum y position if it starts at x1
|
||||||
|
static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste)
|
||||||
|
{
|
||||||
|
stbrp_node *node = first;
|
||||||
|
int x1 = x0 + width;
|
||||||
|
int min_y, visited_width, waste_area;
|
||||||
|
|
||||||
|
STBRP__NOTUSED(c);
|
||||||
|
|
||||||
|
STBRP_ASSERT(first->x <= x0);
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
// skip in case we're past the node
|
||||||
|
while (node->next->x <= x0)
|
||||||
|
++node;
|
||||||
|
#else
|
||||||
|
STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency
|
||||||
|
#endif
|
||||||
|
|
||||||
|
STBRP_ASSERT(node->x <= x0);
|
||||||
|
|
||||||
|
min_y = 0;
|
||||||
|
waste_area = 0;
|
||||||
|
visited_width = 0;
|
||||||
|
while (node->x < x1) {
|
||||||
|
if (node->y > min_y) {
|
||||||
|
// raise min_y higher.
|
||||||
|
// we've accounted for all waste up to min_y,
|
||||||
|
// but we'll now add more waste for everything we've visted
|
||||||
|
waste_area += visited_width * (node->y - min_y);
|
||||||
|
min_y = node->y;
|
||||||
|
// the first time through, visited_width might be reduced
|
||||||
|
if (node->x < x0)
|
||||||
|
visited_width += node->next->x - x0;
|
||||||
|
else
|
||||||
|
visited_width += node->next->x - node->x;
|
||||||
|
} else {
|
||||||
|
// add waste area
|
||||||
|
int under_width = node->next->x - node->x;
|
||||||
|
if (under_width + visited_width > width)
|
||||||
|
under_width = width - visited_width;
|
||||||
|
waste_area += under_width * (min_y - node->y);
|
||||||
|
visited_width += under_width;
|
||||||
|
}
|
||||||
|
node = node->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
*pwaste = waste_area;
|
||||||
|
return min_y;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int x,y;
|
||||||
|
stbrp_node **prev_link;
|
||||||
|
} stbrp__findresult;
|
||||||
|
|
||||||
|
static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height)
|
||||||
|
{
|
||||||
|
int best_waste = (1<<30), best_x, best_y = (1 << 30);
|
||||||
|
stbrp__findresult fr;
|
||||||
|
stbrp_node **prev, *node, *tail, **best = NULL;
|
||||||
|
|
||||||
|
// align to multiple of c->align
|
||||||
|
width = (width + c->align - 1);
|
||||||
|
width -= width % c->align;
|
||||||
|
STBRP_ASSERT(width % c->align == 0);
|
||||||
|
|
||||||
|
node = c->active_head;
|
||||||
|
prev = &c->active_head;
|
||||||
|
while (node->x + width <= c->width) {
|
||||||
|
int y,waste;
|
||||||
|
y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste);
|
||||||
|
if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { // actually just want to test BL
|
||||||
|
// bottom left
|
||||||
|
if (y < best_y) {
|
||||||
|
best_y = y;
|
||||||
|
best = prev;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// best-fit
|
||||||
|
if (y + height <= c->height) {
|
||||||
|
// can only use it if it first vertically
|
||||||
|
if (y < best_y || (y == best_y && waste < best_waste)) {
|
||||||
|
best_y = y;
|
||||||
|
best_waste = waste;
|
||||||
|
best = prev;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prev = &node->next;
|
||||||
|
node = node->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
best_x = (best == NULL) ? 0 : (*best)->x;
|
||||||
|
|
||||||
|
// if doing best-fit (BF), we also have to try aligning right edge to each node position
|
||||||
|
//
|
||||||
|
// e.g, if fitting
|
||||||
|
//
|
||||||
|
// ____________________
|
||||||
|
// |____________________|
|
||||||
|
//
|
||||||
|
// into
|
||||||
|
//
|
||||||
|
// | |
|
||||||
|
// | ____________|
|
||||||
|
// |____________|
|
||||||
|
//
|
||||||
|
// then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned
|
||||||
|
//
|
||||||
|
// This makes BF take about 2x the time
|
||||||
|
|
||||||
|
if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) {
|
||||||
|
tail = c->active_head;
|
||||||
|
node = c->active_head;
|
||||||
|
prev = &c->active_head;
|
||||||
|
// find first node that's admissible
|
||||||
|
while (tail->x < width)
|
||||||
|
tail = tail->next;
|
||||||
|
while (tail) {
|
||||||
|
int xpos = tail->x - width;
|
||||||
|
int y,waste;
|
||||||
|
STBRP_ASSERT(xpos >= 0);
|
||||||
|
// find the left position that matches this
|
||||||
|
while (node->next->x <= xpos) {
|
||||||
|
prev = &node->next;
|
||||||
|
node = node->next;
|
||||||
|
}
|
||||||
|
STBRP_ASSERT(node->next->x > xpos && node->x <= xpos);
|
||||||
|
y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste);
|
||||||
|
if (y + height < c->height) {
|
||||||
|
if (y <= best_y) {
|
||||||
|
if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) {
|
||||||
|
best_x = xpos;
|
||||||
|
STBRP_ASSERT(y <= best_y);
|
||||||
|
best_y = y;
|
||||||
|
best_waste = waste;
|
||||||
|
best = prev;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tail = tail->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fr.prev_link = best;
|
||||||
|
fr.x = best_x;
|
||||||
|
fr.y = best_y;
|
||||||
|
return fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height)
|
||||||
|
{
|
||||||
|
// find best position according to heuristic
|
||||||
|
stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height);
|
||||||
|
stbrp_node *node, *cur;
|
||||||
|
|
||||||
|
// bail if:
|
||||||
|
// 1. it failed
|
||||||
|
// 2. the best node doesn't fit (we don't always check this)
|
||||||
|
// 3. we're out of memory
|
||||||
|
if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) {
|
||||||
|
res.prev_link = NULL;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
// on success, create new node
|
||||||
|
node = context->free_head;
|
||||||
|
node->x = (stbrp_coord) res.x;
|
||||||
|
node->y = (stbrp_coord) (res.y + height);
|
||||||
|
|
||||||
|
context->free_head = node->next;
|
||||||
|
|
||||||
|
// insert the new node into the right starting point, and
|
||||||
|
// let 'cur' point to the remaining nodes needing to be
|
||||||
|
// stiched back in
|
||||||
|
|
||||||
|
cur = *res.prev_link;
|
||||||
|
if (cur->x < res.x) {
|
||||||
|
// preserve the existing one, so start testing with the next one
|
||||||
|
stbrp_node *next = cur->next;
|
||||||
|
cur->next = node;
|
||||||
|
cur = next;
|
||||||
|
} else {
|
||||||
|
*res.prev_link = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
// from here, traverse cur and free the nodes, until we get to one
|
||||||
|
// that shouldn't be freed
|
||||||
|
while (cur->next && cur->next->x <= res.x + width) {
|
||||||
|
stbrp_node *next = cur->next;
|
||||||
|
// move the current node to the free list
|
||||||
|
cur->next = context->free_head;
|
||||||
|
context->free_head = cur;
|
||||||
|
cur = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
// stitch the list back in
|
||||||
|
node->next = cur;
|
||||||
|
|
||||||
|
if (cur->x < res.x + width)
|
||||||
|
cur->x = (stbrp_coord) (res.x + width);
|
||||||
|
|
||||||
|
#ifdef _DEBUG
|
||||||
|
cur = context->active_head;
|
||||||
|
while (cur->x < context->width) {
|
||||||
|
STBRP_ASSERT(cur->x < cur->next->x);
|
||||||
|
cur = cur->next;
|
||||||
|
}
|
||||||
|
STBRP_ASSERT(cur->next == NULL);
|
||||||
|
|
||||||
|
{
|
||||||
|
int count=0;
|
||||||
|
cur = context->active_head;
|
||||||
|
while (cur) {
|
||||||
|
cur = cur->next;
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
cur = context->free_head;
|
||||||
|
while (cur) {
|
||||||
|
cur = cur->next;
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
STBRP_ASSERT(count == context->num_nodes+2);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int STBRP__CDECL rect_height_compare(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
const stbrp_rect *p = (const stbrp_rect *) a;
|
||||||
|
const stbrp_rect *q = (const stbrp_rect *) b;
|
||||||
|
if (p->h > q->h)
|
||||||
|
return -1;
|
||||||
|
if (p->h < q->h)
|
||||||
|
return 1;
|
||||||
|
return (p->w > q->w) ? -1 : (p->w < q->w);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int STBRP__CDECL rect_original_order(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
const stbrp_rect *p = (const stbrp_rect *) a;
|
||||||
|
const stbrp_rect *q = (const stbrp_rect *) b;
|
||||||
|
return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef STBRP_LARGE_RECTS
|
||||||
|
#define STBRP__MAXVAL 0xffffffff
|
||||||
|
#else
|
||||||
|
#define STBRP__MAXVAL 0xffff
|
||||||
|
#endif
|
||||||
|
|
||||||
|
STBRP_DEF int stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects)
|
||||||
|
{
|
||||||
|
int i, all_rects_packed = 1;
|
||||||
|
|
||||||
|
// we use the 'was_packed' field internally to allow sorting/unsorting
|
||||||
|
for (i=0; i < num_rects; ++i) {
|
||||||
|
rects[i].was_packed = i;
|
||||||
|
#ifndef STBRP_LARGE_RECTS
|
||||||
|
STBRP_ASSERT(rects[i].w <= 0xffff && rects[i].h <= 0xffff);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// sort according to heuristic
|
||||||
|
STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare);
|
||||||
|
|
||||||
|
for (i=0; i < num_rects; ++i) {
|
||||||
|
if (rects[i].w == 0 || rects[i].h == 0) {
|
||||||
|
rects[i].x = rects[i].y = 0; // empty rect needs no space
|
||||||
|
} else {
|
||||||
|
stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h);
|
||||||
|
if (fr.prev_link) {
|
||||||
|
rects[i].x = (stbrp_coord) fr.x;
|
||||||
|
rects[i].y = (stbrp_coord) fr.y;
|
||||||
|
} else {
|
||||||
|
rects[i].x = rects[i].y = STBRP__MAXVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// unsort
|
||||||
|
STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order);
|
||||||
|
|
||||||
|
// set was_packed flags and all_rects_packed status
|
||||||
|
for (i=0; i < num_rects; ++i) {
|
||||||
|
rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL);
|
||||||
|
if (!rects[i].was_packed)
|
||||||
|
all_rects_packed = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// return the all_rects_packed status
|
||||||
|
return all_rects_packed;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
This software is available under 2 licenses -- choose whichever you prefer.
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
ALTERNATIVE A - MIT License
|
||||||
|
Copyright (c) 2017 Sean Barrett
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
|
so, subject to the following conditions:
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
ALTERNATIVE B - Public Domain (www.unlicense.org)
|
||||||
|
This is free and unencumbered software released into the public domain.
|
||||||
|
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
||||||
|
software, either in source code form or as a compiled binary, for any purpose,
|
||||||
|
commercial or non-commercial, and by any means.
|
||||||
|
In jurisdictions that recognize copyright laws, the author or authors of this
|
||||||
|
software dedicate any and all copyright interest in the software to the public
|
||||||
|
domain. We make this dedication for the benefit of the public at large and to
|
||||||
|
the detriment of our heirs and successors. We intend this dedication to be an
|
||||||
|
overt act of relinquishment in perpetuity of all present and future rights to
|
||||||
|
this software under copyright law.
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
*/
|
1409
src/imgui/imstb_textedit.h
Normal file
1409
src/imgui/imstb_textedit.h
Normal file
File diff suppressed because it is too large
Load Diff
4854
src/imgui/imstb_truetype.h
Normal file
4854
src/imgui/imstb_truetype.h
Normal file
File diff suppressed because it is too large
Load Diff
54
src/invoker.cpp
Normal file
54
src/invoker.cpp
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
#include "common.hpp"
|
||||||
|
#include "crossmap.hpp"
|
||||||
|
#include "invoker.hpp"
|
||||||
|
#include "logger.hpp"
|
||||||
|
#include "pointers.hpp"
|
||||||
|
|
||||||
|
namespace big
|
||||||
|
{
|
||||||
|
native_call_context::native_call_context()
|
||||||
|
{
|
||||||
|
m_return_value = &m_return_stack[0];
|
||||||
|
m_args = &m_arg_stack[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
bool native_invoker::map_native(rage::scrNativeHash *hash)
|
||||||
|
{
|
||||||
|
for (auto const &mapping : g_crossmap)
|
||||||
|
{
|
||||||
|
if (mapping.first == *hash)
|
||||||
|
{
|
||||||
|
*hash = mapping.second;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void native_invoker::begin_call()
|
||||||
|
{
|
||||||
|
m_call_context.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void native_invoker::end_call(rage::scrNativeHash hash)
|
||||||
|
{
|
||||||
|
map_native(&hash);
|
||||||
|
if (auto handler = g_pointers->m_get_native_handler(g_pointers->m_native_registration_table, hash))
|
||||||
|
{
|
||||||
|
__try
|
||||||
|
{
|
||||||
|
handler(&m_call_context);
|
||||||
|
g_pointers->m_fix_vectors(&m_call_context);
|
||||||
|
}
|
||||||
|
__except (EXCEPTION_EXECUTE_HANDLER)
|
||||||
|
{
|
||||||
|
LOG_ERROR("Exception caught while trying to call 0x{:X} native.", hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG_ERROR("Failed to find 0x{:X} native's handler.", hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
43
src/invoker.hpp
Normal file
43
src/invoker.hpp
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "common.hpp"
|
||||||
|
#include "gta/natives.hpp"
|
||||||
|
|
||||||
|
namespace big
|
||||||
|
{
|
||||||
|
class native_call_context : public rage::scrNativeCallContext
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
native_call_context();
|
||||||
|
private:
|
||||||
|
std::uint64_t m_return_stack[10];
|
||||||
|
std::uint64_t m_arg_stack[100];
|
||||||
|
};
|
||||||
|
|
||||||
|
class native_invoker
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit native_invoker() = default;
|
||||||
|
~native_invoker() = default;
|
||||||
|
|
||||||
|
bool map_native(rage::scrNativeHash *hash);
|
||||||
|
|
||||||
|
void begin_call();
|
||||||
|
void end_call(rage::scrNativeHash hash);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void push_arg(T &&value)
|
||||||
|
{
|
||||||
|
m_call_context.push_arg(std::forward<T>(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T &get_return_value()
|
||||||
|
{
|
||||||
|
return *m_call_context.get_return_value<T>();
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
native_call_context m_call_context;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline native_invoker g_native_invoker;
|
||||||
|
}
|
188
src/logger.hpp
Normal file
188
src/logger.hpp
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "common.hpp"
|
||||||
|
|
||||||
|
namespace big
|
||||||
|
{
|
||||||
|
enum class log_color : std::uint16_t
|
||||||
|
{
|
||||||
|
red = FOREGROUND_RED,
|
||||||
|
green = FOREGROUND_GREEN,
|
||||||
|
blue = FOREGROUND_BLUE,
|
||||||
|
intensify = FOREGROUND_INTENSITY
|
||||||
|
};
|
||||||
|
|
||||||
|
inline log_color operator|(log_color a, log_color b)
|
||||||
|
{
|
||||||
|
return static_cast<log_color>(static_cast<std::underlying_type_t<log_color>>(a) | static_cast<std::underlying_type_t<log_color>>(b));
|
||||||
|
}
|
||||||
|
|
||||||
|
class logger;
|
||||||
|
inline logger *g_logger{};
|
||||||
|
|
||||||
|
class logger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit logger() :
|
||||||
|
m_file_path(std::getenv("appdata"))
|
||||||
|
{
|
||||||
|
m_file_path /= "BigBaseV2";
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!std::filesystem::exists(m_file_path))
|
||||||
|
{
|
||||||
|
std::filesystem::create_directory(m_file_path);
|
||||||
|
}
|
||||||
|
else if (!std::filesystem::is_directory(m_file_path))
|
||||||
|
{
|
||||||
|
std::filesystem::remove(m_file_path);
|
||||||
|
std::filesystem::create_directory(m_file_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_file_path /= "BigBaseV2.log";
|
||||||
|
m_file_out.open(m_file_path, std::ios_base::out | std::ios_base::app);
|
||||||
|
}
|
||||||
|
catch (std::filesystem::filesystem_error const&)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((m_did_console_exist = AttachConsole(GetCurrentProcessId())) == false)
|
||||||
|
AllocConsole();
|
||||||
|
|
||||||
|
if ((m_console_handle = GetStdHandle(STD_OUTPUT_HANDLE)) != nullptr)
|
||||||
|
{
|
||||||
|
SetConsoleTitleA("BigBaseV2");
|
||||||
|
SetConsoleOutputCP(CP_UTF8);
|
||||||
|
|
||||||
|
m_console_out.open("CONOUT$", std::ios_base::out | std::ios_base::app);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_logger = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
~logger()
|
||||||
|
{
|
||||||
|
if (!m_did_console_exist)
|
||||||
|
FreeConsole();
|
||||||
|
|
||||||
|
g_logger = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ...Args>
|
||||||
|
void raw(log_color color, Args const &...args)
|
||||||
|
{
|
||||||
|
raw_to_console(color, args...);
|
||||||
|
raw_to_file(args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ...Args>
|
||||||
|
void log(log_color color, std::string_view prefix, std::string_view format, Args const &...args)
|
||||||
|
{
|
||||||
|
auto message = fmt::format(format, args...);
|
||||||
|
|
||||||
|
auto time_since_epoch = std::time(nullptr);
|
||||||
|
auto local_time = std::localtime(&time_since_epoch);
|
||||||
|
|
||||||
|
auto console_timestamp = fmt::format("[{:0>2}:{:0>2}:{:0>2}]", local_time->tm_hour, local_time->tm_min, local_time->tm_sec);
|
||||||
|
auto file_timestamp = fmt::format("[{}-{}-{} {:0>2}:{:0>2}:{:0>2}]", local_time->tm_year + 1900, local_time->tm_mon + 1, local_time->tm_mday, local_time->tm_hour, local_time->tm_min, local_time->tm_sec);
|
||||||
|
|
||||||
|
raw_to_console(color, console_timestamp, " [", prefix, "] ", message, "\n");
|
||||||
|
raw_to_file(file_timestamp, " [", prefix, "] ", message, "\n");
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
template <typename ...Args>
|
||||||
|
void raw_to_console(log_color color, Args const &...args)
|
||||||
|
{
|
||||||
|
if (m_console_handle)
|
||||||
|
{
|
||||||
|
SetConsoleTextAttribute(m_console_handle, static_cast<std::uint16_t>(color));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_console_out)
|
||||||
|
{
|
||||||
|
((m_console_out << args), ...);
|
||||||
|
m_console_out << std::flush;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ...Args>
|
||||||
|
void raw_to_file(Args const &...args)
|
||||||
|
{
|
||||||
|
if (m_file_out)
|
||||||
|
{
|
||||||
|
((m_file_out << args), ...);
|
||||||
|
m_file_out << std::flush;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
bool m_did_console_exist{};
|
||||||
|
HANDLE m_console_handle{};
|
||||||
|
std::ofstream m_console_out;
|
||||||
|
|
||||||
|
std::filesystem::path m_file_path;
|
||||||
|
std::ofstream m_file_out;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename ...Args>
|
||||||
|
inline void log_info(std::string_view format, Args const &...args)
|
||||||
|
{
|
||||||
|
if (g_logger)
|
||||||
|
{
|
||||||
|
g_logger->log(log_color::blue | log_color::intensify, "Info", format, args...);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DebugBreak();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ...Args>
|
||||||
|
inline void log_error(std::string_view format, Args const &...args)
|
||||||
|
{
|
||||||
|
if (g_logger)
|
||||||
|
{
|
||||||
|
g_logger->log(log_color::green | log_color::intensify, "Error", format, args...);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DebugBreak();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ...Args>
|
||||||
|
inline void log_trace(std::string_view format, Args const &...args)
|
||||||
|
{
|
||||||
|
if (g_logger)
|
||||||
|
{
|
||||||
|
g_logger->log(log_color::green | log_color::intensify, "Trace", format, args...);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DebugBreak();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ...Args>
|
||||||
|
inline void log_raw(log_color color, Args const &...args)
|
||||||
|
{
|
||||||
|
if (g_logger)
|
||||||
|
{
|
||||||
|
g_logger->raw(color, args...);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DebugBreak();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define LOG_INFO_IMPL(format, ...) (::big::log_info(format, __VA_ARGS__))
|
||||||
|
#define LOG_INFO(format, ...) LOG_INFO_IMPL(format, __VA_ARGS__)
|
||||||
|
|
||||||
|
#define LOG_ERROR_IMPL(format, ...) (::big::log_error(format, __VA_ARGS__))
|
||||||
|
#define LOG_ERROR(format, ...) LOG_ERROR_IMPL(format, __VA_ARGS__)
|
||||||
|
|
||||||
|
#define LOG_TRACE_IMPL(format, ...) (::big::log_trace(format, __VA_ARGS__))
|
||||||
|
#define LOG_TRACE(format, ...) LOG_TRACE_IMPL(format, __VA_ARGS__)
|
||||||
|
|
||||||
|
#define LOG_RAW_IMPL(color, ...) (::big::log_raw(color, __VA_ARGS__))
|
||||||
|
#define LOG_RAW(color, ...) LOG_RAW_IMPL(color, __VA_ARGS__)
|
||||||
|
}
|
128
src/main.cpp
Normal file
128
src/main.cpp
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
#include "common.hpp"
|
||||||
|
#include "features.hpp"
|
||||||
|
#include "fiber_pool.hpp"
|
||||||
|
#include "gui.hpp"
|
||||||
|
#include "logger.hpp"
|
||||||
|
#include "hooking.hpp"
|
||||||
|
#include "pointers.hpp"
|
||||||
|
#include "renderer.hpp"
|
||||||
|
#include "script_mgr.hpp"
|
||||||
|
|
||||||
|
BOOL APIENTRY DllMain(HMODULE hmod, DWORD reason, PVOID)
|
||||||
|
{
|
||||||
|
using namespace big;
|
||||||
|
if (reason == DLL_PROCESS_ATTACH)
|
||||||
|
{
|
||||||
|
DisableThreadLibraryCalls(hmod);
|
||||||
|
|
||||||
|
g_hmodule = hmod;
|
||||||
|
g_main_thread = CreateThread(nullptr, 0, [](PVOID) -> DWORD
|
||||||
|
{
|
||||||
|
auto logger_instance = std::make_unique<logger>();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
|
||||||
|
LOG_RAW(log_color::green | log_color::intensify,
|
||||||
|
u8R"kek( ...
|
||||||
|
;::::;
|
||||||
|
;::::; :;
|
||||||
|
;:::::' :;
|
||||||
|
;:::::; ;.
|
||||||
|
,:::::' ; OOO\
|
||||||
|
::::::; ; OOOOO\
|
||||||
|
;:::::; ; OOOOOOOO
|
||||||
|
,;::::::; ;' / OOOOOOO
|
||||||
|
;:::::::::`. ,,,;. / / DOOOOOO
|
||||||
|
.';:::::::::::::::::;, / / DOOOO
|
||||||
|
,::::::;::::::;;;;::::;, / / DOOO
|
||||||
|
;`::::::`'::::::;;;::::: ,#/ / DOOO
|
||||||
|
:`:::::::`;::::::;;::: ;::# / DOOO
|
||||||
|
::`:::::::`;:::::::: ;::::# / DOO
|
||||||
|
`:`:::::::`;:::::: ;::::::#/ DOO
|
||||||
|
:::`:::::::`;; ;:::::::::## OO
|
||||||
|
::::`:::::::`;::::::::;:::# OO
|
||||||
|
`:::::`::::::::::::;'`:;::# O
|
||||||
|
`:::::`::::::::;' / / `:#
|
||||||
|
::::::`:::::;' / / `#
|
||||||
|
|
||||||
|
)kek");
|
||||||
|
|
||||||
|
auto pointers_instance = std::make_unique<pointers>();
|
||||||
|
LOG_INFO("Pointers initialized.");
|
||||||
|
|
||||||
|
if (*g_pointers->m_game_state != eGameState::Playing)
|
||||||
|
{
|
||||||
|
LOG_INFO("Waiting for the game to load.");
|
||||||
|
do
|
||||||
|
{
|
||||||
|
std::this_thread::sleep_for(100ms);
|
||||||
|
} while (*g_pointers->m_game_state != eGameState::Playing);
|
||||||
|
|
||||||
|
LOG_INFO("The game has loaded.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG_INFO("The game is already loaded.");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto renderer_instance = std::make_unique<renderer>();
|
||||||
|
LOG_INFO("Renderer initialized.");
|
||||||
|
|
||||||
|
auto fiber_pool_instance = std::make_unique<fiber_pool>(10);
|
||||||
|
LOG_INFO("Fiber pool initialized.");
|
||||||
|
|
||||||
|
auto hooking_instance = std::make_unique<hooking>();
|
||||||
|
LOG_INFO("Hooking initialized.");
|
||||||
|
|
||||||
|
g_script_mgr.add_script(std::make_unique<script>(&features::script_func));
|
||||||
|
g_script_mgr.add_script(std::make_unique<script>(&gui::script_func));
|
||||||
|
LOG_INFO("Scripts registered.");
|
||||||
|
|
||||||
|
g_hooking->enable();
|
||||||
|
LOG_INFO("Hooking enabled.");
|
||||||
|
|
||||||
|
while (g_running)
|
||||||
|
{
|
||||||
|
if (GetAsyncKeyState(VK_END) & 0x8000)
|
||||||
|
g_running = false;
|
||||||
|
|
||||||
|
g_hooking->ensure_dynamic_hooks();
|
||||||
|
std::this_thread::sleep_for(10ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_hooking->disable();
|
||||||
|
LOG_INFO("Hooking disabled.");
|
||||||
|
|
||||||
|
std::this_thread::sleep_for(1000ms);
|
||||||
|
|
||||||
|
g_script_mgr.remove_all_scripts();
|
||||||
|
LOG_INFO("Scripts unregistered.");
|
||||||
|
|
||||||
|
hooking_instance.reset();
|
||||||
|
LOG_INFO("Hooking uninitialized.");
|
||||||
|
|
||||||
|
fiber_pool_instance.reset();
|
||||||
|
LOG_INFO("Fiber pool uninitialized.");
|
||||||
|
|
||||||
|
renderer_instance.reset();
|
||||||
|
LOG_INFO("Renderer uninitialized.");
|
||||||
|
|
||||||
|
pointers_instance.reset();
|
||||||
|
LOG_INFO("Pointers uninitialized.");
|
||||||
|
}
|
||||||
|
catch (std::exception const &ex)
|
||||||
|
{
|
||||||
|
LOG_ERROR("{}", ex.what());
|
||||||
|
MessageBoxA(nullptr, ex.what(), nullptr, MB_OK | MB_ICONEXCLAMATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_INFO("Farewell!");
|
||||||
|
logger_instance.reset();
|
||||||
|
|
||||||
|
CloseHandle(g_main_thread);
|
||||||
|
FreeLibraryAndExitThread(g_hmodule, 0);
|
||||||
|
}, nullptr, 0, &g_main_thread_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
7
src/memory/all.hpp
Normal file
7
src/memory/all.hpp
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "handle.hpp"
|
||||||
|
#include "module.hpp"
|
||||||
|
#include "pattern.hpp"
|
||||||
|
#include "pattern_batch.hpp"
|
||||||
|
#include "range.hpp"
|
10
src/memory/fwddec.hpp
Normal file
10
src/memory/fwddec.hpp
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace memory
|
||||||
|
{
|
||||||
|
class handle;
|
||||||
|
class range;
|
||||||
|
class module;
|
||||||
|
class pattern;
|
||||||
|
class pattern_batch;
|
||||||
|
}
|
96
src/memory/handle.hpp
Normal file
96
src/memory/handle.hpp
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace memory
|
||||||
|
{
|
||||||
|
class handle
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
handle(void* ptr = nullptr);
|
||||||
|
explicit handle(std::uintptr_t ptr);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
std::enable_if_t<std::is_pointer_v<T>, T> as();
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
std::enable_if_t<std::is_lvalue_reference_v<T>, T> as();
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
std::enable_if_t<std::is_same_v<T, std::uintptr_t>, T> as();
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
handle add(T offset);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
handle sub(T offset);
|
||||||
|
|
||||||
|
handle rip();
|
||||||
|
|
||||||
|
explicit operator bool();
|
||||||
|
|
||||||
|
friend bool operator==(handle a, handle b);
|
||||||
|
friend bool operator!=(handle a, handle b);
|
||||||
|
private:
|
||||||
|
void* ptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline handle::handle(void* ptr) :
|
||||||
|
ptr(ptr)
|
||||||
|
{}
|
||||||
|
|
||||||
|
inline handle::handle(std::uintptr_t ptr) :
|
||||||
|
ptr(reinterpret_cast<void*>(ptr))
|
||||||
|
{}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline std::enable_if_t<std::is_pointer_v<T>, T> handle::as()
|
||||||
|
{
|
||||||
|
return static_cast<T>(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline std::enable_if_t<std::is_lvalue_reference_v<T>, T> handle::as()
|
||||||
|
{
|
||||||
|
return *static_cast<std::add_pointer_t<std::remove_reference_t<T>>>(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline std::enable_if_t<std::is_same_v<T, std::uintptr_t>, T> handle::as()
|
||||||
|
{
|
||||||
|
return reinterpret_cast<std::uintptr_t>(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline handle handle::add(T offset)
|
||||||
|
{
|
||||||
|
return handle(as<std::uintptr_t>() + offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline handle handle::sub(T offset)
|
||||||
|
{
|
||||||
|
return handle(as<std::uintptr_t>() - offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline handle handle::rip()
|
||||||
|
{
|
||||||
|
return add(as<std::int32_t&>()).add(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool operator==(handle a, handle b)
|
||||||
|
{
|
||||||
|
return a.ptr == b.ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool operator!=(handle a, handle b)
|
||||||
|
{
|
||||||
|
return a.ptr != b.ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline handle::operator bool()
|
||||||
|
{
|
||||||
|
return ptr != nullptr;
|
||||||
|
}
|
||||||
|
}
|
33
src/memory/module.cpp
Normal file
33
src/memory/module.cpp
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#include "../common.hpp"
|
||||||
|
#include "module.hpp"
|
||||||
|
|
||||||
|
namespace memory
|
||||||
|
{
|
||||||
|
module::module(HMODULE mod) :
|
||||||
|
range(mod, 0)
|
||||||
|
{
|
||||||
|
auto dosHeader = m_base.as<IMAGE_DOS_HEADER*>();
|
||||||
|
auto ntHeader = m_base.add(dosHeader->e_lfanew).as<IMAGE_NT_HEADERS*>();
|
||||||
|
m_size = ntHeader->OptionalHeader.SizeOfImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
module::module(std::nullptr_t) :
|
||||||
|
module(GetModuleHandle(nullptr))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
module::module(std::string_view name) :
|
||||||
|
module(GetModuleHandleA(name.data()))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
module::module(std::wstring_view name) :
|
||||||
|
module(GetModuleHandleW(name.data()))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
handle module::get_export(std::string_view symbol_name)
|
||||||
|
{
|
||||||
|
return GetProcAddress(m_base.as<HMODULE>(), symbol_name.data());
|
||||||
|
}
|
||||||
|
}
|
16
src/memory/module.hpp
Normal file
16
src/memory/module.hpp
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "range.hpp"
|
||||||
|
|
||||||
|
namespace memory
|
||||||
|
{
|
||||||
|
class module : public range
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
module(HMODULE mod);
|
||||||
|
explicit module(std::nullptr_t);
|
||||||
|
explicit module(std::string_view name);
|
||||||
|
explicit module(std::wstring_view name);
|
||||||
|
|
||||||
|
memory::handle get_export(std::string_view symbol_name);
|
||||||
|
};
|
||||||
|
}
|
90
src/memory/pattern.cpp
Normal file
90
src/memory/pattern.cpp
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
#include "../common.hpp"
|
||||||
|
#include "pattern.hpp"
|
||||||
|
|
||||||
|
namespace memory
|
||||||
|
{
|
||||||
|
pattern::pattern(std::string_view ida_sig)
|
||||||
|
{
|
||||||
|
auto to_upper = [](char c) -> char
|
||||||
|
{
|
||||||
|
return c >= 'a' && c <= 'z' ? static_cast<char>(c + ('A' - 'a')) : static_cast<char>(c);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto to_hex = [&](char c) -> std::optional<std::uint8_t>
|
||||||
|
{
|
||||||
|
switch (to_upper(c))
|
||||||
|
{
|
||||||
|
case '0':
|
||||||
|
return static_cast<std::uint8_t>(0);
|
||||||
|
case '1':
|
||||||
|
return static_cast<std::uint8_t>(1);
|
||||||
|
case '2':
|
||||||
|
return static_cast<std::uint8_t>(2);
|
||||||
|
case '3':
|
||||||
|
return static_cast<std::uint8_t>(3);
|
||||||
|
case '4':
|
||||||
|
return static_cast<std::uint8_t>(4);
|
||||||
|
case '5':
|
||||||
|
return static_cast<std::uint8_t>(5);
|
||||||
|
case '6':
|
||||||
|
return static_cast<std::uint8_t>(6);
|
||||||
|
case '7':
|
||||||
|
return static_cast<std::uint8_t>(7);
|
||||||
|
case '8':
|
||||||
|
return static_cast<std::uint8_t>(8);
|
||||||
|
case '9':
|
||||||
|
return static_cast<std::uint8_t>(9);
|
||||||
|
case 'A':
|
||||||
|
return static_cast<std::uint8_t>(10);
|
||||||
|
case 'B':
|
||||||
|
return static_cast<std::uint8_t>(11);
|
||||||
|
case 'C':
|
||||||
|
return static_cast<std::uint8_t>(12);
|
||||||
|
case 'D':
|
||||||
|
return static_cast<std::uint8_t>(13);
|
||||||
|
case 'E':
|
||||||
|
return static_cast<std::uint8_t>(14);
|
||||||
|
case 'F':
|
||||||
|
return static_cast<std::uint8_t>(15);
|
||||||
|
default:
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < ida_sig.size(); ++i)
|
||||||
|
{
|
||||||
|
if (ida_sig[i] == ' ')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
bool last = (i == ida_sig.size() - 1);
|
||||||
|
if (ida_sig[i] != '?')
|
||||||
|
{
|
||||||
|
if (!last)
|
||||||
|
{
|
||||||
|
auto c1 = to_hex(ida_sig[i]);
|
||||||
|
auto c2 = to_hex(ida_sig[i + 1]);
|
||||||
|
|
||||||
|
if (c1 && c2)
|
||||||
|
{
|
||||||
|
m_bytes.emplace_back(static_cast<std::uint8_t>((*c1 * 0x10) + *c2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_bytes.push_back(std::nullopt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pattern::pattern(const void *bytes, std::string_view mask)
|
||||||
|
{
|
||||||
|
for (std::size_t i = 0; i < mask.size(); ++i)
|
||||||
|
{
|
||||||
|
if (mask[i] != '?')
|
||||||
|
m_bytes.emplace_back(static_cast<const std::uint8_t*>(bytes)[i]);
|
||||||
|
else
|
||||||
|
m_bytes.push_back(std::nullopt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
25
src/memory/pattern.hpp
Normal file
25
src/memory/pattern.hpp
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <cstdint>
|
||||||
|
#include <optional>
|
||||||
|
#include <string_view>
|
||||||
|
#include <vector>
|
||||||
|
#include "fwddec.hpp"
|
||||||
|
#include "handle.hpp"
|
||||||
|
|
||||||
|
namespace memory
|
||||||
|
{
|
||||||
|
class pattern
|
||||||
|
{
|
||||||
|
friend pattern_batch;
|
||||||
|
friend range;
|
||||||
|
public:
|
||||||
|
pattern(std::string_view ida_sig);
|
||||||
|
explicit pattern(const void *bytes, std::string_view mask);
|
||||||
|
|
||||||
|
inline pattern(const char* ida_sig) :
|
||||||
|
pattern(std::string_view(ida_sig))
|
||||||
|
{}
|
||||||
|
private:
|
||||||
|
std::vector<std::optional<std::uint8_t>> m_bytes;
|
||||||
|
};
|
||||||
|
}
|
39
src/memory/pattern_batch.cpp
Normal file
39
src/memory/pattern_batch.cpp
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
#include "../common.hpp"
|
||||||
|
#include "../logger.hpp"
|
||||||
|
#include "pattern_batch.hpp"
|
||||||
|
#include "range.hpp"
|
||||||
|
|
||||||
|
namespace memory
|
||||||
|
{
|
||||||
|
void pattern_batch::add(std::string name, pattern pattern, std::function<void(handle)> callback)
|
||||||
|
{
|
||||||
|
m_entries.emplace_back(std::move(name), std::move(pattern), std::move(callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
void pattern_batch::run(range region)
|
||||||
|
{
|
||||||
|
bool all_found = true;
|
||||||
|
for (auto &entry : m_entries)
|
||||||
|
{
|
||||||
|
if (auto result = region.scan(entry.m_pattern))
|
||||||
|
{
|
||||||
|
if (entry.m_callback)
|
||||||
|
{
|
||||||
|
std::invoke(std::move(entry.m_callback), result);
|
||||||
|
LOG_INFO("Found '{}'.", entry.m_name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
all_found = false;
|
||||||
|
LOG_ERROR("Failed to find '{}'.", entry.m_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_entries.clear();
|
||||||
|
if (!all_found)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Failed to find some patterns.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
33
src/memory/pattern_batch.hpp
Normal file
33
src/memory/pattern_batch.hpp
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <functional>
|
||||||
|
#include <vector>
|
||||||
|
#include "pattern.hpp"
|
||||||
|
|
||||||
|
namespace memory
|
||||||
|
{
|
||||||
|
class pattern_batch
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit pattern_batch() = default;
|
||||||
|
~pattern_batch() noexcept = default;
|
||||||
|
|
||||||
|
void add(std::string name, pattern pattern, std::function<void(memory::handle)> callback);
|
||||||
|
void run(range region);
|
||||||
|
|
||||||
|
struct entry
|
||||||
|
{
|
||||||
|
std::string m_name;
|
||||||
|
pattern m_pattern;
|
||||||
|
std::function<void(memory::handle)> m_callback;
|
||||||
|
|
||||||
|
explicit entry(std::string name, pattern pattern, std::function<void(memory::handle)> callback) :
|
||||||
|
m_name(std::move(name)),
|
||||||
|
m_pattern(std::move(pattern)),
|
||||||
|
m_callback(std::move(callback))
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<entry> m_entries;
|
||||||
|
};
|
||||||
|
}
|
74
src/memory/range.cpp
Normal file
74
src/memory/range.cpp
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
#include "../common.hpp"
|
||||||
|
#include "range.hpp"
|
||||||
|
#include "pattern.hpp"
|
||||||
|
|
||||||
|
namespace memory
|
||||||
|
{
|
||||||
|
range::range(handle base, std::size_t size) :
|
||||||
|
m_base(base), m_size(size)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
handle range::begin()
|
||||||
|
{
|
||||||
|
return m_base;
|
||||||
|
}
|
||||||
|
|
||||||
|
handle range::end()
|
||||||
|
{
|
||||||
|
return m_base.add(m_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t range::size()
|
||||||
|
{
|
||||||
|
return m_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool range::contains(handle h)
|
||||||
|
{
|
||||||
|
return h.as<std::uintptr_t>() >= begin().as<std::uintptr_t>() && h.as<std::uintptr_t>() <= end().as<std::uintptr_t>();
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool pattern_matches(std::uint8_t* target, const std::optional<std::uint8_t>* sig, std::size_t length)
|
||||||
|
{
|
||||||
|
for (std::size_t i = 0; i < length; ++i)
|
||||||
|
{
|
||||||
|
if (sig[i] && *sig[i] != target[i])
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
handle range::scan(pattern const &sig)
|
||||||
|
{
|
||||||
|
auto data = sig.m_bytes.data();
|
||||||
|
auto length = sig.m_bytes.size();
|
||||||
|
for (std::uintptr_t i = 0; i < m_size - length; ++i)
|
||||||
|
{
|
||||||
|
if (pattern_matches(m_base.add(i).as<std::uint8_t*>(), data, length))
|
||||||
|
{
|
||||||
|
return m_base.add(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<handle> range::scan_all(pattern const &sig)
|
||||||
|
{
|
||||||
|
std::vector<handle> result;
|
||||||
|
|
||||||
|
auto data = sig.m_bytes.data();
|
||||||
|
auto length = sig.m_bytes.size();
|
||||||
|
for (std::uintptr_t i = 0; i < m_size - length; ++i)
|
||||||
|
{
|
||||||
|
if (pattern_matches(m_base.add(i).as<std::uint8_t*>(), data, length))
|
||||||
|
{
|
||||||
|
result.push_back(m_base.add(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::move(result);
|
||||||
|
}
|
||||||
|
}
|
25
src/memory/range.hpp
Normal file
25
src/memory/range.hpp
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <vector>
|
||||||
|
#include "fwddec.hpp"
|
||||||
|
#include "handle.hpp"
|
||||||
|
|
||||||
|
namespace memory
|
||||||
|
{
|
||||||
|
class range
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
range(handle base, std::size_t size);
|
||||||
|
|
||||||
|
handle begin();
|
||||||
|
handle end();
|
||||||
|
std::size_t size();
|
||||||
|
|
||||||
|
bool contains(handle h);
|
||||||
|
|
||||||
|
handle scan(pattern const& sig);
|
||||||
|
std::vector<handle> scan_all(pattern const& sig);
|
||||||
|
protected:
|
||||||
|
handle m_base;
|
||||||
|
std::size_t m_size;
|
||||||
|
};
|
||||||
|
}
|
5372
src/natives.hpp
Normal file
5372
src/natives.hpp
Normal file
File diff suppressed because it is too large
Load Diff
82
src/pointers.cpp
Normal file
82
src/pointers.cpp
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
#include "common.hpp"
|
||||||
|
#include "logger.hpp"
|
||||||
|
#include "pointers.hpp"
|
||||||
|
#include "memory/all.hpp"
|
||||||
|
|
||||||
|
namespace big
|
||||||
|
{
|
||||||
|
pointers::pointers()
|
||||||
|
{
|
||||||
|
memory::pattern_batch main_batch;
|
||||||
|
|
||||||
|
main_batch.add("Game state", "83 3D ? ? ? ? ? 75 17 8B 42 20 25", [this](memory::handle ptr)
|
||||||
|
{
|
||||||
|
m_game_state = ptr.add(2).rip().as<eGameState*>();
|
||||||
|
});
|
||||||
|
|
||||||
|
main_batch.add("Is session started", "40 38 35 ? ? ? ? 75 0E 4C 8B C3 49 8B D7 49 8B CE", [this](memory::handle ptr)
|
||||||
|
{
|
||||||
|
m_is_session_started = ptr.add(3).rip().as<bool*>();
|
||||||
|
});
|
||||||
|
|
||||||
|
main_batch.add("Ped factory", "48 8B 05 ? ? ? ? 48 8B 48 08 48 85 C9 74 52 8B 81", [this](memory::handle ptr)
|
||||||
|
{
|
||||||
|
m_ped_factory = ptr.add(3).rip().as<CPedFactory**>();
|
||||||
|
});
|
||||||
|
|
||||||
|
main_batch.add("Network player manager", "48 8B 0D ? ? ? ? 8A D3 48 8B 01 FF 50 ? 4C 8B 07 48 8B CF", [this](memory::handle ptr)
|
||||||
|
{
|
||||||
|
m_network_player_mgr = ptr.add(3).rip().as<CNetworkPlayerMgr**>();
|
||||||
|
});
|
||||||
|
|
||||||
|
main_batch.add("Native handlers", "48 8D 0D ? ? ? ? 48 8B 14 FA E8 ? ? ? ? 48 85 C0 75 0A", [this](memory::handle ptr)
|
||||||
|
{
|
||||||
|
m_native_registration_table = ptr.add(3).rip().as<rage::scrNativeRegistrationTable*>();
|
||||||
|
m_get_native_handler = ptr.add(12).rip().as<functions::get_native_handler_t>();
|
||||||
|
});
|
||||||
|
|
||||||
|
main_batch.add("Fix vectors", "83 79 18 00 48 8B D1 74 4A FF 4A 18 48 63 4A 18 48 8D 41 04 48 8B 4C CA", [this](memory::handle ptr)
|
||||||
|
{
|
||||||
|
m_fix_vectors = ptr.as<functions::fix_vectors_t>();
|
||||||
|
});
|
||||||
|
|
||||||
|
main_batch.add("Script threads", "45 33 F6 8B E9 85 C9 B8", [this](memory::handle ptr)
|
||||||
|
{
|
||||||
|
m_script_threads = ptr.sub(4).rip().sub(8).as<decltype(m_script_threads)>();
|
||||||
|
m_run_script_threads = ptr.sub(0x1F).as<functions::run_script_threads_t>();
|
||||||
|
});
|
||||||
|
|
||||||
|
main_batch.add("Script programs", "44 8B 0D ? ? ? ? 4C 8B 1D ? ? ? ? 48 8B 1D ? ? ? ? 41 83 F8 FF 74 3F 49 63 C0 42 0F B6 0C 18 81 E1", [this](memory::handle ptr)
|
||||||
|
{
|
||||||
|
m_script_program_table = ptr.add(17).rip().as<decltype(m_script_program_table)>();
|
||||||
|
});
|
||||||
|
|
||||||
|
main_batch.add("Script globals", "48 8D 15 ? ? ? ? 4C 8B C0 E8 ? ? ? ? 48 85 FF 48 89 1D", [this](memory::handle ptr)
|
||||||
|
{
|
||||||
|
m_script_globals = ptr.add(3).rip().as<std::int64_t**>();
|
||||||
|
});
|
||||||
|
|
||||||
|
main_batch.add("CGameScriptHandlerMgr", "48 8B 0D ? ? ? ? 4C 8B CE E8 ? ? ? ? 48 85 C0 74 05 40 32 FF", [this](memory::handle ptr)
|
||||||
|
{
|
||||||
|
m_script_handler_mgr = ptr.add(3).rip().as<CGameScriptHandlerMgr**>();
|
||||||
|
});
|
||||||
|
|
||||||
|
main_batch.add("Swapchain", "48 8B 0D ? ? ? ? 48 8B 01 44 8D 43 01 33 D2 FF 50 40 8B C8", [this](memory::handle ptr)
|
||||||
|
{
|
||||||
|
m_swapchain = ptr.add(3).rip().as<IDXGISwapChain**>();
|
||||||
|
});
|
||||||
|
|
||||||
|
main_batch.run(memory::module(nullptr));
|
||||||
|
|
||||||
|
m_hwnd = FindWindowW(L"grcWindow", nullptr);
|
||||||
|
if (!m_hwnd)
|
||||||
|
throw std::runtime_error("Failed to find the game's window.");
|
||||||
|
|
||||||
|
g_pointers = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
pointers::~pointers()
|
||||||
|
{
|
||||||
|
g_pointers = nullptr;
|
||||||
|
}
|
||||||
|
}
|
38
src/pointers.hpp
Normal file
38
src/pointers.hpp
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "common.hpp"
|
||||||
|
#include "gta/fwddec.hpp"
|
||||||
|
#include "gta/enums.hpp"
|
||||||
|
#include "function_types.hpp"
|
||||||
|
|
||||||
|
namespace big
|
||||||
|
{
|
||||||
|
class pointers
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit pointers();
|
||||||
|
~pointers();
|
||||||
|
public:
|
||||||
|
HWND m_hwnd{};
|
||||||
|
|
||||||
|
eGameState *m_game_state{};
|
||||||
|
bool *m_is_session_started{};
|
||||||
|
|
||||||
|
CPedFactory **m_ped_factory{};
|
||||||
|
CNetworkPlayerMgr **m_network_player_mgr{};
|
||||||
|
|
||||||
|
rage::scrNativeRegistrationTable *m_native_registration_table{};
|
||||||
|
functions::get_native_handler_t m_get_native_handler{};
|
||||||
|
functions::fix_vectors_t m_fix_vectors{};
|
||||||
|
|
||||||
|
rage::atArray<GtaThread*> *m_script_threads{};
|
||||||
|
rage::scrProgramTable *m_script_program_table{};
|
||||||
|
functions::run_script_threads_t m_run_script_threads{};
|
||||||
|
std::int64_t **m_script_globals{};
|
||||||
|
|
||||||
|
CGameScriptHandlerMgr **m_script_handler_mgr{};
|
||||||
|
|
||||||
|
IDXGISwapChain **m_swapchain{};
|
||||||
|
};
|
||||||
|
|
||||||
|
inline pointers *g_pointers{};
|
||||||
|
}
|
100
src/renderer.cpp
Normal file
100
src/renderer.cpp
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
#include "common.hpp"
|
||||||
|
#include "fonts.hpp"
|
||||||
|
#include "logger.hpp"
|
||||||
|
#include "gui.hpp"
|
||||||
|
#include "pointers.hpp"
|
||||||
|
#include "renderer.hpp"
|
||||||
|
#include "imgui/imgui.h"
|
||||||
|
#include "imgui/imgui_impl_dx11.h"
|
||||||
|
#include "imgui/imgui_impl_win32.h"
|
||||||
|
|
||||||
|
IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
||||||
|
|
||||||
|
namespace big
|
||||||
|
{
|
||||||
|
renderer::renderer() :
|
||||||
|
m_dxgi_swapchain(*g_pointers->m_swapchain)
|
||||||
|
{
|
||||||
|
void *d3d_device{};
|
||||||
|
if (SUCCEEDED(m_dxgi_swapchain->GetDevice(__uuidof(ID3D11Device), &d3d_device)))
|
||||||
|
{
|
||||||
|
m_d3d_device.Attach(static_cast<ID3D11Device*>(d3d_device));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Failed to get D3D device.");
|
||||||
|
}
|
||||||
|
|
||||||
|
m_d3d_device->GetImmediateContext(m_d3d_device_context.GetAddressOf());
|
||||||
|
|
||||||
|
ImGui::CreateContext();
|
||||||
|
ImGui_ImplDX11_Init(m_d3d_device.Get(), m_d3d_device_context.Get());
|
||||||
|
ImGui_ImplWin32_Init(g_pointers->m_hwnd);
|
||||||
|
|
||||||
|
ImFontConfig font_cfg{};
|
||||||
|
font_cfg.FontDataOwnedByAtlas = false;
|
||||||
|
std::strcpy(font_cfg.Name, "Rubik");
|
||||||
|
|
||||||
|
m_font = ImGui::GetIO().Fonts->AddFontFromMemoryTTF(const_cast<std::uint8_t*>(font_rubik), sizeof(font_rubik), 20.f, &font_cfg);
|
||||||
|
m_monospace_font = ImGui::GetIO().Fonts->AddFontDefault();
|
||||||
|
|
||||||
|
g_gui.dx_init();
|
||||||
|
g_renderer = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderer::~renderer()
|
||||||
|
{
|
||||||
|
ImGui_ImplWin32_Shutdown();
|
||||||
|
ImGui_ImplDX11_Shutdown();
|
||||||
|
ImGui::DestroyContext();
|
||||||
|
|
||||||
|
g_renderer = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void renderer::on_present()
|
||||||
|
{
|
||||||
|
if (g_gui.m_opened)
|
||||||
|
{
|
||||||
|
ImGui::GetIO().MouseDrawCursor = true;
|
||||||
|
ImGui::GetIO().ConfigFlags &= ~ImGuiConfigFlags_NoMouse;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ImGui::GetIO().MouseDrawCursor = false;
|
||||||
|
ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_NoMouse;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui_ImplDX11_NewFrame();
|
||||||
|
ImGui_ImplWin32_NewFrame();
|
||||||
|
ImGui::NewFrame();
|
||||||
|
|
||||||
|
if (g_gui.m_opened)
|
||||||
|
{
|
||||||
|
g_gui.dx_on_tick();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Render();
|
||||||
|
ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
|
||||||
|
}
|
||||||
|
|
||||||
|
void renderer::pre_reset()
|
||||||
|
{
|
||||||
|
ImGui_ImplDX11_InvalidateDeviceObjects();
|
||||||
|
}
|
||||||
|
|
||||||
|
void renderer::post_reset()
|
||||||
|
{
|
||||||
|
ImGui_ImplDX11_CreateDeviceObjects();
|
||||||
|
}
|
||||||
|
|
||||||
|
void renderer::wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
|
||||||
|
{
|
||||||
|
if (msg == WM_KEYUP && wparam == VK_INSERT)
|
||||||
|
g_gui.m_opened ^= true;
|
||||||
|
|
||||||
|
if (g_gui.m_opened)
|
||||||
|
{
|
||||||
|
ImGui_ImplWin32_WndProcHandler(hwnd, msg, wparam, lparam);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
29
src/renderer.hpp
Normal file
29
src/renderer.hpp
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "common.hpp"
|
||||||
|
#include "imgui/imgui.h"
|
||||||
|
|
||||||
|
namespace big
|
||||||
|
{
|
||||||
|
class renderer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit renderer();
|
||||||
|
~renderer();
|
||||||
|
|
||||||
|
void on_present();
|
||||||
|
|
||||||
|
void pre_reset();
|
||||||
|
void post_reset();
|
||||||
|
|
||||||
|
void wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
|
||||||
|
public:
|
||||||
|
ImFont *m_font;
|
||||||
|
ImFont *m_monospace_font;
|
||||||
|
private:
|
||||||
|
comptr<IDXGISwapChain> m_dxgi_swapchain;
|
||||||
|
comptr<ID3D11Device> m_d3d_device;
|
||||||
|
comptr<ID3D11DeviceContext> m_d3d_device_context;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline renderer *g_renderer{};
|
||||||
|
}
|
92
src/script.cpp
Normal file
92
src/script.cpp
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "common.hpp"
|
||||||
|
#include "logger.hpp"
|
||||||
|
#include "script.hpp"
|
||||||
|
|
||||||
|
namespace big
|
||||||
|
{
|
||||||
|
static void script_exception_handler(PEXCEPTION_POINTERS exp)
|
||||||
|
{
|
||||||
|
LOG_ERROR("Script threw an exception!");
|
||||||
|
g_stackwalker.ShowCallstack(GetCurrentThread(), exp->ContextRecord);
|
||||||
|
}
|
||||||
|
|
||||||
|
script::script(func_t func, std::optional<std::size_t> stack_size) :
|
||||||
|
m_func(func),
|
||||||
|
m_script_fiber(nullptr),
|
||||||
|
m_main_fiber(nullptr)
|
||||||
|
{
|
||||||
|
m_script_fiber = CreateFiber(stack_size.has_value() ? stack_size.value() : 0, [](void* param)
|
||||||
|
{
|
||||||
|
auto this_script = static_cast<script*>(param);
|
||||||
|
this_script->fiber_func();
|
||||||
|
}, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
script::~script()
|
||||||
|
{
|
||||||
|
if (m_script_fiber)
|
||||||
|
DeleteFiber(m_script_fiber);
|
||||||
|
}
|
||||||
|
|
||||||
|
void script::tick()
|
||||||
|
{
|
||||||
|
m_main_fiber = GetCurrentFiber();
|
||||||
|
if (!m_wake_time.has_value() || m_wake_time.value() <= std::chrono::high_resolution_clock::now())
|
||||||
|
{
|
||||||
|
SwitchToFiber(m_script_fiber);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void script::yield(std::optional<std::chrono::high_resolution_clock::duration> time)
|
||||||
|
{
|
||||||
|
if (time.has_value())
|
||||||
|
{
|
||||||
|
m_wake_time = std::chrono::high_resolution_clock::now() + time.value();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_wake_time = std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
SwitchToFiber(m_main_fiber);
|
||||||
|
}
|
||||||
|
|
||||||
|
script *script::get_current()
|
||||||
|
{
|
||||||
|
return static_cast<script*>(GetFiberData());
|
||||||
|
}
|
||||||
|
|
||||||
|
void script::fiber_func()
|
||||||
|
{
|
||||||
|
__try
|
||||||
|
{
|
||||||
|
[this]()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
m_func();
|
||||||
|
}
|
||||||
|
catch (std::exception const &ex)
|
||||||
|
{
|
||||||
|
auto ex_class = typeid(ex).name() + 6;
|
||||||
|
LOG_INFO("Script threw an C++ expection! {}: {}", ex_class, ex.what());
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
LOG_INFO("Script threw a C++ exception!");
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
}
|
||||||
|
__except (script_exception_handler(GetExceptionInformation()), EXCEPTION_EXECUTE_HANDLER)
|
||||||
|
{
|
||||||
|
LOG_INFO("Script threw an exception!");
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_INFO("Script finished!");
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
yield();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
25
src/script.hpp
Normal file
25
src/script.hpp
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "common.hpp"
|
||||||
|
|
||||||
|
namespace big
|
||||||
|
{
|
||||||
|
class script
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using func_t = void(*)();
|
||||||
|
public:
|
||||||
|
explicit script(func_t func, std::optional<std::size_t> stack_size = std::nullopt);
|
||||||
|
~script();
|
||||||
|
|
||||||
|
void tick();
|
||||||
|
void yield(std::optional<std::chrono::high_resolution_clock::duration> time = std::nullopt);
|
||||||
|
static script *get_current();
|
||||||
|
private:
|
||||||
|
void fiber_func();
|
||||||
|
private:
|
||||||
|
void *m_script_fiber;
|
||||||
|
void *m_main_fiber;
|
||||||
|
func_t m_func;
|
||||||
|
std::optional<std::chrono::high_resolution_clock::time_point> m_wake_time;
|
||||||
|
};
|
||||||
|
}
|
26
src/script_global.cpp
Normal file
26
src/script_global.cpp
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#include "common.hpp"
|
||||||
|
#include "pointers.hpp"
|
||||||
|
#include "script_global.hpp"
|
||||||
|
|
||||||
|
namespace big
|
||||||
|
{
|
||||||
|
script_global::script_global(std::size_t index) :
|
||||||
|
m_index(index)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
script_global script_global::at(std::ptrdiff_t index)
|
||||||
|
{
|
||||||
|
return script_global(m_index + index);
|
||||||
|
}
|
||||||
|
|
||||||
|
script_global script_global::at(std::ptrdiff_t index, std::size_t size)
|
||||||
|
{
|
||||||
|
return script_global(m_index + 1 + (index * size));
|
||||||
|
}
|
||||||
|
|
||||||
|
void *script_global::get()
|
||||||
|
{
|
||||||
|
return g_pointers->m_script_globals[m_index >> 0x12 & 0x3F] + (m_index & 0x3FFFF);
|
||||||
|
}
|
||||||
|
}
|
29
src/script_global.hpp
Normal file
29
src/script_global.hpp
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "common.hpp"
|
||||||
|
|
||||||
|
namespace big
|
||||||
|
{
|
||||||
|
class script_global
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit script_global(std::size_t index);
|
||||||
|
|
||||||
|
script_global at(std::ptrdiff_t index);
|
||||||
|
script_global at(std::ptrdiff_t index, std::size_t size);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
std::enable_if_t<std::is_pointer_v<T>, T> as()
|
||||||
|
{
|
||||||
|
return static_cast<T>(get());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
std::enable_if_t<std::is_lvalue_reference_v<T>, T> as()
|
||||||
|
{
|
||||||
|
return *static_cast<std::add_pointer_t<std::remove_reference_t<T>>>(get());
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
void *get();
|
||||||
|
std::size_t m_index;
|
||||||
|
};
|
||||||
|
}
|
107
src/script_hook.cpp
Normal file
107
src/script_hook.cpp
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
#include "common.hpp"
|
||||||
|
#include "crossmap.hpp"
|
||||||
|
#include "gta/script_program.hpp"
|
||||||
|
#include "logger.hpp"
|
||||||
|
#include "pointers.hpp"
|
||||||
|
#include "script_hook.hpp"
|
||||||
|
|
||||||
|
namespace big
|
||||||
|
{
|
||||||
|
inline std::unordered_map<rage::scrProgram*, script_hook*> script_hook::s_map;
|
||||||
|
|
||||||
|
static bool map_native(rage::scrNativeHash *hash)
|
||||||
|
{
|
||||||
|
for (auto const &mapping : g_crossmap)
|
||||||
|
{
|
||||||
|
if (mapping.first == *hash)
|
||||||
|
{
|
||||||
|
*hash = mapping.second;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
script_hook::script_hook(rage::joaat_t script_hash, std::unordered_map<rage::scrNativeHash, rage::scrNativeHandler> native_replacements) :
|
||||||
|
m_script_hash(script_hash),
|
||||||
|
m_native_replacements(std::move(native_replacements))
|
||||||
|
{
|
||||||
|
ensure();
|
||||||
|
}
|
||||||
|
|
||||||
|
script_hook::~script_hook()
|
||||||
|
{
|
||||||
|
if (m_program)
|
||||||
|
{
|
||||||
|
for (auto[hash, handler_ptr] : m_native_handler_ptrs)
|
||||||
|
{
|
||||||
|
auto og_handler = g_pointers->m_get_native_handler(g_pointers->m_native_registration_table, hash);
|
||||||
|
*handler_ptr = og_handler;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_vmt_hook)
|
||||||
|
{
|
||||||
|
m_vmt_hook->disable();
|
||||||
|
s_map.erase(m_program);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void script_hook::ensure()
|
||||||
|
{
|
||||||
|
if (m_vmt_hook)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (auto program = g_pointers->m_script_program_table->find_script(m_script_hash))
|
||||||
|
{
|
||||||
|
if (program->is_valid())
|
||||||
|
{
|
||||||
|
hook_instance(program);
|
||||||
|
LOG_INFO("Hooked {} script ({})", program->m_name, static_cast<void*>(program));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void script_hook::hook_instance(rage::scrProgram *program)
|
||||||
|
{
|
||||||
|
m_program = program;
|
||||||
|
s_map.emplace(m_program, this);
|
||||||
|
m_vmt_hook = std::make_unique<vmt_hook>(m_program, 3);
|
||||||
|
m_vmt_hook->hook(0, &scrprogram_dtor);
|
||||||
|
|
||||||
|
for (auto [replacement_hash, replacement_handler] : m_native_replacements)
|
||||||
|
{
|
||||||
|
auto hash = replacement_hash;
|
||||||
|
map_native(&hash);
|
||||||
|
|
||||||
|
auto og_handler = g_pointers->m_get_native_handler(g_pointers->m_native_registration_table, hash);
|
||||||
|
if (!og_handler)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
auto handler_ptr = m_program->get_address_of_native_entrypoint(og_handler);
|
||||||
|
if (!handler_ptr)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
m_native_handler_ptrs.emplace(hash, reinterpret_cast<rage::scrNativeHandler*>(handler_ptr));
|
||||||
|
*handler_ptr = replacement_handler;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void script_hook::scrprogram_dtor(rage::scrProgram *this_, bool free_memory)
|
||||||
|
{
|
||||||
|
if (auto it = s_map.find(this_); it != s_map.end())
|
||||||
|
{
|
||||||
|
auto hook = it->second;
|
||||||
|
|
||||||
|
hook->m_program = nullptr;
|
||||||
|
s_map.erase(it);
|
||||||
|
|
||||||
|
auto og_func = hook->m_vmt_hook->get_original<decltype(&scrprogram_dtor)>(0);
|
||||||
|
hook->m_vmt_hook->disable();
|
||||||
|
hook->m_vmt_hook.reset();
|
||||||
|
|
||||||
|
og_func(this_, free_memory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
29
src/script_hook.hpp
Normal file
29
src/script_hook.hpp
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "common.hpp"
|
||||||
|
#include "gta/fwddec.hpp"
|
||||||
|
#include "gta/joaat.hpp"
|
||||||
|
#include "gta/natives.hpp"
|
||||||
|
#include "vmt_hook.hpp"
|
||||||
|
|
||||||
|
namespace big
|
||||||
|
{
|
||||||
|
class script_hook
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit script_hook(rage::joaat_t script_hash, std::unordered_map<rage::scrNativeHash, rage::scrNativeHandler> native_replacements);
|
||||||
|
~script_hook();
|
||||||
|
|
||||||
|
void ensure();
|
||||||
|
private:
|
||||||
|
void hook_instance(rage::scrProgram *program);
|
||||||
|
|
||||||
|
static void scrprogram_dtor(rage::scrProgram *this_, bool free_memory);
|
||||||
|
static std::unordered_map<rage::scrProgram*, script_hook*> s_map;
|
||||||
|
|
||||||
|
rage::joaat_t m_script_hash;
|
||||||
|
rage::scrProgram* m_program;
|
||||||
|
std::unique_ptr<vmt_hook> m_vmt_hook;
|
||||||
|
std::unordered_map<rage::scrNativeHash, rage::scrNativeHandler> m_native_replacements;
|
||||||
|
std::unordered_map<rage::scrNativeHash, rage::scrNativeHandler*> m_native_handler_ptrs;
|
||||||
|
};
|
||||||
|
}
|
38
src/script_mgr.cpp
Normal file
38
src/script_mgr.cpp
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#include "common.hpp"
|
||||||
|
#include "gta/array.hpp"
|
||||||
|
#include "gta/script_thread.hpp"
|
||||||
|
#include "gta/tls_context.hpp"
|
||||||
|
#include "gta_util.hpp"
|
||||||
|
#include "pointers.hpp"
|
||||||
|
#include "script_mgr.hpp"
|
||||||
|
|
||||||
|
namespace big
|
||||||
|
{
|
||||||
|
void script_mgr::add_script(std::unique_ptr<script> script)
|
||||||
|
{
|
||||||
|
std::lock_guard lock(m_mutex);
|
||||||
|
m_scripts.push_back(std::move(script));
|
||||||
|
}
|
||||||
|
|
||||||
|
void script_mgr::remove_all_scripts()
|
||||||
|
{
|
||||||
|
std::lock_guard lock(m_mutex);
|
||||||
|
m_scripts.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void script_mgr::tick()
|
||||||
|
{
|
||||||
|
gta_util::execute_as_script(RAGE_JOAAT("main_persistent"), std::mem_fn(&script_mgr::tick_internal), this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void script_mgr::tick_internal()
|
||||||
|
{
|
||||||
|
static bool ensure_main_fiber = (ConvertThreadToFiber(nullptr), true);
|
||||||
|
|
||||||
|
std::lock_guard lock(m_mutex);
|
||||||
|
for (auto const &script : m_scripts)
|
||||||
|
{
|
||||||
|
script->tick();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
25
src/script_mgr.hpp
Normal file
25
src/script_mgr.hpp
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "common.hpp"
|
||||||
|
#include "script.hpp"
|
||||||
|
|
||||||
|
namespace big
|
||||||
|
{
|
||||||
|
class script_mgr
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit script_mgr() = default;
|
||||||
|
~script_mgr() = default;
|
||||||
|
|
||||||
|
void add_script(std::unique_ptr<script> script);
|
||||||
|
void remove_all_scripts();
|
||||||
|
|
||||||
|
void tick();
|
||||||
|
private:
|
||||||
|
void tick_internal();
|
||||||
|
private:
|
||||||
|
std::recursive_mutex m_mutex;
|
||||||
|
std::vector<std::unique_ptr<script>> m_scripts;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline script_mgr g_script_mgr;
|
||||||
|
}
|
34
src/vmt_hook.cpp
Normal file
34
src/vmt_hook.cpp
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
#include "common.hpp"
|
||||||
|
#include "vmt_hook.hpp"
|
||||||
|
|
||||||
|
namespace big
|
||||||
|
{
|
||||||
|
vmt_hook::vmt_hook(void* obj, std::size_t num_funcs) :
|
||||||
|
m_object(static_cast<void***>(obj)),
|
||||||
|
m_num_funcs(num_funcs + 1),
|
||||||
|
m_original_table(*m_object),
|
||||||
|
m_new_table(std::make_unique<void*[]>(m_num_funcs))
|
||||||
|
{
|
||||||
|
std::copy_n(m_original_table - 1, m_num_funcs, m_new_table.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
void vmt_hook::hook(std::size_t index, void* func)
|
||||||
|
{
|
||||||
|
m_new_table[index + 1] = func;
|
||||||
|
}
|
||||||
|
|
||||||
|
void vmt_hook::unhook(std::size_t index)
|
||||||
|
{
|
||||||
|
m_new_table[index + 1] = m_original_table[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
void vmt_hook::enable()
|
||||||
|
{
|
||||||
|
*m_object = m_new_table.get() + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void vmt_hook::disable()
|
||||||
|
{
|
||||||
|
*m_object = m_original_table;
|
||||||
|
}
|
||||||
|
}
|
37
src/vmt_hook.hpp
Normal file
37
src/vmt_hook.hpp
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "common.hpp"
|
||||||
|
|
||||||
|
namespace big
|
||||||
|
{
|
||||||
|
class vmt_hook
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit vmt_hook(void* obj, std::size_t num_funcs);
|
||||||
|
|
||||||
|
vmt_hook(vmt_hook&& that) = delete;
|
||||||
|
vmt_hook& operator=(vmt_hook&& that) = delete;
|
||||||
|
vmt_hook(vmt_hook const&) = delete;
|
||||||
|
vmt_hook& operator=(vmt_hook const&) = delete;
|
||||||
|
|
||||||
|
void hook(std::size_t index, void* func);
|
||||||
|
void unhook(std::size_t index);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T get_original(std::size_t index);
|
||||||
|
|
||||||
|
void enable();
|
||||||
|
void disable();
|
||||||
|
private:
|
||||||
|
void*** m_object;
|
||||||
|
std::size_t m_num_funcs;
|
||||||
|
|
||||||
|
void** m_original_table;
|
||||||
|
std::unique_ptr<void*[]> m_new_table;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline T vmt_hook::get_original(std::size_t index)
|
||||||
|
{
|
||||||
|
return static_cast<T>(m_original_table[index]);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user