179 lines
3.4 KiB
Ruby
Executable File
179 lines
3.4 KiB
Ruby
Executable File
#!/usr/bin/env ruby
|
|
|
|
require 'rmagick'
|
|
require 'yaml'
|
|
require 'erb'
|
|
|
|
data = YAML.load(File.read('spritesheet.yml'))
|
|
|
|
class Sprite
|
|
def initialize(image:, sx:, sy:, width:, height:)
|
|
@image = image
|
|
@sx = sx
|
|
@sy = sy
|
|
@width = width
|
|
@height = height
|
|
end
|
|
|
|
def pixel_data
|
|
@height.times.each_with_object([]) do |y, obj|
|
|
obj << @width.times.map do |x|
|
|
color = @image.pixel_color(x + @sx, y + @sy)
|
|
|
|
colormap.find_index { |c| c == color }
|
|
end
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
def colormap
|
|
@colormap ||= @image.colors.times.map do |i|
|
|
Magick::Pixel.from_color(@image.colormap(i))
|
|
end
|
|
end
|
|
end
|
|
|
|
MovBytePtr = Data.define(:offset, :data)
|
|
|
|
ASM_TEMPLATE = <<~ASM
|
|
<% sprites.each do |sprite| %>
|
|
PUBLIC <%= sprite.exported_name %><% end %>
|
|
|
|
.386
|
|
.model flat,c
|
|
|
|
.CODE
|
|
|
|
<% sprites.each do |sprite| %>
|
|
<%= sprite.exported_name %>:
|
|
push ebp
|
|
mov ebp, esp
|
|
|
|
<% sprite.bytes.each do |byte| %>
|
|
mov BYTE PTR [eax + <%= byte.offset %>], <%= byte.data %><% end %>
|
|
|
|
pop ebp
|
|
ret
|
|
|
|
<% end %>
|
|
end
|
|
ASM
|
|
|
|
C_TEMPLATE = <<~C
|
|
#ifndef __SPRITES_H__
|
|
#define __SPRITES_H__
|
|
|
|
#include "types.h"
|
|
|
|
<% sprites.each do |sprite| %>
|
|
extern void <%= sprite.function_name %>(byte *);
|
|
<% end %>
|
|
|
|
// TODO: something with bounds checking here
|
|
|
|
#endif
|
|
C
|
|
|
|
class AssemblerSprite
|
|
def initialize(
|
|
name:,
|
|
pixel_data:,
|
|
screen_width:,
|
|
transparent_color:,
|
|
offset_x:,
|
|
offset_y:,
|
|
prefix:
|
|
)
|
|
@name = name
|
|
@pixel_data = pixel_data
|
|
@screen_width = screen_width
|
|
@transparent_color = transparent_color
|
|
@offset_x = offset_x
|
|
@offset_y = offset_y
|
|
@prefix = prefix
|
|
end
|
|
|
|
def exported_name
|
|
"#{function_name}_"
|
|
end
|
|
|
|
def function_name
|
|
"#{@prefix}_#{@name}"
|
|
end
|
|
|
|
def bytes
|
|
return @bytes if defined?(@bytes)
|
|
|
|
bytes = []
|
|
|
|
@pixel_data.each_with_index do |row, y|
|
|
row.each_with_index do |column, x|
|
|
next if @pixel_data[y][x] == @transparent_color
|
|
|
|
bytes << MovBytePtr.new(
|
|
data: @pixel_data[y][x],
|
|
offset: x - @offset_x + (y - @offset_y) * @screen_width
|
|
)
|
|
end
|
|
end
|
|
|
|
bytes
|
|
end
|
|
end
|
|
|
|
Screen = Data.define(:width, :transparent_color)
|
|
SpriteData = Data.define(:image, :name, :position, :dimensions, :offset, :prefix)
|
|
|
|
screen = Screen.new(
|
|
width: data["screen"]["width"],
|
|
transparent_color: data["screen"]["transparentColor"]
|
|
)
|
|
|
|
sprite_data = []
|
|
|
|
data["files"].each do |spritesheet|
|
|
image = Magick::Image.read(spritesheet["file"]).first
|
|
|
|
spritesheet["sprites"].each do |name, details|
|
|
sprite_data << SpriteData.new(
|
|
image:,
|
|
name:,
|
|
prefix: spritesheet["prefix"],
|
|
position: details["position"],
|
|
dimensions: details["dimensions"],
|
|
offset: details["offset"]
|
|
)
|
|
end
|
|
end
|
|
|
|
sprites = sprite_data.map do |sd|
|
|
sprite = Sprite.new(
|
|
image: sd.image,
|
|
sx: sd.position[0],
|
|
sy: sd.position[1],
|
|
width: sd.dimensions[0],
|
|
height: sd.dimensions[1],
|
|
)
|
|
|
|
AssemblerSprite.new(
|
|
name: sd.name,
|
|
prefix: sd.prefix,
|
|
pixel_data: sprite.pixel_data,
|
|
screen_width: screen.width,
|
|
transparent_color: screen.transparent_color,
|
|
offset_x: sd.offset[0],
|
|
offset_y: sd.offset[1],
|
|
)
|
|
end
|
|
|
|
File.open('sprites.asm', 'w') do |fh|
|
|
fh.puts ERB.new(ASM_TEMPLATE).result_with_hash(sprites:)
|
|
end
|
|
|
|
File.open('sprites.h', 'w') do |fh|
|
|
fh.puts ERB.new(C_TEMPLATE).result_with_hash(sprites:)
|
|
end
|
|
|
|
puts "sprites.{asm,h} written"
|