/*
Copyright © 2014–2019 Thomas Michael Edwards. All rights reserved.
Use of this source code is governed by a Simplified BSD License which
can be found in the LICENSE file.
*/
package main
import (
// standard packages
"bytes"
"fmt"
"log"
"path/filepath"
"strconv"
"strings"
"unicode"
// internal packages
twee2 "bitbucket.org/tmedwards/tweego/internal/twee2compat"
twlex "bitbucket.org/tmedwards/tweego/internal/tweelexer"
// external packages
"golang.org/x/net/html"
)
func (s *story) load(filenames []string, c *config) {
for _, filename := range filenames {
if s.processed[filename] {
log.Printf("warning: load %s: Skipping duplicate.", filename)
continue
}
switch normalizedFileExt(filename) {
// NOTE: The case values here should match those in `filesystem.go:knownFileType()`.
case "tw", "twee":
if err := s.loadTwee(filename, c.encoding, c.twee2Compat); err != nil {
log.Fatalf("error: load %s: %s", filename, err.Error())
}
case "tw2", "twee2":
if err := s.loadTwee(filename, c.encoding, true); err != nil {
log.Fatalf("error: load %s: %s", filename, err.Error())
}
case "htm", "html":
if err := s.loadHTML(filename, c.encoding); err != nil {
log.Fatalf("error: load %s: %s", filename, err.Error())
}
case "css":
if err := s.loadTagged("stylesheet", filename, c.encoding); err != nil {
log.Fatalf("error: load %s: %s", filename, err.Error())
}
case "js":
if err := s.loadTagged("script", filename, c.encoding); err != nil {
log.Fatalf("error: load %s: %s", filename, err.Error())
}
case "otf", "ttf", "woff", "woff2":
if err := s.loadFont(filename); err != nil {
log.Fatalf("error: load %s: %s", filename, err.Error())
}
case "gif", "jpeg", "jpg", "png", "svg", "tif", "tiff", "webp":
if err := s.loadMedia("Twine.image", filename); err != nil {
log.Fatalf("error: load %s: %s", filename, err.Error())
}
case "aac", "flac", "m4a", "mp3", "oga", "ogg", "opus", "wav", "wave", "weba":
if err := s.loadMedia("Twine.audio", filename); err != nil {
log.Fatalf("error: load %s: %s", filename, err.Error())
}
case "mp4", "ogv", "webm":
if err := s.loadMedia("Twine.video", filename); err != nil {
log.Fatalf("error: load %s: %s", filename, err.Error())
}
case "vtt":
if err := s.loadMedia("Twine.vtt", filename); err != nil {
log.Fatalf("error: load %s: %s", filename, err.Error())
}
default:
// Simply ignore all other file types.
continue
}
s.processed[filename] = true
statsAddProjectFile(filename)
}
/*
Postprocessing.
*/
// Prepend the `StoryTitle` special passage, if necessary.
if s.name != "" && !s.has("StoryTitle") {
s.prepend(newPassage("StoryTitle", []string{}, s.name))
}
}
func (s *story) loadTwee(filename, encoding string, twee2Compat bool) error {
source, err := fileReadAllWithEncoding(filename, encoding)
if err != nil {
return err
}
if twee2Compat {
source = twee2.ToV3(source)
}
var (
pCount = 0
lastType twlex.ItemType
lex = twlex.NewTweelexer(source)
)
ParseLoop:
for {
p := &passage{}
for item, ok := lex.NextItem(); ok; item, ok = lex.NextItem() {
// switch item.Type {
// case twlex.ItemEOF, twlex.ItemHeader:
// log.Println()
// }
// log.Printf("%v\n", item)
switch item.Type {
case twlex.ItemError:
return fmt.Errorf("line %d: Malformed twee source; %s.", item.Line, item.Val)
case twlex.ItemEOF:
// Add the final passage, if any.
if pCount > 0 {
s.add(p)
}
break ParseLoop
case twlex.ItemHeader:
pCount++
if pCount > 1 {
s.add(p)
p = &passage{}
}
case twlex.ItemName:
p.name = string(bytes.TrimSpace(tweeUnescapeBytes(item.Val)))
if len(p.name) == 0 {
lex.Drain()
return fmt.Errorf("line %d: Malformed twee source; passage with no name.", item.Line)
}
case twlex.ItemTags:
if lastType != twlex.ItemName {
lex.Drain()
return fmt.Errorf("line %d: Malformed twee source; optional tags block must immediately follow the passage name.", item.Line)
}
p.tags = strings.Fields(string(tweeUnescapeBytes(item.Val[1 : len(item.Val)-1])))
case twlex.ItemMetadata:
if lastType != twlex.ItemName && lastType != twlex.ItemTags {
lex.Drain()
return fmt.Errorf("line %d: Malformed twee source; optional metadata block must immediately follow the passage name or tags block.", item.Line)
}
if err := p.unmarshalMetadata(item.Val); err != nil {
log.Printf("warning: load %s: line %d: Malformed twee source; could not decode metadata (reason: %s).", filename, item.Line, err.Error())
}
case twlex.ItemContent:
// p.text = string(bytes.TrimSpace(item.Val))
p.text = string(bytes.TrimRightFunc(item.Val, unicode.IsSpace))
}
lastType = item.Type
}
}
return nil
}
func (s *story) loadHTML(filename, encoding string) error {
source, err := fileReadAllWithEncoding(filename, encoding)
if err != nil {
return err
}
doc, err := getDocumentTree(bytes.TrimSpace(source))
if err != nil {
return fmt.Errorf("Malformed HTML source; %s.", err.Error())
}
if storyData := getElementByTag(doc, "tw-storydata"); storyData != nil {
// Twine 2 style story data chunk.
/*