Add write bin and JSON read/write

This commit is contained in:
Madeline Lim 2018-05-20 00:09:51 -07:00
parent c91b1799cb
commit 664c684b79
1 changed files with 161 additions and 22 deletions

View File

@ -1,5 +1,6 @@
require './rom'
require './binwriter'
require 'json'
# Value Types
# :boolean, :u8, :s16, :s32, :float, :lookup, :bin, :rle
@ -63,22 +64,154 @@ class Element
def [] name
attributes[name.to_s]
end
def to_h
{
'name' => name,
'package' => package,
'attributes' => attributes,
'attribute_types' => attributes_value_types,
'children' => children.map(&:to_h)
}
end
def strings
strs = []
strs << name
attributes.each do |k, v|
strs << k
strs << v if attributes_value_types[k].to_sym == :lookup
end
children.each do |c|
strs += c.strings
end
strs.compact.uniq
end
def self.from_h obj
element = self.new
element.name = obj['name']
element.package = obj['package']
element.attributes = obj['attributes']
element.attributes_value_types = obj['attribute_types']
element.children = obj['children'].map { |c| from_h c }
element
end
end
class CelesteMapReader
attr_accessor :rom, :package, :string_lookup, :root
def initialize fn
@rom = ROM.from_file(fn)
raise "Not a celeste map" unless rom.read_str_varlen == 'CELESTE MAP'
@package = rom.read_str_varlen
@string_lookup = (0...rom.read_u16_le).map { rom.read_str_varlen }
@root = read_element
@root.package = @package
attr_accessor :debug, :rom, :package, :string_lookup, :root, :writer
def initialize fn, fmt: :bin, debug: false
@debug = debug
case fmt
when :bin
@rom = ROM.from_file(fn)
raise "Not a celeste map" unless rom.read_str_varlen == 'CELESTE MAP'
@package = rom.read_str_varlen
@string_lookup = (0...rom.read_u16_le).map { rom.read_str_varlen }
@root = read_element
@root.package = @package
when :json
obj = File.open(fn, 'rb') { |f| JSON.parse(f.read) }
raise "Not a celeste map" unless obj['type'] == 'CELESTE MAP'
@root = Element.from_h(obj['root'])
@package = @root.package
else
raise "unknown fmt #{fmt}"
end
end
def write fn
@writer = BinWriter.new(fn)
@string_lookup = root.strings
writer.write_str_varlen 'CELESTE MAP'
writer.write_str_varlen root.package
writer.write_u16_le string_lookup.size
string_lookup.each do |s|
writer.write_str_varlen s
end
write_element root
writer.close
end
def write_json fn
File.open(fn, 'wb') do |f|
f.write JSON.pretty_generate(
obj = {
'type' => 'CELESTE MAP',
'root' => root.to_h
}
)
end
end
def write_element element
writer.write_u16_le string_lookup.index(element.name)
writer.write_byte element.attributes.size
element.attributes.each do |k, v|
writer.write_u16_le string_lookup.index(k)
vt = element.attributes_value_types[k]
case vt.to_sym
when :boolean
writer.write_byte 0
writer.write_bool v
when :u8
writer.write_byte 1
writer.write_byte v
when :s16
writer.write_byte 2
writer.write_s16_le v
when :s32
writer.write_byte 3
writer.write_s32_le v
when :float
writer.write_byte 4
writer.write_f32_le v
when :lookup
writer.write_byte 5
writer.write_u16_le string_lookup.index(v)
when :bin
writer.write_byte 6
writer.write_varlen_le v.size
v.each do |b|
writer.write_byte b
end
when :rle
writer.write_byte 7
rle = []
count = 0
lb = -1
v.each do |b|
if b != lb
if lb >= 0
rle << count
rle << lb
end
count = 0
lb = b
end
count += 1
end
if lb >= 0
rle << count
rle << lb
end
writer.write_u16_le rle.size
rle.each do |b|
writer.write_byte b
end
else
raise "unknown value type #{vt} for key #{k} with value #{v}"
end
end
writer.write_u16_le element.children.size
element.children.each do |child|
write_element child
end
end
def read_element pre=0
element = Element.new
element.name = string_lookup[rom.read_u16_le]
# puts "#{" " * pre}<#{element.name}"
dputs "#{" " * pre}<#{element.name}"
rom.read_byte.times do |i|
key = string_lookup[rom.read_u16_le]
value_type_enc = rom.read_byte
@ -88,30 +221,30 @@ class CelesteMapReader
when 0
value = rom.read_bool
if value
# puts "#{" " * (pre + 2)}#{key}"
dputs "#{" " * (pre + 2)}#{key}"
else
# puts "#{" " * (pre + 2)}#{key}={#{value}}"
dputs "#{" " * (pre + 2)}#{key}={#{value}}"
end
value_type = :boolean
when 1
value = rom.read_byte
# puts "#{" " * (pre + 2)}#{key}={#{value}}"
dputs "#{" " * (pre + 2)}#{key}={#{value}}"
value_type = :u8
when 2
value = rom.read_s16_le
# puts "#{" " * (pre + 2)}#{key}={#{value}}"
dputs "#{" " * (pre + 2)}#{key}={#{value}}"
value_type = :s16
when 3
value = rom.read_s32_le
# puts "#{" " * (pre + 2)}#{key}={#{value}}"
dputs "#{" " * (pre + 2)}#{key}={#{value}}"
value_type = :s32
when 4
value = rom.read_f32_le
# puts "#{" " * (pre + 2)}#{key}={#{value}}"
dputs "#{" " * (pre + 2)}#{key}={#{value}}"
value_type = :float
when 5
value = string_lookup[rom.read_u16_le]
# puts "#{" " * (pre + 2)}#{key}=\"#{value}\""
dputs "#{" " * (pre + 2)}#{key}=\"#{value}\""
value_type = :lookup
when 6
count = rom.read_varlen_le
@ -121,8 +254,9 @@ class CelesteMapReader
bin << rom.read_byte
end
# value = rom.read_str_varlen
# # puts "#{" " * (pre + 2)}#{key}=\"#{value}\""
# puts "#{" " * (pre + 2)}#{key}={bin#{bin.inspect}}"
dputs "#{" " * (pre + 2)}#{key}=\"#{value}\""
dputs "#{" " * (pre + 2)}#{key}={bin#{bin.inspect}}"
value = bin
value_type = :bin
when 7
count = rom.read_u16_le
@ -134,20 +268,25 @@ class CelesteMapReader
num.times { bin << val }
end
value = bin
# puts "#{" " * (pre + 2)}#{key}={rle#{bin.inspect}}"
dputs "#{" " * (pre + 2)}#{key}={rle#{bin.inspect}}"
value_type = :rle
else
raise "unknown value type byte #{value_type_enc} for key #{key}"
end
element.attributes[key] = value
element.attributes_value_types[key] = value_type
end
# puts "#{" " * pre}>"
dputs "#{" " * pre}>"
rom.read_u16_le.times do |j|
element.children << read_element(pre + 2)
end
# puts "#{" " * pre}</#{element.name}>"
dputs "#{" " * pre}</#{element.name}>"
element
end
def inspect
"<Bin #{package} />"
end
def dputs s
puts s if @debug
end
end