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/hajimehoshi/ebiten"
|
||||
"github.com/hajimehoshi/ebiten/audio"
|
||||
"github.com/hajimehoshi/ebiten/audio/wav"
|
||||
)
|
||||
|
||||
// EngineConfig defines the configuration for the engine, loaded from config.json
|
||||
@ -26,6 +28,13 @@ type EngineConfig struct {
|
||||
}
|
||||
|
||||
// Engine is the core OpenDiablo2 engine
|
||||
type CursorButton uint8
|
||||
|
||||
const (
|
||||
CursorButtonLeft CursorButton = 1
|
||||
CursorButtonRight CursorButton = 2
|
||||
)
|
||||
|
||||
type Engine struct {
|
||||
Settings EngineConfig // Engine configuration settings from json file
|
||||
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
|
||||
CursorX int // X 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.
|
||||
CurrentScene Common.SceneInterface // The current scene being rendered
|
||||
nextScene Common.SceneInterface // The next scene to be loaded at the end of the game loop
|
||||
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
|
||||
@ -53,6 +65,11 @@ func CreateEngine() *Engine {
|
||||
result.mapMpqFiles()
|
||||
result.loadPalettes()
|
||||
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.LoadingSprite = result.LoadSprite(ResourcePaths.LoadingScreen, result.Palettes["loading"])
|
||||
loadingSpriteSizeX, loadingSpriteSizeY := result.LoadingSprite.GetSize()
|
||||
@ -65,7 +82,7 @@ func (v *Engine) loadConfigurationFile() {
|
||||
log.Println("loading configuration file")
|
||||
configJSON, err := ioutil.ReadFile("config.json")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
log.Fatal(err)
|
||||
}
|
||||
var config EngineConfig
|
||||
|
||||
@ -81,11 +98,11 @@ func (v *Engine) mapMpqFiles() {
|
||||
mpqPath := path.Join(v.Settings.MpqPath, mpqFileName)
|
||||
mpq, err := LoadMPQ(mpqPath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
log.Fatal(err)
|
||||
}
|
||||
fileListText, err := mpq.ReadFile("(listfile)")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
log.Fatal(err)
|
||||
}
|
||||
fileList := strings.Split(string(fileListText), "\r\n")
|
||||
for _, filePath := range fileList {
|
||||
@ -104,11 +121,12 @@ func (v *Engine) GetFile(fileName string) []byte {
|
||||
mpqFile := v.Files[strings.ToLower(fileName)]
|
||||
mpq, err := LoadMPQ(mpqFile)
|
||||
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 {
|
||||
panic(err)
|
||||
log.Fatal(err)
|
||||
}
|
||||
mpqStream := CreateMPQStream(mpq, blockTableEntry, fileName)
|
||||
result := make([]byte, blockTableEntry.UncompressedFileSize)
|
||||
@ -169,11 +187,23 @@ func (v *Engine) updateScene() {
|
||||
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
|
||||
func (v *Engine) Update() {
|
||||
v.updateScene()
|
||||
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()
|
||||
}
|
||||
@ -186,7 +216,7 @@ func (v *Engine) Draw(screen *ebiten.Image) {
|
||||
v.LoadingSprite.Draw(screen)
|
||||
} else {
|
||||
if v.CurrentScene == nil {
|
||||
panic("no scene loaded")
|
||||
log.Fatal("no scene loaded")
|
||||
}
|
||||
v.CurrentScene.Render(screen)
|
||||
}
|
||||
@ -210,3 +240,28 @@ func (v *Engine) GetFont(font, palette string) *MPQFont {
|
||||
v.fontCache[font+"_"+palette] = 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"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/JoshVarga/blast"
|
||||
"github.com/essial/OpenDiablo2/Compression"
|
||||
)
|
||||
|
||||
// MPQStream represents a stream of data in an MPQ archive
|
||||
@ -23,13 +25,14 @@ type MPQStream struct {
|
||||
}
|
||||
|
||||
// CreateMPQStream creates an MPQ stream
|
||||
func CreateMPQStream(mpq MPQ, blockTableEntry MPQBlockTableEntry, fileName string) MPQStream {
|
||||
result := MPQStream{
|
||||
func CreateMPQStream(mpq MPQ, blockTableEntry MPQBlockTableEntry, fileName string) *MPQStream {
|
||||
result := &MPQStream{
|
||||
MPQData: mpq,
|
||||
BlockTableEntry: blockTableEntry,
|
||||
CurrentBlockIndex: 0xFFFFFFFF,
|
||||
}
|
||||
result.EncryptionSeed = hashString(fileName, 3)
|
||||
fileSegs := strings.Split(fileName, `\`)
|
||||
result.EncryptionSeed = hashString(fileSegs[len(fileSegs)-1], 3)
|
||||
if result.BlockTableEntry.HasFlag(MpqFileFixKey) {
|
||||
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
|
||||
panic("sparse decompression + bzip2 decompression not supported")
|
||||
case 0x41:
|
||||
//sinput = MpqHuffman.Decompress(sinput);
|
||||
//return MpqWavCompression.Decompress(sinput, 1);
|
||||
panic("mpqhuffman decompression not supported")
|
||||
sinput := Compression.HuffmanDecompress(data[1:])
|
||||
sinput = Compression.WavDecompress(sinput, 1)
|
||||
tmp := make([]byte, len(sinput))
|
||||
copy(tmp, sinput)
|
||||
return tmp
|
||||
case 0x48:
|
||||
//byte[] result = PKDecompress(sinput, outputLength);
|
||||
//return MpqWavCompression.Decompress(new MemoryStream(result), 1);
|
||||
panic("pk + mpqwav decompression not supported")
|
||||
case 0x81:
|
||||
//sinput = MpqHuffman.Decompress(sinput);
|
||||
//return MpqWavCompression.Decompress(sinput, 2);
|
||||
panic("huff + mpqwav decompression not supported")
|
||||
sinput := Compression.HuffmanDecompress(data[1:])
|
||||
sinput = Compression.WavDecompress(sinput, 1)
|
||||
tmp := make([]byte, len(sinput))
|
||||
copy(tmp, sinput)
|
||||
return tmp
|
||||
case 0x88:
|
||||
//byte[] result = PKDecompress(sinput, outputLength);
|
||||
//return MpqWavCompression.Decompress(new MemoryStream(result), 2);
|
||||
|
@ -18,6 +18,7 @@ type MainMenu struct {
|
||||
CopyrightLabel *UILabel
|
||||
CopyrightLabel2 *UILabel
|
||||
ShowTrademarkScreen bool
|
||||
LeftButtonHeld bool
|
||||
}
|
||||
|
||||
func CreateMainMenu(engine *Engine) *MainMenu {
|
||||
@ -30,6 +31,7 @@ func CreateMainMenu(engine *Engine) *MainMenu {
|
||||
}
|
||||
|
||||
func (v *MainMenu) Load() {
|
||||
v.Engine.PlayBGM(ResourcePaths.BGMTitle)
|
||||
go func() {
|
||||
loadStep := 1.0 / 8.0
|
||||
v.Engine.LoadingProgress = 0
|
||||
@ -110,5 +112,11 @@ func (v *MainMenu) Render(screen *ebiten.Image) {
|
||||
}
|
||||
|
||||
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