mirror of
https://github.com/maddievision/Celestial.git
synced 2024-08-14 23:55:37 +00:00
Merge branch 'macros' of github.com:deltabouche/celestial.rb
This commit is contained in:
commit
1ad8bad443
7 changed files with 537 additions and 286 deletions
3
Gemfile
Normal file
3
Gemfile
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
source 'https://rubygems.org'
|
||||||
|
|
||||||
|
gem 'bin_tools'
|
13
Gemfile.lock
Normal file
13
Gemfile.lock
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
GEM
|
||||||
|
remote: https://rubygems.org/
|
||||||
|
specs:
|
||||||
|
bin_tools (0.0.3)
|
||||||
|
|
||||||
|
PLATFORMS
|
||||||
|
ruby
|
||||||
|
|
||||||
|
DEPENDENCIES
|
||||||
|
bin_tools
|
||||||
|
|
||||||
|
BUNDLED WITH
|
||||||
|
1.16.1
|
123
binwriter.rb
123
binwriter.rb
|
@ -1,123 +0,0 @@
|
||||||
16# 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 BinWriter
|
|
||||||
def initialize fn
|
|
||||||
@f = File.new fn, "wb"
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.open fn, &block
|
|
||||||
f = BinWriter.new fn
|
|
||||||
block.call f
|
|
||||||
f.close
|
|
||||||
end
|
|
||||||
|
|
||||||
def seek pos
|
|
||||||
@f.seek pos
|
|
||||||
end
|
|
||||||
|
|
||||||
def tell
|
|
||||||
@f.tell
|
|
||||||
end
|
|
||||||
|
|
||||||
def write_str str
|
|
||||||
@f.write str
|
|
||||||
end
|
|
||||||
|
|
||||||
def write_str_varlen str
|
|
||||||
write_varlen_le str.size
|
|
||||||
@f.write str
|
|
||||||
end
|
|
||||||
|
|
||||||
def write_byte val
|
|
||||||
@f.write [val].pack("C")
|
|
||||||
end
|
|
||||||
|
|
||||||
def write_bool val
|
|
||||||
write_byte val ? 1 : 0
|
|
||||||
end
|
|
||||||
|
|
||||||
def write_u16_le val
|
|
||||||
@f.write [val].pack("S<")
|
|
||||||
end
|
|
||||||
|
|
||||||
def write_s16_le val
|
|
||||||
@f.write [val].pack("s<")
|
|
||||||
end
|
|
||||||
|
|
||||||
def write_u16_be val
|
|
||||||
@f.write [val].pack("S>")
|
|
||||||
end
|
|
||||||
|
|
||||||
def write_u32_le val
|
|
||||||
@f.write [val].pack("L<")
|
|
||||||
end
|
|
||||||
|
|
||||||
def write_s32_le val
|
|
||||||
@f.write [val].pack("l<")
|
|
||||||
end
|
|
||||||
|
|
||||||
def write_u32_be val
|
|
||||||
@f.write [val].pack("L>")
|
|
||||||
end
|
|
||||||
|
|
||||||
def write_f32_le val
|
|
||||||
@f.write [val].pack("e")
|
|
||||||
end
|
|
||||||
|
|
||||||
def write_u24_be val
|
|
||||||
write_byte val >> 16
|
|
||||||
write_u16_be val & 0xFFFF
|
|
||||||
end
|
|
||||||
|
|
||||||
def write_bin str
|
|
||||||
@f.write str
|
|
||||||
end
|
|
||||||
|
|
||||||
def write_varlen_be val
|
|
||||||
out = [val & 0x7F]
|
|
||||||
val = val >> 7
|
|
||||||
while val > 0
|
|
||||||
out << (val & 0x7f) + 0x80
|
|
||||||
val = val >> 7
|
|
||||||
end
|
|
||||||
out.reverse.each do |x|
|
|
||||||
write_byte x
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def write_varlen_le val
|
|
||||||
out = [val & 0x7F]
|
|
||||||
val = val >> 7
|
|
||||||
while val > 0
|
|
||||||
out << (val & 0x7f)
|
|
||||||
val = val >> 7
|
|
||||||
end
|
|
||||||
out.each_with_index do |x, i|
|
|
||||||
write_byte(x + ((i == out.size - 1) ? 0 : 0x80))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def write_binswap str
|
|
||||||
togo = str.size
|
|
||||||
pos = 0
|
|
||||||
while togo > 0
|
|
||||||
r = @rom[pos..(pos+3)].unpack('L>')
|
|
||||||
d = r.pack('L<')
|
|
||||||
write_bin d
|
|
||||||
pos += 4
|
|
||||||
togo -= 4
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def close
|
|
||||||
@f.close
|
|
||||||
end
|
|
||||||
end
|
|
390
celeste_map.rb
390
celeste_map.rb
|
@ -1,9 +1,11 @@
|
||||||
require './rom'
|
require 'bin_tools'
|
||||||
require './binwriter'
|
|
||||||
require 'json'
|
require 'json'
|
||||||
|
|
||||||
# Value Types
|
# Value Types
|
||||||
# :boolean, :u8, :s16, :s32, :float, :lookup, :bin, :rle
|
# :boolean, :u8, :s16, :s32, :float, :lookup, :bin, :rle
|
||||||
|
|
||||||
|
$ElementAutoParser = {}
|
||||||
|
|
||||||
class Element
|
class Element
|
||||||
attr_accessor :package, :name, :attributes, :children, :attributes_value_types;
|
attr_accessor :package, :name, :attributes, :children, :attributes_value_types;
|
||||||
def initialize
|
def initialize
|
||||||
|
@ -37,18 +39,18 @@ class Element
|
||||||
case value_type
|
case value_type
|
||||||
when :boolean
|
when :boolean
|
||||||
if v
|
if v
|
||||||
"#{pres}#{k}"
|
"#{pres}#{k}(#{value_type})"
|
||||||
else
|
else
|
||||||
"#{pres}#{k}={#{v}}"
|
"#{pres}#{k}(#{value_type})={#{v}}"
|
||||||
end
|
end
|
||||||
when :u8, :s16, :s32, :float
|
when :u8, :s16, :s32, :float
|
||||||
"#{pres}#{k}={#{v}}"
|
"#{pres}#{k}(#{value_type})={#{v}}"
|
||||||
when :lookup
|
when :lookup
|
||||||
"#{pres}#{k}=\"#{v}\""
|
"#{pres}#{k}(#{value_type})=\"#{v}\""
|
||||||
when :bin
|
when :bin
|
||||||
"#{pres}#{k}={bin#{v.inspect}}"
|
"#{pres}#{k}(#{value_type})={#{v.inspect}}"
|
||||||
when :rle
|
when :rle
|
||||||
"#{pres}#{k}={rle#{v.inspect}}"
|
"#{pres}#{k}(#{value_type})={#{v.inspect}}"
|
||||||
end
|
end
|
||||||
end.join("\n")
|
end.join("\n")
|
||||||
end
|
end
|
||||||
|
@ -64,6 +66,35 @@ class Element
|
||||||
def [] name
|
def [] name
|
||||||
attributes[name.to_s]
|
attributes[name.to_s]
|
||||||
end
|
end
|
||||||
|
def set_attribute name, value_type, value
|
||||||
|
attributes[name.to_s] = value
|
||||||
|
attributes_value_types[name.to_s] = value_type
|
||||||
|
end
|
||||||
|
def self.auto_type num
|
||||||
|
if (num >= 0 && num <= 255)
|
||||||
|
:u8
|
||||||
|
elsif num >= -32768 && num < 32768
|
||||||
|
:s16
|
||||||
|
else
|
||||||
|
:s32
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_num_attribute name, num
|
||||||
|
num = num.to_i(16) if num.is_a? String
|
||||||
|
set_attribute name, self.class.auto_type(num), num
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def set_lookup_attribute name, str
|
||||||
|
set_attribute name, :lookup, str
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_boolean_attribute name, val
|
||||||
|
set_attribute name, :boolean, val
|
||||||
|
end
|
||||||
|
|
||||||
def to_h
|
def to_h
|
||||||
{
|
{
|
||||||
'name' => name,
|
'name' => name,
|
||||||
|
@ -91,18 +122,51 @@ class Element
|
||||||
element.package = obj['package']
|
element.package = obj['package']
|
||||||
element.attributes = obj['attributes']
|
element.attributes = obj['attributes']
|
||||||
element.attributes_value_types = obj['attribute_types']
|
element.attributes_value_types = obj['attribute_types']
|
||||||
element.children = obj['children'].map { |c| from_h c }
|
extra_children = []
|
||||||
|
element.children = obj['children'].map do |c|
|
||||||
|
parser = nil
|
||||||
|
c.each do |k, v|
|
||||||
|
parser = $ElementAutoParser[k] if $ElementAutoParser[k]
|
||||||
|
end
|
||||||
|
z = if parser
|
||||||
|
parser.from_h c
|
||||||
|
else
|
||||||
|
if c.has_key?('repeat')
|
||||||
|
clones = (0..c['repeat']).each do |rr|
|
||||||
|
nn = c.clone
|
||||||
|
nn['attributes'] = nn['attributes'].clone
|
||||||
|
nn['attribute_types'] = nn['attribute_types'].clone
|
||||||
|
c['repeat_attributes'].each do |k, v|
|
||||||
|
nn['attributes'][k] += (rr + 1) * v
|
||||||
|
nn['attribute_types'][k] = 's16'
|
||||||
|
end
|
||||||
|
extra_children << from_h(nn)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
from_h c
|
||||||
|
end
|
||||||
|
if z.is_a? Array
|
||||||
|
z.each_with_index do |zz, i|
|
||||||
|
extra_children << zz if i > 0
|
||||||
|
end
|
||||||
|
z.first
|
||||||
|
else
|
||||||
|
z
|
||||||
|
end
|
||||||
|
end
|
||||||
|
element.children += extra_children
|
||||||
element
|
element
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
class CelesteMap
|
class CelesteMap
|
||||||
attr_accessor :debug, :rom, :package, :string_lookup, :root, :writer
|
attr_accessor :debug, :rom, :package, :string_lookup, :root, :writer
|
||||||
def initialize fn, fmt: :bin, debug: false
|
def initialize fn, fmt: :bin, debug: false
|
||||||
@debug = debug
|
@debug = debug
|
||||||
case fmt
|
case fmt
|
||||||
when :bin
|
when :bin
|
||||||
@rom = ROM.from_file(fn)
|
@rom = BinTools::Reader.from_file(fn)
|
||||||
raise "Not a celeste map" unless rom.read_str_varlen == 'CELESTE MAP'
|
raise "Not a celeste map" unless rom.read_str_varlen == 'CELESTE MAP'
|
||||||
@package = rom.read_str_varlen
|
@package = rom.read_str_varlen
|
||||||
@string_lookup = (0...rom.read_u16_le).map { rom.read_str_varlen }
|
@string_lookup = (0...rom.read_u16_le).map { rom.read_str_varlen }
|
||||||
|
@ -119,7 +183,7 @@ class CelesteMap
|
||||||
end
|
end
|
||||||
|
|
||||||
def write fn
|
def write fn
|
||||||
@writer = BinWriter.new(fn)
|
@writer = BinTools::BinWriter.new(fn)
|
||||||
@string_lookup = root.strings
|
@string_lookup = root.strings
|
||||||
writer.write_str_varlen 'CELESTE MAP'
|
writer.write_str_varlen 'CELESTE MAP'
|
||||||
writer.write_str_varlen root.package
|
writer.write_str_varlen root.package
|
||||||
|
@ -290,3 +354,307 @@ class CelesteMap
|
||||||
puts s if @debug
|
puts s if @debug
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class NodeElement < Element
|
||||||
|
def self.with_xy x, y
|
||||||
|
e = self.new
|
||||||
|
e.name = 'node'
|
||||||
|
e.set_num_attribute 'x', x
|
||||||
|
e.set_num_attribute 'y', y
|
||||||
|
e
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class EntityElement < Element
|
||||||
|
def self.gen_id key
|
||||||
|
@gen_id = {} unless @gen_id
|
||||||
|
@gen_id[key] = 0 unless @gen_id[key]
|
||||||
|
@gen_id[key] = @gen_id[key] + 1
|
||||||
|
@gen_id[key]
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.reset_id key
|
||||||
|
@gen_id = {} unless @gen_id
|
||||||
|
@gen_id[key] = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_gen_id key
|
||||||
|
set_num_attribute 'id', self.class.gen_id(key)
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_auto_name obj
|
||||||
|
self.name = obj['__entity']
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_auto_num_attribute obj, name
|
||||||
|
set_num_attribute name, obj[name]
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_auto_lookup_attribute obj, name
|
||||||
|
set_lookup_attribute name, obj[name]
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_auto_boolean_attribute obj, name
|
||||||
|
set_boolean_attribute name, obj[name]
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_auto_xy obj, oX = 0, oY = 0
|
||||||
|
x = obj['x']
|
||||||
|
y = obj['y']
|
||||||
|
x = x.to_i(16) if x.is_a? String
|
||||||
|
y = y.to_i(16) if y.is_a? String
|
||||||
|
|
||||||
|
set_num_attribute 'x', x + oX
|
||||||
|
set_num_attribute 'y', y + oY
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_auto_width obj
|
||||||
|
set_auto_num_attribute obj, 'width'
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_auto_height obj
|
||||||
|
set_auto_num_attribute obj, 'height'
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_auto_wh obj
|
||||||
|
set_auto_width obj
|
||||||
|
set_auto_height obj
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_origin x, y
|
||||||
|
set_num_attribute 'originX', x
|
||||||
|
set_num_attribute 'originY', y
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.parse_player obj
|
||||||
|
e = self.new
|
||||||
|
e.set_auto_name obj
|
||||||
|
e.set_gen_id 'entity'
|
||||||
|
e.set_auto_xy obj
|
||||||
|
e.set_num_attribute 'width', 8
|
||||||
|
e.set_origin 4, 8
|
||||||
|
e
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.parse_badeline_chaser obj
|
||||||
|
e = self.new
|
||||||
|
e.name = 'darkChaser'
|
||||||
|
e.set_gen_id 'entity'
|
||||||
|
e.set_auto_xy obj
|
||||||
|
e.set_origin 4, 8
|
||||||
|
e
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.parse_booster obj
|
||||||
|
e = self.new
|
||||||
|
e.set_auto_name obj
|
||||||
|
e.set_gen_id 'entity'
|
||||||
|
e.set_auto_xy obj
|
||||||
|
e.set_auto_boolean_attribute obj, 'red'
|
||||||
|
e.set_origin 4, 4
|
||||||
|
e
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.parse_move_block obj
|
||||||
|
e = self.new
|
||||||
|
e.name = 'moveBlock'
|
||||||
|
e.set_gen_id 'entity'
|
||||||
|
e.set_auto_xy obj
|
||||||
|
e.set_auto_wh obj
|
||||||
|
e.set_auto_lookup_attribute obj, 'direction'
|
||||||
|
e.set_auto_boolean_attribute obj, 'canSteer'
|
||||||
|
e.set_auto_boolean_attribute obj, 'fast'
|
||||||
|
e.set_origin 0, 0
|
||||||
|
e
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.parse_black_gem obj
|
||||||
|
e = self.new
|
||||||
|
e.name = 'blackGem'
|
||||||
|
e.set_gen_id 'entity'
|
||||||
|
e.set_auto_xy obj
|
||||||
|
e.set_auto_boolean_attribute obj, 'removeCameraTriggers'
|
||||||
|
e.set_origin 6, 6
|
||||||
|
e
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.parse_golden_berry obj
|
||||||
|
e = self.new
|
||||||
|
e.name = 'goldenBerry'
|
||||||
|
e.set_gen_id 'entity'
|
||||||
|
e.set_auto_xy obj
|
||||||
|
e.set_origin 8, 8
|
||||||
|
e
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.parse_dream_block obj
|
||||||
|
e = self.new
|
||||||
|
e.name = 'dreamBlock'
|
||||||
|
e.set_gen_id 'entity'
|
||||||
|
e.set_auto_xy obj
|
||||||
|
e.set_auto_wh obj
|
||||||
|
e.set_auto_boolean_attribute obj, 'fastMoving'
|
||||||
|
e.set_origin 0, 0
|
||||||
|
e
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.parse_kevin obj
|
||||||
|
e = self.new
|
||||||
|
e.name = 'crushBlock'
|
||||||
|
e.set_gen_id 'entity'
|
||||||
|
e.set_auto_xy obj
|
||||||
|
e.set_auto_wh obj
|
||||||
|
e.set_auto_lookup_attribute obj, 'axes'
|
||||||
|
e.set_auto_boolean_attribute obj, 'chillout'
|
||||||
|
e.set_origin 0, 0
|
||||||
|
e
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.parse_water obj
|
||||||
|
e = self.new
|
||||||
|
e.name = 'water'
|
||||||
|
e.set_gen_id 'entity'
|
||||||
|
e.set_auto_xy obj
|
||||||
|
e.set_auto_wh obj
|
||||||
|
e.set_auto_boolean_attribute obj, 'steamy'
|
||||||
|
e.set_auto_boolean_attribute obj, 'hasBottom'
|
||||||
|
e.set_origin 0, 0
|
||||||
|
e
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.parse_jump_thru obj
|
||||||
|
e = self.new
|
||||||
|
e.name = 'jumpThru'
|
||||||
|
e.set_gen_id 'entity'
|
||||||
|
e.set_auto_xy obj
|
||||||
|
e.set_auto_width obj
|
||||||
|
e.set_origin 0, 0
|
||||||
|
e
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.parse_zip_mover obj
|
||||||
|
e = self.new
|
||||||
|
e.name = 'zipMover'
|
||||||
|
e.set_gen_id 'entity'
|
||||||
|
e.set_auto_xy obj
|
||||||
|
e.set_auto_wh obj
|
||||||
|
e.set_origin 0, 0
|
||||||
|
e.children << NodeElement.with_xy(obj['x2'], obj['y2'])
|
||||||
|
e
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.parse_switch_gate obj
|
||||||
|
e = self.new
|
||||||
|
e.name = 'switchGate'
|
||||||
|
e.set_gen_id 'entity'
|
||||||
|
e.set_auto_xy obj
|
||||||
|
e.set_auto_wh obj
|
||||||
|
e.set_auto_boolean_attribute obj, 'persistent'
|
||||||
|
e.set_auto_lookup_attribute obj, 'sprite'
|
||||||
|
e.set_origin 0, 0
|
||||||
|
e.children << NodeElement.with_xy(obj['x2'], obj['y2'])
|
||||||
|
e
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.parse_bumper obj
|
||||||
|
e = self.new
|
||||||
|
e.name = 'bigSpinner'
|
||||||
|
e.set_gen_id 'entity'
|
||||||
|
e.set_auto_xy obj
|
||||||
|
e.set_origin 16, 16
|
||||||
|
e
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.parse_cloud obj
|
||||||
|
e = self.new
|
||||||
|
e.set_auto_name obj
|
||||||
|
e.set_gen_id 'entity'
|
||||||
|
e.set_auto_xy obj
|
||||||
|
e.set_auto_boolean_attribute obj, 'fragile'
|
||||||
|
e.set_origin 16, 0
|
||||||
|
e
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.parse_spikes obj
|
||||||
|
e = self.new
|
||||||
|
e.name = 'spikes' + obj['direction']
|
||||||
|
e.set_gen_id 'entity'
|
||||||
|
e.set_auto_xy obj
|
||||||
|
e.set_auto_lookup_attribute obj, 'type'
|
||||||
|
case obj['direction']
|
||||||
|
when 'Up'
|
||||||
|
e.set_auto_width obj
|
||||||
|
e.set_origin 0, 4
|
||||||
|
when 'Down'
|
||||||
|
e.set_auto_width obj
|
||||||
|
e.set_origin 0, 0
|
||||||
|
when 'Left'
|
||||||
|
e.set_auto_height obj
|
||||||
|
e.set_origin 4, 0
|
||||||
|
when 'Right'
|
||||||
|
e.set_auto_height obj
|
||||||
|
e.set_origin 0, 0
|
||||||
|
end
|
||||||
|
e
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.parse_touch_switch obj
|
||||||
|
e = self.new
|
||||||
|
e.name = 'touchSwitch'
|
||||||
|
e.set_gen_id 'entity'
|
||||||
|
e.set_auto_xy obj
|
||||||
|
e.set_origin 4, 4
|
||||||
|
e
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.parse_spinner obj
|
||||||
|
nY = obj['nY'] || 1
|
||||||
|
nX = obj['nX'] || 1
|
||||||
|
dY = obj['dY'] || 16
|
||||||
|
dX = obj['dX'] || 16
|
||||||
|
elements = []
|
||||||
|
nY.times do |iY|
|
||||||
|
nX.times do |iX|
|
||||||
|
e = self.new
|
||||||
|
e.set_auto_name obj
|
||||||
|
e.set_gen_id 'entity'
|
||||||
|
e.set_auto_boolean_attribute obj, 'attachToSolid'
|
||||||
|
e.set_auto_xy obj, iX * dX, iY * dY
|
||||||
|
e.set_origin 8, 8
|
||||||
|
elements << e
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elements
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.from_h obj
|
||||||
|
self.send("parse_#{obj['__entity']}", obj)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
$ElementAutoParser['__entity'] = EntityElement
|
||||||
|
|
||||||
|
class SolidsElement < Element
|
||||||
|
def set_auto_xy obj, oX = 0, oY = 0
|
||||||
|
x = obj['offsetX'] || 0
|
||||||
|
y = obj['offsetY'] || 0
|
||||||
|
x = x.to_i(16) if x.is_a? String
|
||||||
|
y = y.to_i(16) if y.is_a? String
|
||||||
|
|
||||||
|
set_num_attribute 'offsetX', x + oX
|
||||||
|
set_num_attribute 'offsetY', y + oY
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.from_h obj
|
||||||
|
e = self.new
|
||||||
|
e.name = 'solids'
|
||||||
|
e.set_auto_xy obj
|
||||||
|
inner = obj['map'].map do |str|
|
||||||
|
str.split('').map { |ch| ch.ord == 32 && '0'.ord || ch.ord } + [10]
|
||||||
|
end.flatten
|
||||||
|
e.set_attribute 'innerText', :rle, inner
|
||||||
|
e
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
$ElementAutoParser['__solids'] = SolidsElement
|
||||||
|
|
|
@ -3,6 +3,9 @@ require "./celeste_map"
|
||||||
|
|
||||||
ARGV.each do |fn|
|
ARGV.each do |fn|
|
||||||
base = File.basename(fn, ".json")
|
base = File.basename(fn, ".json")
|
||||||
|
puts "Opening #{fn}"
|
||||||
a = CelesteMap.new(fn, fmt: :json)
|
a = CelesteMap.new(fn, fmt: :json)
|
||||||
a.write "bin/#{base}.bin"
|
outfn = "bin/#{base}.bin"
|
||||||
|
puts "Writing #{outfn}"
|
||||||
|
a.write outfn
|
||||||
end
|
end
|
||||||
|
|
138
report.json
Normal file
138
report.json
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
{
|
||||||
|
"report": [
|
||||||
|
"player",
|
||||||
|
"introCar",
|
||||||
|
"lamp",
|
||||||
|
"wire",
|
||||||
|
"introCrusher",
|
||||||
|
"jumpThru",
|
||||||
|
"flutterbird",
|
||||||
|
"bird",
|
||||||
|
"npc",
|
||||||
|
"bridge",
|
||||||
|
"hahaha",
|
||||||
|
"spikesUp",
|
||||||
|
"spikesLeft",
|
||||||
|
"goldenBerry",
|
||||||
|
"zipMover",
|
||||||
|
"spikesRight",
|
||||||
|
"crumbleBlock",
|
||||||
|
"refill",
|
||||||
|
"spring",
|
||||||
|
"strawberry",
|
||||||
|
"spikesDown",
|
||||||
|
"lightbeam",
|
||||||
|
"cassette",
|
||||||
|
"cassetteBlock",
|
||||||
|
"fallingBlock",
|
||||||
|
"fakeWall",
|
||||||
|
"dashBlock",
|
||||||
|
"checkpoint",
|
||||||
|
"bonfire",
|
||||||
|
"coverupWall",
|
||||||
|
"memorial",
|
||||||
|
"memorialTextController",
|
||||||
|
"birdForsakenCityGem",
|
||||||
|
"towerviewer",
|
||||||
|
"blackGem",
|
||||||
|
"touchSwitch",
|
||||||
|
"switchGate",
|
||||||
|
"dreamBlock",
|
||||||
|
"hanginglamp",
|
||||||
|
"floatingDebris",
|
||||||
|
"foregroundDebris",
|
||||||
|
"darkChaser",
|
||||||
|
"invisibleBarrier",
|
||||||
|
"exitBlock",
|
||||||
|
"payphone",
|
||||||
|
"dreammirror",
|
||||||
|
"rotateSpinner",
|
||||||
|
"door",
|
||||||
|
"redBlocks",
|
||||||
|
"yellowBlocks",
|
||||||
|
"resortLantern",
|
||||||
|
"greenBlocks",
|
||||||
|
"spinner",
|
||||||
|
"cobweb",
|
||||||
|
"soundSource",
|
||||||
|
"trackSpinner",
|
||||||
|
"blockField",
|
||||||
|
"resortmirror",
|
||||||
|
"waterfall",
|
||||||
|
"lockBlock",
|
||||||
|
"sinkingPlatform",
|
||||||
|
"key",
|
||||||
|
"triggerSpikesLeft",
|
||||||
|
"triggerSpikesRight",
|
||||||
|
"triggerSpikesUp",
|
||||||
|
"movingPlatform",
|
||||||
|
"trapdoor",
|
||||||
|
"water",
|
||||||
|
"clutterDoor",
|
||||||
|
"oshirodoor",
|
||||||
|
"colorSwitch",
|
||||||
|
"clutterCabinet",
|
||||||
|
"picoconsole",
|
||||||
|
"clothesline",
|
||||||
|
"triggerSpikesDown",
|
||||||
|
"friendlyGhost",
|
||||||
|
"resortRoofEnding",
|
||||||
|
"killbox",
|
||||||
|
"cloud",
|
||||||
|
"booster",
|
||||||
|
"cliffside_flag",
|
||||||
|
"moveBlock",
|
||||||
|
"cliffflag",
|
||||||
|
"ridgeGate",
|
||||||
|
"whiteblock",
|
||||||
|
"gondola",
|
||||||
|
"templeGate",
|
||||||
|
"dashSwitchH",
|
||||||
|
"torch",
|
||||||
|
"swapBlock",
|
||||||
|
"templeMirror",
|
||||||
|
"dashSwitchV",
|
||||||
|
"templeMirrorPortal",
|
||||||
|
"seeker",
|
||||||
|
"templeCrackedBlock",
|
||||||
|
"seekerStatue",
|
||||||
|
"seekerBarrier",
|
||||||
|
"conditionBlock",
|
||||||
|
"theoCrystalPedestal",
|
||||||
|
"theoCrystal",
|
||||||
|
"templeEye",
|
||||||
|
"theoCrystalHoldingBarrier",
|
||||||
|
"templeBigEyeball",
|
||||||
|
"playerSeeker",
|
||||||
|
"crushBlock",
|
||||||
|
"bigWaterfall",
|
||||||
|
"infiniteStar",
|
||||||
|
"reflectionHeartStatue",
|
||||||
|
"bigSpinner",
|
||||||
|
"badelineBoost",
|
||||||
|
"tentacles",
|
||||||
|
"finalBoss",
|
||||||
|
"finalBossFallingBlock",
|
||||||
|
"finalBossMovingBlock",
|
||||||
|
"plateau",
|
||||||
|
"starJumpBlock",
|
||||||
|
"SummitBackgroundManager",
|
||||||
|
"wallSpringLeft",
|
||||||
|
"wallSpringRight",
|
||||||
|
"summitgem",
|
||||||
|
"bridgeFixed",
|
||||||
|
"summitcheckpoint",
|
||||||
|
"summitGemManager",
|
||||||
|
"summitcloud",
|
||||||
|
"heartGemDoor",
|
||||||
|
"wallBooster",
|
||||||
|
"fireBall",
|
||||||
|
"bounceBlock",
|
||||||
|
"iceBlock",
|
||||||
|
"coreModeToggle",
|
||||||
|
"fireBarrier",
|
||||||
|
"risingLava",
|
||||||
|
"sandwichLava",
|
||||||
|
"coreMessage"
|
||||||
|
]
|
||||||
|
}
|
151
rom.rb
151
rom.rb
|
@ -1,151 +0,0 @@
|
||||||
# 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
|
|
Loading…
Reference in a new issue