Initial commit

This commit is contained in:
Madeline Lim 2018-05-19 21:29:39 -07:00
commit ca3c5efbfe
3 changed files with 305 additions and 0 deletions

1
README.md Normal file
View File

@ -0,0 +1 @@
Celeste Reverse Engineering tools in Ruby

153
celeste_map_reader.rb Normal file
View File

@ -0,0 +1,153 @@
require './rom'
# Value Types
# :boolean, :u8, :s16, :s32, :float, :lookup, :bin, :rle
class Element
attr_accessor :package, :name, :attributes, :children, :attributes_value_types;
def initialize
@attributes = {}
@attributes_value_types = {}
@children = []
@package = nil
@name = nil
end
def inspect
disp
end
def disp pre=0
fend = children.size == 0 ? (attributes.size == 0 ? " />" : "/>") : ">"
pres = ' ' * pre
fstart = if attributes.size == 0
"#{pres}<#{name}#{fend}"
else
"#{pres}<#{name}\n#{attributes_disp(pre)}\n#{pres}#{fend}"
end
if children.size > 0
"#{fstart}\n#{children_disp(pre)}#{pres}</#{name}>\n"
else
"#{fstart}\n"
end
end
def attributes_disp pre=0
pres = ' ' * (pre + 2)
attributes.map do |k, v|
value_type = attributes_value_types[k]
case value_type
when :boolean
if v
"#{pres}#{k}"
else
"#{pres}#{k}={#{v}}"
end
when :u8, :s16, :s32, :float
"#{pres}#{k}={#{v}}"
when :lookup
"#{pres}#{k}=\"#{v}\""
when :bin
"#{pres}#{k}={bin#{v.inspect}}"
when :rle
"#{pres}#{k}={rle#{v.inspect}}"
end
end.join("\n")
end
def children_disp pre=0
children.map { |c| c.disp(pre + 2) }.join("\n")
end
def get_children_by_name child_name
children.select { |c| c.name == child_name }
end
def find_child_by_name child_name
children.detect { |c| c.name == child_name }
end
def [] name
attributes[name.to_s]
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
end
def read_element pre=0
element = Element.new
element.name = string_lookup[rom.read_u16_le]
# puts "#{" " * pre}<#{element.name}"
rom.read_byte.times do |i|
key = string_lookup[rom.read_u16_le]
value_type_enc = rom.read_byte
value = nil
value_type = nil
case value_type_enc
when 0
value = rom.read_bool
if value
# puts "#{" " * (pre + 2)}#{key}"
else
# puts "#{" " * (pre + 2)}#{key}={#{value}}"
end
value_type = :boolean
when 1
value = rom.read_byte
# puts "#{" " * (pre + 2)}#{key}={#{value}}"
value_type = :u8
when 2
value = rom.read_s16_le
# puts "#{" " * (pre + 2)}#{key}={#{value}}"
value_type = :s16
when 3
value = rom.read_s32_le
# puts "#{" " * (pre + 2)}#{key}={#{value}}"
value_type = :s32
when 4
value = rom.read_f32_le
# puts "#{" " * (pre + 2)}#{key}={#{value}}"
value_type = :float
when 5
value = string_lookup[rom.read_u16_le]
# puts "#{" " * (pre + 2)}#{key}=\"#{value}\""
value_type = :lookup
when 6
count = rom.read_varlen_le
base = rom.tell
bin = []
while rom.tell < base + count
bin << rom.read_byte
end
# value = rom.read_str_varlen
# # puts "#{" " * (pre + 2)}#{key}=\"#{value}\""
# puts "#{" " * (pre + 2)}#{key}={bin#{bin.inspect}}"
value_type = :bin
when 7
count = rom.read_u16_le
base = rom.tell
bin = []
while rom.tell < base + count
num = rom.read_byte
val = rom.read_byte
num.times { bin << val }
end
value = bin
# puts "#{" " * (pre + 2)}#{key}={rle#{bin.inspect}}"
value_type = :rle
end
element.attributes[key] = value
element.attributes_value_types[key] = value_type
end
# puts "#{" " * pre}>"
rom.read_u16_le.times do |j|
element.children << read_element(pre + 2)
end
# puts "#{" " * pre}</#{element.name}>"
element
end
def inspect
"<Bin #{package} />"
end
end

151
rom.rb Normal file
View File

@ -0,0 +1,151 @@
# General purpose binary reader
# 8 16 32 64
# C S L Q - unsigned
# c s l q - unsigned
# < LE
# > BE
# A - binary string
# z - null terminated
# H - hex string
class ROM
def initialize str
@rom = str
@cur = 0
@base = 0
end
def self.from_file fn
data = File.open(fn, "rb") { |io| io.read }
ROM.new data
end
def set_base pos
@base = pos
end
def seek pos
@cur = pos + @base
end
def seek_rel pos
@cur += pos
end
def tell
@cur - @base
end
def read_str len
r = @rom[@cur..(@cur + len - 1)].unpack("A#{len}").first
@cur += len
r
end
def read_str_varlen
len = read_varlen_le
r = @rom[@cur..(@cur + len - 1)].unpack("A#{len}").first
@cur += len
r
end
def read_byte
r = @rom[@cur].ord
@cur += 1
r
end
def read_bool
read_byte == 1
end
def read_s8
r = @rom[@cur].unpack("c").first
@cur += 1
r
end
def read_u16_le
r = @rom[@cur..(@cur + 3)].unpack('S<').first
@cur += 2
r
end
def read_s16_le
r = @rom[@cur..(@cur + 3)].unpack('s<').first
@cur += 2
r
end
def read_u16_be
r = @rom[@cur..(@cur + 3)].unpack('S>').first
@cur += 2
r
end
def read_u32_le
r = @rom[@cur..(@cur + 3)].unpack('L<').first
@cur += 4
r
end
def read_s32_le
r = @rom[@cur..(@cur + 3)].unpack('l<').first
@cur += 4
r
end
def read_u32_be
r = @rom[@cur..(@cur + 3)].unpack('L>').first
@cur += 4
r
end
def read_f32_le
r = @rom[@cur..(@cur + 3)].unpack('e').first
@cur += 4
r
end
def read_bin len
r = @rom[@cur..(@cur + len - 1)]
@cur += len
r
end
def read_binswap len
togo = len
bin_data = "".b
while togo > 0
r = @rom[@cur..(@cur + 3)].unpack('L>')
bin_data += r.pack('L<')
@cur += 4
togo -= 4
end
bin_data
end
def read_varlen_le
val = 0
r = read_byte
val = r & 0x7F
return val if r < 0x80
r = read_byte << 7
val + r
end
def read_varlen_be
val = 0
r = read_byte
val = r & 0x7F
return val if r < 0x80
r = read_byte
val = (val << 8) + r
val
end
def msg str
puts "%08X(%08X): %s" % [@cur, tell, str]
end
end