123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536 |
- package door
- import (
- "bytes"
- "log"
- "math/rand"
- "time"
- "unicode"
- )
- /*
- No More Secrets - from "Sneakers"
- https://github.com/bartobri/no-more-secrets
- */
- const NORANDOM bool = false
- const DEBUG_NMS bool = true
- type NoMoreSecretsConfig struct {
- Jumble_Sec int // in sec
- Jumble_Loop_Speed int // in ms
- Reveal_Loop_Speed int // in ms
- Max_Time int // Max value before reveal (per character)
- Color []byte // Color to use before reveal
- }
- // The default configuration for NoMoreSecrets
- var NoMoreSecretsDefault NoMoreSecretsConfig
- // Initialize the Defaults
- func init() {
- NoMoreSecretsDefault = NoMoreSecretsConfig{
- Jumble_Sec: 2,
- Jumble_Loop_Speed: 35,
- Reveal_Loop_Speed: 50,
- Max_Time: 5000,
- Color: ColorText("CYAN ON BLACK"),
- }
- }
- // Get random character code for jumble
- // We use chr 0x21 - 0xdf (excluding 0x7f)
- func getRandom() byte {
- // 0x7f = backspace / rubout
- var rb byte = 0x7f
- for rb == 0x7f {
- rb = byte(rand.Intn(0xdf-0x21) + 0x21)
- }
- return rb
- }
- var rb byte
- func nonRandom() byte {
- if rb == 0 {
- rb = 0x21
- return rb
- }
- rb++
- if rb == 0x7f {
- rb++
- }
- if rb >= 0xdf {
- rb = 0x21
- }
- return rb
- }
- // NoMoreSecrets Sleep
- //
- // This allows the user to abort the effect with a key or left mouse click.
- func NMSSleep(Door *Door, sleep time.Duration) bool {
- _, ex, err := Door.WaitKey(sleep)
- if err == nil {
- if ex == MOUSE {
- m, ok := Door.GetMouse()
- if ok {
- if m.Button == 1 {
- log.Println("NoMore")
- return true
- }
- }
- return false
- }
- log.Println("NoMore")
- return true
- }
- return false
- }
- /*
- NoMoreSecrets - render output as random, then fade in the original text.
- Example:
- func About_Example_NoMoreSecrets(d *door.Door) {
- W := 60
- center_x := (door.Width - W) / 2
- center_y := (door.Height - 16) / 2
- about := door.Panel{X: center_x,
- Y: center_y,
- Width: W,
- Style: door.SINGLE_DOUBLE,
- BorderColor: door.ColorText("BOLD YELLOW ON BLUE"),
- }
- about.Lines = append(about.Lines, door.Line{Text: fmt.Sprintf("%*s", -W, "About This Door"),
- DefaultColor: door.ColorText("BOLD CYAN ON BLUE")})
- about.Lines = append(about.Lines, about.Spacer())
- about.Lines = append(about.Lines, door.Line{Text: fmt.Sprintf("%*s", -W, "Test Door written in go, using go door.")})
- var copyright string = "(C) 2022 Bugz, Red Green Software"
- if door.Unicode {
- copyright = strings.Replace(copyright, "(C)", "\u00a9", -1)
- }
- about.Lines = append(about.Lines,
- door.Line{Text: fmt.Sprintf("%*s", -W, copyright),
- DefaultColor: door.ColorText("BOLD WHITE ON BLUE")})
- for _, text := range []string{"",
- "This door was written by Bugz.",
- "",
- "It is written in Go, understands CP437 and unicode, adapts",
- "to screen sizes, uses door32.sys, supports TheDraw Fonts,",
- "and runs on Linux and Windows."} {
- about.Lines = append(about.Lines, door.Line{Text: fmt.Sprintf("%*s", -W, text)})
- }
- better := door.NoMoreSecretsDefault
- better.Color = door.ColorText("BOLD CYAN ON BLUE")
- door.NoMoreSecrets(about.Output(), d, &better)
- }
- */
- func NoMoreSecrets(output []byte, Door *Door, config *NoMoreSecretsConfig) {
- // Find ANSI codes, strip color (We'll handle color)
- var keyAbort bool
- rand.Seed(time.Now().UnixNano())
- if Unicode {
- var original []rune = []rune(string(output)) // original / master
- var work []rune = make([]rune, 0, len(original)) // work copy we modify
- var workpos int = 0 // where we are in the work copy
- var charpos []int = make([]int, 0, len(original)) // where characters are we can change
- var chartime []int = make([]int, 0, len(original)) // character time / reveal timeout
- var revealpos map[int]int // workpos to original position
- var colormap map[int][]byte // index to color end position (workpos)
- var coloridx []int // index of positions (map isn't ordered)
- var lastcolor []int // LastColor tracking for keeping colors proper
- var currentANSI string // ANSI Escape code we've extracted
- var ANSIchar rune // Last character of the ANSI Escape code
- colormap = make(map[int][]byte)
- revealpos = make(map[int]int)
- coloridx = make([]int, 0)
- // default color = "reset"
- lastcolor = make([]int, 1)
- lastcolor[0] = 0
- // Not using range, I want to be able to look ahead and modify
- // x.
- for x := 0; x < len(original); x++ {
- var char rune = original[x]
- if char == '\x1b' {
- // ANSI Code
- currentANSI = "\x1b["
- if original[x+1] != '[' {
- log.Println("NoMoreSecrets: Found \\x1b not followed by [!")
- }
- x += 2
- for {
- currentANSI += string(original[x])
- if unicode.IsLetter(original[x]) {
- ANSIchar = original[x]
- break
- }
- x++
- }
- // log.Printf("curentANSI: END @ %d [%#v]\n", x, currentANSI[1:])
- // Is this a color code?
- if ANSIchar == 'm' {
- // Yes, don't store in work. Process code.
- Door.UpdateLastColor(currentANSI, &lastcolor)
- colormap[workpos] = Color(lastcolor)
- coloridx = append(coloridx, workpos)
- // log.Printf("Added %d with %s\n", workpos, colormap[workpos][1:])
- } else {
- // Not a color code. Add to work.
- var ANSIrunes []rune = []rune(currentANSI)
- work = append(work, ANSIrunes...)
- workpos += len(ANSIrunes)
- }
- // currentANSI = ""
- } else {
- // Not escape, so what is it?
- if unicode.IsPrint(char) {
- if char == ' ' {
- work = append(work, char)
- chartime = append(chartime, 0)
- } else {
- work = append(work, char)
- chartime = append(chartime, rand.Intn(config.Max_Time+1))
- }
- charpos = append(charpos, workpos)
- revealpos[workpos] = x
- workpos++
- } else {
- // control code, CR NL.
- work = append(work, char)
- workpos++
- }
- }
- }
- // jumble loop
- var renderResults *bytes.Buffer = &bytes.Buffer{}
- var renderF func() []byte = func() []byte {
- renderResults.Reset()
- var lastcolor *[]byte
- var pos int = 0
- for idx, char := range work {
- var found bool
- _, found = revealpos[idx]
- if found {
- for charpos[pos] != idx {
- pos++
- }
- // This is a character
- if chartime[pos] != 0 && char != ' ' {
- if lastcolor != &config.Color {
- renderResults.Write(config.Color)
- lastcolor = &config.Color
- }
- } else {
- // look up the color in the colormap
- var best []byte
- // use the coloridx, lookup in colormap
- for _, cpos := range coloridx {
- if cpos > idx {
- break
- }
- best = colormap[cpos]
- }
- if lastcolor != &best {
- renderResults.Write(best)
- lastcolor = &best
- }
- }
- }
- renderResults.WriteRune(char)
- }
- return renderResults.Bytes()
- }
- for i := 0; (i < (config.Jumble_Sec*1000)/config.Jumble_Loop_Speed) && (!keyAbort); i++ {
- for _, pos := range charpos {
- if work[pos] != ' ' {
- // Safe way to handle bytes to unicode
- var rb byte
- if NORANDOM {
- rb = nonRandom()
- } else {
- rb = getRandom()
- }
- var safe []byte = []byte{rb}
- var rndchar string = CP437_to_Unicode(string(safe))
- work[pos] = []rune(rndchar)[0]
- }
- }
- Door.Write(renderF())
- if Door.Disconnect() {
- return
- }
- // time.Sleep()
- keyAbort = NMSSleep(Door, time.Millisecond*time.Duration(config.Jumble_Loop_Speed))
- }
- for {
- var revealed bool = true
- for idx, pos := range charpos {
- if work[pos] != ' ' {
- if !keyAbort && (chartime[idx] > 0) {
- if chartime[idx] < 500 {
- if rand.Intn(3) == 0 {
- var safe [1]byte
- if NORANDOM {
- safe[0] = nonRandom()
- } else {
- safe[0] = getRandom()
- }
- var rndchar string = CP437_to_Unicode(string(safe[:]))
- work[pos] = []rune(rndchar)[0]
- }
- } else {
- if rand.Intn(10) == 0 {
- var safe [1]byte
- if NORANDOM {
- safe[0] = nonRandom()
- } else {
- safe[0] = getRandom()
- }
- var rndchar string = CP437_to_Unicode(string(safe[:]))
- work[pos] = []rune(rndchar)[0]
- }
- }
- if chartime[idx] < config.Reveal_Loop_Speed {
- chartime[idx] = 0
- } else {
- chartime[idx] -= config.Reveal_Loop_Speed
- }
- revealed = false
- } else {
- chartime[idx] = 0
- work[pos] = original[revealpos[pos]]
- }
- }
- }
- Door.Write(renderF())
- if Door.Disconnect() {
- return
- }
- // time.Sleep()
- keyAbort = NMSSleep(Door, time.Millisecond*time.Duration(config.Reveal_Loop_Speed))
- if revealed {
- break
- }
- }
- } else {
- // CP437
- var original []byte = []byte(output) // original / master
- var work []byte // work copy we modify
- var workpos int = 0 // where are we in the work copy
- var charpos []int // where characters are we can change
- var chartime []int // character time / reveal timeout
- var revealpos map[int]int // workpos to original position
- var colormap map[int][]byte // index to color end position (workpos)
- var coloridx []int // index of positions (map isn't ordered)
- var lastcolor []int // LastColor tracking for keeping color proper
- var currentANSI string // ANSI Escape code we've extracted
- var ANSIchar byte // Last character of the ANSI Escape code
- work = make([]byte, 0)
- colormap = make(map[int][]byte)
- revealpos = make(map[int]int)
- coloridx = make([]int, 0)
- // default color = "reset"
- lastcolor = make([]int, 1)
- lastcolor[0] = 0
- // Not using range, I want to be able to look ahead and modify
- // x.
- for x := 0; x < len(original); x++ {
- var char byte = original[x]
- if char == '\x1b' {
- // ANSI Code
- currentANSI = "\x1b["
- if original[x+1] != '[' {
- log.Println("NoMoreSecrets: Found \\x1b not followed by [!")
- }
- x += 2
- for {
- currentANSI += string(original[x])
- if unicode.IsLetter(rune(original[x])) {
- ANSIchar = original[x]
- break
- }
- x++
- }
- // Is this a color code?
- if ANSIchar == 'm' {
- // Yes, don't store in work. Process code.
- Door.UpdateLastColor(currentANSI, &lastcolor)
- colormap[workpos] = Color(lastcolor)
- coloridx = append(coloridx, workpos)
- } else {
- // Not a color code. Add to work.
- work = append(work, []byte(currentANSI)...)
- workpos += len(currentANSI)
- }
- // currentANSI = ""
- } else {
- // Not escape, so what is it?
- if unicode.IsPrint(rune(char)) {
- if char == ' ' {
- work = append(work, char)
- chartime = append(chartime, 0)
- } else {
- work = append(work, char)
- chartime = append(chartime, rand.Intn(config.Max_Time+1))
- }
- charpos = append(charpos, workpos)
- revealpos[workpos] = x
- workpos++
- } else {
- // control code, CR NL.
- work = append(work, char)
- workpos++
- }
- }
- }
- var renderResults *bytes.Buffer = &bytes.Buffer{}
- // jumble loop
- var renderF func() []byte = func() []byte {
- renderResults.Reset()
- var lastcolor *[]byte
- var pos int = 0
- for idx, char := range work {
- _, found := revealpos[idx]
- if found {
- for charpos[pos] != idx {
- pos++
- }
- // This is a character
- if chartime[pos] != 0 && char != ' ' {
- if lastcolor != &config.Color {
- renderResults.Write(config.Color)
- lastcolor = &config.Color
- }
- } else {
- // look up the color in the colormap
- var best []byte
- for _, cpos := range coloridx {
- if cpos > idx {
- break
- }
- best = colormap[cpos]
- }
- if lastcolor != &best {
- renderResults.Write(best)
- lastcolor = &best
- }
- }
- }
- renderResults.WriteByte(char)
- }
- return renderResults.Bytes()
- }
- for i := 0; (i < (config.Jumble_Sec*1000)/config.Jumble_Loop_Speed) && (!keyAbort); i++ {
- for _, pos := range charpos {
- if work[pos] != ' ' {
- if NORANDOM {
- work[pos] = nonRandom()
- } else {
- work[pos] = getRandom()
- }
- }
- }
- Door.Write(renderF())
- if Door.Disconnect() {
- return
- }
- // time.Sleep()
- keyAbort = NMSSleep(Door, time.Millisecond*time.Duration(config.Jumble_Loop_Speed))
- }
- for {
- var revealed bool = true
- for idx, pos := range charpos {
- if work[pos] != ' ' {
- if !keyAbort && (chartime[idx] > 0) {
- if chartime[idx] < 500 {
- if rand.Intn(3) == 0 {
- if NORANDOM {
- work[pos] = nonRandom()
- } else {
- work[pos] = getRandom()
- }
- }
- } else {
- if rand.Intn(10) == 0 {
- if NORANDOM {
- work[pos] = nonRandom()
- } else {
- work[pos] = getRandom()
- }
- }
- }
- if chartime[idx] < config.Reveal_Loop_Speed {
- chartime[idx] = 0
- } else {
- chartime[idx] -= config.Reveal_Loop_Speed
- }
- revealed = false
- } else {
- chartime[idx] = 0
- work[pos] = original[revealpos[pos]]
- }
- }
- }
- Door.Write(renderF())
- if Door.Disconnect() {
- return
- }
- // time.Sleep()
- keyAbort = NMSSleep(Door, time.Millisecond*time.Duration(config.Reveal_Loop_Speed))
- if revealed {
- break
- }
- }
- }
- }
|