mirror of
https://github.com/OpenDiablo2/OpenDiablo2
synced 2024-11-17 18:06:03 -05:00
Compression updates
This commit is contained in:
parent
036d35956e
commit
cb5c068279
59
Common/BitStream.go
Normal file
59
Common/BitStream.go
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
package Common
|
||||||
|
|
||||||
|
import "log"
|
||||||
|
|
||||||
|
// BitStream is a utility class for reading groups of bits from a stream
|
||||||
|
type BitStream struct {
|
||||||
|
data []byte
|
||||||
|
dataPosition int
|
||||||
|
current int
|
||||||
|
bitCount int
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateBitStream(newData []byte) *BitStream {
|
||||||
|
result := &BitStream{
|
||||||
|
data: newData,
|
||||||
|
dataPosition: 0,
|
||||||
|
current: 0,
|
||||||
|
bitCount: 0,
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *BitStream) ReadBits(bitCount int) int {
|
||||||
|
if bitCount > 16 {
|
||||||
|
log.Panic("Maximum BitCount is 16")
|
||||||
|
}
|
||||||
|
if !v.EnsureBits(bitCount) {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
result := v.current & (0xffff >> (16 - bitCount))
|
||||||
|
v.WasteBits(bitCount)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *BitStream) PeekByte() int {
|
||||||
|
if !v.EnsureBits(8) {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
return v.current & 0xff
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *BitStream) EnsureBits(bitCount int) bool {
|
||||||
|
if bitCount <= v.bitCount {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if v.dataPosition >= len(v.data) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
nextvalue := v.data[v.dataPosition]
|
||||||
|
v.dataPosition++
|
||||||
|
v.current |= int(nextvalue) << v.bitCount
|
||||||
|
v.bitCount += 8
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *BitStream) WasteBits(bitCount int) {
|
||||||
|
v.current >>= bitCount
|
||||||
|
v.bitCount -= bitCount
|
||||||
|
}
|
33
Common/BitStream_test.go
Normal file
33
Common/BitStream_test.go
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package Common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBitStreamBits(t *testing.T) {
|
||||||
|
data := []byte{0xAA}
|
||||||
|
bitStream := CreateBitStream(data)
|
||||||
|
shouldBeOne := 0
|
||||||
|
for i := 0; i < 8; i++ {
|
||||||
|
bit := bitStream.ReadBits(1)
|
||||||
|
if bit != shouldBeOne {
|
||||||
|
t.Fatalf("Expected %d but got %d on iteration %d", shouldBeOne, bit, i)
|
||||||
|
}
|
||||||
|
if shouldBeOne == 1 {
|
||||||
|
shouldBeOne = 0
|
||||||
|
} else {
|
||||||
|
shouldBeOne = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBitStreamBytes(t *testing.T) {
|
||||||
|
data := []byte{0xAA, 0xBB, 0xCC, 0xDD, 0x12, 0x34, 0x56, 0x78}
|
||||||
|
bitStream := CreateBitStream(data)
|
||||||
|
for i := 0; i < 8; i++ {
|
||||||
|
b := byte(bitStream.ReadBits(8))
|
||||||
|
if b != data[i] {
|
||||||
|
t.Fatalf("Expected %d but got %d on iteration %d", data[i], b, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
372
Compression/Huffman.go
Normal file
372
Compression/Huffman.go
Normal file
@ -0,0 +1,372 @@
|
|||||||
|
//
|
||||||
|
// MpqHuffman.go based on the origina CS file
|
||||||
|
//
|
||||||
|
// Authors:
|
||||||
|
// Foole (fooleau@gmail.com)
|
||||||
|
// Tim Sarbin (tim.sarbin@gmail.com) (go translation)
|
||||||
|
//
|
||||||
|
// (C) 2006 Foole (fooleau@gmail.com)
|
||||||
|
// Based on code from StormLib by Ladislav Zezula and ShadowFlare
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
// the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
//
|
||||||
|
package Compression
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/essial/OpenDiablo2/Common"
|
||||||
|
)
|
||||||
|
|
||||||
|
// linkedNode is a node which is both hierachcical (parent/child) and doubly linked (next/prev)
|
||||||
|
type linkedNode struct {
|
||||||
|
DecompressedValue int
|
||||||
|
Weight int
|
||||||
|
Parent *linkedNode
|
||||||
|
Child0 *linkedNode
|
||||||
|
Prev *linkedNode
|
||||||
|
Next *linkedNode
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateLinkedNode(decompVal, weight int) *linkedNode {
|
||||||
|
result := &linkedNode{
|
||||||
|
DecompressedValue: decompVal,
|
||||||
|
Weight: weight,
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *linkedNode) GetChild1() *linkedNode {
|
||||||
|
return v.Child0.Prev
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *linkedNode) Insert(other *linkedNode) *linkedNode {
|
||||||
|
// 'Next' should have a lower weight we should return the lower weight
|
||||||
|
if other.Weight <= v.Weight {
|
||||||
|
// insert before
|
||||||
|
if v.Next != nil {
|
||||||
|
v.Next.Prev = other
|
||||||
|
other.Next = v.Next
|
||||||
|
}
|
||||||
|
v.Next = other
|
||||||
|
other.Prev = v
|
||||||
|
return other
|
||||||
|
}
|
||||||
|
if v.Prev == nil {
|
||||||
|
// Insert after
|
||||||
|
other.Prev = nil
|
||||||
|
v.Prev = other
|
||||||
|
other.Next = v
|
||||||
|
} else {
|
||||||
|
v.Prev.Insert(other)
|
||||||
|
}
|
||||||
|
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
var sPrime = [][]byte{
|
||||||
|
{ // Compression type 0
|
||||||
|
0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
|
||||||
|
}, { // Compression type 1
|
||||||
|
0x54, 0x16, 0x16, 0x0D, 0x0C, 0x08, 0x06, 0x05, 0x06, 0x05, 0x06, 0x03, 0x04, 0x04, 0x03, 0x05,
|
||||||
|
0x0E, 0x0B, 0x14, 0x13, 0x13, 0x09, 0x0B, 0x06, 0x05, 0x04, 0x03, 0x02, 0x03, 0x02, 0x02, 0x02,
|
||||||
|
0x0D, 0x07, 0x09, 0x06, 0x06, 0x04, 0x03, 0x02, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02,
|
||||||
|
0x09, 0x06, 0x04, 0x04, 0x04, 0x04, 0x03, 0x02, 0x03, 0x02, 0x02, 0x02, 0x02, 0x03, 0x02, 0x04,
|
||||||
|
0x08, 0x03, 0x04, 0x07, 0x09, 0x05, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x03, 0x02, 0x02,
|
||||||
|
0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x01, 0x02, 0x02,
|
||||||
|
0x06, 0x0A, 0x08, 0x08, 0x06, 0x07, 0x04, 0x03, 0x04, 0x04, 0x02, 0x02, 0x04, 0x02, 0x03, 0x03,
|
||||||
|
0x04, 0x03, 0x07, 0x07, 0x09, 0x06, 0x04, 0x03, 0x03, 0x02, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02,
|
||||||
|
0x0A, 0x02, 0x02, 0x03, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x06, 0x03, 0x05, 0x02, 0x03,
|
||||||
|
0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x03, 0x01, 0x01, 0x01,
|
||||||
|
0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x04, 0x04, 0x04, 0x07, 0x09, 0x08, 0x0C, 0x02,
|
||||||
|
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x03,
|
||||||
|
0x04, 0x01, 0x02, 0x04, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01,
|
||||||
|
0x04, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||||
|
0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||||
|
0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x06, 0x4B,
|
||||||
|
}, { // Compression type 2
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x27, 0x00, 0x00, 0x23, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0xFF, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x06, 0x0E, 0x10, 0x04,
|
||||||
|
0x06, 0x08, 0x05, 0x04, 0x04, 0x03, 0x03, 0x02, 0x02, 0x03, 0x03, 0x01, 0x01, 0x02, 0x01, 0x01,
|
||||||
|
0x01, 0x04, 0x02, 0x04, 0x02, 0x02, 0x02, 0x01, 0x01, 0x04, 0x01, 0x01, 0x02, 0x03, 0x03, 0x02,
|
||||||
|
0x03, 0x01, 0x03, 0x06, 0x04, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x02, 0x01, 0x01,
|
||||||
|
0x01, 0x29, 0x07, 0x16, 0x12, 0x40, 0x0A, 0x0A, 0x11, 0x25, 0x01, 0x03, 0x17, 0x10, 0x26, 0x2A,
|
||||||
|
0x10, 0x01, 0x23, 0x23, 0x2F, 0x10, 0x06, 0x07, 0x02, 0x09, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||||
|
}, { // Compression type 3
|
||||||
|
0xFF, 0x0B, 0x07, 0x05, 0x0B, 0x02, 0x02, 0x02, 0x06, 0x02, 0x02, 0x01, 0x04, 0x02, 0x01, 0x03,
|
||||||
|
0x09, 0x01, 0x01, 0x01, 0x03, 0x04, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01,
|
||||||
|
0x05, 0x01, 0x01, 0x01, 0x0D, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||||
|
0x02, 0x01, 0x01, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01,
|
||||||
|
0x0A, 0x04, 0x02, 0x01, 0x06, 0x03, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x01,
|
||||||
|
0x05, 0x02, 0x03, 0x04, 0x03, 0x03, 0x03, 0x02, 0x01, 0x01, 0x01, 0x02, 0x01, 0x02, 0x03, 0x03,
|
||||||
|
0x01, 0x03, 0x01, 0x01, 0x02, 0x05, 0x01, 0x01, 0x04, 0x03, 0x05, 0x01, 0x03, 0x01, 0x03, 0x03,
|
||||||
|
0x02, 0x01, 0x04, 0x03, 0x0A, 0x06, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||||
|
0x02, 0x02, 0x01, 0x0A, 0x02, 0x05, 0x01, 0x01, 0x02, 0x07, 0x02, 0x17, 0x01, 0x05, 0x01, 0x01,
|
||||||
|
0x0E, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||||
|
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||||
|
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||||
|
0x06, 0x02, 0x01, 0x04, 0x05, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01,
|
||||||
|
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||||
|
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x07, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01,
|
||||||
|
0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x11,
|
||||||
|
}, { // Compression type 4
|
||||||
|
0xFF, 0xFB, 0x98, 0x9A, 0x84, 0x85, 0x63, 0x64, 0x3E, 0x3E, 0x22, 0x22, 0x13, 0x13, 0x18, 0x17,
|
||||||
|
}, { // Compression type 5
|
||||||
|
0xFF, 0xF1, 0x9D, 0x9E, 0x9A, 0x9B, 0x9A, 0x97, 0x93, 0x93, 0x8C, 0x8E, 0x86, 0x88, 0x80, 0x82,
|
||||||
|
0x7C, 0x7C, 0x72, 0x73, 0x69, 0x6B, 0x5F, 0x60, 0x55, 0x56, 0x4A, 0x4B, 0x40, 0x41, 0x37, 0x37,
|
||||||
|
0x2F, 0x2F, 0x27, 0x27, 0x21, 0x21, 0x1B, 0x1C, 0x17, 0x17, 0x13, 0x13, 0x10, 0x10, 0x0D, 0x0D,
|
||||||
|
0x0B, 0x0B, 0x09, 0x09, 0x08, 0x08, 0x07, 0x07, 0x06, 0x05, 0x05, 0x04, 0x04, 0x04, 0x19, 0x18,
|
||||||
|
}, { // Compression type 6
|
||||||
|
0xC3, 0xCB, 0xF5, 0x41, 0xFF, 0x7B, 0xF7, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0xBF, 0xCC, 0xF2, 0x40, 0xFD, 0x7C, 0xF7, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x7A, 0x46,
|
||||||
|
}, { // Compression type 7
|
||||||
|
0xC3, 0xD9, 0xEF, 0x3D, 0xF9, 0x7C, 0xE9, 0x1E, 0xFD, 0xAB, 0xF1, 0x2C, 0xFC, 0x5B, 0xFE, 0x17,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0xBD, 0xD9, 0xEC, 0x3D, 0xF5, 0x7D, 0xE8, 0x1D, 0xFB, 0xAE, 0xF0, 0x2C, 0xFB, 0x5C, 0xFF, 0x18,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x70, 0x6C,
|
||||||
|
}, { // Compression type 8
|
||||||
|
0xBA, 0xC5, 0xDA, 0x33, 0xE3, 0x6D, 0xD8, 0x18, 0xE5, 0x94, 0xDA, 0x23, 0xDF, 0x4A, 0xD1, 0x10,
|
||||||
|
0xEE, 0xAF, 0xE4, 0x2C, 0xEA, 0x5A, 0xDE, 0x15, 0xF4, 0x87, 0xE9, 0x21, 0xF6, 0x43, 0xFC, 0x12,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0xB0, 0xC7, 0xD8, 0x33, 0xE3, 0x6B, 0xD6, 0x18, 0xE7, 0x95, 0xD8, 0x23, 0xDB, 0x49, 0xD0, 0x11,
|
||||||
|
0xE9, 0xB2, 0xE2, 0x2B, 0xE8, 0x5C, 0xDD, 0x15, 0xF1, 0x87, 0xE7, 0x20, 0xF7, 0x44, 0xFF, 0x13,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x5F, 0x9E,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func decode(input *Common.BitStream, head *linkedNode) *linkedNode {
|
||||||
|
node := head
|
||||||
|
|
||||||
|
for node.Child0 != nil {
|
||||||
|
bit := input.ReadBits(1)
|
||||||
|
if bit == -1 {
|
||||||
|
log.Fatal("unexpected end of file")
|
||||||
|
}
|
||||||
|
if bit == 0 {
|
||||||
|
node = node.Child0
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
node = node.GetChild1()
|
||||||
|
}
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildList(primeData []byte) *linkedNode {
|
||||||
|
root := CreateLinkedNode(256, 1)
|
||||||
|
root = root.Insert(CreateLinkedNode(257, 1))
|
||||||
|
|
||||||
|
for i := 0; i < len(primeData); i++ {
|
||||||
|
if primeData[i] != 0 {
|
||||||
|
root = root.Insert(CreateLinkedNode(i, int(primeData[i])))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return root
|
||||||
|
}
|
||||||
|
|
||||||
|
func insertNode(tail *linkedNode, decomp int) *linkedNode {
|
||||||
|
parent := tail
|
||||||
|
result := tail.Prev // This will be the new tail after the tree is updated
|
||||||
|
|
||||||
|
temp := CreateLinkedNode(parent.DecompressedValue, parent.Weight)
|
||||||
|
temp.Parent = parent
|
||||||
|
|
||||||
|
newnode := CreateLinkedNode(decomp, 0)
|
||||||
|
newnode.Parent = parent
|
||||||
|
|
||||||
|
parent.Child0 = newnode
|
||||||
|
|
||||||
|
tail.Next = temp
|
||||||
|
temp.Prev = tail
|
||||||
|
newnode.Prev = temp
|
||||||
|
temp.Next = newnode
|
||||||
|
|
||||||
|
adjustTree(newnode)
|
||||||
|
// TODO: For compression type 0, AdjustTree should be called
|
||||||
|
// once for every value written and only once here
|
||||||
|
adjustTree(newnode)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// This increases the weight of the new node and its antecendants
|
||||||
|
// and adjusts the tree if needed
|
||||||
|
func adjustTree(newNode *linkedNode) {
|
||||||
|
current := newNode
|
||||||
|
|
||||||
|
for current != nil {
|
||||||
|
current.Weight++
|
||||||
|
var insertpoint *linkedNode
|
||||||
|
var prev *linkedNode
|
||||||
|
// Go backwards thru the list looking for the insertion point
|
||||||
|
insertpoint = current
|
||||||
|
for true {
|
||||||
|
prev = insertpoint.Prev
|
||||||
|
if prev == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if prev.Weight >= current.Weight {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
insertpoint = prev
|
||||||
|
}
|
||||||
|
|
||||||
|
// No insertion point found
|
||||||
|
if insertpoint == current {
|
||||||
|
current = current.Parent
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// The following code basicly swaps insertpoint with current
|
||||||
|
|
||||||
|
// remove insert point
|
||||||
|
if insertpoint.Prev != nil {
|
||||||
|
insertpoint.Prev.Next = insertpoint.Next
|
||||||
|
}
|
||||||
|
insertpoint.Next.Prev = insertpoint.Prev
|
||||||
|
|
||||||
|
// Insert insertpoint after current
|
||||||
|
insertpoint.Next = current.Next
|
||||||
|
insertpoint.Prev = current
|
||||||
|
if current.Next != nil {
|
||||||
|
current.Next.Prev = insertpoint
|
||||||
|
}
|
||||||
|
current.Next = insertpoint
|
||||||
|
|
||||||
|
// remove current
|
||||||
|
current.Prev.Next = current.Next
|
||||||
|
current.Next.Prev = current.Prev
|
||||||
|
|
||||||
|
// insert current after prev
|
||||||
|
if prev == nil {
|
||||||
|
log.Fatal("previous frame not defined!")
|
||||||
|
}
|
||||||
|
|
||||||
|
temp := prev.Next
|
||||||
|
current.Next = temp
|
||||||
|
current.Prev = prev
|
||||||
|
temp.Prev = current
|
||||||
|
prev.Next = current
|
||||||
|
|
||||||
|
// Set up parent/child links
|
||||||
|
currentparent := current.Parent
|
||||||
|
insertparent := insertpoint.Parent
|
||||||
|
|
||||||
|
if currentparent.Child0 == current {
|
||||||
|
currentparent.Child0 = insertpoint
|
||||||
|
}
|
||||||
|
|
||||||
|
if currentparent != insertparent && insertparent.Child0 == insertpoint {
|
||||||
|
insertparent.Child0 = current
|
||||||
|
}
|
||||||
|
|
||||||
|
current.Parent = insertparent
|
||||||
|
insertpoint.Parent = currentparent
|
||||||
|
|
||||||
|
current = current.Parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildTree(tail *linkedNode) *linkedNode {
|
||||||
|
current := tail
|
||||||
|
|
||||||
|
for current != nil {
|
||||||
|
child0 := current
|
||||||
|
child1 := current.Prev
|
||||||
|
if child1 == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
parent := CreateLinkedNode(0, child0.Weight+child1.Weight)
|
||||||
|
parent.Child0 = child0
|
||||||
|
child0.Parent = parent
|
||||||
|
child1.Parent = parent
|
||||||
|
|
||||||
|
current.Insert(parent)
|
||||||
|
current = current.Prev.Prev
|
||||||
|
}
|
||||||
|
return current
|
||||||
|
}
|
||||||
|
|
||||||
|
func HuffmanDecompress(data []byte) []byte {
|
||||||
|
comptype := data[0]
|
||||||
|
|
||||||
|
if comptype == 0 {
|
||||||
|
log.Panic("compression type 0 is not currently supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
tail := buildList(sPrime[comptype])
|
||||||
|
head := buildTree(tail)
|
||||||
|
|
||||||
|
var outputstream bytes.Buffer
|
||||||
|
bitstream := Common.CreateBitStream(data[1:])
|
||||||
|
var decoded int
|
||||||
|
for true {
|
||||||
|
node := decode(bitstream, head)
|
||||||
|
decoded = node.DecompressedValue
|
||||||
|
switch decoded {
|
||||||
|
case 256:
|
||||||
|
break
|
||||||
|
case 257:
|
||||||
|
newvalue := bitstream.ReadBits(8)
|
||||||
|
outputstream.WriteByte(byte(newvalue))
|
||||||
|
tail = insertNode(tail, newvalue)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
outputstream.WriteByte(byte(decoded))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if decoded == 256 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return outputstream.Bytes()
|
||||||
|
}
|
141
Compression/Wav.go
Normal file
141
Compression/Wav.go
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
package Compression
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
var sLookup = []int{
|
||||||
|
0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E,
|
||||||
|
0x0010, 0x0011, 0x0013, 0x0015, 0x0017, 0x0019, 0x001C, 0x001F,
|
||||||
|
0x0022, 0x0025, 0x0029, 0x002D, 0x0032, 0x0037, 0x003C, 0x0042,
|
||||||
|
0x0049, 0x0050, 0x0058, 0x0061, 0x006B, 0x0076, 0x0082, 0x008F,
|
||||||
|
0x009D, 0x00AD, 0x00BE, 0x00D1, 0x00E6, 0x00FD, 0x0117, 0x0133,
|
||||||
|
0x0151, 0x0173, 0x0198, 0x01C1, 0x01EE, 0x0220, 0x0256, 0x0292,
|
||||||
|
0x02D4, 0x031C, 0x036C, 0x03C3, 0x0424, 0x048E, 0x0502, 0x0583,
|
||||||
|
0x0610, 0x06AB, 0x0756, 0x0812, 0x08E0, 0x09C3, 0x0ABD, 0x0BD0,
|
||||||
|
0x0CFF, 0x0E4C, 0x0FBA, 0x114C, 0x1307, 0x14EE, 0x1706, 0x1954,
|
||||||
|
0x1BDC, 0x1EA5, 0x21B6, 0x2515, 0x28CA, 0x2CDF, 0x315B, 0x364B,
|
||||||
|
0x3BB9, 0x41B2, 0x4844, 0x4F7E, 0x5771, 0x602F, 0x69CE, 0x7462,
|
||||||
|
0x7FFF,
|
||||||
|
}
|
||||||
|
|
||||||
|
var sLookup2 = []int{
|
||||||
|
-1, 0, -1, 4, -1, 2, -1, 6,
|
||||||
|
-1, 1, -1, 5, -1, 3, -1, 7,
|
||||||
|
-1, 1, -1, 5, -1, 3, -1, 7,
|
||||||
|
-1, 2, -1, 4, -1, 6, -1, 8,
|
||||||
|
}
|
||||||
|
|
||||||
|
func WavDecompress(data []byte, channelCount int) []byte {
|
||||||
|
Array1 := []int{0x2c, 0x2c}
|
||||||
|
Array2 := make([]int, channelCount)
|
||||||
|
|
||||||
|
input := bytes.NewReader(data)
|
||||||
|
var output bytes.Buffer
|
||||||
|
outputWriter := bufio.NewWriter(&output)
|
||||||
|
input.ReadByte()
|
||||||
|
|
||||||
|
shift, _ := input.ReadByte()
|
||||||
|
|
||||||
|
for i := 0; i < channelCount; i++ {
|
||||||
|
temp := int16(0)
|
||||||
|
binary.Read(input, binary.LittleEndian, &temp)
|
||||||
|
Array2[i] = int(temp)
|
||||||
|
binary.Write(outputWriter, binary.LittleEndian, &temp)
|
||||||
|
}
|
||||||
|
|
||||||
|
channel := channelCount - 1
|
||||||
|
pos, _ := input.Seek(0, io.SeekCurrent)
|
||||||
|
input.Seek(pos, io.SeekStart)
|
||||||
|
for pos < int64(input.Len()) {
|
||||||
|
value, _ := input.ReadByte()
|
||||||
|
|
||||||
|
if channelCount == 2 {
|
||||||
|
channel = 1 - channel
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value & 0x80) != 0 {
|
||||||
|
switch value & 0x7f {
|
||||||
|
case 0:
|
||||||
|
if Array1[channel] != 0 {
|
||||||
|
Array1[channel]--
|
||||||
|
}
|
||||||
|
d := int16(Array2[channel])
|
||||||
|
binary.Write(outputWriter, binary.LittleEndian, &d)
|
||||||
|
break
|
||||||
|
case 1:
|
||||||
|
Array1[channel] += 8
|
||||||
|
if Array1[channel] > 0x58 {
|
||||||
|
Array1[channel] = 0x58
|
||||||
|
}
|
||||||
|
if channelCount == 2 {
|
||||||
|
channel = 1 - channel
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 2:
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
Array1[channel] -= 8
|
||||||
|
if Array1[channel] < 0 {
|
||||||
|
Array1[channel] = 0
|
||||||
|
}
|
||||||
|
if channelCount == 2 {
|
||||||
|
channel = 1 - channel
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
temp1 := sLookup[Array1[channel]]
|
||||||
|
temp2 := temp1 >> shift
|
||||||
|
|
||||||
|
if (value & 1) != 0 {
|
||||||
|
temp2 += temp1 >> 0
|
||||||
|
}
|
||||||
|
if (value & 2) != 0 {
|
||||||
|
temp2 += temp1 >> 1
|
||||||
|
}
|
||||||
|
if (value & 4) != 0 {
|
||||||
|
temp2 += temp1 >> 2
|
||||||
|
}
|
||||||
|
if (value & 8) != 0 {
|
||||||
|
temp2 += temp1 >> 3
|
||||||
|
}
|
||||||
|
if (value & 0x10) != 0 {
|
||||||
|
temp2 += temp1 >> 4
|
||||||
|
}
|
||||||
|
if (value & 0x20) != 0 {
|
||||||
|
temp2 += temp1 >> 5
|
||||||
|
}
|
||||||
|
|
||||||
|
temp3 := Array2[channel]
|
||||||
|
if (value & 0x40) != 0 {
|
||||||
|
temp3 -= temp2
|
||||||
|
if temp3 <= -32768 {
|
||||||
|
temp3 = -32768
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
temp3 += temp2
|
||||||
|
if temp3 >= 32767 {
|
||||||
|
temp3 = 32767
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Array2[channel] = temp3
|
||||||
|
d := int16(temp3)
|
||||||
|
binary.Write(outputWriter, binary.LittleEndian, &d)
|
||||||
|
|
||||||
|
Array1[channel] += sLookup2[value&0x1f]
|
||||||
|
|
||||||
|
if Array1[channel] < 0 {
|
||||||
|
Array1[channel] = 0
|
||||||
|
} else {
|
||||||
|
if Array1[channel] > 0x58 {
|
||||||
|
Array1[channel] = 0x58
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return output.Bytes()
|
||||||
|
}
|
71
Engine.go
71
Engine.go
@ -12,6 +12,8 @@ import (
|
|||||||
"github.com/essial/OpenDiablo2/ResourcePaths"
|
"github.com/essial/OpenDiablo2/ResourcePaths"
|
||||||
|
|
||||||
"github.com/hajimehoshi/ebiten"
|
"github.com/hajimehoshi/ebiten"
|
||||||
|
"github.com/hajimehoshi/ebiten/audio"
|
||||||
|
"github.com/hajimehoshi/ebiten/audio/wav"
|
||||||
)
|
)
|
||||||
|
|
||||||
// EngineConfig defines the configuration for the engine, loaded from config.json
|
// EngineConfig defines the configuration for the engine, loaded from config.json
|
||||||
@ -26,6 +28,13 @@ type EngineConfig struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Engine is the core OpenDiablo2 engine
|
// Engine is the core OpenDiablo2 engine
|
||||||
|
type CursorButton uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
CursorButtonLeft CursorButton = 1
|
||||||
|
CursorButtonRight CursorButton = 2
|
||||||
|
)
|
||||||
|
|
||||||
type Engine struct {
|
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
|
Files map[string]string // Map that defines which files are in which MPQs
|
||||||
@ -35,10 +44,13 @@ type Engine struct {
|
|||||||
LoadingSprite Sprite // The sprite shown when loading stuff
|
LoadingSprite Sprite // The sprite shown when loading stuff
|
||||||
CursorX int // X position of the cursor
|
CursorX int // X position of the cursor
|
||||||
CursorY int // Y position of the cursor
|
CursorY int // Y position of the cursor
|
||||||
|
CursorButtons CursorButton // The buttons that are currently being pressed
|
||||||
LoadingProgress float64 // LoadingProcess is a range between 0.0 and 1.0. If set, loading screen displays.
|
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
|
CurrentScene Common.SceneInterface // The current scene being rendered
|
||||||
nextScene Common.SceneInterface // The next scene to be loaded at the end of the game loop
|
nextScene Common.SceneInterface // The next scene to be loaded at the end of the game loop
|
||||||
fontCache map[string]*MPQFont // The font cash
|
fontCache map[string]*MPQFont // The font cash
|
||||||
|
audioContext *audio.Context // The Audio context
|
||||||
|
bgmAudio *audio.Player // The audio player
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateEngine creates and instance of the OpenDiablo2 engine
|
// CreateEngine creates and instance of the OpenDiablo2 engine
|
||||||
@ -53,6 +65,11 @@ func CreateEngine() *Engine {
|
|||||||
result.mapMpqFiles()
|
result.mapMpqFiles()
|
||||||
result.loadPalettes()
|
result.loadPalettes()
|
||||||
result.loadSoundEntries()
|
result.loadSoundEntries()
|
||||||
|
audioContext, err := audio.NewContext(48000)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
result.audioContext = audioContext
|
||||||
result.CursorSprite = result.LoadSprite(ResourcePaths.CursorDefault, result.Palettes["units"])
|
result.CursorSprite = result.LoadSprite(ResourcePaths.CursorDefault, result.Palettes["units"])
|
||||||
result.LoadingSprite = result.LoadSprite(ResourcePaths.LoadingScreen, result.Palettes["loading"])
|
result.LoadingSprite = result.LoadSprite(ResourcePaths.LoadingScreen, result.Palettes["loading"])
|
||||||
loadingSpriteSizeX, loadingSpriteSizeY := result.LoadingSprite.GetSize()
|
loadingSpriteSizeX, loadingSpriteSizeY := result.LoadingSprite.GetSize()
|
||||||
@ -65,7 +82,7 @@ func (v *Engine) loadConfigurationFile() {
|
|||||||
log.Println("loading configuration file")
|
log.Println("loading configuration file")
|
||||||
configJSON, err := ioutil.ReadFile("config.json")
|
configJSON, err := ioutil.ReadFile("config.json")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
var config EngineConfig
|
var config EngineConfig
|
||||||
|
|
||||||
@ -81,11 +98,11 @@ func (v *Engine) mapMpqFiles() {
|
|||||||
mpqPath := path.Join(v.Settings.MpqPath, mpqFileName)
|
mpqPath := path.Join(v.Settings.MpqPath, mpqFileName)
|
||||||
mpq, err := LoadMPQ(mpqPath)
|
mpq, err := LoadMPQ(mpqPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
fileListText, err := mpq.ReadFile("(listfile)")
|
fileListText, err := mpq.ReadFile("(listfile)")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
fileList := strings.Split(string(fileListText), "\r\n")
|
fileList := strings.Split(string(fileListText), "\r\n")
|
||||||
for _, filePath := range fileList {
|
for _, filePath := range fileList {
|
||||||
@ -104,11 +121,12 @@ func (v *Engine) GetFile(fileName string) []byte {
|
|||||||
mpqFile := v.Files[strings.ToLower(fileName)]
|
mpqFile := v.Files[strings.ToLower(fileName)]
|
||||||
mpq, err := LoadMPQ(mpqFile)
|
mpq, err := LoadMPQ(mpqFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
blockTableEntry, err := mpq.getFileBlockData(strings.ReplaceAll(fileName, `/`, `\`)[1:])
|
fileName = strings.ReplaceAll(fileName, `/`, `\`)[1:]
|
||||||
|
blockTableEntry, err := mpq.getFileBlockData(fileName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
mpqStream := CreateMPQStream(mpq, blockTableEntry, fileName)
|
mpqStream := CreateMPQStream(mpq, blockTableEntry, fileName)
|
||||||
result := make([]byte, blockTableEntry.UncompressedFileSize)
|
result := make([]byte, blockTableEntry.UncompressedFileSize)
|
||||||
@ -169,11 +187,23 @@ func (v *Engine) updateScene() {
|
|||||||
v.CurrentScene.Load()
|
v.CurrentScene.Load()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CursorButtonPressed determines if the specified button has been pressed
|
||||||
|
func (v *Engine) CursorButtonPressed(button CursorButton) bool {
|
||||||
|
return v.CursorButtons&button > 0
|
||||||
|
}
|
||||||
|
|
||||||
// Update updates the internal state of the engine
|
// Update updates the internal state of the engine
|
||||||
func (v *Engine) Update() {
|
func (v *Engine) Update() {
|
||||||
v.updateScene()
|
v.updateScene()
|
||||||
if v.CurrentScene == nil {
|
if v.CurrentScene == nil {
|
||||||
panic("no scene loaded")
|
log.Fatal("no scene loaded")
|
||||||
|
}
|
||||||
|
v.CursorButtons = 0
|
||||||
|
if ebiten.IsMouseButtonPressed(ebiten.MouseButtonLeft) {
|
||||||
|
v.CursorButtons |= CursorButtonLeft
|
||||||
|
}
|
||||||
|
if ebiten.IsMouseButtonPressed(ebiten.MouseButtonRight) {
|
||||||
|
v.CursorButtons |= CursorButtonRight
|
||||||
}
|
}
|
||||||
v.CurrentScene.Update()
|
v.CurrentScene.Update()
|
||||||
}
|
}
|
||||||
@ -186,7 +216,7 @@ func (v *Engine) Draw(screen *ebiten.Image) {
|
|||||||
v.LoadingSprite.Draw(screen)
|
v.LoadingSprite.Draw(screen)
|
||||||
} else {
|
} else {
|
||||||
if v.CurrentScene == nil {
|
if v.CurrentScene == nil {
|
||||||
panic("no scene loaded")
|
log.Fatal("no scene loaded")
|
||||||
}
|
}
|
||||||
v.CurrentScene.Render(screen)
|
v.CurrentScene.Render(screen)
|
||||||
}
|
}
|
||||||
@ -210,3 +240,28 @@ func (v *Engine) GetFont(font, palette string) *MPQFont {
|
|||||||
v.fontCache[font+"_"+palette] = newFont
|
v.fontCache[font+"_"+palette] = newFont
|
||||||
return newFont
|
return newFont
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PlayBGM plays an infinitely looping background track
|
||||||
|
func (v *Engine) PlayBGM(song string) {
|
||||||
|
go func() {
|
||||||
|
if v.bgmAudio != nil {
|
||||||
|
v.bgmAudio.Close()
|
||||||
|
}
|
||||||
|
audioData := v.GetFile(song)
|
||||||
|
//audioData2, _ := ioutil.ReadFile(`C:\Users\lunat\Desktop\D2\Extracted\data\global\music\introedit.wav`)
|
||||||
|
//log.Printf("%d, %d", len(audioData), len(audioData2))
|
||||||
|
d, err := wav.Decode(v.audioContext, audio.BytesReadSeekCloser(audioData))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
//s := audio.NewInfiniteLoop(d, int64(len(audioData)))
|
||||||
|
|
||||||
|
v.bgmAudio, err = audio.NewPlayer(v.audioContext, d)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
// Play the infinite-length stream. This never ends.
|
||||||
|
v.bgmAudio.Rewind()
|
||||||
|
v.bgmAudio.Play()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
25
MPQStream.go
25
MPQStream.go
@ -5,8 +5,10 @@ import (
|
|||||||
"compress/zlib"
|
"compress/zlib"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/JoshVarga/blast"
|
"github.com/JoshVarga/blast"
|
||||||
|
"github.com/essial/OpenDiablo2/Compression"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MPQStream represents a stream of data in an MPQ archive
|
// MPQStream represents a stream of data in an MPQ archive
|
||||||
@ -23,13 +25,14 @@ type MPQStream struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CreateMPQStream creates an MPQ stream
|
// CreateMPQStream creates an MPQ stream
|
||||||
func CreateMPQStream(mpq MPQ, blockTableEntry MPQBlockTableEntry, fileName string) MPQStream {
|
func CreateMPQStream(mpq MPQ, blockTableEntry MPQBlockTableEntry, fileName string) *MPQStream {
|
||||||
result := MPQStream{
|
result := &MPQStream{
|
||||||
MPQData: mpq,
|
MPQData: mpq,
|
||||||
BlockTableEntry: blockTableEntry,
|
BlockTableEntry: blockTableEntry,
|
||||||
CurrentBlockIndex: 0xFFFFFFFF,
|
CurrentBlockIndex: 0xFFFFFFFF,
|
||||||
}
|
}
|
||||||
result.EncryptionSeed = hashString(fileName, 3)
|
fileSegs := strings.Split(fileName, `\`)
|
||||||
|
result.EncryptionSeed = hashString(fileSegs[len(fileSegs)-1], 3)
|
||||||
if result.BlockTableEntry.HasFlag(MpqFileFixKey) {
|
if result.BlockTableEntry.HasFlag(MpqFileFixKey) {
|
||||||
result.EncryptionSeed = (result.EncryptionSeed + result.BlockTableEntry.FilePosition) ^ result.BlockTableEntry.UncompressedFileSize
|
result.EncryptionSeed = (result.EncryptionSeed + result.BlockTableEntry.FilePosition) ^ result.BlockTableEntry.UncompressedFileSize
|
||||||
}
|
}
|
||||||
@ -184,17 +187,21 @@ func decompressMulti(data []byte, expectedLength uint32) []byte {
|
|||||||
// TODO: sparse then bzip2
|
// TODO: sparse then bzip2
|
||||||
panic("sparse decompression + bzip2 decompression not supported")
|
panic("sparse decompression + bzip2 decompression not supported")
|
||||||
case 0x41:
|
case 0x41:
|
||||||
//sinput = MpqHuffman.Decompress(sinput);
|
sinput := Compression.HuffmanDecompress(data[1:])
|
||||||
//return MpqWavCompression.Decompress(sinput, 1);
|
sinput = Compression.WavDecompress(sinput, 1)
|
||||||
panic("mpqhuffman decompression not supported")
|
tmp := make([]byte, len(sinput))
|
||||||
|
copy(tmp, sinput)
|
||||||
|
return tmp
|
||||||
case 0x48:
|
case 0x48:
|
||||||
//byte[] result = PKDecompress(sinput, outputLength);
|
//byte[] result = PKDecompress(sinput, outputLength);
|
||||||
//return MpqWavCompression.Decompress(new MemoryStream(result), 1);
|
//return MpqWavCompression.Decompress(new MemoryStream(result), 1);
|
||||||
panic("pk + mpqwav decompression not supported")
|
panic("pk + mpqwav decompression not supported")
|
||||||
case 0x81:
|
case 0x81:
|
||||||
//sinput = MpqHuffman.Decompress(sinput);
|
sinput := Compression.HuffmanDecompress(data[1:])
|
||||||
//return MpqWavCompression.Decompress(sinput, 2);
|
sinput = Compression.WavDecompress(sinput, 1)
|
||||||
panic("huff + mpqwav decompression not supported")
|
tmp := make([]byte, len(sinput))
|
||||||
|
copy(tmp, sinput)
|
||||||
|
return tmp
|
||||||
case 0x88:
|
case 0x88:
|
||||||
//byte[] result = PKDecompress(sinput, outputLength);
|
//byte[] result = PKDecompress(sinput, outputLength);
|
||||||
//return MpqWavCompression.Decompress(new MemoryStream(result), 2);
|
//return MpqWavCompression.Decompress(new MemoryStream(result), 2);
|
||||||
|
@ -18,6 +18,7 @@ type MainMenu struct {
|
|||||||
CopyrightLabel *UILabel
|
CopyrightLabel *UILabel
|
||||||
CopyrightLabel2 *UILabel
|
CopyrightLabel2 *UILabel
|
||||||
ShowTrademarkScreen bool
|
ShowTrademarkScreen bool
|
||||||
|
LeftButtonHeld bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateMainMenu(engine *Engine) *MainMenu {
|
func CreateMainMenu(engine *Engine) *MainMenu {
|
||||||
@ -30,6 +31,7 @@ func CreateMainMenu(engine *Engine) *MainMenu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (v *MainMenu) Load() {
|
func (v *MainMenu) Load() {
|
||||||
|
v.Engine.PlayBGM(ResourcePaths.BGMTitle)
|
||||||
go func() {
|
go func() {
|
||||||
loadStep := 1.0 / 8.0
|
loadStep := 1.0 / 8.0
|
||||||
v.Engine.LoadingProgress = 0
|
v.Engine.LoadingProgress = 0
|
||||||
@ -110,5 +112,11 @@ func (v *MainMenu) Render(screen *ebiten.Image) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (v *MainMenu) Update() {
|
func (v *MainMenu) Update() {
|
||||||
|
if v.ShowTrademarkScreen {
|
||||||
|
if v.Engine.CursorButtonPressed(CursorButtonLeft) {
|
||||||
|
v.LeftButtonHeld = true
|
||||||
|
v.ShowTrademarkScreen = false
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user