Analyzing Twine Files (with Node.js)

The Extwee project is both a Twee compiler (Twee -> HTML) and a HTML decompiler (HTML -> Twee). This means that it can take a Twine 2-compatible HTML file and create a Twee file from its contents, converting the elements back into textual passages. Working within this process, a Story object and its into Passage objects can be examined.

Creating a Project Directory

Create a new directory. From the command-line, run the following

npm init

Assume the defaults or otherwise change them as needed.

Next, as Extwee is on NPM, install that module.

npm i extwee

Once the dependencies have been installed, create a new file “index.js”

Loading Extwee’s Objects

Once the Extwee module has been downloaded, its objects can be used in a project. Looking at the “index.js” file of Extwee on GitHub, its model can be taken and changed slightly to meet the new needs of only decompiling a HTML file.

const FileReader = require('extwee/FileReader.js');
const TweeWriter = require('extwee/TweeWriter.js');
const HTMLParser = require('extwee/HTMLParser.js');

The same is true of the decompiling process. However, as argv is not part of this project, it can be ignored. Instead, some local variables can be added for testing purposes.

let input = "HTMLExample.html";
let output = "testing.twee";

let fr = new FileReader(input);
let hd = new HTMLParser(fr.contents);
let tw = new TweeWriter(hd.story, output);

Reading a Story

As the “Story.js” file of Extwee shows on GitHub, a Story object has an internal array property of passages. Instead of passing the Story object to TweeWriter, then, it can be used to read through a Twine Story.

Looking at the “Passage.js” file of Extwee on GitHub shows that each Passage of a Story object has its own internal property called text that contains its text.

Knowing these two pieces of information, the last line where TweeWriter was used can be changed to move through the passages of a Story object and, as an initial test, print the text of each Passage it encounters.

for(let p of hd.story.passages) {
  console.log(p.text);
}

Counting Words

A quick example that read through each Passage of a Story counted the total number of words might look like the following:

const FileReader = require('extwee/FileReader.js');
const TweeWriter = require('extwee/TweeWriter.js');
const HTMLParser = require('extwee/HTMLParser.js');

let input = "HTMLExample.html";
let output = "testing.twee";

let fr = new FileReader(input);
let hd = new HTMLParser(fr.contents);

let totalWords = 0;

for(let p of hd.story.passages) {
  totalWords += p.text.length;
}

console.log("Total words is " + totalWords);

Counting Basic Links

To only count (and not record) the number of basic links in a Twine 2-compatible HTML file, the code might look like the following:

const FileReader = require('extwee/FileReader.js');
const TweeWriter = require('extwee/TweeWriter.js');
const HTMLParser = require('extwee/HTMLParser.js');

let input = "HTMLExample.html";
let output = "testing.twee";

let fr = new FileReader(input);
let hd = new HTMLParser(fr.contents);

let linkCount = 0;

for(let p of hd.story.passages) {

  let links = p.text.match(/\[\[.*\]\]/g);

  if(links != null) {
    linkCount++;
  }

}

console.log("Total basic links are " + linkCount);

Reading Metadata (StoryData)

In the Twee 3 specification, the StoryData passage holds information about the story itself. To read this, look for a passage named “StoryData”. As it is JSON-encoded values, it can also be parsed back into an object.

const FileReader = require('extwee/FileReader.js');
const TweeWriter = require('extwee/TweeWriter.js');
const HTMLParser = require('extwee/HTMLParser.js');

let input = "HTMLExample.html";
let output = "testing.twee";

let fr = new FileReader(input);
let hd = new HTMLParser(fr.contents);

let metadata = {};

for(let p of hd.story.passages) {

  if(p.name == "StoryData") {
    metadata = JSON.parse(p.text)
  }

}

console.log(metadata);

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.