mirror of
https://github.com/OpenDiablo2/OpenDiablo2
synced 2025-02-10 02:26:29 -05:00
343 lines
11 KiB
Go
343 lines
11 KiB
Go
package d2systems
|
|
|
|
import (
|
|
"io"
|
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2cache"
|
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2animdata"
|
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2cof"
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2dat"
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2dc6"
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2dcc"
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2ds1"
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2dt1"
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2pl2"
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2tbl"
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2txt"
|
|
"github.com/gravestench/akara"
|
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2components"
|
|
)
|
|
|
|
const (
|
|
assetCacheBudget = 1024
|
|
assetCacheEntryWeight = 1 // may want to make different weights for different asset types
|
|
)
|
|
|
|
// NewAssetLoader creates a new asset loader instance
|
|
func NewAssetLoader() *AssetLoaderSystem {
|
|
// we are going to check entities that dont yet have loaded asset types
|
|
filesToLoad := akara.NewFilter().
|
|
Require(d2components.FilePath).
|
|
Require(d2components.FileType).
|
|
Require(d2components.FileHandle).
|
|
Forbid(d2components.FileSource).
|
|
Forbid(d2components.GameConfig).
|
|
Forbid(d2components.StringTable).
|
|
Forbid(d2components.DataDictionary).
|
|
Forbid(d2components.Palette).
|
|
Forbid(d2components.PaletteTransform).
|
|
Forbid(d2components.Cof).
|
|
Forbid(d2components.Dc6).
|
|
Forbid(d2components.Dcc).
|
|
Forbid(d2components.Ds1).
|
|
Forbid(d2components.Dt1).
|
|
Forbid(d2components.Wav).
|
|
Forbid(d2components.AnimData).
|
|
Build()
|
|
|
|
fileSources := akara.NewFilter().
|
|
Require(d2components.FileSource).
|
|
Build()
|
|
|
|
return &AssetLoaderSystem{
|
|
SubscriberSystem: akara.NewSubscriberSystem(filesToLoad, fileSources),
|
|
cache: d2cache.CreateCache(assetCacheBudget).(*d2cache.Cache),
|
|
}
|
|
}
|
|
|
|
var _ akara.System = &AssetLoaderSystem{}
|
|
|
|
type AssetLoaderSystem struct {
|
|
*akara.SubscriberSystem
|
|
fileSub *akara.Subscription
|
|
sourceSub *akara.Subscription
|
|
cache *d2cache.Cache
|
|
filePaths *d2components.FilePathMap
|
|
fileTypes *d2components.FileTypeMap
|
|
fileHandles *d2components.FileHandleMap
|
|
fileSources *d2components.FileSourceMap
|
|
stringTables *d2components.StringTableMap
|
|
fontTables *d2components.FontTableMap
|
|
dataDictionaries *d2components.DataDictionaryMap
|
|
palettes *d2components.PaletteMap
|
|
transforms *d2components.PaletteTransformMap
|
|
cof *d2components.CofMap
|
|
dc6 *d2components.Dc6Map
|
|
dcc *d2components.DccMap
|
|
ds1 *d2components.Ds1Map
|
|
dt1 *d2components.Dt1Map
|
|
wav *d2components.WavMap
|
|
animDatas *d2components.AnimDataMap
|
|
}
|
|
|
|
// Init initializes the system with the given world
|
|
func (m *AssetLoaderSystem) Init(world *akara.World) {
|
|
m.World = world
|
|
|
|
if world == nil {
|
|
m.SetActive(false)
|
|
return
|
|
}
|
|
|
|
for subIdx := range m.Subscriptions {
|
|
m.Subscriptions[subIdx] = m.AddSubscription(m.Subscriptions[subIdx].Filter)
|
|
}
|
|
|
|
m.fileSub = m.Subscriptions[0]
|
|
m.sourceSub = 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)
|
|
m.stringTables = m.InjectMap(d2components.StringTable).(*d2components.StringTableMap)
|
|
m.dataDictionaries = m.InjectMap(d2components.DataDictionary).(*d2components.DataDictionaryMap)
|
|
m.palettes = m.InjectMap(d2components.Palette).(*d2components.PaletteMap)
|
|
m.transforms = m.InjectMap(d2components.PaletteTransform).(*d2components.PaletteTransformMap)
|
|
m.fontTables = m.InjectMap(d2components.FontTable).(*d2components.FontTableMap)
|
|
m.cof = m.InjectMap(d2components.Cof).(*d2components.CofMap)
|
|
m.dc6 = m.InjectMap(d2components.Dc6).(*d2components.Dc6Map)
|
|
m.dcc = m.InjectMap(d2components.Dcc).(*d2components.DccMap)
|
|
m.ds1 = m.InjectMap(d2components.Ds1).(*d2components.Ds1Map)
|
|
m.dt1 = m.InjectMap(d2components.Dt1).(*d2components.Dt1Map)
|
|
m.wav = m.InjectMap(d2components.Wav).(*d2components.WavMap)
|
|
m.animDatas = m.InjectMap(d2components.AnimData).(*d2components.AnimDataMap)
|
|
}
|
|
|
|
// Process processes all of the Entities
|
|
func (m *AssetLoaderSystem) Process() {
|
|
for _, eid := range m.fileSub.GetEntities() {
|
|
m.loadAsset(eid)
|
|
}
|
|
}
|
|
|
|
func (m *AssetLoaderSystem) loadAsset(id akara.EID) {
|
|
fp, found := m.filePaths.GetFilePath(id)
|
|
if !found {
|
|
return
|
|
}
|
|
|
|
ft, found := m.fileTypes.GetFileType(id)
|
|
if !found {
|
|
return
|
|
}
|
|
|
|
fh, found := m.fileHandles.GetFileHandle(id)
|
|
if !found {
|
|
return
|
|
}
|
|
|
|
if found := m.pullFromCache(id, fp.Path, ft.Type); found {
|
|
return
|
|
}
|
|
|
|
_, _ = fh.Data.Seek(0, 0)
|
|
|
|
data, buf := make([]byte, 0), make([]byte, 16)
|
|
|
|
for {
|
|
numRead, err := fh.Data.Read(buf)
|
|
data = append(data, buf[:numRead]...)
|
|
|
|
if numRead < 1 || err != nil {
|
|
break
|
|
}
|
|
}
|
|
|
|
m.parseAndCache(id, fp.Path, ft.Type, data)
|
|
}
|
|
|
|
func (m *AssetLoaderSystem) pullFromCache(id akara.EID, path string, t d2enum.FileType) bool {
|
|
entry, found := m.cache.Retrieve(path)
|
|
if !found {
|
|
return found
|
|
}
|
|
|
|
switch t {
|
|
case d2enum.FileTypeStringTable:
|
|
m.stringTables.AddStringTable(id).TextDictionary = entry.(*d2tbl.TextDictionary)
|
|
case d2enum.FileTypeFontTable:
|
|
m.fontTables.AddFontTable(id).Data = entry.([]byte)
|
|
case d2enum.FileTypeDataDictionary:
|
|
m.dataDictionaries.AddDataDictionary(id).DataDictionary = entry.(*d2txt.DataDictionary)
|
|
case d2enum.FileTypePalette:
|
|
m.palettes.AddPalette(id).Palette = entry.(d2interface.Palette)
|
|
case d2enum.FileTypePaletteTransform:
|
|
m.transforms.AddPaletteTransform(id).Transform = entry.(*d2pl2.PL2)
|
|
case d2enum.FileTypeCOF:
|
|
m.cof.AddCof(id).COF = entry.(*d2cof.COF)
|
|
case d2enum.FileTypeDC6:
|
|
m.dc6.AddDc6(id).DC6 = entry.(*d2dc6.DC6)
|
|
case d2enum.FileTypeDCC:
|
|
m.dcc.AddDcc(id).DCC = entry.(*d2dcc.DCC)
|
|
case d2enum.FileTypeDS1:
|
|
m.ds1.AddDs1(id).DS1 = entry.(*d2ds1.DS1)
|
|
case d2enum.FileTypeDT1:
|
|
m.dt1.AddDt1(id).DT1 = entry.(*d2dt1.DT1)
|
|
case d2enum.FileTypeWAV:
|
|
m.wav.AddWav(id).Data = entry.(d2interface.DataStream)
|
|
case d2enum.FileTypeD2:
|
|
m.animDatas.AddAnimData(id).AnimationData = entry.(*d2animdata.AnimationData)
|
|
}
|
|
|
|
return found
|
|
}
|
|
|
|
func (m *AssetLoaderSystem) parseAndCache(id akara.EID, path string, t d2enum.FileType, data []byte) {
|
|
go func() {
|
|
switch t {
|
|
case d2enum.FileTypeStringTable:
|
|
m.loadFileTypeStringTable(id, path, data) // TODO: add error handling for string table load
|
|
case d2enum.FileTypeFontTable:
|
|
m.loadFileTypeFontTable(id, path, data) // TODO: add error handling for string table load
|
|
case d2enum.FileTypeDataDictionary:
|
|
m.loadFileTypeDataDictionary(id, path, data) // TODO: add error handling for data dict load
|
|
case d2enum.FileTypePalette:
|
|
m.loadFileTypePalette(id, path, data)
|
|
case d2enum.FileTypePaletteTransform:
|
|
m.loadFileTypePaletteTransform(id, path, data)
|
|
case d2enum.FileTypeCOF:
|
|
m.loadFileTypeCOF(id, path, data)
|
|
case d2enum.FileTypeDC6:
|
|
m.loadFileTypeDC6(id, path, data)
|
|
case d2enum.FileTypeDCC:
|
|
m.loadFileTypeDCC(id, path, data)
|
|
case d2enum.FileTypeDS1:
|
|
m.loadFileTypeDS1(id, path, data)
|
|
case d2enum.FileTypeDT1:
|
|
m.loadFileTypeDT1(id, path, data)
|
|
case d2enum.FileTypeWAV:
|
|
fh, found := m.fileHandles.GetFileHandle(id)
|
|
if !found {
|
|
return
|
|
}
|
|
|
|
m.loadFileTypeWAV(id, path, fh.Data)
|
|
case d2enum.FileTypeD2:
|
|
m.loadFileTypeD2(id, path, data)
|
|
}
|
|
}()
|
|
}
|
|
|
|
func (m *AssetLoaderSystem) loadFileTypeStringTable(id akara.EID, path string, data []byte) {
|
|
txt := d2tbl.LoadTextDictionary(data)
|
|
loaded := &txt
|
|
m.stringTables.AddStringTable(id).TextDictionary = loaded
|
|
m.cache.Insert(path, loaded, assetCacheEntryWeight)
|
|
}
|
|
|
|
func (m *AssetLoaderSystem) loadFileTypeFontTable(id akara.EID, path string, data []byte) {
|
|
m.fontTables.AddFontTable(id).Data = data
|
|
m.cache.Insert(path, data, assetCacheEntryWeight)
|
|
}
|
|
|
|
func (m *AssetLoaderSystem) loadFileTypeDataDictionary(id akara.EID, path string, data []byte) {
|
|
loaded := d2txt.LoadDataDictionary(data)
|
|
m.dataDictionaries.AddDataDictionary(id).DataDictionary = loaded
|
|
m.cache.Insert(path, loaded, assetCacheEntryWeight)
|
|
}
|
|
|
|
func (m *AssetLoaderSystem) loadFileTypePalette(id akara.EID, path string, data []byte) error {
|
|
loaded, err := d2dat.Load(data)
|
|
if err == nil {
|
|
m.palettes.AddPalette(id).Palette = loaded
|
|
m.cache.Insert(path, loaded, assetCacheEntryWeight)
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
func (m *AssetLoaderSystem) loadFileTypePaletteTransform(id akara.EID, path string, data []byte) error {
|
|
loaded, err := d2pl2.Load(data)
|
|
if err == nil {
|
|
m.transforms.AddPaletteTransform(id).Transform = loaded
|
|
m.cache.Insert(path, loaded, assetCacheEntryWeight)
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
func (m *AssetLoaderSystem) loadFileTypeCOF(id akara.EID, path string, data []byte) error {
|
|
loaded, err := d2cof.Load(data)
|
|
if err == nil {
|
|
m.cof.AddCof(id).COF = loaded
|
|
m.cache.Insert(path, loaded, assetCacheEntryWeight)
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
func (m *AssetLoaderSystem) loadFileTypeDC6(id akara.EID, path string, data []byte) error {
|
|
loaded, err := d2dc6.Load(data)
|
|
if err == nil {
|
|
m.dc6.AddDc6(id).DC6 = loaded
|
|
m.cache.Insert(path, loaded, assetCacheEntryWeight)
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
func (m *AssetLoaderSystem) loadFileTypeDCC(id akara.EID, path string, data []byte) error {
|
|
loaded, err := d2dcc.Load(data)
|
|
if err == nil {
|
|
m.dcc.AddDcc(id).DCC = loaded
|
|
m.cache.Insert(path, loaded, assetCacheEntryWeight)
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
func (m *AssetLoaderSystem) loadFileTypeDS1(id akara.EID, path string, data []byte) error {
|
|
loaded, err := d2ds1.LoadDS1(data)
|
|
if err == nil {
|
|
m.ds1.AddDs1(id).DS1 = loaded
|
|
m.cache.Insert(path, loaded, assetCacheEntryWeight)
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
func (m *AssetLoaderSystem) loadFileTypeDT1(id akara.EID, path string, data []byte) error {
|
|
loaded, err := d2dt1.LoadDT1(data)
|
|
if err == nil {
|
|
m.dt1.AddDt1(id).DT1 = loaded
|
|
m.cache.Insert(path, loaded, assetCacheEntryWeight)
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
func (m *AssetLoaderSystem) loadFileTypeWAV(id akara.EID, path string, seeker io.ReadSeeker) {
|
|
component := m.wav.AddWav(id)
|
|
component.Data = seeker
|
|
m.cache.Insert(path, seeker, assetCacheEntryWeight)
|
|
}
|
|
|
|
func (m *AssetLoaderSystem) loadFileTypeD2(id akara.EID, path string, data []byte) error {
|
|
loaded, err := d2animdata.Load(data)
|
|
if err == nil {
|
|
m.animDatas.AddAnimData(id).AnimationData = loaded
|
|
m.cache.Insert(path, loaded, assetCacheEntryWeight)
|
|
}
|
|
|
|
return err
|
|
}
|