Files
questshock/modding/hacked/ss1/content/text/ElectronicMessage.go
T

202 lines
4.9 KiB
Go

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
}