Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,9 @@ gem 'filewatcher'
gem 'haml'
gem 'coffee-script'
gem 'nokogiri'
gem 'sass'
gem 'sass'

group :development do
gem 'twee2', path: '.'
gem 'pry-byebug'
end
1 change: 0 additions & 1 deletion doc/usage.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ Usage:
twee2 decompile [URL] [output.tw2]
Decompiles a Twee2/Twine 2 HTML output file at a specified URL into
a Twee2 source file.
NOT AVAILABLE ON MICROSOFT WINDOWS.

twee2 version
Reports what version of Twee2 you're using, and checks what the
Expand Down
16 changes: 8 additions & 8 deletions lib/twee2.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,10 @@ def self.build(input, output, options = {})
puts "NOTICE: You haven't specified your IFID. Consider adding to your code -"
puts "::StoryIFID[twee2]\nTwee2::build_config.story_ifid = '#{build_config.story_ifid}'"
end
# Make sure output directory exists
FileUtils.mkdir_p(File.dirname(output))
# Produce output file
File::open(output, 'w') do |out|
File::open(output, 'w', encoding: "utf-8") do |out|
out.print build_config.story_format.compile
end
puts "Done"
Expand Down Expand Up @@ -74,14 +76,12 @@ def self.version_check
end
end

unless Gem.win_platform?
# Reverse-engineers a Twee2/Twine 2 output HTML file into a Twee2 source file
def self.decompile(url, output)
File::open(output, 'w') do |out|
out.print Decompiler::decompile(url)
end
puts "Done"
# Reverse-engineers a Twee2/Twine 2 output HTML file into a Twee2 source file
def self.decompile(url, output, encoding:"utf-8")
File::open(output, 'w') do |out|
out.print Decompiler::decompile(url)
end
puts "Done"
end

def self.help
Expand Down
97 changes: 51 additions & 46 deletions lib/twee2/decompiler.rb
Original file line number Diff line number Diff line change
@@ -1,54 +1,59 @@
unless Gem.win_platform?
require 'rubygems'
require 'open-uri'
require 'nokogiri'
require 'rubygems'
require 'open-uri'
require 'nokogiri'

module Twee2
class DecompilationFailedException < Exception; end
module Twee2
class DecompilationFailedException < Exception; end

class Decompiler
def self.decompile(url)
result = ''
# Load the compiled HTML and sanity-check it
html = Nokogiri::HTML(open(url))
raise(DecompilationFailedException, 'tw-storydata not found') unless storydata = html.at_css('tw-storydata')
# Extract the tw-storydata#name (StoryTitle) and #startnode
result << "::StoryTitle\n#{storydata[:name].strip}\n\n"
startnode_pid, startnode_name = storydata[:startnode].strip, nil
# Extract the custom CSS and Javascript, if applicable
if (css = storydata.at_css('#twine-user-stylesheet')) && ((css_content = css.content.strip) != '')
result << "::StoryCSS [stylesheet]\n#{css_content}\n\n"
end
if (js = storydata.at_css('#twine-user-script')) && ((js_content = js.content.strip) != '')
result << "::StoryJS [script]\n#{js.content}\n\n"
end
# Extract each passage
storydata.css('tw-passagedata').each do |passagedata|
# Check if this is the start passage and record this accordingly
startnode_name = passagedata[:name] if(startnode_pid == passagedata[:pid])
# Write the passage out
result << "::#{passagedata[:name].strip}"
result << " [#{passagedata[:tags].strip}]" if passagedata[:tags].strip != ''
result << " <#{passagedata[:position].strip}>" if passagedata[:position].strip != ''
result << "\n#{tidyup_passagedata(passagedata.content.strip)}\n\n"
end
# Write the Twee2 settings out (compatability layer)
result << "::Twee2Settings [twee2]\n"
result << "@story_start_name = '#{startnode_name.gsub("'", "\\'")}'\n" if startnode_name
result << "\n"
# Return the result
result
class Decompiler
def self.decompile(url)
result = ''
# Load the compiled HTML and sanity-check it
html = Nokogiri::HTML(open(url), nil, 'utf-8')
raise(DecompilationFailedException, 'tw-storydata not found') unless storydata = html.at_css('tw-storydata')
# Extract the tw-storydata#name (StoryTitle) and #startnode
result << "::StoryTitle\n#{storydata[:name].strip}\n\n"
startnode_pid, startnode_name = storydata[:startnode].strip, nil
ifid = storydata[:ifid]
story_format = storydata[:format]
if story_format == 'SugarCube' && storydata[:'format-version'].start_with?('2.')
story_format = 'SugarCube2'
end
# Extract the custom CSS and Javascript, if applicable
if (css = storydata.at_css('#twine-user-stylesheet')) && ((css_content = css.content.strip) != '')
result << "::StoryCSS [stylesheet]\n#{css_content}\n\n"
end
if (js = storydata.at_css('#twine-user-script')) && ((js_content = js.content.strip) != '')
result << "::StoryJS [script]\n#{js.content}\n\n"
end
# Extract each passage
storydata.css('tw-passagedata').each do |passagedata|
# Check if this is the start passage and record this accordingly
startnode_name = passagedata[:name] if(startnode_pid == passagedata[:pid])
# Write the passage out
result << "::#{passagedata[:name].strip}"
result << " [#{passagedata[:tags].strip}]" if passagedata[:tags].strip != ''
result << " <#{passagedata[:position].strip}>" if passagedata[:position].strip != ''
result << "\n#{tidyup_passagedata(passagedata.content.strip)}\n\n"
end
# Write the Twee2 settings out (compatability layer)
result << "::Twee2Settings [twee2]\n"
result << "@story_start_name = '#{startnode_name.gsub("'", "\\'")}'\n" if startnode_name
result << "Twee2::build_config.story_ifid = '#{ifid}'\n"
result << "Twee2::build_config.story_format = '#{story_format}'\n"
result << "\n"
# Return the result
result
end

protected
protected

# Fixes common problems with decompiled passage content
def self.tidyup_passagedata(passagedata_content)
passagedata_content.gsub(/\[\[ *(.*?) *\]\]/, '[[\1]]'). # remove excess spacing within links: not suitable for Twee-style source
gsub(/\[\[ *(.*?) *<- *(.*?) *\]\]/, '[[\1<-\2]]'). # ditto
gsub(/\[\[ *(.*?) *-> *(.*?) *\]\]/, '[[\1->\2]]'). # ditto
gsub(/\[\[ *(.*?) *\| *(.*?) *\]\]/, '[[\1|\2]]') # ditto
end
# Fixes common problems with decompiled passage content
def self.tidyup_passagedata(passagedata_content)
passagedata_content.gsub(/\[\[ *(.*?) *\]\]/, '[[\1]]'). # remove excess spacing within links: not suitable for Twee-style source
gsub(/\[\[ *(.*?) *<- *(.*?) *\]\]/, '[[\1<-\2]]'). # ditto
gsub(/\[\[ *(.*?) *-> *(.*?) *\]\]/, '[[\1->\2]]'). # ditto
gsub(/\[\[ *(.*?) *\| *(.*?) *\]\]/, '[[\1|\2]]') # ditto
end
end
end
6 changes: 3 additions & 3 deletions lib/twee2/story_file.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def initialize(filename)
@child_story_files = []

# Load file into memory to begin with
lines = File::read(filename).split(/\r?\n/)
lines = File::read(filename, encoding: 'utf-8').split(/\r?\n/)
# First pass - go through and perform 'includes'
i, in_story_includes_section = 0, false
while i < lines.length
Expand All @@ -48,7 +48,7 @@ def initialize(filename)
# include a file here because an @include directive was spotted
prefix, filename = $1, $2.strip
if File::exists?(filename)
lines[i,1] = File::read(filename).split(/\r?\n/).map{|l|"#{prefix}#{l}"} # insert in-place, with prefix of appropriate amount of whitespace
lines[i,1] = File::read(filename, encoding: 'utf-8').split(/\r?\n/).map{|l|"#{prefix}#{l}"} # insert in-place, with prefix of appropriate amount of whitespace
i-=1 # process this line again, in case of ::@include nesting
else
puts "WARNING: tried to ::@include file '#{filename}' but file was not found."
Expand Down Expand Up @@ -114,7 +114,7 @@ def initialize(filename)
# Returns the rendered XML that represents this story
def xmldata
data = @story_data.target!
data.gsub('{{STORY_JS}}', @story_js)
data.gsub('{{STORY_JS}}') {@story_js}
end

# Runs HAML, Coffeescript etc. preprocessors across each applicable passage
Expand Down
7 changes: 5 additions & 2 deletions lib/twee2/story_format.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,18 @@ class StoryFormat
def initialize(name)
raise(StoryFormatNotFoundException) if !File::exists?(format_file_path = Twee2::buildpath("storyFormats/#{name}/format.js")) && !File::exists?(format_file_path = "#{name}/format.js")
@name = name
format_file = File::read(format_file_path)
format_file = File::read(format_file_path, encoding: 'utf-8')
format_data = format_file.match(/(["'])source\1 *: *(["']).*?[^\\]\2/)[0]
format_data_for_json = "\{#{format_data}\}"
@source = JSON.parse(format_data_for_json)['source']
end

# Given a story file, injects it into the StoryFormat and returns the HTML results
def compile
@source.gsub('{{STORY_NAME}}', Twee2::build_config.story_name).gsub('{{STORY_DATA}}', Twee2::build_config.story_file.xmldata).gsub('{{STORY_FORMAT}}', @name)
@source \
.gsub('{{STORY_NAME}}') { Twee2::build_config.story_name } \
.gsub('{{STORY_DATA}}') { Twee2::build_config.story_file.xmldata } \
.gsub('{{STORY_FORMAT}}') { @name }
end

# Returns an array containing the known StoryFormat names
Expand Down
2 changes: 1 addition & 1 deletion lib/twee2/version.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module Twee2
VERSION = "0.5.0"
VERSION = "0.6.0"
end
2 changes: 1 addition & 1 deletion web/source/documentation.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,7 @@ title: Full documentation
%p
It's possible to convert existing (compiled) Twee2/Twine 2 story files, in HTML format, back into
Twee2 source files for further editing. This can be used to convert your Twine 2 projects into Twee2
files or to easily examine the contents of somebody else's story. This feature does not work on Microsoft
files or to easily examine the contents of somebody else's story. This feature now works on Microsoft
Windows. To use it, run:
%p
%code twee2 decompile input.html output.tw2
Expand Down
2 changes: 0 additions & 2 deletions web/source/install.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@ title: Install
%h3 Windows
%p
%a{href: 'http://rubyinstaller.org/downloads/'} Download RubyInstaller
%p.small
Note that the 'decompile' feature, which turns Twine 2 story files into Twee2 source code, is not supported on Windows.

.col-sm-4
.well
Expand Down