2020-09-08 15:45:26 -04:00
|
|
|
package d2loader
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
2020-09-14 14:47:11 -04:00
|
|
|
"strings"
|
2020-09-08 15:45:26 -04:00
|
|
|
|
2020-09-08 17:39:34 -04:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2cache"
|
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
2020-09-08 15:45:26 -04:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2loader/asset"
|
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2loader/asset/types"
|
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2loader/filesystem"
|
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2loader/mpq"
|
2020-09-14 14:47:11 -04:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
|
2020-09-09 08:22:13 -04:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2util"
|
2020-09-14 14:47:11 -04:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2config"
|
2020-09-08 15:45:26 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
2020-09-09 14:35:52 -04:00
|
|
|
defaultCacheBudget = 1024 * 1024 * 512
|
2020-09-08 15:45:26 -04:00
|
|
|
defaultCacheEntryWeight = 1
|
2020-09-14 14:47:11 -04:00
|
|
|
errFmtFileNotFound = "file not found: %s"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
defaultLanguage = "ENG"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
fontToken = d2resource.LanguageFontToken
|
|
|
|
tableToken = d2resource.LanguageTableToken
|
2020-09-08 15:45:26 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
// NewLoader creates a new loader
|
2020-09-14 14:47:11 -04:00
|
|
|
func NewLoader(config *d2config.Configuration) *Loader {
|
|
|
|
loader := &Loader{
|
|
|
|
config: config,
|
|
|
|
}
|
|
|
|
|
2020-09-08 17:39:34 -04:00
|
|
|
loader.Cache = d2cache.CreateCache(defaultCacheBudget)
|
2020-09-08 15:45:26 -04:00
|
|
|
|
2020-09-14 14:47:11 -04:00
|
|
|
loader.initFromConfig()
|
|
|
|
|
2020-09-08 15:45:26 -04:00
|
|
|
return loader
|
|
|
|
}
|
|
|
|
|
2020-09-09 14:35:52 -04:00
|
|
|
// Loader represents the manager that handles loading and caching assets with the asset Sources
|
2020-09-08 15:45:26 -04:00
|
|
|
// that have been added
|
|
|
|
type Loader struct {
|
2020-09-14 14:47:11 -04:00
|
|
|
config *d2config.Configuration
|
2020-09-08 15:45:26 -04:00
|
|
|
d2interface.Cache
|
2020-09-09 08:22:13 -04:00
|
|
|
*d2util.Logger
|
2020-09-09 14:35:52 -04:00
|
|
|
Sources []asset.Source
|
2020-09-08 15:45:26 -04:00
|
|
|
}
|
|
|
|
|
2020-09-14 14:47:11 -04:00
|
|
|
func (l *Loader) initFromConfig() {
|
|
|
|
if l.config == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, mpqName := range l.config.MpqLoadOrder {
|
|
|
|
cleanDir := filepath.Clean(l.config.MpqPath)
|
|
|
|
srcPath := filepath.Join(cleanDir, mpqName)
|
|
|
|
|
|
|
|
_, err := l.AddSource(srcPath)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err.Error())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-08 15:45:26 -04:00
|
|
|
// Load attempts to load an asset with the given sub-path. The sub-path is relative to the root
|
|
|
|
// of each asset source root (regardless of the type of asset source)
|
|
|
|
func (l *Loader) Load(subPath string) (asset.Asset, error) {
|
2020-09-14 14:47:11 -04:00
|
|
|
lang := defaultLanguage
|
|
|
|
|
|
|
|
if l.config != nil {
|
|
|
|
lang = l.config.Language
|
|
|
|
}
|
|
|
|
|
2020-09-08 15:45:26 -04:00
|
|
|
subPath = filepath.Clean(subPath)
|
2020-09-14 14:47:11 -04:00
|
|
|
subPath = strings.ReplaceAll(subPath, fontToken, "latin")
|
|
|
|
subPath = strings.ReplaceAll(subPath, tableToken, lang)
|
2020-09-08 15:45:26 -04:00
|
|
|
|
|
|
|
// first, we check the cache for an existing entry
|
|
|
|
if cached, found := l.Retrieve(subPath); found {
|
|
|
|
l.Debug(fmt.Sprintf("file `%s` exists in loader cache", subPath))
|
2020-09-14 14:47:11 -04:00
|
|
|
|
|
|
|
a := cached.(asset.Asset)
|
|
|
|
_, err := a.Seek(0, 0)
|
|
|
|
|
|
|
|
return a, err
|
2020-09-08 15:45:26 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// if it isn't in the cache, we check if each source can open the file
|
2020-09-09 14:35:52 -04:00
|
|
|
for idx := range l.Sources {
|
|
|
|
source := l.Sources[idx]
|
2020-09-08 15:45:26 -04:00
|
|
|
|
|
|
|
// if the source can open the file, then we cache it and return it
|
|
|
|
if loadedAsset, err := source.Open(subPath); err == nil {
|
2020-09-09 14:35:52 -04:00
|
|
|
err := l.Insert(subPath, loadedAsset, defaultCacheEntryWeight)
|
|
|
|
return loadedAsset, err
|
2020-09-08 15:45:26 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-14 14:47:11 -04:00
|
|
|
return nil, fmt.Errorf(errFmtFileNotFound, subPath)
|
2020-09-08 15:45:26 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// AddSource adds an asset source with the given path. The path will either resolve to a directory
|
|
|
|
// or a file on the host filesystem. In the case that it is a file, the file extension is used
|
|
|
|
// to determine the type of asset source. In the case that the path points to a directory, a
|
|
|
|
// FileSystemSource will be added.
|
2020-09-09 14:35:52 -04:00
|
|
|
func (l *Loader) AddSource(path string) (asset.Source, error) {
|
|
|
|
if l.Sources == nil {
|
|
|
|
l.Sources = make([]asset.Source, 0)
|
2020-09-08 15:45:26 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
cleanPath := filepath.Clean(path)
|
|
|
|
|
|
|
|
info, err := os.Lstat(cleanPath)
|
|
|
|
if err != nil {
|
|
|
|
l.Warning(err.Error())
|
2020-09-09 14:35:52 -04:00
|
|
|
return nil, err
|
2020-09-08 15:45:26 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
mode := info.Mode()
|
|
|
|
|
2020-09-09 14:35:52 -04:00
|
|
|
sourceType := types.AssetSourceUnknown
|
2020-09-08 15:45:26 -04:00
|
|
|
|
2020-09-09 14:35:52 -04:00
|
|
|
if mode.IsDir() {
|
|
|
|
sourceType = types.AssetSourceFileSystem
|
2020-09-08 15:45:26 -04:00
|
|
|
}
|
|
|
|
|
2020-09-09 14:35:52 -04:00
|
|
|
if mode.IsRegular() {
|
2020-09-14 14:47:11 -04:00
|
|
|
sourceType = types.CheckSourceType(cleanPath)
|
2020-09-08 15:45:26 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
switch sourceType {
|
|
|
|
case types.AssetSourceMPQ:
|
|
|
|
source, err := mpq.NewSource(cleanPath)
|
|
|
|
if err == nil {
|
|
|
|
l.Debug(fmt.Sprintf("adding MPQ source `%s`", cleanPath))
|
2020-09-09 14:35:52 -04:00
|
|
|
l.Sources = append(l.Sources, source)
|
|
|
|
|
|
|
|
return source, nil
|
2020-09-08 15:45:26 -04:00
|
|
|
}
|
2020-09-09 14:35:52 -04:00
|
|
|
case types.AssetSourceFileSystem:
|
|
|
|
source := &filesystem.Source{
|
|
|
|
Root: cleanPath,
|
|
|
|
}
|
|
|
|
|
|
|
|
l.Debug(fmt.Sprintf("adding filesystem source `%s`", cleanPath))
|
|
|
|
l.Sources = append(l.Sources, source)
|
|
|
|
|
|
|
|
return source, nil
|
2020-09-08 15:45:26 -04:00
|
|
|
case types.AssetSourceUnknown:
|
|
|
|
l.Warning(fmt.Sprintf("unknown asset source `%s`", cleanPath))
|
|
|
|
}
|
2020-09-09 14:35:52 -04:00
|
|
|
|
|
|
|
return nil, fmt.Errorf("unknown asset source `%s`", cleanPath)
|
2020-09-08 15:45:26 -04:00
|
|
|
}
|