From 721a67b4047bf763b2b068987f0bbbd0e4ecd606 Mon Sep 17 00:00:00 2001 From: "M. Sz" Date: Mon, 8 Feb 2021 13:21:50 +0100 Subject: [PATCH 1/5] font table interpreter: moved d stuff responsible for font table into d2fileformats/d2font --- .../d2fileformats/d2font}/font.go | 23 +++++++++++++++---- d2core/d2asset/asset_manager.go | 19 +++++---------- d2core/d2gui/label.go | 6 ++--- d2core/d2gui/layout.go | 3 ++- d2core/d2ui/label.go | 4 ++-- 5 files changed, 32 insertions(+), 23 deletions(-) rename {d2core/d2asset => d2common/d2fileformats/d2font}/font.go (87%) diff --git a/d2core/d2asset/font.go b/d2common/d2fileformats/d2font/font.go similarity index 87% rename from d2core/d2asset/font.go rename to d2common/d2fileformats/d2font/font.go index 5d898272..3c8af7f1 100644 --- a/d2core/d2asset/font.go +++ b/d2common/d2fileformats/d2font/font.go @@ -1,7 +1,8 @@ -package d2asset +package d2font import ( "encoding/binary" + "fmt" "image/color" "strings" @@ -23,6 +24,23 @@ type Font struct { color color.Color } +// Load loads a new font from byte slice +func Load(data []byte, sheet d2interface.Animation) (*Font, error) { + if string(data[:5]) != "Woo!\x01" { + return nil, fmt.Errorf("invalid font table format") + } + + font := &Font{ + table: data, + sheet: sheet, + color: color.White, + } + + font.initGlyphs() + + return font, nil +} + // SetColor sets the fonts color func (f *Font) SetColor(c color.Color) { f.color = c @@ -30,9 +48,6 @@ func (f *Font) SetColor(c color.Color) { // GetTextMetrics returns the dimensions of the Font element in pixels func (f *Font) GetTextMetrics(text string) (width, height int) { - if f.glyphs == nil { - f.initGlyphs() - } var ( lineWidth int diff --git a/d2core/d2asset/asset_manager.go b/d2core/d2asset/asset_manager.go index 59444de8..e6b72db3 100644 --- a/d2core/d2asset/asset_manager.go +++ b/d2core/d2asset/asset_manager.go @@ -2,7 +2,6 @@ package d2asset import ( "fmt" - "image/color" "io" "io/ioutil" "path/filepath" @@ -19,6 +18,7 @@ import ( "github.com/OpenDiablo2/OpenDiablo2/d2core/d2records" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2font" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2txt" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" @@ -209,11 +209,11 @@ func (am *AssetManager) LoadComposite(baseType d2enum.ObjectType, token, palette } // LoadFont loads a font the resource files -func (am *AssetManager) LoadFont(tablePath, spritePath, palettePath string) (*Font, error) { +func (am *AssetManager) LoadFont(tablePath, spritePath, palettePath string) (*d2font.Font, error) { cachePath := fmt.Sprintf("%s;%s;%s", tablePath, spritePath, palettePath) if cached, found := am.fonts.Retrieve(cachePath); found { - return cached.(*Font), nil + return cached.(*d2font.Font), nil } sheet, err := am.LoadAnimation(spritePath, palettePath) @@ -226,16 +226,9 @@ func (am *AssetManager) LoadFont(tablePath, spritePath, palettePath string) (*Fo return nil, err } - if string(tableData[:5]) != "Woo!\x01" { - return nil, fmt.Errorf("invalid font table format: %s", tablePath) - } - - am.Debugf(fmtLoadFont, tablePath, spritePath, palettePath) - - font := &Font{ - table: tableData, - sheet: sheet, - color: color.White, + font, err := d2font.Load(tableData, sheet) + if err != nil { + return nil, fmt.Errorf("error while loading font table %s: %v", tablePath, err) } err = am.fonts.Insert(cachePath, font, defaultCacheEntryWeight) diff --git a/d2core/d2gui/label.go b/d2core/d2gui/label.go index 03427f79..3130d5d1 100644 --- a/d2core/d2gui/label.go +++ b/d2core/d2gui/label.go @@ -4,8 +4,8 @@ import ( "image/color" "time" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2font" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" ) // Constants defining the main shades of basic colors @@ -25,7 +25,7 @@ type Label struct { renderer d2interface.Renderer text string - font *d2asset.Font + font *d2font.Font surface d2interface.Surface color color.RGBA hoverColor color.RGBA @@ -35,7 +35,7 @@ type Label struct { blinkTimer time.Time } -func createLabel(renderer d2interface.Renderer, text string, font *d2asset.Font, col color.RGBA) (*Label, error) { +func createLabel(renderer d2interface.Renderer, text string, font *d2font.Font, col color.RGBA) (*Label, error) { label := &Label{ font: font, renderer: renderer, diff --git a/d2core/d2gui/layout.go b/d2core/d2gui/layout.go index b6c487b5..25ad1af0 100644 --- a/d2core/d2gui/layout.go +++ b/d2core/d2gui/layout.go @@ -4,6 +4,7 @@ import ( "errors" "image/color" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2font" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2math" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2util" @@ -532,7 +533,7 @@ func (l *Layout) createButton(renderer d2interface.Renderer, text string, return button, nil } -func (l *Layout) loadFont(fontStyle FontStyle) (*d2asset.Font, error) { +func (l *Layout) loadFont(fontStyle FontStyle) (*d2font.Font, error) { config := getFontStyleConfig(fontStyle) if config == nil { return nil, errors.New("invalid font style") diff --git a/d2core/d2ui/label.go b/d2core/d2ui/label.go index 9026697a..912b0788 100644 --- a/d2core/d2ui/label.go +++ b/d2core/d2ui/label.go @@ -5,7 +5,7 @@ import ( "regexp" "strings" - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2font" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2util" @@ -19,7 +19,7 @@ type Label struct { *BaseWidget text string Alignment HorizontalAlign - font *d2asset.Font + font *d2font.Font Color map[int]color.Color backgroundColor color.Color From 6df66b51c1b09a943a28c845abafecc6995d1d4e Mon Sep 17 00:00:00 2001 From: "M. Sz" Date: Mon, 8 Feb 2021 14:11:51 +0100 Subject: [PATCH 2/5] d2font: rewritten initGlyphs ethod to use stream reader --- d2common/d2fileformats/d2font/font.go | 63 ++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 6 deletions(-) diff --git a/d2common/d2fileformats/d2font/font.go b/d2common/d2fileformats/d2font/font.go index 3c8af7f1..c421db75 100644 --- a/d2common/d2fileformats/d2font/font.go +++ b/d2common/d2fileformats/d2font/font.go @@ -1,11 +1,14 @@ package d2font import ( - "encoding/binary" + //"os" + + //"encoding/binary" "fmt" "image/color" "strings" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2datautils" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2math" ) @@ -38,6 +41,19 @@ func Load(data []byte, sheet d2interface.Animation) (*Font, error) { font.initGlyphs() + ok := true + dw := font.Marshal() + for i := range dw { + if dw[i] != data[i] { + ok = false + } + } + + _ = ok + //fmt.Println(ok) + //fmt.Println(len(data) == len(dw)) + //os.Exit(0) + return font, nil } @@ -116,21 +132,56 @@ func (f *Font) RenderText(text string, target d2interface.Surface) error { return nil } -func (f *Font) initGlyphs() { +func (f *Font) initGlyphs() error { + sr := d2datautils.CreateStreamReader(f.table) + sr.SkipBytes(12) + _, maxCharHeight := f.sheet.GetFrameBounds() glyphs := make(map[rune]fontGlyph) for i := 12; i < len(f.table); i += 14 { - code := rune(binary.LittleEndian.Uint16(f.table[i : i+2])) + sr.SetPosition(uint64(i)) + + code, err := sr.ReadUInt16() + if err != nil { + return err + } var glyph fontGlyph - glyph.frame = int(binary.LittleEndian.Uint16(f.table[i+8 : i+10])) - glyph.width = int(f.table[i+3]) + + sr.SkipBytes(1) + + width, err := sr.ReadByte() + if err != nil { + return err + } + + glyph.width = int(width) + glyph.height = maxCharHeight - glyphs[code] = glyph + sr.SkipBytes(4) + + frame, err := sr.ReadUInt16() + if err != nil { + return err + } + + glyph.frame = int(frame) + + glyphs[rune(code)] = glyph } f.glyphs = glyphs + + return nil +} + +func (f *Font) Marshal() []byte { + sw := d2datautils.CreateStreamWriter() + + sw.PushBytes([]byte("Woo!\x01")...) + + return sw.GetBytes() } From 662d4489c45cfdf6a97d25e502fdd88f08b21a7d Mon Sep 17 00:00:00 2001 From: "M. Sz" Date: Mon, 8 Feb 2021 15:03:59 +0100 Subject: [PATCH 3/5] d2font: encoder --- d2common/d2fileformats/d2font/font.go | 87 ++++++++++++++++----------- 1 file changed, 51 insertions(+), 36 deletions(-) diff --git a/d2common/d2fileformats/d2font/font.go b/d2common/d2fileformats/d2font/font.go index c421db75..44e4d812 100644 --- a/d2common/d2fileformats/d2font/font.go +++ b/d2common/d2fileformats/d2font/font.go @@ -1,9 +1,6 @@ package d2font import ( - //"os" - - //"encoding/binary" "fmt" "image/color" "strings" @@ -13,23 +10,38 @@ import ( "github.com/OpenDiablo2/OpenDiablo2/d2common/d2math" ) +const ( + knownSignature = "Woo!\x01" +) + type fontGlyph struct { - frame int - width int - height int + unknown1 []byte + unknown2 []byte + unknown3 []byte + frame int + width int + height int } // Font represents a displayable font type Font struct { - sheet d2interface.Animation - table []byte - glyphs map[rune]fontGlyph - color color.Color + unknownHeaderBytes []byte + sheet d2interface.Animation + table []byte + glyphs map[rune]fontGlyph + color color.Color } // Load loads a new font from byte slice func Load(data []byte, sheet d2interface.Animation) (*Font, error) { - if string(data[:5]) != "Woo!\x01" { + sr := d2datautils.CreateStreamReader(data) + + signature, err := sr.ReadBytes(5) + if err != nil { + return nil, err + } + + if string(signature) != knownSignature { return nil, fmt.Errorf("invalid font table format") } @@ -39,20 +51,12 @@ func Load(data []byte, sheet d2interface.Animation) (*Font, error) { color: color.White, } - font.initGlyphs() - - ok := true - dw := font.Marshal() - for i := range dw { - if dw[i] != data[i] { - ok = false - } + font.unknownHeaderBytes, err = sr.ReadBytes(7) + if err != nil { + return nil, err } - _ = ok - //fmt.Println(ok) - //fmt.Println(len(data) == len(dw)) - //os.Exit(0) + font.initGlyphs(sr) return font, nil } @@ -90,11 +94,6 @@ func (f *Font) GetTextMetrics(text string) (width, height int) { // RenderText prints a text using its configured style on a Surface (multi-lines are left-aligned, use label otherwise) func (f *Font) RenderText(text string, target d2interface.Surface) error { - if f.glyphs == nil { - f.sheet.BindRenderer(target.Renderer()) - f.initGlyphs() - } - f.sheet.SetColorMod(f.color) lines := strings.Split(text, "\n") @@ -132,17 +131,12 @@ func (f *Font) RenderText(text string, target d2interface.Surface) error { return nil } -func (f *Font) initGlyphs() error { - sr := d2datautils.CreateStreamReader(f.table) - sr.SkipBytes(12) - +func (f *Font) initGlyphs(sr *d2datautils.StreamReader) error { _, maxCharHeight := f.sheet.GetFrameBounds() glyphs := make(map[rune]fontGlyph) for i := 12; i < len(f.table); i += 14 { - sr.SetPosition(uint64(i)) - code, err := sr.ReadUInt16() if err != nil { return err @@ -150,7 +144,10 @@ func (f *Font) initGlyphs() error { var glyph fontGlyph - sr.SkipBytes(1) + glyph.unknown1, err = sr.ReadBytes(1) + if err != nil { + return err + } width, err := sr.ReadByte() if err != nil { @@ -161,7 +158,10 @@ func (f *Font) initGlyphs() error { glyph.height = maxCharHeight - sr.SkipBytes(4) + glyph.unknown2, err = sr.ReadBytes(4) + if err != nil { + return err + } frame, err := sr.ReadUInt16() if err != nil { @@ -170,6 +170,11 @@ func (f *Font) initGlyphs() error { glyph.frame = int(frame) + glyph.unknown3, err = sr.ReadBytes(4) + if err != nil { + return err + } + glyphs[rune(code)] = glyph } @@ -182,6 +187,16 @@ func (f *Font) Marshal() []byte { sw := d2datautils.CreateStreamWriter() sw.PushBytes([]byte("Woo!\x01")...) + sw.PushBytes(f.unknownHeaderBytes...) + + for c, i := range f.glyphs { + sw.PushUint16(uint16(c)) + sw.PushBytes(i.unknown1...) + sw.PushBytes(byte(i.width)) + sw.PushBytes(i.unknown2...) + sw.PushUint16(uint16(i.frame)) + sw.PushBytes(i.unknown3...) + } return sw.GetBytes() } From e2ec1c6613ebaba7fb90385ac8c0fd2b0a998208 Mon Sep 17 00:00:00 2001 From: "M. Sz" Date: Mon, 8 Feb 2021 17:25:02 +0100 Subject: [PATCH 4/5] d2font: fixed lint errors --- d2common/d2fileformats/d2font/doc.go | 3 +++ d2common/d2fileformats/d2font/font.go | 25 ++++++++++++++++++------- d2core/d2asset/asset_manager.go | 2 ++ 3 files changed, 23 insertions(+), 7 deletions(-) create mode 100644 d2common/d2fileformats/d2font/doc.go diff --git a/d2common/d2fileformats/d2font/doc.go b/d2common/d2fileformats/d2font/doc.go new file mode 100644 index 00000000..28f60bf9 --- /dev/null +++ b/d2common/d2fileformats/d2font/doc.go @@ -0,0 +1,3 @@ +// Package d2font contains logic for loading and processing +// d2 fonts +package d2font diff --git a/d2common/d2fileformats/d2font/font.go b/d2common/d2fileformats/d2font/font.go index 44e4d812..7dfeb0ca 100644 --- a/d2common/d2fileformats/d2font/font.go +++ b/d2common/d2fileformats/d2font/font.go @@ -14,6 +14,14 @@ const ( knownSignature = "Woo!\x01" ) +const ( + signatureBytesCount = 5 + unknownHeaderBytesCount = 7 + unknown1BytesCount = 1 + unknown2BytesCount = 4 + unknown3BytesCount = 4 +) + type fontGlyph struct { unknown1 []byte unknown2 []byte @@ -36,7 +44,7 @@ type Font struct { func Load(data []byte, sheet d2interface.Animation) (*Font, error) { sr := d2datautils.CreateStreamReader(data) - signature, err := sr.ReadBytes(5) + signature, err := sr.ReadBytes(signatureBytesCount) if err != nil { return nil, err } @@ -51,12 +59,15 @@ func Load(data []byte, sheet d2interface.Animation) (*Font, error) { color: color.White, } - font.unknownHeaderBytes, err = sr.ReadBytes(7) + font.unknownHeaderBytes, err = sr.ReadBytes(unknownHeaderBytesCount) if err != nil { return nil, err } - font.initGlyphs(sr) + err = font.initGlyphs(sr) + if err != nil { + return nil, err + } return font, nil } @@ -68,7 +79,6 @@ func (f *Font) SetColor(c color.Color) { // GetTextMetrics returns the dimensions of the Font element in pixels func (f *Font) GetTextMetrics(text string) (width, height int) { - var ( lineWidth int lineHeight int @@ -144,7 +154,7 @@ func (f *Font) initGlyphs(sr *d2datautils.StreamReader) error { var glyph fontGlyph - glyph.unknown1, err = sr.ReadBytes(1) + glyph.unknown1, err = sr.ReadBytes(unknown1BytesCount) if err != nil { return err } @@ -158,7 +168,7 @@ func (f *Font) initGlyphs(sr *d2datautils.StreamReader) error { glyph.height = maxCharHeight - glyph.unknown2, err = sr.ReadBytes(4) + glyph.unknown2, err = sr.ReadBytes(unknown2BytesCount) if err != nil { return err } @@ -170,7 +180,7 @@ func (f *Font) initGlyphs(sr *d2datautils.StreamReader) error { glyph.frame = int(frame) - glyph.unknown3, err = sr.ReadBytes(4) + glyph.unknown3, err = sr.ReadBytes(unknown3BytesCount) if err != nil { return err } @@ -183,6 +193,7 @@ func (f *Font) initGlyphs(sr *d2datautils.StreamReader) error { return nil } +// Marshal encodes font back into byte slice func (f *Font) Marshal() []byte { sw := d2datautils.CreateStreamWriter() diff --git a/d2core/d2asset/asset_manager.go b/d2core/d2asset/asset_manager.go index e6b72db3..5a93f832 100644 --- a/d2core/d2asset/asset_manager.go +++ b/d2core/d2asset/asset_manager.go @@ -226,6 +226,8 @@ func (am *AssetManager) LoadFont(tablePath, spritePath, palettePath string) (*d2 return nil, err } + am.Debugf(fmtLoadFont, tablePath, spritePath, palettePath) + font, err := d2font.Load(tableData, sheet) if err != nil { return nil, fmt.Errorf("error while loading font table %s: %v", tablePath, err) From d9cfe7f435d2285d29120600fdb257a8c28580d2 Mon Sep 17 00:00:00 2001 From: "M. Sz" Date: Tue, 9 Feb 2021 08:43:46 +0100 Subject: [PATCH 5/5] d2font: removed d2interface.Animation argument from d2font.Load; added height reading in glyphs loader --- d2common/d2fileformats/d2font/font.go | 40 +++++++++++++++++++++------ d2core/d2asset/asset_manager.go | 4 ++- 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/d2common/d2fileformats/d2font/font.go b/d2common/d2fileformats/d2font/font.go index 7dfeb0ca..7789e277 100644 --- a/d2common/d2fileformats/d2font/font.go +++ b/d2common/d2fileformats/d2font/font.go @@ -18,7 +18,7 @@ const ( signatureBytesCount = 5 unknownHeaderBytesCount = 7 unknown1BytesCount = 1 - unknown2BytesCount = 4 + unknown2BytesCount = 3 unknown3BytesCount = 4 ) @@ -31,17 +31,21 @@ type fontGlyph struct { height int } +func (fg *fontGlyph) setHeight(h int) { + fg.height = h +} + // Font represents a displayable font type Font struct { unknownHeaderBytes []byte sheet d2interface.Animation table []byte - glyphs map[rune]fontGlyph + glyphs map[rune]*fontGlyph color color.Color } // Load loads a new font from byte slice -func Load(data []byte, sheet d2interface.Animation) (*Font, error) { +func Load(data []byte) (*Font, error) { sr := d2datautils.CreateStreamReader(data) signature, err := sr.ReadBytes(signatureBytesCount) @@ -55,7 +59,6 @@ func Load(data []byte, sheet d2interface.Animation) (*Font, error) { font := &Font{ table: data, - sheet: sheet, color: color.White, } @@ -72,6 +75,18 @@ func Load(data []byte, sheet d2interface.Animation) (*Font, error) { return font, nil } +// SetBackground sets font's background +func (f *Font) SetBackground(sheet d2interface.Animation) { + f.sheet = sheet + + // recalculate max height + _, h := f.sheet.GetFrameBounds() + + for i := range f.glyphs { + f.glyphs[i].setHeight(h) + } +} + // SetColor sets the fonts color func (f *Font) SetColor(c color.Color) { f.color = c @@ -142,9 +157,7 @@ func (f *Font) RenderText(text string, target d2interface.Surface) error { } func (f *Font) initGlyphs(sr *d2datautils.StreamReader) error { - _, maxCharHeight := f.sheet.GetFrameBounds() - - glyphs := make(map[rune]fontGlyph) + glyphs := make(map[rune]*fontGlyph) for i := 12; i < len(f.table); i += 14 { code, err := sr.ReadUInt16() @@ -154,6 +167,7 @@ func (f *Font) initGlyphs(sr *d2datautils.StreamReader) error { var glyph fontGlyph + // two bytes of 0 glyph.unknown1, err = sr.ReadBytes(unknown1BytesCount) if err != nil { return err @@ -166,8 +180,14 @@ func (f *Font) initGlyphs(sr *d2datautils.StreamReader) error { glyph.width = int(width) - glyph.height = maxCharHeight + height, err := sr.ReadByte() + if err != nil { + return err + } + glyph.height = int(height) + + // 1, 0, 0 glyph.unknown2, err = sr.ReadBytes(unknown2BytesCount) if err != nil { return err @@ -180,12 +200,13 @@ func (f *Font) initGlyphs(sr *d2datautils.StreamReader) error { glyph.frame = int(frame) + // 1, 0, 0, character code repeated, and further 0. glyph.unknown3, err = sr.ReadBytes(unknown3BytesCount) if err != nil { return err } - glyphs[rune(code)] = glyph + glyphs[rune(code)] = &glyph } f.glyphs = glyphs @@ -204,6 +225,7 @@ func (f *Font) Marshal() []byte { sw.PushUint16(uint16(c)) sw.PushBytes(i.unknown1...) sw.PushBytes(byte(i.width)) + sw.PushBytes(byte(i.height)) sw.PushBytes(i.unknown2...) sw.PushUint16(uint16(i.frame)) sw.PushBytes(i.unknown3...) diff --git a/d2core/d2asset/asset_manager.go b/d2core/d2asset/asset_manager.go index c8327d7d..d3c8cb6f 100644 --- a/d2core/d2asset/asset_manager.go +++ b/d2core/d2asset/asset_manager.go @@ -228,11 +228,13 @@ func (am *AssetManager) LoadFont(tablePath, spritePath, palettePath string) (*d2 am.Debugf(fmtLoadFont, tablePath, spritePath, palettePath) - font, err := d2font.Load(tableData, sheet) + font, err := d2font.Load(tableData) if err != nil { return nil, fmt.Errorf("error while loading font table %s: %v", tablePath, err) } + font.SetBackground(sheet) + err = am.fonts.Insert(cachePath, font, defaultCacheEntryWeight) return font, err