wolftrans 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +363 -0
- data/README.md +42 -0
- data/bin/wolftrans +11 -0
- data/lib/wolfrpg.rb +65 -0
- data/lib/wolfrpg/command.rb +218 -0
- data/lib/wolfrpg/common_events.rb +160 -0
- data/lib/wolfrpg/database.rb +315 -0
- data/lib/wolfrpg/game_dat.rb +76 -0
- data/lib/wolfrpg/io.rb +63 -0
- data/lib/wolfrpg/map.rb +250 -0
- data/lib/wolfrpg/route.rb +36 -0
- data/lib/wolftrans.rb +169 -0
- data/lib/wolftrans/context.rb +193 -0
- data/lib/wolftrans/patch_data.rb +341 -0
- data/lib/wolftrans/patch_text.rb +319 -0
- data/wolftrans.gemspec +14 -0
- metadata +60 -0
@@ -0,0 +1,319 @@
|
|
1
|
+
require 'wolftrans/context'
|
2
|
+
require 'wolfrpg'
|
3
|
+
|
4
|
+
require 'fileutils'
|
5
|
+
require 'find'
|
6
|
+
|
7
|
+
module WolfTrans
|
8
|
+
class Patch
|
9
|
+
######################
|
10
|
+
# Loading Patch data #
|
11
|
+
def load_patch(patch_dir)
|
12
|
+
@patch_dir = WolfTrans.sanitize_path(patch_dir)
|
13
|
+
@patch_assets_dir = "#{@patch_dir}/Assets"
|
14
|
+
@patch_strings_dir = "#{@patch_dir}/Patch"
|
15
|
+
|
16
|
+
# Make sure these directories all exist
|
17
|
+
[@patch_assets_dir, @patch_strings_dir].each do |dir|
|
18
|
+
FileUtils.mkdir_p dir
|
19
|
+
end
|
20
|
+
|
21
|
+
# Find data dir
|
22
|
+
@patch_data_dir = WolfTrans.join_path_nocase(@patch_assets_dir, 'data')
|
23
|
+
|
24
|
+
# Load blacklist
|
25
|
+
@file_blacklist = []
|
26
|
+
if File.exists? "#{patch_dir}/blacklist.txt"
|
27
|
+
IO.read_txt("#{patch_dir}/blacklist.txt").each_line do |line|
|
28
|
+
line.strip!
|
29
|
+
next if line.empty?
|
30
|
+
if line.include? '\\'
|
31
|
+
raise "file specified in blacklist contains a backslash (use a forward slash instead)"
|
32
|
+
end
|
33
|
+
@file_blacklist << line.downcase!
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Load strings
|
38
|
+
Find.find(@patch_strings_dir) do |path|
|
39
|
+
next if FileTest.directory? path
|
40
|
+
next unless File.extname(path).casecmp '.txt'
|
41
|
+
process_patch_file(path, :load)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Write back to patch files
|
45
|
+
processed_filenames = []
|
46
|
+
|
47
|
+
Find.find(@patch_strings_dir) do |path|
|
48
|
+
next if FileTest.directory? path
|
49
|
+
next unless File.extname(path).casecmp '.txt'
|
50
|
+
process_patch_file(path, :update)
|
51
|
+
processed_filenames << path[@patch_strings_dir.length+1..-1]
|
52
|
+
end
|
53
|
+
|
54
|
+
# Now "process" any files that should be generated
|
55
|
+
@strings.each do |string, contexts|
|
56
|
+
contexts.each do |context, trans|
|
57
|
+
unless processed_filenames.include? trans.patch_filename
|
58
|
+
process_patch_file("#{@patch_strings_dir}/#{trans.patch_filename}", :update)
|
59
|
+
processed_filenames << trans.patch_filename
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Load the translation strings indicated in the patch file,
|
66
|
+
# generate a new patch file with updated context information,
|
67
|
+
# and overwrite the patch
|
68
|
+
def process_patch_file(filename, mode)
|
69
|
+
patch_filename = filename[@patch_strings_dir.length+1..-1]
|
70
|
+
|
71
|
+
txt_version = nil
|
72
|
+
|
73
|
+
# Parser state information
|
74
|
+
state = :expecting
|
75
|
+
original_string = ''
|
76
|
+
contexts = []
|
77
|
+
translated_string = ''
|
78
|
+
new_contexts = nil
|
79
|
+
|
80
|
+
# Variables for the revised patch
|
81
|
+
context_comments = {}
|
82
|
+
|
83
|
+
# The revised patch
|
84
|
+
output = ''
|
85
|
+
output_write = false
|
86
|
+
pristine_translated_string = ''
|
87
|
+
|
88
|
+
if File.exists? filename
|
89
|
+
output_write = true if mode == :update
|
90
|
+
IO.read_txt(filename).each_line.with_index do |pristine_line, index|
|
91
|
+
# Remove comments and strip
|
92
|
+
pristine_line.gsub!(/\n$/, '')
|
93
|
+
line = pristine_line.gsub(/(?!\\)#.*$/, '').rstrip
|
94
|
+
comment = pristine_line.match(/(?<!\\)#.*$/).to_s.rstrip
|
95
|
+
line_num = index + 1
|
96
|
+
|
97
|
+
if line.start_with? '>'
|
98
|
+
instruction = line.gsub(/^>\s+/, '')
|
99
|
+
|
100
|
+
# Parse the patch version
|
101
|
+
parse_instruction(instruction, 'WOLF TRANS PATCH FILE VERSION') do |args|
|
102
|
+
unless txt_version == nil
|
103
|
+
raise "two version strings in file (line #{line_num})"
|
104
|
+
end
|
105
|
+
txt_version = Version.new(str: args.first)
|
106
|
+
if txt_version > TXT_VERSION
|
107
|
+
raise "patch version (#{new_version}) newer than can be read (#{TXT_VERSION})"
|
108
|
+
end
|
109
|
+
if mode == :update
|
110
|
+
output << "> WOLF TRANS PATCH FILE VERSION #{TXT_VERSION}"
|
111
|
+
output << comment unless comment.empty?
|
112
|
+
output << "\n"
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# Make sure we have a version specified before reading other instructions
|
117
|
+
if txt_version == nil
|
118
|
+
raise "no version specified before first instruction"
|
119
|
+
end
|
120
|
+
|
121
|
+
# Now parse the instructions
|
122
|
+
parse_instruction(instruction, 'BEGIN STRING') do |args|
|
123
|
+
unless state == :expecting
|
124
|
+
raise "began another string without ending previous string (line #{line_num})"
|
125
|
+
end
|
126
|
+
state = :reading_original
|
127
|
+
original_string = ''
|
128
|
+
if mode == :update
|
129
|
+
output << pristine_line << "\n"
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
parse_instruction(instruction, 'END STRING') do |args|
|
134
|
+
if state == :expecting
|
135
|
+
raise "ended string without a begin (line #{line_num})"
|
136
|
+
elsif state == :reading_original
|
137
|
+
raise "ended string without a translation block (line #{line_num})"
|
138
|
+
end
|
139
|
+
state = :expecting
|
140
|
+
new_contexts = []
|
141
|
+
end
|
142
|
+
|
143
|
+
parse_instruction(instruction, 'CONTEXT') do |args|
|
144
|
+
if state == :expecting
|
145
|
+
raise "context outside of begin/end block (line #{line_num})"
|
146
|
+
end
|
147
|
+
if args.empty?
|
148
|
+
raise "no context string provided in context line (line #{line_num})"
|
149
|
+
end
|
150
|
+
|
151
|
+
# After a context, we're no longer reading the original text.
|
152
|
+
state = :reading_translation
|
153
|
+
begin
|
154
|
+
new_context = Context.from_string(args.shift)
|
155
|
+
rescue => e
|
156
|
+
raise e, "#{e} (line #{line_num})", e.backtrace
|
157
|
+
end
|
158
|
+
# Append context if translated_string is empty, since that means
|
159
|
+
# no translation was given.
|
160
|
+
if translated_string.empty?
|
161
|
+
contexts << new_context
|
162
|
+
else
|
163
|
+
new_contexts = [new_context]
|
164
|
+
end
|
165
|
+
if mode == :update
|
166
|
+
# Save the comment for later
|
167
|
+
context_comments[new_context] = comment
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
# If we have a new context list queued, flush the translation to all
|
172
|
+
# of the collected contexts
|
173
|
+
if new_contexts
|
174
|
+
original_string_new = unescape_string(original_string, false)
|
175
|
+
translated_string_new = unescape_string(translated_string, true)
|
176
|
+
contexts.each do |context|
|
177
|
+
if mode == :update
|
178
|
+
# Write an appropriate context line to the output
|
179
|
+
output << '> CONTEXT '
|
180
|
+
if @strings.include?(original_string_new) &&
|
181
|
+
@strings[original_string_new].include?(context)
|
182
|
+
output << @strings[original_string_new].select { |k,v| k.eql? context }.keys.first.to_s
|
183
|
+
output << ' < UNTRANSLATED' if translated_string_new.empty?
|
184
|
+
else
|
185
|
+
output << context.to_s << ' < UNUSED'
|
186
|
+
end
|
187
|
+
output << " " << context_comments[context] unless comment.empty?
|
188
|
+
output << "\n"
|
189
|
+
else
|
190
|
+
# Put translation in hash
|
191
|
+
@strings[original_string_new][context] = Translation.new(patch_filename, translated_string_new, false)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
if mode == :update
|
195
|
+
# Write the translation
|
196
|
+
output << pristine_translated_string.rstrip << "\n"
|
197
|
+
# If the state is "expecting", that means we need to write the END STRING
|
198
|
+
# line to the output too.
|
199
|
+
if state == :expecting
|
200
|
+
output << pristine_line << "\n"
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
# Reset variables for next read
|
205
|
+
translated_string = ''
|
206
|
+
pristine_translated_string = ''
|
207
|
+
contexts = new_contexts
|
208
|
+
|
209
|
+
new_contexts = nil
|
210
|
+
end
|
211
|
+
else
|
212
|
+
# Parse text
|
213
|
+
if state == :expecting
|
214
|
+
unless line.empty?
|
215
|
+
raise "stray text outside of begin/end block (line #{line_num})"
|
216
|
+
end
|
217
|
+
elsif state == :reading_original
|
218
|
+
original_string << line << "\n"
|
219
|
+
elsif state == :reading_translation
|
220
|
+
translated_string << line << "\n"
|
221
|
+
if mode == :update
|
222
|
+
pristine_translated_string << pristine_line << "\n"
|
223
|
+
end
|
224
|
+
end
|
225
|
+
# Make no modifications to the patch line if we're not reading translations
|
226
|
+
unless state == :reading_translation
|
227
|
+
if mode == :update
|
228
|
+
output << pristine_line << "\n"
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
# Final error checking
|
235
|
+
if state != :expecting
|
236
|
+
raise "final begin/end block has no end"
|
237
|
+
end
|
238
|
+
else
|
239
|
+
# It's a new file, so just stick a header on it
|
240
|
+
if mode == :update
|
241
|
+
output << "> WOLF TRANS PATCH FILE VERSION #{TXT_VERSION}\n"
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
if mode == :update
|
246
|
+
# Write all the new strings to the file
|
247
|
+
@strings.each do |orig_string, contexts|
|
248
|
+
if contexts.values.any? { |trans| trans.autogenerate? && trans.patch_filename == patch_filename }
|
249
|
+
output_write = true
|
250
|
+
output << "\n> BEGIN STRING\n#{escape_string(orig_string)}\n"
|
251
|
+
contexts.each do |context, trans|
|
252
|
+
next unless trans.autogenerate?
|
253
|
+
trans.autogenerate = false
|
254
|
+
output << "> CONTEXT " << context.to_s << " < UNTRANSLATED\n"
|
255
|
+
end
|
256
|
+
output << "\n> END STRING\n"
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
# Write the output to the file
|
261
|
+
if output_write
|
262
|
+
FileUtils.mkdir_p(File.dirname(filename))
|
263
|
+
File.open(filename, 'wb') { |file| file.write(output) }
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
private
|
269
|
+
# Yields the arguments of the instruction if it matches tested_instruction
|
270
|
+
def parse_instruction(instruction, tested_instruction)
|
271
|
+
if instruction.start_with? tested_instruction
|
272
|
+
args = instruction.slice(tested_instruction.length..-1).strip.split(/\s+<\s+/)
|
273
|
+
if args.first == nil || args.first.empty?
|
274
|
+
yield []
|
275
|
+
else
|
276
|
+
yield args
|
277
|
+
end
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
# Unescapes a patch file string
|
282
|
+
def unescape_string(string, do_rtrim)
|
283
|
+
# Remove trailing whitespace or just newline
|
284
|
+
if do_rtrim
|
285
|
+
string = string.rstrip
|
286
|
+
else
|
287
|
+
string = string.gsub(/\n\z/m, '')
|
288
|
+
end
|
289
|
+
# Change escape sequences
|
290
|
+
string.match(/(?<!\\)\\[^sntr><#\\]/) do |match|
|
291
|
+
raise "unknown escape sequence '#{match}' #{string}"
|
292
|
+
end
|
293
|
+
string.gsub!("\\\\", "\x00") #HACK
|
294
|
+
string.gsub!("\\s", " ")
|
295
|
+
string.gsub!("\\n", "\n")
|
296
|
+
string.gsub!("\\t", "\t")
|
297
|
+
string.gsub!("\\r", "\r")
|
298
|
+
string.gsub!("\\>", ">")
|
299
|
+
string.gsub!("\\#", "#")
|
300
|
+
string.gsub!("\x00") { "\\" } #HACK
|
301
|
+
string
|
302
|
+
end
|
303
|
+
|
304
|
+
# Escapes a string for writing into a patch file
|
305
|
+
def escape_string(string)
|
306
|
+
string = string.gsub("\\", "\x00") #HACK
|
307
|
+
string.gsub!("\t", "\\t")
|
308
|
+
string.gsub!("\r", "\\r")
|
309
|
+
string.gsub!(">", "\\>")
|
310
|
+
string.gsub!("#", "\\#")
|
311
|
+
string.gsub!("\x00") { "\\\\" } #HACK
|
312
|
+
# Replace trailing spaces with \s
|
313
|
+
string.gsub!(/ *$/) { |m| "\\s" * m.length }
|
314
|
+
# Replace trailing newlines with \n
|
315
|
+
string.gsub!(/\n*\z/m) { |m| "\\n" * m.length }
|
316
|
+
string
|
317
|
+
end
|
318
|
+
end
|
319
|
+
end
|
data/wolftrans.gemspec
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
Gem::Specification.new do |s|
|
3
|
+
s.name = 'wolftrans'
|
4
|
+
s.version = '0.0.1'
|
5
|
+
s.date = '2015-09-14'
|
6
|
+
s.summary = 'A utility to translate Wolf RPG Editor games'
|
7
|
+
s.description = s.summary
|
8
|
+
s.authors = ['Mathew Velasquez']
|
9
|
+
s.email = 'mathewvq@gmail.com'
|
10
|
+
s.files = `git ls-files -z`.split("\x0")
|
11
|
+
s.executables << 'wolftrans'
|
12
|
+
s.homepage = 'http://mathew.link/'
|
13
|
+
s.license = 'MPL'
|
14
|
+
end
|
metadata
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: wolftrans
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mathew Velasquez
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-09-14 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: A utility to translate Wolf RPG Editor games
|
14
|
+
email: mathewvq@gmail.com
|
15
|
+
executables:
|
16
|
+
- wolftrans
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- LICENSE
|
21
|
+
- README.md
|
22
|
+
- bin/wolftrans
|
23
|
+
- lib/wolfrpg.rb
|
24
|
+
- lib/wolfrpg/command.rb
|
25
|
+
- lib/wolfrpg/common_events.rb
|
26
|
+
- lib/wolfrpg/database.rb
|
27
|
+
- lib/wolfrpg/game_dat.rb
|
28
|
+
- lib/wolfrpg/io.rb
|
29
|
+
- lib/wolfrpg/map.rb
|
30
|
+
- lib/wolfrpg/route.rb
|
31
|
+
- lib/wolftrans.rb
|
32
|
+
- lib/wolftrans/context.rb
|
33
|
+
- lib/wolftrans/patch_data.rb
|
34
|
+
- lib/wolftrans/patch_text.rb
|
35
|
+
- wolftrans.gemspec
|
36
|
+
homepage: http://mathew.link/
|
37
|
+
licenses:
|
38
|
+
- MPL
|
39
|
+
metadata: {}
|
40
|
+
post_install_message:
|
41
|
+
rdoc_options: []
|
42
|
+
require_paths:
|
43
|
+
- lib
|
44
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
requirements: []
|
55
|
+
rubyforge_project:
|
56
|
+
rubygems_version: 2.4.5.1
|
57
|
+
signing_key:
|
58
|
+
specification_version: 4
|
59
|
+
summary: A utility to translate Wolf RPG Editor games
|
60
|
+
test_files: []
|