Skip to content

Commit

Permalink
Added cleaned-up language scripts
Browse files Browse the repository at this point in the history
  • Loading branch information
mathewv committed Mar 30, 2017
1 parent e3f572d commit 2eb67bf
Show file tree
Hide file tree
Showing 2 changed files with 340 additions and 0 deletions.
20 changes: 20 additions & 0 deletions mklang.rb
@@ -0,0 +1,20 @@
#!/usr/bin/ruby

require 'get_pomo'
require 'zlib'

if ARGV.length != 2
STDERR.puts "usage: mklang.rb src_file out_file"
exit 1
end

src_file = ARGV[0]
dst_file = ARGV[1]

po = GetPomo::PoFile.parse(File.read(src_file))

File.open(dst_file, 'wb') do |file|
file.write(Marshal.dump(po.map do |t|
[Zlib.crc32(t.msgid), t.msgstr]
end.to_h))
end
320 changes: 320 additions & 0 deletions mklangsrc.rb
@@ -0,0 +1,320 @@
#!/usr/bin/ruby
require 'zlib'

# Dummy RGSS template for unmarshalling
module RPG
# Useful
class Actor
attr_reader :name
end
class Map
attr_reader :events
end
class MapInfo
attr_reader :name
attr_reader :parent_id
end
class Event
attr_reader :name
attr_reader :pages
class Page
attr_reader :list
class Condition
end
class Graphic
end
end
end
class EventCommand
attr_reader :code
attr_reader :parameters
end
class CommonEvent
attr_reader :name
attr_reader :list
end
class Item
attr_reader :name
attr_reader :description
end

# Useless
class AudioFile
end
class MoveRoute
end
class MoveCommand
end
end
class Table
def self._load(foo)
end
end
class Color
def self._load(foo)
end
end
class Tone
def self._load(foo)
end
end

# Various escape functions
# https://stackoverflow.com/questions/8639642/best-way-to-escape-and-unescape-strings-in-ruby
module Escape
# Ruby
UNESCAPES = {
'a' => "\x07", 'b' => "\x08", 't' => "\x09",
'n' => "\x0a", 'v' => "\x0b", 'f' => "\x0c",
'r' => "\x0d", 'e' => "\x1b", "\\" => "\x5c",
'"' => "\x22", "'" => "\x27"
}
ESCAPES = {
"\x07" => 'a', "\x08" => 'b', "\x09" => 't',
"\x0a" => 'n', "\x0b" => 'v', "\x0c" => 'f',
"\x0d" => 'r', "\x1b" => 'e', "\x5c" => "\\",
"\x22" => '"'
}

def self.unescape(str)
str.gsub(/\\(?:([#{Regexp.escape(UNESCAPES.keys.join)}])|u([\da-fA-F]{4}))|\\0?x([\da-fA-F]{2})/) do
if $1
UNESCAPES[$1]
elsif $2 # escape \u0000 unicode
["#$2".hex].pack('U*')
elsif $3 # escape \0xff or \xff
[$3].pack('H2')
end
end
end

# gettext
def self.escape(str)
str.gsub(/[#{Regexp.escape(ESCAPES.keys.join)}]/) do |m|
"\\" + ESCAPES[m]
end
end
end

class StringList
class Entry
attr_accessor :string
attr_accessor :contexts
attr_accessor :comments

def initialize(string)
@string = string
@contexts = []
@comments = []
end

def add(context, comment)
@contexts << context unless @contexts.include?(context) || context == nil
case comment
when String
@comments << comment unless @comments.include?(comment)
when Array
comment.each do |com|
@comments << com unless @comments.include?(com)
end
end
end
end

def initialize
@strings = Hash.new { |k, v| k[v] = Entry.new(v) }
@order = []
end

def add(string, context, comment)
@order << string unless @strings.include? string
@strings[string].add(context, comment)
end

def dump(filename)
File.open(filename, 'w') do |file|
# Write header
file.puts '# Translation template for OneShot'
file.puts
file.puts 'msgid ""'
file.puts 'msgstr ""'
file.puts
@order.each do |str|
entry = @strings[str]
entry.comments.each do |comment|
file.puts "#. #{comment}"
end
unless entry.contexts.empty?
file.puts "#: #{entry.contexts.join(' ')}"
end
file.puts "msgid \"#{Escape.escape(entry.string)}\""
file.puts "msgstr \"\""
file.puts
end
end
end
end

def load_data(filename)
Marshal.load(File.read(filename))
end

# Script
if ARGV.size < 2
STDERR.puts "usage: mklangsrc.rb game_dir out_file"
exit 1
end
game_dir = ARGV[0]
out_file = ARGV[1]

strlist = StringList.new

# Actors
load_data(File.join(game_dir, 'Data/Actors.rxdata')).each_with_index do |actor, i|
next if !actor || actor.name.empty?
strlist.add(actor.name, "Actors:#{i}", 'actor name')
end

# Items
load_data(File.join(game_dir, 'Data/Items.rxdata')).each_with_index do |item, i|
next if !item || item.name.empty?
strlist.add(item.name, "Items:#{i}", 'item name')
next if item.description.empty?
strlist.add(item.description, "Items:#{i}", 'item description')
end

# Scripts
load_data(File.join(game_dir, 'Data/xScripts.rxdata')).each do |script|
script_name = script[1]
comment = nil
Zlib::Inflate.inflate(script[2]).each_line.with_index do |line, line_num|
# Scan for tr() calls
line.scan(/(?:^|[^_A-Za-z0-9])tr\s*\(\s*(?:'((?:\\.|[^'])*)'|"((?:\\.|[^"])*)")\s*\)/) do |single, double|
string = single || double
string = Escape.unescape(string)
next if string =~ /^\s*$/
strlist.add(string, "Scripts/#{script_name}:#{line_num+1}", comment)
end
# Scan for any comments on this line
m = line.match(/^(?:[^"'#]|"(?:\\"|[^"])*"|'(?:\\'|[^'])*')*(?:#\s*(.*))?$/)
if !m || m.captures.empty?
comment = nil
else
comment = m.captures.first
end
end
end

# Yields all translatable EdText strings
def tr_edtext(script)
# Scan for tr() calls
script.scan(/(?:^|[^_A-Za-z0-9])EdText\.\w+\s*\(\s*(?:'((?:\\.|[^'])*)'|"((?:\\.|[^"])*)")\s*\)/m) do |single, double|
string = single || double
string = Escape.unescape(string).gsub(/\s+/, ' ').gsub(/^\s+/, '').gsub(/\s+$/, '')
next if string =~ /^\s*$/
yield string
end
end

# Events
def parse_event_list(strlist, name, page, list)
# Parse commands
if page == nil
context = name
else
context = "#{name}:#{page}"
end
comment = nil
i = 0
while i < list.size
case list[i].code
when 101
# Message box
string = list[i].parameters[0].rstrip
loop do
i += 1
break unless list[i].code == 401
string << " " << list[i].parameters[0].rstrip
end
string.gsub!(/\s*\\n\s*/, '\\n')
string.strip!
unless string.empty?
strlist.add(string, "#{context}:#{i}:text", comment)
end
comment = nil
when 102
# Choices
list[i].parameters[0].each_with_index do |choice, j|
unless choice.empty?
strlist.add(choice, "#{context}:#{i}:choice:#{j}", comment)
end
end
i += 1
comment = nil
when 108
# Comment
comment = []
loop do
comment << list[i].parameters[0].rstrip
i += 1
break unless list[i].code == 408
end
when 111
# Conditional branch (may contain script text)
if list[i].parameters[0] == 12
tr_edtext(list[i].parameters[1]) do |string|
strlist.add(string, "#{context}:#{i}:condition", comment)
end
end
i += 1
comment = nil
when 355
# Script
script = list[i].parameters[0]
loop do
i += 1
break unless list[i].code == 655
script += "\n" + list[i].parameters[0]
end
tr_edtext(script) do |string|
strlist.add(string, "#{context}:#{i}:script", comment)
end
comment = nil
else
i += 1
comment = nil
end
end
end

# Do common events
load_data(File.join(game_dir, 'Data/CommonEvents.rxdata')).each do |event|
parse_event_list(strlist, 'CommonEvents/' + event.name, nil, event.list) if event
end

# Do map events
map_info = load_data(File.join(game_dir, 'Data/MapInfos.rxdata'))
map_info.each do |map_id, foo|
# Construct full name of map
map_name = ''
i = map_id
begin
map_name.insert(0, "/" + map_info[i].name)
i = map_info[i].parent_id
end while i > 0

# Load map
map = Marshal.load(File.read(File.join(game_dir, sprintf('Data/Map%03d.rxdata', map_id))))

# Parse each event
map.events.sort.each do |_, event|
# Construct full name of event
name = "Maps#{map_name}/#{event.name}"
# Iterate through pages
event.pages.each_with_index do |page, i|
parse_event_list(strlist, name, i + 1, page.list)
end
end
end

strlist.dump(out_file)

0 comments on commit 2eb67bf

Please sign in to comment.