package text import ( "fmt" "io/ioutil" "regexp" "strconv" "github.com/inkyblackness/hacked/ss1/resource" ) const ( metaExpressionInterrupt = "(t)" metaExpressionNextMessage = "(?:i([0-9a-fA-F]{2}))" metaExpressionColorIndex = "(?:c([0-9a-fA-F]{2}))" metaExpressionLeftDisplay = "([0-9]+)" metaExpressionRightDisplay = "(?:,[ ]*([0-9]+))" ) var metaExpression = regexp.MustCompile("^[ ]*(?:(?:" + metaExpressionInterrupt + "|" + metaExpressionNextMessage + "|" + metaExpressionColorIndex + "|" + metaExpressionLeftDisplay + "|" + metaExpressionRightDisplay + ")[ ]*)*$") type metaStringError struct { MetaString string } func (err metaStringError) Error() string { return fmt.Sprintf("failed to parse meta string: '%s'", err.MetaString) } type blockError struct { Action string Index int Nested error } func (err blockError) Error() string { return fmt.Sprintf("failed to %s block %d: %v", err.Action, err.Index, err.Nested) } func (err blockError) Unwrap() error { return err.Nested } // ElectronicMessage describes one message. type ElectronicMessage struct { // NextMessage identifies the message that should follow this one. -1 for none. NextMessage int // IsInterrupt marks messages that are the ones following another. IsInterrupt bool // ColorIndex is into the palette for the sender text. -1 for default. ColorIndex int // LeftDisplay indicates the image to show in the left MFD. -1 for none. LeftDisplay int // RightDisplay indicates the image to show in the right MFD. -1 for none. RightDisplay int // Title of the message. Title string // Sender of the message. Sender string // Subject of the message. Subject string // VerboseText the long form. VerboseText string // TerseText the short form. TerseText string } // EmptyElectronicMessage returns an instance of an empty electronic message. func EmptyElectronicMessage() ElectronicMessage { message := ElectronicMessage{ NextMessage: -1, ColorIndex: -1, LeftDisplay: -1, RightDisplay: -1, } return message } // DecodeElectronicMessage tries to decode a message from given block holder. func DecodeElectronicMessage(cp Codepage, provider resource.BlockProvider) (message ElectronicMessage, err error) { blockIndex := 0 nextBlockString := func() (line string) { if err != nil { return } if blockIndex < provider.BlockCount() { blockReader, blockErr := provider.Block(blockIndex) if blockErr != nil { err = blockError{ Action: "access", Index: blockIndex, Nested: blockErr, } return } data, dataErr := ioutil.ReadAll(blockReader) if dataErr != nil { err = blockError{ Action: "read data from", Index: blockIndex, Nested: dataErr, } } line = cp.Decode(data) blockIndex++ } return } nextText := func() (text string) { for line := nextBlockString(); len(line) > 0; line = nextBlockString() { text += line } return } metaString := nextBlockString() metaData := metaExpression.FindStringSubmatch(metaString) parseInt := func(metaIndex int, base, bits int) (result int) { var value uint64 result = -1 if (err == nil) && (len(metaData[metaIndex]) > 0) { value, err = strconv.ParseUint(metaData[metaIndex], base, bits) if err == nil { result = int(value) } } return } message = EmptyElectronicMessage() if (len(metaData) == 0) || (len(metaData[0]) != len(metaString)) { err = metaStringError{MetaString: metaString} } if (err == nil) && (len(metaData[1]) > 0) { message.IsInterrupt = true } message.NextMessage = parseInt(2, 16, 16) message.ColorIndex = parseInt(3, 16, 8) message.LeftDisplay = parseInt(4, 10, 16) message.RightDisplay = parseInt(5, 10, 16) message.Title = nextBlockString() message.Sender = nextBlockString() message.Subject = nextBlockString() message.VerboseText = nextText() message.TerseText = nextText() return } // Encode serializes the message into a block holder. func (message ElectronicMessage) Encode(cp Codepage) [][]byte { var blocks [][]byte blocks = append(blocks, cp.Encode(message.metaString())) blocks = append(blocks, cp.Encode(message.Title)) blocks = append(blocks, cp.Encode(message.Sender)) blocks = append(blocks, cp.Encode(message.Subject)) for _, line := range Blocked(message.VerboseText) { blocks = append(blocks, cp.Encode(line)) } for _, line := range Blocked(message.TerseText) { blocks = append(blocks, cp.Encode(line)) } return blocks } func (message ElectronicMessage) metaString() string { result := "" add := func(sep, part string) { if len(result) > 0 { result += sep } result += part } if message.IsInterrupt { add("", "t") } if message.NextMessage >= 0 { add(" ", fmt.Sprintf("i%02X", message.NextMessage)) } if message.ColorIndex >= 0 { add(" ", fmt.Sprintf("c%02X", message.ColorIndex)) } if message.LeftDisplay >= 0 { add(" ", fmt.Sprintf("%d", message.LeftDisplay)) } if message.RightDisplay >= 0 { add("", fmt.Sprintf(",%d", message.RightDisplay)) } return result }