Big fixes. Added start of video decode. Enhanced MPQ error messages.

This commit is contained in:
Tim Sarbin 2019-10-31 21:17:13 -04:00
parent 02082925b5
commit bf0412554f
14 changed files with 251 additions and 20 deletions

22
Common/GameState.go Normal file
View File

@ -0,0 +1,22 @@
package Common
import (
"log"
"time"
)
type GameState struct {
Seed int64
}
func CreateGameState() *GameState {
result := &GameState{
Seed: time.Now().UnixNano(),
}
return result
}
func LoadGameState(path string) *GameState {
log.Fatal("LoadGameState not implemented.")
return nil
}

View File

@ -38,7 +38,7 @@ type EngineConfig struct {
// Engine is the core OpenDiablo2 engine
type Engine struct {
Settings *EngineConfig // Engine configuration settings from json file
Settings *EngineConfig // Engine configuration settings from json file
Files map[string]string // Map that defines which files are in which MPQs
Palettes map[Palettes.Palette]Common.Palette // Color palettes
SoundEntries map[string]Sound.SoundEntry // Sound configurations
@ -72,6 +72,7 @@ func CreateEngine() *Engine {
loadingSpriteSizeX, loadingSpriteSizeY := result.LoadingSprite.GetSize()
result.LoadingSprite.MoveTo(int(400-(loadingSpriteSizeX/2)), int(300+(loadingSpriteSizeY/2)))
result.SetNextScene(Scenes.CreateMainMenu(result, result, result.UIManager, result.SoundManager))
//result.SetNextScene(Scenes.CreateBlizzardIntro(result, result))
return result
}
@ -92,8 +93,8 @@ func (v *Engine) loadConfigurationFile() {
if v.Settings.MpqPath[0] != '/' {
if _, err := os.Stat(v.Settings.MpqPath); os.IsNotExist(err) {
homeDir, _ := homedir.Dir()
newPath := strings.ReplaceAll(v.Settings.MpqPath, `C:\`, homeDir + "/.wine/drive_c/")
newPath = strings.ReplaceAll(newPath, "C:/", homeDir + "/.wine/drive_c/")
newPath := strings.ReplaceAll(v.Settings.MpqPath, `C:\`, homeDir+"/.wine/drive_c/")
newPath = strings.ReplaceAll(newPath, "C:/", homeDir+"/.wine/drive_c/")
newPath = strings.ReplaceAll(newPath, `\`, "/")
if _, err := os.Stat(newPath); !os.IsNotExist(err) {
log.Printf("Detected linux wine installation, path updated to wine prefix path.")
@ -137,11 +138,13 @@ func (v *Engine) LoadFile(fileName string) []byte {
mpqFile := v.Files[strings.ToLower(fileName)]
mpq, err := MPQ.Load(mpqFile)
if err != nil {
log.Printf("Error loading file '%s'", fileName)
log.Fatal(err)
}
fileName = strings.ReplaceAll(fileName, `/`, `\`)[1:]
blockTableEntry, err := mpq.GetFileBlockData(fileName)
if err != nil {
log.Printf("Error locating block data entry for '%s' in mpq file '%s'", fileName, mpq.FileName)
log.Fatal(err)
}
mpqStream := MPQ.CreateStream(mpq, blockTableEntry, fileName)

View File

@ -11,6 +11,7 @@ import (
// MPQ represents an MPQ archive
type MPQ struct {
FileName string
File *os.File
HashTableEntries []HashTableEntry
BlockTableEntries []BlockTableEntry
@ -83,7 +84,9 @@ func (v BlockTableEntry) HasFlag(flag FileFlag) bool {
// Load loads an MPQ file and returns a MPQ structure
func Load(fileName string) (MPQ, error) {
result := MPQ{}
result := MPQ{
FileName: fileName,
}
file, err := os.Open(fileName)
if err != nil {
return MPQ{}, err

View File

@ -1,4 +1,4 @@
package MapEngine
package Map
import (
"github.com/essial/OpenDiablo2/Common"

View File

@ -1,4 +1,4 @@
package MapEngine
package Map
import (
"log"

46
Map/Engine.go Normal file
View File

@ -0,0 +1,46 @@
package Map
import (
"image"
"github.com/essial/OpenDiablo2/Common"
"github.com/essial/OpenDiablo2/Sound"
"github.com/hajimehoshi/ebiten"
)
type EngineRegion struct {
Rect image.Rectangle
Region Region
}
type Engine struct {
soundManager *Sound.Manager
gameState *Common.GameState
fileProvider Common.FileProvider
regions []*EngineRegion
}
func CreateMapEngine(gameState *Common.GameState, soundManager *Sound.Manager, fileProvider Common.FileProvider) *Engine {
result := &Engine{
gameState: gameState,
soundManager: soundManager,
fileProvider: fileProvider,
regions: make([]*EngineRegion, 0),
}
return result
}
func (v *Engine) GenerateMap(regionType RegionIdType, levelPreset int) {
//randomSource := rand.NewSource(v.gameState.Seed)
//region := LoadRegion(randomSource, regionType, levelPreset, v.fileProvider)
v.soundManager.PlayBGM("/data/global/music/Act1/tristram.wav")
v.ReloadMapCache()
}
func (v *Engine) ReloadMapCache() {
}
func (v *Engine) Render(target *ebiten.Image) {
//v.region.RenderTile(300, 300, 0, 0, Map.RegionLayerTypeFloors, 0, screen)
}

View File

@ -1,4 +1,4 @@
package MapEngine
package Map
type Orientation int32

View File

@ -1,4 +1,4 @@
package MapEngine
package Map
import (
"log"
@ -77,9 +77,7 @@ func LoadRegion(seed rand.Source, levelType RegionIdType, levelPreset int, fileP
continue
}
dt1 := LoadDT1("/data/global/tiles/"+levelTypeDt1, fileProvider)
for _, tileData := range dt1.Tiles {
result.Tiles = append(result.Tiles, tileData)
}
result.Tiles = append(result.Tiles, dt1.Tiles...)
}
levelFilesToPick := make([]string, 0)
for _, fileRecord := range result.levelPreset.Files {

43
Scenes/BlizzardIntro.go Normal file
View File

@ -0,0 +1,43 @@
package Scenes
import (
"github.com/essial/OpenDiablo2/Common"
"github.com/essial/OpenDiablo2/Video"
"github.com/hajimehoshi/ebiten"
)
type BlizzardIntro struct {
fileProvider Common.FileProvider
sceneProvider SceneProvider
videoDecoder *Video.BinkDecoder
}
func CreateBlizzardIntro(fileProvider Common.FileProvider, sceneProvider SceneProvider) *BlizzardIntro {
result := &BlizzardIntro{
fileProvider: fileProvider,
sceneProvider: sceneProvider,
}
return result
}
func (v *BlizzardIntro) Load() []func() {
return []func(){
func() {
videoBytes := v.fileProvider.LoadFile("/data/local/video/BlizNorth640x480.bik")
v.videoDecoder = Video.CreateBinkDecoder(videoBytes)
},
}
}
func (v *BlizzardIntro) Unload() {
}
func (v *BlizzardIntro) Render(screen *ebiten.Image) {
}
func (v *BlizzardIntro) Update(tickTime float64) {
}

View File

@ -1,11 +1,8 @@
package Scenes
import (
"math/rand"
"time"
"github.com/essial/OpenDiablo2/Common"
"github.com/essial/OpenDiablo2/MapEngine"
"github.com/essial/OpenDiablo2/Map"
"github.com/essial/OpenDiablo2/Sound"
"github.com/essial/OpenDiablo2/UI"
"github.com/hajimehoshi/ebiten"
@ -16,7 +13,9 @@ type MapEngineTest struct {
soundManager *Sound.Manager
fileProvider Common.FileProvider
sceneProvider SceneProvider
region *MapEngine.Region
//region *Map.Region
gameState *Common.GameState
mapEngine *Map.Engine
}
func CreateMapEngineTest(fileProvider Common.FileProvider, sceneProvider SceneProvider, uiManager *UI.Manager, soundManager *Sound.Manager) *MapEngineTest {
@ -26,16 +25,18 @@ func CreateMapEngineTest(fileProvider Common.FileProvider, sceneProvider ScenePr
soundManager: soundManager,
sceneProvider: sceneProvider,
}
result.gameState = Common.CreateGameState()
return result
}
func (v *MapEngineTest) Load() []func() {
// TODO: Game seed comes from the game state object
randomSource := rand.NewSource(time.Now().UnixNano())
v.soundManager.PlayBGM("")
return []func(){
func() {
v.region = MapEngine.LoadRegion(randomSource, MapEngine.RegionAct1Tristram, 300, v.fileProvider)
v.mapEngine = Map.CreateMapEngine(v.gameState, v.soundManager, v.fileProvider)
v.mapEngine.GenerateMap(Map.RegionAct1Tristram, 300)
},
}
}
@ -45,7 +46,7 @@ func (v *MapEngineTest) Unload() {
}
func (v *MapEngineTest) Render(screen *ebiten.Image) {
v.region.RenderTile(300, 300, 0, 0, MapEngine.RegionLayerTypeFloors, 0, screen)
v.mapEngine.Render(screen)
}
func (v *MapEngineTest) Update(tickTime float64) {

111
Video/BinkDecoder.go Normal file
View File

@ -0,0 +1,111 @@
package Video
import (
"log"
"github.com/essial/OpenDiablo2/Common"
)
type BinkVideoMode uint32
const (
BinkVideoModeNormal BinkVideoMode = 0
BinkVideoModeHeightDoubled BinkVideoMode = 1
BinkVideoModeHeightInterlaced BinkVideoMode = 2
BinkVideoModeWidthDoubled BinkVideoMode = 3
BinkVideoModeWidthAndHeightDoubled BinkVideoMode = 4
BinkVideoModeWidthAndHeightInterlaced BinkVideoMode = 5
)
type BinkAudioAlgorithm uint32
const (
BinkAudioAlgorithmFFT BinkAudioAlgorithm = 0
BinkAudioAlgorithmDCT BinkAudioAlgorithm = 1
)
type BinkAudioTrack struct {
AudioChannels uint16
AudioSampleRateHz uint16
Stereo bool
Algorithm BinkAudioAlgorithm
AudioTrackId uint32
}
type BinkDecoder struct {
videoCodecRevision byte
fileSize uint32
numberOfFrames uint32
largestFrameSizeBytes uint32
VideoWidth uint32
VideoHeight uint32
FPS uint32
FrameTimeMS uint32
streamReader *Common.StreamReader
VideoMode BinkVideoMode
HasAlphaPlane bool
Grayscale bool
AudioTracks []BinkAudioTrack
FrameIndexTable []uint32 // Mask bit 0, as this is defined as a keyframe
frameIndex uint32
}
func CreateBinkDecoder(source []byte) *BinkDecoder {
result := &BinkDecoder{
streamReader: Common.CreateStreamReader(source),
}
result.loadHeaderInformation()
return result
}
func (v *BinkDecoder) GetNextFrame() {
//v.streamReader.SetPosition(uint64(v.FrameIndexTable[i] & 0xFFFFFFFE))
lengthOfAudioPackets := v.streamReader.GetUInt32() - 4
samplesInPacket := v.streamReader.GetUInt32()
v.streamReader.SkipBytes(int(lengthOfAudioPackets))
log.Printf("Frame %d:\tSamp: %d", v.frameIndex, samplesInPacket)
v.frameIndex++
}
func (v *BinkDecoder) loadHeaderInformation() {
v.streamReader.SetPosition(0)
headerBytes, _ := v.streamReader.ReadBytes(3)
if string(headerBytes) != "BIK" {
log.Fatal("Invalid header for bink video")
}
v.videoCodecRevision = v.streamReader.GetByte()
v.fileSize = v.streamReader.GetUInt32()
v.numberOfFrames = v.streamReader.GetUInt32()
v.largestFrameSizeBytes = v.streamReader.GetUInt32()
v.streamReader.SkipBytes(4) // Number of frames again?
v.VideoWidth = v.streamReader.GetUInt32()
v.VideoHeight = v.streamReader.GetUInt32()
fpsDividend := v.streamReader.GetUInt32()
fpsDivider := v.streamReader.GetUInt32()
v.FPS = uint32(float32(fpsDividend) / float32(fpsDivider))
v.FrameTimeMS = 1000 / v.FPS
videoFlags := v.streamReader.GetUInt32()
v.VideoMode = BinkVideoMode((videoFlags >> 28) & 0x0F)
v.HasAlphaPlane = ((videoFlags >> 20) & 0x1) == 1
v.Grayscale = ((videoFlags >> 17) & 0x1) == 1
numberOfAudioTracks := v.streamReader.GetUInt32()
v.AudioTracks = make([]BinkAudioTrack, numberOfAudioTracks)
for i := 0; i < int(numberOfAudioTracks); i++ {
v.streamReader.SkipBytes(2) // Unknown
v.AudioTracks[i].AudioChannels = v.streamReader.GetUInt16()
}
for i := 0; i < int(numberOfAudioTracks); i++ {
v.AudioTracks[i].AudioSampleRateHz = v.streamReader.GetUInt16()
flags := v.streamReader.GetUInt16()
v.AudioTracks[i].Stereo = ((flags >> 13) & 0x1) == 1
v.AudioTracks[i].Algorithm = BinkAudioAlgorithm((flags >> 12) & 0x1)
}
for i := 0; i < int(numberOfAudioTracks); i++ {
v.AudioTracks[i].AudioTrackId = v.streamReader.GetUInt32()
}
v.FrameIndexTable = make([]uint32, v.numberOfFrames+1)
for i := 0; i < int(v.numberOfFrames+1); i++ {
v.FrameIndexTable[i] = v.streamReader.GetUInt32()
}
}

1
go.mod
View File

@ -4,6 +4,7 @@ go 1.13
require (
github.com/JoshVarga/blast v0.0.0-20180421040937-681c804fb9f0
github.com/giorgisio/goav v0.1.0
github.com/hajimehoshi/ebiten v1.9.3
github.com/mitchellh/go-homedir v1.1.0
)

4
go.sum
View File

@ -1,5 +1,7 @@
github.com/JoshVarga/blast v0.0.0-20180421040937-681c804fb9f0 h1:tDnuU0igiBiQFjsvq1Bi7DpoUjqI76VVvW045vpeFeM=
github.com/JoshVarga/blast v0.0.0-20180421040937-681c804fb9f0/go.mod h1:h/5OEGj4G+fpYxluLjSMZbFY011ZxAntO98nCl8mrCs=
github.com/giorgisio/goav v0.1.0 h1:ZyfG3NfX7PMSimv4ulhmnQJf/XeHpMdGCn+afRmY5Oc=
github.com/giorgisio/goav v0.1.0/go.mod h1:RtH8HyxLRLU1iY0pjfhWBKRhnbsnmfoI+FxMwb5bfEo=
github.com/go-gl/glfw v0.0.0-20181213070059-819e8ce5125f h1:7MsFMbSn8Lcw0blK4+NEOf8DuHoOBDhJsHz04yh13pM=
github.com/go-gl/glfw v0.0.0-20181213070059-819e8ce5125f/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/gofrs/flock v0.7.0/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
@ -11,6 +13,8 @@ github.com/gopherjs/gopherwasm v0.1.1/go.mod h1:kx4n9a+MzHH0BJJhvlsQ65hqLFXDO/m2
github.com/gopherjs/gopherwasm v1.0.0/go.mod h1:SkZ8z7CWBz5VXbhJel8TxCmAcsQqzgWGR/8nMhyhZSI=
github.com/gopherjs/gopherwasm v1.1.0 h1:fA2uLoctU5+T3OhOn2vYP0DVT6pxc7xhTlBB1paATqQ=
github.com/gopherjs/gopherwasm v1.1.0/go.mod h1:SkZ8z7CWBz5VXbhJel8TxCmAcsQqzgWGR/8nMhyhZSI=
github.com/gosuri/uilive v0.0.0-20170323041506-ac356e6e42cd/go.mod h1:qkLSc0A5EXSP6B04TrN4oQoxqFI7A8XvoXSlJi8cwk8=
github.com/gosuri/uiprogress v0.0.0-20170224063937-d0567a9d84a1/go.mod h1:C1RTYn4Sc7iEyf6j8ft5dyoZ4212h8G1ol9QQluh5+0=
github.com/hajimehoshi/bitmapfont v1.1.1/go.mod h1:Hamfxgney7tDSmVOSDh2AWzoDH70OaC+P24zc02Gum4=
github.com/hajimehoshi/ebiten v1.9.3 h1:YijWGMBwH2XA1ZytUQFy33UDHeCSS6d4JZKH1wq38O0=
github.com/hajimehoshi/ebiten v1.9.3/go.mod h1:XxiJ4Eltvb1KmcD0i6F81eIB1asJhK47y5DC+FPkyso=

View File

@ -4,7 +4,6 @@ import (
"log"
"github.com/essial/OpenDiablo2/Core"
"github.com/essial/OpenDiablo2/MPQ"
"github.com/hajimehoshi/ebiten"
)