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



139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/RGSS.rb', line 139

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



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

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

.convert(src, dest, options) ⇒ Object



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

def self.convert(src, dest, options)
  f = Formatador.new
  f.display_line("[green]#{options[:files].join('|')}")
  files = options[:files]
  files ||= Dir.entries(src[:directory])
  files = files.select { |file| File.extname(file) == 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



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

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

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



78
79
80
81
82
83
# File 'lib/RGSS/serialize.rb', line 78

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



48
49
50
51
52
53
# File 'lib/RGSS/serialize.rb', line 48

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



71
72
73
74
75
76
# File 'lib/RGSS/serialize.rb', line 71

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



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

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



55
56
57
58
59
60
# File 'lib/RGSS/serialize.rb', line 55

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



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

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

.get_data_directory(base, files) ⇒ Object



268
269
270
# File 'lib/RGSS.rb', line 268

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

.get_script_directory(base) ⇒ Object



276
277
278
# File 'lib/RGSS.rb', line 276

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

.get_yaml_directory(base) ⇒ Object



272
273
274
# File 'lib/RGSS.rb', line 272

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

.hash_to_array(hash) ⇒ Object



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

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

.inflate(str) ⇒ Object



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

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

.load(loader, file) ⇒ Object



160
161
162
163
164
165
# File 'lib/RGSS/serialize.rb', line 160

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

.load_data_file(file) ⇒ Object



85
86
87
88
89
# File 'lib/RGSS/serialize.rb', line 85

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

.load_raw_file(file) ⇒ Object



143
144
145
146
147
# File 'lib/RGSS/serialize.rb', line 143

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

.load_save(file) ⇒ Object



149
150
151
152
153
154
155
156
157
158
# File 'lib/RGSS/serialize.rb', line 149

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



91
92
93
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
# File 'lib/RGSS/serialize.rb', line 91

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



164
165
166
167
168
169
170
# File 'lib/RGSS.rb', line 164

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



238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
# File 'lib/RGSS/serialize.rb', line 238

def self.process_file(file, src_file, dest_file, dest_ext, loader, dumper, options)
  formatador = Formatador.new
  fbase = File.basename(file, File.extname(file))
  src_time = File.mtime(src_file)
  begin
    if !options[:force] && File.exist?(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
  rescue ArgumentError
    formatador.display_line("[_yellow_] Could not convert #{file} due to ArgumentError")
  end
end

.remove_defined_method(scope, name) ⇒ Object



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

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



134
135
136
137
# File 'lib/RGSS.rb', line 134

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



129
130
131
132
# File 'lib/RGSS.rb', line 129

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

.sanitize_filename(filename) ⇒ Object



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

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

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



209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
# File 'lib/RGSS/serialize.rb', line 209

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.exist?(src_file)
  check_time = !options[:force] && File.exist?(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.exist?(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



167
168
169
170
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
# File 'lib/RGSS/serialize.rb', line 167

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.exist?(src_file)
  script_entries = load(:load_data_file, src_file)
  check_time = !options[:force] && File.exist?(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.exist?(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
# 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, options[:files]),
    :yaml => get_yaml_directory(base),
    :script => get_script_directory(base)
  }

  dirs.values.each do |d|
    FileUtils.mkdir(d) unless File.exist?(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
  }

  convert_saves = options[:database][:saves]
  convert_scripts = options[:database][:scripts]

  case direction
    # below were not used in rv packer
  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
    # below are only options used by fusionpacker
  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



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

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



239
240
241
242
243
244
245
246
247
248
249
# File 'lib/RGSS.rb', line 239

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



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

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



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

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