package door import ( "bytes" "fmt" "strconv" "strings" ) // Not possible to do, without help from: // http://www.roysac.com/blog/2014/04/thedraw-fonts-file-tdf-specifications/ type BlockFont struct { Characters []int Data [][][]byte } /* func BytesHexed(output []byte) { fmt.Printf("BH: ") for _, ch := range output { fmt.Printf("%02x ", ch) } fmt.Println("") } func BytesArrayHexed(output *[][]byte) { for _, out := range *output { BytesHexed(out) } } */ // Make []strings all the same length func normalizeBlock(block *[][]byte) { var max int // StringO(block) for _, line := range *block { var l int = len(line) if max < l { max = l } } // fmt.Printf("max = %d\n", max) for idx := range *block { // l := len(line) for len((*block)[idx]) < max { //(*block)[idx] += byte(0x20) //append((*block)[idx], byte{0x20}) (*block)[idx] = append((*block)[idx], byte(0x20)) } } // StringO(block) // fmt.Println("== normalized ==") } // Get Character information from BlockFont return Normalize func (bf *BlockFont) GetCharacter(c int) [][]byte { var result [][]byte if c == 32 { // Space character result = append(result, []byte{0x20, 0x20}) return result } // 33-126 are the possible characters. if c >= 33 && c <= 126 { c -= 33 var idx int = bf.Characters[c] if idx == -1 { // This character is not supported by this font. return result } result = bf.Data[bf.Characters[c]] // fmt.Println("normalizeCharBlock") normalizeBlock(&result) // fmt.Println("normalizeChar done") return result } else { return result } } func expandBlock(block *[][]byte) { // l := len((*block)[0]) var l int = len((*block)[0]) var blank []byte = []byte{0x20} *block = append(*block, bytes.Repeat(blank, l)) } // Given text, translate to thedraw font. func (bf *BlockFont) Output(input string) ([]string, int) { var out [][]byte var max int for _, ch := range input { // fmt.Printf("%d %x\n", ch, ch) block := bf.GetCharacter(int(ch)) // fmt.Println("ok") var l int = len(block) max += l if l != 0 { if len(out) == 0 { // First time out = append(out, block...) /* for _, b := range block { out = append(out, b) } */ } else { if len(out) != 0 { for l > len(out) { // We need to expand the out // fmt.Println("expandBlock") expandBlock(&out) } } // fmt.Println("Append block to out") // Ok, we have something! for idx, b := range block { out[idx] = append(out[idx], byte(0x20)) out[idx] = append(out[idx], b...) /* for _, inner := range b { out[idx] = append(out[idx], inner) } */ // out[idx] += byte(0x20) + b // fmt.Printf("%s\n", CP437_to_Unicode(b)) } } } // fmt.Println("normalizeBlock") normalizeBlock(&out) // fmt.Println("normalizeBlock done") } var output []string for idx := range out { if Unicode { output = append(output, CP437_to_Unicode(string(out[idx]))) } else { output = append(output, string(out[idx])) } } return output, len(out[0]) } type ColorFont struct { Characters []int Data [][][]byte } func normalizeColor(block *[][]byte) int { var max int for _, line := range *block { l := len(line) if max < l { max = l } } for idx := range *block { // l := len(line) var blank []byte = []byte{0x20, 0x0f} for len((*block)[idx]) < max { (*block)[idx] = append((*block)[idx], blank...) /* for _, b := range blank { (*block)[idx] = append((*block)[idx], b) } */ // (*block)[idx] += strings.Repeat(" \x0f", max-l/2) } } return max } func (cf *ColorFont) GetCharacter(c int) ([][]byte, int) { var result [][]byte if c == 32 { var blank []byte = []byte{0x20, 0x0f, 0x20, 0x0f} result = append(result, blank) // " \x0f \x0f") return result, len(result[0]) / 2 } if c >= 33 && c <= 126 { c -= 33 var idx int = cf.Characters[c] if idx == -1 { return result, 0 } result = cf.Data[cf.Characters[c]] // fmt.Println("Character:") // BytesArrayHexed(&result) // fmt.Println("normalizing...") var max int = normalizeColor(&result) // BytesArrayHexed(&result) // StringHexO(&result) return result, max } else { return result, 0 } } func thedraw_to_ansi(c int) int { var trans []int = []int{0, 4, 2, 6, 1, 5, 3, 7} // 0, 1, 2, 3, 4, 5, 6, 7 return trans[c] } /* // Before color output was optimized func colorout(c int) string { bg := thedraw_to_ansi(c / 16) fg := c % 16 bold := (fg & 0x8) == 0x8 fg = thedraw_to_ansi(fg & 0x7) if bold { return fmt.Sprintf("\x1b[0;1;%d;%dm", fg+30, bg+40) } else { return fmt.Sprintf("\x1b[0;%d;%dm", fg+30, bg+40) } } */ type ColorParts struct { Background int Foreground int Bold bool } func ColorSplit(color int) ColorParts { return ColorParts{Background: thedraw_to_ansi(color / 16), Foreground: thedraw_to_ansi(color & 7), Bold: (color%16)&0x08 == 0x08} } func ColorOutput(previous int, color int) string { var prev ColorParts = ColorSplit(previous) var c ColorParts = ColorSplit(color) var codes []int8 if c.Bold { if !prev.Bold { codes = append(codes, 1) } } else { if prev.Bold { // bold was set previously, there's only one way to reset it codes = append(codes, 0) prev.Background = 0 prev.Foreground = 7 } } if prev.Background == 0 && prev.Foreground == 0 { // output everything codes = append(codes, int8(c.Foreground)+30) codes = append(codes, int8(c.Background)+40) } else { if c.Foreground != prev.Foreground { codes = append(codes, int8(c.Foreground)+30) } if c.Background != prev.Background { codes = append(codes, int8(c.Background)+40) } } if len(codes) == 0 { // Everything matched return "" } var text []string for _, code := range codes { text = append(text, strconv.Itoa(int(code))) } return "\x1b[" + strings.Join(text, ";") + "m" } func Colorize(input []byte) []byte { var result []byte // runes := []rune(input) var previous int // BytesHexed(input) for pos := 0; pos < len(input); pos += 2 { var ch byte = input[pos] var color int = int(input[pos+1]) // fmt.Printf("%d : CH %d / %x, Color %d / %x\n", pos, ch, ch, color, color) var colorstring string = ColorOutput(previous, color) result = append(result, []byte(colorstring)...) /* for _, c := range []byte(colorstring) { result = append(result, c) } */ // result = append(result, []byte(colorstring)) // result += []byte(ColorOutput(previous, color)) // result += ColorOutput(previous, color) + string(ch) result = append(result, ch) // result += string(ch) previous = color // result += colorout(color) + string(ch) } return result } /* func __expandColor(block *[]string, need int) { *block = append(*block, strings.Repeat(" \x0f", need)) } */ func expandColor(block *[][]byte, need int) { var blank []byte = []byte{0x20, 0x0f} // *block = append(*block, strings.Repeat(" \x0f", need)) *block = append(*block, bytes.Repeat(blank, need)) } func (bf *ColorFont) Output(input string) ([]string, int) { var out [][]byte var max int for _, ch := range input { // fmt.Printf("%d %x\n", ch, ch) var block [][]byte var blklen int block, blklen = bf.GetCharacter(int(ch)) if blklen == 0 { continue } var l int = len(block) max += blklen if l != 0 { if len(out) == 0 { // First time out = append(out, block...) /* for _, b := range block { out = append(out, b) } */ } else { if len(out) != 0 { for l > len(out) { // We need to expand the out expandColor(&out, len(out[0])/2) // ExpandColor(&out) } } for len(out) > len(block) { // We need to expand the block out expandColor(&block, len(block)/2) } // Normalizing the character blocks normalizeColor(&block) if len(out) != len(block) { panic(fmt.Sprintf("len(out) %d != len(block) %d", len(out), len(block))) } var blank []byte = []byte{0x20, 0x0f} // Ok, we have something! for idx, b := range block { /* out[idx] = append(out[idx], byte(0x20)) out[idx] = append(out[idx], byte(0x0f)) */ out[idx] = append(out[idx], blank...) out[idx] = append(out[idx], b...) /* for _, inner := range b { out[idx] = append(out[idx], inner) } */ //out[idx] += " \x0f" + b // fmt.Printf("%s\n", CP437_to_Unicode(b)) } } // NormalizeColor(&out) } } if len(out) == 0 { return []string{}, 0 } // StringHexO(&out) max = len(out[0]) / 2 for idx := range out { out[idx] = Colorize(out[idx]) } var output []string for idx := range out { if Unicode { output = append(output, CP437_to_Unicode(string(out[idx]))) } else { output = append(output, string(out[idx])) } } return output, max } // Given a color to look for, see if it is in the color byte. // 0x01 = Upper match // 0x02 = Lower match func MatchStyle(color byte, look byte) int { var match int = 0 if ((color >> 4) & 0x07) == look { // Top match |= 1 } if (color & 0x07) == look { // Bottom match |= 2 } return match } // Update a color byte with the new color information. // Style 0x01 = Upper, 0x02 = Lower, 0x03 = Both. func PatchColor(color byte, new_color byte, style int) byte { var c byte = color if style&1 == 1 { c = (c & 0x8f) | new_color<<4 } if style&2 == 2 { c = (c & 0xf8) | new_color } return c } type ColorMap map[[2]int][][2]int // Scan a ColorFont for a specific color. // This returns a map key of character Index + line Index // with an array of [2]int (Index, Style) to change. func (cf *ColorFont) Scan(find_color int) ColorMap { var Targets ColorMap = make(ColorMap, 0) // Scan the font looking for the given color FG/BG // Covert color code to TheDraw Color var actual byte = byte(thedraw_to_ansi(find_color)) for charIndex := range cf.Data { for lineIndex := range cf.Data[charIndex] { var found bool = false var patches [][2]int = make([][2]int, 0) for offset := 1; offset < len(cf.Data[charIndex][lineIndex]); offset += 2 { var color byte = cf.Data[charIndex][lineIndex][offset] var style int = MatchStyle(color, actual) if style != 0 { // log.Printf("color: %x actual %x style: %d\n", color, actual, style) patches = append(patches, [2]int{offset, style}) found = true } } if found { var pos [2]int = [2]int{charIndex, lineIndex} Targets[pos] = make([][2]int, len(patches)) copy(Targets[pos], patches) /* for i := range patches { Targets[pos][i] = patches[i] } */ // Targets[pos] = patches } } } return Targets } // This modifies the font color, using the Targets found with // Scan. func (cf *ColorFont) Modify(new_color int, Targets ColorMap) { // Covert color code to TheDraw Color var actual byte = byte(thedraw_to_ansi(new_color)) for pos, patch := range Targets { for _, p := range patch { cf.Data[pos[0]][pos[1]][p[0]] = PatchColor(cf.Data[pos[0]][pos[1]][p[0]], actual, p[1]) } } }