Module: RGSS

Defined in:
lib/RGSS.rb,
lib/RGSS/serialize.rb,
lib/RGSS/BasicCoder.rb

Defined Under Namespace

Modules: BasicCoder Classes: Game_SelfSwitches, Game_Switches, Game_System, Game_Variables

Constant Summary collapse

FLOW_CLASSES =
[Color, Tone, RPG::BGM, RPG::BGS, RPG::MoveCommand, RPG::SE]
SCRIPTS_BASE =
'Scripts'
ACE_DATA_EXT =
'.rvdata2'
VX_DATA_EXT =
'.rvdata'
XP_DATA_EXT =
'.rxdata'
YAML_EXT =
'.yaml'
RUBY_EXT =
'.rb'

Class Method Summary collapse

Class Method Details

.array_to_hash(arr, &block) ⇒ Object



131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/RGSS.rb', line 131

def self.array_to_hash(arr, &block)
  h = {}
  arr.each_with_index do |val, index|
    r = block_given? ? block.call(val) : val
    h[index] = r unless r.nil?
  end
  if arr.length > 0
    last = arr.length - 1
    h[last] = nil unless h.has_key?(last)
  end
  return h
end

.change_extension(file, new_ext) ⇒ Object



28
29
30
# File 'lib/RGSS/serialize.rb', line 28

def self.change_extension(file, new_ext)
  return File.basename(file, '.*') + new_ext
end

.convert(src, dest, options) ⇒ Object



257
258
259
260
261
262
263
264
265
266
267
268
# File 'lib/RGSS/serialize.rb', line 257

def self.convert(src, dest, options)
  files = files_with_extension(src[:directory], src[:ext])
  files -= src[:exclude]

  files.each do |file|
    src_file = File.join(src[:directory], file)
    dest_file = File.join(dest[:directory], change_extension(file, dest[:ext]))

    process_file(file, src_file, dest_file, dest[:ext], src[:load_file],
                 dest[:dump_file], options)
  end
end

.convert_saves(base, src, dest, options) ⇒ Object



270
271
272
273
274
275
276
277
278
279
# File 'lib/RGSS/serialize.rb', line 270

def self.convert_saves(base, src, dest, options)
  save_files = files_with_extension(base, src[:ext])
  save_files.each do |file|
    src_file = File.join(base, file)
    dest_file = File.join(base, change_extension(file, dest[:ext]))

    process_file(file, src_file, dest_file, dest[:ext], src[:load_save],
                 dest[:dump_save], options)
  end
end

.deflate(str) ⇒ Object



45
46
47
# File 'lib/RGSS/serialize.rb', line 45

def self.deflate(str)
  return Zlib::Deflate.deflate(str, Zlib::BEST_COMPRESSION)
end

.dump(dumper, file, data, time, options) ⇒ Object



80
81
82
83
84
85
# File 'lib/RGSS/serialize.rb', line 80

def self.dump(dumper, file, data, time, options)
  self.method(dumper).call(file, data, time, options)
rescue
  warn "Exception dumping #{file}"
  raise
end

.dump_data_file(file, data, time, options) ⇒ Object



50
51
52
53
54
55
# File 'lib/RGSS/serialize.rb', line 50

def self.dump_data_file(file, data, time, options)
  File.open(file, "wb") do |f|
    Marshal.dump(data, f)
  end
  File.utime(time, time, file)
end

.dump_raw_file(file, data, time, options) ⇒ Object



73
74
75
76
77
78
# File 'lib/RGSS/serialize.rb', line 73

def self.dump_raw_file(file, data, time, options)
  File.open(file, "wb") do |f|
    f.write(data)
  end
  File.utime(time, time, file)
end

.dump_save(file, data, time, options) ⇒ Object



64
65
66
67
68
69
70
71
# File 'lib/RGSS/serialize.rb', line 64

def self.dump_save(file, data, time, options)
  File.open(file, "wb") do |f|
    data.each do |chunk|
      Marshal.dump(chunk, f)
    end
  end
  File.utime(time, time, file)
end

.dump_yaml_file(file, data, time, options) ⇒ Object



57
58
59
60
61
62
# File 'lib/RGSS/serialize.rb', line 57

def self.dump_yaml_file(file, data, time, options)
  File.open(file, "wb") do |f|
    Psych::dump(data, f, options)
  end
  File.utime(time, time, file)
end

.files_with_extension(directory, extension) ⇒ Object



36
37
38
# File 'lib/RGSS/serialize.rb', line 36

def self.files_with_extension(directory, extension)
  return Dir.entries(directory).select{|file| File.extname(file) == extension}
end

.get_data_directory(base) ⇒ Object



253
254
255
# File 'lib/RGSS.rb', line 253

def self.get_data_directory(base)
  return File.join(base, 'Data')
end

.get_script_directory(base) ⇒ Object



261
262
263
# File 'lib/RGSS.rb', line 261

def self.get_script_directory(base)
  return File.join(base, 'Scripts')
end

.get_yaml_directory(base) ⇒ Object



257
258
259
# File 'lib/RGSS.rb', line 257

def self.get_yaml_directory(base)
  return File.join(base, 'YAML')
end

.hash_to_array(hash) ⇒ Object



144
145
146
147
148
149
150
# File 'lib/RGSS.rb', line 144

def self.hash_to_array(hash)
  arr = []
  hash.each do |k, v|
    arr[k] = v
  end
  return arr
end

.inflate(str) ⇒ Object



40
41
42
43
# File 'lib/RGSS/serialize.rb', line 40

def self.inflate(str)
  text = Zlib::Inflate.inflate(str)
  return text.force_encoding("UTF-8")
end

.load(loader, file) ⇒ Object



163
164
165
166
167
168
# File 'lib/RGSS/serialize.rb', line 163

def self.load(loader, file)
  return self.method(loader).call(file)
rescue
  warn "Exception loading #{file}"
  raise
end

.load_data_file(file) ⇒ Object



88
89
90
91
92
# File 'lib/RGSS/serialize.rb', line 88

def self.load_data_file(file)
  File.open(file, "rb") do |f|
    return Marshal.load(f)
  end
end

.load_raw_file(file) ⇒ Object



146
147
148
149
150
# File 'lib/RGSS/serialize.rb', line 146

def self.load_raw_file(file)
  File.open(file, "rb") do |f|
    return f.read
  end
end

.load_save(file) ⇒ Object



152
153
154
155
156
157
158
159
160
161
# File 'lib/RGSS/serialize.rb', line 152

def self.load_save(file)
  File.open(file, "rb") do |f|
    data = []
    while not f.eof?
      o = Marshal.load(f)
      data.push(o)
    end
    return data
  end
end

.load_yaml_file(file) ⇒ Object



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/RGSS/serialize.rb', line 94

def self.load_yaml_file(file)
  formatador = Formatador.new
  obj = nil
  File.open(file, "rb") do |f|
    obj = Psych::load(f)
  end
  max = 0
  return obj unless obj.kind_of?(Array)
  seen = {}
  idx =
    obj.each do |elem|
    next if elem.nil?
    if elem.instance_variable_defined?("@id")
      id = elem.instance_variable_get("@id")
    else
      id = nil
      elem.instance_variable_set("@id", nil)
    end
    next if id.nil?

    if seen.has_key?(id)
      formatador.display_line("[red]#{file}: Duplicate ID #{id}[/]")
      formatador.indent {
        formatador.indent {
          elem.pretty_inspect.split(/\n/).each do |line|
            formatador.display_line("[red]#{line}[/]")
          end
        }
        formatador.display_line
        formatador.display_line("[red]Last seen at:\n[/]")
        formatador.indent {
          elem.pretty_inspect.split(/\n/).each do |line|
            formatador.display_line("[red]#{line}[/]")
          end
        }
      }
      exit
    end
    seen[id] = elem
    max = ((id + 1) unless id < max)
  end
  obj.each do |elem|
    next if elem.nil?
    id = elem.instance_variable_get("@id")
    if id.nil?
      elem.instance_variable_set("@id", max)
      max += 1
    end
  end
  return obj
end

.process(root, name, *args) ⇒ Object

creates an empty class in a potentially nested scope



156
157
158
159
160
161
162
# File 'lib/RGSS.rb', line 156

def self.process(root, name, *args)
  if args.length > 0
    process(root.const_get(name), *args)
  else
    root.const_set(name, Class.new) unless root.const_defined?(name, false)
  end
end

.process_file(file, src_file, dest_file, dest_ext, loader, dumper, options) ⇒ Object



243
244
245
246
247
248
249
250
251
252
253
254
255
# File 'lib/RGSS/serialize.rb', line 243

def self.process_file(file, src_file, dest_file, dest_ext, loader, dumper, options)
  formatador = Formatador.new
  fbase = File.basename(file, File.extname(file))
  return if (! options[:database].nil? ) and (options[:database].downcase != fbase.downcase)
  src_time = File.mtime(src_file)
  if !options[:force] && File.exists?(dest_file) && (src_time - 1) < File.mtime(dest_file)
    formatador.display_line("[yellow]Skipping #{file}[/]") if $VERBOSE
  else
    formatador.display_line("[green]Converting #{file} to #{dest_ext}[/]") if $VERBOSE
    data = load(loader, src_file)
    dump(dumper, dest_file, data, src_time, options)
  end
end

.remove_defined_method(scope, name) ⇒ Object



117
118
119
# File 'lib/RGSS.rb', line 117

def self.remove_defined_method(scope, name)
  scope.send(:remove_method, name) if scope.instance_methods(false).include?(name)
end

.reset_const(scope, sym, value) ⇒ Object



126
127
128
129
# File 'lib/RGSS.rb', line 126

def self.reset_const(scope, sym, value)
  scope.send(:remove_const, sym) if scope.const_defined?(sym)
  scope.send(:const_set, sym, value)
end

.reset_method(scope, name, method) ⇒ Object



121
122
123
124
# File 'lib/RGSS.rb', line 121

def self.reset_method(scope, name, method)
  remove_defined_method(scope, name)
  scope.send(:define_method, name, method)
end

.sanitize_filename(filename) ⇒ Object



32
33
34
# File 'lib/RGSS/serialize.rb', line 32

def self.sanitize_filename(filename)
  return filename.gsub(/[^0-9A-Za-z]+/, '_')
end

.scripts_to_binary(dirs, src, dest, options) ⇒ Object



214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
# File 'lib/RGSS/serialize.rb', line 214

def self.scripts_to_binary(dirs, src, dest, options)
  formatador = Formatador.new
  src_file = File.join(dirs[:yaml], src)
  dest_file = File.join(dirs[:data], dest)
  raise "Missing #{src}" unless File.exists?(src_file)
  check_time = !options[:force] && File.exists?(dest_file)
  newest_time = File.mtime(src_file) if check_time

  index = load(:load_yaml_file, src_file)
  script_entries = []
  index.each do |entry|
    magic_number, script_name, filename = entry
    code = ''
    if filename
      full_filename = File.join(dirs[:script], filename)
      raise "Missing script file #{filename}" unless File.exists?(full_filename)
      newest_time = [File.mtime(full_filename), newest_time].max if check_time
      code = load(:load_raw_file, full_filename)
    end
    script_entries.push([magic_number, script_name, deflate(code)])
  end
  if check_time && (newest_time - 1) < File.mtime(dest_file)
    formatador.display_line("[yellow]Skipping scripts to binary[/]") if $VERBOSE
  else
    formatador.display_line("[green]Converting scripts to binary[/]") if $VERBOSE
    dump(:dump_data_file, dest_file, script_entries, newest_time, options)
  end
end

.scripts_to_text(dirs, src, dest, options) ⇒ Object



171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/RGSS/serialize.rb', line 171

def self.scripts_to_text(dirs, src, dest, options)
  formatador = Formatador.new
  src_file = File.join(dirs[:data], src)
  dest_file = File.join(dirs[:yaml], dest)
  raise "Missing #{src}" unless File.exists?(src_file)

  script_entries = load(:load_data_file, src_file)
  check_time = !options[:force] && File.exists?(dest_file)
  oldest_time = File.mtime(dest_file) if check_time

  file_map, script_index, script_code = Hash.new(-1), [], {}

  idx=0
  script_entries.each do |script|
    idx += 1
    magic_number, script_name, code = idx, script[1], inflate(script[2])
    script_name.force_encoding("UTF-8")

    if code.length > 0
      filename = script_name.empty? ? 'blank' : sanitize_filename(script_name)
      key = filename.upcase
      value = (file_map[key] += 1)
      actual_filename = filename + (value == 0 ? "" : ".#{value}") + RUBY_EXT
      script_index.push([magic_number, script_name, actual_filename])
      full_filename = File.join(dirs[:script], actual_filename)
      script_code[full_filename] = code
      check_time = false unless File.exists?(full_filename)
      oldest_time = [File.mtime(full_filename), oldest_time].min if check_time
    else
      script_index.push([magic_number, script_name, nil])
    end
  end

  src_time = File.mtime(src_file)
  if check_time && (src_time - 1) < oldest_time
    formatador.display_line("[yellow]Skipping scripts to text[/]") if $VERBOSE
  else
    formatador.display_line("[green]Converting scripts to text[/]") if $VERBOSE
    dump(:dump_yaml_file, dest_file, script_index, src_time, options)
    script_code.each {|file, code| dump(:dump_raw_file, file, code, src_time, options)}
  end
end

.serialize(version, direction, directory, options = {}) ⇒ Object

version

one of :ace, :vx, :xp

direction

one of :data_bin_to_text, :data_text_to_bin, :save_bin_to_text, :save_text_to_bin, :scripts_bin_to_text, :scripts_text_to_bin, :all_text_to_bin, :all_bin_to_text

directory

directory that project file is in

options

:force - ignores file dates when converting (default false) :round_trip - create yaml data that matches original marshalled data skips

data cleanup operations (default false)

:line_width - line width form YAML files, -1 for no line width limit

(default 130)

:table_width - maximum number of entries per row for table data, -1 for no

table row limit (default 20)


293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
# File 'lib/RGSS/serialize.rb', line 293

def self.serialize(version, direction, directory, options = {})
  raise "#{directory} not found" unless File.exist?(directory)

  setup_classes(version, options)
  options = options.clone()
  options[:sort] = true if [:vx, :xp].include?(version)
  options[:flow_classes] = FLOW_CLASSES
  options[:line_width] ||= 130

  table_width = options[:table_width]
  RGSS::reset_const(Table, :MAX_ROW_LENGTH, table_width ? table_width : 20)

  base = File.realpath(directory)

  dirs = {
    :base   => base,
    :data   => get_data_directory(base),
    :yaml   => get_yaml_directory(base),
    :script => get_script_directory(base)
  }

  dirs.values.each do |d|
    FileUtils.mkdir(d) unless File.exists?(d)
  end

  exts = {
    :ace => ACE_DATA_EXT,
    :vx  => VX_DATA_EXT,
    :xp  => XP_DATA_EXT
  }

  yaml_scripts = SCRIPTS_BASE + YAML_EXT
  yaml = {
    :directory => dirs[:yaml],
    :exclude   => [yaml_scripts],
    :ext       => YAML_EXT,
    :load_file => :load_yaml_file,
    :dump_file => :dump_yaml_file,
    :load_save => :load_yaml_file,
    :dump_save => :dump_yaml_file
  }

  scripts = SCRIPTS_BASE + exts[version]
  data = {
    :directory => dirs[:data],
    :exclude   => [scripts],
    :ext       => exts[version],
    :load_file => :load_data_file,
    :dump_file => :dump_data_file,
    :load_save => :load_save,
    :dump_save => :dump_save
  }

  if options[:database].nil? or options[:database].downcase == 'scripts'
    convert_scripts = true
  else
    convert_scripts = false
  end
  if options[:database].nil? or options[:database].downcase == 'saves'
    convert_saves = true
  else
    convert_saves = false
  end

  case direction
  when :data_bin_to_text
    convert(data, yaml, options)
    scripts_to_text(dirs, scripts, yaml_scripts, options) if convert_scripts
  when :data_text_to_bin
    convert(yaml, data, options)
    scripts_to_binary(dirs, yaml_scripts, scripts, options) if convert_scripts
  when :save_bin_to_text
    convert_saves(base, data, yaml, options) if convert_saves
  when :save_text_to_bin
    convert_saves(base, yaml, data, options) if convert_saves
  when :scripts_bin_to_text
    scripts_to_text(dirs, scripts, yaml_scripts, options) if convert_scripts
  when :scripts_text_to_bin
    scripts_to_binary(dirs, yaml_scripts, scripts, options) if convert_scripts
  when :all_bin_to_text
    convert(data, yaml, options)
    scripts_to_text(dirs, scripts, yaml_scripts, options) if convert_scripts
    convert_saves(base, data, yaml, options) if convert_saves
  when :all_text_to_bin
    convert(yaml, data, options)
    scripts_to_binary(dirs, yaml_scripts, scripts, options) if convert_scripts
    convert_saves(base, yaml, data, options) if convert_saves
  else
    raise "Unrecognized direction :#{direction}"
  end
end

.setup_classes(version, options) ⇒ Object



236
237
238
239
240
241
# File 'lib/RGSS.rb', line 236

def self.setup_classes(version, options)
  setup_system(version, options)
  setup_interpreter(version)
  setup_event_command(version, options)
  BasicCoder.set_ivars_methods(version)
end

.setup_event_command(version, options) ⇒ Object



224
225
226
227
228
229
230
231
232
233
234
# File 'lib/RGSS.rb', line 224

def self.setup_event_command(version, options)
  # format event commands to flow style for the event codes that aren't move commands

  if options[:round_trip]
    reset_method(RPG::EventCommand, :clean, ->{})
  else
    reset_method(RPG::EventCommand, :clean, ->{
                             @parameters[0].rstrip! if @code == 401
                           })
  end
  reset_const(RPG::EventCommand, :MOVE_LIST_CODE, version == :xp ? 209 : 205)
end

.setup_interpreter(version) ⇒ Object



209
210
211
212
213
214
215
216
217
218
219
220
221
222
# File 'lib/RGSS.rb', line 209

def self.setup_interpreter(version)
  # Game_Interpreter is marshalled differently in VX Ace

  if version == :ace
    reset_method(Game_Interpreter, :marshal_dump, ->{
                             return @data
                           })
    reset_method(Game_Interpreter, :marshal_load, ->(obj) {
                             @data = obj
                           })
  else
      remove_defined_method(Game_Interpreter, :marshal_dump)
    remove_defined_method(Game_Interpreter, :marshal_load)
  end
end

.setup_system(version, options) ⇒ Object



188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
# File 'lib/RGSS.rb', line 188

def self.setup_system(version, options)
  # convert variable and switch name arrays to a hash when serialized

  # if round_trip isn't set change version_id to fixed number

  if options[:round_trip]
    iso = ->(val) { return val }
    reset_method(RPG::System, :reduce_string, iso)
    reset_method(RPG::System, :map_version, iso)
    reset_method(Game_System, :map_version, iso)
  else
    reset_method(RPG::System, :reduce_string, ->(str) {
                             return nil if str.nil?
                             stripped = str.strip
                             return stripped.empty? ? nil : stripped
                           })
    # These magic numbers should be different. If they are the same, the saved version

    # of the map in save files will be used instead of any updated version of the map

    reset_method(RPG::System, :map_version, ->(ignored) { return 12345678 })
    reset_method(Game_System, :map_version, ->(ignored) { return 87654321 })
  end
end