1
1
mirror of https://github.com/OpenDiablo2/OpenDiablo2 synced 2025-02-04 15:46:51 -05:00
OpenDiablo2/d2core/d2systems/file_handle_resolver.go

180 lines
4.7 KiB
Go
Raw Normal View History

package d2systems
import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2util"
"strings"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2cache"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
2020-10-12 17:35:11 -04:00
"github.com/gravestench/akara"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2components"
)
const (
languageTokenFont = "{LANG_FONT}"
languageTokenStringTable = "{LANG}"
)
const (
fileHandleCacheBudget = 1024
fileHandleCacheEntryWeight = 1
)
const (
logPrefixFileHandleResolver = "File Handle Resolver"
)
func NewFileHandleResolver() *FileHandleResolutionSystem {
// this filter is for entities that have a file path and file type but no file handle.
2020-10-12 17:35:11 -04:00
filesToSource := akara.NewFilter().
Require(d2components.FilePath).
Require(d2components.FileType).
Forbid(d2components.FileHandle).
Forbid(d2components.FileSource).
Build()
sourcesToUse := akara.NewFilter().
RequireOne(d2components.FileSource).
Build()
fhr := &FileHandleResolutionSystem{
BaseSubscriberSystem: akara.NewBaseSubscriberSystem(filesToSource, sourcesToUse),
cache: d2cache.CreateCache(fileHandleCacheBudget).(*d2cache.Cache),
Logger: d2util.NewLogger(),
}
fhr.SetPrefix(logPrefixFileHandleResolver)
return fhr
}
type FileHandleResolutionSystem struct {
*akara.BaseSubscriberSystem
*d2util.Logger
cache *d2cache.Cache
2020-10-12 17:35:11 -04:00
filesToLoad *akara.Subscription
sourcesToUse *akara.Subscription
filePaths *d2components.FilePathMap
fileTypes *d2components.FileTypeMap
fileSources *d2components.FileSourceMap
fileHandles *d2components.FileHandleMap
}
// Init initializes the system with the given world
2020-10-12 17:35:11 -04:00
func (m *FileHandleResolutionSystem) Init(world *akara.World) {
m.Info("initializing ...")
2020-10-12 17:35:11 -04:00
m.filesToLoad = m.Subscriptions[0]
m.sourcesToUse = m.Subscriptions[1]
// try to inject the components we require, then cast the returned
// abstract ComponentMap back to the concrete implementation
m.filePaths = m.InjectMap(d2components.FilePath).(*d2components.FilePathMap)
m.fileTypes = m.InjectMap(d2components.FileType).(*d2components.FileTypeMap)
m.fileHandles = m.InjectMap(d2components.FileHandle).(*d2components.FileHandleMap)
m.fileSources = m.InjectMap(d2components.FileSource).(*d2components.FileSourceMap)
}
// Process processes all of the Entities
func (m *FileHandleResolutionSystem) Update() {
2020-10-12 17:35:11 -04:00
filesToLoad := m.filesToLoad.GetEntities()
sourcesToUse := m.sourcesToUse.GetEntities()
for _, fileID := range filesToLoad {
for _, sourceID := range sourcesToUse {
if m.loadFileWithSource(fileID, sourceID) {
break
}
}
}
}
2020-10-12 17:35:11 -04:00
// try to load a file with a source, returns true if loaded
func (m *FileHandleResolutionSystem) loadFileWithSource(fileID, sourceID akara.EID) bool {
fp, found := m.filePaths.GetFilePath(fileID)
if !found {
2020-10-12 17:35:11 -04:00
return false
}
ft, found := m.fileTypes.GetFileType(fileID)
if !found {
return false
}
2020-10-12 17:35:11 -04:00
source, found := m.fileSources.GetFileSource(sourceID)
if !found {
return false
}
sourceFp, found := m.filePaths.GetFilePath(sourceID)
if !found {
return false
}
// replace the locale tokens if present
if strings.Contains(fp.Path, languageTokenFont) {
fp.Path = strings.ReplaceAll(fp.Path, languageTokenFont, "latin")
} else if strings.Contains(fp.Path, languageTokenStringTable) {
fp.Path = strings.ReplaceAll(fp.Path, languageTokenStringTable, "ENG")
}
cacheKey := m.makeCacheKey(fp.Path, sourceFp.Path)
if entry, found := m.cache.Retrieve(cacheKey); found {
component := m.fileHandles.AddFileHandle(fileID)
component.Data = entry.(d2interface.DataStream)
return true
}
2020-10-12 17:35:11 -04:00
data, err := source.Open(fp)
if err != nil {
// HACK: sound environment stuff doesnt specify the path, just the filename
// so we gotta check this edge case
if ft.Type != d2enum.FileTypeWAV {
return false
}
if !strings.Contains(fp.Path, "sfx") {
return false
}
tryPath := strings.ReplaceAll(fp.Path, "sfx", "music")
tmpComponent := &d2components.FilePathComponent{Path: tryPath}
cacheKey := m.makeCacheKey(tryPath, sourceFp.Path)
if entry, found := m.cache.Retrieve(cacheKey); found {
component := m.fileHandles.AddFileHandle(fileID)
component.Data = entry.(d2interface.DataStream)
fp.Path = tryPath
return true
} else {
data, err = source.Open(tmpComponent)
if err != nil {
return false
}
fp.Path = tryPath
}
}
2020-10-12 17:35:11 -04:00
m.Infof("resolved `%s` with source `%s`", fp.Path, sourceFp.Path)
component := m.fileHandles.AddFileHandle(fileID)
component.Data = data
m.cache.Insert(cacheKey, data, assetCacheEntryWeight)
2020-10-12 17:35:11 -04:00
return true
}
func (m *FileHandleResolutionSystem) makeCacheKey(path, source string) string {
const sep = "->"
return strings.Join([]string{source, path}, sep)
}