.obj files are not LZO
This commit is contained in:
204
scripts/try-uncompress
Executable file
204
scripts/try-uncompress
Executable file
@@ -0,0 +1,204 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
require 'pp'
|
||||
|
||||
module Obj
|
||||
class Header
|
||||
SIZE = 5*4 # 5x 32-bit little-endian integers
|
||||
|
||||
attr_reader(
|
||||
:num_sprites, # Number of entries in the directory. Always dir_size/8?
|
||||
:dir_offset, # Offset of the sprite directory. Also the size of the main header
|
||||
:dir_size, # Number of bytes allocated to the sprite directory. Always num_sprites*8?
|
||||
:data_offset, # Number of bytes allocated to the sprites themselves
|
||||
:data_size # Total size of the sprite data
|
||||
# JUNGTIL.obj has 0x0000 0x0000 0x0000 following. Probably just padding to
|
||||
# allow the sprite directory to start at a word boundary.
|
||||
)
|
||||
|
||||
def self.parse(data)
|
||||
hdr = new(*data[0..SIZE - 1].unpack("V*"))
|
||||
pp hdr
|
||||
hdr.validate!(data.bytes.size)
|
||||
hdr
|
||||
end
|
||||
|
||||
def initialize(*entries)
|
||||
@num_sprites, @dir_offset, @dir_size, @data_offset, @data_size = *entries
|
||||
end
|
||||
|
||||
def validate!(overall_size)
|
||||
raise "Directory overlaps EOF" if overall_size < dir_size + dir_offset
|
||||
raise "Data overlaps EOF" if overall_size < data_offset + data_size
|
||||
raise "Bad dir_size or num_sprites" if num_sprites * DirEntry::SIZE != dir_size
|
||||
raise "Directory overlaps data" if data_range.cover?(dir_offset+1)
|
||||
raise "Data overlaps directory" if dir_range.cover?(data_offset+1)
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def dir_range
|
||||
dir_offset...(dir_offset+dir_size)
|
||||
end
|
||||
|
||||
def data_range
|
||||
data_offset...(data_offset+data_size)
|
||||
end
|
||||
end
|
||||
|
||||
class DirEntry
|
||||
SIZE = 8
|
||||
|
||||
attr_reader :rel_offset # Relative to the main header's data_offset
|
||||
attr_reader :sprite_size
|
||||
|
||||
def self.parse(rel_data)
|
||||
new(*rel_data.unpack("VV"))
|
||||
end
|
||||
|
||||
def initialize(rel_offset, sprite_size)
|
||||
@rel_offset = rel_offset
|
||||
@sprite_size = sprite_size
|
||||
end
|
||||
|
||||
def sprite_range
|
||||
rel_offset...(rel_offset+sprite_size)
|
||||
end
|
||||
end
|
||||
|
||||
class SpriteDir
|
||||
attr_reader :entries
|
||||
|
||||
def self.parse(rel_data)
|
||||
count = rel_data.bytes.size
|
||||
num_entries = count / DirEntry::SIZE
|
||||
trailing = count%DirEntry::SIZE
|
||||
|
||||
raise "SpriteDir block has #{trailing} trailing bytes" unless trailing == 0
|
||||
|
||||
entries = 0.upto(num_entries-1).map do |n|
|
||||
rel_offset = n * DirEntry::SIZE
|
||||
DirEntry.parse(rel_data.byteslice(rel_offset, DirEntry::SIZE))
|
||||
end
|
||||
|
||||
pp entries
|
||||
|
||||
new(entries)
|
||||
end
|
||||
|
||||
def initialize(entries)
|
||||
@entries = entries
|
||||
end
|
||||
|
||||
# Convert the directory into an Array of bytes. Until we work out how to
|
||||
# parse sprites, anyway...
|
||||
def realize(rel_data)
|
||||
entries.map { |entry| rel_data[entry.sprite_range] }
|
||||
end
|
||||
end
|
||||
|
||||
=begin
|
||||
SpriteHeader = Struct.new(
|
||||
:unknown0, # Possibly magic data? It's the same for every sprite in jungtil.obj
|
||||
:maybe_dimension, # Low nibble comes to 63 in jungtil.obj which would work for a 64x64 tile
|
||||
:size, # Number of bytes of pixel data following this header
|
||||
) do
|
||||
SIZE = 4*6 # Seems to be, anyway. Based on
|
||||
|
||||
def self.parse(rel_data)
|
||||
new(*sprite_data[0..SIZE-1]).unpack("V*")
|
||||
end
|
||||
|
||||
def pixel_range
|
||||
SIZE...size # maybe,anyway
|
||||
end
|
||||
end
|
||||
|
||||
Sprite = Struct.new(
|
||||
:header, :data
|
||||
) do
|
||||
def self.parse(rel_data)
|
||||
hdr = SpriteHeader.parse(rel_data)
|
||||
sprite_pixels = rel_data[hdr.pixel_range]
|
||||
|
||||
Sprite.new(hdr, sprite_pixels)
|
||||
end
|
||||
end
|
||||
=end
|
||||
|
||||
class Parsed
|
||||
attr_reader :header
|
||||
attr_reader :directory
|
||||
attr_reader :sprites
|
||||
|
||||
def initialize(header, directory, sprites)
|
||||
@header = header
|
||||
@directory = directory
|
||||
@sprites = sprites
|
||||
end
|
||||
end
|
||||
|
||||
def self.parse(data)
|
||||
hdr = Header.parse(data)
|
||||
dir = SpriteDir.parse(data[hdr.dir_range])
|
||||
sprites = dir.realize(data[hdr.data_range])
|
||||
|
||||
Parsed.new(hdr, dir, sprites)
|
||||
end
|
||||
end
|
||||
|
||||
parsed = Obj.parse(File.read("orig/Obj/jungtil.obj").force_encoding("BINARY"))
|
||||
|
||||
def hex(num, leading=0)
|
||||
"%0#{leading}x"%num
|
||||
end
|
||||
|
||||
def display(data, blocksize=8, skip=0)
|
||||
bytes = data.bytes
|
||||
nrows = (bytes.count / blocksize)
|
||||
|
||||
skip.upto(nrows) do |i|
|
||||
block = bytes[(i*blocksize)...(i*blocksize+blocksize)]
|
||||
out = [
|
||||
"0x#{hex(i*blocksize, 4)}",
|
||||
block.map { |b| hex(b, 2) },
|
||||
]
|
||||
|
||||
puts out.flatten.join(' ')
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
puts "Dumping blank sprite"
|
||||
display(parsed.sprites[0], 8, 0)
|
||||
|
||||
# The per-sprite data in jungtil.obj is too small to represent a 64x64 block,
|
||||
# even at 1bpp, so try some decompression algorithms to see what comes out.
|
||||
# Step through every byte of each sprite so we manage if some header is present
|
||||
#
|
||||
# Tried so far, with no success:
|
||||
# * LZO
|
||||
|
||||
puts "\nAttempting decompression..."
|
||||
|
||||
require 'lzo'
|
||||
|
||||
parsed.sprites.each_with_index do |sprite, i|
|
||||
print "Sprite %02d..."%i
|
||||
|
||||
(0...sprite.size).each do |offset|
|
||||
block = sprite.byteslice(offset, sprite.size-offset)
|
||||
|
||||
begin
|
||||
decompressed = LZO.decompress(block)
|
||||
puts "succeeded! sprite=#{i} offset=#{offset} decompressed_size=#{decompressed.size}"
|
||||
puts "data:"
|
||||
puts decompressed.inspect
|
||||
exit 0
|
||||
rescue => err
|
||||
end
|
||||
end
|
||||
|
||||
puts "failed"
|
||||
end
|
||||
|
Reference in New Issue
Block a user