mirror of
https://github.com/OpenDiablo2/OpenDiablo2
synced 2024-09-27 21:56:19 -04:00
Migrated to golang codebase.
This commit is contained in:
parent
5ab92737ec
commit
361b60febf
5
.gitattributes
vendored
5
.gitattributes
vendored
@ -1,5 +0,0 @@
|
|||||||
# Auto detect text files and perform LF normalization
|
|
||||||
# http://davidlaing.com/2012/09/19/customise-your-gitattributes-to-become-a-git-ninja/
|
|
||||||
* text=auto
|
|
||||||
|
|
||||||
*.cs diff=csharp
|
|
6
.gitignore
vendored
6
.gitignore
vendored
@ -1,4 +1,2 @@
|
|||||||
cmake-build-*
|
__debug_bin
|
||||||
.idea
|
.vscode/*.*
|
||||||
.vscode
|
|
||||||
build/
|
|
||||||
|
0
.gitmodules
vendored
0
.gitmodules
vendored
33
App/OpenDiablo2.go
Normal file
33
App/OpenDiablo2.go
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"../OpenDiablo2"
|
||||||
|
"github.com/hajimehoshi/ebiten"
|
||||||
|
)
|
||||||
|
|
||||||
|
var d2Engine *OpenDiablo2.Engine
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
log.Println("OpenDiablo2 - Open source Diablo 2 engine")
|
||||||
|
OpenDiablo2.InitializeCryptoBuffer()
|
||||||
|
d2Engine = OpenDiablo2.CreateEngine()
|
||||||
|
ebiten.SetCursorVisible(false)
|
||||||
|
ebiten.SetFullscreen(d2Engine.Settings.FullScreen)
|
||||||
|
ebiten.SetRunnableInBackground(d2Engine.Settings.RunInBackground)
|
||||||
|
ebiten.SetVsyncEnabled(d2Engine.Settings.VsyncEnabled)
|
||||||
|
ebiten.SetMaxTPS(d2Engine.Settings.TicksPerSecond)
|
||||||
|
if err := ebiten.Run(update, 800, 600, d2Engine.Settings.Scale, "OpenDiablo 2"); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func update(screen *ebiten.Image) error {
|
||||||
|
d2Engine.Update()
|
||||||
|
if ebiten.IsDrawingSkipped() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
d2Engine.Draw(screen)
|
||||||
|
return nil
|
||||||
|
}
|
12
OpenDiablo2/Common/SceneInterface.go
Normal file
12
OpenDiablo2/Common/SceneInterface.go
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package Common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hajimehoshi/ebiten"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SceneInterface interface {
|
||||||
|
Load()
|
||||||
|
Unload()
|
||||||
|
Render(screen *ebiten.Image)
|
||||||
|
Update()
|
||||||
|
}
|
20
OpenDiablo2/CryptoBuff.go
Normal file
20
OpenDiablo2/CryptoBuff.go
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package OpenDiablo2
|
||||||
|
|
||||||
|
// CryptoBuffer contains the crypto bytes for filename hashing
|
||||||
|
var CryptoBuffer [0x500]uint32
|
||||||
|
|
||||||
|
// InitializeCryptoBuffer initializes the crypto buffer
|
||||||
|
func InitializeCryptoBuffer() {
|
||||||
|
seed := uint32(0x00100001)
|
||||||
|
for index1 := 0; index1 < 0x100; index1++ {
|
||||||
|
index2 := index1
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
seed = (seed*125 + 3) % 0x2AAAAB
|
||||||
|
temp1 := (seed & 0xFFFF) << 0x10
|
||||||
|
seed = (seed*125 + 3) % 0x2AAAAB
|
||||||
|
temp2 := (seed & 0xFFFF)
|
||||||
|
CryptoBuffer[index2] = temp1 | temp2
|
||||||
|
index2 += 0x100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
212
OpenDiablo2/Engine.go
Normal file
212
OpenDiablo2/Engine.go
Normal file
@ -0,0 +1,212 @@
|
|||||||
|
package OpenDiablo2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"./Common"
|
||||||
|
"./ResourcePaths"
|
||||||
|
|
||||||
|
"github.com/hajimehoshi/ebiten"
|
||||||
|
)
|
||||||
|
|
||||||
|
// EngineConfig defines the configuration for the engine, loaded from config.json
|
||||||
|
type EngineConfig struct {
|
||||||
|
FullScreen bool
|
||||||
|
Scale float64
|
||||||
|
RunInBackground bool
|
||||||
|
TicksPerSecond int
|
||||||
|
VsyncEnabled bool
|
||||||
|
MpqPath string
|
||||||
|
MpqLoadOrder []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Engine is the core OpenDiablo2 engine
|
||||||
|
type Engine struct {
|
||||||
|
Settings EngineConfig // Engine configuration settings from json file
|
||||||
|
Files map[string]string // Map that defines which files are in which MPQs
|
||||||
|
Palettes map[string]Palette // Color palettes
|
||||||
|
SoundEntries map[string]SoundEntry // Sound configurations
|
||||||
|
CursorSprite Sprite // The sprite shown for cursors
|
||||||
|
LoadingSprite Sprite // The sprite shown when loading stuff
|
||||||
|
CursorX int // X position of the cursor
|
||||||
|
CursorY int // Y position of the cursor
|
||||||
|
LoadingProgress float64 // LoadingProcess is a range between 0.0 and 1.0. If set, loading screen displays.
|
||||||
|
CurrentScene Common.SceneInterface // The current scene being rendered
|
||||||
|
nextScene Common.SceneInterface // The next scene to be loaded at the end of the game loop
|
||||||
|
fontCache map[string]*MPQFont // The font cash
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateEngine creates and instance of the OpenDiablo2 engine
|
||||||
|
func CreateEngine() *Engine {
|
||||||
|
result := &Engine{
|
||||||
|
LoadingProgress: float64(0.0),
|
||||||
|
CurrentScene: nil,
|
||||||
|
nextScene: nil,
|
||||||
|
fontCache: make(map[string]*MPQFont),
|
||||||
|
}
|
||||||
|
result.loadConfigurationFile()
|
||||||
|
result.mapMpqFiles()
|
||||||
|
result.loadPalettes()
|
||||||
|
result.loadSoundEntries()
|
||||||
|
result.CursorSprite = result.LoadSprite(ResourcePaths.CursorDefault, result.Palettes["units"])
|
||||||
|
result.LoadingSprite = result.LoadSprite(ResourcePaths.LoadingScreen, result.Palettes["loading"])
|
||||||
|
loadingSpriteSizeX, loadingSpriteSizeY := result.LoadingSprite.GetSize()
|
||||||
|
result.LoadingSprite.MoveTo(int(400-(loadingSpriteSizeX/2)), int(300+(loadingSpriteSizeY/2)))
|
||||||
|
result.SetNextScene(CreateMainMenu(result))
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Engine) loadConfigurationFile() {
|
||||||
|
log.Println("loading configuration file")
|
||||||
|
configJSON, err := ioutil.ReadFile("config.json")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
var config EngineConfig
|
||||||
|
|
||||||
|
json.Unmarshal(configJSON, &config)
|
||||||
|
v.Settings = config
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Engine) mapMpqFiles() {
|
||||||
|
log.Println("mapping mpq file structure")
|
||||||
|
v.Files = make(map[string]string)
|
||||||
|
lock := sync.RWMutex{}
|
||||||
|
for _, mpqFileName := range v.Settings.MpqLoadOrder {
|
||||||
|
mpqPath := path.Join(v.Settings.MpqPath, mpqFileName)
|
||||||
|
mpq, err := LoadMPQ(mpqPath)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
fileListText, err := mpq.ReadFile("(listfile)")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
fileList := strings.Split(string(fileListText), "\r\n")
|
||||||
|
for _, filePath := range fileList {
|
||||||
|
if _, exists := v.Files[strings.ToLower(filePath)]; exists {
|
||||||
|
lock.RUnlock()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
v.Files[`/`+strings.ReplaceAll(strings.ToLower(filePath), `\`, `/`)] = mpqPath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFile loads a file from the specified mpq and returns the data as a byte array
|
||||||
|
func (v *Engine) GetFile(fileName string) []byte {
|
||||||
|
// TODO: May want to cache some things if performance becomes an issue
|
||||||
|
mpqFile := v.Files[strings.ToLower(fileName)]
|
||||||
|
mpq, err := LoadMPQ(mpqFile)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
blockTableEntry, err := mpq.getFileBlockData(strings.ReplaceAll(fileName, `/`, `\`)[1:])
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
mpqStream := CreateMPQStream(mpq, blockTableEntry, fileName)
|
||||||
|
result := make([]byte, blockTableEntry.UncompressedFileSize)
|
||||||
|
mpqStream.Read(result, 0, blockTableEntry.UncompressedFileSize)
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsLoading returns true if the engine is currently in a loading state
|
||||||
|
func (v *Engine) IsLoading() bool {
|
||||||
|
return v.LoadingProgress < 1.0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Engine) loadPalettes() {
|
||||||
|
v.Palettes = make(map[string]Palette)
|
||||||
|
log.Println("loading palettes")
|
||||||
|
for file := range v.Files {
|
||||||
|
if strings.Index(file, "/data/global/palette/") != 0 || strings.Index(file, ".dat") != len(file)-4 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
nameParts := strings.Split(file, `/`)
|
||||||
|
paletteName := nameParts[len(nameParts)-2]
|
||||||
|
palette := CreatePalette(paletteName, v.GetFile(file))
|
||||||
|
v.Palettes[paletteName] = palette
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Engine) loadSoundEntries() {
|
||||||
|
log.Println("loading sound configurations")
|
||||||
|
v.SoundEntries = make(map[string]SoundEntry)
|
||||||
|
soundData := strings.Split(string(v.GetFile(ResourcePaths.SoundSettings)), "\r\n")[1:]
|
||||||
|
for _, line := range soundData {
|
||||||
|
if len(line) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
soundEntry := CreateSoundEntry(line)
|
||||||
|
v.SoundEntries[soundEntry.Handle] = soundEntry
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadSprite loads a sprite from the game's data files
|
||||||
|
func (v *Engine) LoadSprite(fileName string, palette Palette) Sprite {
|
||||||
|
data := v.GetFile(fileName)
|
||||||
|
sprite := CreateSprite(data, palette)
|
||||||
|
return sprite
|
||||||
|
}
|
||||||
|
|
||||||
|
// updateScene handles the scene maintenance for the engine
|
||||||
|
func (v *Engine) updateScene() {
|
||||||
|
if v.nextScene == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if v.CurrentScene != nil {
|
||||||
|
v.CurrentScene.Unload()
|
||||||
|
}
|
||||||
|
v.CurrentScene = v.nextScene
|
||||||
|
v.nextScene = nil
|
||||||
|
v.CurrentScene.Load()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update updates the internal state of the engine
|
||||||
|
func (v *Engine) Update() {
|
||||||
|
v.updateScene()
|
||||||
|
if v.CurrentScene == nil {
|
||||||
|
panic("no scene loaded")
|
||||||
|
}
|
||||||
|
v.CurrentScene.Update()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw draws the game
|
||||||
|
func (v *Engine) Draw(screen *ebiten.Image) {
|
||||||
|
v.CursorX, v.CursorY = ebiten.CursorPosition()
|
||||||
|
if v.LoadingProgress < 1.0 {
|
||||||
|
v.LoadingSprite.Frame = uint8(Max(0, Min(uint32(len(v.LoadingSprite.Frames)-1), uint32(float64(len(v.LoadingSprite.Frames)-1)*v.LoadingProgress))))
|
||||||
|
v.LoadingSprite.Draw(screen)
|
||||||
|
} else {
|
||||||
|
if v.CurrentScene == nil {
|
||||||
|
panic("no scene loaded")
|
||||||
|
}
|
||||||
|
v.CurrentScene.Render(screen)
|
||||||
|
}
|
||||||
|
|
||||||
|
v.CursorSprite.MoveTo(v.CursorX, v.CursorY)
|
||||||
|
v.CursorSprite.Draw(screen)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNextScene tells the engine what scene to load on the next update cycle
|
||||||
|
func (v *Engine) SetNextScene(nextScene Common.SceneInterface) {
|
||||||
|
v.nextScene = nextScene
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFont creates or loads an existing font
|
||||||
|
func (v *Engine) GetFont(font, palette string) *MPQFont {
|
||||||
|
cacheItem, exists := v.fontCache[font+"_"+palette]
|
||||||
|
if exists {
|
||||||
|
return cacheItem
|
||||||
|
}
|
||||||
|
newFont := CreateMPQFont(v, font, v.Palettes[palette])
|
||||||
|
v.fontCache[font+"_"+palette] = newFont
|
||||||
|
return newFont
|
||||||
|
}
|
256
OpenDiablo2/MPQ.go
Normal file
256
OpenDiablo2/MPQ.go
Normal file
@ -0,0 +1,256 @@
|
|||||||
|
package OpenDiablo2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MPQ represents an MPQ archive
|
||||||
|
type MPQ struct {
|
||||||
|
File *os.File
|
||||||
|
HashTableEntries []MPQHashTableEntry
|
||||||
|
BlockTableEntries []MPQBlockTableEntry
|
||||||
|
Data MPQData
|
||||||
|
}
|
||||||
|
|
||||||
|
// MPQData Represents a MPQ file
|
||||||
|
type MPQData struct {
|
||||||
|
Magic [4]byte
|
||||||
|
HeaderSize uint32
|
||||||
|
ArchiveSize uint32
|
||||||
|
FormatVersion uint16
|
||||||
|
BlockSize uint16
|
||||||
|
HashTableOffset uint32
|
||||||
|
BlockTableOffset uint32
|
||||||
|
HashTableEntries uint32
|
||||||
|
BlockTableEntries uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// MPQHashTableEntry represents a hashed file entry in the MPQ file
|
||||||
|
type MPQHashTableEntry struct { // 16 bytes
|
||||||
|
NamePartA uint32
|
||||||
|
NamePartB uint32
|
||||||
|
Locale uint16
|
||||||
|
Platform uint16
|
||||||
|
BlockIndex uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// MPQFileFlag represents flags for a file record in the MPQ archive
|
||||||
|
type MPQFileFlag uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
// MpqFileImplode - File is compressed using PKWARE Data compression library
|
||||||
|
MpqFileImplode MPQFileFlag = 0x00000100
|
||||||
|
// MpqFileCompress - File is compressed using combination of compression methods
|
||||||
|
MpqFileCompress MPQFileFlag = 0x00000200
|
||||||
|
// MpqFileEncrypted - The file is encrypted
|
||||||
|
MpqFileEncrypted MPQFileFlag = 0x00010000
|
||||||
|
// MpqFileFixKey - The decryption key for the file is altered according to the position of the file in the archive
|
||||||
|
MpqFileFixKey MPQFileFlag = 0x00020000
|
||||||
|
// MpqFilePatchFile - The file contains incremental patch for an existing file in base MPQ
|
||||||
|
MpqFilePatchFile MPQFileFlag = 0x00100000
|
||||||
|
// MpqFileSingleUnit - Instead of being divided to 0x1000-bytes blocks, the file is stored as single unit
|
||||||
|
MpqFileSingleUnit MPQFileFlag = 0x01000000
|
||||||
|
// FileDeleteMarker - File is a deletion marker, indicating that the file no longer exists. This is used to allow patch
|
||||||
|
// archives to delete files present in lower-priority archives in the search chain. The file usually
|
||||||
|
// has length of 0 or 1 byte and its name is a hash
|
||||||
|
FileDeleteMarker MPQFileFlag = 0x02000000
|
||||||
|
// FileSEctorCrc - File has checksums for each sector. Ignored if file is not compressed or imploded.
|
||||||
|
FileSEctorCrc MPQFileFlag = 0x04000000
|
||||||
|
// MpqFileExists - Set if file exists, reset when the file was deleted
|
||||||
|
MpqFileExists MPQFileFlag = 0x80000000
|
||||||
|
)
|
||||||
|
|
||||||
|
// MPQBlockTableEntry represents an entry in the block table
|
||||||
|
type MPQBlockTableEntry struct { // 16 bytes
|
||||||
|
FilePosition uint32
|
||||||
|
CompressedFileSize uint32
|
||||||
|
UncompressedFileSize uint32
|
||||||
|
Flags MPQFileFlag
|
||||||
|
// Local Stuff...
|
||||||
|
FileName string
|
||||||
|
EncryptionSeed uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasFlag returns true if the specified flag is present
|
||||||
|
func (v MPQBlockTableEntry) HasFlag(flag MPQFileFlag) bool {
|
||||||
|
return (v.Flags & flag) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadMPQ loads an MPQ file and returns a MPQ structure
|
||||||
|
func LoadMPQ(fileName string) (MPQ, error) {
|
||||||
|
result := MPQ{}
|
||||||
|
file, err := os.Open(fileName)
|
||||||
|
if err != nil {
|
||||||
|
return MPQ{}, err
|
||||||
|
}
|
||||||
|
result.File = file
|
||||||
|
err = result.readHeader()
|
||||||
|
if err != nil {
|
||||||
|
return MPQ{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *MPQ) readHeader() error {
|
||||||
|
err := binary.Read(v.File, binary.LittleEndian, &v.Data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if string(v.Data.Magic[:]) != "MPQ\x1A" {
|
||||||
|
return errors.New("invalid mpq header")
|
||||||
|
}
|
||||||
|
v.loadHashTable()
|
||||||
|
v.loadBlockTable()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *MPQ) loadHashTable() {
|
||||||
|
v.File.Seek(int64(v.Data.HashTableOffset), 0)
|
||||||
|
hashData := make([]uint32, v.Data.HashTableEntries*4)
|
||||||
|
binary.Read(v.File, binary.LittleEndian, &hashData)
|
||||||
|
decrypt(hashData, hashString("(hash table)", 3))
|
||||||
|
for i := uint32(0); i < v.Data.HashTableEntries; i++ {
|
||||||
|
v.HashTableEntries = append(v.HashTableEntries, MPQHashTableEntry{
|
||||||
|
NamePartA: hashData[i*4],
|
||||||
|
NamePartB: hashData[(i*4)+1],
|
||||||
|
// TODO: Verify that we're grabbing the right high/lo word for the vars below
|
||||||
|
Locale: uint16(hashData[(i*4)+2] >> 16),
|
||||||
|
Platform: uint16(hashData[(i*4)+2] & 0xFFFF),
|
||||||
|
BlockIndex: hashData[(i*4)+3],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *MPQ) loadBlockTable() {
|
||||||
|
v.File.Seek(int64(v.Data.BlockTableOffset), 0)
|
||||||
|
blockData := make([]uint32, v.Data.BlockTableEntries*4)
|
||||||
|
binary.Read(v.File, binary.LittleEndian, &blockData)
|
||||||
|
decrypt(blockData, hashString("(block table)", 3))
|
||||||
|
for i := uint32(0); i < v.Data.BlockTableEntries; i++ {
|
||||||
|
v.BlockTableEntries = append(v.BlockTableEntries, MPQBlockTableEntry{
|
||||||
|
FilePosition: blockData[(i * 4)],
|
||||||
|
CompressedFileSize: blockData[(i*4)+1],
|
||||||
|
UncompressedFileSize: blockData[(i*4)+2],
|
||||||
|
Flags: MPQFileFlag(blockData[(i*4)+3]),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func decrypt(data []uint32, seed uint32) {
|
||||||
|
seed2 := uint32(0xeeeeeeee)
|
||||||
|
|
||||||
|
for i := 0; i < len(data); i++ {
|
||||||
|
seed2 += CryptoBuffer[0x400+(seed&0xff)]
|
||||||
|
result := data[i]
|
||||||
|
result ^= seed + seed2
|
||||||
|
|
||||||
|
seed = ((^seed << 21) + 0x11111111) | (seed >> 11)
|
||||||
|
seed2 = result + seed2 + (seed2 << 5) + 3
|
||||||
|
data[i] = result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func decryptBytes(data []byte, seed uint32) {
|
||||||
|
seed2 := uint32(0xEEEEEEEE)
|
||||||
|
for i := 0; i < len(data)-3; i += 4 {
|
||||||
|
seed2 += CryptoBuffer[0x400+(seed&0xFF)]
|
||||||
|
result := binary.LittleEndian.Uint32(data[i : i+4])
|
||||||
|
result ^= seed + seed2
|
||||||
|
seed = ((^seed << 21) + 0x11111111) | (seed >> 11)
|
||||||
|
seed2 = result + seed2 + (seed2 << 5) + 3
|
||||||
|
|
||||||
|
data[i+0] = uint8(result & 0xff)
|
||||||
|
data[i+1] = uint8((result >> 8) & 0xff)
|
||||||
|
data[i+2] = uint8((result >> 16) & 0xff)
|
||||||
|
data[i+3] = uint8((result >> 24) & 0xff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func hashString(key string, hashType uint32) uint32 {
|
||||||
|
|
||||||
|
seed1 := uint32(0x7FED7FED)
|
||||||
|
seed2 := uint32(0xEEEEEEEE)
|
||||||
|
|
||||||
|
/* prepare seeds. */
|
||||||
|
for _, char := range strings.ToUpper(key) {
|
||||||
|
seed1 = CryptoBuffer[(hashType*0x100)+uint32(char)] ^ (seed1 + seed2)
|
||||||
|
seed2 = uint32(char) + seed1 + seed2 + (seed2 << 5) + 3
|
||||||
|
}
|
||||||
|
return seed1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v MPQ) getFileHashEntry(fileName string) (MPQHashTableEntry, error) {
|
||||||
|
hashA := hashString(fileName, 1)
|
||||||
|
hashB := hashString(fileName, 2)
|
||||||
|
|
||||||
|
for idx, hashEntry := range v.HashTableEntries {
|
||||||
|
if hashEntry.NamePartA != hashA || hashEntry.NamePartB != hashB {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
return v.HashTableEntries[idx], nil
|
||||||
|
}
|
||||||
|
return MPQHashTableEntry{}, errors.New("file not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v MPQ) getFileBlockData(fileName string) (MPQBlockTableEntry, error) {
|
||||||
|
fileEntry, err := v.getFileHashEntry(fileName)
|
||||||
|
if err != nil {
|
||||||
|
return MPQBlockTableEntry{}, err
|
||||||
|
}
|
||||||
|
return v.BlockTableEntries[fileEntry.BlockIndex], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closses the MPQ file
|
||||||
|
func (v *MPQ) Close() {
|
||||||
|
v.File.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadFile reads a file from the MPQ and returns a memory stream
|
||||||
|
func (v MPQ) ReadFile(fileName string) ([]byte, error) {
|
||||||
|
fileBlockData, err := v.getFileBlockData(fileName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
fileBlockData.FileName = strings.ToLower(fileName)
|
||||||
|
fileBlockData.calculateEncryptionSeed()
|
||||||
|
mpqStream := CreateMPQStream(v, fileBlockData, fileName)
|
||||||
|
buffer := make([]byte, fileBlockData.UncompressedFileSize)
|
||||||
|
mpqStream.Read(buffer, 0, fileBlockData.UncompressedFileSize)
|
||||||
|
return buffer, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadTextFile reads a file and returns it as a string
|
||||||
|
func (v MPQ) ReadTextFile(fileName string) (string, error) {
|
||||||
|
data, err := v.ReadFile(fileName)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return string(data), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *MPQBlockTableEntry) calculateEncryptionSeed() {
|
||||||
|
fileName := path.Base(v.FileName)
|
||||||
|
v.EncryptionSeed = hashString(fileName, 3)
|
||||||
|
if !v.HasFlag(MpqFileFixKey) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
v.EncryptionSeed = (v.EncryptionSeed + v.FilePosition) ^ v.UncompressedFileSize
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFileList returns the list of files in this MPQ
|
||||||
|
func (v MPQ) GetFileList() ([]string, error) {
|
||||||
|
data, err := v.ReadFile("(listfile)")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
log.Printf("File Contents:\n%s", strings.TrimRight(string(data), "\x00"))
|
||||||
|
data = nil
|
||||||
|
return []string{""}, nil
|
||||||
|
}
|
34
OpenDiablo2/MPQFont.go
Normal file
34
OpenDiablo2/MPQFont.go
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
package OpenDiablo2
|
||||||
|
|
||||||
|
type MPQFontSize struct {
|
||||||
|
Width uint8
|
||||||
|
Height uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
type MPQFont struct {
|
||||||
|
Engine *Engine
|
||||||
|
FontSprite Sprite
|
||||||
|
Metrics map[uint8]MPQFontSize
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateMPQFont creates an instance of a MPQ Font
|
||||||
|
func CreateMPQFont(engine *Engine, font string, palette Palette) *MPQFont {
|
||||||
|
result := &MPQFont{
|
||||||
|
Engine: engine,
|
||||||
|
Metrics: make(map[uint8]MPQFontSize),
|
||||||
|
}
|
||||||
|
result.FontSprite = result.Engine.LoadSprite(font+".dc6", palette)
|
||||||
|
woo := "Woo!\x01"
|
||||||
|
fontData := result.Engine.GetFile(font + ".tbl")
|
||||||
|
if string(fontData[0:5]) != woo {
|
||||||
|
panic("No woo :(")
|
||||||
|
}
|
||||||
|
for i := 12; i < len(fontData); i += 14 {
|
||||||
|
fontSize := MPQFontSize{
|
||||||
|
Width: fontData[i+3],
|
||||||
|
Height: fontData[i+4],
|
||||||
|
}
|
||||||
|
result.Metrics[fontData[i+8]] = fontSize
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
229
OpenDiablo2/MPQStream.go
Normal file
229
OpenDiablo2/MPQStream.go
Normal file
@ -0,0 +1,229 @@
|
|||||||
|
package OpenDiablo2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"compress/zlib"
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/JoshVarga/blast"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MPQStream represents a stream of data in an MPQ archive
|
||||||
|
type MPQStream struct {
|
||||||
|
MPQData MPQ
|
||||||
|
BlockTableEntry MPQBlockTableEntry
|
||||||
|
FileName string
|
||||||
|
EncryptionSeed uint32
|
||||||
|
BlockPositions []uint32
|
||||||
|
CurrentPosition uint32
|
||||||
|
CurrentData []byte
|
||||||
|
CurrentBlockIndex uint32
|
||||||
|
BlockSize uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateMPQStream creates an MPQ stream
|
||||||
|
func CreateMPQStream(mpq MPQ, blockTableEntry MPQBlockTableEntry, fileName string) MPQStream {
|
||||||
|
result := MPQStream{
|
||||||
|
MPQData: mpq,
|
||||||
|
BlockTableEntry: blockTableEntry,
|
||||||
|
CurrentBlockIndex: 0xFFFFFFFF,
|
||||||
|
}
|
||||||
|
result.EncryptionSeed = hashString(fileName, 3)
|
||||||
|
if result.BlockTableEntry.HasFlag(MpqFileFixKey) {
|
||||||
|
result.EncryptionSeed = (result.EncryptionSeed + result.BlockTableEntry.FilePosition) ^ result.BlockTableEntry.UncompressedFileSize
|
||||||
|
}
|
||||||
|
result.BlockSize = 0x200 << result.MPQData.Data.BlockSize
|
||||||
|
|
||||||
|
if (result.BlockTableEntry.HasFlag(MpqFileCompress) || result.BlockTableEntry.HasFlag(MpqFileImplode)) && !result.BlockTableEntry.HasFlag(MpqFileSingleUnit) {
|
||||||
|
result.loadBlockOffsets()
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *MPQStream) loadBlockOffsets() {
|
||||||
|
blockPositionCount := ((v.BlockTableEntry.UncompressedFileSize + v.BlockSize - 1) / v.BlockSize) + 1
|
||||||
|
v.BlockPositions = make([]uint32, blockPositionCount)
|
||||||
|
v.MPQData.File.Seek(int64(v.BlockTableEntry.FilePosition), 0)
|
||||||
|
binary.Read(v.MPQData.File, binary.LittleEndian, &v.BlockPositions)
|
||||||
|
blockPosSize := blockPositionCount << 2
|
||||||
|
if v.BlockTableEntry.HasFlag(MpqFileEncrypted) {
|
||||||
|
decrypt(v.BlockPositions, v.EncryptionSeed-1)
|
||||||
|
if v.BlockPositions[0] != blockPosSize {
|
||||||
|
panic("Decryption of MPQ failed!")
|
||||||
|
}
|
||||||
|
if v.BlockPositions[1] > v.BlockSize+blockPosSize {
|
||||||
|
panic("Decryption of MPQ failed!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *MPQStream) Read(buffer []byte, offset, count uint32) uint32 {
|
||||||
|
if v.BlockTableEntry.HasFlag(MpqFileSingleUnit) {
|
||||||
|
return v.readInternalSingleUnit(buffer, offset, count)
|
||||||
|
}
|
||||||
|
toRead := count
|
||||||
|
readTotal := uint32(0)
|
||||||
|
for toRead > 0 {
|
||||||
|
read := v.readInternal(buffer, offset, count)
|
||||||
|
if read == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
readTotal += read
|
||||||
|
offset += read
|
||||||
|
toRead -= read
|
||||||
|
}
|
||||||
|
return readTotal
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *MPQStream) readInternalSingleUnit(buffer []byte, offset, count uint32) uint32 {
|
||||||
|
if len(v.CurrentData) == 0 {
|
||||||
|
v.loadSingleUnit()
|
||||||
|
}
|
||||||
|
|
||||||
|
bytesToCopy := Min(uint32(len(v.CurrentData))-v.CurrentPosition, count)
|
||||||
|
copy(buffer[offset:offset+bytesToCopy], v.CurrentData[v.CurrentPosition:v.CurrentPosition+bytesToCopy])
|
||||||
|
v.CurrentPosition += bytesToCopy
|
||||||
|
return bytesToCopy
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *MPQStream) readInternal(buffer []byte, offset, count uint32) uint32 {
|
||||||
|
v.bufferData()
|
||||||
|
localPosition := v.CurrentPosition % v.BlockSize
|
||||||
|
bytesToCopy := Min(uint32(len(v.CurrentData))-localPosition, count)
|
||||||
|
if bytesToCopy <= 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
copy(buffer[offset:offset+bytesToCopy], v.CurrentData[localPosition:localPosition+bytesToCopy])
|
||||||
|
v.CurrentPosition += bytesToCopy
|
||||||
|
return bytesToCopy
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *MPQStream) bufferData() {
|
||||||
|
requiredBlock := uint32(v.CurrentPosition / v.BlockSize)
|
||||||
|
if requiredBlock == v.CurrentBlockIndex {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
expectedLength := Min(v.BlockTableEntry.UncompressedFileSize-(requiredBlock*v.BlockSize), v.BlockSize)
|
||||||
|
v.CurrentData = v.loadBlock(requiredBlock, expectedLength)
|
||||||
|
v.CurrentBlockIndex = requiredBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *MPQStream) loadSingleUnit() {
|
||||||
|
fileData := make([]byte, v.BlockSize)
|
||||||
|
v.MPQData.File.Seek(int64(v.MPQData.Data.HeaderSize), 0)
|
||||||
|
binary.Read(v.MPQData.File, binary.LittleEndian, &fileData)
|
||||||
|
if v.BlockSize == v.BlockTableEntry.UncompressedFileSize {
|
||||||
|
v.CurrentData = fileData
|
||||||
|
return
|
||||||
|
}
|
||||||
|
v.CurrentData = decompressMulti(fileData, v.BlockTableEntry.UncompressedFileSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *MPQStream) loadBlock(blockIndex, expectedLength uint32) []byte {
|
||||||
|
var (
|
||||||
|
offset uint32
|
||||||
|
toRead uint32
|
||||||
|
)
|
||||||
|
if v.BlockTableEntry.HasFlag(MpqFileCompress) || v.BlockTableEntry.HasFlag(MpqFileImplode) {
|
||||||
|
offset = v.BlockPositions[blockIndex]
|
||||||
|
toRead = v.BlockPositions[blockIndex+1] - offset
|
||||||
|
} else {
|
||||||
|
offset = blockIndex * v.BlockSize
|
||||||
|
toRead = expectedLength
|
||||||
|
}
|
||||||
|
offset += v.BlockTableEntry.FilePosition
|
||||||
|
data := make([]byte, toRead)
|
||||||
|
v.MPQData.File.Seek(int64(offset), 0)
|
||||||
|
binary.Read(v.MPQData.File, binary.LittleEndian, &data)
|
||||||
|
if v.BlockTableEntry.HasFlag(MpqFileEncrypted) && v.BlockTableEntry.UncompressedFileSize > 3 {
|
||||||
|
if v.EncryptionSeed == 0 {
|
||||||
|
panic("Unable to determine encryption key")
|
||||||
|
}
|
||||||
|
|
||||||
|
decryptBytes(data, blockIndex+v.EncryptionSeed)
|
||||||
|
}
|
||||||
|
if v.BlockTableEntry.HasFlag(MpqFileCompress) && (toRead != expectedLength) {
|
||||||
|
if !v.BlockTableEntry.HasFlag(MpqFileSingleUnit) {
|
||||||
|
data = decompressMulti(data, expectedLength)
|
||||||
|
} else {
|
||||||
|
data = pkDecompress(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if v.BlockTableEntry.HasFlag(MpqFileImplode) && (toRead != expectedLength) {
|
||||||
|
data = pkDecompress(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
func decompressMulti(data []byte, expectedLength uint32) []byte {
|
||||||
|
copmressionType := data[0]
|
||||||
|
switch copmressionType {
|
||||||
|
case 1: // Huffman
|
||||||
|
panic("huffman decompression not supported")
|
||||||
|
case 2: // ZLib/Deflate
|
||||||
|
return deflate(data[1:])
|
||||||
|
case 8: // PKLib/Impode
|
||||||
|
return pkDecompress(data[1:])
|
||||||
|
case 0x10: // BZip2
|
||||||
|
panic("bzip2 decompression not supported")
|
||||||
|
case 0x80: // IMA ADPCM Stereo
|
||||||
|
//return MpqWavCompression.Decompress(sinput, 2);
|
||||||
|
panic("ima adpcm sterio decompression not supported")
|
||||||
|
case 0x40: // IMA ADPCM Mono
|
||||||
|
//return MpqWavCompression.Decompress(sinput, 1)
|
||||||
|
panic("mpq wav decompression not supported")
|
||||||
|
case 0x12:
|
||||||
|
panic("lzma decompression not supported")
|
||||||
|
// Combos
|
||||||
|
case 0x22:
|
||||||
|
// TODO: sparse then zlib
|
||||||
|
panic("sparse decompression + deflate decompression not supported")
|
||||||
|
case 0x30:
|
||||||
|
// TODO: sparse then bzip2
|
||||||
|
panic("sparse decompression + bzip2 decompression not supported")
|
||||||
|
case 0x41:
|
||||||
|
//sinput = MpqHuffman.Decompress(sinput);
|
||||||
|
//return MpqWavCompression.Decompress(sinput, 1);
|
||||||
|
panic("mpqhuffman decompression not supported")
|
||||||
|
case 0x48:
|
||||||
|
//byte[] result = PKDecompress(sinput, outputLength);
|
||||||
|
//return MpqWavCompression.Decompress(new MemoryStream(result), 1);
|
||||||
|
panic("pk + mpqwav decompression not supported")
|
||||||
|
case 0x81:
|
||||||
|
//sinput = MpqHuffman.Decompress(sinput);
|
||||||
|
//return MpqWavCompression.Decompress(sinput, 2);
|
||||||
|
panic("huff + mpqwav decompression not supported")
|
||||||
|
case 0x88:
|
||||||
|
//byte[] result = PKDecompress(sinput, outputLength);
|
||||||
|
//return MpqWavCompression.Decompress(new MemoryStream(result), 2);
|
||||||
|
panic("pk + wav decompression not supported")
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("decompression not supported for unknown compression type %X", copmressionType))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func deflate(data []byte) []byte {
|
||||||
|
b := bytes.NewReader(data)
|
||||||
|
r, err := zlib.NewReader(b)
|
||||||
|
defer r.Close()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
buffer := new(bytes.Buffer)
|
||||||
|
buffer.ReadFrom(r)
|
||||||
|
return buffer.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
func pkDecompress(data []byte) []byte {
|
||||||
|
b := bytes.NewReader(data)
|
||||||
|
r, err := blast.NewReader(b)
|
||||||
|
defer r.Close()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
buffer := new(bytes.Buffer)
|
||||||
|
buffer.ReadFrom(r)
|
||||||
|
return buffer.Bytes()
|
||||||
|
}
|
31
OpenDiablo2/Math.go
Normal file
31
OpenDiablo2/Math.go
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package OpenDiablo2
|
||||||
|
|
||||||
|
// Min returns the lower of two values
|
||||||
|
func Min(a, b uint32) uint32 {
|
||||||
|
if a < b {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// Max returns the higher of two values
|
||||||
|
func Max(a, b uint32) uint32 {
|
||||||
|
if a > b {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// Max returns the higher of two values
|
||||||
|
func MaxInt32(a, b int32) int32 {
|
||||||
|
if a > b {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// BytesToInt32 converts 4 bytes to int32
|
||||||
|
func BytesToInt32(b []byte) int32 {
|
||||||
|
// equivalnt of return int32(binary.LittleEndian.Uint32(b))
|
||||||
|
return int32(uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24)
|
||||||
|
}
|
26
OpenDiablo2/Palette.go
Normal file
26
OpenDiablo2/Palette.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package OpenDiablo2
|
||||||
|
|
||||||
|
type PaletteRGB struct {
|
||||||
|
R, G, B uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
// Palette represents a palette
|
||||||
|
type Palette struct {
|
||||||
|
Name string
|
||||||
|
Colors [256]PaletteRGB
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreatePalette creates a palette
|
||||||
|
func CreatePalette(name string, data []byte) Palette {
|
||||||
|
result := Palette{Name: name}
|
||||||
|
|
||||||
|
for i := 0; i <= 255; i++ {
|
||||||
|
result.Colors[i] = PaletteRGB{
|
||||||
|
B: data[i*3],
|
||||||
|
G: data[(i*3)+1],
|
||||||
|
R: data[(i*3)+2],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
247
OpenDiablo2/ResourcePaths/ResourcePaths.go
Normal file
247
OpenDiablo2/ResourcePaths/ResourcePaths.go
Normal file
@ -0,0 +1,247 @@
|
|||||||
|
package ResourcePaths
|
||||||
|
|
||||||
|
const (
|
||||||
|
// --- Screens ---
|
||||||
|
|
||||||
|
LoadingScreen = "/data/global/ui/Loading/loadingscreen.dc6"
|
||||||
|
|
||||||
|
// --- Main Menu ---
|
||||||
|
|
||||||
|
TrademarkScreen = "/data/global/ui/FrontEnd/trademarkscreenEXP.dc6"
|
||||||
|
GameSelectScreen = "/data/global/ui/FrontEnd/gameselectscreenEXP.dc6"
|
||||||
|
Diablo2LogoFireLeft = "/data/global/ui/FrontEnd/D2logoFireLeft.DC6"
|
||||||
|
Diablo2LogoFireRight = "/data/global/ui/FrontEnd/D2logoFireRight.DC6"
|
||||||
|
Diablo2LogoBlackLeft = "/data/global/ui/FrontEnd/D2logoBlackLeft.DC6"
|
||||||
|
Diablo2LogoBlackRight = "/data/global/ui/FrontEnd/D2logoBlackRight.DC6"
|
||||||
|
|
||||||
|
// --- Credits ---
|
||||||
|
|
||||||
|
CreditsBackground = "/data/global/ui/CharSelect/creditsbckgexpand.dc6"
|
||||||
|
CreditsText = "/data/local/ui/eng/ExpansionCredits.txt"
|
||||||
|
|
||||||
|
// --- Character Select Screen ---
|
||||||
|
|
||||||
|
CharacterSelectBackground = "/data/global/ui/FrontEnd/charactercreationscreenEXP.dc6"
|
||||||
|
CharacterSelectCampfire = "/data/global/ui/FrontEnd/fire.DC6"
|
||||||
|
|
||||||
|
CharacterSelectBarbarianUnselected = "/data/global/ui/FrontEnd/barbarian/banu1.DC6"
|
||||||
|
CharacterSelectBarbarianUnselectedH = "/data/global/ui/FrontEnd/barbarian/banu2.DC6"
|
||||||
|
CharacterSelectBarbarianSelected = "/data/global/ui/FrontEnd/barbarian/banu3.DC6"
|
||||||
|
CharacterSelectBarbarianForwardWalk = "/data/global/ui/FrontEnd/barbarian/bafw.DC6"
|
||||||
|
CharacterSelectBarbarianForwardWalkOverlay = "/data/global/ui/FrontEnd/barbarian/BAFWs.DC6"
|
||||||
|
CharacterSelectBarbarianBackWalk = "/data/global/ui/FrontEnd/barbarian/babw.DC6"
|
||||||
|
|
||||||
|
CharacterSelecSorceressUnselected = "/data/global/ui/FrontEnd/sorceress/SONU1.DC6"
|
||||||
|
CharacterSelecSorceressUnselectedH = "/data/global/ui/FrontEnd/sorceress/SONU2.DC6"
|
||||||
|
CharacterSelecSorceressSelected = "/data/global/ui/FrontEnd/sorceress/SONU3.DC6"
|
||||||
|
CharacterSelecSorceressSelectedOverlay = "/data/global/ui/FrontEnd/sorceress/SONU3s.DC6"
|
||||||
|
CharacterSelecSorceressForwardWalk = "/data/global/ui/FrontEnd/sorceress/SOFW.DC6"
|
||||||
|
CharacterSelecSorceressForwardWalkOverlay = "/data/global/ui/FrontEnd/sorceress/SOFWs.DC6"
|
||||||
|
CharacterSelecSorceressBackWalk = "/data/global/ui/FrontEnd/sorceress/SOBW.DC6"
|
||||||
|
CharacterSelecSorceressBackWalkOverlay = "/data/global/ui/FrontEnd/sorceress/SOBWs.DC6"
|
||||||
|
|
||||||
|
CharacterSelectNecromancerUnselected = "/data/global/ui/FrontEnd/necromancer/NENU1.DC6"
|
||||||
|
CharacterSelectNecromancerUnselectedH = "/data/global/ui/FrontEnd/necromancer/NENU2.DC6"
|
||||||
|
CharacterSelecNecromancerSelected = "/data/global/ui/FrontEnd/necromancer/NENU3.DC6"
|
||||||
|
CharacterSelecNecromancerSelectedOverlay = "/data/global/ui/FrontEnd/necromancer/NENU3s.DC6"
|
||||||
|
CharacterSelecNecromancerForwardWalk = "/data/global/ui/FrontEnd/necromancer/NEFW.DC6"
|
||||||
|
CharacterSelecNecromancerForwardWalkOverlay = "/data/global/ui/FrontEnd/necromancer/NEFWs.DC6"
|
||||||
|
CharacterSelecNecromancerBackWalk = "/data/global/ui/FrontEnd/necromancer/NEBW.DC6"
|
||||||
|
CharacterSelecNecromancerBackWalkOverlay = "/data/global/ui/FrontEnd/necromancer/NEBWs.DC6"
|
||||||
|
|
||||||
|
CharacterSelectPaladinUnselected = "/data/global/ui/FrontEnd/paladin/PANU1.DC6"
|
||||||
|
CharacterSelectPaladinUnselectedH = "/data/global/ui/FrontEnd/paladin/PANU2.DC6"
|
||||||
|
CharacterSelecPaladinSelected = "/data/global/ui/FrontEnd/paladin/PANU3.DC6"
|
||||||
|
CharacterSelecPaladinForwardWalk = "/data/global/ui/FrontEnd/paladin/PAFW.DC6"
|
||||||
|
CharacterSelecPaladinForwardWalkOverlay = "/data/global/ui/FrontEnd/paladin/PAFWs.DC6"
|
||||||
|
CharacterSelecPaladinBackWalk = "/data/global/ui/FrontEnd/paladin/PABW.DC6"
|
||||||
|
|
||||||
|
CharacterSelectAmazonUnselected = "/data/global/ui/FrontEnd/amazon/AMNU1.DC6"
|
||||||
|
CharacterSelectAmazonUnselectedH = "/data/global/ui/FrontEnd/amazon/AMNU2.DC6"
|
||||||
|
CharacterSelecAmazonSelected = "/data/global/ui/FrontEnd/amazon/AMNU3.DC6"
|
||||||
|
CharacterSelecAmazonForwardWalk = "/data/global/ui/FrontEnd/amazon/AMFW.DC6"
|
||||||
|
CharacterSelecAmazonForwardWalkOverlay = "/data/global/ui/FrontEnd/amazon/AMFWs.DC6"
|
||||||
|
CharacterSelecAmazonBackWalk = "/data/global/ui/FrontEnd/amazon/AMBW.DC6"
|
||||||
|
|
||||||
|
CharacterSelectAssassinUnselected = "/data/global/ui/FrontEnd/assassin/ASNU1.DC6"
|
||||||
|
CharacterSelectAssassinUnselectedH = "/data/global/ui/FrontEnd/assassin/ASNU2.DC6"
|
||||||
|
CharacterSelectAssassinSelected = "/data/global/ui/FrontEnd/assassin/ASNU3.DC6"
|
||||||
|
CharacterSelectAssassinForwardWalk = "/data/global/ui/FrontEnd/assassin/ASFW.DC6"
|
||||||
|
CharacterSelectAssassinBackWalk = "/data/global/ui/FrontEnd/assassin/ASBW.DC6"
|
||||||
|
|
||||||
|
CharacterSelectDruidUnselected = "/data/global/ui/FrontEnd/druid/DZNU1.dc6"
|
||||||
|
CharacterSelectDruidUnselectedH = "/data/global/ui/FrontEnd/druid/DZNU2.dc6"
|
||||||
|
CharacterSelectDruidSelected = "/data/global/ui/FrontEnd/druid/DZNU3.DC6"
|
||||||
|
CharacterSelectDruidForwardWalk = "/data/global/ui/FrontEnd/druid/DZFW.DC6"
|
||||||
|
CharacterSelectDruidBackWalk = "/data/global/ui/FrontEnd/druid/DZBW.DC6"
|
||||||
|
|
||||||
|
// -- Character Selection
|
||||||
|
|
||||||
|
CharacterSelectionBackground = "/data/global/ui/CharSelect/characterselectscreenEXP.dc6"
|
||||||
|
|
||||||
|
// --- Game ---
|
||||||
|
|
||||||
|
GamePanels = "/data/global/ui/PANEL/800ctrlpnl7.dc6"
|
||||||
|
GameGlobeOverlap = "/data/global/ui/PANEL/overlap.DC6"
|
||||||
|
HealthMana = "/data/global/ui/PANEL/hlthmana.DC6"
|
||||||
|
GameSmallMenuButton = "/data/global/ui/PANEL/menubutton.DC6" // TODO: Used for inventory popout
|
||||||
|
SkillIcon = "/data/global/ui/PANEL/Skillicon.DC6" // TODO: Used for skill icon button
|
||||||
|
AddSkillButton = "/data/global/ui/PANEL/level.DC6"
|
||||||
|
|
||||||
|
// --- Mouse Pointers ---
|
||||||
|
|
||||||
|
CursorDefault = "/data/global/ui/CURSOR/ohand.DC6"
|
||||||
|
|
||||||
|
// --- Fonts ---
|
||||||
|
|
||||||
|
Font6 = "/data/local/font/latin/font6"
|
||||||
|
Font8 = "/data/local/font/latin/font8"
|
||||||
|
Font16 = "/data/local/font/latin/font16"
|
||||||
|
Font24 = "/data/local/font/latin/font24"
|
||||||
|
Font30 = "/data/local/font/latin/font30"
|
||||||
|
FontFormal12 = "/data/local/font/latin/fontformal12"
|
||||||
|
FontFormal11 = "/data/local/font/latin/fontformal11"
|
||||||
|
FontFormal10 = "/data/local/font/latin/fontformal10"
|
||||||
|
FontExocet10 = "/data/local/font/latin/fontexocet10"
|
||||||
|
FontExocet8 = "/data/local/font/latin/fontexocet8"
|
||||||
|
|
||||||
|
// --- UI ---
|
||||||
|
|
||||||
|
WideButtonBlank = "/data/global/ui/FrontEnd/WideButtonBlank.dc6"
|
||||||
|
MediumButtonBlank = "/data/global/ui/FrontEnd/MediumButtonBlank.dc6"
|
||||||
|
CancelButton = "/data/global/ui/FrontEnd/CancelButtonBlank.dc6"
|
||||||
|
NarrowButtonBlank = "/data/global/ui/FrontEnd/NarrowButtonBlank.dc6"
|
||||||
|
ShortButtonBlank = "/data/global/ui/CharSelect/ShortButtonBlank.dc6"
|
||||||
|
TextBox2 = "/data/global/ui/FrontEnd/textbox2.dc6"
|
||||||
|
TallButtonBlank = "/data/global/ui/CharSelect/TallButtonBlank.dc6"
|
||||||
|
|
||||||
|
// --- GAME UI ---
|
||||||
|
|
||||||
|
MinipanelSmall = "/data/global/ui/PANEL/minipanel_s.dc6"
|
||||||
|
MinipanelButton = "/data/global/ui/PANEL/minipanelbtn.DC6"
|
||||||
|
|
||||||
|
Frame = "/data/global/ui/PANEL/800borderframe.dc6"
|
||||||
|
InventoryCharacterPanel = "/data/global/ui/PANEL/invchar6.DC6"
|
||||||
|
InventoryWeaponsTab = "/data/global/ui/PANEL/invchar6Tab.DC6"
|
||||||
|
SkillsPanelAmazon = "/data/global/ui/SPELLS/skltree_a_back.DC6"
|
||||||
|
SkillsPanelBarbarian = "/data/global/ui/SPELLS/skltree_b_back.DC6"
|
||||||
|
SkillsPanelDruid = "/data/global/ui/SPELLS/skltree_d_back.DC6"
|
||||||
|
SkillsPanelAssassin = "/data/global/ui/SPELLS/skltree_i_back.DC6"
|
||||||
|
SkillsPanelNecromancer = "/data/global/ui/SPELLS/skltree_n_back.DC6"
|
||||||
|
SkillsPanelPaladin = "/data/global/ui/SPELLS/skltree_p_back.DC6"
|
||||||
|
SkillsPanelSorcerer = "/data/global/ui/SPELLS/skltree_s_back.DC6"
|
||||||
|
|
||||||
|
GenericSkills = "/data/global/ui/SPELLS/Skillicon.DC6"
|
||||||
|
AmazonSkills = "/data/global/ui/SPELLS/AmSkillicon.DC6"
|
||||||
|
BarbarianSkills = "/data/global/ui/SPELLS/BaSkillicon.DC6"
|
||||||
|
DruidSkills = "/data/global/ui/SPELLS/DrSkillicon.DC6"
|
||||||
|
AssassinSkills = "/data/global/ui/SPELLS/AsSkillicon.DC6"
|
||||||
|
NecromancerSkills = "/data/global/ui/SPELLS/NeSkillicon.DC6"
|
||||||
|
PaladinSkills = "/data/global/ui/SPELLS/PaSkillicon.DC6"
|
||||||
|
SorcererSkills = "/data/global/ui/SPELLS/SoSkillicon.DC6"
|
||||||
|
|
||||||
|
RunButton = "/data/global/ui/PANEL/runbutton.dc6"
|
||||||
|
MenuButton = "/data/global/ui/PANEL/menubutton.DC6"
|
||||||
|
GoldCoinButton = "/data/global/ui/panel/goldcoinbtn.dc6"
|
||||||
|
SquareButton = "/data/global/ui/panel/buysellbtn.dc6"
|
||||||
|
|
||||||
|
ArmorPlaceholder = "/data/global/ui/PANEL/inv_armor.DC6"
|
||||||
|
BeltPlaceholder = "/data/global/ui/PANEL/inv_belt.DC6"
|
||||||
|
BootsPlaceholder = "/data/global/ui/PANEL/inv_boots.DC6"
|
||||||
|
HelmGlovePlaceholder = "/data/global/ui/PANEL/inv_helm_glove.DC6"
|
||||||
|
RingAmuletPlaceholder = "/data/global/ui/PANEL/inv_ring_amulet.DC6"
|
||||||
|
WeaponsPlaceholder = "/data/global/ui/PANEL/inv_weapons.DC6"
|
||||||
|
|
||||||
|
// --- Data ---
|
||||||
|
|
||||||
|
EnglishTable = "/data/local/lng/eng/English.txt"
|
||||||
|
ExpansionStringTable = "/data/local/lng/eng/expansionstring.tbl"
|
||||||
|
LevelPreset = "/data/global/excel/LvlPrest.txt"
|
||||||
|
LevelType = "/data/global/excel/LvlTypes.txt"
|
||||||
|
LevelDetails = "/data/global/excel/Levels.txt"
|
||||||
|
ObjectDetails = "/data/global/excel/Objects.txt"
|
||||||
|
SoundSettings = "/data/global/excel/Sounds.txt"
|
||||||
|
|
||||||
|
// --- Animations ---
|
||||||
|
|
||||||
|
ObjectData = "/data/global/objects"
|
||||||
|
AnimationData = "/data/global/animdata.d2"
|
||||||
|
PlayerAnimationBase = "/data/global/CHARS"
|
||||||
|
|
||||||
|
// --- Inventory Data ---
|
||||||
|
|
||||||
|
Weapons = "/data/global/excel/weapons.txt"
|
||||||
|
Armor = "/data/global/excel/armor.txt"
|
||||||
|
Misc = "/data/global/excel/misc.txt"
|
||||||
|
|
||||||
|
// --- Character Data ---
|
||||||
|
|
||||||
|
Experience = "/data/global/excel/experience.txt"
|
||||||
|
CharStats = "/data/global/excel/charstats.txt"
|
||||||
|
|
||||||
|
// --- Music ---
|
||||||
|
|
||||||
|
BGMTitle = "/data/global/music/introedit.wav"
|
||||||
|
BGMOptions = "/data/global/music/Common/options.wav"
|
||||||
|
BGMAct1AndarielAction = "/data/global/music/Act1/andarielaction.wav"
|
||||||
|
BGMAct1BloodRavenResolution = "/data/global/music/Act1/bloodravenresolution.wav"
|
||||||
|
BGMAct1Caves = "/data/global/music/Act1/caves.wav"
|
||||||
|
BGMAct1Crypt = "/data/global/music/Act1/crypt.wav"
|
||||||
|
BGMAct1DenOfEvilAction = "/data/global/music/Act1/denofevilaction.wav"
|
||||||
|
BGMAct1Monastery = "/data/global/music/Act1/monastery.wav"
|
||||||
|
BGMAct1Town1 = "/data/global/music/Act1/town1.wav"
|
||||||
|
BGMAct1Tristram = "/data/global/music/Act1/tristram.wav"
|
||||||
|
BGMAct1Wild = "/data/global/music/Act1/wild.wav"
|
||||||
|
BGMAct2Desert = "/data/global/music/Act2/desert.wav"
|
||||||
|
BGMAct2Harem = "/data/global/music/Act2/harem.wav"
|
||||||
|
BGMAct2HoradricAction = "/data/global/music/Act2/horadricaction.wav"
|
||||||
|
BGMAct2Lair = "/data/global/music/Act2/lair.wav"
|
||||||
|
BGMAct2RadamentResolution = "/data/global/music/Act2/radamentresolution.wav"
|
||||||
|
BGMAct2Sanctuary = "/data/global/music/Act2/sanctuary.wav"
|
||||||
|
BGMAct2Sewer = "/data/global/music/Act2/sewer.wav"
|
||||||
|
BGMAct2TaintedSunAction = "/data/global/music/Act2/taintedsunaction.wav"
|
||||||
|
BGMAct2Tombs = "/data/global/music/Act2/tombs.wav"
|
||||||
|
BGMAct2Town2 = "/data/global/music/Act2/town2.wav"
|
||||||
|
BGMAct2Valley = "/data/global/music/Act2/valley.wav"
|
||||||
|
BGMAct3Jungle = "/data/global/music/Act3/jungle.wav"
|
||||||
|
BGMAct3Kurast = "/data/global/music/Act3/kurast.wav"
|
||||||
|
BGMAct3KurastSewer = "/data/global/music/Act3/kurastsewer.wav"
|
||||||
|
BGMAct3MefDeathAction = "/data/global/music/Act3/mefdeathaction.wav"
|
||||||
|
BGMAct3OrbAction = "/data/global/music/Act3/orbaction.wav"
|
||||||
|
BGMAct3Spider = "/data/global/music/Act3/spider.wav"
|
||||||
|
BGMAct3Town3 = "/data/global/music/Act3/town3.wav"
|
||||||
|
BGMAct4Diablo = "/data/global/music/Act4/diablo.wav"
|
||||||
|
BGMAct4DiabloAction = "/data/global/music/Act4/diabloaction.wav"
|
||||||
|
BGMAct4ForgeAction = "/data/global/music/Act4/forgeaction.wav"
|
||||||
|
BGMAct4IzualAction = "/data/global/music/Act4/izualaction.wav"
|
||||||
|
BGMAct4Mesa = "/data/global/music/Act4/mesa.wav"
|
||||||
|
BGMAct4Town4 = "/data/global/music/Act4/town4.wav"
|
||||||
|
BGMAct5Baal = "/data/global/music/Act5/baal.wav"
|
||||||
|
BGMAct5XTown = "/data/global/music/Act5/xtown.wav"
|
||||||
|
|
||||||
|
// --- Sound Effects ---
|
||||||
|
|
||||||
|
SFXButtonClick = "/data/global/sfx/Cursor/button.wav"
|
||||||
|
SFXAmazonDeselect = "/data/global/sfx/Cursor/intro/amazon deselect.wav"
|
||||||
|
SFXAmazonSelect = "/data/global/sfx/Cursor/intro/amazon select.wav"
|
||||||
|
SFXAssassinDeselect = "/data/global/sfx/Cursor/intro/assassin deselect.wav"
|
||||||
|
SFXAssassinSelect = "/data/global/sfx/Cursor/intro/assassin select.wav"
|
||||||
|
SFXBarbarianDeselect = "/data/global/sfx/Cursor/intro/barbarian deselect.wav"
|
||||||
|
SFXBarbarianSelect = "/data/global/sfx/Cursor/intro/barbarian select.wav"
|
||||||
|
SFXDruidDeselect = "/data/global/sfx/Cursor/intro/druid deselect.wav"
|
||||||
|
SFXDruidSelect = "/data/global/sfx/Cursor/intro/druid select.wav"
|
||||||
|
SFXNecromancerDeselect = "/data/global/sfx/Cursor/intro/necromancer deselect.wav"
|
||||||
|
SFXNecromancerSelect = "/data/global/sfx/Cursor/intro/necromancer select.wav"
|
||||||
|
SFXPaladinDeselect = "/data/global/sfx/Cursor/intro/paladin deselect.wav"
|
||||||
|
SFXPaladinSelect = "/data/global/sfx/Cursor/intro/paladin select.wav"
|
||||||
|
SFXSorceressDeselect = "/data/global/sfx/Cursor/intro/sorceress deselect.wav"
|
||||||
|
SFXSorceressSelect = "/data/global/sfx/Cursor/intro/sorceress select.wav"
|
||||||
|
|
||||||
|
// --- Enemy Data ---
|
||||||
|
|
||||||
|
MonStats = "/data//global//excel//monstats.txt"
|
||||||
|
|
||||||
|
// --- Skill Data ---
|
||||||
|
|
||||||
|
Missiles = "/data//global//excel//missiles.txt"
|
||||||
|
)
|
86
OpenDiablo2/SceneMainMenu.go
Normal file
86
OpenDiablo2/SceneMainMenu.go
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
package OpenDiablo2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"./ResourcePaths"
|
||||||
|
"github.com/hajimehoshi/ebiten"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MainMenu struct {
|
||||||
|
Engine *Engine
|
||||||
|
TrademarkBackground Sprite
|
||||||
|
Background Sprite
|
||||||
|
DiabloLogoLeft Sprite
|
||||||
|
DiabloLogoRight Sprite
|
||||||
|
DiabloLogoLeftBack Sprite
|
||||||
|
DiabloLogoRightBack Sprite
|
||||||
|
CopyrightLabel *UILabel
|
||||||
|
ShowTrademarkScreen bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateMainMenu(engine *Engine) *MainMenu {
|
||||||
|
result := &MainMenu{
|
||||||
|
Engine: engine,
|
||||||
|
ShowTrademarkScreen: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *MainMenu) Load() {
|
||||||
|
go func() {
|
||||||
|
loadStep := 1.0 / 7.0
|
||||||
|
v.Engine.LoadingProgress = 0
|
||||||
|
v.CopyrightLabel = CreateUILabel(v.Engine, ResourcePaths.FontFormal11, "static")
|
||||||
|
v.CopyrightLabel.SetText("Hello, world!")
|
||||||
|
v.CopyrightLabel.MoveTo(0, 0)
|
||||||
|
v.Engine.LoadingProgress += loadStep
|
||||||
|
v.Background = v.Engine.LoadSprite(ResourcePaths.GameSelectScreen, v.Engine.Palettes["sky"])
|
||||||
|
v.Background.MoveTo(0, 0)
|
||||||
|
v.Engine.LoadingProgress += loadStep
|
||||||
|
v.TrademarkBackground = v.Engine.LoadSprite(ResourcePaths.TrademarkScreen, v.Engine.Palettes["sky"])
|
||||||
|
v.TrademarkBackground.MoveTo(0, 0)
|
||||||
|
v.Engine.LoadingProgress += loadStep
|
||||||
|
v.DiabloLogoLeft = v.Engine.LoadSprite(ResourcePaths.Diablo2LogoFireLeft, v.Engine.Palettes["units"])
|
||||||
|
v.DiabloLogoLeft.Blend = true
|
||||||
|
v.DiabloLogoLeft.Animate = true
|
||||||
|
v.DiabloLogoLeft.MoveTo(400, 120)
|
||||||
|
v.Engine.LoadingProgress += loadStep
|
||||||
|
v.DiabloLogoRight = v.Engine.LoadSprite(ResourcePaths.Diablo2LogoFireRight, v.Engine.Palettes["units"])
|
||||||
|
v.DiabloLogoRight.Blend = true
|
||||||
|
v.DiabloLogoRight.Animate = true
|
||||||
|
v.DiabloLogoRight.MoveTo(400, 120)
|
||||||
|
v.Engine.LoadingProgress += loadStep
|
||||||
|
v.DiabloLogoLeftBack = v.Engine.LoadSprite(ResourcePaths.Diablo2LogoBlackLeft, v.Engine.Palettes["units"])
|
||||||
|
v.DiabloLogoLeftBack.MoveTo(400, 120)
|
||||||
|
v.Engine.LoadingProgress += loadStep
|
||||||
|
v.DiabloLogoRightBack = v.Engine.LoadSprite(ResourcePaths.Diablo2LogoBlackRight, v.Engine.Palettes["units"])
|
||||||
|
v.DiabloLogoRightBack.MoveTo(400, 120)
|
||||||
|
v.Engine.LoadingProgress = 1.0
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *MainMenu) Unload() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *MainMenu) Render(screen *ebiten.Image) {
|
||||||
|
if v.ShowTrademarkScreen {
|
||||||
|
v.TrademarkBackground.DrawSegments(screen, 4, 3, 0)
|
||||||
|
} else {
|
||||||
|
v.Background.DrawSegments(screen, 4, 3, 0)
|
||||||
|
}
|
||||||
|
v.DiabloLogoLeftBack.Draw(screen)
|
||||||
|
v.DiabloLogoRightBack.Draw(screen)
|
||||||
|
v.DiabloLogoLeft.Draw(screen)
|
||||||
|
v.DiabloLogoRight.Draw(screen)
|
||||||
|
|
||||||
|
if v.ShowTrademarkScreen {
|
||||||
|
v.CopyrightLabel.Draw(screen)
|
||||||
|
} else {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *MainMenu) Update() {
|
||||||
|
|
||||||
|
}
|
67
OpenDiablo2/SoundEntry.go
Normal file
67
OpenDiablo2/SoundEntry.go
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
package OpenDiablo2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SoundEntry represents a sound entry
|
||||||
|
type SoundEntry struct {
|
||||||
|
Handle string
|
||||||
|
Index int
|
||||||
|
FileName string
|
||||||
|
Volume byte
|
||||||
|
GroupSize uint8
|
||||||
|
Loop bool
|
||||||
|
FadeIn uint8
|
||||||
|
FadeOut uint8
|
||||||
|
DeferInst uint8
|
||||||
|
StopInst uint8
|
||||||
|
Duration uint8
|
||||||
|
Compound int8
|
||||||
|
Reverb bool
|
||||||
|
Falloff uint8
|
||||||
|
Cache uint8
|
||||||
|
AsyncOnly bool
|
||||||
|
Priority uint8
|
||||||
|
Stream uint8
|
||||||
|
Stereo uint8
|
||||||
|
Tracking uint8
|
||||||
|
Solo uint8
|
||||||
|
MusicVol uint8
|
||||||
|
Block1 int
|
||||||
|
Block2 int
|
||||||
|
Block3 int
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateSoundEntry creates a sound entry based on a sound row on sounds.txt
|
||||||
|
func CreateSoundEntry(soundLine string) SoundEntry {
|
||||||
|
props := strings.Split(soundLine, "\t")
|
||||||
|
result := SoundEntry{
|
||||||
|
Handle: props[0],
|
||||||
|
Index: StringToInt(props[1]),
|
||||||
|
FileName: props[2],
|
||||||
|
Volume: StringToUint8(props[3]),
|
||||||
|
GroupSize: StringToUint8(props[4]),
|
||||||
|
Loop: StringToUint8(props[5]) == 1,
|
||||||
|
FadeIn: StringToUint8(props[6]),
|
||||||
|
FadeOut: StringToUint8(props[7]),
|
||||||
|
DeferInst: StringToUint8(props[8]),
|
||||||
|
StopInst: StringToUint8(props[9]),
|
||||||
|
Duration: StringToUint8(props[10]),
|
||||||
|
Compound: StringToInt8(props[11]),
|
||||||
|
Reverb: StringToUint8(props[12]) == 1,
|
||||||
|
Falloff: StringToUint8(props[13]),
|
||||||
|
Cache: StringToUint8(props[14]),
|
||||||
|
AsyncOnly: StringToUint8(props[15]) == 1,
|
||||||
|
Priority: StringToUint8(props[16]),
|
||||||
|
Stream: StringToUint8(props[17]),
|
||||||
|
Stereo: StringToUint8(props[18]),
|
||||||
|
Tracking: StringToUint8(props[19]),
|
||||||
|
Solo: StringToUint8(props[20]),
|
||||||
|
MusicVol: StringToUint8(props[21]),
|
||||||
|
Block1: StringToInt(props[22]),
|
||||||
|
Block2: StringToInt(props[23]),
|
||||||
|
Block3: StringToInt(props[24]),
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
181
OpenDiablo2/Sprite.go
Normal file
181
OpenDiablo2/Sprite.go
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
package OpenDiablo2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/hajimehoshi/ebiten"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Sprite struct {
|
||||||
|
Directions uint32
|
||||||
|
FramesPerDirection uint32
|
||||||
|
Frames []SpriteFrame
|
||||||
|
X, Y int
|
||||||
|
Frame, Direction uint8
|
||||||
|
Blend bool
|
||||||
|
LastFrameTime time.Time
|
||||||
|
Animate bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type SpriteFrame struct {
|
||||||
|
Flip uint32
|
||||||
|
Width uint32
|
||||||
|
Height uint32
|
||||||
|
OffsetX int32
|
||||||
|
OffsetY int32
|
||||||
|
Unknown uint32
|
||||||
|
NextBlock uint32
|
||||||
|
Length uint32
|
||||||
|
ImageData []int16
|
||||||
|
Image *ebiten.Image
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateSprite(data []byte, palette Palette) Sprite {
|
||||||
|
result := Sprite{
|
||||||
|
X: 50,
|
||||||
|
Y: 50,
|
||||||
|
Frame: 0,
|
||||||
|
Direction: 0,
|
||||||
|
Blend: false,
|
||||||
|
Directions: binary.LittleEndian.Uint32(data[16:20]),
|
||||||
|
FramesPerDirection: binary.LittleEndian.Uint32(data[20:24]),
|
||||||
|
Animate: false,
|
||||||
|
LastFrameTime: time.Now(),
|
||||||
|
}
|
||||||
|
dataPointer := uint32(24)
|
||||||
|
totalFrames := result.Directions * result.FramesPerDirection
|
||||||
|
framePointers := make([]uint32, totalFrames)
|
||||||
|
for i := uint32(0); i < totalFrames; i++ {
|
||||||
|
framePointers[i] = binary.LittleEndian.Uint32(data[dataPointer : dataPointer+4])
|
||||||
|
dataPointer += 4
|
||||||
|
}
|
||||||
|
result.Frames = make([]SpriteFrame, totalFrames)
|
||||||
|
for i := uint32(0); i < totalFrames; i++ {
|
||||||
|
dataPointer = framePointers[i]
|
||||||
|
|
||||||
|
result.Frames[i].Flip = binary.LittleEndian.Uint32(data[dataPointer : dataPointer+4])
|
||||||
|
dataPointer += 4
|
||||||
|
result.Frames[i].Width = binary.LittleEndian.Uint32(data[dataPointer : dataPointer+4])
|
||||||
|
dataPointer += 4
|
||||||
|
result.Frames[i].Height = binary.LittleEndian.Uint32(data[dataPointer : dataPointer+4])
|
||||||
|
dataPointer += 4
|
||||||
|
result.Frames[i].OffsetX = BytesToInt32(data[dataPointer : dataPointer+4])
|
||||||
|
dataPointer += 4
|
||||||
|
result.Frames[i].OffsetY = BytesToInt32(data[dataPointer : dataPointer+4])
|
||||||
|
dataPointer += 4
|
||||||
|
result.Frames[i].Unknown = binary.LittleEndian.Uint32(data[dataPointer : dataPointer+4])
|
||||||
|
dataPointer += 4
|
||||||
|
result.Frames[i].NextBlock = binary.LittleEndian.Uint32(data[dataPointer : dataPointer+4])
|
||||||
|
dataPointer += 4
|
||||||
|
result.Frames[i].Length = binary.LittleEndian.Uint32(data[dataPointer : dataPointer+4])
|
||||||
|
dataPointer += 4
|
||||||
|
result.Frames[i].ImageData = make([]int16, result.Frames[i].Width*result.Frames[i].Height)
|
||||||
|
for fi := range result.Frames[i].ImageData {
|
||||||
|
result.Frames[i].ImageData[fi] = -1
|
||||||
|
}
|
||||||
|
|
||||||
|
x := uint32(0)
|
||||||
|
y := uint32(result.Frames[i].Height - 1)
|
||||||
|
for true {
|
||||||
|
b := data[dataPointer]
|
||||||
|
dataPointer++
|
||||||
|
if b == 0x80 {
|
||||||
|
if y == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
y--
|
||||||
|
x = 0
|
||||||
|
} else if (b & 0x80) > 0 {
|
||||||
|
transparentPixels := b & 0x7F
|
||||||
|
for ti := byte(0); ti < transparentPixels; ti++ {
|
||||||
|
result.Frames[i].ImageData[x+(y*result.Frames[i].Width)+uint32(ti)] = -1
|
||||||
|
}
|
||||||
|
x += uint32(transparentPixels)
|
||||||
|
} else {
|
||||||
|
for bi := 0; bi < int(b); bi++ {
|
||||||
|
result.Frames[i].ImageData[x+(y*result.Frames[i].Width)+uint32(bi)] = int16(data[dataPointer])
|
||||||
|
dataPointer++
|
||||||
|
}
|
||||||
|
x += uint32(b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.Frames[i].Image, _ = ebiten.NewImage(int(result.Frames[i].Width), int(result.Frames[i].Height), ebiten.FilterNearest)
|
||||||
|
newData := make([]byte, result.Frames[i].Width*result.Frames[i].Height*4)
|
||||||
|
for ii := uint32(0); ii < result.Frames[i].Width*result.Frames[i].Height; ii++ {
|
||||||
|
if result.Frames[i].ImageData[ii] == -1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
newData[ii*4] = palette.Colors[result.Frames[i].ImageData[ii]].R
|
||||||
|
newData[(ii*4)+1] = palette.Colors[result.Frames[i].ImageData[ii]].G
|
||||||
|
newData[(ii*4)+2] = palette.Colors[result.Frames[i].ImageData[ii]].B
|
||||||
|
newData[(ii*4)+3] = 0xFF
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Frames[i].Image.ReplacePixels(newData)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Sprite) GetSize() (uint32, uint32) {
|
||||||
|
frame := v.Frames[uint32(v.Frame)+(uint32(v.Direction)*v.FramesPerDirection)]
|
||||||
|
return frame.Width, frame.Height
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Sprite) updateAnimation() {
|
||||||
|
if !v.Animate {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tNow := time.Now()
|
||||||
|
if v.LastFrameTime.Add(time.Millisecond * 25).After(tNow) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
v.LastFrameTime = tNow
|
||||||
|
v.Frame++
|
||||||
|
if v.Frame >= uint8(v.FramesPerDirection) {
|
||||||
|
v.Frame = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw draws the sprite onto the target
|
||||||
|
func (v *Sprite) Draw(target *ebiten.Image) {
|
||||||
|
v.updateAnimation()
|
||||||
|
opts := &ebiten.DrawImageOptions{}
|
||||||
|
frame := v.Frames[uint32(v.Frame)+(uint32(v.Direction)*v.FramesPerDirection)]
|
||||||
|
opts.GeoM.Translate(
|
||||||
|
float64(int32(v.X)+frame.OffsetX),
|
||||||
|
float64((int32(v.Y) - int32(frame.Height) + frame.OffsetY)),
|
||||||
|
)
|
||||||
|
if v.Blend {
|
||||||
|
opts.CompositeMode = ebiten.CompositeModeLighter
|
||||||
|
}
|
||||||
|
//opts.ColorM.ChangeHSV(0.0, 1.0, 0.9)
|
||||||
|
target.DrawImage(frame.Image, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Sprite) DrawSegments(target *ebiten.Image, xSegments, ySegments, offset int) {
|
||||||
|
v.updateAnimation()
|
||||||
|
yOffset := int32(0)
|
||||||
|
for y := 0; y < ySegments; y++ {
|
||||||
|
xOffset := int32(0)
|
||||||
|
biggestYOffset := int32(0)
|
||||||
|
for x := 0; x < xSegments; x++ {
|
||||||
|
frame := v.Frames[uint32(x+(y*xSegments)+(offset*xSegments*ySegments))]
|
||||||
|
opts := &ebiten.DrawImageOptions{}
|
||||||
|
opts.GeoM.Translate(
|
||||||
|
float64(int32(v.X)+frame.OffsetX+xOffset),
|
||||||
|
float64(int32(v.Y)+frame.OffsetY+yOffset),
|
||||||
|
)
|
||||||
|
target.DrawImage(frame.Image, opts)
|
||||||
|
xOffset += int32(frame.Width)
|
||||||
|
biggestYOffset = MaxInt32(biggestYOffset, int32(frame.Height))
|
||||||
|
}
|
||||||
|
yOffset += biggestYOffset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MoveTo moves the sprite to the specified coordinates
|
||||||
|
func (v *Sprite) MoveTo(x, y int) {
|
||||||
|
v.X = x
|
||||||
|
v.Y = y
|
||||||
|
}
|
36
OpenDiablo2/StringUtils.go
Normal file
36
OpenDiablo2/StringUtils.go
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package OpenDiablo2
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
// StringToInt converts a string to an integer
|
||||||
|
func StringToInt(text string) int {
|
||||||
|
result, err := strconv.Atoi(text)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringToUint8 converts a string to an uint8
|
||||||
|
func StringToUint8(text string) uint8 {
|
||||||
|
result, err := strconv.Atoi(text)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if result < 0 || result > 255 {
|
||||||
|
panic("value out of range of byte")
|
||||||
|
}
|
||||||
|
return uint8(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringToInt8 converts a string to an int8
|
||||||
|
func StringToInt8(text string) int8 {
|
||||||
|
result, err := strconv.Atoi(text)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if result < -128 || result > 122 {
|
||||||
|
panic("value out of range of a signed byte")
|
||||||
|
}
|
||||||
|
return int8(result)
|
||||||
|
}
|
80
OpenDiablo2/UILabel.go
Normal file
80
OpenDiablo2/UILabel.go
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
package OpenDiablo2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hajimehoshi/ebiten"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UILabel struct {
|
||||||
|
text string
|
||||||
|
X int
|
||||||
|
Y int
|
||||||
|
Width uint32
|
||||||
|
Height uint32
|
||||||
|
font *MPQFont
|
||||||
|
imageData *ebiten.Image
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateUILabel creates a new instance of a UI label
|
||||||
|
func CreateUILabel(engine *Engine, font, palette string) *UILabel {
|
||||||
|
result := &UILabel{
|
||||||
|
font: engine.GetFont(font, palette),
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw draws the label on the screen
|
||||||
|
func (v *UILabel) Draw(target *ebiten.Image) {
|
||||||
|
if len(v.text) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
v.cacheImage()
|
||||||
|
opts := &ebiten.DrawImageOptions{}
|
||||||
|
opts.GeoM.Translate(float64(v.X), float64(v.Y))
|
||||||
|
opts.CompositeMode = ebiten.CompositeModeSourceAtop
|
||||||
|
opts.Filter = ebiten.FilterNearest
|
||||||
|
target.DrawImage(v.imageData, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *UILabel) calculateSize() (uint32, uint32) {
|
||||||
|
width := uint32(0)
|
||||||
|
height := uint32(0)
|
||||||
|
for ch := range v.text {
|
||||||
|
metric := v.font.Metrics[uint8(ch)]
|
||||||
|
width += uint32(metric.Width)
|
||||||
|
height = Max(height, uint32(metric.Height))
|
||||||
|
}
|
||||||
|
return width, height
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *UILabel) MoveTo(x, y int) {
|
||||||
|
v.X = x
|
||||||
|
v.Y = y
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *UILabel) cacheImage() {
|
||||||
|
if v.imageData != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
width, height := v.calculateSize()
|
||||||
|
v.Width = width
|
||||||
|
v.Height = height
|
||||||
|
v.imageData, _ = ebiten.NewImage(int(width), int(height), ebiten.FilterNearest)
|
||||||
|
x := uint32(0)
|
||||||
|
for _, ch := range v.text {
|
||||||
|
char := uint8(ch)
|
||||||
|
metric := v.font.Metrics[char]
|
||||||
|
v.font.FontSprite.Frame = char
|
||||||
|
v.font.FontSprite.MoveTo(v.X+int(x), int(v.Height))
|
||||||
|
v.font.FontSprite.Draw(v.imageData)
|
||||||
|
x += uint32(metric.Width)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *UILabel) SetText(newText string) {
|
||||||
|
if v.text == newText {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
v.text = newText
|
||||||
|
v.imageData = nil
|
||||||
|
}
|
20
config.json
Normal file
20
config.json
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"FullScreen": false,
|
||||||
|
"Scale": 1,
|
||||||
|
"TicksPerSecond": 60,
|
||||||
|
"RunInBackground": true,
|
||||||
|
"VsyncEnabled": true,
|
||||||
|
"MpqPath": "C:/Program Files (x86)/Diablo II",
|
||||||
|
"MpqLoadOrder": [
|
||||||
|
"d2exp.mpq",
|
||||||
|
"d2xmusic.mpq",
|
||||||
|
"d2xtalk.mpq",
|
||||||
|
"d2xvideo.mpq",
|
||||||
|
"d2data.mpq",
|
||||||
|
"d2char.mpq",
|
||||||
|
"d2music.mpq",
|
||||||
|
"d2sfx.mpq",
|
||||||
|
"d2video.mpq",
|
||||||
|
"d2speech.mpq"
|
||||||
|
]
|
||||||
|
}
|
@ -1,44 +0,0 @@
|
|||||||
# Initialize the CMake version stuff
|
|
||||||
cmake_minimum_required(VERSION 3.1...3.13)
|
|
||||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${project_SOURCE_DIR}/cmake")
|
|
||||||
|
|
||||||
if(${CMAKE_VERSION} VERSION_LESS 3.12)
|
|
||||||
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/output)
|
|
||||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/output)
|
|
||||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/output)
|
|
||||||
|
|
||||||
# Add our nifty CMake scripts ------------------------------------------------------------------------------------------
|
|
||||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
|
|
||||||
|
|
||||||
# Prevent users from shooting themselves in the foot -------------------------------------------------------------------
|
|
||||||
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR)
|
|
||||||
message(FATAL_ERROR
|
|
||||||
"Do not build in-source.
|
|
||||||
Please remove CMakeCache.txt and the CMakeFiles/ directory. Then build out-of-source.")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------------------------------------------------------------
|
|
||||||
project(OpenDiablo2 VERSION 0.1
|
|
||||||
DESCRIPTION "An open source Diablo2 engine.")
|
|
||||||
# ----------------------------------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
enable_language(CXX)
|
|
||||||
|
|
||||||
# Freaky relocatable exe stuff -----------------------------------------------------------------------------------------
|
|
||||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON CACHE BOOL "")
|
|
||||||
|
|
||||||
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
|
||||||
|
|
||||||
# Import the actual projects -------------------------------------------------------------------------------------------
|
|
||||||
add_subdirectory(OpenDiablo2.Common)
|
|
||||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src/OpenDiablo2.Common/include)
|
|
||||||
|
|
||||||
add_subdirectory(OpenDiablo2.SDL2)
|
|
||||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src/OpenDiablo2.SDL2/include)
|
|
||||||
|
|
||||||
add_subdirectory(OpenDiablo2.Game)
|
|
||||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src/OpenDiablo2.Game/include)
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
@ -1,87 +0,0 @@
|
|||||||
|
|
||||||
//
|
|
||||||
// Copyright(c) 2018 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
//
|
|
||||||
// Async logging using global thread pool
|
|
||||||
// All loggers created here share same global thread pool.
|
|
||||||
// Each log message is pushed to a queue along withe a shared pointer to the
|
|
||||||
// logger.
|
|
||||||
// If a logger deleted while having pending messages in the queue, it's actual
|
|
||||||
// destruction will defer
|
|
||||||
// until all its messages are processed by the thread pool.
|
|
||||||
// This is because each message in the queue holds a shared_ptr to the
|
|
||||||
// originating logger.
|
|
||||||
|
|
||||||
#include "spdlog/async_logger.h"
|
|
||||||
#include "spdlog/details/registry.h"
|
|
||||||
#include "spdlog/details/thread_pool.h"
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <mutex>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
|
|
||||||
namespace details {
|
|
||||||
static const size_t default_async_q_size = 8192;
|
|
||||||
}
|
|
||||||
|
|
||||||
// async logger factory - creates async loggers backed with thread pool.
|
|
||||||
// if a global thread pool doesn't already exist, create it with default queue
|
|
||||||
// size of 8192 items and single thread.
|
|
||||||
template<async_overflow_policy OverflowPolicy = async_overflow_policy::block>
|
|
||||||
struct async_factory_impl
|
|
||||||
{
|
|
||||||
template<typename Sink, typename... SinkArgs>
|
|
||||||
static std::shared_ptr<async_logger> create(std::string logger_name, SinkArgs &&... args)
|
|
||||||
{
|
|
||||||
auto ®istry_inst = details::registry::instance();
|
|
||||||
|
|
||||||
// create global thread pool if not already exists..
|
|
||||||
std::lock_guard<std::recursive_mutex> tp_lock(registry_inst.tp_mutex());
|
|
||||||
auto tp = registry_inst.get_tp();
|
|
||||||
if (tp == nullptr)
|
|
||||||
{
|
|
||||||
tp = std::make_shared<details::thread_pool>(details::default_async_q_size, 1);
|
|
||||||
registry_inst.set_tp(tp);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto sink = std::make_shared<Sink>(std::forward<SinkArgs>(args)...);
|
|
||||||
auto new_logger = std::make_shared<async_logger>(std::move(logger_name), std::move(sink), std::move(tp), OverflowPolicy);
|
|
||||||
registry_inst.initialize_logger(new_logger);
|
|
||||||
return new_logger;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
using async_factory = async_factory_impl<async_overflow_policy::block>;
|
|
||||||
using async_factory_nonblock = async_factory_impl<async_overflow_policy::overrun_oldest>;
|
|
||||||
|
|
||||||
template<typename Sink, typename... SinkArgs>
|
|
||||||
inline std::shared_ptr<spdlog::logger> create_async(std::string logger_name, SinkArgs &&... sink_args)
|
|
||||||
{
|
|
||||||
return async_factory::create<Sink>(std::move(logger_name), std::forward<SinkArgs>(sink_args)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Sink, typename... SinkArgs>
|
|
||||||
inline std::shared_ptr<spdlog::logger> create_async_nb(std::string logger_name, SinkArgs &&... sink_args)
|
|
||||||
{
|
|
||||||
return async_factory_nonblock::create<Sink>(std::move(logger_name), std::forward<SinkArgs>(sink_args)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
// set global thread pool.
|
|
||||||
inline void init_thread_pool(size_t q_size, size_t thread_count)
|
|
||||||
{
|
|
||||||
auto tp = std::make_shared<details::thread_pool>(q_size, thread_count);
|
|
||||||
details::registry::instance().set_tp(std::move(tp));
|
|
||||||
}
|
|
||||||
|
|
||||||
// get the global thread pool.
|
|
||||||
inline std::shared_ptr<spdlog::details::thread_pool> thread_pool()
|
|
||||||
{
|
|
||||||
return details::registry::instance().get_tp();
|
|
||||||
}
|
|
||||||
} // namespace spdlog
|
|
@ -1,73 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright(c) 2015 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
// Very fast asynchronous logger (millions of logs per second on an average
|
|
||||||
// desktop)
|
|
||||||
// Uses pre allocated lockfree queue for maximum throughput even under large
|
|
||||||
// number of threads.
|
|
||||||
// Creates a single back thread to pop messages from the queue and log them.
|
|
||||||
//
|
|
||||||
// Upon each log write the logger:
|
|
||||||
// 1. Checks if its log level is enough to log the message
|
|
||||||
// 2. Push a new copy of the message to a queue (or block the caller until
|
|
||||||
// space is available in the queue)
|
|
||||||
// 3. will throw spdlog_ex upon log exceptions
|
|
||||||
// Upon destruction, logs all remaining messages in the queue before
|
|
||||||
// destructing..
|
|
||||||
|
|
||||||
#include "spdlog/common.h"
|
|
||||||
#include "spdlog/logger.h"
|
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
|
|
||||||
// Async overflow policy - block by default.
|
|
||||||
enum class async_overflow_policy
|
|
||||||
{
|
|
||||||
block, // Block until message can be enqueued
|
|
||||||
overrun_oldest // Discard oldest message in the queue if full when trying to
|
|
||||||
// add new item.
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace details {
|
|
||||||
class thread_pool;
|
|
||||||
}
|
|
||||||
|
|
||||||
class async_logger final : public std::enable_shared_from_this<async_logger>, public logger
|
|
||||||
{
|
|
||||||
friend class details::thread_pool;
|
|
||||||
|
|
||||||
public:
|
|
||||||
template<typename It>
|
|
||||||
async_logger(std::string logger_name, It begin, It end, std::weak_ptr<details::thread_pool> tp,
|
|
||||||
async_overflow_policy overflow_policy = async_overflow_policy::block);
|
|
||||||
|
|
||||||
async_logger(std::string logger_name, sinks_init_list sinks_list, std::weak_ptr<details::thread_pool> tp,
|
|
||||||
async_overflow_policy overflow_policy = async_overflow_policy::block);
|
|
||||||
|
|
||||||
async_logger(std::string logger_name, sink_ptr single_sink, std::weak_ptr<details::thread_pool> tp,
|
|
||||||
async_overflow_policy overflow_policy = async_overflow_policy::block);
|
|
||||||
|
|
||||||
std::shared_ptr<logger> clone(std::string new_name) override;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void sink_it_(details::log_msg &msg) override;
|
|
||||||
void flush_() override;
|
|
||||||
|
|
||||||
void backend_log_(const details::log_msg &incoming_log_msg);
|
|
||||||
void backend_flush_();
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::weak_ptr<details::thread_pool> thread_pool_;
|
|
||||||
async_overflow_policy overflow_policy_;
|
|
||||||
};
|
|
||||||
} // namespace spdlog
|
|
||||||
|
|
||||||
#include "details/async_logger_impl.h"
|
|
@ -1,246 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright(c) 2015 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "spdlog/tweakme.h"
|
|
||||||
|
|
||||||
#include <atomic>
|
|
||||||
#include <chrono>
|
|
||||||
#include <functional>
|
|
||||||
#include <initializer_list>
|
|
||||||
#include <memory>
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <string>
|
|
||||||
#include <cstring>
|
|
||||||
#include <type_traits>
|
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
#if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT)
|
|
||||||
#include <codecvt>
|
|
||||||
#include <locale>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "spdlog/details/null_mutex.h"
|
|
||||||
|
|
||||||
#include "spdlog/fmt/fmt.h"
|
|
||||||
|
|
||||||
// visual studio upto 2013 does not support noexcept nor constexpr
|
|
||||||
#if defined(_MSC_VER) && (_MSC_VER < 1900)
|
|
||||||
#define SPDLOG_NOEXCEPT throw()
|
|
||||||
#define SPDLOG_CONSTEXPR
|
|
||||||
#else
|
|
||||||
#define SPDLOG_NOEXCEPT noexcept
|
|
||||||
#define SPDLOG_CONSTEXPR constexpr
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(__GNUC__) || defined(__clang__)
|
|
||||||
#define SPDLOG_DEPRECATED __attribute__((deprecated))
|
|
||||||
#elif defined(_MSC_VER)
|
|
||||||
#define SPDLOG_DEPRECATED __declspec(deprecated)
|
|
||||||
#else
|
|
||||||
#define SPDLOG_DEPRECATED
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// disable thread local on msvc 2013
|
|
||||||
#ifndef SPDLOG_NO_TLS
|
|
||||||
#if (defined(_MSC_VER) && (_MSC_VER < 1900)) || defined(__cplusplus_winrt)
|
|
||||||
#define SPDLOG_NO_TLS 1
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Get the basename of __FILE__ (at compile time if possible)
|
|
||||||
#if FMT_HAS_FEATURE(__builtin_strrchr)
|
|
||||||
#define SPDLOG_STRRCHR(str, sep) __builtin_strrchr(str, sep)
|
|
||||||
#else
|
|
||||||
#define SPDLOG_STRRCHR(str, sep) strrchr(str, sep)
|
|
||||||
#endif //__builtin_strrchr not defined
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#define SPDLOG_FILE_BASENAME(file) SPDLOG_STRRCHR("\\" file, '\\') + 1
|
|
||||||
#else
|
|
||||||
#define SPDLOG_FILE_BASENAME(file) SPDLOG_STRRCHR("/" file, '/') + 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef SPDLOG_FUNCTION
|
|
||||||
#define SPDLOG_FUNCTION __FUNCTION__
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
|
|
||||||
class formatter;
|
|
||||||
|
|
||||||
namespace sinks {
|
|
||||||
class sink;
|
|
||||||
}
|
|
||||||
|
|
||||||
using log_clock = std::chrono::system_clock;
|
|
||||||
using sink_ptr = std::shared_ptr<sinks::sink>;
|
|
||||||
using sinks_init_list = std::initializer_list<sink_ptr>;
|
|
||||||
using log_err_handler = std::function<void(const std::string &err_msg)>;
|
|
||||||
|
|
||||||
// string_view type - either std::string_view or fmt::string_view (pre c++17)
|
|
||||||
#if defined(FMT_USE_STD_STRING_VIEW)
|
|
||||||
using string_view_t = std::string_view;
|
|
||||||
#else
|
|
||||||
using string_view_t = fmt::string_view;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(SPDLOG_NO_ATOMIC_LEVELS)
|
|
||||||
using level_t = details::null_atomic_int;
|
|
||||||
#else
|
|
||||||
using level_t = std::atomic<int>;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define SPDLOG_LEVEL_TRACE 0
|
|
||||||
#define SPDLOG_LEVEL_DEBUG 1
|
|
||||||
#define SPDLOG_LEVEL_INFO 2
|
|
||||||
#define SPDLOG_LEVEL_WARN 3
|
|
||||||
#define SPDLOG_LEVEL_ERROR 4
|
|
||||||
#define SPDLOG_LEVEL_CRITICAL 5
|
|
||||||
#define SPDLOG_LEVEL_OFF 6
|
|
||||||
|
|
||||||
#if !defined(SPDLOG_ACTIVE_LEVEL)
|
|
||||||
#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Log level enum
|
|
||||||
namespace level {
|
|
||||||
enum level_enum
|
|
||||||
{
|
|
||||||
trace = SPDLOG_LEVEL_TRACE,
|
|
||||||
debug = SPDLOG_LEVEL_DEBUG,
|
|
||||||
info = SPDLOG_LEVEL_INFO,
|
|
||||||
warn = SPDLOG_LEVEL_WARN,
|
|
||||||
err = SPDLOG_LEVEL_ERROR,
|
|
||||||
critical = SPDLOG_LEVEL_CRITICAL,
|
|
||||||
off = SPDLOG_LEVEL_OFF,
|
|
||||||
};
|
|
||||||
|
|
||||||
#if !defined(SPDLOG_LEVEL_NAMES)
|
|
||||||
#define SPDLOG_LEVEL_NAMES \
|
|
||||||
{ \
|
|
||||||
"trace", "debug", "info", "warning", "error", "critical", "off" \
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
static string_view_t level_string_views[] SPDLOG_LEVEL_NAMES;
|
|
||||||
|
|
||||||
#if !defined(SPDLOG_SHORT_LEVEL_NAMES)
|
|
||||||
#define SPDLOG_SHORT_LEVEL_NAMES {"T", "D", "I", "W", "E", "C", "O"}
|
|
||||||
#endif
|
|
||||||
static const char *short_level_names[] SPDLOG_SHORT_LEVEL_NAMES;
|
|
||||||
|
|
||||||
inline string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT
|
|
||||||
{
|
|
||||||
return level_string_views[l];
|
|
||||||
}
|
|
||||||
|
|
||||||
inline const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT
|
|
||||||
{
|
|
||||||
return short_level_names[l];
|
|
||||||
}
|
|
||||||
|
|
||||||
inline spdlog::level::level_enum from_str(const std::string &name) SPDLOG_NOEXCEPT
|
|
||||||
{
|
|
||||||
int level = 0;
|
|
||||||
for (const auto &level_str : level_string_views)
|
|
||||||
{
|
|
||||||
if (level_str == name)
|
|
||||||
{
|
|
||||||
return static_cast<level::level_enum>(level);
|
|
||||||
}
|
|
||||||
level++;
|
|
||||||
}
|
|
||||||
return level::off;
|
|
||||||
}
|
|
||||||
|
|
||||||
using level_hasher = std::hash<int>;
|
|
||||||
} // namespace level
|
|
||||||
|
|
||||||
//
|
|
||||||
// Pattern time - specific time getting to use for pattern_formatter.
|
|
||||||
// local time by default
|
|
||||||
//
|
|
||||||
enum class pattern_time_type
|
|
||||||
{
|
|
||||||
local, // log localtime
|
|
||||||
utc // log utc
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// Log exception
|
|
||||||
//
|
|
||||||
class spdlog_ex : public std::exception
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit spdlog_ex(std::string msg)
|
|
||||||
: msg_(std::move(msg))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
spdlog_ex(const std::string &msg, int last_errno)
|
|
||||||
{
|
|
||||||
fmt::memory_buffer outbuf;
|
|
||||||
fmt::format_system_error(outbuf, last_errno, msg);
|
|
||||||
msg_ = fmt::to_string(outbuf);
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *what() const SPDLOG_NOEXCEPT override
|
|
||||||
{
|
|
||||||
return msg_.c_str();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::string msg_;
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined)
|
|
||||||
//
|
|
||||||
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
|
|
||||||
using filename_t = std::wstring;
|
|
||||||
#else
|
|
||||||
using filename_t = std::string;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct source_loc
|
|
||||||
{
|
|
||||||
SPDLOG_CONSTEXPR source_loc()
|
|
||||||
: filename{""}
|
|
||||||
, line{0}
|
|
||||||
, funcname{""}
|
|
||||||
{
|
|
||||||
}
|
|
||||||
SPDLOG_CONSTEXPR source_loc(const char *filename_in, int line_in, const char *funcname_in)
|
|
||||||
: filename{filename_in}
|
|
||||||
, line{static_cast<uint32_t>(line_in)}
|
|
||||||
, funcname{funcname_in}
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_CONSTEXPR bool empty() const SPDLOG_NOEXCEPT
|
|
||||||
{
|
|
||||||
return line == 0;
|
|
||||||
}
|
|
||||||
const char *filename;
|
|
||||||
uint32_t line;
|
|
||||||
const char *funcname;
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace details {
|
|
||||||
// make_unique support for pre c++14
|
|
||||||
|
|
||||||
#if __cplusplus >= 201402L // C++14 and beyond
|
|
||||||
using std::make_unique;
|
|
||||||
#else
|
|
||||||
template<typename T, typename... Args>
|
|
||||||
std::unique_ptr<T> make_unique(Args &&... args)
|
|
||||||
{
|
|
||||||
static_assert(!std::is_array<T>::value, "arrays not supported");
|
|
||||||
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
} // namespace details
|
|
||||||
} // namespace spdlog
|
|
@ -1,110 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright(c) 2015 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
// async logger implementation
|
|
||||||
// uses a thread pool to perform the actual logging
|
|
||||||
|
|
||||||
#include "spdlog/details/thread_pool.h"
|
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
template<typename It>
|
|
||||||
inline spdlog::async_logger::async_logger(
|
|
||||||
std::string logger_name, It begin, It end, std::weak_ptr<details::thread_pool> tp, async_overflow_policy overflow_policy)
|
|
||||||
: logger(std::move(logger_name), begin, end)
|
|
||||||
, thread_pool_(std::move(tp))
|
|
||||||
, overflow_policy_(overflow_policy)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
inline spdlog::async_logger::async_logger(
|
|
||||||
std::string logger_name, sinks_init_list sinks_list, std::weak_ptr<details::thread_pool> tp, async_overflow_policy overflow_policy)
|
|
||||||
: async_logger(std::move(logger_name), sinks_list.begin(), sinks_list.end(), std::move(tp), overflow_policy)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
inline spdlog::async_logger::async_logger(
|
|
||||||
std::string logger_name, sink_ptr single_sink, std::weak_ptr<details::thread_pool> tp, async_overflow_policy overflow_policy)
|
|
||||||
: async_logger(std::move(logger_name), {std::move(single_sink)}, std::move(tp), overflow_policy)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// send the log message to the thread pool
|
|
||||||
inline void spdlog::async_logger::sink_it_(details::log_msg &msg)
|
|
||||||
{
|
|
||||||
#if defined(SPDLOG_ENABLE_MESSAGE_COUNTER)
|
|
||||||
incr_msg_counter_(msg);
|
|
||||||
#endif
|
|
||||||
if (auto pool_ptr = thread_pool_.lock())
|
|
||||||
{
|
|
||||||
pool_ptr->post_log(shared_from_this(), msg, overflow_policy_);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw spdlog_ex("async log: thread pool doesn't exist anymore");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// send flush request to the thread pool
|
|
||||||
inline void spdlog::async_logger::flush_()
|
|
||||||
{
|
|
||||||
if (auto pool_ptr = thread_pool_.lock())
|
|
||||||
{
|
|
||||||
pool_ptr->post_flush(shared_from_this(), overflow_policy_);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw spdlog_ex("async flush: thread pool doesn't exist anymore");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// backend functions - called from the thread pool to do the actual job
|
|
||||||
//
|
|
||||||
inline void spdlog::async_logger::backend_log_(const details::log_msg &incoming_log_msg)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
for (auto &s : sinks_)
|
|
||||||
{
|
|
||||||
if (s->should_log(incoming_log_msg.level))
|
|
||||||
{
|
|
||||||
s->log(incoming_log_msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SPDLOG_CATCH_AND_HANDLE
|
|
||||||
|
|
||||||
if (should_flush_(incoming_log_msg))
|
|
||||||
{
|
|
||||||
backend_flush_();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void spdlog::async_logger::backend_flush_()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
for (auto &sink : sinks_)
|
|
||||||
{
|
|
||||||
sink->flush();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SPDLOG_CATCH_AND_HANDLE
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::shared_ptr<spdlog::logger> spdlog::async_logger::clone(std::string new_name)
|
|
||||||
{
|
|
||||||
auto cloned = std::make_shared<spdlog::async_logger>(std::move(new_name), sinks_.begin(), sinks_.end(), thread_pool_, overflow_policy_);
|
|
||||||
|
|
||||||
cloned->set_level(this->level());
|
|
||||||
cloned->flush_on(this->flush_level());
|
|
||||||
cloned->set_error_handler(this->error_handler());
|
|
||||||
return std::move(cloned);
|
|
||||||
}
|
|
@ -1,72 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright(c) 2018 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
//
|
|
||||||
|
|
||||||
// cirucal q view of std::vector.
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace details {
|
|
||||||
template<typename T>
|
|
||||||
class circular_q
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using item_type = T;
|
|
||||||
|
|
||||||
explicit circular_q(size_t max_items)
|
|
||||||
: max_items_(max_items + 1) // one item is reserved as marker for full q
|
|
||||||
, v_(max_items_)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// push back, overrun (oldest) item if no room left
|
|
||||||
void push_back(T &&item)
|
|
||||||
{
|
|
||||||
v_[tail_] = std::move(item);
|
|
||||||
tail_ = (tail_ + 1) % max_items_;
|
|
||||||
|
|
||||||
if (tail_ == head_) // overrun last item if full
|
|
||||||
{
|
|
||||||
head_ = (head_ + 1) % max_items_;
|
|
||||||
++overrun_counter_;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pop item from front.
|
|
||||||
// If there are no elements in the container, the behavior is undefined.
|
|
||||||
void pop_front(T &popped_item)
|
|
||||||
{
|
|
||||||
popped_item = std::move(v_[head_]);
|
|
||||||
head_ = (head_ + 1) % max_items_;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool empty()
|
|
||||||
{
|
|
||||||
return tail_ == head_;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool full()
|
|
||||||
{
|
|
||||||
// head is ahead of the tail by 1
|
|
||||||
return ((tail_ + 1) % max_items_) == head_;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t overrun_counter() const
|
|
||||||
{
|
|
||||||
return overrun_counter_;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
size_t max_items_;
|
|
||||||
typename std::vector<T>::size_type head_ = 0;
|
|
||||||
typename std::vector<T>::size_type tail_ = 0;
|
|
||||||
|
|
||||||
std::vector<T> v_;
|
|
||||||
|
|
||||||
size_t overrun_counter_ = 0;
|
|
||||||
};
|
|
||||||
} // namespace details
|
|
||||||
} // namespace spdlog
|
|
@ -1,74 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
//
|
|
||||||
// Copyright(c) 2018 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "spdlog/details/null_mutex.h"
|
|
||||||
#include <cstdio>
|
|
||||||
#include <mutex>
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
|
|
||||||
#ifndef NOMINMAX
|
|
||||||
#define NOMINMAX // prevent windows redefining min/max
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef WIN32_LEAN_AND_MEAN
|
|
||||||
#define WIN32_LEAN_AND_MEAN
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <windows.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace details {
|
|
||||||
struct console_stdout
|
|
||||||
{
|
|
||||||
static std::FILE *stream()
|
|
||||||
{
|
|
||||||
return stdout;
|
|
||||||
}
|
|
||||||
#ifdef _WIN32
|
|
||||||
static HANDLE handle()
|
|
||||||
{
|
|
||||||
return ::GetStdHandle(STD_OUTPUT_HANDLE);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
struct console_stderr
|
|
||||||
{
|
|
||||||
static std::FILE *stream()
|
|
||||||
{
|
|
||||||
return stderr;
|
|
||||||
}
|
|
||||||
#ifdef _WIN32
|
|
||||||
static HANDLE handle()
|
|
||||||
{
|
|
||||||
return ::GetStdHandle(STD_ERROR_HANDLE);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
struct console_mutex
|
|
||||||
{
|
|
||||||
using mutex_t = std::mutex;
|
|
||||||
static mutex_t &mutex()
|
|
||||||
{
|
|
||||||
static mutex_t s_mutex;
|
|
||||||
return s_mutex;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct console_nullmutex
|
|
||||||
{
|
|
||||||
using mutex_t = null_mutex;
|
|
||||||
static mutex_t &mutex()
|
|
||||||
{
|
|
||||||
static mutex_t s_mutex;
|
|
||||||
return s_mutex;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} // namespace details
|
|
||||||
} // namespace spdlog
|
|
@ -1,152 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright(c) 2015 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
// Helper class for file sinks.
|
|
||||||
// When failing to open a file, retry several times(5) with a delay interval(10 ms).
|
|
||||||
// Throw spdlog_ex exception on errors.
|
|
||||||
|
|
||||||
#include "spdlog/details/log_msg.h"
|
|
||||||
#include "spdlog/details/os.h"
|
|
||||||
|
|
||||||
#include <cerrno>
|
|
||||||
#include <chrono>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <string>
|
|
||||||
#include <thread>
|
|
||||||
#include <tuple>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace details {
|
|
||||||
|
|
||||||
class file_helper
|
|
||||||
{
|
|
||||||
|
|
||||||
public:
|
|
||||||
const int open_tries = 5;
|
|
||||||
const int open_interval = 10;
|
|
||||||
|
|
||||||
explicit file_helper() = default;
|
|
||||||
|
|
||||||
file_helper(const file_helper &) = delete;
|
|
||||||
file_helper &operator=(const file_helper &) = delete;
|
|
||||||
|
|
||||||
~file_helper()
|
|
||||||
{
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
|
|
||||||
void open(const filename_t &fname, bool truncate = false)
|
|
||||||
{
|
|
||||||
close();
|
|
||||||
auto *mode = truncate ? SPDLOG_FILENAME_T("wb") : SPDLOG_FILENAME_T("ab");
|
|
||||||
_filename = fname;
|
|
||||||
for (int tries = 0; tries < open_tries; ++tries)
|
|
||||||
{
|
|
||||||
if (!os::fopen_s(&fd_, fname, mode))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
details::os::sleep_for_millis(open_interval);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw spdlog_ex("Failed opening file " + os::filename_to_str(_filename) + " for writing", errno);
|
|
||||||
}
|
|
||||||
|
|
||||||
void reopen(bool truncate)
|
|
||||||
{
|
|
||||||
if (_filename.empty())
|
|
||||||
{
|
|
||||||
throw spdlog_ex("Failed re opening file - was not opened before");
|
|
||||||
}
|
|
||||||
open(_filename, truncate);
|
|
||||||
}
|
|
||||||
|
|
||||||
void flush()
|
|
||||||
{
|
|
||||||
std::fflush(fd_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void close()
|
|
||||||
{
|
|
||||||
if (fd_ != nullptr)
|
|
||||||
{
|
|
||||||
std::fclose(fd_);
|
|
||||||
fd_ = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void write(const fmt::memory_buffer &buf)
|
|
||||||
{
|
|
||||||
size_t msg_size = buf.size();
|
|
||||||
auto data = buf.data();
|
|
||||||
if (std::fwrite(data, 1, msg_size, fd_) != msg_size)
|
|
||||||
{
|
|
||||||
throw spdlog_ex("Failed writing to file " + os::filename_to_str(_filename), errno);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t size() const
|
|
||||||
{
|
|
||||||
if (fd_ == nullptr)
|
|
||||||
{
|
|
||||||
throw spdlog_ex("Cannot use size() on closed file " + os::filename_to_str(_filename));
|
|
||||||
}
|
|
||||||
return os::filesize(fd_);
|
|
||||||
}
|
|
||||||
|
|
||||||
const filename_t &filename() const
|
|
||||||
{
|
|
||||||
return _filename;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool file_exists(const filename_t &fname)
|
|
||||||
{
|
|
||||||
return os::file_exists(fname);
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// return file path and its extension:
|
|
||||||
//
|
|
||||||
// "mylog.txt" => ("mylog", ".txt")
|
|
||||||
// "mylog" => ("mylog", "")
|
|
||||||
// "mylog." => ("mylog.", "")
|
|
||||||
// "/dir1/dir2/mylog.txt" => ("/dir1/dir2/mylog", ".txt")
|
|
||||||
//
|
|
||||||
// the starting dot in filenames is ignored (hidden files):
|
|
||||||
//
|
|
||||||
// ".mylog" => (".mylog". "")
|
|
||||||
// "my_folder/.mylog" => ("my_folder/.mylog", "")
|
|
||||||
// "my_folder/.mylog.txt" => ("my_folder/.mylog", ".txt")
|
|
||||||
static std::tuple<filename_t, filename_t> split_by_extension(const spdlog::filename_t &fname)
|
|
||||||
{
|
|
||||||
auto ext_index = fname.rfind('.');
|
|
||||||
|
|
||||||
// no valid extension found - return whole path and empty string as
|
|
||||||
// extension
|
|
||||||
if (ext_index == filename_t::npos || ext_index == 0 || ext_index == fname.size() - 1)
|
|
||||||
{
|
|
||||||
return std::make_tuple(fname, spdlog::filename_t());
|
|
||||||
}
|
|
||||||
|
|
||||||
// treat casese like "/etc/rc.d/somelogfile or "/abc/.hiddenfile"
|
|
||||||
auto folder_index = fname.rfind(details::os::folder_sep);
|
|
||||||
if (folder_index != filename_t::npos && folder_index >= ext_index - 1)
|
|
||||||
{
|
|
||||||
return std::make_tuple(fname, spdlog::filename_t());
|
|
||||||
}
|
|
||||||
|
|
||||||
// finally - return a valid base and extension tuple
|
|
||||||
return std::make_tuple(fname.substr(0, ext_index), fname.substr(ext_index));
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::FILE *fd_{nullptr};
|
|
||||||
filename_t _filename;
|
|
||||||
};
|
|
||||||
} // namespace details
|
|
||||||
} // namespace spdlog
|
|
@ -1,122 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by gabi on 6/15/18.
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
#include <type_traits>
|
|
||||||
#include "spdlog/fmt/fmt.h"
|
|
||||||
|
|
||||||
// Some fmt helpers to efficiently format and pad ints and strings
|
|
||||||
namespace spdlog {
|
|
||||||
namespace details {
|
|
||||||
namespace fmt_helper {
|
|
||||||
|
|
||||||
template<size_t Buffer_Size>
|
|
||||||
inline spdlog::string_view_t to_string_view(const fmt::basic_memory_buffer<char, Buffer_Size> &buf) SPDLOG_NOEXCEPT
|
|
||||||
{
|
|
||||||
return spdlog::string_view_t(buf.data(), buf.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
template<size_t Buffer_Size1, size_t Buffer_Size2>
|
|
||||||
inline void append_buf(const fmt::basic_memory_buffer<char, Buffer_Size1> &buf, fmt::basic_memory_buffer<char, Buffer_Size2> &dest)
|
|
||||||
{
|
|
||||||
auto *buf_ptr = buf.data();
|
|
||||||
dest.append(buf_ptr, buf_ptr + buf.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
template<size_t Buffer_Size>
|
|
||||||
inline void append_string_view(spdlog::string_view_t view, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
|
|
||||||
{
|
|
||||||
auto *buf_ptr = view.data();
|
|
||||||
if (buf_ptr != nullptr)
|
|
||||||
{
|
|
||||||
dest.append(buf_ptr, buf_ptr + view.size());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T, size_t Buffer_Size>
|
|
||||||
inline void append_int(T n, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
|
|
||||||
{
|
|
||||||
fmt::format_int i(n);
|
|
||||||
dest.append(i.data(), i.data() + i.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
inline unsigned count_digits(T n)
|
|
||||||
{
|
|
||||||
using count_type = typename std::conditional<(sizeof(T) > sizeof(uint32_t)), uint64_t, uint32_t>::type;
|
|
||||||
return static_cast<unsigned>(fmt::internal::count_digits(static_cast<count_type>(n)));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<size_t Buffer_Size>
|
|
||||||
inline void pad2(int n, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
|
|
||||||
{
|
|
||||||
if (n > 99)
|
|
||||||
{
|
|
||||||
append_int(n, dest);
|
|
||||||
}
|
|
||||||
else if (n > 9) // 10-99
|
|
||||||
{
|
|
||||||
dest.push_back(static_cast<char>('0' + n / 10));
|
|
||||||
dest.push_back(static_cast<char>('0' + n % 10));
|
|
||||||
}
|
|
||||||
else if (n >= 0) // 0-9
|
|
||||||
{
|
|
||||||
dest.push_back('0');
|
|
||||||
dest.push_back(static_cast<char>('0' + n));
|
|
||||||
}
|
|
||||||
else // negatives (unlikely, but just in case, let fmt deal with it)
|
|
||||||
{
|
|
||||||
fmt::format_to(dest, "{:02}", n);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T, size_t Buffer_Size>
|
|
||||||
inline void pad_uint(T n, unsigned int width, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
|
|
||||||
{
|
|
||||||
static_assert(std::is_unsigned<T>::value, "pad_uint must get unsigned T");
|
|
||||||
auto digits = count_digits(n);
|
|
||||||
if (width > digits)
|
|
||||||
{
|
|
||||||
const char *zeroes = "0000000000000000000";
|
|
||||||
dest.append(zeroes, zeroes + width - digits);
|
|
||||||
}
|
|
||||||
append_int(n, dest);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T, size_t Buffer_Size>
|
|
||||||
inline void pad3(T n, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
|
|
||||||
{
|
|
||||||
pad_uint(n, 3, dest);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T, size_t Buffer_Size>
|
|
||||||
inline void pad6(T n, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
|
|
||||||
{
|
|
||||||
pad_uint(n, 6, dest);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T, size_t Buffer_Size>
|
|
||||||
inline void pad9(T n, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
|
|
||||||
{
|
|
||||||
pad_uint(n, 9, dest);
|
|
||||||
}
|
|
||||||
|
|
||||||
// return fraction of a second of the given time_point.
|
|
||||||
// e.g.
|
|
||||||
// fraction<std::milliseconds>(tp) -> will return the millis part of the second
|
|
||||||
template<typename ToDuration>
|
|
||||||
inline ToDuration time_fraction(const log_clock::time_point &tp)
|
|
||||||
{
|
|
||||||
using std::chrono::duration_cast;
|
|
||||||
using std::chrono::seconds;
|
|
||||||
auto duration = tp.time_since_epoch();
|
|
||||||
auto secs = duration_cast<seconds>(duration);
|
|
||||||
return duration_cast<ToDuration>(duration) - duration_cast<ToDuration>(secs);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace fmt_helper
|
|
||||||
} // namespace details
|
|
||||||
} // namespace spdlog
|
|
@ -1,55 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright(c) 2015 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "spdlog/common.h"
|
|
||||||
#include "spdlog/details/os.h"
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace details {
|
|
||||||
struct log_msg
|
|
||||||
{
|
|
||||||
|
|
||||||
log_msg(source_loc loc, const std::string *loggers_name, level::level_enum lvl, string_view_t view)
|
|
||||||
: logger_name(loggers_name)
|
|
||||||
, level(lvl)
|
|
||||||
#ifndef SPDLOG_NO_DATETIME
|
|
||||||
, time(os::now())
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef SPDLOG_NO_THREAD_ID
|
|
||||||
, thread_id(os::thread_id())
|
|
||||||
#endif
|
|
||||||
, source(loc)
|
|
||||||
, payload(view)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
log_msg(const std::string *loggers_name, level::level_enum lvl, string_view_t view)
|
|
||||||
: log_msg(source_loc{}, loggers_name, lvl, view)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
log_msg(const log_msg &other) = default;
|
|
||||||
|
|
||||||
const std::string *logger_name{nullptr};
|
|
||||||
level::level_enum level{level::off};
|
|
||||||
log_clock::time_point time;
|
|
||||||
size_t thread_id{0};
|
|
||||||
size_t msg_id{0};
|
|
||||||
|
|
||||||
// wrapping the formatted text with color (updated by pattern_formatter).
|
|
||||||
mutable size_t color_range_start{0};
|
|
||||||
mutable size_t color_range_end{0};
|
|
||||||
|
|
||||||
source_loc source;
|
|
||||||
const string_view_t payload;
|
|
||||||
};
|
|
||||||
} // namespace details
|
|
||||||
} // namespace spdlog
|
|
@ -1,435 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright(c) 2015 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "spdlog/details/fmt_helper.h"
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#define SPDLOG_CATCH_AND_HANDLE \
|
|
||||||
catch (const std::exception &ex) \
|
|
||||||
{ \
|
|
||||||
err_handler_(ex.what()); \
|
|
||||||
} \
|
|
||||||
catch (...) \
|
|
||||||
{ \
|
|
||||||
err_handler_("Unknown exception in logger"); \
|
|
||||||
}
|
|
||||||
|
|
||||||
// create logger with given name, sinks and the default pattern formatter
|
|
||||||
// all other ctors will call this one
|
|
||||||
template<typename It>
|
|
||||||
inline spdlog::logger::logger(std::string logger_name, It begin, It end)
|
|
||||||
: name_(std::move(logger_name))
|
|
||||||
, sinks_(begin, end)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// ctor with sinks as init list
|
|
||||||
inline spdlog::logger::logger(std::string logger_name, sinks_init_list sinks_list)
|
|
||||||
: logger(std::move(logger_name), sinks_list.begin(), sinks_list.end())
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// ctor with single sink
|
|
||||||
inline spdlog::logger::logger(std::string logger_name, spdlog::sink_ptr single_sink)
|
|
||||||
: logger(std::move(logger_name), {std::move(single_sink)})
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
inline spdlog::logger::~logger() = default;
|
|
||||||
|
|
||||||
inline void spdlog::logger::set_formatter(std::unique_ptr<spdlog::formatter> f)
|
|
||||||
{
|
|
||||||
for (auto &sink : sinks_)
|
|
||||||
{
|
|
||||||
sink->set_formatter(f->clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void spdlog::logger::set_pattern(std::string pattern, pattern_time_type time_type)
|
|
||||||
{
|
|
||||||
auto new_formatter = details::make_unique<spdlog::pattern_formatter>(std::move(pattern), time_type);
|
|
||||||
set_formatter(std::move(new_formatter));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
inline void spdlog::logger::log(source_loc source, level::level_enum lvl, const char *fmt, const Args &... args)
|
|
||||||
{
|
|
||||||
if (!should_log(lvl))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using details::fmt_helper::to_string_view;
|
|
||||||
fmt::memory_buffer buf;
|
|
||||||
fmt::format_to(buf, fmt, args...);
|
|
||||||
details::log_msg log_msg(source, &name_, lvl, to_string_view(buf));
|
|
||||||
sink_it_(log_msg);
|
|
||||||
}
|
|
||||||
SPDLOG_CATCH_AND_HANDLE
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
inline void spdlog::logger::log(level::level_enum lvl, const char *fmt, const Args &... args)
|
|
||||||
{
|
|
||||||
log(source_loc{}, lvl, fmt, args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void spdlog::logger::log(source_loc source, level::level_enum lvl, const char *msg)
|
|
||||||
{
|
|
||||||
if (!should_log(lvl))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
details::log_msg log_msg(source, &name_, lvl, spdlog::string_view_t(msg));
|
|
||||||
sink_it_(log_msg);
|
|
||||||
}
|
|
||||||
SPDLOG_CATCH_AND_HANDLE
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void spdlog::logger::log(level::level_enum lvl, const char *msg)
|
|
||||||
{
|
|
||||||
log(source_loc{}, lvl, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class T>
|
|
||||||
inline void spdlog::logger::log(level::level_enum lvl, const T &msg)
|
|
||||||
{
|
|
||||||
log(source_loc{}, lvl, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class T, typename std::enable_if<std::is_convertible<T, spdlog::string_view_t>::value, T>::type *>
|
|
||||||
inline void spdlog::logger::log(source_loc source, level::level_enum lvl, const T &msg)
|
|
||||||
{
|
|
||||||
if (!should_log(lvl))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try
|
|
||||||
{
|
|
||||||
details::log_msg log_msg(source, &name_, lvl, msg);
|
|
||||||
sink_it_(log_msg);
|
|
||||||
}
|
|
||||||
SPDLOG_CATCH_AND_HANDLE
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class T, typename std::enable_if<!std::is_convertible<T, spdlog::string_view_t>::value, T>::type *>
|
|
||||||
inline void spdlog::logger::log(source_loc source, level::level_enum lvl, const T &msg)
|
|
||||||
{
|
|
||||||
if (!should_log(lvl))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using details::fmt_helper::to_string_view;
|
|
||||||
fmt::memory_buffer buf;
|
|
||||||
fmt::format_to(buf, "{}", msg);
|
|
||||||
details::log_msg log_msg(source, &name_, lvl, to_string_view(buf));
|
|
||||||
sink_it_(log_msg);
|
|
||||||
}
|
|
||||||
SPDLOG_CATCH_AND_HANDLE
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
inline void spdlog::logger::trace(const char *fmt, const Args &... args)
|
|
||||||
{
|
|
||||||
log(level::trace, fmt, args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
inline void spdlog::logger::debug(const char *fmt, const Args &... args)
|
|
||||||
{
|
|
||||||
log(level::debug, fmt, args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
inline void spdlog::logger::info(const char *fmt, const Args &... args)
|
|
||||||
{
|
|
||||||
log(level::info, fmt, args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
inline void spdlog::logger::warn(const char *fmt, const Args &... args)
|
|
||||||
{
|
|
||||||
log(level::warn, fmt, args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
inline void spdlog::logger::error(const char *fmt, const Args &... args)
|
|
||||||
{
|
|
||||||
log(level::err, fmt, args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
inline void spdlog::logger::critical(const char *fmt, const Args &... args)
|
|
||||||
{
|
|
||||||
log(level::critical, fmt, args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
inline void spdlog::logger::trace(const T &msg)
|
|
||||||
{
|
|
||||||
log(level::trace, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
inline void spdlog::logger::debug(const T &msg)
|
|
||||||
{
|
|
||||||
log(level::debug, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
inline void spdlog::logger::info(const T &msg)
|
|
||||||
{
|
|
||||||
log(level::info, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
inline void spdlog::logger::warn(const T &msg)
|
|
||||||
{
|
|
||||||
log(level::warn, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
inline void spdlog::logger::error(const T &msg)
|
|
||||||
{
|
|
||||||
log(level::err, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
inline void spdlog::logger::critical(const T &msg)
|
|
||||||
{
|
|
||||||
log(level::critical, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
|
||||||
|
|
||||||
inline void wbuf_to_utf8buf(const fmt::wmemory_buffer &wbuf, fmt::memory_buffer &target)
|
|
||||||
{
|
|
||||||
int wbuf_size = static_cast<int>(wbuf.size());
|
|
||||||
if (wbuf_size == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto result_size = ::WideCharToMultiByte(CP_UTF8, 0, wbuf.data(), wbuf_size, NULL, 0, NULL, NULL);
|
|
||||||
|
|
||||||
if (result_size > 0)
|
|
||||||
{
|
|
||||||
target.resize(result_size);
|
|
||||||
::WideCharToMultiByte(CP_UTF8, 0, wbuf.data(), wbuf_size, &target.data()[0], result_size, NULL, NULL);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw spdlog::spdlog_ex(fmt::format("WideCharToMultiByte failed. Last error: {}", ::GetLastError()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
inline void spdlog::logger::log(source_loc source, level::level_enum lvl, const wchar_t *fmt, const Args &... args)
|
|
||||||
{
|
|
||||||
if (!should_log(lvl))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// format to wmemory_buffer and convert to utf8
|
|
||||||
using details::fmt_helper::to_string_view;
|
|
||||||
fmt::wmemory_buffer wbuf;
|
|
||||||
fmt::format_to(wbuf, fmt, args...);
|
|
||||||
fmt::memory_buffer buf;
|
|
||||||
wbuf_to_utf8buf(wbuf, buf);
|
|
||||||
details::log_msg log_msg(source, &name_, lvl, to_string_view(buf));
|
|
||||||
sink_it_(log_msg);
|
|
||||||
}
|
|
||||||
SPDLOG_CATCH_AND_HANDLE
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
inline void spdlog::logger::log(level::level_enum lvl, const wchar_t *fmt, const Args &... args)
|
|
||||||
{
|
|
||||||
log(source_loc{}, lvl, fmt, args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
inline void spdlog::logger::trace(const wchar_t *fmt, const Args &... args)
|
|
||||||
{
|
|
||||||
log(level::trace, fmt, args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
inline void spdlog::logger::debug(const wchar_t *fmt, const Args &... args)
|
|
||||||
{
|
|
||||||
log(level::debug, fmt, args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
inline void spdlog::logger::info(const wchar_t *fmt, const Args &... args)
|
|
||||||
{
|
|
||||||
log(level::info, fmt, args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
inline void spdlog::logger::warn(const wchar_t *fmt, const Args &... args)
|
|
||||||
{
|
|
||||||
log(level::warn, fmt, args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
inline void spdlog::logger::error(const wchar_t *fmt, const Args &... args)
|
|
||||||
{
|
|
||||||
log(level::err, fmt, args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
inline void spdlog::logger::critical(const wchar_t *fmt, const Args &... args)
|
|
||||||
{
|
|
||||||
log(level::critical, fmt, args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
|
||||||
|
|
||||||
//
|
|
||||||
// name and level
|
|
||||||
//
|
|
||||||
inline const std::string &spdlog::logger::name() const
|
|
||||||
{
|
|
||||||
return name_;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void spdlog::logger::set_level(spdlog::level::level_enum log_level)
|
|
||||||
{
|
|
||||||
level_.store(log_level);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void spdlog::logger::set_error_handler(spdlog::log_err_handler err_handler)
|
|
||||||
{
|
|
||||||
err_handler_ = std::move(err_handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline spdlog::log_err_handler spdlog::logger::error_handler() const
|
|
||||||
{
|
|
||||||
return err_handler_;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void spdlog::logger::flush()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
flush_();
|
|
||||||
}
|
|
||||||
SPDLOG_CATCH_AND_HANDLE
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void spdlog::logger::flush_on(level::level_enum log_level)
|
|
||||||
{
|
|
||||||
flush_level_.store(log_level);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline spdlog::level::level_enum spdlog::logger::flush_level() const
|
|
||||||
{
|
|
||||||
return static_cast<spdlog::level::level_enum>(flush_level_.load(std::memory_order_relaxed));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool spdlog::logger::should_flush_(const details::log_msg &msg)
|
|
||||||
{
|
|
||||||
auto flush_level = flush_level_.load(std::memory_order_relaxed);
|
|
||||||
return (msg.level >= flush_level) && (msg.level != level::off);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline spdlog::level::level_enum spdlog::logger::default_level()
|
|
||||||
{
|
|
||||||
return static_cast<spdlog::level::level_enum>(SPDLOG_ACTIVE_LEVEL);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline spdlog::level::level_enum spdlog::logger::level() const
|
|
||||||
{
|
|
||||||
return static_cast<spdlog::level::level_enum>(level_.load(std::memory_order_relaxed));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool spdlog::logger::should_log(spdlog::level::level_enum msg_level) const
|
|
||||||
{
|
|
||||||
return msg_level >= level_.load(std::memory_order_relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// protected virtual called at end of each user log call (if enabled) by the
|
|
||||||
// line_logger
|
|
||||||
//
|
|
||||||
inline void spdlog::logger::sink_it_(details::log_msg &msg)
|
|
||||||
{
|
|
||||||
#if defined(SPDLOG_ENABLE_MESSAGE_COUNTER)
|
|
||||||
incr_msg_counter_(msg);
|
|
||||||
#endif
|
|
||||||
for (auto &sink : sinks_)
|
|
||||||
{
|
|
||||||
if (sink->should_log(msg.level))
|
|
||||||
{
|
|
||||||
sink->log(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (should_flush_(msg))
|
|
||||||
{
|
|
||||||
flush_();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void spdlog::logger::flush_()
|
|
||||||
{
|
|
||||||
for (auto &sink : sinks_)
|
|
||||||
{
|
|
||||||
sink->flush();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void spdlog::logger::default_err_handler_(const std::string &msg)
|
|
||||||
{
|
|
||||||
auto now = time(nullptr);
|
|
||||||
if (now - last_err_time_ < 60)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
last_err_time_ = now;
|
|
||||||
auto tm_time = details::os::localtime(now);
|
|
||||||
char date_buf[100];
|
|
||||||
std::strftime(date_buf, sizeof(date_buf), "%Y-%m-%d %H:%M:%S", &tm_time);
|
|
||||||
fmt::print(stderr, "[*** LOG ERROR ***] [{}] [{}] {}\n", date_buf, name(), msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void spdlog::logger::incr_msg_counter_(details::log_msg &msg)
|
|
||||||
{
|
|
||||||
msg.msg_id = msg_counter_.fetch_add(1, std::memory_order_relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline const std::vector<spdlog::sink_ptr> &spdlog::logger::sinks() const
|
|
||||||
{
|
|
||||||
return sinks_;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::vector<spdlog::sink_ptr> &spdlog::logger::sinks()
|
|
||||||
{
|
|
||||||
return sinks_;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::shared_ptr<spdlog::logger> spdlog::logger::clone(std::string logger_name)
|
|
||||||
{
|
|
||||||
auto cloned = std::make_shared<spdlog::logger>(std::move(logger_name), sinks_.begin(), sinks_.end());
|
|
||||||
cloned->set_level(this->level());
|
|
||||||
cloned->flush_on(this->flush_level());
|
|
||||||
cloned->set_error_handler(this->error_handler());
|
|
||||||
return cloned;
|
|
||||||
}
|
|
@ -1,121 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
//
|
|
||||||
// Copyright(c) 2018 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
//
|
|
||||||
|
|
||||||
// multi producer-multi consumer blocking queue.
|
|
||||||
// enqueue(..) - will block until room found to put the new message.
|
|
||||||
// enqueue_nowait(..) - will return immediately with false if no room left in
|
|
||||||
// the queue.
|
|
||||||
// dequeue_for(..) - will block until the queue is not empty or timeout have
|
|
||||||
// passed.
|
|
||||||
|
|
||||||
#include "spdlog/details/circular_q.h"
|
|
||||||
|
|
||||||
#include <condition_variable>
|
|
||||||
#include <mutex>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace details {
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
class mpmc_blocking_queue
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using item_type = T;
|
|
||||||
explicit mpmc_blocking_queue(size_t max_items)
|
|
||||||
: q_(max_items)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef __MINGW32__
|
|
||||||
// try to enqueue and block if no room left
|
|
||||||
void enqueue(T &&item)
|
|
||||||
{
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> lock(queue_mutex_);
|
|
||||||
pop_cv_.wait(lock, [this] { return !this->q_.full(); });
|
|
||||||
q_.push_back(std::move(item));
|
|
||||||
}
|
|
||||||
push_cv_.notify_one();
|
|
||||||
}
|
|
||||||
|
|
||||||
// enqueue immediately. overrun oldest message in the queue if no room left.
|
|
||||||
void enqueue_nowait(T &&item)
|
|
||||||
{
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> lock(queue_mutex_);
|
|
||||||
q_.push_back(std::move(item));
|
|
||||||
}
|
|
||||||
push_cv_.notify_one();
|
|
||||||
}
|
|
||||||
|
|
||||||
// try to dequeue item. if no item found. wait upto timeout and try again
|
|
||||||
// Return true, if succeeded dequeue item, false otherwise
|
|
||||||
bool dequeue_for(T &popped_item, std::chrono::milliseconds wait_duration)
|
|
||||||
{
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> lock(queue_mutex_);
|
|
||||||
if (!push_cv_.wait_for(lock, wait_duration, [this] { return !this->q_.empty(); }))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
q_.pop_front(popped_item);
|
|
||||||
}
|
|
||||||
pop_cv_.notify_one();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
// apparently mingw deadlocks if the mutex is released before cv.notify_one(),
|
|
||||||
// so release the mutex at the very end each function.
|
|
||||||
|
|
||||||
// try to enqueue and block if no room left
|
|
||||||
void enqueue(T &&item)
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> lock(queue_mutex_);
|
|
||||||
pop_cv_.wait(lock, [this] { return !this->q_.full(); });
|
|
||||||
q_.push_back(std::move(item));
|
|
||||||
push_cv_.notify_one();
|
|
||||||
}
|
|
||||||
|
|
||||||
// enqueue immediately. overrun oldest message in the queue if no room left.
|
|
||||||
void enqueue_nowait(T &&item)
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> lock(queue_mutex_);
|
|
||||||
q_.push_back(std::move(item));
|
|
||||||
push_cv_.notify_one();
|
|
||||||
}
|
|
||||||
|
|
||||||
// try to dequeue item. if no item found. wait upto timeout and try again
|
|
||||||
// Return true, if succeeded dequeue item, false otherwise
|
|
||||||
bool dequeue_for(T &popped_item, std::chrono::milliseconds wait_duration)
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> lock(queue_mutex_);
|
|
||||||
if (!push_cv_.wait_for(lock, wait_duration, [this] { return !this->q_.empty(); }))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
q_.pop_front(popped_item);
|
|
||||||
pop_cv_.notify_one();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
size_t overrun_counter()
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> lock(queue_mutex_);
|
|
||||||
return q_.overrun_counter();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::mutex queue_mutex_;
|
|
||||||
std::condition_variable push_cv_;
|
|
||||||
std::condition_variable pop_cv_;
|
|
||||||
spdlog::details::circular_q<T> q_;
|
|
||||||
};
|
|
||||||
} // namespace details
|
|
||||||
} // namespace spdlog
|
|
@ -1,45 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright(c) 2015 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <atomic>
|
|
||||||
// null, no cost dummy "mutex" and dummy "atomic" int
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace details {
|
|
||||||
struct null_mutex
|
|
||||||
{
|
|
||||||
void lock() {}
|
|
||||||
void unlock() {}
|
|
||||||
bool try_lock()
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct null_atomic_int
|
|
||||||
{
|
|
||||||
int value;
|
|
||||||
null_atomic_int() = default;
|
|
||||||
|
|
||||||
explicit null_atomic_int(int val)
|
|
||||||
: value(val)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
int load(std::memory_order) const
|
|
||||||
{
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void store(int val)
|
|
||||||
{
|
|
||||||
value = val;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace details
|
|
||||||
} // namespace spdlog
|
|
@ -1,421 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright(c) 2015 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
//
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "../common.h"
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <chrono>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <cstring>
|
|
||||||
#include <ctime>
|
|
||||||
#include <functional>
|
|
||||||
#include <string>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
|
|
||||||
#ifndef NOMINMAX
|
|
||||||
#define NOMINMAX // prevent windows redefining min/max
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef WIN32_LEAN_AND_MEAN
|
|
||||||
#define WIN32_LEAN_AND_MEAN
|
|
||||||
#endif
|
|
||||||
#include <io.h> // _get_osfhandle and _isatty support
|
|
||||||
#include <process.h> // _get_pid support
|
|
||||||
#include <windows.h>
|
|
||||||
|
|
||||||
#ifdef __MINGW32__
|
|
||||||
#include <share.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#else // unix
|
|
||||||
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#ifdef __linux__
|
|
||||||
#include <sys/syscall.h> //Use gettid() syscall under linux to get thread id
|
|
||||||
|
|
||||||
#elif __FreeBSD__
|
|
||||||
#include <sys/thr.h> //Use thr_self() syscall under FreeBSD to get thread id
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // unix
|
|
||||||
|
|
||||||
#ifndef __has_feature // Clang - feature checking macros.
|
|
||||||
#define __has_feature(x) 0 // Compatibility with non-clang compilers.
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace details {
|
|
||||||
namespace os {
|
|
||||||
|
|
||||||
inline spdlog::log_clock::time_point now() SPDLOG_NOEXCEPT
|
|
||||||
{
|
|
||||||
|
|
||||||
#if defined __linux__ && defined SPDLOG_CLOCK_COARSE
|
|
||||||
timespec ts;
|
|
||||||
::clock_gettime(CLOCK_REALTIME_COARSE, &ts);
|
|
||||||
return std::chrono::time_point<log_clock, typename log_clock::duration>(
|
|
||||||
std::chrono::duration_cast<typename log_clock::duration>(std::chrono::seconds(ts.tv_sec) + std::chrono::nanoseconds(ts.tv_nsec)));
|
|
||||||
|
|
||||||
#else
|
|
||||||
return log_clock::now();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
inline std::tm localtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT
|
|
||||||
{
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
std::tm tm;
|
|
||||||
localtime_s(&tm, &time_tt);
|
|
||||||
#else
|
|
||||||
std::tm tm;
|
|
||||||
localtime_r(&time_tt, &tm);
|
|
||||||
#endif
|
|
||||||
return tm;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::tm localtime() SPDLOG_NOEXCEPT
|
|
||||||
{
|
|
||||||
std::time_t now_t = time(nullptr);
|
|
||||||
return localtime(now_t);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::tm gmtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT
|
|
||||||
{
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
std::tm tm;
|
|
||||||
gmtime_s(&tm, &time_tt);
|
|
||||||
#else
|
|
||||||
std::tm tm;
|
|
||||||
gmtime_r(&time_tt, &tm);
|
|
||||||
#endif
|
|
||||||
return tm;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::tm gmtime() SPDLOG_NOEXCEPT
|
|
||||||
{
|
|
||||||
std::time_t now_t = time(nullptr);
|
|
||||||
return gmtime(now_t);
|
|
||||||
}
|
|
||||||
|
|
||||||
// eol definition
|
|
||||||
#if !defined(SPDLOG_EOL)
|
|
||||||
#ifdef _WIN32
|
|
||||||
#define SPDLOG_EOL "\r\n"
|
|
||||||
#else
|
|
||||||
#define SPDLOG_EOL "\n"
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
SPDLOG_CONSTEXPR static const char *default_eol = SPDLOG_EOL;
|
|
||||||
|
|
||||||
// folder separator
|
|
||||||
#ifdef _WIN32
|
|
||||||
SPDLOG_CONSTEXPR static const char folder_sep = '\\';
|
|
||||||
#else
|
|
||||||
SPDLOG_CONSTEXPR static const char folder_sep = '/';
|
|
||||||
#endif
|
|
||||||
|
|
||||||
inline void prevent_child_fd(FILE *f)
|
|
||||||
{
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#if !defined(__cplusplus_winrt)
|
|
||||||
auto file_handle = (HANDLE)_get_osfhandle(_fileno(f));
|
|
||||||
if (!::SetHandleInformation(file_handle, HANDLE_FLAG_INHERIT, 0))
|
|
||||||
throw spdlog_ex("SetHandleInformation failed", errno);
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
auto fd = fileno(f);
|
|
||||||
if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
|
|
||||||
{
|
|
||||||
throw spdlog_ex("fcntl with FD_CLOEXEC failed", errno);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// fopen_s on non windows for writing
|
|
||||||
inline bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode)
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
#ifdef SPDLOG_WCHAR_FILENAMES
|
|
||||||
*fp = _wfsopen((filename.c_str()), mode.c_str(), _SH_DENYNO);
|
|
||||||
#else
|
|
||||||
*fp = _fsopen((filename.c_str()), mode.c_str(), _SH_DENYNO);
|
|
||||||
#endif
|
|
||||||
#else // unix
|
|
||||||
*fp = fopen((filename.c_str()), mode.c_str());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef SPDLOG_PREVENT_CHILD_FD
|
|
||||||
if (*fp != nullptr)
|
|
||||||
{
|
|
||||||
prevent_child_fd(*fp);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return *fp == nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int remove(const filename_t &filename) SPDLOG_NOEXCEPT
|
|
||||||
{
|
|
||||||
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
|
|
||||||
return _wremove(filename.c_str());
|
|
||||||
#else
|
|
||||||
return std::remove(filename.c_str());
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int rename(const filename_t &filename1, const filename_t &filename2) SPDLOG_NOEXCEPT
|
|
||||||
{
|
|
||||||
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
|
|
||||||
return _wrename(filename1.c_str(), filename2.c_str());
|
|
||||||
#else
|
|
||||||
return std::rename(filename1.c_str(), filename2.c_str());
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return if file exists
|
|
||||||
inline bool file_exists(const filename_t &filename) SPDLOG_NOEXCEPT
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
#ifdef SPDLOG_WCHAR_FILENAMES
|
|
||||||
auto attribs = GetFileAttributesW(filename.c_str());
|
|
||||||
#else
|
|
||||||
auto attribs = GetFileAttributesA(filename.c_str());
|
|
||||||
#endif
|
|
||||||
return (attribs != INVALID_FILE_ATTRIBUTES && !(attribs & FILE_ATTRIBUTE_DIRECTORY));
|
|
||||||
#else // common linux/unix all have the stat system call
|
|
||||||
struct stat buffer;
|
|
||||||
return (stat(filename.c_str(), &buffer) == 0);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return file size according to open FILE* object
|
|
||||||
inline size_t filesize(FILE *f)
|
|
||||||
{
|
|
||||||
if (f == nullptr)
|
|
||||||
{
|
|
||||||
throw spdlog_ex("Failed getting file size. fd is null");
|
|
||||||
}
|
|
||||||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
|
||||||
int fd = _fileno(f);
|
|
||||||
#if _WIN64 // 64 bits
|
|
||||||
__int64 ret = _filelengthi64(fd);
|
|
||||||
if (ret >= 0)
|
|
||||||
{
|
|
||||||
return static_cast<size_t>(ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
#else // windows 32 bits
|
|
||||||
long ret = _filelength(fd);
|
|
||||||
if (ret >= 0)
|
|
||||||
{
|
|
||||||
return static_cast<size_t>(ret);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#else // unix
|
|
||||||
int fd = fileno(f);
|
|
||||||
// 64 bits(but not in osx or cygwin, where fstat64 is deprecated)
|
|
||||||
#if !defined(__FreeBSD__) && !defined(__APPLE__) && (defined(__x86_64__) || defined(__ppc64__)) && !defined(__CYGWIN__)
|
|
||||||
struct stat64 st;
|
|
||||||
if (fstat64(fd, &st) == 0)
|
|
||||||
{
|
|
||||||
return static_cast<size_t>(st.st_size);
|
|
||||||
}
|
|
||||||
#else // unix 32 bits or cygwin
|
|
||||||
struct stat st;
|
|
||||||
|
|
||||||
if (fstat(fd, &st) == 0)
|
|
||||||
{
|
|
||||||
return static_cast<size_t>(st.st_size);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
throw spdlog_ex("Failed getting file size from fd", errno);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return utc offset in minutes or throw spdlog_ex on failure
|
|
||||||
inline int utc_minutes_offset(const std::tm &tm = details::os::localtime())
|
|
||||||
{
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#if _WIN32_WINNT < _WIN32_WINNT_WS08
|
|
||||||
TIME_ZONE_INFORMATION tzinfo;
|
|
||||||
auto rv = GetTimeZoneInformation(&tzinfo);
|
|
||||||
#else
|
|
||||||
DYNAMIC_TIME_ZONE_INFORMATION tzinfo;
|
|
||||||
auto rv = GetDynamicTimeZoneInformation(&tzinfo);
|
|
||||||
#endif
|
|
||||||
if (rv == TIME_ZONE_ID_INVALID)
|
|
||||||
throw spdlog::spdlog_ex("Failed getting timezone info. ", errno);
|
|
||||||
|
|
||||||
int offset = -tzinfo.Bias;
|
|
||||||
if (tm.tm_isdst)
|
|
||||||
{
|
|
||||||
offset -= tzinfo.DaylightBias;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
offset -= tzinfo.StandardBias;
|
|
||||||
}
|
|
||||||
return offset;
|
|
||||||
#else
|
|
||||||
|
|
||||||
#if defined(sun) || defined(__sun) || defined(_AIX)
|
|
||||||
// 'tm_gmtoff' field is BSD extension and it's missing on SunOS/Solaris
|
|
||||||
struct helper
|
|
||||||
{
|
|
||||||
static long int calculate_gmt_offset(const std::tm &localtm = details::os::localtime(), const std::tm &gmtm = details::os::gmtime())
|
|
||||||
{
|
|
||||||
int local_year = localtm.tm_year + (1900 - 1);
|
|
||||||
int gmt_year = gmtm.tm_year + (1900 - 1);
|
|
||||||
|
|
||||||
long int days = (
|
|
||||||
// difference in day of year
|
|
||||||
localtm.tm_yday -
|
|
||||||
gmtm.tm_yday
|
|
||||||
|
|
||||||
// + intervening leap days
|
|
||||||
+ ((local_year >> 2) - (gmt_year >> 2)) - (local_year / 100 - gmt_year / 100) +
|
|
||||||
((local_year / 100 >> 2) - (gmt_year / 100 >> 2))
|
|
||||||
|
|
||||||
// + difference in years * 365 */
|
|
||||||
+ (long int)(local_year - gmt_year) * 365);
|
|
||||||
|
|
||||||
long int hours = (24 * days) + (localtm.tm_hour - gmtm.tm_hour);
|
|
||||||
long int mins = (60 * hours) + (localtm.tm_min - gmtm.tm_min);
|
|
||||||
long int secs = (60 * mins) + (localtm.tm_sec - gmtm.tm_sec);
|
|
||||||
|
|
||||||
return secs;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
auto offset_seconds = helper::calculate_gmt_offset(tm);
|
|
||||||
#else
|
|
||||||
auto offset_seconds = tm.tm_gmtoff;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return static_cast<int>(offset_seconds / 60);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return current thread id as size_t
|
|
||||||
// It exists because the std::this_thread::get_id() is much slower(especially
|
|
||||||
// under VS 2013)
|
|
||||||
inline size_t _thread_id() SPDLOG_NOEXCEPT
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
return static_cast<size_t>(::GetCurrentThreadId());
|
|
||||||
#elif __linux__
|
|
||||||
#if defined(__ANDROID__) && defined(__ANDROID_API__) && (__ANDROID_API__ < 21)
|
|
||||||
#define SYS_gettid __NR_gettid
|
|
||||||
#endif
|
|
||||||
return static_cast<size_t>(syscall(SYS_gettid));
|
|
||||||
#elif __FreeBSD__
|
|
||||||
long tid;
|
|
||||||
thr_self(&tid);
|
|
||||||
return static_cast<size_t>(tid);
|
|
||||||
#elif __APPLE__
|
|
||||||
uint64_t tid;
|
|
||||||
pthread_threadid_np(nullptr, &tid);
|
|
||||||
return static_cast<size_t>(tid);
|
|
||||||
#else // Default to standard C++11 (other Unix)
|
|
||||||
return static_cast<size_t>(std::hash<std::thread::id>()(std::this_thread::get_id()));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return current thread id as size_t (from thread local storage)
|
|
||||||
inline size_t thread_id() SPDLOG_NOEXCEPT
|
|
||||||
{
|
|
||||||
#if defined(SPDLOG_NO_TLS)
|
|
||||||
return _thread_id();
|
|
||||||
#else // cache thread id in tls
|
|
||||||
static thread_local const size_t tid = _thread_id();
|
|
||||||
return tid;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is avoid msvc issue in sleep_for that happens if the clock changes.
|
|
||||||
// See https://github.com/gabime/spdlog/issues/609
|
|
||||||
inline void sleep_for_millis(int milliseconds) SPDLOG_NOEXCEPT
|
|
||||||
{
|
|
||||||
#if defined(_WIN32)
|
|
||||||
::Sleep(milliseconds);
|
|
||||||
#else
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined)
|
|
||||||
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
|
|
||||||
#define SPDLOG_FILENAME_T(s) L##s
|
|
||||||
inline std::string filename_to_str(const filename_t &filename)
|
|
||||||
{
|
|
||||||
std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> c;
|
|
||||||
return c.to_bytes(filename);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
#define SPDLOG_FILENAME_T(s) s
|
|
||||||
inline std::string filename_to_str(const filename_t &filename)
|
|
||||||
{
|
|
||||||
return filename;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
inline int pid()
|
|
||||||
{
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
return static_cast<int>(::GetCurrentProcessId());
|
|
||||||
#else
|
|
||||||
return static_cast<int>(::getpid());
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine if the terminal supports colors
|
|
||||||
// Source: https://github.com/agauniyal/rang/
|
|
||||||
inline bool is_color_terminal() SPDLOG_NOEXCEPT
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
return true;
|
|
||||||
#else
|
|
||||||
static constexpr const char *Terms[] = {
|
|
||||||
"ansi", "color", "console", "cygwin", "gnome", "konsole", "kterm", "linux", "msys", "putty", "rxvt", "screen", "vt100", "xterm"};
|
|
||||||
|
|
||||||
const char *env_p = std::getenv("TERM");
|
|
||||||
if (env_p == nullptr)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const bool result =
|
|
||||||
std::any_of(std::begin(Terms), std::end(Terms), [&](const char *term) { return std::strstr(env_p, term) != nullptr; });
|
|
||||||
return result;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// Detrmine if the terminal attached
|
|
||||||
// Source: https://github.com/agauniyal/rang/
|
|
||||||
inline bool in_terminal(FILE *file) SPDLOG_NOEXCEPT
|
|
||||||
{
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
return _isatty(_fileno(file)) != 0;
|
|
||||||
#else
|
|
||||||
return isatty(fileno(file)) != 0;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
} // namespace os
|
|
||||||
} // namespace details
|
|
||||||
} // namespace spdlog
|
|
File diff suppressed because it is too large
Load Diff
@ -1,71 +0,0 @@
|
|||||||
|
|
||||||
//
|
|
||||||
// Copyright(c) 2018 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
// periodic worker thread - periodically executes the given callback function.
|
|
||||||
//
|
|
||||||
// RAII over the owned thread:
|
|
||||||
// creates the thread on construction.
|
|
||||||
// stops and joins the thread on destruction (if the thread is executing a callback, wait for it to finish first).
|
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
#include <condition_variable>
|
|
||||||
#include <functional>
|
|
||||||
#include <mutex>
|
|
||||||
#include <thread>
|
|
||||||
namespace spdlog {
|
|
||||||
namespace details {
|
|
||||||
|
|
||||||
class periodic_worker
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
periodic_worker(const std::function<void()> &callback_fun, std::chrono::seconds interval)
|
|
||||||
{
|
|
||||||
active_ = (interval > std::chrono::seconds::zero());
|
|
||||||
if (!active_)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
worker_thread_ = std::thread([this, callback_fun, interval]() {
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> lock(this->mutex_);
|
|
||||||
if (this->cv_.wait_for(lock, interval, [this] { return !this->active_; }))
|
|
||||||
{
|
|
||||||
return; // active_ == false, so exit this thread
|
|
||||||
}
|
|
||||||
callback_fun();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
periodic_worker(const periodic_worker &) = delete;
|
|
||||||
periodic_worker &operator=(const periodic_worker &) = delete;
|
|
||||||
|
|
||||||
// stop the worker thread and join it
|
|
||||||
~periodic_worker()
|
|
||||||
{
|
|
||||||
if (worker_thread_.joinable())
|
|
||||||
{
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(mutex_);
|
|
||||||
active_ = false;
|
|
||||||
}
|
|
||||||
cv_.notify_one();
|
|
||||||
worker_thread_.join();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool active_;
|
|
||||||
std::thread worker_thread_;
|
|
||||||
std::mutex mutex_;
|
|
||||||
std::condition_variable cv_;
|
|
||||||
};
|
|
||||||
} // namespace details
|
|
||||||
} // namespace spdlog
|
|
@ -1,285 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright(c) 2015 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
// Loggers registy of unique name->logger pointer
|
|
||||||
// An attempt to create a logger with an already existing name will be ignored
|
|
||||||
// If user requests a non existing logger, nullptr will be returned
|
|
||||||
// This class is thread safe
|
|
||||||
|
|
||||||
#include "spdlog/common.h"
|
|
||||||
#include "spdlog/details/periodic_worker.h"
|
|
||||||
#include "spdlog/logger.h"
|
|
||||||
|
|
||||||
#ifndef SPDLOG_DISABLE_DEFAULT_LOGGER
|
|
||||||
// support for the default stdout color logger
|
|
||||||
#ifdef _WIN32
|
|
||||||
#include "spdlog/sinks/wincolor_sink.h"
|
|
||||||
#else
|
|
||||||
#include "spdlog/sinks/ansicolor_sink.h"
|
|
||||||
#endif
|
|
||||||
#endif // SPDLOG_DISABLE_DEFAULT_LOGGER
|
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
#include <functional>
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace details {
|
|
||||||
class thread_pool;
|
|
||||||
|
|
||||||
class registry
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
registry(const registry &) = delete;
|
|
||||||
registry &operator=(const registry &) = delete;
|
|
||||||
|
|
||||||
void register_logger(std::shared_ptr<logger> new_logger)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
|
||||||
register_logger_(std::move(new_logger));
|
|
||||||
}
|
|
||||||
|
|
||||||
void initialize_logger(std::shared_ptr<logger> new_logger)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
|
||||||
new_logger->set_formatter(formatter_->clone());
|
|
||||||
|
|
||||||
if (err_handler_)
|
|
||||||
{
|
|
||||||
new_logger->set_error_handler(err_handler_);
|
|
||||||
}
|
|
||||||
|
|
||||||
new_logger->set_level(level_);
|
|
||||||
new_logger->flush_on(flush_level_);
|
|
||||||
|
|
||||||
if (automatic_registration_)
|
|
||||||
{
|
|
||||||
register_logger_(std::move(new_logger));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<logger> get(const std::string &logger_name)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
|
||||||
auto found = loggers_.find(logger_name);
|
|
||||||
return found == loggers_.end() ? nullptr : found->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<logger> default_logger()
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
|
||||||
return default_logger_;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return raw ptr to the default logger.
|
|
||||||
// To be used directly by the spdlog default api (e.g. spdlog::info)
|
|
||||||
// This make the default API faster, but cannot be used concurrently with set_default_logger().
|
|
||||||
// e.g do not call set_default_logger() from one thread while calling spdlog::info() from another.
|
|
||||||
logger *get_default_raw()
|
|
||||||
{
|
|
||||||
return default_logger_.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
// set default logger.
|
|
||||||
// default logger is stored in default_logger_ (for faster retrieval) and in the loggers_ map.
|
|
||||||
void set_default_logger(std::shared_ptr<logger> new_default_logger)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
|
||||||
// remove previous default logger from the map
|
|
||||||
if (default_logger_ != nullptr)
|
|
||||||
{
|
|
||||||
loggers_.erase(default_logger_->name());
|
|
||||||
}
|
|
||||||
if (new_default_logger != nullptr)
|
|
||||||
{
|
|
||||||
loggers_[new_default_logger->name()] = new_default_logger;
|
|
||||||
}
|
|
||||||
default_logger_ = std::move(new_default_logger);
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_tp(std::shared_ptr<thread_pool> tp)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::recursive_mutex> lock(tp_mutex_);
|
|
||||||
tp_ = std::move(tp);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<thread_pool> get_tp()
|
|
||||||
{
|
|
||||||
std::lock_guard<std::recursive_mutex> lock(tp_mutex_);
|
|
||||||
return tp_;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set global formatter. Each sink in each logger will get a clone of this object
|
|
||||||
void set_formatter(std::unique_ptr<formatter> formatter)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
|
||||||
formatter_ = std::move(formatter);
|
|
||||||
for (auto &l : loggers_)
|
|
||||||
{
|
|
||||||
l.second->set_formatter(formatter_->clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_level(level::level_enum log_level)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
|
||||||
for (auto &l : loggers_)
|
|
||||||
{
|
|
||||||
l.second->set_level(log_level);
|
|
||||||
}
|
|
||||||
level_ = log_level;
|
|
||||||
}
|
|
||||||
|
|
||||||
void flush_on(level::level_enum log_level)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
|
||||||
for (auto &l : loggers_)
|
|
||||||
{
|
|
||||||
l.second->flush_on(log_level);
|
|
||||||
}
|
|
||||||
flush_level_ = log_level;
|
|
||||||
}
|
|
||||||
|
|
||||||
void flush_every(std::chrono::seconds interval)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(flusher_mutex_);
|
|
||||||
std::function<void()> clbk = std::bind(®istry::flush_all, this);
|
|
||||||
periodic_flusher_ = details::make_unique<periodic_worker>(clbk, interval);
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_error_handler(log_err_handler handler)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
|
||||||
for (auto &l : loggers_)
|
|
||||||
{
|
|
||||||
l.second->set_error_handler(handler);
|
|
||||||
}
|
|
||||||
err_handler_ = handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
void apply_all(const std::function<void(const std::shared_ptr<logger>)> &fun)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
|
||||||
for (auto &l : loggers_)
|
|
||||||
{
|
|
||||||
fun(l.second);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void flush_all()
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
|
||||||
for (auto &l : loggers_)
|
|
||||||
{
|
|
||||||
l.second->flush();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void drop(const std::string &logger_name)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
|
||||||
loggers_.erase(logger_name);
|
|
||||||
if (default_logger_ && default_logger_->name() == logger_name)
|
|
||||||
{
|
|
||||||
default_logger_.reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void drop_all()
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
|
||||||
loggers_.clear();
|
|
||||||
default_logger_.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
// clean all resources and threads started by the registry
|
|
||||||
void shutdown()
|
|
||||||
{
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(flusher_mutex_);
|
|
||||||
periodic_flusher_.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
drop_all();
|
|
||||||
|
|
||||||
{
|
|
||||||
std::lock_guard<std::recursive_mutex> lock(tp_mutex_);
|
|
||||||
tp_.reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::recursive_mutex &tp_mutex()
|
|
||||||
{
|
|
||||||
return tp_mutex_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_automatic_registration(bool automatic_regsistration)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
|
||||||
automatic_registration_ = automatic_regsistration;
|
|
||||||
}
|
|
||||||
|
|
||||||
static registry &instance()
|
|
||||||
{
|
|
||||||
static registry s_instance;
|
|
||||||
return s_instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
registry()
|
|
||||||
: formatter_(new pattern_formatter())
|
|
||||||
{
|
|
||||||
|
|
||||||
#ifndef SPDLOG_DISABLE_DEFAULT_LOGGER
|
|
||||||
// create default logger (ansicolor_stdout_sink_mt or wincolor_stdout_sink_mt in windows).
|
|
||||||
#ifdef _WIN32
|
|
||||||
auto color_sink = std::make_shared<sinks::wincolor_stdout_sink_mt>();
|
|
||||||
#else
|
|
||||||
auto color_sink = std::make_shared<sinks::ansicolor_stdout_sink_mt>();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
const char *default_logger_name = "";
|
|
||||||
default_logger_ = std::make_shared<spdlog::logger>(default_logger_name, std::move(color_sink));
|
|
||||||
loggers_[default_logger_name] = default_logger_;
|
|
||||||
|
|
||||||
#endif // SPDLOG_DISABLE_DEFAULT_LOGGER
|
|
||||||
}
|
|
||||||
|
|
||||||
~registry() = default;
|
|
||||||
|
|
||||||
void throw_if_exists_(const std::string &logger_name)
|
|
||||||
{
|
|
||||||
if (loggers_.find(logger_name) != loggers_.end())
|
|
||||||
{
|
|
||||||
throw spdlog_ex("logger with name '" + logger_name + "' already exists");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void register_logger_(std::shared_ptr<logger> new_logger)
|
|
||||||
{
|
|
||||||
auto logger_name = new_logger->name();
|
|
||||||
throw_if_exists_(logger_name);
|
|
||||||
loggers_[logger_name] = std::move(new_logger);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::mutex logger_map_mutex_, flusher_mutex_;
|
|
||||||
std::recursive_mutex tp_mutex_;
|
|
||||||
std::unordered_map<std::string, std::shared_ptr<logger>> loggers_;
|
|
||||||
std::unique_ptr<formatter> formatter_;
|
|
||||||
level::level_enum level_ = spdlog::logger::default_level();
|
|
||||||
level::level_enum flush_level_ = level::off;
|
|
||||||
log_err_handler err_handler_;
|
|
||||||
std::shared_ptr<thread_pool> tp_;
|
|
||||||
std::unique_ptr<periodic_worker> periodic_flusher_;
|
|
||||||
std::shared_ptr<logger> default_logger_;
|
|
||||||
bool automatic_registration_ = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace details
|
|
||||||
} // namespace spdlog
|
|
@ -1,238 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "spdlog/details/fmt_helper.h"
|
|
||||||
#include "spdlog/details/log_msg.h"
|
|
||||||
#include "spdlog/details/mpmc_blocking_q.h"
|
|
||||||
#include "spdlog/details/os.h"
|
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
#include <memory>
|
|
||||||
#include <thread>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace details {
|
|
||||||
|
|
||||||
using async_logger_ptr = std::shared_ptr<spdlog::async_logger>;
|
|
||||||
|
|
||||||
enum class async_msg_type
|
|
||||||
{
|
|
||||||
log,
|
|
||||||
flush,
|
|
||||||
terminate
|
|
||||||
};
|
|
||||||
|
|
||||||
// Async msg to move to/from the queue
|
|
||||||
// Movable only. should never be copied
|
|
||||||
struct async_msg
|
|
||||||
{
|
|
||||||
async_msg_type msg_type;
|
|
||||||
level::level_enum level;
|
|
||||||
log_clock::time_point time;
|
|
||||||
size_t thread_id;
|
|
||||||
fmt::basic_memory_buffer<char, 176> raw;
|
|
||||||
|
|
||||||
size_t msg_id;
|
|
||||||
source_loc source;
|
|
||||||
async_logger_ptr worker_ptr;
|
|
||||||
|
|
||||||
async_msg() = default;
|
|
||||||
~async_msg() = default;
|
|
||||||
|
|
||||||
// should only be moved in or out of the queue..
|
|
||||||
async_msg(const async_msg &) = delete;
|
|
||||||
|
|
||||||
// support for vs2013 move
|
|
||||||
#if defined(_MSC_VER) && _MSC_VER <= 1800
|
|
||||||
async_msg(async_msg &&other) SPDLOG_NOEXCEPT : msg_type(other.msg_type),
|
|
||||||
level(other.level),
|
|
||||||
time(other.time),
|
|
||||||
thread_id(other.thread_id),
|
|
||||||
raw(move(other.raw)),
|
|
||||||
msg_id(other.msg_id),
|
|
||||||
source(other.source),
|
|
||||||
worker_ptr(std::move(other.worker_ptr))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
async_msg &operator=(async_msg &&other) SPDLOG_NOEXCEPT
|
|
||||||
{
|
|
||||||
msg_type = other.msg_type;
|
|
||||||
level = other.level;
|
|
||||||
time = other.time;
|
|
||||||
thread_id = other.thread_id;
|
|
||||||
raw = std::move(other.raw);
|
|
||||||
msg_id = other.msg_id;
|
|
||||||
source = other.source;
|
|
||||||
worker_ptr = std::move(other.worker_ptr);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
#else // (_MSC_VER) && _MSC_VER <= 1800
|
|
||||||
async_msg(async_msg &&) = default;
|
|
||||||
async_msg &operator=(async_msg &&) = default;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// construct from log_msg with given type
|
|
||||||
async_msg(async_logger_ptr &&worker, async_msg_type the_type, details::log_msg &m)
|
|
||||||
: msg_type(the_type)
|
|
||||||
, level(m.level)
|
|
||||||
, time(m.time)
|
|
||||||
, thread_id(m.thread_id)
|
|
||||||
, msg_id(m.msg_id)
|
|
||||||
, source(m.source)
|
|
||||||
, worker_ptr(std::move(worker))
|
|
||||||
{
|
|
||||||
fmt_helper::append_string_view(m.payload, raw);
|
|
||||||
}
|
|
||||||
|
|
||||||
async_msg(async_logger_ptr &&worker, async_msg_type the_type)
|
|
||||||
: msg_type(the_type)
|
|
||||||
, level(level::off)
|
|
||||||
, time()
|
|
||||||
, thread_id(0)
|
|
||||||
, msg_id(0)
|
|
||||||
, source()
|
|
||||||
, worker_ptr(std::move(worker))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
explicit async_msg(async_msg_type the_type)
|
|
||||||
: async_msg(nullptr, the_type)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// copy into log_msg
|
|
||||||
log_msg to_log_msg()
|
|
||||||
{
|
|
||||||
log_msg msg(&worker_ptr->name(), level, string_view_t(raw.data(), raw.size()));
|
|
||||||
msg.time = time;
|
|
||||||
msg.thread_id = thread_id;
|
|
||||||
msg.msg_id = msg_id;
|
|
||||||
msg.source = source;
|
|
||||||
msg.color_range_start = 0;
|
|
||||||
msg.color_range_end = 0;
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class thread_pool
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using item_type = async_msg;
|
|
||||||
using q_type = details::mpmc_blocking_queue<item_type>;
|
|
||||||
|
|
||||||
thread_pool(size_t q_max_items, size_t threads_n)
|
|
||||||
: q_(q_max_items)
|
|
||||||
{
|
|
||||||
// std::cout << "thread_pool() q_size_bytes: " << q_size_bytes <<
|
|
||||||
// "\tthreads_n: " << threads_n << std::endl;
|
|
||||||
if (threads_n == 0 || threads_n > 1000)
|
|
||||||
{
|
|
||||||
throw spdlog_ex("spdlog::thread_pool(): invalid threads_n param (valid "
|
|
||||||
"range is 1-1000)");
|
|
||||||
}
|
|
||||||
for (size_t i = 0; i < threads_n; i++)
|
|
||||||
{
|
|
||||||
threads_.emplace_back(&thread_pool::worker_loop_, this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// message all threads to terminate gracefully join them
|
|
||||||
~thread_pool()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
for (size_t i = 0; i < threads_.size(); i++)
|
|
||||||
{
|
|
||||||
post_async_msg_(async_msg(async_msg_type::terminate), async_overflow_policy::block);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto &t : threads_)
|
|
||||||
{
|
|
||||||
t.join();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
thread_pool(const thread_pool &) = delete;
|
|
||||||
thread_pool &operator=(thread_pool &&) = delete;
|
|
||||||
|
|
||||||
void post_log(async_logger_ptr &&worker_ptr, details::log_msg &msg, async_overflow_policy overflow_policy)
|
|
||||||
{
|
|
||||||
async_msg async_m(std::move(worker_ptr), async_msg_type::log, msg);
|
|
||||||
post_async_msg_(std::move(async_m), overflow_policy);
|
|
||||||
}
|
|
||||||
|
|
||||||
void post_flush(async_logger_ptr &&worker_ptr, async_overflow_policy overflow_policy)
|
|
||||||
{
|
|
||||||
post_async_msg_(async_msg(std::move(worker_ptr), async_msg_type::flush), overflow_policy);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t overrun_counter()
|
|
||||||
{
|
|
||||||
return q_.overrun_counter();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
q_type q_;
|
|
||||||
|
|
||||||
std::vector<std::thread> threads_;
|
|
||||||
|
|
||||||
void post_async_msg_(async_msg &&new_msg, async_overflow_policy overflow_policy)
|
|
||||||
{
|
|
||||||
if (overflow_policy == async_overflow_policy::block)
|
|
||||||
{
|
|
||||||
q_.enqueue(std::move(new_msg));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
q_.enqueue_nowait(std::move(new_msg));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void worker_loop_()
|
|
||||||
{
|
|
||||||
while (process_next_msg_()) {};
|
|
||||||
}
|
|
||||||
|
|
||||||
// process next message in the queue
|
|
||||||
// return true if this thread should still be active (while no terminate msg
|
|
||||||
// was received)
|
|
||||||
bool process_next_msg_()
|
|
||||||
{
|
|
||||||
async_msg incoming_async_msg;
|
|
||||||
bool dequeued = q_.dequeue_for(incoming_async_msg, std::chrono::seconds(10));
|
|
||||||
if (!dequeued)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (incoming_async_msg.msg_type)
|
|
||||||
{
|
|
||||||
case async_msg_type::log:
|
|
||||||
{
|
|
||||||
auto msg = incoming_async_msg.to_log_msg();
|
|
||||||
incoming_async_msg.worker_ptr->backend_log_(msg);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
case async_msg_type::flush:
|
|
||||||
{
|
|
||||||
incoming_async_msg.worker_ptr->backend_flush_();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
case async_msg_type::terminate:
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert(false && "Unexpected async_msg_type");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace details
|
|
||||||
} // namespace spdlog
|
|
@ -1,172 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright(c) 2015 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
//
|
|
||||||
// Support for logging binary data as hex
|
|
||||||
// format flags:
|
|
||||||
// {:X} - print in uppercase.
|
|
||||||
// {:s} - don't separate each byte with space.
|
|
||||||
// {:p} - don't print the position on each line start.
|
|
||||||
// {:n} - don't split the output to lines.
|
|
||||||
|
|
||||||
//
|
|
||||||
// Examples:
|
|
||||||
//
|
|
||||||
// std::vector<char> v(200, 0x0b);
|
|
||||||
// logger->info("Some buffer {}", spdlog::to_hex(v));
|
|
||||||
// char buf[128];
|
|
||||||
// logger->info("Some buffer {:X}", spdlog::to_hex(std::begin(buf), std::end(buf)));
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace details {
|
|
||||||
|
|
||||||
template<typename It>
|
|
||||||
class bytes_range
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
bytes_range(It range_begin, It range_end)
|
|
||||||
: begin_(range_begin)
|
|
||||||
, end_(range_end)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
It begin() const
|
|
||||||
{
|
|
||||||
return begin_;
|
|
||||||
}
|
|
||||||
It end() const
|
|
||||||
{
|
|
||||||
return end_;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
It begin_, end_;
|
|
||||||
};
|
|
||||||
} // namespace details
|
|
||||||
|
|
||||||
// create a bytes_range that wraps the given container
|
|
||||||
template<typename Container>
|
|
||||||
inline details::bytes_range<typename Container::const_iterator> to_hex(const Container &container)
|
|
||||||
{
|
|
||||||
static_assert(sizeof(typename Container::value_type) == 1, "sizeof(Container::value_type) != 1");
|
|
||||||
using Iter = typename Container::const_iterator;
|
|
||||||
return details::bytes_range<Iter>(std::begin(container), std::end(container));
|
|
||||||
}
|
|
||||||
|
|
||||||
// create bytes_range from ranges
|
|
||||||
template<typename It>
|
|
||||||
inline details::bytes_range<It> to_hex(const It range_begin, const It range_end)
|
|
||||||
{
|
|
||||||
return details::bytes_range<It>(range_begin, range_end);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace spdlog
|
|
||||||
|
|
||||||
namespace fmt {
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
struct formatter<spdlog::details::bytes_range<T>>
|
|
||||||
{
|
|
||||||
const std::size_t line_size = 100;
|
|
||||||
const char delimiter = ' ';
|
|
||||||
|
|
||||||
bool put_newlines = true;
|
|
||||||
bool put_delimiters = true;
|
|
||||||
bool use_uppercase = false;
|
|
||||||
bool put_positions = true; // position on start of each line
|
|
||||||
|
|
||||||
// parse the format string flags
|
|
||||||
template<typename ParseContext>
|
|
||||||
auto parse(ParseContext &ctx) -> decltype(ctx.begin())
|
|
||||||
{
|
|
||||||
auto it = ctx.begin();
|
|
||||||
while (*it && *it != '}')
|
|
||||||
{
|
|
||||||
switch (*it)
|
|
||||||
{
|
|
||||||
case 'X':
|
|
||||||
use_uppercase = true;
|
|
||||||
break;
|
|
||||||
case 's':
|
|
||||||
put_delimiters = false;
|
|
||||||
break;
|
|
||||||
case 'p':
|
|
||||||
put_positions = false;
|
|
||||||
break;
|
|
||||||
case 'n':
|
|
||||||
put_newlines = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
++it;
|
|
||||||
}
|
|
||||||
return it;
|
|
||||||
}
|
|
||||||
|
|
||||||
// format the given bytes range as hex
|
|
||||||
template<typename FormatContext, typename Container>
|
|
||||||
auto format(const spdlog::details::bytes_range<Container> &the_range, FormatContext &ctx) -> decltype(ctx.out())
|
|
||||||
{
|
|
||||||
SPDLOG_CONSTEXPR const char *hex_upper = "0123456789ABCDEF";
|
|
||||||
SPDLOG_CONSTEXPR const char *hex_lower = "0123456789abcdef";
|
|
||||||
const char *hex_chars = use_uppercase ? hex_upper : hex_lower;
|
|
||||||
|
|
||||||
std::size_t pos = 0;
|
|
||||||
std::size_t column = line_size;
|
|
||||||
auto inserter = ctx.begin();
|
|
||||||
|
|
||||||
for (auto &item : the_range)
|
|
||||||
{
|
|
||||||
auto ch = static_cast<unsigned char>(item);
|
|
||||||
pos++;
|
|
||||||
|
|
||||||
if (put_newlines && column >= line_size)
|
|
||||||
{
|
|
||||||
column = put_newline(inserter, pos);
|
|
||||||
|
|
||||||
// put first byte without delimiter in front of it
|
|
||||||
*inserter++ = hex_chars[(ch >> 4) & 0x0f];
|
|
||||||
*inserter++ = hex_chars[ch & 0x0f];
|
|
||||||
column += 2;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (put_delimiters)
|
|
||||||
{
|
|
||||||
*inserter++ = delimiter;
|
|
||||||
++column;
|
|
||||||
}
|
|
||||||
|
|
||||||
*inserter++ = hex_chars[(ch >> 4) & 0x0f];
|
|
||||||
*inserter++ = hex_chars[ch & 0x0f];
|
|
||||||
column += 2;
|
|
||||||
}
|
|
||||||
return inserter;
|
|
||||||
}
|
|
||||||
|
|
||||||
// put newline(and position header)
|
|
||||||
// return the next column
|
|
||||||
template<typename It>
|
|
||||||
std::size_t put_newline(It inserter, std::size_t pos)
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
*inserter++ = '\r';
|
|
||||||
#endif
|
|
||||||
*inserter++ = '\n';
|
|
||||||
|
|
||||||
if (put_positions)
|
|
||||||
{
|
|
||||||
fmt::format_to(inserter, "{:<04X}: ", pos - 1);
|
|
||||||
return 7;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} // namespace fmt
|
|
@ -1,23 +0,0 @@
|
|||||||
Copyright (c) 2012 - 2016, Victor Zverovich
|
|
||||||
|
|
||||||
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 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.
|
|
@ -1,452 +0,0 @@
|
|||||||
// 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_
|
|
@ -1,577 +0,0 @@
|
|||||||
// 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_
|
|
File diff suppressed because it is too large
Load Diff
@ -1,972 +0,0 @@
|
|||||||
// 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_
|
|
File diff suppressed because it is too large
Load Diff
@ -1,77 +0,0 @@
|
|||||||
// 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_
|
|
@ -1,153 +0,0 @@
|
|||||||
// 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_
|
|
@ -1,324 +0,0 @@
|
|||||||
// 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_
|
|
@ -1,855 +0,0 @@
|
|||||||
// 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_
|
|
@ -1,308 +0,0 @@
|
|||||||
// 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_
|
|
||||||
|
|
@ -1,160 +0,0 @@
|
|||||||
// 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_
|
|
@ -1,25 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright(c) 2016-2018 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
//
|
|
||||||
// Include a bundled header-only copy of fmtlib or an external one.
|
|
||||||
// By default spdlog include its own copy.
|
|
||||||
//
|
|
||||||
|
|
||||||
#if !defined(SPDLOG_FMT_EXTERNAL)
|
|
||||||
#ifndef FMT_HEADER_ONLY
|
|
||||||
#define FMT_HEADER_ONLY
|
|
||||||
#endif
|
|
||||||
#ifndef FMT_USE_WINDOWS_H
|
|
||||||
#define FMT_USE_WINDOWS_H 0
|
|
||||||
#endif
|
|
||||||
#include "bundled/core.h"
|
|
||||||
#include "bundled/format.h"
|
|
||||||
#else // external fmtlib
|
|
||||||
#include <fmt/core.h>
|
|
||||||
#include <fmt/format.h>
|
|
||||||
#endif
|
|
@ -1,18 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright(c) 2016 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
//
|
|
||||||
// include bundled or external copy of fmtlib's ostream support
|
|
||||||
//
|
|
||||||
#if !defined(SPDLOG_FMT_EXTERNAL)
|
|
||||||
#ifndef FMT_HEADER_ONLY
|
|
||||||
#define FMT_HEADER_ONLY
|
|
||||||
#endif
|
|
||||||
#include "bundled/ostream.h"
|
|
||||||
#include "fmt.h"
|
|
||||||
#else
|
|
||||||
#include <fmt/ostream.h>
|
|
||||||
#endif
|
|
@ -1,20 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright(c) 2015 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "fmt/fmt.h"
|
|
||||||
#include "spdlog/details/log_msg.h"
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
|
|
||||||
class formatter
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual ~formatter() = default;
|
|
||||||
virtual void format(const details::log_msg &msg, fmt::memory_buffer &dest) = 0;
|
|
||||||
virtual std::unique_ptr<formatter> clone() const = 0;
|
|
||||||
};
|
|
||||||
} // namespace spdlog
|
|
@ -1,183 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright(c) 2015-2108 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
// Thread safe logger (except for set_pattern(..), set_formatter(..) and
|
|
||||||
// set_error_handler())
|
|
||||||
// Has name, log level, vector of std::shared sink pointers and formatter
|
|
||||||
// Upon each log write the logger:
|
|
||||||
// 1. Checks if its log level is enough to log the message and if yes:
|
|
||||||
// 2. Call the underlying sinks to do the job.
|
|
||||||
// 3. Each sink use its own private copy of a formatter to format the message
|
|
||||||
// and send to its destination.
|
|
||||||
//
|
|
||||||
// The use of private formatter per sink provides the opportunity to cache some
|
|
||||||
// formatted data,
|
|
||||||
// and support customize format per each sink.
|
|
||||||
|
|
||||||
#include "spdlog/common.h"
|
|
||||||
#include "spdlog/formatter.h"
|
|
||||||
#include "spdlog/sinks/sink.h"
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
|
|
||||||
class logger
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
logger(std::string name, sink_ptr single_sink);
|
|
||||||
logger(std::string name, sinks_init_list sinks);
|
|
||||||
|
|
||||||
template<typename It>
|
|
||||||
logger(std::string name, It begin, It end);
|
|
||||||
|
|
||||||
virtual ~logger();
|
|
||||||
|
|
||||||
logger(const logger &) = delete;
|
|
||||||
logger &operator=(const logger &) = delete;
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
void log(level::level_enum lvl, const char *fmt, const Args &... args);
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
void log(source_loc loc, level::level_enum lvl, const char *fmt, const Args &... args);
|
|
||||||
|
|
||||||
void log(level::level_enum lvl, const char *msg);
|
|
||||||
|
|
||||||
void log(source_loc loc, level::level_enum lvl, const char *msg);
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
void trace(const char *fmt, const Args &... args);
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
void debug(const char *fmt, const Args &... args);
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
void info(const char *fmt, const Args &... args);
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
void warn(const char *fmt, const Args &... args);
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
void error(const char *fmt, const Args &... args);
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
void critical(const char *fmt, const Args &... args);
|
|
||||||
|
|
||||||
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
|
||||||
#ifndef _WIN32
|
|
||||||
#error SPDLOG_WCHAR_TO_UTF8_SUPPORT only supported on windows
|
|
||||||
#else
|
|
||||||
template<typename... Args>
|
|
||||||
void log(level::level_enum lvl, const wchar_t *fmt, const Args &... args);
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
void log(source_loc source, level::level_enum lvl, const wchar_t *fmt, const Args &... args);
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
void trace(const wchar_t *fmt, const Args &... args);
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
void debug(const wchar_t *fmt, const Args &... args);
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
void info(const wchar_t *fmt, const Args &... args);
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
void warn(const wchar_t *fmt, const Args &... args);
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
void error(const wchar_t *fmt, const Args &... args);
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
void critical(const wchar_t *fmt, const Args &... args);
|
|
||||||
#endif // _WIN32
|
|
||||||
#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
|
||||||
|
|
||||||
template<class T>
|
|
||||||
void log(level::level_enum lvl, const T &);
|
|
||||||
|
|
||||||
// T can be statically converted to string_view
|
|
||||||
template<class T, typename std::enable_if<std::is_convertible<T, spdlog::string_view_t>::value, T>::type * = nullptr>
|
|
||||||
void log(source_loc loc, level::level_enum lvl, const T &);
|
|
||||||
|
|
||||||
// T cannot be statically converted to string_view
|
|
||||||
template<class T, typename std::enable_if<!std::is_convertible<T, spdlog::string_view_t>::value, T>::type * = nullptr>
|
|
||||||
void log(source_loc loc, level::level_enum lvl, const T &);
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
void trace(const T &msg);
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
void debug(const T &msg);
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
void info(const T &msg);
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
void warn(const T &msg);
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
void error(const T &msg);
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
void critical(const T &msg);
|
|
||||||
|
|
||||||
bool should_log(level::level_enum msg_level) const;
|
|
||||||
void set_level(level::level_enum log_level);
|
|
||||||
|
|
||||||
static level::level_enum default_level();
|
|
||||||
level::level_enum level() const;
|
|
||||||
const std::string &name() const;
|
|
||||||
|
|
||||||
// set formatting for the sinks in this logger.
|
|
||||||
// each sink will get a seperate instance of the formatter object.
|
|
||||||
void set_formatter(std::unique_ptr<formatter> formatter);
|
|
||||||
void set_pattern(std::string pattern, pattern_time_type time_type = pattern_time_type::local);
|
|
||||||
|
|
||||||
// flush functions
|
|
||||||
void flush();
|
|
||||||
void flush_on(level::level_enum log_level);
|
|
||||||
level::level_enum flush_level() const;
|
|
||||||
|
|
||||||
// sinks
|
|
||||||
const std::vector<sink_ptr> &sinks() const;
|
|
||||||
std::vector<sink_ptr> &sinks();
|
|
||||||
|
|
||||||
// error handler
|
|
||||||
void set_error_handler(log_err_handler err_handler);
|
|
||||||
log_err_handler error_handler() const;
|
|
||||||
|
|
||||||
// create new logger with same sinks and configuration.
|
|
||||||
virtual std::shared_ptr<logger> clone(std::string logger_name);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual void sink_it_(details::log_msg &msg);
|
|
||||||
virtual void flush_();
|
|
||||||
|
|
||||||
bool should_flush_(const details::log_msg &msg);
|
|
||||||
|
|
||||||
// default error handler.
|
|
||||||
// print the error to stderr with the max rate of 1 message/minute.
|
|
||||||
void default_err_handler_(const std::string &msg);
|
|
||||||
|
|
||||||
// increment the message count (only if defined(SPDLOG_ENABLE_MESSAGE_COUNTER))
|
|
||||||
void incr_msg_counter_(details::log_msg &msg);
|
|
||||||
|
|
||||||
const std::string name_;
|
|
||||||
std::vector<sink_ptr> sinks_;
|
|
||||||
spdlog::level_t level_{spdlog::logger::default_level()};
|
|
||||||
spdlog::level_t flush_level_{level::off};
|
|
||||||
log_err_handler err_handler_{[this](const std::string &msg) { this->default_err_handler_(msg); }};
|
|
||||||
std::atomic<time_t> last_err_time_{0};
|
|
||||||
std::atomic<size_t> msg_counter_{1};
|
|
||||||
};
|
|
||||||
} // namespace spdlog
|
|
||||||
|
|
||||||
#include "details/logger_impl.h"
|
|
@ -1,121 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright(c) 2015 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifndef SPDLOG_H
|
|
||||||
#include "spdlog/spdlog.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "spdlog/details/fmt_helper.h"
|
|
||||||
#include "spdlog/details/null_mutex.h"
|
|
||||||
#include "spdlog/details/os.h"
|
|
||||||
#include "spdlog/sinks/base_sink.h"
|
|
||||||
|
|
||||||
#include <android/log.h>
|
|
||||||
#include <chrono>
|
|
||||||
#include <mutex>
|
|
||||||
#include <string>
|
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
#if !defined(SPDLOG_ANDROID_RETRIES)
|
|
||||||
#define SPDLOG_ANDROID_RETRIES 2
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace sinks {
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Android sink (logging using __android_log_write)
|
|
||||||
*/
|
|
||||||
template<typename Mutex>
|
|
||||||
class android_sink final : public base_sink<Mutex>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit android_sink(std::string tag = "spdlog", bool use_raw_msg = false)
|
|
||||||
: tag_(std::move(tag))
|
|
||||||
, use_raw_msg_(use_raw_msg)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void sink_it_(const details::log_msg &msg) override
|
|
||||||
{
|
|
||||||
const android_LogPriority priority = convert_to_android_(msg.level);
|
|
||||||
fmt::memory_buffer formatted;
|
|
||||||
if (use_raw_msg_)
|
|
||||||
{
|
|
||||||
details::fmt_helper::append_string_view(msg.payload, formatted);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sink::formatter_->format(msg, formatted);
|
|
||||||
}
|
|
||||||
formatted.push_back('\0');
|
|
||||||
const char *msg_output = formatted.data();
|
|
||||||
|
|
||||||
// See system/core/liblog/logger_write.c for explanation of return value
|
|
||||||
int ret = __android_log_write(priority, tag_.c_str(), msg_output);
|
|
||||||
int retry_count = 0;
|
|
||||||
while ((ret == -11 /*EAGAIN*/) && (retry_count < SPDLOG_ANDROID_RETRIES))
|
|
||||||
{
|
|
||||||
details::os::sleep_for_millis(5);
|
|
||||||
ret = __android_log_write(priority, tag_.c_str(), msg_output);
|
|
||||||
retry_count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ret < 0)
|
|
||||||
{
|
|
||||||
throw spdlog_ex("__android_log_write() failed", ret);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void flush_() override {}
|
|
||||||
|
|
||||||
private:
|
|
||||||
static android_LogPriority convert_to_android_(spdlog::level::level_enum level)
|
|
||||||
{
|
|
||||||
switch (level)
|
|
||||||
{
|
|
||||||
case spdlog::level::trace:
|
|
||||||
return ANDROID_LOG_VERBOSE;
|
|
||||||
case spdlog::level::debug:
|
|
||||||
return ANDROID_LOG_DEBUG;
|
|
||||||
case spdlog::level::info:
|
|
||||||
return ANDROID_LOG_INFO;
|
|
||||||
case spdlog::level::warn:
|
|
||||||
return ANDROID_LOG_WARN;
|
|
||||||
case spdlog::level::err:
|
|
||||||
return ANDROID_LOG_ERROR;
|
|
||||||
case spdlog::level::critical:
|
|
||||||
return ANDROID_LOG_FATAL;
|
|
||||||
default:
|
|
||||||
return ANDROID_LOG_DEFAULT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string tag_;
|
|
||||||
bool use_raw_msg_;
|
|
||||||
};
|
|
||||||
|
|
||||||
using android_sink_mt = android_sink<std::mutex>;
|
|
||||||
using android_sink_st = android_sink<details::null_mutex>;
|
|
||||||
} // namespace sinks
|
|
||||||
|
|
||||||
// Create and register android syslog logger
|
|
||||||
|
|
||||||
template<typename Factory = default_factory>
|
|
||||||
inline std::shared_ptr<logger> android_logger_mt(const std::string &logger_name, const std::string &tag = "spdlog")
|
|
||||||
{
|
|
||||||
return Factory::template create<sinks::android_sink_mt>(logger_name, tag);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Factory = default_factory>
|
|
||||||
inline std::shared_ptr<logger> android_logger_st(const std::string &logger_name, const std::string &tag = "spdlog")
|
|
||||||
{
|
|
||||||
return Factory::template create<sinks::android_sink_st>(logger_name, tag);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace spdlog
|
|
@ -1,161 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright(c) 2017 spdlog authors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifndef SPDLOG_H
|
|
||||||
#include "spdlog/spdlog.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "spdlog/details/console_globals.h"
|
|
||||||
#include "spdlog/details/null_mutex.h"
|
|
||||||
#include "spdlog/details/os.h"
|
|
||||||
#include "spdlog/sinks/sink.h"
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <mutex>
|
|
||||||
#include <string>
|
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace sinks {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This sink prefixes the output with an ANSI escape sequence color code
|
|
||||||
* depending on the severity
|
|
||||||
* of the message.
|
|
||||||
* If no color terminal detected, omit the escape codes.
|
|
||||||
*/
|
|
||||||
template<typename TargetStream, class ConsoleMutex>
|
|
||||||
class ansicolor_sink final : public sink
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using mutex_t = typename ConsoleMutex::mutex_t;
|
|
||||||
ansicolor_sink()
|
|
||||||
: target_file_(TargetStream::stream())
|
|
||||||
, mutex_(ConsoleMutex::mutex())
|
|
||||||
|
|
||||||
{
|
|
||||||
should_do_colors_ = details::os::in_terminal(target_file_) && details::os::is_color_terminal();
|
|
||||||
colors_[level::trace] = white;
|
|
||||||
colors_[level::debug] = cyan;
|
|
||||||
colors_[level::info] = green;
|
|
||||||
colors_[level::warn] = yellow + bold;
|
|
||||||
colors_[level::err] = red + bold;
|
|
||||||
colors_[level::critical] = bold + on_red;
|
|
||||||
colors_[level::off] = reset;
|
|
||||||
}
|
|
||||||
|
|
||||||
~ansicolor_sink() override = default;
|
|
||||||
|
|
||||||
ansicolor_sink(const ansicolor_sink &other) = delete;
|
|
||||||
ansicolor_sink &operator=(const ansicolor_sink &other) = delete;
|
|
||||||
|
|
||||||
void set_color(level::level_enum color_level, const std::string &color)
|
|
||||||
{
|
|
||||||
std::lock_guard<mutex_t> lock(mutex_);
|
|
||||||
colors_[color_level] = color;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Formatting codes
|
|
||||||
const std::string reset = "\033[m";
|
|
||||||
const std::string bold = "\033[1m";
|
|
||||||
const std::string dark = "\033[2m";
|
|
||||||
const std::string underline = "\033[4m";
|
|
||||||
const std::string blink = "\033[5m";
|
|
||||||
const std::string reverse = "\033[7m";
|
|
||||||
const std::string concealed = "\033[8m";
|
|
||||||
const std::string clear_line = "\033[K";
|
|
||||||
|
|
||||||
// Foreground colors
|
|
||||||
const std::string black = "\033[30m";
|
|
||||||
const std::string red = "\033[31m";
|
|
||||||
const std::string green = "\033[32m";
|
|
||||||
const std::string yellow = "\033[33m";
|
|
||||||
const std::string blue = "\033[34m";
|
|
||||||
const std::string magenta = "\033[35m";
|
|
||||||
const std::string cyan = "\033[36m";
|
|
||||||
const std::string white = "\033[37m";
|
|
||||||
|
|
||||||
/// Background colors
|
|
||||||
const std::string on_black = "\033[40m";
|
|
||||||
const std::string on_red = "\033[41m";
|
|
||||||
const std::string on_green = "\033[42m";
|
|
||||||
const std::string on_yellow = "\033[43m";
|
|
||||||
const std::string on_blue = "\033[44m";
|
|
||||||
const std::string on_magenta = "\033[45m";
|
|
||||||
const std::string on_cyan = "\033[46m";
|
|
||||||
const std::string on_white = "\033[47m";
|
|
||||||
|
|
||||||
void log(const details::log_msg &msg) override
|
|
||||||
{
|
|
||||||
// Wrap the originally formatted message in color codes.
|
|
||||||
// If color is not supported in the terminal, log as is instead.
|
|
||||||
std::lock_guard<mutex_t> lock(mutex_);
|
|
||||||
|
|
||||||
fmt::memory_buffer formatted;
|
|
||||||
formatter_->format(msg, formatted);
|
|
||||||
if (should_do_colors_ && msg.color_range_end > msg.color_range_start)
|
|
||||||
{
|
|
||||||
// before color range
|
|
||||||
print_range_(formatted, 0, msg.color_range_start);
|
|
||||||
// in color range
|
|
||||||
print_ccode_(colors_[msg.level]);
|
|
||||||
print_range_(formatted, msg.color_range_start, msg.color_range_end);
|
|
||||||
print_ccode_(reset);
|
|
||||||
// after color range
|
|
||||||
print_range_(formatted, msg.color_range_end, formatted.size());
|
|
||||||
}
|
|
||||||
else // no color
|
|
||||||
{
|
|
||||||
print_range_(formatted, 0, formatted.size());
|
|
||||||
}
|
|
||||||
fflush(target_file_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void flush() override
|
|
||||||
{
|
|
||||||
std::lock_guard<mutex_t> lock(mutex_);
|
|
||||||
fflush(target_file_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_pattern(const std::string &pattern) final
|
|
||||||
{
|
|
||||||
std::lock_guard<mutex_t> lock(mutex_);
|
|
||||||
formatter_ = std::unique_ptr<spdlog::formatter>(new pattern_formatter(pattern));
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) override
|
|
||||||
{
|
|
||||||
std::lock_guard<mutex_t> lock(mutex_);
|
|
||||||
formatter_ = std::move(sink_formatter);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
void print_ccode_(const std::string &color_code)
|
|
||||||
{
|
|
||||||
fwrite(color_code.data(), sizeof(char), color_code.size(), target_file_);
|
|
||||||
}
|
|
||||||
void print_range_(const fmt::memory_buffer &formatted, size_t start, size_t end)
|
|
||||||
{
|
|
||||||
fwrite(formatted.data() + start, sizeof(char), end - start, target_file_);
|
|
||||||
}
|
|
||||||
|
|
||||||
FILE *target_file_;
|
|
||||||
mutex_t &mutex_;
|
|
||||||
|
|
||||||
bool should_do_colors_;
|
|
||||||
std::unordered_map<level::level_enum, std::string, level::level_hasher> colors_;
|
|
||||||
};
|
|
||||||
|
|
||||||
using ansicolor_stdout_sink_mt = ansicolor_sink<details::console_stdout, details::console_mutex>;
|
|
||||||
using ansicolor_stdout_sink_st = ansicolor_sink<details::console_stdout, details::console_nullmutex>;
|
|
||||||
|
|
||||||
using ansicolor_stderr_sink_mt = ansicolor_sink<details::console_stderr, details::console_mutex>;
|
|
||||||
using ansicolor_stderr_sink_st = ansicolor_sink<details::console_stderr, details::console_nullmutex>;
|
|
||||||
|
|
||||||
} // namespace sinks
|
|
||||||
|
|
||||||
} // namespace spdlog
|
|
@ -1,69 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright(c) 2015 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
//
|
|
||||||
// base sink templated over a mutex (either dummy or real)
|
|
||||||
// concrete implementation should override the sink_it_() and flush_() methods.
|
|
||||||
// locking is taken care of in this class - no locking needed by the
|
|
||||||
// implementers..
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "spdlog/common.h"
|
|
||||||
#include "spdlog/details/log_msg.h"
|
|
||||||
#include "spdlog/formatter.h"
|
|
||||||
#include "spdlog/sinks/sink.h"
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace sinks {
|
|
||||||
template<typename Mutex>
|
|
||||||
class base_sink : public sink
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
base_sink() = default;
|
|
||||||
base_sink(const base_sink &) = delete;
|
|
||||||
base_sink &operator=(const base_sink &) = delete;
|
|
||||||
|
|
||||||
void log(const details::log_msg &msg) final
|
|
||||||
{
|
|
||||||
std::lock_guard<Mutex> lock(mutex_);
|
|
||||||
sink_it_(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
void flush() final
|
|
||||||
{
|
|
||||||
std::lock_guard<Mutex> lock(mutex_);
|
|
||||||
flush_();
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_pattern(const std::string &pattern) final
|
|
||||||
{
|
|
||||||
std::lock_guard<Mutex> lock(mutex_);
|
|
||||||
set_pattern_(pattern);
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) final
|
|
||||||
{
|
|
||||||
std::lock_guard<Mutex> lock(mutex_);
|
|
||||||
set_formatter_(std::move(sink_formatter));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual void sink_it_(const details::log_msg &msg) = 0;
|
|
||||||
virtual void flush_() = 0;
|
|
||||||
|
|
||||||
virtual void set_pattern_(const std::string &pattern)
|
|
||||||
{
|
|
||||||
set_formatter_(details::make_unique<spdlog::pattern_formatter>(pattern));
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void set_formatter_(std::unique_ptr<spdlog::formatter> sink_formatter)
|
|
||||||
{
|
|
||||||
formatter_ = std::move(sink_formatter);
|
|
||||||
}
|
|
||||||
Mutex mutex_;
|
|
||||||
};
|
|
||||||
} // namespace sinks
|
|
||||||
} // namespace spdlog
|
|
@ -1,75 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright(c) 2015-2018 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifndef SPDLOG_H
|
|
||||||
#include "spdlog/spdlog.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "spdlog/details/file_helper.h"
|
|
||||||
#include "spdlog/details/null_mutex.h"
|
|
||||||
#include "spdlog/sinks/base_sink.h"
|
|
||||||
|
|
||||||
#include <mutex>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace sinks {
|
|
||||||
/*
|
|
||||||
* Trivial file sink with single file as target
|
|
||||||
*/
|
|
||||||
template<typename Mutex>
|
|
||||||
class basic_file_sink final : public base_sink<Mutex>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit basic_file_sink(const filename_t &filename, bool truncate = false)
|
|
||||||
{
|
|
||||||
file_helper_.open(filename, truncate);
|
|
||||||
}
|
|
||||||
|
|
||||||
const filename_t &filename() const
|
|
||||||
{
|
|
||||||
return file_helper_.filename();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void sink_it_(const details::log_msg &msg) override
|
|
||||||
{
|
|
||||||
fmt::memory_buffer formatted;
|
|
||||||
sink::formatter_->format(msg, formatted);
|
|
||||||
file_helper_.write(formatted);
|
|
||||||
}
|
|
||||||
|
|
||||||
void flush_() override
|
|
||||||
{
|
|
||||||
file_helper_.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
details::file_helper file_helper_;
|
|
||||||
};
|
|
||||||
|
|
||||||
using basic_file_sink_mt = basic_file_sink<std::mutex>;
|
|
||||||
using basic_file_sink_st = basic_file_sink<details::null_mutex>;
|
|
||||||
|
|
||||||
} // namespace sinks
|
|
||||||
|
|
||||||
//
|
|
||||||
// factory functions
|
|
||||||
//
|
|
||||||
template<typename Factory = default_factory>
|
|
||||||
inline std::shared_ptr<logger> basic_logger_mt(const std::string &logger_name, const filename_t &filename, bool truncate = false)
|
|
||||||
{
|
|
||||||
return Factory::template create<sinks::basic_file_sink_mt>(logger_name, filename, truncate);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Factory = default_factory>
|
|
||||||
inline std::shared_ptr<logger> basic_logger_st(const std::string &logger_name, const filename_t &filename, bool truncate = false)
|
|
||||||
{
|
|
||||||
return Factory::template create<sinks::basic_file_sink_st>(logger_name, filename, truncate);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace spdlog
|
|
@ -1,141 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright(c) 2015 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifndef SPDLOG_H
|
|
||||||
#include "spdlog/spdlog.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "spdlog/details/file_helper.h"
|
|
||||||
#include "spdlog/details/null_mutex.h"
|
|
||||||
#include "spdlog/fmt/fmt.h"
|
|
||||||
#include "spdlog/sinks/base_sink.h"
|
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <ctime>
|
|
||||||
#include <mutex>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace sinks {
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Generator of daily log file names in format basename.YYYY-MM-DD.ext
|
|
||||||
*/
|
|
||||||
struct daily_filename_calculator
|
|
||||||
{
|
|
||||||
// Create filename for the form basename.YYYY-MM-DD
|
|
||||||
static filename_t calc_filename(const filename_t &filename, const tm &now_tm)
|
|
||||||
{
|
|
||||||
filename_t basename, ext;
|
|
||||||
std::tie(basename, ext) = details::file_helper::split_by_extension(filename);
|
|
||||||
std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::memory_buffer, fmt::wmemory_buffer>::type w;
|
|
||||||
fmt::format_to(
|
|
||||||
w, SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}{}"), basename, now_tm.tm_year + 1900, now_tm.tm_mon + 1, now_tm.tm_mday, ext);
|
|
||||||
return fmt::to_string(w);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Rotating file sink based on date. rotates at midnight
|
|
||||||
*/
|
|
||||||
template<typename Mutex, typename FileNameCalc = daily_filename_calculator>
|
|
||||||
class daily_file_sink final : public base_sink<Mutex>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
// create daily file sink which rotates on given time
|
|
||||||
daily_file_sink(filename_t base_filename, int rotation_hour, int rotation_minute, bool truncate = false)
|
|
||||||
: base_filename_(std::move(base_filename))
|
|
||||||
, rotation_h_(rotation_hour)
|
|
||||||
, rotation_m_(rotation_minute)
|
|
||||||
, truncate_(truncate)
|
|
||||||
{
|
|
||||||
if (rotation_hour < 0 || rotation_hour > 23 || rotation_minute < 0 || rotation_minute > 59)
|
|
||||||
{
|
|
||||||
throw spdlog_ex("daily_file_sink: Invalid rotation time in ctor");
|
|
||||||
}
|
|
||||||
auto now = log_clock::now();
|
|
||||||
file_helper_.open(FileNameCalc::calc_filename(base_filename_, now_tm(now)), truncate_);
|
|
||||||
rotation_tp_ = next_rotation_tp_();
|
|
||||||
}
|
|
||||||
|
|
||||||
const filename_t &filename() const
|
|
||||||
{
|
|
||||||
return file_helper_.filename();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void sink_it_(const details::log_msg &msg) override
|
|
||||||
{
|
|
||||||
|
|
||||||
if (msg.time >= rotation_tp_)
|
|
||||||
{
|
|
||||||
file_helper_.open(FileNameCalc::calc_filename(base_filename_, now_tm(msg.time)), truncate_);
|
|
||||||
rotation_tp_ = next_rotation_tp_();
|
|
||||||
}
|
|
||||||
fmt::memory_buffer formatted;
|
|
||||||
sink::formatter_->format(msg, formatted);
|
|
||||||
file_helper_.write(formatted);
|
|
||||||
}
|
|
||||||
|
|
||||||
void flush_() override
|
|
||||||
{
|
|
||||||
file_helper_.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
tm now_tm(log_clock::time_point tp)
|
|
||||||
{
|
|
||||||
time_t tnow = log_clock::to_time_t(tp);
|
|
||||||
return spdlog::details::os::localtime(tnow);
|
|
||||||
}
|
|
||||||
|
|
||||||
log_clock::time_point next_rotation_tp_()
|
|
||||||
{
|
|
||||||
auto now = log_clock::now();
|
|
||||||
tm date = now_tm(now);
|
|
||||||
date.tm_hour = rotation_h_;
|
|
||||||
date.tm_min = rotation_m_;
|
|
||||||
date.tm_sec = 0;
|
|
||||||
auto rotation_time = log_clock::from_time_t(std::mktime(&date));
|
|
||||||
if (rotation_time > now)
|
|
||||||
{
|
|
||||||
return rotation_time;
|
|
||||||
}
|
|
||||||
return {rotation_time + std::chrono::hours(24)};
|
|
||||||
}
|
|
||||||
|
|
||||||
filename_t base_filename_;
|
|
||||||
int rotation_h_;
|
|
||||||
int rotation_m_;
|
|
||||||
log_clock::time_point rotation_tp_;
|
|
||||||
details::file_helper file_helper_;
|
|
||||||
bool truncate_;
|
|
||||||
};
|
|
||||||
|
|
||||||
using daily_file_sink_mt = daily_file_sink<std::mutex>;
|
|
||||||
using daily_file_sink_st = daily_file_sink<details::null_mutex>;
|
|
||||||
|
|
||||||
} // namespace sinks
|
|
||||||
|
|
||||||
//
|
|
||||||
// factory functions
|
|
||||||
//
|
|
||||||
template<typename Factory = default_factory>
|
|
||||||
inline std::shared_ptr<logger> daily_logger_mt(
|
|
||||||
const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false)
|
|
||||||
{
|
|
||||||
return Factory::template create<sinks::daily_file_sink_mt>(logger_name, filename, hour, minute, truncate);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Factory = default_factory>
|
|
||||||
inline std::shared_ptr<logger> daily_logger_st(
|
|
||||||
const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false)
|
|
||||||
{
|
|
||||||
return Factory::template create<sinks::daily_file_sink_st>(logger_name, filename, hour, minute, truncate);
|
|
||||||
}
|
|
||||||
} // namespace spdlog
|
|
@ -1,94 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2015 David Schury, Gabi Melman
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifndef SPDLOG_H
|
|
||||||
#include "spdlog/spdlog.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "base_sink.h"
|
|
||||||
#include "spdlog/details/log_msg.h"
|
|
||||||
#include "spdlog/details/null_mutex.h"
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <memory>
|
|
||||||
#include <mutex>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
// Distribution sink (mux). Stores a vector of sinks which get called when log
|
|
||||||
// is called
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace sinks {
|
|
||||||
|
|
||||||
template<typename Mutex>
|
|
||||||
class dist_sink : public base_sink<Mutex>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
dist_sink() = default;
|
|
||||||
dist_sink(const dist_sink &) = delete;
|
|
||||||
dist_sink &operator=(const dist_sink &) = delete;
|
|
||||||
|
|
||||||
void add_sink(std::shared_ptr<sink> sink)
|
|
||||||
{
|
|
||||||
std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
|
|
||||||
sinks_.push_back(sink);
|
|
||||||
}
|
|
||||||
|
|
||||||
void remove_sink(std::shared_ptr<sink> sink)
|
|
||||||
{
|
|
||||||
std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
|
|
||||||
sinks_.erase(std::remove(sinks_.begin(), sinks_.end(), sink), sinks_.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_sinks(std::vector<std::shared_ptr<sink>> sinks)
|
|
||||||
{
|
|
||||||
std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
|
|
||||||
sinks_ = std::move(sinks);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void sink_it_(const details::log_msg &msg) override
|
|
||||||
{
|
|
||||||
|
|
||||||
for (auto &sink : sinks_)
|
|
||||||
{
|
|
||||||
if (sink->should_log(msg.level))
|
|
||||||
{
|
|
||||||
sink->log(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void flush_() override
|
|
||||||
{
|
|
||||||
for (auto &sink : sinks_)
|
|
||||||
{
|
|
||||||
sink->flush();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_pattern_(const std::string &pattern) override
|
|
||||||
{
|
|
||||||
set_formatter_(details::make_unique<spdlog::pattern_formatter>(pattern));
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_formatter_(std::unique_ptr<spdlog::formatter> sink_formatter) override
|
|
||||||
{
|
|
||||||
base_sink<Mutex>::formatter_ = std::move(sink_formatter);
|
|
||||||
for (auto &sink : sinks_)
|
|
||||||
{
|
|
||||||
sink->set_formatter(base_sink<Mutex>::formatter_->clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::vector<std::shared_ptr<sink>> sinks_;
|
|
||||||
};
|
|
||||||
|
|
||||||
using dist_sink_mt = dist_sink<std::mutex>;
|
|
||||||
using dist_sink_st = dist_sink<details::null_mutex>;
|
|
||||||
|
|
||||||
} // namespace sinks
|
|
||||||
} // namespace spdlog
|
|
@ -1,54 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright(c) 2016 Alexander Dalshov.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifndef SPDLOG_H
|
|
||||||
#include "spdlog/spdlog.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(_WIN32)
|
|
||||||
|
|
||||||
#include "spdlog/details/null_mutex.h"
|
|
||||||
#include "spdlog/sinks/base_sink.h"
|
|
||||||
|
|
||||||
#include <winbase.h>
|
|
||||||
|
|
||||||
#include <mutex>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace sinks {
|
|
||||||
/*
|
|
||||||
* MSVC sink (logging using OutputDebugStringA)
|
|
||||||
*/
|
|
||||||
template<typename Mutex>
|
|
||||||
class msvc_sink : public base_sink<Mutex>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit msvc_sink() {}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void sink_it_(const details::log_msg &msg) override
|
|
||||||
{
|
|
||||||
|
|
||||||
fmt::memory_buffer formatted;
|
|
||||||
sink::formatter_->format(msg, formatted);
|
|
||||||
OutputDebugStringA(fmt::to_string(formatted).c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
void flush_() override {}
|
|
||||||
};
|
|
||||||
|
|
||||||
using msvc_sink_mt = msvc_sink<std::mutex>;
|
|
||||||
using msvc_sink_st = msvc_sink<details::null_mutex>;
|
|
||||||
|
|
||||||
using windebug_sink_mt = msvc_sink_mt;
|
|
||||||
using windebug_sink_st = msvc_sink_st;
|
|
||||||
|
|
||||||
} // namespace sinks
|
|
||||||
} // namespace spdlog
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,49 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright(c) 2015 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifndef SPDLOG_H
|
|
||||||
#include "spdlog/spdlog.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "spdlog/details/null_mutex.h"
|
|
||||||
#include "spdlog/sinks/base_sink.h"
|
|
||||||
|
|
||||||
#include <mutex>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace sinks {
|
|
||||||
|
|
||||||
template<typename Mutex>
|
|
||||||
class null_sink : public base_sink<Mutex>
|
|
||||||
{
|
|
||||||
protected:
|
|
||||||
void sink_it_(const details::log_msg &) override {}
|
|
||||||
void flush_() override {}
|
|
||||||
};
|
|
||||||
|
|
||||||
using null_sink_mt = null_sink<std::mutex>;
|
|
||||||
using null_sink_st = null_sink<details::null_mutex>;
|
|
||||||
|
|
||||||
} // namespace sinks
|
|
||||||
|
|
||||||
template<typename Factory = default_factory>
|
|
||||||
inline std::shared_ptr<logger> null_logger_mt(const std::string &logger_name)
|
|
||||||
{
|
|
||||||
auto null_logger = Factory::template create<sinks::null_sink_mt>(logger_name);
|
|
||||||
null_logger->set_level(level::off);
|
|
||||||
return null_logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Factory = default_factory>
|
|
||||||
inline std::shared_ptr<logger> null_logger_st(const std::string &logger_name)
|
|
||||||
{
|
|
||||||
auto null_logger = Factory::template create<sinks::null_sink_st>(logger_name);
|
|
||||||
null_logger->set_level(level::off);
|
|
||||||
return null_logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace spdlog
|
|
@ -1,57 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright(c) 2015 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifndef SPDLOG_H
|
|
||||||
#include "spdlog/spdlog.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "spdlog/details/null_mutex.h"
|
|
||||||
#include "spdlog/sinks/base_sink.h"
|
|
||||||
|
|
||||||
#include <mutex>
|
|
||||||
#include <ostream>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace sinks {
|
|
||||||
template<typename Mutex>
|
|
||||||
class ostream_sink final : public base_sink<Mutex>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit ostream_sink(std::ostream &os, bool force_flush = false)
|
|
||||||
: ostream_(os)
|
|
||||||
, force_flush_(force_flush)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
ostream_sink(const ostream_sink &) = delete;
|
|
||||||
ostream_sink &operator=(const ostream_sink &) = delete;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void sink_it_(const details::log_msg &msg) override
|
|
||||||
{
|
|
||||||
fmt::memory_buffer formatted;
|
|
||||||
sink::formatter_->format(msg, formatted);
|
|
||||||
ostream_.write(formatted.data(), static_cast<std::streamsize>(formatted.size()));
|
|
||||||
if (force_flush_)
|
|
||||||
{
|
|
||||||
ostream_.flush();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void flush_() override
|
|
||||||
{
|
|
||||||
ostream_.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::ostream &ostream_;
|
|
||||||
bool force_flush_;
|
|
||||||
};
|
|
||||||
|
|
||||||
using ostream_sink_mt = ostream_sink<std::mutex>;
|
|
||||||
using ostream_sink_st = ostream_sink<details::null_mutex>;
|
|
||||||
|
|
||||||
} // namespace sinks
|
|
||||||
} // namespace spdlog
|
|
@ -1,164 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright(c) 2015 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifndef SPDLOG_H
|
|
||||||
#include "spdlog/spdlog.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "spdlog/details/file_helper.h"
|
|
||||||
#include "spdlog/details/null_mutex.h"
|
|
||||||
#include "spdlog/fmt/fmt.h"
|
|
||||||
#include "spdlog/sinks/base_sink.h"
|
|
||||||
|
|
||||||
#include <cerrno>
|
|
||||||
#include <chrono>
|
|
||||||
#include <ctime>
|
|
||||||
#include <mutex>
|
|
||||||
#include <string>
|
|
||||||
#include <tuple>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace sinks {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Rotating file sink based on size
|
|
||||||
//
|
|
||||||
template<typename Mutex>
|
|
||||||
class rotating_file_sink final : public base_sink<Mutex>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
rotating_file_sink(filename_t base_filename, std::size_t max_size, std::size_t max_files, bool rotate_on_open=false)
|
|
||||||
: base_filename_(std::move(base_filename))
|
|
||||||
, max_size_(max_size)
|
|
||||||
, max_files_(max_files)
|
|
||||||
{
|
|
||||||
file_helper_.open(calc_filename(base_filename_, 0));
|
|
||||||
current_size_ = file_helper_.size(); // expensive. called only once
|
|
||||||
if (rotate_on_open && current_size_ > 0)
|
|
||||||
{
|
|
||||||
rotate_();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// calc filename according to index and file extension if exists.
|
|
||||||
// e.g. calc_filename("logs/mylog.txt, 3) => "logs/mylog.3.txt".
|
|
||||||
static filename_t calc_filename(const filename_t &filename, std::size_t index)
|
|
||||||
{
|
|
||||||
typename std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::memory_buffer, fmt::wmemory_buffer>::type w;
|
|
||||||
if (index != 0u)
|
|
||||||
{
|
|
||||||
filename_t basename, ext;
|
|
||||||
std::tie(basename, ext) = details::file_helper::split_by_extension(filename);
|
|
||||||
fmt::format_to(w, SPDLOG_FILENAME_T("{}.{}{}"), basename, index, ext);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fmt::format_to(w, SPDLOG_FILENAME_T("{}"), filename);
|
|
||||||
}
|
|
||||||
return fmt::to_string(w);
|
|
||||||
}
|
|
||||||
|
|
||||||
const filename_t &filename() const
|
|
||||||
{
|
|
||||||
return file_helper_.filename();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void sink_it_(const details::log_msg &msg) override
|
|
||||||
{
|
|
||||||
fmt::memory_buffer formatted;
|
|
||||||
sink::formatter_->format(msg, formatted);
|
|
||||||
current_size_ += formatted.size();
|
|
||||||
if (current_size_ > max_size_)
|
|
||||||
{
|
|
||||||
rotate_();
|
|
||||||
current_size_ = formatted.size();
|
|
||||||
}
|
|
||||||
file_helper_.write(formatted);
|
|
||||||
}
|
|
||||||
|
|
||||||
void flush_() override
|
|
||||||
{
|
|
||||||
file_helper_.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Rotate files:
|
|
||||||
// log.txt -> log.1.txt
|
|
||||||
// log.1.txt -> log.2.txt
|
|
||||||
// log.2.txt -> log.3.txt
|
|
||||||
// log.3.txt -> delete
|
|
||||||
void rotate_()
|
|
||||||
{
|
|
||||||
using details::os::filename_to_str;
|
|
||||||
file_helper_.close();
|
|
||||||
for (auto i = max_files_; i > 0; --i)
|
|
||||||
{
|
|
||||||
filename_t src = calc_filename(base_filename_, i - 1);
|
|
||||||
if (!details::file_helper::file_exists(src))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
filename_t target = calc_filename(base_filename_, i);
|
|
||||||
|
|
||||||
if (!rename_file(src, target))
|
|
||||||
{
|
|
||||||
// if failed try again after a small delay.
|
|
||||||
// this is a workaround to a windows issue, where very high rotation
|
|
||||||
// rates can cause the rename to fail with permission denied (because of antivirus?).
|
|
||||||
details::os::sleep_for_millis(100);
|
|
||||||
if (!rename_file(src, target))
|
|
||||||
{
|
|
||||||
file_helper_.reopen(true); // truncate the log file anyway to prevent it to grow beyond its limit!
|
|
||||||
current_size_ = 0;
|
|
||||||
throw spdlog_ex(
|
|
||||||
"rotating_file_sink: failed renaming " + filename_to_str(src) + " to " + filename_to_str(target), errno);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
file_helper_.reopen(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// delete the target if exists, and rename the src file to target
|
|
||||||
// return true on success, false otherwise.
|
|
||||||
bool rename_file(const filename_t &src_filename, const filename_t &target_filename)
|
|
||||||
{
|
|
||||||
// try to delete the target file in case it already exists.
|
|
||||||
(void)details::os::remove(target_filename);
|
|
||||||
return details::os::rename(src_filename, target_filename) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
filename_t base_filename_;
|
|
||||||
std::size_t max_size_;
|
|
||||||
std::size_t max_files_;
|
|
||||||
std::size_t current_size_;
|
|
||||||
details::file_helper file_helper_;
|
|
||||||
};
|
|
||||||
|
|
||||||
using rotating_file_sink_mt = rotating_file_sink<std::mutex>;
|
|
||||||
using rotating_file_sink_st = rotating_file_sink<details::null_mutex>;
|
|
||||||
|
|
||||||
} // namespace sinks
|
|
||||||
|
|
||||||
//
|
|
||||||
// factory functions
|
|
||||||
//
|
|
||||||
|
|
||||||
template<typename Factory = default_factory>
|
|
||||||
inline std::shared_ptr<logger> rotating_logger_mt(
|
|
||||||
const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files, bool rotate_on_open=false)
|
|
||||||
{
|
|
||||||
return Factory::template create<sinks::rotating_file_sink_mt>(logger_name, filename, max_file_size, max_files, rotate_on_open);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Factory = default_factory>
|
|
||||||
inline std::shared_ptr<logger> rotating_logger_st(
|
|
||||||
const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files, bool rotate_on_open = false)
|
|
||||||
{
|
|
||||||
return Factory::template create<sinks::rotating_file_sink_st>(logger_name, filename, max_file_size, max_files, rotate_on_open);
|
|
||||||
}
|
|
||||||
} // namespace spdlog
|
|
@ -1,54 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright(c) 2015 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "spdlog/details/log_msg.h"
|
|
||||||
#include "spdlog/details/pattern_formatter.h"
|
|
||||||
#include "spdlog/formatter.h"
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace sinks {
|
|
||||||
class sink
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
sink() = default;
|
|
||||||
|
|
||||||
explicit sink(std::unique_ptr<spdlog::formatter> formatter)
|
|
||||||
: formatter_{std::move(formatter)}
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~sink() = default;
|
|
||||||
virtual void log(const details::log_msg &msg) = 0;
|
|
||||||
virtual void flush() = 0;
|
|
||||||
virtual void set_pattern(const std::string &pattern) = 0;
|
|
||||||
virtual void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) = 0;
|
|
||||||
|
|
||||||
bool should_log(level::level_enum msg_level) const
|
|
||||||
{
|
|
||||||
return msg_level >= level_.load(std::memory_order_relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_level(level::level_enum log_level)
|
|
||||||
{
|
|
||||||
level_.store(log_level);
|
|
||||||
}
|
|
||||||
|
|
||||||
level::level_enum level() const
|
|
||||||
{
|
|
||||||
return static_cast<spdlog::level::level_enum>(level_.load(std::memory_order_relaxed));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
// sink log level - default is all
|
|
||||||
level_t level_{level::trace};
|
|
||||||
|
|
||||||
// sink formatter - default is full format
|
|
||||||
std::unique_ptr<spdlog::formatter> formatter_{details::make_unique<spdlog::pattern_formatter>()};
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace sinks
|
|
||||||
} // namespace spdlog
|
|
@ -1,56 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright(c) 2018 spdlog
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifndef SPDLOG_H
|
|
||||||
#include "spdlog/spdlog.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#include "spdlog/sinks/wincolor_sink.h"
|
|
||||||
#else
|
|
||||||
#include "spdlog/sinks/ansicolor_sink.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace sinks {
|
|
||||||
#ifdef _WIN32
|
|
||||||
using stdout_color_sink_mt = wincolor_stdout_sink_mt;
|
|
||||||
using stdout_color_sink_st = wincolor_stdout_sink_st;
|
|
||||||
using stderr_color_sink_mt = wincolor_stderr_sink_mt;
|
|
||||||
using stderr_color_sink_st = wincolor_stderr_sink_st;
|
|
||||||
#else
|
|
||||||
using stdout_color_sink_mt = ansicolor_stdout_sink_mt;
|
|
||||||
using stdout_color_sink_st = ansicolor_stdout_sink_st;
|
|
||||||
using stderr_color_sink_mt = ansicolor_stderr_sink_mt;
|
|
||||||
using stderr_color_sink_st = ansicolor_stderr_sink_st;
|
|
||||||
#endif
|
|
||||||
} // namespace sinks
|
|
||||||
|
|
||||||
template<typename Factory = default_factory>
|
|
||||||
inline std::shared_ptr<logger> stdout_color_mt(const std::string &logger_name)
|
|
||||||
{
|
|
||||||
return Factory::template create<sinks::stdout_color_sink_mt>(logger_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Factory = default_factory>
|
|
||||||
inline std::shared_ptr<logger> stdout_color_st(const std::string &logger_name)
|
|
||||||
{
|
|
||||||
return Factory::template create<sinks::stdout_color_sink_st>(logger_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Factory = default_factory>
|
|
||||||
inline std::shared_ptr<logger> stderr_color_mt(const std::string &logger_name)
|
|
||||||
{
|
|
||||||
return Factory::template create<sinks::stderr_color_sink_mt>(logger_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Factory = default_factory>
|
|
||||||
inline std::shared_ptr<logger> stderr_color_st(const std::string &logger_name)
|
|
||||||
{
|
|
||||||
return Factory::template create<sinks::stderr_color_sink_st>(logger_name);
|
|
||||||
}
|
|
||||||
} // namespace spdlog
|
|
@ -1,102 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright(c) 2015 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifndef SPDLOG_H
|
|
||||||
#include "spdlog/spdlog.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "spdlog/details/console_globals.h"
|
|
||||||
#include "spdlog/details/null_mutex.h"
|
|
||||||
|
|
||||||
#include <cstdio>
|
|
||||||
#include <memory>
|
|
||||||
#include <mutex>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
|
|
||||||
namespace sinks {
|
|
||||||
|
|
||||||
template<typename TargetStream, typename ConsoleMutex>
|
|
||||||
class stdout_sink final : public sink
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using mutex_t = typename ConsoleMutex::mutex_t;
|
|
||||||
stdout_sink()
|
|
||||||
: mutex_(ConsoleMutex::mutex())
|
|
||||||
, file_(TargetStream::stream())
|
|
||||||
{
|
|
||||||
}
|
|
||||||
~stdout_sink() override = default;
|
|
||||||
|
|
||||||
stdout_sink(const stdout_sink &other) = delete;
|
|
||||||
stdout_sink &operator=(const stdout_sink &other) = delete;
|
|
||||||
|
|
||||||
void log(const details::log_msg &msg) override
|
|
||||||
{
|
|
||||||
std::lock_guard<mutex_t> lock(mutex_);
|
|
||||||
fmt::memory_buffer formatted;
|
|
||||||
formatter_->format(msg, formatted);
|
|
||||||
fwrite(formatted.data(), sizeof(char), formatted.size(), file_);
|
|
||||||
fflush(TargetStream::stream());
|
|
||||||
}
|
|
||||||
|
|
||||||
void flush() override
|
|
||||||
{
|
|
||||||
std::lock_guard<mutex_t> lock(mutex_);
|
|
||||||
fflush(file_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_pattern(const std::string &pattern) override
|
|
||||||
{
|
|
||||||
std::lock_guard<mutex_t> lock(mutex_);
|
|
||||||
formatter_ = std::unique_ptr<spdlog::formatter>(new pattern_formatter(pattern));
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) override
|
|
||||||
{
|
|
||||||
std::lock_guard<mutex_t> lock(mutex_);
|
|
||||||
formatter_ = std::move(sink_formatter);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
mutex_t &mutex_;
|
|
||||||
FILE *file_;
|
|
||||||
};
|
|
||||||
|
|
||||||
using stdout_sink_mt = stdout_sink<details::console_stdout, details::console_mutex>;
|
|
||||||
using stdout_sink_st = stdout_sink<details::console_stdout, details::console_nullmutex>;
|
|
||||||
|
|
||||||
using stderr_sink_mt = stdout_sink<details::console_stderr, details::console_mutex>;
|
|
||||||
using stderr_sink_st = stdout_sink<details::console_stderr, details::console_nullmutex>;
|
|
||||||
|
|
||||||
} // namespace sinks
|
|
||||||
|
|
||||||
// factory methods
|
|
||||||
template<typename Factory = default_factory>
|
|
||||||
inline std::shared_ptr<logger> stdout_logger_mt(const std::string &logger_name)
|
|
||||||
{
|
|
||||||
return Factory::template create<sinks::stdout_sink_mt>(logger_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Factory = default_factory>
|
|
||||||
inline std::shared_ptr<logger> stdout_logger_st(const std::string &logger_name)
|
|
||||||
{
|
|
||||||
return Factory::template create<sinks::stdout_sink_st>(logger_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Factory = default_factory>
|
|
||||||
inline std::shared_ptr<logger> stderr_logger_mt(const std::string &logger_name)
|
|
||||||
{
|
|
||||||
return Factory::template create<sinks::stderr_sink_mt>(logger_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Factory = default_factory>
|
|
||||||
inline std::shared_ptr<logger> stderr_logger_st(const std::string &logger_name)
|
|
||||||
{
|
|
||||||
return Factory::template create<sinks::stderr_sink_st>(logger_name);
|
|
||||||
}
|
|
||||||
} // namespace spdlog
|
|
@ -1,94 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright(c) 2015 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifndef SPDLOG_H
|
|
||||||
#include "spdlog/spdlog.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "spdlog/sinks/base_sink.h"
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <string>
|
|
||||||
#include <syslog.h>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace sinks {
|
|
||||||
/**
|
|
||||||
* Sink that write to syslog using the `syscall()` library call.
|
|
||||||
*
|
|
||||||
* Locking is not needed, as `syslog()` itself is thread-safe.
|
|
||||||
*/
|
|
||||||
template<typename Mutex>
|
|
||||||
class syslog_sink : public base_sink<Mutex>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
//
|
|
||||||
explicit syslog_sink(std::string ident = "", int syslog_option = 0, int syslog_facility = LOG_USER)
|
|
||||||
: ident_(std::move(ident))
|
|
||||||
{
|
|
||||||
priorities_[static_cast<size_t>(level::trace)] = LOG_DEBUG;
|
|
||||||
priorities_[static_cast<size_t>(level::debug)] = LOG_DEBUG;
|
|
||||||
priorities_[static_cast<size_t>(level::info)] = LOG_INFO;
|
|
||||||
priorities_[static_cast<size_t>(level::warn)] = LOG_WARNING;
|
|
||||||
priorities_[static_cast<size_t>(level::err)] = LOG_ERR;
|
|
||||||
priorities_[static_cast<size_t>(level::critical)] = LOG_CRIT;
|
|
||||||
priorities_[static_cast<size_t>(level::off)] = LOG_INFO;
|
|
||||||
|
|
||||||
// set ident to be program name if empty
|
|
||||||
::openlog(ident_.empty() ? nullptr : ident_.c_str(), syslog_option, syslog_facility);
|
|
||||||
}
|
|
||||||
|
|
||||||
~syslog_sink() override
|
|
||||||
{
|
|
||||||
::closelog();
|
|
||||||
}
|
|
||||||
|
|
||||||
syslog_sink(const syslog_sink &) = delete;
|
|
||||||
syslog_sink &operator=(const syslog_sink &) = delete;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void sink_it_(const details::log_msg &msg) override
|
|
||||||
{
|
|
||||||
::syslog(syslog_prio_from_level(msg), "%s", fmt::to_string(msg.payload).c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
void flush_() override {}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::array<int, 7> priorities_;
|
|
||||||
// must store the ident because the man says openlog might use the pointer as
|
|
||||||
// is and not a string copy
|
|
||||||
const std::string ident_;
|
|
||||||
|
|
||||||
//
|
|
||||||
// Simply maps spdlog's log level to syslog priority level.
|
|
||||||
//
|
|
||||||
int syslog_prio_from_level(const details::log_msg &msg) const
|
|
||||||
{
|
|
||||||
return priorities_[static_cast<size_t>(msg.level)];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
using syslog_sink_mt = syslog_sink<std::mutex>;
|
|
||||||
using syslog_sink_st = syslog_sink<details::null_mutex>;
|
|
||||||
} // namespace sinks
|
|
||||||
|
|
||||||
// Create and register a syslog logger
|
|
||||||
template<typename Factory = default_factory>
|
|
||||||
inline std::shared_ptr<logger> syslog_logger_mt(
|
|
||||||
const std::string &logger_name, const std::string &syslog_ident = "", int syslog_option = 0, int syslog_facility = (1 << 3))
|
|
||||||
{
|
|
||||||
return Factory::template create<sinks::syslog_sink_mt>(logger_name, syslog_ident, syslog_option, syslog_facility);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Factory = default_factory>
|
|
||||||
inline std::shared_ptr<logger> syslog_logger_st(
|
|
||||||
const std::string &logger_name, const std::string &syslog_ident = "", int syslog_option = 0, int syslog_facility = (1 << 3))
|
|
||||||
{
|
|
||||||
return Factory::template create<sinks::syslog_sink_st>(logger_name, syslog_ident, syslog_option, syslog_facility);
|
|
||||||
}
|
|
||||||
} // namespace spdlog
|
|
@ -1,143 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright(c) 2016 spdlog
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifndef SPDLOG_H
|
|
||||||
#include "spdlog/spdlog.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "spdlog/common.h"
|
|
||||||
#include "spdlog/details/console_globals.h"
|
|
||||||
#include "spdlog/details/null_mutex.h"
|
|
||||||
#include "spdlog/sinks/sink.h"
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <mutex>
|
|
||||||
#include <string>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <wincon.h>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace sinks {
|
|
||||||
/*
|
|
||||||
* Windows color console sink. Uses WriteConsoleA to write to the console with
|
|
||||||
* colors
|
|
||||||
*/
|
|
||||||
template<typename OutHandle, typename ConsoleMutex>
|
|
||||||
class wincolor_sink : public sink
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
const WORD BOLD = FOREGROUND_INTENSITY;
|
|
||||||
const WORD RED = FOREGROUND_RED;
|
|
||||||
const WORD GREEN = FOREGROUND_GREEN;
|
|
||||||
const WORD CYAN = FOREGROUND_GREEN | FOREGROUND_BLUE;
|
|
||||||
const WORD WHITE = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
|
|
||||||
const WORD YELLOW = FOREGROUND_RED | FOREGROUND_GREEN;
|
|
||||||
|
|
||||||
wincolor_sink()
|
|
||||||
: out_handle_(OutHandle::handle())
|
|
||||||
, mutex_(ConsoleMutex::mutex())
|
|
||||||
{
|
|
||||||
colors_[level::trace] = WHITE;
|
|
||||||
colors_[level::debug] = CYAN;
|
|
||||||
colors_[level::info] = GREEN;
|
|
||||||
colors_[level::warn] = YELLOW | BOLD;
|
|
||||||
colors_[level::err] = RED | BOLD; // red bold
|
|
||||||
colors_[level::critical] = BACKGROUND_RED | WHITE | BOLD; // white bold on red background
|
|
||||||
colors_[level::off] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
~wincolor_sink() override
|
|
||||||
{
|
|
||||||
this->flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
wincolor_sink(const wincolor_sink &other) = delete;
|
|
||||||
wincolor_sink &operator=(const wincolor_sink &other) = delete;
|
|
||||||
|
|
||||||
// change the color for the given level
|
|
||||||
void set_color(level::level_enum level, WORD color)
|
|
||||||
{
|
|
||||||
std::lock_guard<mutex_t> lock(mutex_);
|
|
||||||
colors_[level] = color;
|
|
||||||
}
|
|
||||||
|
|
||||||
void log(const details::log_msg &msg) final override
|
|
||||||
{
|
|
||||||
std::lock_guard<mutex_t> lock(mutex_);
|
|
||||||
fmt::memory_buffer formatted;
|
|
||||||
formatter_->format(msg, formatted);
|
|
||||||
if (msg.color_range_end > msg.color_range_start)
|
|
||||||
{
|
|
||||||
// before color range
|
|
||||||
print_range_(formatted, 0, msg.color_range_start);
|
|
||||||
|
|
||||||
// in color range
|
|
||||||
auto orig_attribs = set_console_attribs(colors_[msg.level]);
|
|
||||||
print_range_(formatted, msg.color_range_start, msg.color_range_end);
|
|
||||||
::SetConsoleTextAttribute(out_handle_,
|
|
||||||
orig_attribs); // reset to orig colors
|
|
||||||
// after color range
|
|
||||||
print_range_(formatted, msg.color_range_end, formatted.size());
|
|
||||||
}
|
|
||||||
else // print without colors if color range is invalid
|
|
||||||
{
|
|
||||||
print_range_(formatted, 0, formatted.size());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void flush() final override
|
|
||||||
{
|
|
||||||
// windows console always flushed?
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_pattern(const std::string &pattern) override final
|
|
||||||
{
|
|
||||||
std::lock_guard<mutex_t> lock(mutex_);
|
|
||||||
formatter_ = std::unique_ptr<spdlog::formatter>(new pattern_formatter(pattern));
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) override final
|
|
||||||
{
|
|
||||||
std::lock_guard<mutex_t> lock(mutex_);
|
|
||||||
formatter_ = std::move(sink_formatter);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
using mutex_t = typename ConsoleMutex::mutex_t;
|
|
||||||
// set color and return the orig console attributes (for resetting later)
|
|
||||||
WORD set_console_attribs(WORD attribs)
|
|
||||||
{
|
|
||||||
CONSOLE_SCREEN_BUFFER_INFO orig_buffer_info;
|
|
||||||
::GetConsoleScreenBufferInfo(out_handle_, &orig_buffer_info);
|
|
||||||
WORD back_color = orig_buffer_info.wAttributes;
|
|
||||||
// retrieve the current background color
|
|
||||||
back_color &= static_cast<WORD>(~(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY));
|
|
||||||
// keep the background color unchanged
|
|
||||||
::SetConsoleTextAttribute(out_handle_, attribs | back_color);
|
|
||||||
return orig_buffer_info.wAttributes; // return orig attribs
|
|
||||||
}
|
|
||||||
|
|
||||||
// print a range of formatted message to console
|
|
||||||
void print_range_(const fmt::memory_buffer &formatted, size_t start, size_t end)
|
|
||||||
{
|
|
||||||
auto size = static_cast<DWORD>(end - start);
|
|
||||||
::WriteConsoleA(out_handle_, formatted.data() + start, size, nullptr, nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
HANDLE out_handle_;
|
|
||||||
mutex_t &mutex_;
|
|
||||||
std::unordered_map<level::level_enum, WORD, level::level_hasher> colors_;
|
|
||||||
};
|
|
||||||
|
|
||||||
using wincolor_stdout_sink_mt = wincolor_sink<details::console_stdout, details::console_mutex>;
|
|
||||||
using wincolor_stdout_sink_st = wincolor_sink<details::console_stdout, details::console_nullmutex>;
|
|
||||||
|
|
||||||
using wincolor_stderr_sink_mt = wincolor_sink<details::console_stderr, details::console_mutex>;
|
|
||||||
using wincolor_stderr_sink_st = wincolor_sink<details::console_stderr, details::console_nullmutex>;
|
|
||||||
|
|
||||||
} // namespace sinks
|
|
||||||
} // namespace spdlog
|
|
@ -1,366 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright(c) 2015-2018 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
//
|
|
||||||
// spdlog main header file.
|
|
||||||
// see example.cpp for usage example
|
|
||||||
|
|
||||||
#ifndef SPDLOG_H
|
|
||||||
#define SPDLOG_H
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "spdlog/common.h"
|
|
||||||
#include "spdlog/details/registry.h"
|
|
||||||
#include "spdlog/logger.h"
|
|
||||||
#include "spdlog/version.h"
|
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
#include <functional>
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
|
|
||||||
// Default logger factory- creates synchronous loggers
|
|
||||||
struct synchronous_factory
|
|
||||||
{
|
|
||||||
template<typename Sink, typename... SinkArgs>
|
|
||||||
static std::shared_ptr<spdlog::logger> create(std::string logger_name, SinkArgs &&... args)
|
|
||||||
{
|
|
||||||
auto sink = std::make_shared<Sink>(std::forward<SinkArgs>(args)...);
|
|
||||||
auto new_logger = std::make_shared<logger>(std::move(logger_name), std::move(sink));
|
|
||||||
details::registry::instance().initialize_logger(new_logger);
|
|
||||||
return new_logger;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
using default_factory = synchronous_factory;
|
|
||||||
|
|
||||||
// Create and register a logger with a templated sink type
|
|
||||||
// The logger's level, formatter and flush level will be set according the
|
|
||||||
// global settings.
|
|
||||||
// Example:
|
|
||||||
// spdlog::create<daily_file_sink_st>("logger_name", "dailylog_filename", 11, 59);
|
|
||||||
template<typename Sink, typename... SinkArgs>
|
|
||||||
inline std::shared_ptr<spdlog::logger> create(std::string logger_name, SinkArgs &&... sink_args)
|
|
||||||
{
|
|
||||||
return default_factory::create<Sink>(std::move(logger_name), std::forward<SinkArgs>(sink_args)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return an existing logger or nullptr if a logger with such name doesn't
|
|
||||||
// exist.
|
|
||||||
// example: spdlog::get("my_logger")->info("hello {}", "world");
|
|
||||||
inline std::shared_ptr<logger> get(const std::string &name)
|
|
||||||
{
|
|
||||||
return details::registry::instance().get(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set global formatter. Each sink in each logger will get a clone of this object
|
|
||||||
inline void set_formatter(std::unique_ptr<spdlog::formatter> formatter)
|
|
||||||
{
|
|
||||||
details::registry::instance().set_formatter(std::move(formatter));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set global format string.
|
|
||||||
// example: spdlog::set_pattern("%Y-%m-%d %H:%M:%S.%e %l : %v");
|
|
||||||
inline void set_pattern(std::string pattern, pattern_time_type time_type = pattern_time_type::local)
|
|
||||||
{
|
|
||||||
set_formatter(std::unique_ptr<spdlog::formatter>(new pattern_formatter(std::move(pattern), time_type)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set global logging level
|
|
||||||
inline void set_level(level::level_enum log_level)
|
|
||||||
{
|
|
||||||
details::registry::instance().set_level(log_level);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set global flush level
|
|
||||||
inline void flush_on(level::level_enum log_level)
|
|
||||||
{
|
|
||||||
details::registry::instance().flush_on(log_level);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start/Restart a periodic flusher thread
|
|
||||||
// Warning: Use only if all your loggers are thread safe!
|
|
||||||
inline void flush_every(std::chrono::seconds interval)
|
|
||||||
{
|
|
||||||
details::registry::instance().flush_every(interval);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set global error handler
|
|
||||||
inline void set_error_handler(log_err_handler handler)
|
|
||||||
{
|
|
||||||
details::registry::instance().set_error_handler(std::move(handler));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register the given logger with the given name
|
|
||||||
inline void register_logger(std::shared_ptr<logger> logger)
|
|
||||||
{
|
|
||||||
details::registry::instance().register_logger(std::move(logger));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply a user defined function on all registered loggers
|
|
||||||
// Example:
|
|
||||||
// spdlog::apply_all([&](std::shared_ptr<spdlog::logger> l) {l->flush();});
|
|
||||||
inline void apply_all(const std::function<void(std::shared_ptr<logger>)> &fun)
|
|
||||||
{
|
|
||||||
details::registry::instance().apply_all(fun);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Drop the reference to the given logger
|
|
||||||
inline void drop(const std::string &name)
|
|
||||||
{
|
|
||||||
details::registry::instance().drop(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Drop all references from the registry
|
|
||||||
inline void drop_all()
|
|
||||||
{
|
|
||||||
details::registry::instance().drop_all();
|
|
||||||
}
|
|
||||||
|
|
||||||
// stop any running threads started by spdlog and clean registry loggers
|
|
||||||
inline void shutdown()
|
|
||||||
{
|
|
||||||
details::registry::instance().shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Automatic registration of loggers when using spdlog::create() or spdlog::create_async
|
|
||||||
inline void set_automatic_registration(bool automatic_registation)
|
|
||||||
{
|
|
||||||
details::registry::instance().set_automatic_registration(automatic_registation);
|
|
||||||
}
|
|
||||||
|
|
||||||
// API for using default logger (stdout_color_mt),
|
|
||||||
// e.g: spdlog::info("Message {}", 1);
|
|
||||||
//
|
|
||||||
// The default logger object can be accessed using the spdlog::default_logger():
|
|
||||||
// For example, to add another sink to it:
|
|
||||||
// spdlog::default_logger()->sinks()->push_back(some_sink);
|
|
||||||
//
|
|
||||||
// The default logger can replaced using spdlog::set_default_logger(new_logger).
|
|
||||||
// For example, to replace it with a file logger.
|
|
||||||
//
|
|
||||||
// IMPORTANT:
|
|
||||||
// The default API is thread safe (for _mt loggers), but:
|
|
||||||
// set_default_logger() *should not* be used concurrently with the default API.
|
|
||||||
// e.g do not call set_default_logger() from one thread while calling spdlog::info() from another.
|
|
||||||
|
|
||||||
inline std::shared_ptr<spdlog::logger> default_logger()
|
|
||||||
{
|
|
||||||
return details::registry::instance().default_logger();
|
|
||||||
}
|
|
||||||
|
|
||||||
inline spdlog::logger *default_logger_raw()
|
|
||||||
{
|
|
||||||
return details::registry::instance().get_default_raw();
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void set_default_logger(std::shared_ptr<spdlog::logger> default_logger)
|
|
||||||
{
|
|
||||||
details::registry::instance().set_default_logger(std::move(default_logger));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
inline void log(source_loc source, level::level_enum lvl, const char *fmt, const Args &... args)
|
|
||||||
{
|
|
||||||
default_logger_raw()->log(source, lvl, fmt, args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
inline void log(level::level_enum lvl, const char *fmt, const Args &... args)
|
|
||||||
{
|
|
||||||
default_logger_raw()->log(source_loc{}, lvl, fmt, args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
inline void trace(const char *fmt, const Args &... args)
|
|
||||||
{
|
|
||||||
default_logger_raw()->trace(fmt, args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
inline void debug(const char *fmt, const Args &... args)
|
|
||||||
{
|
|
||||||
default_logger_raw()->debug(fmt, args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
inline void info(const char *fmt, const Args &... args)
|
|
||||||
{
|
|
||||||
default_logger_raw()->info(fmt, args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
inline void warn(const char *fmt, const Args &... args)
|
|
||||||
{
|
|
||||||
default_logger_raw()->warn(fmt, args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
inline void error(const char *fmt, const Args &... args)
|
|
||||||
{
|
|
||||||
default_logger_raw()->error(fmt, args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
inline void critical(const char *fmt, const Args &... args)
|
|
||||||
{
|
|
||||||
default_logger_raw()->critical(fmt, args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
inline void log(level::level_enum lvl, const T &msg)
|
|
||||||
{
|
|
||||||
default_logger_raw()->log(lvl, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
inline void trace(const T &msg)
|
|
||||||
{
|
|
||||||
default_logger_raw()->trace(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
inline void debug(const T &msg)
|
|
||||||
{
|
|
||||||
default_logger_raw()->debug(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
inline void info(const T &msg)
|
|
||||||
{
|
|
||||||
default_logger_raw()->info(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
inline void warn(const T &msg)
|
|
||||||
{
|
|
||||||
default_logger_raw()->warn(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
inline void error(const T &msg)
|
|
||||||
{
|
|
||||||
default_logger_raw()->error(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
inline void critical(const T &msg)
|
|
||||||
{
|
|
||||||
default_logger_raw()->critical(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
|
||||||
template<typename... Args>
|
|
||||||
inline void log(level::level_enum lvl, const wchar_t *fmt, const Args &... args)
|
|
||||||
{
|
|
||||||
default_logger_raw()->log(lvl, fmt, args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
inline void trace(const wchar_t *fmt, const Args &... args)
|
|
||||||
{
|
|
||||||
default_logger_raw()->trace(fmt, args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
inline void debug(const wchar_t *fmt, const Args &... args)
|
|
||||||
{
|
|
||||||
default_logger_raw()->debug(fmt, args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
inline void info(const wchar_t *fmt, const Args &... args)
|
|
||||||
{
|
|
||||||
default_logger_raw()->info(fmt, args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
inline void warn(const wchar_t *fmt, const Args &... args)
|
|
||||||
{
|
|
||||||
default_logger_raw()->warn(fmt, args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
inline void error(const wchar_t *fmt, const Args &... args)
|
|
||||||
{
|
|
||||||
default_logger_raw()->error(fmt, args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
inline void critical(const wchar_t *fmt, const Args &... args)
|
|
||||||
{
|
|
||||||
default_logger_raw()->critical(fmt, args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
|
||||||
|
|
||||||
} // namespace spdlog
|
|
||||||
|
|
||||||
//
|
|
||||||
// enable/disable log calls at compile time according to global level.
|
|
||||||
//
|
|
||||||
// define SPDLOG_ACTIVE_LEVEL to one of those (before including spdlog.h):
|
|
||||||
// SPDLOG_LEVEL_TRACE,
|
|
||||||
// SPDLOG_LEVEL_DEBUG,
|
|
||||||
// SPDLOG_LEVEL_INFO,
|
|
||||||
// SPDLOG_LEVEL_WARN,
|
|
||||||
// SPDLOG_LEVEL_ERROR,
|
|
||||||
// SPDLOG_LEVEL_CRITICAL,
|
|
||||||
// SPDLOG_LEVEL_OFF
|
|
||||||
//
|
|
||||||
|
|
||||||
#define SPDLOG_LOGGER_CALL(logger, level, ...) \
|
|
||||||
if (logger->should_log(level)) \
|
|
||||||
logger->log(spdlog::source_loc{SPDLOG_FILE_BASENAME(__FILE__), __LINE__, SPDLOG_FUNCTION}, level, __VA_ARGS__)
|
|
||||||
|
|
||||||
#if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_TRACE
|
|
||||||
#define SPDLOG_LOGGER_TRACE(logger, ...) SPDLOG_LOGGER_CALL(logger, spdlog::level::trace, __VA_ARGS__)
|
|
||||||
#define SPDLOG_TRACE(...) SPDLOG_LOGGER_TRACE(spdlog::default_logger_raw(), __VA_ARGS__)
|
|
||||||
#else
|
|
||||||
#define SPDLOG_LOGGER_TRACE(logger, ...) (void)0
|
|
||||||
#define SPDLOG_TRACE(...) (void)0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_DEBUG
|
|
||||||
#define SPDLOG_LOGGER_DEBUG(logger, ...) SPDLOG_LOGGER_CALL(logger, spdlog::level::debug, __VA_ARGS__)
|
|
||||||
#define SPDLOG_DEBUG(...) SPDLOG_LOGGER_DEBUG(spdlog::default_logger_raw(), __VA_ARGS__)
|
|
||||||
#else
|
|
||||||
#define SPDLOG_LOGGER_DEBUG(logger, ...) (void)0
|
|
||||||
#define SPDLOG_DEBUG(...) (void)0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_INFO
|
|
||||||
#define SPDLOG_LOGGER_INFO(logger, ...) SPDLOG_LOGGER_CALL(logger, spdlog::level::info, __VA_ARGS__)
|
|
||||||
#define SPDLOG_INFO(...) SPDLOG_LOGGER_INFO(spdlog::default_logger_raw(), __VA_ARGS__)
|
|
||||||
#else
|
|
||||||
#define SPDLOG_LOGGER_INFO(logger, ...) (void)0
|
|
||||||
#define SPDLOG_INFO(...) (void)0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_WARN
|
|
||||||
#define SPDLOG_LOGGER_WARN(logger, ...) SPDLOG_LOGGER_CALL(logger, spdlog::level::warn, __VA_ARGS__)
|
|
||||||
#define SPDLOG_WARN(...) SPDLOG_LOGGER_WARN(spdlog::default_logger_raw(), __VA_ARGS__)
|
|
||||||
#else
|
|
||||||
#define SPDLOG_LOGGER_WARN(logger, ...) (void)0
|
|
||||||
#define SPDLOG_WARN(...) (void)0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_ERROR
|
|
||||||
#define SPDLOG_LOGGER_ERROR(logger, ...) SPDLOG_LOGGER_CALL(logger, spdlog::level::err, __VA_ARGS__)
|
|
||||||
#define SPDLOG_ERROR(...) SPDLOG_LOGGER_ERROR(spdlog::default_logger_raw(), __VA_ARGS__)
|
|
||||||
#else
|
|
||||||
#define SPDLOG_LOGGER_ERROR(logger, ...) (void)0
|
|
||||||
#define SPDLOG_ERROR(...) (void)0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_CRITICAL
|
|
||||||
#define SPDLOG_LOGGER_CRITICAL(logger, ...) SPDLOG_LOGGER_CALL(logger, spdlog::level::critical, __VA_ARGS__)
|
|
||||||
#define SPDLOG_CRITICAL(...) SPDLOG_LOGGER_CRITICAL(spdlog::default_logger_raw(), __VA_ARGS__)
|
|
||||||
#else
|
|
||||||
#define SPDLOG_LOGGER_CRITICAL(logger, ...) (void)0
|
|
||||||
#define SPDLOG_CRITICAL(...) (void)0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // SPDLOG_H
|
|
@ -1,152 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright(c) 2015 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Edit this file to squeeze more performance, and to customize supported
|
|
||||||
// features
|
|
||||||
//
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Under Linux, the much faster CLOCK_REALTIME_COARSE clock can be used.
|
|
||||||
// This clock is less accurate - can be off by dozens of millis - depending on
|
|
||||||
// the kernel HZ.
|
|
||||||
// Uncomment to use it instead of the regular clock.
|
|
||||||
//
|
|
||||||
// #define SPDLOG_CLOCK_COARSE
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Uncomment if date/time logging is not needed and never appear in the log
|
|
||||||
// pattern.
|
|
||||||
// This will prevent spdlog from querying the clock on each log call.
|
|
||||||
//
|
|
||||||
// WARNING: If the log pattern contains any date/time while this flag is on, the
|
|
||||||
// result is undefined.
|
|
||||||
// You must set new pattern(spdlog::set_pattern(..") without any
|
|
||||||
// date/time in it
|
|
||||||
//
|
|
||||||
// #define SPDLOG_NO_DATETIME
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Uncomment if thread id logging is not needed (i.e. no %t in the log pattern).
|
|
||||||
// This will prevent spdlog from querying the thread id on each log call.
|
|
||||||
//
|
|
||||||
// WARNING: If the log pattern contains thread id (i.e, %t) while this flag is
|
|
||||||
// on, the result is undefined.
|
|
||||||
//
|
|
||||||
// #define SPDLOG_NO_THREAD_ID
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Uncomment to prevent spdlog from using thread local storage.
|
|
||||||
//
|
|
||||||
// WARNING: if your program forks, UNCOMMENT this flag to prevent undefined
|
|
||||||
// thread ids in the children logs.
|
|
||||||
//
|
|
||||||
// #define SPDLOG_NO_TLS
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Uncomment if logger name logging is not needed.
|
|
||||||
// This will prevent spdlog from copying the logger name on each log call.
|
|
||||||
//
|
|
||||||
// #define SPDLOG_NO_NAME
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Uncomment to enable the SPDLOG_DEBUG/SPDLOG_TRACE macros.
|
|
||||||
//
|
|
||||||
// #define SPDLOG_DEBUG_ON
|
|
||||||
// #define SPDLOG_TRACE_ON
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Uncomment to avoid spdlog's usage of atomic log levels
|
|
||||||
// Use only if your code never modifies a logger's log levels concurrently by
|
|
||||||
// different threads.
|
|
||||||
//
|
|
||||||
// #define SPDLOG_NO_ATOMIC_LEVELS
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Uncomment to enable usage of wchar_t for file names on Windows.
|
|
||||||
//
|
|
||||||
// #define SPDLOG_WCHAR_FILENAMES
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Uncomment to override default eol ("\n" or "\r\n" under Linux/Windows)
|
|
||||||
//
|
|
||||||
// #define SPDLOG_EOL ";-)\n"
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Uncomment to use your own copy of the fmt library instead of spdlog's copy.
|
|
||||||
// In this case spdlog will try to include <fmt/format.h> so set your -I flag
|
|
||||||
// accordingly.
|
|
||||||
//
|
|
||||||
// #define SPDLOG_FMT_EXTERNAL
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Uncomment to enable wchar_t support (convert to utf8)
|
|
||||||
//
|
|
||||||
// #define SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Uncomment to prevent child processes from inheriting log file descriptors
|
|
||||||
//
|
|
||||||
// #define SPDLOG_PREVENT_CHILD_FD
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Uncomment to enable message counting feature.
|
|
||||||
// Use the %i in the logger pattern to display log message sequence id.
|
|
||||||
//
|
|
||||||
// #define SPDLOG_ENABLE_MESSAGE_COUNTER
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Uncomment to customize level names (e.g. "MT TRACE")
|
|
||||||
//
|
|
||||||
// #define SPDLOG_LEVEL_NAMES { "MY TRACE", "MY DEBUG", "MY INFO", "MY WARNING",
|
|
||||||
// "MY ERROR", "MY CRITICAL", "OFF" }
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Uncomment to customize short level names (e.g. "MT")
|
|
||||||
// These can be longer than one character.
|
|
||||||
//
|
|
||||||
// #define SPDLOG_SHORT_LEVEL_NAMES { "T", "D", "I", "W", "E", "C", "O" }
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Uncomment to disable default logger creation.
|
|
||||||
// This might save some (very) small initialization time if no default logger is needed.
|
|
||||||
//
|
|
||||||
// #define SPDLOG_DISABLE_DEFAULT_LOGGER
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Uncomment and set to compile time level with zero cost (default is INFO).
|
|
||||||
// Macros like SPDLOG_DEBUG(..), SPDLOG_INFO(..) will expand to empty statements if not enabled
|
|
||||||
//
|
|
||||||
// #define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Uncomment (and change if desired) macro to use for function names.
|
|
||||||
// This is compiler dependent.
|
|
||||||
// __PRETTY_FUNCTION__ might be nicer in clang/gcc, and __FUNCTION__ in msvc.
|
|
||||||
// Defaults to __FUNCTION__ (should work on all compilers) if not defined.
|
|
||||||
//
|
|
||||||
// #define SPDLOG_FUNCTION __PRETTY_FUNCTION__
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
@ -1,12 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright(c) 2015 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#define SPDLOG_VER_MAJOR 1
|
|
||||||
#define SPDLOG_VER_MINOR 3
|
|
||||||
#define SPDLOG_VER_PATCH 1
|
|
||||||
|
|
||||||
#define SPDLOG_VERSION (SPDLOG_VER_MAJOR * 10000 + SPDLOG_VER_MINOR * 100 + SPDLOG_VER_PATCH)
|
|
@ -1,31 +0,0 @@
|
|||||||
project(OpenDiablo2.Common VERSION 0.1 LANGUAGES CXX)
|
|
||||||
|
|
||||||
set (SOURCES
|
|
||||||
src/D2Sprite.cpp
|
|
||||||
src/D2DataManager.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
set (HEADERS
|
|
||||||
include/OpenDiablo2.Common/D2EngineConfig.h
|
|
||||||
include/OpenDiablo2.Common/D2DataManager.h
|
|
||||||
include/OpenDiablo2.Common/D2Palette.h
|
|
||||||
include/OpenDiablo2.Common/D2Point.h
|
|
||||||
include/OpenDiablo2.Common/D2ResourcePath.h
|
|
||||||
include/OpenDiablo2.Common/D2Size.h
|
|
||||||
include/OpenDiablo2.Common/D2Sprite.h
|
|
||||||
)
|
|
||||||
|
|
||||||
add_library(OpenDiablo2.Common SHARED ${SOURCES} ${HEADERS})
|
|
||||||
|
|
||||||
target_include_directories(OpenDiablo2.Common
|
|
||||||
PUBLIC
|
|
||||||
include
|
|
||||||
PRIVATE
|
|
||||||
include
|
|
||||||
../ExtraUtils
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
set_target_properties(OpenDiablo2.Common PROPERTIES
|
|
||||||
CXX_STANDARD 17
|
|
||||||
)
|
|
@ -1,21 +0,0 @@
|
|||||||
#ifndef OPENDIABLO2_COMMON_D2DATAMANAGER_H
|
|
||||||
#define OPENDIABLO2_COMMON_D2DATAMANAGER_H
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <map>
|
|
||||||
#include <string>
|
|
||||||
#include <OpenDiablo2.Common/D2EngineConfig.h>
|
|
||||||
|
|
||||||
namespace OpenDiablo2::Common {
|
|
||||||
|
|
||||||
class D2DataManager {
|
|
||||||
public:
|
|
||||||
typedef std::unique_ptr<D2DataManager> Ptr;
|
|
||||||
D2DataManager(const D2EngineConfig& engineConfig);
|
|
||||||
private:
|
|
||||||
std::map<std::string, std::string> fileEntries;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // OPENDIABLO2_COMMON_D2DATAMANAGER_H
|
|
@ -1,16 +0,0 @@
|
|||||||
#ifndef OPENDIABLO2_COMMON_D2ENGINECONFIG_H
|
|
||||||
#define OPENDIABLO2_COMMON_D2ENGINECONFIG_H
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace OpenDiablo2::Common
|
|
||||||
{
|
|
||||||
|
|
||||||
struct D2EngineConfig
|
|
||||||
{
|
|
||||||
std::string BasePath; // The base path where the MPQ files are located
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif //OPENDIABLO2_COMMON_D2ENGINECONFIG_H
|
|
@ -1,55 +0,0 @@
|
|||||||
#ifndef OPENDIABLO2_COMMON_D2PALETTE_H
|
|
||||||
#define OPENDIABLO2_COMMON_D2PALETTE_H
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace OpenDiablo2::Common {
|
|
||||||
class D2Palette {
|
|
||||||
public:
|
|
||||||
typedef std::string Entry;
|
|
||||||
static const Entry Act1;
|
|
||||||
static const Entry Act2;
|
|
||||||
static const Entry Act3;
|
|
||||||
static const Entry Act4;
|
|
||||||
static const Entry Act5;
|
|
||||||
static const Entry EndGame;
|
|
||||||
static const Entry EndGame2;
|
|
||||||
static const Entry Fechar;
|
|
||||||
static const Entry Loading;
|
|
||||||
static const Entry Menu0;
|
|
||||||
static const Entry Menu1;
|
|
||||||
static const Entry Menu2;
|
|
||||||
static const Entry Menu3;
|
|
||||||
static const Entry Menu4;
|
|
||||||
static const Entry Sky;
|
|
||||||
static const Entry Static;
|
|
||||||
static const Entry Trademark;
|
|
||||||
static const Entry Units;
|
|
||||||
private:
|
|
||||||
D2Palette() {}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
const D2Palette::Entry D2Palette::Act1 = "ACT1";
|
|
||||||
const D2Palette::Entry D2Palette::Act2 = "ACT2";
|
|
||||||
const D2Palette::Entry D2Palette::Act3 = "ACT3";
|
|
||||||
const D2Palette::Entry D2Palette::Act4 = "ACT4";
|
|
||||||
const D2Palette::Entry D2Palette::Act5 = "ACT5";
|
|
||||||
const D2Palette::Entry D2Palette::EndGame = "EndGame";
|
|
||||||
const D2Palette::Entry D2Palette::EndGame2 = "EndGame2";
|
|
||||||
const D2Palette::Entry D2Palette::Fechar = "fechar";
|
|
||||||
const D2Palette::Entry D2Palette::Loading = "loading";
|
|
||||||
const D2Palette::Entry D2Palette::Menu0 = "Menu0";
|
|
||||||
const D2Palette::Entry D2Palette::Menu1 = "menu1";
|
|
||||||
const D2Palette::Entry D2Palette::Menu2 = "menu2";
|
|
||||||
const D2Palette::Entry D2Palette::Menu3 = "menu3";
|
|
||||||
const D2Palette::Entry D2Palette::Menu4 = "menu4";
|
|
||||||
const D2Palette::Entry D2Palette::Sky = "Sky";
|
|
||||||
const D2Palette::Entry D2Palette::Static = "STATIC";
|
|
||||||
const D2Palette::Entry D2Palette::Trademark = "Trademark";
|
|
||||||
const D2Palette::Entry D2Palette::Units = "Units";
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // OPENDIABLO2_COMMON_D2PALETTE_H
|
|
@ -1,13 +0,0 @@
|
|||||||
#ifndef OPENDIABLO2_COMMON_D2POINT_H
|
|
||||||
#define OPENDIABLO2_COMMON_D2POINT_H
|
|
||||||
|
|
||||||
namespace OpenDiablo2::Common {
|
|
||||||
|
|
||||||
struct D2Point {
|
|
||||||
int X;
|
|
||||||
int Y;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // OPENDIABLO2_COMMON_D2POINT_H
|
|
@ -1,467 +0,0 @@
|
|||||||
#ifndef OPENDIABLO2_COMMON_D2RESOURCEPATH_H
|
|
||||||
#define OPENDIABLO2_COMMON_D2RESOURCEPATH_H
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace OpenDiablo2::Common {
|
|
||||||
|
|
||||||
class D2ResourcePath {
|
|
||||||
public:
|
|
||||||
// --- Loading Screen ---
|
|
||||||
static const std::string LoadingScreen;
|
|
||||||
|
|
||||||
// --- Main Menu ---
|
|
||||||
static const std::string GameSelectScreen;
|
|
||||||
static const std::string Diablo2LogoFireLeft;
|
|
||||||
static const std::string Diablo2LogoFireRight;
|
|
||||||
static const std::string Diablo2LogoBlackLeft;
|
|
||||||
static const std::string Diablo2LogoBlackRight;
|
|
||||||
|
|
||||||
// --- Credits ---
|
|
||||||
static const std::string CreditsBackground;
|
|
||||||
static const std::string CreditsText;
|
|
||||||
|
|
||||||
// --- Character Select Screen ---
|
|
||||||
static const std::string CharacterSelectBackground;
|
|
||||||
static const std::string CharacterSelectCampfire;
|
|
||||||
|
|
||||||
static const std::string CharacterSelectBarbarianUnselected;
|
|
||||||
static const std::string CharacterSelectBarbarianUnselectedH;
|
|
||||||
static const std::string CharacterSelectBarbarianSelected;
|
|
||||||
static const std::string CharacterSelectBarbarianForwardWalk;
|
|
||||||
static const std::string CharacterSelectBarbarianForwardWalkOverlay;
|
|
||||||
static const std::string CharacterSelectBarbarianBackWalk;
|
|
||||||
|
|
||||||
static const std::string CharacterSelecSorceressUnselected;
|
|
||||||
static const std::string CharacterSelecSorceressUnselectedH;
|
|
||||||
static const std::string CharacterSelecSorceressSelected;
|
|
||||||
static const std::string CharacterSelecSorceressSelectedOverlay;
|
|
||||||
static const std::string CharacterSelecSorceressForwardWalk;
|
|
||||||
static const std::string CharacterSelecSorceressForwardWalkOverlay;
|
|
||||||
static const std::string CharacterSelecSorceressBackWalk;
|
|
||||||
static const std::string CharacterSelecSorceressBackWalkOverlay;
|
|
||||||
|
|
||||||
static const std::string CharacterSelectNecromancerUnselected;
|
|
||||||
static const std::string CharacterSelectNecromancerUnselectedH;
|
|
||||||
static const std::string CharacterSelecNecromancerSelected;
|
|
||||||
static const std::string CharacterSelecNecromancerSelectedOverlay;
|
|
||||||
static const std::string CharacterSelecNecromancerForwardWalk;
|
|
||||||
static const std::string CharacterSelecNecromancerForwardWalkOverlay;
|
|
||||||
static const std::string CharacterSelecNecromancerBackWalk;
|
|
||||||
static const std::string CharacterSelecNecromancerBackWalkOverlay;
|
|
||||||
|
|
||||||
static const std::string CharacterSelectPaladinUnselected;
|
|
||||||
static const std::string CharacterSelectPaladinUnselectedH;
|
|
||||||
static const std::string CharacterSelecPaladinSelected;
|
|
||||||
static const std::string CharacterSelecPaladinForwardWalk;
|
|
||||||
static const std::string CharacterSelecPaladinForwardWalkOverlay;
|
|
||||||
static const std::string CharacterSelecPaladinBackWalk;
|
|
||||||
|
|
||||||
|
|
||||||
static const std::string CharacterSelectAmazonUnselected;
|
|
||||||
static const std::string CharacterSelectAmazonUnselectedH;
|
|
||||||
static const std::string CharacterSelecAmazonSelected;
|
|
||||||
static const std::string CharacterSelecAmazonForwardWalk;
|
|
||||||
static const std::string CharacterSelecAmazonForwardWalkOverlay;
|
|
||||||
static const std::string CharacterSelecAmazonBackWalk;
|
|
||||||
|
|
||||||
static const std::string CharacterSelectAssassinUnselected;
|
|
||||||
static const std::string CharacterSelectAssassinUnselectedH;
|
|
||||||
static const std::string CharacterSelectAssassinSelected;
|
|
||||||
static const std::string CharacterSelectAssassinForwardWalk;
|
|
||||||
static const std::string CharacterSelectAssassinBackWalk;
|
|
||||||
|
|
||||||
static const std::string CharacterSelectDruidUnselected;
|
|
||||||
static const std::string CharacterSelectDruidUnselectedH;
|
|
||||||
static const std::string CharacterSelectDruidSelected;
|
|
||||||
static const std::string CharacterSelectDruidForwardWalk;
|
|
||||||
static const std::string CharacterSelectDruidBackWalk;
|
|
||||||
|
|
||||||
// -- Character Selection
|
|
||||||
static const std::string CharacterSelectionBackground;
|
|
||||||
|
|
||||||
// --- Game ---
|
|
||||||
static const std::string GamePanels;
|
|
||||||
static const std::string GameGlobeOverlap;
|
|
||||||
static const std::string HealthMana;
|
|
||||||
static const std::string GameSmallMenuButton;
|
|
||||||
static const std::string SkillIcon;
|
|
||||||
static const std::string AddSkillButton;
|
|
||||||
|
|
||||||
// --- Mouse Pointers ---
|
|
||||||
static const std::string CursorDefault;
|
|
||||||
|
|
||||||
// --- Fonts ---
|
|
||||||
static const std::string Font6;
|
|
||||||
static const std::string Font8;
|
|
||||||
static const std::string Font16;
|
|
||||||
static const std::string Font24;
|
|
||||||
static const std::string Font30;
|
|
||||||
static const std::string FontFormal12;
|
|
||||||
static const std::string FontFormal11;
|
|
||||||
static const std::string FontFormal10;
|
|
||||||
static const std::string FontExocet10;
|
|
||||||
static const std::string FontExocet8;
|
|
||||||
|
|
||||||
// --- UI ---
|
|
||||||
static const std::string WideButtonBlank;
|
|
||||||
static const std::string MediumButtonBlank;
|
|
||||||
static const std::string CancelButton;
|
|
||||||
static const std::string NarrowButtonBlank;
|
|
||||||
static const std::string ShortButtonBlank;
|
|
||||||
static const std::string TextBox2;
|
|
||||||
static const std::string TallButtonBlank;
|
|
||||||
|
|
||||||
// --- GAME UI ---
|
|
||||||
static const std::string MinipanelSmall;
|
|
||||||
static const std::string MinipanelButton;
|
|
||||||
|
|
||||||
static const std::string Frame;
|
|
||||||
static const std::string InventoryCharacterPanel;
|
|
||||||
static const std::string InventoryWeaponsTab;
|
|
||||||
static const std::string SkillsPanelAmazon;
|
|
||||||
static const std::string SkillsPanelBarbarian;
|
|
||||||
static const std::string SkillsPanelDruid;
|
|
||||||
static const std::string SkillsPanelAssassin;
|
|
||||||
static const std::string SkillsPanelNecromancer;
|
|
||||||
static const std::string SkillsPanelPaladin;
|
|
||||||
static const std::string SkillsPanelSorcerer;
|
|
||||||
|
|
||||||
static const std::string GenericSkills;
|
|
||||||
static const std::string AmazonSkills;
|
|
||||||
static const std::string BarbarianSkills;
|
|
||||||
static const std::string DruidSkills;
|
|
||||||
static const std::string AssassinSkills;
|
|
||||||
static const std::string NecromancerSkills;
|
|
||||||
static const std::string PaladinSkills;
|
|
||||||
static const std::string SorcererSkills;
|
|
||||||
|
|
||||||
static const std::string RunButton;
|
|
||||||
static const std::string MenuButton;
|
|
||||||
static const std::string GoldCoinButton;
|
|
||||||
static const std::string SquareButton;
|
|
||||||
|
|
||||||
static const std::string ArmorPlaceholder;
|
|
||||||
static const std::string BeltPlaceholder;
|
|
||||||
static const std::string BootsPlaceholder;
|
|
||||||
static const std::string HelmGlovePlaceholder;
|
|
||||||
static const std::string RingAmuletPlaceholder;
|
|
||||||
static const std::string WeaponsPlaceholder;
|
|
||||||
|
|
||||||
// --- Data ---
|
|
||||||
static const std::string EnglishTable;
|
|
||||||
static const std::string ExpansionStringTable;
|
|
||||||
static const std::string LevelPreset;
|
|
||||||
static const std::string LevelType;
|
|
||||||
static const std::string LevelDetails;
|
|
||||||
static const std::string ObjectDetails;
|
|
||||||
|
|
||||||
// --- Animations ---
|
|
||||||
static const std::string ObjectData;
|
|
||||||
static const std::string AnimationData;
|
|
||||||
static const std::string PlayerAnimationBase;
|
|
||||||
|
|
||||||
// --- Inventory Data ---
|
|
||||||
static const std::string Weapons;
|
|
||||||
static const std::string Armor;
|
|
||||||
static const std::string Misc;
|
|
||||||
|
|
||||||
// --- Character Data ---
|
|
||||||
static const std::string Experience;
|
|
||||||
static const std::string CharStats;
|
|
||||||
|
|
||||||
// --- Music ---
|
|
||||||
static const std::string BGMTitle;
|
|
||||||
static const std::string BGMOptions;
|
|
||||||
static const std::string BGMAct1AndarielAction;
|
|
||||||
static const std::string BGMAct1BloodRavenResolution;
|
|
||||||
static const std::string BGMAct1Caves;
|
|
||||||
static const std::string BGMAct1Crypt;
|
|
||||||
static const std::string BGMAct1DenOfEvilAction;
|
|
||||||
static const std::string BGMAct1Monastery;
|
|
||||||
static const std::string BGMAct1Town1;
|
|
||||||
static const std::string BGMAct1Tristram;
|
|
||||||
static const std::string BGMAct1Wild;
|
|
||||||
static const std::string BGMAct2Desert;
|
|
||||||
static const std::string BGMAct2Harem;
|
|
||||||
static const std::string BGMAct2HoradricAction;
|
|
||||||
static const std::string BGMAct2Lair;
|
|
||||||
static const std::string BGMAct2RadamentResolution;
|
|
||||||
static const std::string BGMAct2Sanctuary;
|
|
||||||
static const std::string BGMAct2Sewer;
|
|
||||||
static const std::string BGMAct2TaintedSunAction;
|
|
||||||
static const std::string BGMAct2Tombs;
|
|
||||||
static const std::string BGMAct2Town2;
|
|
||||||
static const std::string BGMAct2Valley;
|
|
||||||
static const std::string BGMAct3Jungle;
|
|
||||||
static const std::string BGMAct3Kurast;
|
|
||||||
static const std::string BGMAct3KurastSewer;
|
|
||||||
static const std::string BGMAct3MefDeathAction;
|
|
||||||
static const std::string BGMAct3OrbAction;
|
|
||||||
static const std::string BGMAct3Spider;
|
|
||||||
static const std::string BGMAct3Town3;
|
|
||||||
static const std::string BGMAct4Diablo;
|
|
||||||
static const std::string BGMAct4DiabloAction;
|
|
||||||
static const std::string BGMAct4ForgeAction;
|
|
||||||
static const std::string BGMAct4IzualAction;
|
|
||||||
static const std::string BGMAct4Mesa;
|
|
||||||
static const std::string BGMAct4Town4;
|
|
||||||
static const std::string BGMAct5Baal;
|
|
||||||
static const std::string BGMAct5XTown;
|
|
||||||
|
|
||||||
|
|
||||||
// --- Sound Effects ---
|
|
||||||
static const std::string SFXButtonClick;
|
|
||||||
static const std::string SFXAmazonDeselect;
|
|
||||||
static const std::string SFXAmazonSelect;
|
|
||||||
static const std::string SFXAssassinDeselect;
|
|
||||||
static const std::string SFXAssassinSelect;
|
|
||||||
static const std::string SFXBarbarianDeselect;
|
|
||||||
static const std::string SFXBarbarianSelect;
|
|
||||||
static const std::string SFXDruidDeselect;
|
|
||||||
static const std::string SFXDruidSelect;
|
|
||||||
static const std::string SFXNecromancerDeselect;
|
|
||||||
static const std::string SFXNecromancerSelect;
|
|
||||||
static const std::string SFXPaladinDeselect;
|
|
||||||
static const std::string SFXPaladinSelect;
|
|
||||||
static const std::string SFXSorceressDeselect;
|
|
||||||
static const std::string SFXSorceressSelect;
|
|
||||||
|
|
||||||
// --- Enemy Data ---
|
|
||||||
static const std::string MonStats;
|
|
||||||
|
|
||||||
// --- Skill Data ---
|
|
||||||
static const std::string Missiles;
|
|
||||||
private:
|
|
||||||
D2ResourcePath() { }
|
|
||||||
};
|
|
||||||
|
|
||||||
// --- Loading Screen ---
|
|
||||||
const std::string D2ResourcePath::LoadingScreen = "data\\global\\ui\\Loading\\loadingscreen.dc6";
|
|
||||||
|
|
||||||
// --- Main Menu ---
|
|
||||||
const std::string D2ResourcePath::GameSelectScreen = "data\\global\\ui\\FrontEnd\\gameselectscreenEXP.dc6";
|
|
||||||
const std::string D2ResourcePath::Diablo2LogoFireLeft = "data\\global\\ui\\FrontEnd\\D2logoFireLeft.DC6";
|
|
||||||
const std::string D2ResourcePath::Diablo2LogoFireRight = "data\\global\\ui\\FrontEnd\\D2logoFireRight.DC6";
|
|
||||||
const std::string D2ResourcePath::Diablo2LogoBlackLeft = "data\\global\\ui\\FrontEnd\\D2logoBlackLeft.DC6";
|
|
||||||
const std::string D2ResourcePath::Diablo2LogoBlackRight = "data\\global\\ui\\FrontEnd\\D2logoBlackRight.DC6";
|
|
||||||
|
|
||||||
// --- Credits ---
|
|
||||||
const std::string D2ResourcePath::CreditsBackground = "data\\global\\ui\\CharSelect\\creditsbckgexpand.dc6";
|
|
||||||
const std::string D2ResourcePath::CreditsText = "data\\local\\ui\\eng\\ExpansionCredits.txt";
|
|
||||||
|
|
||||||
// --- Character Select Screen ---
|
|
||||||
const std::string D2ResourcePath::CharacterSelectBackground = "data\\global\\ui\\FrontEnd\\charactercreationscreenEXP.dc6";
|
|
||||||
const std::string D2ResourcePath::CharacterSelectCampfire = "data\\global\\ui\\FrontEnd\\fire.DC6";
|
|
||||||
|
|
||||||
const std::string D2ResourcePath::CharacterSelectBarbarianUnselected = "data\\global\\ui\\FrontEnd\\barbarian\\banu1.DC6";
|
|
||||||
const std::string D2ResourcePath::CharacterSelectBarbarianUnselectedH = "data\\global\\ui\\FrontEnd\\barbarian\\banu2.DC6";
|
|
||||||
const std::string D2ResourcePath::CharacterSelectBarbarianSelected = "data\\global\\ui\\FrontEnd\\barbarian\\banu3.DC6";
|
|
||||||
const std::string D2ResourcePath::CharacterSelectBarbarianForwardWalk = "data\\global\\ui\\FrontEnd\\barbarian\\bafw.DC6";
|
|
||||||
const std::string D2ResourcePath::CharacterSelectBarbarianForwardWalkOverlay = "data\\global\\ui\\FrontEnd\\barbarian\\BAFWs.DC6";
|
|
||||||
const std::string D2ResourcePath::CharacterSelectBarbarianBackWalk = "data\\global\\ui\\FrontEnd\\barbarian\\babw.DC6";
|
|
||||||
|
|
||||||
const std::string D2ResourcePath::CharacterSelecSorceressUnselected = "data\\global\\ui\\FrontEnd\\sorceress\\SONU1.DC6";
|
|
||||||
const std::string D2ResourcePath::CharacterSelecSorceressUnselectedH = "data\\global\\ui\\FrontEnd\\sorceress\\SONU2.DC6";
|
|
||||||
const std::string D2ResourcePath::CharacterSelecSorceressSelected = "data\\global\\ui\\FrontEnd\\sorceress\\SONU3.DC6";
|
|
||||||
const std::string D2ResourcePath::CharacterSelecSorceressSelectedOverlay = "data\\global\\ui\\FrontEnd\\sorceress\\SONU3s.DC6";
|
|
||||||
const std::string D2ResourcePath::CharacterSelecSorceressForwardWalk = "data\\global\\ui\\FrontEnd\\sorceress\\SOFW.DC6";
|
|
||||||
const std::string D2ResourcePath::CharacterSelecSorceressForwardWalkOverlay = "data\\global\\ui\\FrontEnd\\sorceress\\SOFWs.DC6";
|
|
||||||
const std::string D2ResourcePath::CharacterSelecSorceressBackWalk = "data\\global\\ui\\FrontEnd\\sorceress\\SOBW.DC6";
|
|
||||||
const std::string D2ResourcePath::CharacterSelecSorceressBackWalkOverlay = "data\\global\\ui\\FrontEnd\\sorceress\\SOBWs.DC6";
|
|
||||||
|
|
||||||
const std::string D2ResourcePath::CharacterSelectNecromancerUnselected = "data\\global\\ui\\FrontEnd\\necromancer\\NENU1.DC6";
|
|
||||||
const std::string D2ResourcePath::CharacterSelectNecromancerUnselectedH = "data\\global\\ui\\FrontEnd\\necromancer\\NENU2.DC6";
|
|
||||||
const std::string D2ResourcePath::CharacterSelecNecromancerSelected = "data\\global\\ui\\FrontEnd\\necromancer\\NENU3.DC6";
|
|
||||||
const std::string D2ResourcePath::CharacterSelecNecromancerSelectedOverlay = "data\\global\\ui\\FrontEnd\\necromancer\\NENU3s.DC6";
|
|
||||||
const std::string D2ResourcePath::CharacterSelecNecromancerForwardWalk = "data\\global\\ui\\FrontEnd\\necromancer\\NEFW.DC6";
|
|
||||||
const std::string D2ResourcePath::CharacterSelecNecromancerForwardWalkOverlay = "data\\global\\ui\\FrontEnd\\necromancer\\NEFWs.DC6";
|
|
||||||
const std::string D2ResourcePath::CharacterSelecNecromancerBackWalk = "data\\global\\ui\\FrontEnd\\necromancer\\NEBW.DC6";
|
|
||||||
const std::string D2ResourcePath::CharacterSelecNecromancerBackWalkOverlay = "data\\global\\ui\\FrontEnd\\necromancer\\NEBWs.DC6";
|
|
||||||
|
|
||||||
const std::string D2ResourcePath::CharacterSelectPaladinUnselected = "data\\global\\ui\\FrontEnd\\paladin\\PANU1.DC6";
|
|
||||||
const std::string D2ResourcePath::CharacterSelectPaladinUnselectedH = "data\\global\\ui\\FrontEnd\\paladin\\PANU2.DC6";
|
|
||||||
const std::string D2ResourcePath::CharacterSelecPaladinSelected = "data\\global\\ui\\FrontEnd\\paladin\\PANU3.DC6";
|
|
||||||
const std::string D2ResourcePath::CharacterSelecPaladinForwardWalk = "data\\global\\ui\\FrontEnd\\paladin\\PAFW.DC6";
|
|
||||||
const std::string D2ResourcePath::CharacterSelecPaladinForwardWalkOverlay = "data\\global\\ui\\FrontEnd\\paladin\\PAFWs.DC6";
|
|
||||||
const std::string D2ResourcePath::CharacterSelecPaladinBackWalk = "data\\global\\ui\\FrontEnd\\paladin\\PABW.DC6";
|
|
||||||
|
|
||||||
|
|
||||||
const std::string D2ResourcePath::CharacterSelectAmazonUnselected = "data\\global\\ui\\FrontEnd\\amazon\\AMNU1.DC6";
|
|
||||||
const std::string D2ResourcePath::CharacterSelectAmazonUnselectedH = "data\\global\\ui\\FrontEnd\\amazon\\AMNU2.DC6";
|
|
||||||
const std::string D2ResourcePath::CharacterSelecAmazonSelected = "data\\global\\ui\\FrontEnd\\amazon\\AMNU3.DC6";
|
|
||||||
const std::string D2ResourcePath::CharacterSelecAmazonForwardWalk = "data\\global\\ui\\FrontEnd\\amazon\\AMFW.DC6";
|
|
||||||
const std::string D2ResourcePath::CharacterSelecAmazonForwardWalkOverlay = "data\\global\\ui\\FrontEnd\\amazon\\AMFWs.DC6";
|
|
||||||
const std::string D2ResourcePath::CharacterSelecAmazonBackWalk = "data\\global\\ui\\FrontEnd\\amazon\\AMBW.DC6";
|
|
||||||
|
|
||||||
const std::string D2ResourcePath::CharacterSelectAssassinUnselected = "data\\global\\ui\\FrontEnd\\assassin\\ASNU1.DC6";
|
|
||||||
const std::string D2ResourcePath::CharacterSelectAssassinUnselectedH = "data\\global\\ui\\FrontEnd\\assassin\\ASNU2.DC6";
|
|
||||||
const std::string D2ResourcePath::CharacterSelectAssassinSelected = "data\\global\\ui\\FrontEnd\\assassin\\ASNU3.DC6";
|
|
||||||
const std::string D2ResourcePath::CharacterSelectAssassinForwardWalk = "data\\global\\ui\\FrontEnd\\assassin\\ASFW.DC6";
|
|
||||||
const std::string D2ResourcePath::CharacterSelectAssassinBackWalk = "data\\global\\ui\\FrontEnd\\assassin\\ASBW.DC6";
|
|
||||||
|
|
||||||
const std::string D2ResourcePath::CharacterSelectDruidUnselected = "data\\global\\ui\\FrontEnd\\druid\\DZNU1.dc6";
|
|
||||||
const std::string D2ResourcePath::CharacterSelectDruidUnselectedH = "data\\global\\ui\\FrontEnd\\druid\\DZNU2.dc6";
|
|
||||||
const std::string D2ResourcePath::CharacterSelectDruidSelected = "data\\global\\ui\\FrontEnd\\druid\\DZNU3.DC6";
|
|
||||||
const std::string D2ResourcePath::CharacterSelectDruidForwardWalk = "data\\global\\ui\\FrontEnd\\druid\\DZFW.DC6";
|
|
||||||
const std::string D2ResourcePath::CharacterSelectDruidBackWalk = "data\\global\\ui\\FrontEnd\\druid\\DZBW.DC6";
|
|
||||||
|
|
||||||
// -- Character Selection
|
|
||||||
const std::string D2ResourcePath::CharacterSelectionBackground = "data\\global\\ui\\CharSelect\\characterselectscreenEXP.dc6";
|
|
||||||
|
|
||||||
// --- Game ---
|
|
||||||
const std::string D2ResourcePath::GamePanels = "data\\global\\ui\\PANEL\\800ctrlpnl7.dc6";
|
|
||||||
const std::string D2ResourcePath::GameGlobeOverlap = "data\\global\\ui\\PANEL\\overlap.DC6";
|
|
||||||
const std::string D2ResourcePath::HealthMana = "data\\global\\ui\\PANEL\\hlthmana.DC6";
|
|
||||||
const std::string D2ResourcePath::GameSmallMenuButton = "data\\global\\ui\\PANEL\\menubutton.DC6"; // TODO: Used for inventory popout
|
|
||||||
const std::string D2ResourcePath::SkillIcon = "data\\global\\ui\\PANEL\\Skillicon.DC6"; // TODO: Used for skill icon button
|
|
||||||
const std::string D2ResourcePath::AddSkillButton = "data\\global\\ui\\PANEL\\level.DC6";
|
|
||||||
|
|
||||||
// --- Mouse Pointers ---
|
|
||||||
const std::string D2ResourcePath::CursorDefault = "data\\global\\ui\\CURSOR\\ohand.DC6";
|
|
||||||
|
|
||||||
// --- Fonts ---
|
|
||||||
const std::string D2ResourcePath::Font6 = "data\\local\\font\\latin\\font6";
|
|
||||||
const std::string D2ResourcePath::Font8 = "data\\local\\font\\latin\\font8";
|
|
||||||
const std::string D2ResourcePath::Font16 = "data\\local\\font\\latin\\font16";
|
|
||||||
const std::string D2ResourcePath::Font24 = "data\\local\\font\\latin\\font24";
|
|
||||||
const std::string D2ResourcePath::Font30 = "data\\local\\font\\latin\\font30";
|
|
||||||
const std::string D2ResourcePath::FontFormal12 = "data\\local\\font\\latin\\fontformal12";
|
|
||||||
const std::string D2ResourcePath::FontFormal11 = "data\\local\\font\\latin\\fontformal11";
|
|
||||||
const std::string D2ResourcePath::FontFormal10 = "data\\local\\font\\latin\\fontformal10";
|
|
||||||
const std::string D2ResourcePath::FontExocet10 = "data\\local\\font\\latin\\fontexocet10";
|
|
||||||
const std::string D2ResourcePath::FontExocet8 = "data\\local\\font\\latin\\fontexocet8";
|
|
||||||
|
|
||||||
// --- UI ---
|
|
||||||
const std::string D2ResourcePath::WideButtonBlank = "data\\global\\ui\\FrontEnd\\WideButtonBlank.dc6";
|
|
||||||
const std::string D2ResourcePath::MediumButtonBlank = "data\\global\\ui\\FrontEnd\\MediumButtonBlank.dc6";
|
|
||||||
const std::string D2ResourcePath::CancelButton = "data\\global\\ui\\FrontEnd\\CancelButtonBlank.dc6";
|
|
||||||
const std::string D2ResourcePath::NarrowButtonBlank = "data\\global\\ui\\FrontEnd\\NarrowButtonBlank.dc6";
|
|
||||||
const std::string D2ResourcePath::ShortButtonBlank = "data\\global\\ui\\CharSelect\\ShortButtonBlank.dc6";
|
|
||||||
const std::string D2ResourcePath::TextBox2 = "data\\global\\ui\\FrontEnd\\textbox2.dc6";
|
|
||||||
const std::string D2ResourcePath::TallButtonBlank = "data\\global\\ui\\CharSelect\\TallButtonBlank.dc6";
|
|
||||||
|
|
||||||
// --- GAME UI ---
|
|
||||||
const std::string D2ResourcePath::MinipanelSmall = "data\\global\\ui\\PANEL\\minipanel_s.dc6";
|
|
||||||
const std::string D2ResourcePath::MinipanelButton = "data\\global\\ui\\PANEL\\minipanelbtn.DC6";
|
|
||||||
|
|
||||||
const std::string D2ResourcePath::Frame = "data\\global\\ui\\PANEL\\800borderframe.dc6";
|
|
||||||
const std::string D2ResourcePath::InventoryCharacterPanel = "data\\global\\ui\\PANEL\\invchar6.DC6";
|
|
||||||
const std::string D2ResourcePath::InventoryWeaponsTab = "data\\global\\ui\\PANEL\\invchar6Tab.DC6";
|
|
||||||
const std::string D2ResourcePath::SkillsPanelAmazon = "data\\global\\ui\\SPELLS\\skltree_a_back.DC6";
|
|
||||||
const std::string D2ResourcePath::SkillsPanelBarbarian = "data\\global\\ui\\SPELLS\\skltree_b_back.DC6";
|
|
||||||
const std::string D2ResourcePath::SkillsPanelDruid = "data\\global\\ui\\SPELLS\\skltree_d_back.DC6";
|
|
||||||
const std::string D2ResourcePath::SkillsPanelAssassin = "data\\global\\ui\\SPELLS\\skltree_i_back.DC6";
|
|
||||||
const std::string D2ResourcePath::SkillsPanelNecromancer = "data\\global\\ui\\SPELLS\\skltree_n_back.DC6";
|
|
||||||
const std::string D2ResourcePath::SkillsPanelPaladin = "data\\global\\ui\\SPELLS\\skltree_p_back.DC6";
|
|
||||||
const std::string D2ResourcePath::SkillsPanelSorcerer = "data\\global\\ui\\SPELLS\\skltree_s_back.DC6";
|
|
||||||
|
|
||||||
const std::string D2ResourcePath::GenericSkills = "data\\global\\ui\\SPELLS\\Skillicon.DC6";
|
|
||||||
const std::string D2ResourcePath::AmazonSkills = "data\\global\\ui\\SPELLS\\AmSkillicon.DC6";
|
|
||||||
const std::string D2ResourcePath::BarbarianSkills = "data\\global\\ui\\SPELLS\\BaSkillicon.DC6";
|
|
||||||
const std::string D2ResourcePath::DruidSkills = "data\\global\\ui\\SPELLS\\DrSkillicon.DC6";
|
|
||||||
const std::string D2ResourcePath::AssassinSkills = "data\\global\\ui\\SPELLS\\AsSkillicon.DC6";
|
|
||||||
const std::string D2ResourcePath::NecromancerSkills = "data\\global\\ui\\SPELLS\\NeSkillicon.DC6";
|
|
||||||
const std::string D2ResourcePath::PaladinSkills = "data\\global\\ui\\SPELLS\\PaSkillicon.DC6";
|
|
||||||
const std::string D2ResourcePath::SorcererSkills = "data\\global\\ui\\SPELLS\\SoSkillicon.DC6";
|
|
||||||
|
|
||||||
const std::string D2ResourcePath::RunButton = "data\\global\\ui\\PANEL\\runbutton.dc6";
|
|
||||||
const std::string D2ResourcePath::MenuButton = "data\\global\\ui\\PANEL\\menubutton.DC6";
|
|
||||||
const std::string D2ResourcePath::GoldCoinButton = "data\\global\\ui\\panel\\goldcoinbtn.dc6";
|
|
||||||
const std::string D2ResourcePath::SquareButton = "data\\global\\ui\\panel\\buysellbtn.dc6";
|
|
||||||
|
|
||||||
const std::string D2ResourcePath::ArmorPlaceholder = "data\\global\\ui\\PANEL\\inv_armor.DC6";
|
|
||||||
const std::string D2ResourcePath::BeltPlaceholder = "data\\global\\ui\\PANEL\\inv_belt.DC6";
|
|
||||||
const std::string D2ResourcePath::BootsPlaceholder = "data\\global\\ui\\PANEL\\inv_boots.DC6";
|
|
||||||
const std::string D2ResourcePath::HelmGlovePlaceholder = "data\\global\\ui\\PANEL\\inv_helm_glove.DC6";
|
|
||||||
const std::string D2ResourcePath::RingAmuletPlaceholder = "data\\global\\ui\\PANEL\\inv_ring_amulet.DC6";
|
|
||||||
const std::string D2ResourcePath::WeaponsPlaceholder = "data\\global\\ui\\PANEL\\inv_weapons.DC6";
|
|
||||||
|
|
||||||
// --- Data ---
|
|
||||||
const std::string D2ResourcePath::EnglishTable = "data\\local\\lng\\eng\\English.txt";
|
|
||||||
const std::string D2ResourcePath::ExpansionStringTable = "data\\local\\lng\\eng\\expansionstring.tbl";
|
|
||||||
const std::string D2ResourcePath::LevelPreset = "data\\global\\excel\\LvlPrest.txt";
|
|
||||||
const std::string D2ResourcePath::LevelType = "data\\global\\excel\\LvlTypes.txt";
|
|
||||||
const std::string D2ResourcePath::LevelDetails = "data\\global\\excel\\Levels.txt";
|
|
||||||
const std::string D2ResourcePath::ObjectDetails = "data\\global\\excel\\Objects.txt";
|
|
||||||
|
|
||||||
// --- Animations ---
|
|
||||||
const std::string D2ResourcePath::ObjectData = "data\\global\\objects";
|
|
||||||
const std::string D2ResourcePath::AnimationData = "data\\global\\animdata.d2";
|
|
||||||
const std::string D2ResourcePath::PlayerAnimationBase = "data\\global\\CHARS";
|
|
||||||
|
|
||||||
// --- Inventory Data ---
|
|
||||||
const std::string D2ResourcePath::Weapons = "data\\global\\excel\\weapons.txt";
|
|
||||||
const std::string D2ResourcePath::Armor = "data\\global\\excel\\armor.txt";
|
|
||||||
const std::string D2ResourcePath::Misc = "data\\global\\excel\\misc.txt";
|
|
||||||
|
|
||||||
// --- Character Data ---
|
|
||||||
const std::string D2ResourcePath::Experience = "data\\global\\excel\\experience.txt";
|
|
||||||
const std::string D2ResourcePath::CharStats = "data\\global\\excel\\charstats.txt";
|
|
||||||
|
|
||||||
// --- Music ---
|
|
||||||
const std::string D2ResourcePath::BGMTitle = "data\\global\\music\\introedit.wav";
|
|
||||||
const std::string D2ResourcePath::BGMOptions = "data\\global\\music\\Common\\options.wav";
|
|
||||||
const std::string D2ResourcePath::BGMAct1AndarielAction = "data\\global\\music\\Act1\\andarielaction.wav";
|
|
||||||
const std::string D2ResourcePath::BGMAct1BloodRavenResolution = "data\\global\\music\\Act1\\bloodravenresolution.wav";
|
|
||||||
const std::string D2ResourcePath::BGMAct1Caves = "data\\global\\music\\Act1\\caves.wav";
|
|
||||||
const std::string D2ResourcePath::BGMAct1Crypt = "data\\global\\music\\Act1\\crypt.wav";
|
|
||||||
const std::string D2ResourcePath::BGMAct1DenOfEvilAction = "data\\global\\music\\Act1\\denofevilaction.wav";
|
|
||||||
const std::string D2ResourcePath::BGMAct1Monastery = "data\\global\\music\\Act1\\monastery.wav";
|
|
||||||
const std::string D2ResourcePath::BGMAct1Town1 = "data\\global\\music\\Act1\\town1.wav";
|
|
||||||
const std::string D2ResourcePath::BGMAct1Tristram = "data\\global\\music\\Act1\\tristram.wav";
|
|
||||||
const std::string D2ResourcePath::BGMAct1Wild = "data\\global\\music\\Act1\\wild.wav";
|
|
||||||
const std::string D2ResourcePath::BGMAct2Desert = "data\\global\\music\\Act2\\desert.wav";
|
|
||||||
const std::string D2ResourcePath::BGMAct2Harem = "data\\global\\music\\Act2\\harem.wav";
|
|
||||||
const std::string D2ResourcePath::BGMAct2HoradricAction = "data\\global\\music\\Act2\\horadricaction.wav";
|
|
||||||
const std::string D2ResourcePath::BGMAct2Lair = "data\\global\\music\\Act2\\lair.wav";
|
|
||||||
const std::string D2ResourcePath::BGMAct2RadamentResolution = "data\\global\\music\\Act2\\radamentresolution.wav";
|
|
||||||
const std::string D2ResourcePath::BGMAct2Sanctuary = "data\\global\\music\\Act2\\sanctuary.wav";
|
|
||||||
const std::string D2ResourcePath::BGMAct2Sewer = "data\\global\\music\\Act2\\sewer.wav";
|
|
||||||
const std::string D2ResourcePath::BGMAct2TaintedSunAction = "data\\global\\music\\Act2\\taintedsunaction.wav";
|
|
||||||
const std::string D2ResourcePath::BGMAct2Tombs = "data\\global\\music\\Act2\\tombs.wav";
|
|
||||||
const std::string D2ResourcePath::BGMAct2Town2 = "data\\global\\music\\Act2\\town2.wav";
|
|
||||||
const std::string D2ResourcePath::BGMAct2Valley = "data\\global\\music\\Act2\\valley.wav";
|
|
||||||
const std::string D2ResourcePath::BGMAct3Jungle = "data\\global\\music\\Act3\\jungle.wav";
|
|
||||||
const std::string D2ResourcePath::BGMAct3Kurast = "data\\global\\music\\Act3\\kurast.wav";
|
|
||||||
const std::string D2ResourcePath::BGMAct3KurastSewer = "data\\global\\music\\Act3\\kurastsewer.wav";
|
|
||||||
const std::string D2ResourcePath::BGMAct3MefDeathAction = "data\\global\\music\\Act3\\mefdeathaction.wav";
|
|
||||||
const std::string D2ResourcePath::BGMAct3OrbAction = "data\\global\\music\\Act3\\orbaction.wav";
|
|
||||||
const std::string D2ResourcePath::BGMAct3Spider = "data\\global\\music\\Act3\\spider.wav";
|
|
||||||
const std::string D2ResourcePath::BGMAct3Town3 = "data\\global\\music\\Act3\\town3.wav";
|
|
||||||
const std::string D2ResourcePath::BGMAct4Diablo = "data\\global\\music\\Act4\\diablo.wav";
|
|
||||||
const std::string D2ResourcePath::BGMAct4DiabloAction = "data\\global\\music\\Act4\\diabloaction.wav";
|
|
||||||
const std::string D2ResourcePath::BGMAct4ForgeAction = "data\\global\\music\\Act4\\forgeaction.wav";
|
|
||||||
const std::string D2ResourcePath::BGMAct4IzualAction = "data\\global\\music\\Act4\\izualaction.wav";
|
|
||||||
const std::string D2ResourcePath::BGMAct4Mesa = "data\\global\\music\\Act4\\mesa.wav";
|
|
||||||
const std::string D2ResourcePath::BGMAct4Town4 = "data\\global\\music\\Act4\\town4.wav";
|
|
||||||
const std::string D2ResourcePath::BGMAct5Baal = "data\\global\\music\\Act5\\baal.wav";
|
|
||||||
const std::string D2ResourcePath::BGMAct5XTown = "data\\global\\music\\Act5\\xtown.wav";
|
|
||||||
|
|
||||||
|
|
||||||
// --- Sound Effects ---
|
|
||||||
const std::string D2ResourcePath::SFXButtonClick = "data\\global\\sfx\\Cursor\\button.wav";
|
|
||||||
const std::string D2ResourcePath::SFXAmazonDeselect = "data\\global\\sfx\\Cursor\\intro\\amazon deselect.wav";
|
|
||||||
const std::string D2ResourcePath::SFXAmazonSelect = "data\\global\\sfx\\Cursor\\intro\\amazon select.wav";
|
|
||||||
const std::string D2ResourcePath::SFXAssassinDeselect = "data\\global\\sfx\\Cursor\\intro\\assassin deselect.wav";
|
|
||||||
const std::string D2ResourcePath::SFXAssassinSelect = "data\\global\\sfx\\Cursor\\intro\\assassin select.wav";
|
|
||||||
const std::string D2ResourcePath::SFXBarbarianDeselect = "data\\global\\sfx\\Cursor\\intro\\barbarian deselect.wav";
|
|
||||||
const std::string D2ResourcePath::SFXBarbarianSelect = "data\\global\\sfx\\Cursor\\intro\\barbarian select.wav";
|
|
||||||
const std::string D2ResourcePath::SFXDruidDeselect = "data\\global\\sfx\\Cursor\\intro\\druid deselect.wav";
|
|
||||||
const std::string D2ResourcePath::SFXDruidSelect = "data\\global\\sfx\\Cursor\\intro\\druid select.wav";
|
|
||||||
const std::string D2ResourcePath::SFXNecromancerDeselect = "data\\global\\sfx\\Cursor\\intro\\necromancer deselect.wav";
|
|
||||||
const std::string D2ResourcePath::SFXNecromancerSelect = "data\\global\\sfx\\Cursor\\intro\\necromancer select.wav";
|
|
||||||
const std::string D2ResourcePath::SFXPaladinDeselect = "data\\global\\sfx\\Cursor\\intro\\paladin deselect.wav";
|
|
||||||
const std::string D2ResourcePath::SFXPaladinSelect = "data\\global\\sfx\\Cursor\\intro\\paladin select.wav";
|
|
||||||
const std::string D2ResourcePath::SFXSorceressDeselect = "data\\global\\sfx\\Cursor\\intro\\sorceress deselect.wav";
|
|
||||||
const std::string D2ResourcePath::SFXSorceressSelect = "data\\global\\sfx\\Cursor\\intro\\sorceress select.wav";
|
|
||||||
|
|
||||||
// --- Enemy Data ---
|
|
||||||
const std::string D2ResourcePath::MonStats = "data\\global\\excel\\monstats.txt";
|
|
||||||
|
|
||||||
// --- Skill Data ---
|
|
||||||
const std::string D2ResourcePath::Missiles = "data\\global\\excel\\missiles.txt";
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // OPENDIABLO2_COMMON_D2RESOURCEPATH_H
|
|
@ -1,13 +0,0 @@
|
|||||||
#ifndef OPENDIABLO2_COMMON_D2SIZE_H
|
|
||||||
#define OPENDIABLO2_COMMON_D2SIZE_H
|
|
||||||
|
|
||||||
namespace OpenDiablo2::Common {
|
|
||||||
|
|
||||||
struct D2Size {
|
|
||||||
int Width;
|
|
||||||
int Height;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // OPENDIABLO2_COMMON_D2SIZE_H
|
|
@ -1,28 +0,0 @@
|
|||||||
#ifndef OPENDIABLO2_COMMON_D2SPRITE_H
|
|
||||||
#define OPENDIABLO2_COMMON_D2SPRITE_H
|
|
||||||
|
|
||||||
#include <OpenDiablo2.Common/D2Palette.h>
|
|
||||||
#include <OpenDiablo2.Common/D2Point.h>
|
|
||||||
#include <OpenDiablo2.Common/D2Size.h>
|
|
||||||
|
|
||||||
namespace OpenDiablo2::Common {
|
|
||||||
|
|
||||||
class D2Sprite {
|
|
||||||
public:
|
|
||||||
D2Sprite();
|
|
||||||
D2Sprite(std::string resourcePath, std::string palette, D2Point location, bool cacheFrames = false);
|
|
||||||
D2Sprite(std::string resourcePath, std::string palette, bool cacheFrames = false);
|
|
||||||
|
|
||||||
int Frame;
|
|
||||||
int TotalFrames;
|
|
||||||
bool Blend;
|
|
||||||
bool Darken;
|
|
||||||
D2Point Location;
|
|
||||||
D2Size FrameSize;
|
|
||||||
D2Size LocalFrameSize;
|
|
||||||
D2Palette::Entry CurrentPalette;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // OPENDIABLO2_COMMON_D2SPRITE_H
|
|
@ -1,61 +0,0 @@
|
|||||||
#include <experimental/filesystem>
|
|
||||||
#include <spdlog/spdlog.h>
|
|
||||||
#include <StormLib.h>
|
|
||||||
#include <OpenDiablo2.Common/D2DataManager.h>
|
|
||||||
|
|
||||||
namespace fs = std::experimental::filesystem;
|
|
||||||
|
|
||||||
OpenDiablo2::Common::D2DataManager::D2DataManager(
|
|
||||||
const D2EngineConfig &engineConfig)
|
|
||||||
: fileEntries()
|
|
||||||
{
|
|
||||||
spdlog::info("Loading data files");
|
|
||||||
auto mpqExt = std::string(".mpq");
|
|
||||||
for (auto &p : fs::recursive_directory_iterator(engineConfig.BasePath))
|
|
||||||
{
|
|
||||||
if (p.path().extension() != mpqExt)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
HANDLE hMpq = NULL;
|
|
||||||
HANDLE hListFile = NULL;
|
|
||||||
|
|
||||||
if(!SFileOpenArchive(p.path().c_str(), 0, 0, &hMpq)) {
|
|
||||||
spdlog::error(std::string(" > ").append(p.path().string()).append(" [READ ERROR!]"));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!SFileOpenFileEx(hMpq, "(listfile)", 0, &hListFile)) {
|
|
||||||
spdlog::error(std::string(" > ").append(p.path().string()).append(" [LIST FILE NOT FOUND!]"));
|
|
||||||
SFileCloseArchive(hMpq);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto listFileContents = std::string();
|
|
||||||
char szBuffer[0x10000];
|
|
||||||
DWORD dwBytes = 1;
|
|
||||||
|
|
||||||
while(dwBytes > 0)
|
|
||||||
{
|
|
||||||
SFileReadFile(hListFile, szBuffer, sizeof(szBuffer), &dwBytes, NULL);
|
|
||||||
if(dwBytes > 0) {
|
|
||||||
listFileContents.append(szBuffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string delim = "\r\n";
|
|
||||||
auto start = 0U;
|
|
||||||
auto end = listFileContents.find(delim);
|
|
||||||
auto linesFound = 0;
|
|
||||||
while (end != std::string::npos)
|
|
||||||
{
|
|
||||||
linesFound++;
|
|
||||||
fileEntries.emplace(listFileContents.substr(start, end - start), p.path().stem().string());
|
|
||||||
start = end + delim.length();
|
|
||||||
end = listFileContents.find(delim, start);
|
|
||||||
}
|
|
||||||
spdlog::debug(std::string(" > ").append(p.path().string()).append(" [").append(std::to_string(linesFound)).append(" files]"));
|
|
||||||
SFileCloseFile(hListFile);
|
|
||||||
|
|
||||||
SFileCloseArchive(hMpq);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,40 +0,0 @@
|
|||||||
#include <OpenDiablo2.Common/D2Sprite.h>
|
|
||||||
#include <OpenDiablo2.Common/D2Palette.h>
|
|
||||||
|
|
||||||
OpenDiablo2::Common::D2Sprite::D2Sprite()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
OpenDiablo2::Common::D2Sprite::D2Sprite(
|
|
||||||
std::string resourcePath,
|
|
||||||
std::string palette,
|
|
||||||
bool cacheFrames)
|
|
||||||
: Frame (0)
|
|
||||||
, TotalFrames (0)
|
|
||||||
, Blend (false)
|
|
||||||
, Darken (false)
|
|
||||||
, Location (D2Point {0, 0})
|
|
||||||
, FrameSize (D2Size {0, 0})
|
|
||||||
, LocalFrameSize (D2Size {0, 0})
|
|
||||||
, CurrentPalette (palette)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
OpenDiablo2::Common::D2Sprite::D2Sprite(
|
|
||||||
std::string resourcePath,
|
|
||||||
std::string palette,
|
|
||||||
D2Point location,
|
|
||||||
bool cacheFrames)
|
|
||||||
: Frame (0)
|
|
||||||
, TotalFrames (0)
|
|
||||||
, Blend (false)
|
|
||||||
, Darken (false)
|
|
||||||
, Location (location)
|
|
||||||
, FrameSize (D2Size {0, 0})
|
|
||||||
, LocalFrameSize (D2Size {0, 0})
|
|
||||||
, CurrentPalette (palette)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
set (SOURCES
|
|
||||||
src/main.cpp
|
|
||||||
src/D2Engine.cpp
|
|
||||||
src/Scenes/D2MainMenu.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
set (HEADERS
|
|
||||||
include/OpenDiablo2.Game/D2Engine.h
|
|
||||||
include/OpenDiablo2.Game/Scenes/D2Scene.h
|
|
||||||
include/OpenDiablo2.Game/Scenes/D2MainMenu.h
|
|
||||||
)
|
|
||||||
|
|
||||||
add_executable(OpenDiablo2.Game ${SOURCES} ${HEADERS})
|
|
||||||
|
|
||||||
include_directories(OpenDiablo2.Game
|
|
||||||
PUBLIC
|
|
||||||
../ExtraUtils
|
|
||||||
../OpenDiablo2.SDL2/include
|
|
||||||
../OpenDiablo2.Common/include
|
|
||||||
include
|
|
||||||
)
|
|
||||||
|
|
||||||
target_link_libraries(OpenDiablo2.Game
|
|
||||||
OpenDiablo2.SDL2
|
|
||||||
OpenDiablo2.Common
|
|
||||||
storm
|
|
||||||
stdc++fs
|
|
||||||
)
|
|
||||||
|
|
||||||
set_target_properties(OpenDiablo2.Game PROPERTIES
|
|
||||||
CXX_STANDARD 17
|
|
||||||
)
|
|
@ -1,40 +0,0 @@
|
|||||||
#ifndef OPENDIABLO2_D2ENGINE_H
|
|
||||||
#define OPENDIABLO2_D2ENGINE_H
|
|
||||||
|
|
||||||
#include <stack>
|
|
||||||
#include <memory>
|
|
||||||
#include <OpenDiablo2.Common/D2DataManager.h>
|
|
||||||
#include <OpenDiablo2.System/D2Graphics.h>
|
|
||||||
#include <OpenDiablo2.System/D2Input.h>
|
|
||||||
#include <OpenDiablo2.Game/Scenes/D2Scene.h>
|
|
||||||
#include "OpenDiablo2.Common/D2EngineConfig.h"
|
|
||||||
|
|
||||||
namespace OpenDiablo2::Game
|
|
||||||
{
|
|
||||||
|
|
||||||
// The main OpenDiablo2 engine
|
|
||||||
class D2Engine : public std::enable_shared_from_this<D2Engine>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
D2Engine(const Common::D2EngineConfig &config);
|
|
||||||
|
|
||||||
// Runs the engine
|
|
||||||
void Run();
|
|
||||||
|
|
||||||
OpenDiablo2::Common::D2DataManager::Ptr dataManager;
|
|
||||||
OpenDiablo2::System::D2Graphics::Ptr gfx;
|
|
||||||
OpenDiablo2::System::D2Input::Ptr input;
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Represents the engine configuration
|
|
||||||
const Common::D2EngineConfig config;
|
|
||||||
|
|
||||||
// Indicates the system should keep running (if set to true)
|
|
||||||
bool isRunning = true;
|
|
||||||
|
|
||||||
std::stack<std::shared_ptr<OpenDiablo2::Game::Scenes::D2Scene>> sceneStack;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif //OPENDIABLO2_D2ENGINE_H
|
|
@ -1,19 +0,0 @@
|
|||||||
#ifndef OPENDIALOB2_SCENES_D2MAINMENU_H
|
|
||||||
#define OPENDIALOB2_SCENES_D2MAINMENU_H
|
|
||||||
|
|
||||||
#include <OpenDiablo2.Game/Scenes/D2Scene.h>
|
|
||||||
#include <OpenDiablo2.Game/D2Engine.h>
|
|
||||||
|
|
||||||
namespace OpenDiablo2::Game::Scenes {
|
|
||||||
|
|
||||||
class MainMenu : public D2Scene {
|
|
||||||
public:
|
|
||||||
MainMenu(std::shared_ptr<D2Engine> engine);
|
|
||||||
void Render();
|
|
||||||
void Update();
|
|
||||||
private:
|
|
||||||
std::shared_ptr<D2Engine> engine;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
#endif // OPENDIALOB2_SCENES_D2MAINMENU_H
|
|
@ -1,18 +0,0 @@
|
|||||||
#ifndef OPENDIABLO2_D2SCENE_H
|
|
||||||
#define OPENDIABLO2_D2SCENE_H
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
namespace OpenDiablo2::Game::Scenes
|
|
||||||
{
|
|
||||||
|
|
||||||
class D2Scene
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual void Render() = 0;
|
|
||||||
virtual void Update() = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,31 +0,0 @@
|
|||||||
#include <OpenDiablo2.Game/D2Engine.h>
|
|
||||||
#include <OpenDiablo2.Game/Scenes/D2MainMenu.h>
|
|
||||||
|
|
||||||
OpenDiablo2::Game::D2Engine::D2Engine(
|
|
||||||
const Common::D2EngineConfig &config)
|
|
||||||
: config(config)
|
|
||||||
, gfx(std::make_unique<OpenDiablo2::System::D2Graphics>())
|
|
||||||
, input(std::make_unique<OpenDiablo2::System::D2Input>())
|
|
||||||
, dataManager(std::make_unique<OpenDiablo2::Common::D2DataManager>(config))
|
|
||||||
{ }
|
|
||||||
|
|
||||||
void
|
|
||||||
OpenDiablo2::Game::D2Engine::Run()
|
|
||||||
{
|
|
||||||
gfx->InitializeWindow();
|
|
||||||
|
|
||||||
sceneStack.emplace(std::make_shared<Scenes::MainMenu>(shared_from_this()));
|
|
||||||
|
|
||||||
while (isRunning) {
|
|
||||||
input->ProcessEvents();
|
|
||||||
sceneStack.top()->Update();
|
|
||||||
if (input->QuitIsRequested()) {
|
|
||||||
isRunning = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
gfx->Clear();
|
|
||||||
sceneStack.top()->Render();
|
|
||||||
gfx->Present();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
|||||||
#include <OpenDiablo2.Game/Scenes/D2MainMenu.h>
|
|
||||||
#include <OpenDiablo2.Common/D2ResourcePath.h>
|
|
||||||
|
|
||||||
OpenDiablo2::Game::Scenes::MainMenu::MainMenu(std::shared_ptr<D2Engine> engine)
|
|
||||||
: engine(engine) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void OpenDiablo2::Game::Scenes::MainMenu::Render() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void OpenDiablo2::Game::Scenes::MainMenu::Update() {
|
|
||||||
|
|
||||||
}
|
|
@ -1,48 +0,0 @@
|
|||||||
#include <memory>
|
|
||||||
#include <experimental/filesystem>
|
|
||||||
#include <spdlog/spdlog.h>
|
|
||||||
#include <spdlog/sinks/stdout_color_sinks.h>
|
|
||||||
#include <OpenDiablo2.Game/D2Engine.h>
|
|
||||||
#include <CLI/CLI.hpp>
|
|
||||||
#include <StormLib.h>
|
|
||||||
|
|
||||||
int
|
|
||||||
main(
|
|
||||||
int argc,
|
|
||||||
char** argv)
|
|
||||||
{
|
|
||||||
spdlog::set_level(spdlog::level::trace);
|
|
||||||
spdlog::set_pattern("[%^%l%$] %v");
|
|
||||||
spdlog::info("OpenDiablo 2 has started");
|
|
||||||
|
|
||||||
CLI::App app{"OpenDiablo2 - An open source re-implementation of Diablo 2."};
|
|
||||||
|
|
||||||
OpenDiablo2::Common::D2EngineConfig engineConfig;
|
|
||||||
engineConfig.BasePath = std::experimental::filesystem::current_path().string();
|
|
||||||
|
|
||||||
app.add_option("-p,--path", engineConfig.BasePath, "The base path for Diablo 2");
|
|
||||||
|
|
||||||
CLI11_PARSE(app, argc, argv);
|
|
||||||
|
|
||||||
spdlog::info("Base file path is '" + engineConfig.BasePath + "'");
|
|
||||||
|
|
||||||
// Sanity-check that files are where we expect them to be...
|
|
||||||
auto testFilePath = engineConfig.BasePath + "/d2data.mpq";
|
|
||||||
|
|
||||||
HANDLE mpq = nullptr;
|
|
||||||
|
|
||||||
if (!SFileOpenArchive(("flat-file:" + testFilePath).c_str(), 0, STREAM_FLAG_READ_ONLY, &mpq)) {
|
|
||||||
spdlog::error("Diablo 2 content files were not detected. Please make sure the base path is properly set!");
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
SFileCloseFile(mpq);
|
|
||||||
spdlog::info("Content files were located, starting engine.");
|
|
||||||
|
|
||||||
// Off to the races we go!
|
|
||||||
auto engine = std::make_shared<OpenDiablo2::Game::D2Engine>(engineConfig);
|
|
||||||
engine->Run();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,35 +0,0 @@
|
|||||||
project(OpenDiablo2.SDL2 VERSION 0.1 LANGUAGES CXX)
|
|
||||||
|
|
||||||
set (SOURCES
|
|
||||||
src/D2Graphics.cpp
|
|
||||||
src/D2Input.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
set (HEADERS
|
|
||||||
include/OpenDiablo2.System/D2Graphics.h
|
|
||||||
include/OpenDiablo2.System/D2Input.h
|
|
||||||
)
|
|
||||||
|
|
||||||
find_package(SDL2 REQUIRED)
|
|
||||||
|
|
||||||
add_library(OpenDiablo2.SDL2 SHARED ${SOURCES} ${HEADERS})
|
|
||||||
|
|
||||||
target_include_directories(OpenDiablo2.SDL2
|
|
||||||
PUBLIC
|
|
||||||
include
|
|
||||||
PRIVATE
|
|
||||||
../ExtraUtils
|
|
||||||
../OpenDiablo2.Common/include
|
|
||||||
include
|
|
||||||
sdl2::sdl2
|
|
||||||
SDL2::SDL2main
|
|
||||||
)
|
|
||||||
|
|
||||||
target_link_libraries(OpenDiablo2.SDL2
|
|
||||||
OpenDiablo2.Common
|
|
||||||
${SDL2_LIBRARY}
|
|
||||||
)
|
|
||||||
|
|
||||||
set_target_properties(OpenDiablo2.SDL2 PROPERTIES
|
|
||||||
CXX_STANDARD 17
|
|
||||||
)
|
|
@ -1,49 +0,0 @@
|
|||||||
#ifndef OPENDIABLO2_D2GRAPHICS_H
|
|
||||||
#define OPENDIABLO2_D2GRAPHICS_H
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <spdlog/spdlog.h>
|
|
||||||
#include <SDL2/SDL.h>
|
|
||||||
|
|
||||||
namespace OpenDiablo2::System
|
|
||||||
{
|
|
||||||
|
|
||||||
struct SDLWindowDestroyer
|
|
||||||
{
|
|
||||||
void operator()(SDL_Window *w) const
|
|
||||||
{
|
|
||||||
spdlog::debug("Destroying SDL window");
|
|
||||||
if (w)
|
|
||||||
SDL_DestroyWindow(w);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SDLRendererDestroyer
|
|
||||||
{
|
|
||||||
void operator()(SDL_Renderer *r) const
|
|
||||||
{
|
|
||||||
spdlog::debug("Destroying SDL renderer");
|
|
||||||
if (r)
|
|
||||||
SDL_DestroyRenderer(r);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class D2Graphics
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
typedef std::unique_ptr<D2Graphics> Ptr;
|
|
||||||
D2Graphics();
|
|
||||||
void InitializeWindow();
|
|
||||||
void Clear();
|
|
||||||
void Present();
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::unique_ptr<SDL_Window, SDLWindowDestroyer> window;
|
|
||||||
std::unique_ptr<SDL_Renderer, SDLRendererDestroyer> renderer;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif //OPENDIABLO2_D2GRAPHICS_H
|
|
@ -1,20 +0,0 @@
|
|||||||
#ifndef OPENDIABLO2_D2INPUT_H
|
|
||||||
#define OPENDIABLO2_D2INPUT_H
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
namespace OpenDiablo2::System {
|
|
||||||
|
|
||||||
class D2Input {
|
|
||||||
public:
|
|
||||||
typedef std::unique_ptr<D2Input> Ptr;
|
|
||||||
D2Input();
|
|
||||||
void ProcessEvents();
|
|
||||||
bool QuitIsRequested();
|
|
||||||
private:
|
|
||||||
bool quitIsRequested = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif //OPENDIABLO2_D2INPUT_H
|
|
@ -1,51 +0,0 @@
|
|||||||
#include <spdlog/spdlog.h>
|
|
||||||
#include <OpenDiablo2.System/D2Graphics.h>
|
|
||||||
#include <string>
|
|
||||||
#include <SDL2/SDL.h>
|
|
||||||
|
|
||||||
|
|
||||||
#include "OpenDiablo2.System/D2Graphics.h"
|
|
||||||
|
|
||||||
OpenDiablo2::System::D2Graphics::D2Graphics()
|
|
||||||
{
|
|
||||||
atexit(SDL_Quit);
|
|
||||||
|
|
||||||
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
|
|
||||||
spdlog::error("Could not initialize sdl2: " + std::string(SDL_GetError()));
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
OpenDiablo2::System::D2Graphics::Clear()
|
|
||||||
{
|
|
||||||
SDL_RenderClear(renderer.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
OpenDiablo2::System::D2Graphics::InitializeWindow()
|
|
||||||
{
|
|
||||||
spdlog::debug("Initializing SDL window");
|
|
||||||
window = std::unique_ptr<SDL_Window, SDLWindowDestroyer>(SDL_CreateWindow("OpenDiablo 2", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
|
|
||||||
800, 600, SDL_WINDOW_SHOWN));
|
|
||||||
if (window == nullptr) {
|
|
||||||
spdlog::error("Could not create sdl2 window: " + std::string(SDL_GetError()));
|
|
||||||
SDL_Quit();
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
spdlog::debug("Initializing SDL renderer");
|
|
||||||
renderer = std::unique_ptr<SDL_Renderer, SDLRendererDestroyer>(SDL_CreateRenderer(window.get(), -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC));
|
|
||||||
if (renderer == nullptr){
|
|
||||||
spdlog::error("Could not create sdl2 window: " + std::string(SDL_GetError()));
|
|
||||||
SDL_Quit();
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
OpenDiablo2::System::D2Graphics::Present()
|
|
||||||
{
|
|
||||||
SDL_RenderPresent(renderer.get());
|
|
||||||
}
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
|||||||
|
|
||||||
#include <OpenDiablo2.System/D2Input.h>
|
|
||||||
#include <SDL2/SDL.h>
|
|
||||||
|
|
||||||
OpenDiablo2::System::D2Input::D2Input()
|
|
||||||
{
|
|
||||||
SDL_Init(SDL_INIT_EVENTS);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
OpenDiablo2::System::D2Input::ProcessEvents()
|
|
||||||
{
|
|
||||||
SDL_Event event;
|
|
||||||
while (SDL_PollEvent(&event)) {
|
|
||||||
if( event.type == SDL_QUIT ) {
|
|
||||||
quitIsRequested = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
OpenDiablo2::System::D2Input::QuitIsRequested()
|
|
||||||
{
|
|
||||||
return quitIsRequested;
|
|
||||||
}
|
|
@ -1,173 +0,0 @@
|
|||||||
|
|
||||||
# This module defines
|
|
||||||
# SDL2_LIBRARY, the name of the library to link against
|
|
||||||
# SDL2_FOUND, if false, do not try to link to SDL2
|
|
||||||
# SDL2_INCLUDE_DIR, where to find SDL.h
|
|
||||||
#
|
|
||||||
# This module responds to the the flag:
|
|
||||||
# SDL2_BUILDING_LIBRARY
|
|
||||||
# If this is defined, then no SDL2main will be linked in because
|
|
||||||
# only applications need main().
|
|
||||||
# Otherwise, it is assumed you are building an application and this
|
|
||||||
# module will attempt to locate and set the the proper link flags
|
|
||||||
# as part of the returned SDL2_LIBRARY variable.
|
|
||||||
#
|
|
||||||
# Don't forget to include SDLmain.h and SDLmain.m your project for the
|
|
||||||
# OS X framework based version. (Other versions link to -lSDL2main which
|
|
||||||
# this module will try to find on your behalf.) Also for OS X, this
|
|
||||||
# module will automatically add the -framework Cocoa on your behalf.
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# Additional Note: If you see an empty SDL2_LIBRARY_TEMP in your configuration
|
|
||||||
# and no SDL2_LIBRARY, it means CMake did not find your SDL2 library
|
|
||||||
# (SDL2.dll, libsdl2.so, SDL2.framework, etc).
|
|
||||||
# Set SDL2_LIBRARY_TEMP to point to your SDL2 library, and configure again.
|
|
||||||
# Similarly, if you see an empty SDL2MAIN_LIBRARY, you should set this value
|
|
||||||
# as appropriate. These values are used to generate the final SDL2_LIBRARY
|
|
||||||
# variable, but when these values are unset, SDL2_LIBRARY does not get created.
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# $SDL2DIR is an environment variable that would
|
|
||||||
# correspond to the ./configure --prefix=$SDL2DIR
|
|
||||||
# used in building SDL2.
|
|
||||||
# l.e.galup 9-20-02
|
|
||||||
#
|
|
||||||
# Modified by Eric Wing.
|
|
||||||
# Added code to assist with automated building by using environmental variables
|
|
||||||
# and providing a more controlled/consistent search behavior.
|
|
||||||
# Added new modifications to recognize OS X frameworks and
|
|
||||||
# additional Unix paths (FreeBSD, etc).
|
|
||||||
# Also corrected the header search path to follow "proper" SDL guidelines.
|
|
||||||
# Added a search for SDL2main which is needed by some platforms.
|
|
||||||
# Added a search for threads which is needed by some platforms.
|
|
||||||
# Added needed compile switches for MinGW.
|
|
||||||
#
|
|
||||||
# On OSX, this will prefer the Framework version (if found) over others.
|
|
||||||
# People will have to manually change the cache values of
|
|
||||||
# SDL2_LIBRARY to override this selection or set the CMake environment
|
|
||||||
# CMAKE_INCLUDE_PATH to modify the search paths.
|
|
||||||
#
|
|
||||||
# Note that the header path has changed from SDL2/SDL.h to just SDL.h
|
|
||||||
# This needed to change because "proper" SDL convention
|
|
||||||
# is #include "SDL.h", not <SDL2/SDL.h>. This is done for portability
|
|
||||||
# reasons because not all systems place things in SDL2/ (see FreeBSD).
|
|
||||||
|
|
||||||
#=============================================================================
|
|
||||||
# Copyright 2003-2009 Kitware, Inc.
|
|
||||||
#
|
|
||||||
# Distributed under the OSI-approved BSD License (the "License");
|
|
||||||
# see accompanying file Copyright.txt for details.
|
|
||||||
#
|
|
||||||
# This software is distributed WITHOUT ANY WARRANTY; without even the
|
|
||||||
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
# See the License for more information.
|
|
||||||
#=============================================================================
|
|
||||||
# (To distribute this file outside of CMake, substitute the full
|
|
||||||
# License text for the above reference.)
|
|
||||||
|
|
||||||
# message("<FindSDL2.cmake>")
|
|
||||||
|
|
||||||
SET(SDL2_SEARCH_PATHS
|
|
||||||
~/Library/Frameworks
|
|
||||||
/Library/Frameworks
|
|
||||||
/usr/local
|
|
||||||
/usr
|
|
||||||
/sw # Fink
|
|
||||||
/opt/local # DarwinPorts
|
|
||||||
/opt/csw # Blastwave
|
|
||||||
/opt
|
|
||||||
${SDL2_PATH}
|
|
||||||
)
|
|
||||||
|
|
||||||
FIND_PATH(SDL2_INCLUDE_DIR SDL.h
|
|
||||||
HINTS
|
|
||||||
$ENV{SDL2DIR}
|
|
||||||
PATH_SUFFIXES include/SDL2 include
|
|
||||||
PATHS ${SDL2_SEARCH_PATHS}
|
|
||||||
)
|
|
||||||
|
|
||||||
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
|
||||||
set(PATH_SUFFIXES lib64 lib/x64 lib)
|
|
||||||
else()
|
|
||||||
set(PATH_SUFFIXES lib/x86 lib)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
FIND_LIBRARY(SDL2_LIBRARY_TEMP
|
|
||||||
NAMES SDL2
|
|
||||||
HINTS
|
|
||||||
$ENV{SDL2DIR}
|
|
||||||
PATH_SUFFIXES ${PATH_SUFFIXES}
|
|
||||||
PATHS ${SDL2_SEARCH_PATHS}
|
|
||||||
)
|
|
||||||
|
|
||||||
IF(NOT SDL2_BUILDING_LIBRARY)
|
|
||||||
IF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework")
|
|
||||||
# Non-OS X framework versions expect you to also dynamically link to
|
|
||||||
# SDL2main. This is mainly for Windows and OS X. Other (Unix) platforms
|
|
||||||
# seem to provide SDL2main for compatibility even though they don't
|
|
||||||
# necessarily need it.
|
|
||||||
FIND_LIBRARY(SDL2MAIN_LIBRARY
|
|
||||||
NAMES SDL2main
|
|
||||||
HINTS
|
|
||||||
$ENV{SDL2DIR}
|
|
||||||
PATH_SUFFIXES ${PATH_SUFFIXES}
|
|
||||||
PATHS ${SDL2_SEARCH_PATHS}
|
|
||||||
)
|
|
||||||
ENDIF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework")
|
|
||||||
ENDIF(NOT SDL2_BUILDING_LIBRARY)
|
|
||||||
|
|
||||||
# SDL2 may require threads on your system.
|
|
||||||
# The Apple build may not need an explicit flag because one of the
|
|
||||||
# frameworks may already provide it.
|
|
||||||
# But for non-OSX systems, I will use the CMake Threads package.
|
|
||||||
IF(NOT APPLE)
|
|
||||||
FIND_PACKAGE(Threads)
|
|
||||||
ENDIF(NOT APPLE)
|
|
||||||
|
|
||||||
# MinGW needs an additional link flag, -mwindows
|
|
||||||
# It's total link flags should look like -lmingw32 -lSDL2main -lSDL2 -mwindows
|
|
||||||
IF(MINGW)
|
|
||||||
SET(MINGW32_LIBRARY mingw32 "-mwindows" CACHE STRING "mwindows for MinGW")
|
|
||||||
ENDIF(MINGW)
|
|
||||||
|
|
||||||
IF(SDL2_LIBRARY_TEMP)
|
|
||||||
# For SDL2main
|
|
||||||
IF(NOT SDL2_BUILDING_LIBRARY)
|
|
||||||
IF(SDL2MAIN_LIBRARY)
|
|
||||||
SET(SDL2_LIBRARY_TEMP ${SDL2MAIN_LIBRARY} ${SDL2_LIBRARY_TEMP})
|
|
||||||
ENDIF(SDL2MAIN_LIBRARY)
|
|
||||||
ENDIF(NOT SDL2_BUILDING_LIBRARY)
|
|
||||||
|
|
||||||
# For OS X, SDL2 uses Cocoa as a backend so it must link to Cocoa.
|
|
||||||
# CMake doesn't display the -framework Cocoa string in the UI even
|
|
||||||
# though it actually is there if I modify a pre-used variable.
|
|
||||||
# I think it has something to do with the CACHE STRING.
|
|
||||||
# So I use a temporary variable until the end so I can set the
|
|
||||||
# "real" variable in one-shot.
|
|
||||||
IF(APPLE)
|
|
||||||
SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} "-framework Cocoa")
|
|
||||||
ENDIF(APPLE)
|
|
||||||
|
|
||||||
# For threads, as mentioned Apple doesn't need this.
|
|
||||||
# In fact, there seems to be a problem if I used the Threads package
|
|
||||||
# and try using this line, so I'm just skipping it entirely for OS X.
|
|
||||||
IF(NOT APPLE)
|
|
||||||
SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} ${CMAKE_THREAD_LIBS_INIT})
|
|
||||||
ENDIF(NOT APPLE)
|
|
||||||
|
|
||||||
# For MinGW library
|
|
||||||
IF(MINGW)
|
|
||||||
SET(SDL2_LIBRARY_TEMP ${MINGW32_LIBRARY} ${SDL2_LIBRARY_TEMP})
|
|
||||||
ENDIF(MINGW)
|
|
||||||
|
|
||||||
# Set the final string here so the GUI reflects the final state.
|
|
||||||
SET(SDL2_LIBRARY ${SDL2_LIBRARY_TEMP} CACHE STRING "Where the SDL2 Library can be found")
|
|
||||||
# Set the temp variable to INTERNAL so it is not seen in the CMake GUI
|
|
||||||
SET(SDL2_LIBRARY_TEMP "${SDL2_LIBRARY_TEMP}" CACHE INTERNAL "")
|
|
||||||
ENDIF(SDL2_LIBRARY_TEMP)
|
|
||||||
|
|
||||||
# message("</FindSDL2.cmake>")
|
|
||||||
|
|
||||||
INCLUDE(FindPackageHandleStandardArgs)
|
|
||||||
|
|
||||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2 REQUIRED_VARS SDL2_LIBRARY SDL2_INCLUDE_DIR)
|
|
@ -1,100 +0,0 @@
|
|||||||
# Locate SDL_MIXER library
|
|
||||||
#
|
|
||||||
# This module defines:
|
|
||||||
#
|
|
||||||
# ::
|
|
||||||
#
|
|
||||||
# SDL2_MIXER_LIBRARIES, the name of the library to link against
|
|
||||||
# SDL2_MIXER_INCLUDE_DIRS, where to find the headers
|
|
||||||
# SDL2_MIXER_FOUND, if false, do not try to link against
|
|
||||||
# SDL2_MIXER_VERSION_STRING - human-readable string containing the version of SDL_MIXER
|
|
||||||
#
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# For backward compatibility the following variables are also set:
|
|
||||||
#
|
|
||||||
# ::
|
|
||||||
#
|
|
||||||
# SDLMIXER_LIBRARY (same value as SDL2_MIXER_LIBRARIES)
|
|
||||||
# SDLMIXER_INCLUDE_DIR (same value as SDL2_MIXER_INCLUDE_DIRS)
|
|
||||||
# SDLMIXER_FOUND (same value as SDL2_MIXER_FOUND)
|
|
||||||
#
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# $SDLDIR is an environment variable that would correspond to the
|
|
||||||
# ./configure --prefix=$SDLDIR used in building SDL.
|
|
||||||
#
|
|
||||||
# Created by Eric Wing. This was influenced by the FindSDL.cmake
|
|
||||||
# module, but with modifications to recognize OS X frameworks and
|
|
||||||
# additional Unix paths (FreeBSD, etc).
|
|
||||||
|
|
||||||
#=============================================================================
|
|
||||||
# Copyright 2005-2009 Kitware, Inc.
|
|
||||||
# Copyright 2012 Benjamin Eikel
|
|
||||||
#
|
|
||||||
# Distributed under the OSI-approved BSD License (the "License");
|
|
||||||
# see accompanying file Copyright.txt for details.
|
|
||||||
#
|
|
||||||
# This software is distributed WITHOUT ANY WARRANTY; without even the
|
|
||||||
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
# See the License for more information.
|
|
||||||
#=============================================================================
|
|
||||||
# (To distribute this file outside of CMake, substitute the full
|
|
||||||
# License text for the above reference.)
|
|
||||||
|
|
||||||
find_path(SDL2_MIXER_INCLUDE_DIR SDL_mixer.h
|
|
||||||
HINTS
|
|
||||||
ENV SDL2MIXERDIR
|
|
||||||
ENV SDL2DIR
|
|
||||||
PATH_SUFFIXES SDL2
|
|
||||||
# path suffixes to search inside ENV{SDLDIR}
|
|
||||||
include/SDL2 include
|
|
||||||
PATHS ${SDL2_MIXER_PATH}
|
|
||||||
)
|
|
||||||
|
|
||||||
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
|
||||||
set(VC_LIB_PATH_SUFFIX lib/x64)
|
|
||||||
else()
|
|
||||||
set(VC_LIB_PATH_SUFFIX lib/x86)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
find_library(SDL2_MIXER_LIBRARY
|
|
||||||
NAMES SDL2_mixer
|
|
||||||
HINTS
|
|
||||||
ENV SDL2MIXERDIR
|
|
||||||
ENV SDL2DIR
|
|
||||||
PATH_SUFFIXES lib bin ${VC_LIB_PATH_SUFFIX}
|
|
||||||
PATHS ${SDL2_MIXER_PATH}
|
|
||||||
)
|
|
||||||
|
|
||||||
if(SDL2_MIXER_INCLUDE_DIR AND EXISTS "${SDL2_MIXER_INCLUDE_DIR}/SDL_mixer.h")
|
|
||||||
file(STRINGS "${SDL2_MIXER_INCLUDE_DIR}/SDL_mixer.h" SDL2_MIXER_VERSION_MAJOR_LINE REGEX "^#define[ \t]+SDL_MIXER_MAJOR_VERSION[ \t]+[0-9]+$")
|
|
||||||
file(STRINGS "${SDL2_MIXER_INCLUDE_DIR}/SDL_mixer.h" SDL2_MIXER_VERSION_MINOR_LINE REGEX "^#define[ \t]+SDL_MIXER_MINOR_VERSION[ \t]+[0-9]+$")
|
|
||||||
file(STRINGS "${SDL2_MIXER_INCLUDE_DIR}/SDL_mixer.h" SDL2_MIXER_VERSION_PATCH_LINE REGEX "^#define[ \t]+SDL_MIXER_PATCHLEVEL[ \t]+[0-9]+$")
|
|
||||||
string(REGEX REPLACE "^#define[ \t]+SDL_MIXER_MAJOR_VERSION[ \t]+([0-9]+)$" "\\1" SDL2_MIXER_VERSION_MAJOR "${SDL2_MIXER_VERSION_MAJOR_LINE}")
|
|
||||||
string(REGEX REPLACE "^#define[ \t]+SDL_MIXER_MINOR_VERSION[ \t]+([0-9]+)$" "\\1" SDL2_MIXER_VERSION_MINOR "${SDL2_MIXER_VERSION_MINOR_LINE}")
|
|
||||||
string(REGEX REPLACE "^#define[ \t]+SDL_MIXER_PATCHLEVEL[ \t]+([0-9]+)$" "\\1" SDL2_MIXER_VERSION_PATCH "${SDL2_MIXER_VERSION_PATCH_LINE}")
|
|
||||||
set(SDL2_MIXER_VERSION_STRING ${SDL2_MIXER_VERSION_MAJOR}.${SDL2_MIXER_VERSION_MINOR}.${SDL2_MIXER_VERSION_PATCH})
|
|
||||||
unset(SDL2_MIXER_VERSION_MAJOR_LINE)
|
|
||||||
unset(SDL2_MIXER_VERSION_MINOR_LINE)
|
|
||||||
unset(SDL2_MIXER_VERSION_PATCH_LINE)
|
|
||||||
unset(SDL2_MIXER_VERSION_MAJOR)
|
|
||||||
unset(SDL2_MIXER_VERSION_MINOR)
|
|
||||||
unset(SDL2_MIXER_VERSION_PATCH)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(SDL2_MIXER_LIBRARIES ${SDL2_MIXER_LIBRARY})
|
|
||||||
set(SDL2_MIXER_INCLUDE_DIRS ${SDL2_MIXER_INCLUDE_DIR})
|
|
||||||
|
|
||||||
include(FindPackageHandleStandardArgs)
|
|
||||||
|
|
||||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2_mixer
|
|
||||||
REQUIRED_VARS SDL2_MIXER_LIBRARIES SDL2_MIXER_INCLUDE_DIRS
|
|
||||||
VERSION_VAR SDL2_MIXER_VERSION_STRING)
|
|
||||||
|
|
||||||
# for backward compatibility
|
|
||||||
set(SDLMIXER_LIBRARY ${SDL2_MIXER_LIBRARIES})
|
|
||||||
set(SDLMIXER_INCLUDE_DIR ${SDL2_MIXER_INCLUDE_DIRS})
|
|
||||||
set(SDLMIXER_FOUND ${SDL2_MIXER_FOUND})
|
|
||||||
|
|
||||||
mark_as_advanced(SDL2_MIXER_LIBRARY SDL2_MIXER_INCLUDE_DIR)
|
|
Loading…
Reference in New Issue
Block a user